]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_report_editor.js
LP1889113 Staff catalog record holds sticky org select
[Evergreen.git] / Open-ILS / web / reports / oils_rpt_report_editor.js
1 dojo.requireLocalization("openils.reports", "reports");
2
3 var rpt_strings = dojo.i18n.getLocalization("openils.reports", "reports");
4
5 oilsRptSetSubClass('oilsRptReportEditor', 'oilsRptObject');
6 var oilsRptReportEditorFolderTree;
7
8 function oilsRptReportEditor(rptObject, folderWindow, readonly) {
9         var tmpl = rptObject.templateObject;
10         var rpt = rptObject.reportObject;
11         this.folderWindow = folderWindow;
12     this.readonly = readonly;
13
14         this.template = tmpl;
15         this.report = rpt;
16
17     if (rpt && rpt.runs() && rpt.runs().length)
18         this.last_run = rpt.runs()[rpt.runs().length - 1];
19
20         appendClear(DOM.oils_rpt_report_editor_template_name, tmpl.name());
21         appendClear(DOM.oils_rpt_report_editor_template_creator, tmpl.owner().usrname());
22         appendClear(DOM.oils_rpt_report_editor_template_description, tmpl.description());
23
24     hideMe(DOM.oils_rpt_report_editor_template_doc_url_row);
25     if (rptObject.def.version >= 4) {
26         if (URL = rptObject.def.doc_url) {
27             var link = DOM.oils_rpt_report_editor_template_doc_url;
28             link.innerHTML = URL;
29             if (typeof xulG == 'undefined') {
30                 link.setAttribute('href', URL);
31                 link.setAttribute('target', '_blank');
32             } else {
33                 link.onclick = function() {xulG.new_tab(URL); return false}
34             }
35             unHideMe(DOM.oils_rpt_report_editor_template_doc_url_row);
36         }
37     }
38     
39     appendClear(DOM.oils_rpt_report_editor_cols,'');
40         iterate(rptObject.def.select, 
41                 function(i) {
42                         if(i)
43                                 DOM.oils_rpt_report_editor_cols.appendChild(text(i.alias));
44                 if (i.field_doc) {
45                                     DOM.oils_rpt_report_editor_cols.appendChild(
46                         elem('span', {'class':'oils_rpt_field_hint'}, i.field_doc));
47                 }
48                                 DOM.oils_rpt_report_editor_cols.appendChild(document.createElement('br'));
49                 }
50         );
51
52 /*
53 oils_rpt_editor_pivot_label
54 oils_rpt_editor_pivot_data
55 */
56
57     var hasAgg = false;
58     iterate(rptObject.def.select, 
59         function(i) {
60             if(OILS_RPT_TRANSFORMS[i.column.transform].aggregate) 
61                 hasAgg = true; 
62         }
63     );
64
65     while(DOM.oils_rpt_editor_pivot_label.getElementsByTagName('option').length > 1)
66         DOM.oils_rpt_editor_pivot_label.removeChild(DOM.oils_rpt_editor_pivot_label.lastChild);
67
68     while(DOM.oils_rpt_editor_pivot_data.lastChild)
69         DOM.oils_rpt_editor_pivot_data.removeChild(DOM.oils_rpt_editor_pivot_data.lastChild);
70
71     if(hasAgg) {
72         unHideMe(DOM.oils_rpt_editor_pivot_label_row);
73         unHideMe(DOM.oils_rpt_editor_pivot_data_row);
74
75         for(var i in rptObject.def.select) {
76             var col = rptObject.def.select[i];
77             if(OILS_RPT_TRANSFORMS[col.column.transform].aggregate) 
78                insertSelectorVal(DOM.oils_rpt_editor_pivot_data, -1, col.alias, parseInt(i)+1);
79             else
80                insertSelectorVal(DOM.oils_rpt_editor_pivot_label, -1, col.alias, parseInt(i)+1);
81         }
82
83     } else {
84         hideMe(DOM.oils_rpt_editor_pivot_label_row);
85         hideMe(DOM.oils_rpt_editor_pivot_data_row);
86     }
87
88
89     // schedule defaults.
90     DOM.oils_rpt_param_editor_sched_start_date.value = mkYearMonDay();
91     setSelector(DOM.oils_rpt_param_editor_sched_start_hour, '12:00');
92     DOM.oils_rpt_report_editor_run_now.checked = true;
93     DOM.oils_rpt_report_editor_schedule.checked = false;
94     DOM.oils_rpt_param_editor_sched_start_date.disabled = true;
95     DOM.oils_rpt_param_editor_sched_start_hour.disabled = true;
96
97     // recur defaults
98     setSelector(DOM.oils_rpt_recur_interval_type, 'days');
99     setSelector(DOM.oils_rpt_recur_count, '1');
100
101         if( rpt ) {
102         // populate the report edit form w/ report data
103
104         this.orig_rpt_name = rpt.name();
105
106                 DOM.oils_rpt_report_editor_name.value = rpt.name();
107                 DOM.oils_rpt_report_editor_name.onchange(); // validation
108                 DOM.oils_rpt_report_editor_desc.value = rpt.description();
109
110         if (rpt.recur() == 't') {
111             DOM.oils_rpt_recur.checked = true;
112             DOM.oils_rpt_recur.onclick(); // enable recurrance selector
113         }
114
115         if (rpt.recurrence()) {
116             console.log('editing report with recurrence: ' + rpt.recurrence());
117             var parts = rpt.recurrence().split(/ /);
118             var type = parts[1];
119             var count = Number(parts[0]);
120
121             if (type.match(/^mon/)) {
122                 // PG stores 'months' as 'mon(s)'
123                 type = 'months'; 
124             } else if (type.match(/^day/)) {
125                 // PG stores weeks as days.  Assuming a person would typically
126                 // use weeks to represent sets of 7 days, translate back to
127                 // weeks when we can.
128                 if (count % 7 == 0) {
129                     type = 'weeks';
130                     count = count / 7;
131                 }
132             }
133
134             setSelector(DOM.oils_rpt_recur_count, count);
135             setSelector(DOM.oils_rpt_recur_interval_type, type);
136         }
137
138         if (rpt.data()) { 
139             var rpt_data = JSON2js(rpt.data());
140             if (rpt_data.__pivot_label)
141                 setSelector(DOM.oils_rpt_editor_pivot_label, rpt_data.__pivot_label);
142             if (rpt_data.__pivot_data)
143                 setSelector(DOM.oils_rpt_editor_pivot_data, rpt_data.__pivot_data);
144         }
145
146         if (run = this.last_run) {
147                     DOM.oils_rpt_report_editor_name.disabled = true;
148                     DOM.oils_rpt_report_editor_desc.disabled = true;
149             DOM.oils_rpt_format_csv.checked = run.csv_format() == 't';
150             DOM.oils_rpt_format_excel.checked = run.excel_format() == 't';
151             DOM.oils_rpt_format_html.checked = run.html_format() == 't';
152             DOM.oils_rpt_format_chart_bar.checked = run.chart_bar() == 't';
153             DOM.oils_rpt_format_chart_line.checked = run.chart_line() == 't';
154             DOM.oils_rpt_param_editor_sched_email.value = run.email();
155
156             if (run.run_time()) {
157                 console.log('view/edit report with last run_time: ' + run.run_time());
158                 if (new Date(Date.parse(run.run_time())) >= new Date() || this.readonly) {
159                     // Next run of the edited report is scheduled for some time in the future.
160                     // Propagate the value into the data selector and de-select run-now.
161                     // Ditto read-only mode, so the user can see info on the most recent run.
162
163                     DOM.oils_rpt_param_editor_sched_start_date.value = 
164                         run.run_time().match(/(\d{4}-\d{2}-\d{2})/)[1]
165
166                     setSelector(
167                         DOM.oils_rpt_param_editor_sched_start_hour,
168                         run.run_time().match(/T(\d{2})/)[1] + ':00'
169                     );
170
171                     DOM.oils_rpt_report_editor_run_now.checked = false;
172                     DOM.oils_rpt_report_editor_schedule.checked = true;
173                     DOM.oils_rpt_param_editor_sched_start_date.disabled = false;
174                     DOM.oils_rpt_param_editor_sched_start_hour.disabled = false;
175                 }
176             } 
177         }
178         }
179
180     if (this.readonly) {
181         DOM.oils_rpt_report_editor_name.disabled = true;
182         DOM.oils_rpt_report_editor_desc.disabled = true;
183         DOM.oils_rpt_recur.disabled = true;
184         DOM.oils_rpt_recur_count.disabled = true;
185         DOM.oils_rpt_recur_interval_type.disabled = true;
186         DOM.oils_rpt_report_editor_run_now.disabled = true;
187         DOM.oils_rpt_format_csv.disabled = true;
188         DOM.oils_rpt_format_excel.disabled = true;
189         DOM.oils_rpt_format_html.disabled = true;
190         DOM.oils_rpt_format_chart_bar.disabled = true;
191         DOM.oils_rpt_format_chart_line.disabled = true;
192         DOM.oils_rpt_param_editor_sched_email.disabled = true;
193
194         hideMe(DOM.oils_rpt_report_editor_save);
195         hideMe(DOM.oils_rpt_report_editor_save_new);
196         hideMe(DOM.oils_rpt_report_editor_cancel);
197         unHideMe(DOM.oils_rpt_report_editor_exit);
198
199     } else {
200         // these DOM elements are shared across instances
201         // of the UI.  Re-enable everything.
202         DOM.oils_rpt_report_editor_name.disabled = false;
203         DOM.oils_rpt_report_editor_desc.disabled = false;
204         DOM.oils_rpt_recur.disabled = false;
205         DOM.oils_rpt_recur_count.disabled = false;
206         DOM.oils_rpt_recur_interval_type.disabled = false;
207         DOM.oils_rpt_report_editor_run_now.disabled = false;
208         DOM.oils_rpt_format_csv.disabled = false;
209         DOM.oils_rpt_format_excel.disabled = false;
210         DOM.oils_rpt_format_html.disabled = false;
211         DOM.oils_rpt_format_chart_bar.disabled = false;
212         DOM.oils_rpt_format_chart_line.disabled = false;
213         DOM.oils_rpt_param_editor_sched_email.disabled = false;
214         DOM.oils_rpt_report_editor_save.disabled = false;
215
216         unHideMe(DOM.oils_rpt_report_editor_save);
217         unHideMe(DOM.oils_rpt_report_editor_cancel);
218         hideMe(DOM.oils_rpt_report_editor_exit);
219
220     }
221
222     // avoid showing save-as-new for new reports, since the
223     // regular save button acts as save-as-new
224     if (rpt && !this.readonly) {
225         unHideMe(DOM.oils_rpt_report_editor_save_new);
226     } else {
227         hideMe(DOM.oils_rpt_report_editor_save_new);
228     }
229
230         this.paramEditor = new oilsRptParamEditor(
231                 rptObject, DOM.oils_rpt_param_editor_tbody, this.readonly);
232         this.paramEditor.draw();
233
234         removeChildren(DOM.oils_rpt_report_editor_selected_folder);
235         removeChildren(DOM.oils_rpt_output_selected_folder);
236
237         var obj = this;
238         oilsRptBuildFolder(
239                 'report',
240                 DOM.oils_rpt_report_editor_dest_folder,
241                 'oilsRptReportEditorFolderTree',
242                 rpt_strings.REPORT_EDITOR_REPORT_FOLDERS,
243                 function(node) { 
244                         appendClear(DOM.oils_rpt_report_editor_selected_folder, node.folder.name());
245                         obj.selectedFolder = node; 
246         },
247         null,
248         function(node) {
249             // apply the previously selected report folder
250             if (rpt && rpt.folder() == node.folder.id()) {
251                             appendClear(DOM.oils_rpt_report_editor_selected_folder, node.folder.name());
252                             obj.selectedFolder = node; 
253             }
254         }
255     );
256
257         oilsRptBuildFolder(
258                 'output',
259                 DOM.oils_rpt_output_dest_folder,
260                 'oilsRptReportEditorOutputTree',
261                 rpt_strings.REPORT_EDITOR_OUTPUT_FOLDERS,
262                 function(node) { 
263                         appendClear(DOM.oils_rpt_output_selected_folder, node.folder.name());
264                         obj.selectedOutputFolder = node; 
265         },
266         null,
267         function(node) {
268             // apply the previously selected output folder
269             if (obj.last_run && obj.last_run.folder() == node.folder.id()) {
270                             appendClear(DOM.oils_rpt_output_selected_folder, node.folder.name());
271                             obj.selectedOutputFolder = node; 
272             }
273         }
274     );
275
276
277         var obj = this;
278         DOM.oils_rpt_report_editor_save.onclick = function(){obj.save();}
279         DOM.oils_rpt_report_editor_save_new.onclick = function(){obj.save({save_new : true});}
280         DOM.oils_rpt_report_editor_exit.onclick = function(){obj.exit();}
281         DOM.oils_rpt_report_editor_cancel.onclick = function(){obj.exit();}
282
283         DOM.oils_rpt_param_editor_sched_email.value = 
284         this.last_run ? this.last_run.email() : USER.email();
285
286         _debug("fleshing template:\n" + tmpl.name() + '\n' + formatJSON(tmpl.data()));
287 }
288
289
290 // options.save_new : save as a new report, even if we
291 // were editing an exitingn report.
292 //
293 // options.modify_schedule : update the pending schedule
294 // object instead of creating a new one.
295 oilsRptReportEditor.prototype.save = function(options) {
296     if (!options) options = {};
297
298         if(!this.selectedFolder) 
299                 return alert(rpt_strings.REPORT_EDITOR_PROVIDE_FOLDER_ALERT);
300
301         if(!DOM.oils_rpt_report_editor_name.value)
302                 return alert(rpt_strings.REPORT_EDITOR_ENTER_NAME_ALERT);
303
304         if(!this.selectedOutputFolder) 
305                 return alert(rpt_strings.REPORT_EDITOR_PROVIDE_OUTPUT_ALERT);
306
307         var report = this.report;
308
309     if (report && options.save_new) {
310         // user is saving an existing report as a new report.
311         // The new report must have a different name.
312         if (DOM.oils_rpt_report_editor_name.value == this.orig_rpt_name) 
313             return alert(rpt_strings.REPORT_EDITOR_ENTER_NEW_NAME_ALERT);
314
315         report = null;
316     }
317
318     if (!report) {
319         report = new rr();
320         report.isnew(true);
321             report.owner( USER.id() );
322             report.template( this.template.id() );
323     }
324
325         report.folder( this.selectedFolder.folder.id() );
326         report.name( DOM.oils_rpt_report_editor_name.value );
327         report.description( DOM.oils_rpt_report_editor_desc.value );
328         report.recur(this.paramEditor.recur());
329         report.recurrence(this.paramEditor.recurInterval());
330
331         /* collect the param data */
332         var data = {};
333         for( var p in this.paramEditor.params ) {
334                 var par = this.paramEditor.params[p];
335                 _debug("adding report param "+par.key+" to report data");
336                 var val = par.widget.getValue();
337
338                 if(!val || val.length == 0 )
339                         return alertId('oils_rpt_empty_param');
340
341                 if( typeof val == 'object') {
342                         for( var i = i; i < val.length; i++ ) {
343                                 _debug("looking at widget value" + val[i]);
344                                 if( val[i] == '' || val[i] == null ) 
345                                         return alertId('oils_rpt_empty_param');
346                         }
347                 }
348
349                 data[par.key] = val;
350         }
351
352     if(getSelectorVal(DOM.oils_rpt_editor_pivot_data)) {
353         data.__pivot_label = getSelectorVal(DOM.oils_rpt_editor_pivot_label);
354         data.__pivot_data = getSelectorVal(DOM.oils_rpt_editor_pivot_data);
355     }
356
357
358         data = js2JSON(data);
359         _debug("complete report data = "+data);
360         report.data(data);
361
362         _debug("Built report:\n"+js2JSON(report));
363
364
365         var time;
366         if( DOM.oils_rpt_report_editor_run_now.checked ) {
367                 time = 'now';
368
369         } else {
370
371                 var dt = DOM.oils_rpt_param_editor_sched_start_date.value;
372                 if(!dt || !dt.match(/^\d{4}-\d{2}-\d{2}$/) ) {
373                         /* for now.. make this better in the future */
374                         alert(rpt_strings.REPORT_EDITOR_INVALID_DATE_ALERT);
375                         return;
376                 }
377                 var hour = getSelectorVal(DOM.oils_rpt_param_editor_sched_start_hour);
378                 time = dt +'T'+hour+':00';
379                 _debug("built run_time "+time);
380         }
381
382     // if the last run has yet to actually start, then we update it
383     // instead of creating a new one.
384     var schedule = options.save_new ? null : this.last_run;
385
386     if (schedule && !schedule.start_time()) {
387         if (!options.modify_schedule) {
388             // warn the user that this action will modify an existing
389             // schedule object if they continue
390             return this.showPendingScheduleDialog();
391         }
392     } else {
393         // no schedules exist or the most recent one has already
394         // started.  Create a new one.
395             schedule = new rs();
396         schedule.isnew(true);
397     }
398
399         schedule.folder(this.selectedOutputFolder.folder.id());
400         schedule.email(DOM.oils_rpt_param_editor_sched_email.value);
401         schedule.run_time(time);
402         schedule.runner(USER.id());
403
404         schedule.excel_format((DOM.oils_rpt_format_excel.checked) ? 't' : 'f');
405         schedule.html_format((DOM.oils_rpt_format_html.checked) ? 't' : 'f');
406         schedule.csv_format((DOM.oils_rpt_format_csv.checked) ? 't' : 'f');
407         //schedule.chart_pie((DOM.oils_rpt_format_chart_pie.checked) ? 't' : 'f');
408         schedule.chart_bar((DOM.oils_rpt_format_chart_bar.checked) ? 't' : 'f');
409         schedule.chart_line((DOM.oils_rpt_format_chart_line.checked) ? 't' : 'f');
410
411         debugFMObject(report);
412         debugFMObject(schedule);
413
414     if (report.isnew()) {
415         this.createReport(report, schedule);
416     } else {
417         this.updateReport(report, schedule);
418     }
419 }
420
421 // Modify an existing report.
422 // Modify or create the schedule depending on isnew()
423 oilsRptReportEditor.prototype.updateReport = function(report, schedule) {
424
425     var this_ = this;
426     function success() {
427         oilsRptAlertSuccess();
428         this_.exit();
429     }
430
431     oilsRptUpdateReport(report, function(ok) {
432         if (!ok) return oilsRptAlertFailure();
433
434         if (schedule.isnew()) {
435
436             var req = new Request(OILS_RPT_CREATE_SCHEDULE, SESSION, schedule);
437             req.callback(function(res) {
438                 if(checkILSEvent(res)) 
439                     return alertILSEvent(res);
440                 success();
441             });
442             req.send()
443
444         } else {
445
446             oilsRptUpdateSchedule(schedule, function(ok2) {
447                 if (ok2) return success();
448                 _debug("schedule update failed " + js2JSON(schedule));
449                 oilsRptAlertFailure();
450             });
451         }
452     });
453 }
454
455 oilsRptReportEditor.prototype.createReport = function(report, schedule) {
456         var obj = this;
457     var folderReq = new Request(OILS_RPT_REPORT_EXISTS, SESSION, report);
458     folderReq.callback(
459         function(r1) {
460             if(r1.getResultObject() == 1) {
461                 alertId('oils_rpt_report_exists');
462                 return;
463             } else {
464                 var req = new Request(OILS_RPT_CREATE_REPORT, SESSION, report, schedule );
465                 req.callback(
466                     function(r) {
467                         var res = r.getResultObject();
468                         if(checkILSEvent(res)) {
469                             alertILSEvent(res);
470                         } else {
471                             if( res && res != '0' ) {
472                                 oilsRptAlertSuccess();
473                                 obj.exit();
474                             }
475                         }
476                     }
477                 );
478                 req.send();
479             }
480         }
481     );
482     folderReq.send();
483 }
484
485 oilsRptReportEditor.prototype.showPendingScheduleDialog = function() {
486     hideMe(DOM.oils_rpt_editor_table);
487     unHideMe(DOM.oils_rpt_editor_sched_confirm);
488
489     function close() {
490         unHideMe(DOM.oils_rpt_editor_table);
491         hideMe(DOM.oils_rpt_editor_sched_confirm);
492     }
493
494     var this_ = this;
495     DOM.oils_rpt_report_editor_sched_apply.onclick = function() {
496         close();
497         this_.save({modify_schedule : true});
498     }
499
500     DOM.oils_rpt_report_editor_sched_asnew.onclick = function() {
501
502         if (DOM.oils_rpt_report_editor_name.value == this_.orig_rpt_name) {
503             // user is saving as new but has not yet modified the name
504             // Prompt for a new name, then udpate the name entry so save() 
505             // will see it.  Don't let them escape until they comply.
506             var new_name;
507             while (true) { 
508
509                 new_name = prompt(
510                     rpt_strings.REPORT_EDITOR_ENTER_NEW_NAME_ALERT, 
511                     this_.orig_rpt_name
512                 );
513                 
514                 if (new_name && new_name != this_.orig_rpt_name)
515                     break;
516             }
517
518             DOM.oils_rpt_report_editor_name.value = new_name;
519         } 
520
521         close();
522         this_.save({save_new : true})
523     }
524     DOM.oils_rpt_report_editor_sched_cancel.onclick = close;
525 }
526
527
528 oilsRptReportEditor.prototype.exit = function() {
529     unHideMe(DOM.oils_rpt_folder_window_contents_table);                   
530     unHideMe(DOM.oils_rpt_folder_table_right_td);
531     hideMe(DOM.oils_rpt_folder_table_alt_td);
532     hideMe(DOM.oils_rpt_editor_div);
533 }