]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_report_editor.js
LP#1789679: Provide ROLLUP support in report output
[working/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             DOM.oils_rpt_editor_do_rollup.checked = rpt_data.__do_rollup == '1';
145         }
146
147         if (run = this.last_run) {
148                     DOM.oils_rpt_report_editor_name.disabled = true;
149                     DOM.oils_rpt_report_editor_desc.disabled = true;
150             DOM.oils_rpt_format_csv.checked = run.csv_format() == 't';
151             DOM.oils_rpt_format_excel.checked = run.excel_format() == 't';
152             DOM.oils_rpt_format_html.checked = run.html_format() == 't';
153             DOM.oils_rpt_format_chart_bar.checked = run.chart_bar() == 't';
154             DOM.oils_rpt_format_chart_line.checked = run.chart_line() == 't';
155             DOM.oils_rpt_param_editor_sched_email.value = run.email();
156
157             if (run.run_time()) {
158                 console.log('view/edit report with last run_time: ' + run.run_time());
159                 if (new Date(Date.parse(run.run_time())) >= new Date() || this.readonly) {
160                     // Next run of the edited report is scheduled for some time in the future.
161                     // Propagate the value into the data selector and de-select run-now.
162                     // Ditto read-only mode, so the user can see info on the most recent run.
163
164                     DOM.oils_rpt_param_editor_sched_start_date.value = 
165                         run.run_time().match(/(\d{4}-\d{2}-\d{2})/)[1]
166
167                     setSelector(
168                         DOM.oils_rpt_param_editor_sched_start_hour,
169                         run.run_time().match(/T(\d{2})/)[1] + ':00'
170                     );
171
172                     DOM.oils_rpt_report_editor_run_now.checked = false;
173                     DOM.oils_rpt_report_editor_schedule.checked = true;
174                     DOM.oils_rpt_param_editor_sched_start_date.disabled = false;
175                     DOM.oils_rpt_param_editor_sched_start_hour.disabled = false;
176                 }
177             } 
178         }
179         }
180
181     if (this.readonly) {
182         DOM.oils_rpt_report_editor_name.disabled = true;
183         DOM.oils_rpt_report_editor_desc.disabled = true;
184         DOM.oils_rpt_recur.disabled = true;
185         DOM.oils_rpt_recur_count.disabled = true;
186         DOM.oils_rpt_recur_interval_type.disabled = true;
187         DOM.oils_rpt_report_editor_run_now.disabled = true;
188         DOM.oils_rpt_format_csv.disabled = true;
189         DOM.oils_rpt_format_excel.disabled = true;
190         DOM.oils_rpt_format_html.disabled = true;
191         DOM.oils_rpt_format_chart_bar.disabled = true;
192         DOM.oils_rpt_format_chart_line.disabled = true;
193         DOM.oils_rpt_param_editor_sched_email.disabled = true;
194
195         hideMe(DOM.oils_rpt_report_editor_save);
196         hideMe(DOM.oils_rpt_report_editor_save_new);
197         hideMe(DOM.oils_rpt_report_editor_cancel);
198         unHideMe(DOM.oils_rpt_report_editor_exit);
199
200     } else {
201         // these DOM elements are shared across instances
202         // of the UI.  Re-enable everything.
203         DOM.oils_rpt_report_editor_name.disabled = false;
204         DOM.oils_rpt_report_editor_desc.disabled = false;
205         DOM.oils_rpt_recur.disabled = false;
206         DOM.oils_rpt_recur_count.disabled = false;
207         DOM.oils_rpt_recur_interval_type.disabled = false;
208         DOM.oils_rpt_report_editor_run_now.disabled = false;
209         DOM.oils_rpt_format_csv.disabled = false;
210         DOM.oils_rpt_format_excel.disabled = false;
211         DOM.oils_rpt_format_html.disabled = false;
212         DOM.oils_rpt_format_chart_bar.disabled = false;
213         DOM.oils_rpt_format_chart_line.disabled = false;
214         DOM.oils_rpt_param_editor_sched_email.disabled = false;
215         DOM.oils_rpt_report_editor_save.disabled = false;
216
217         unHideMe(DOM.oils_rpt_report_editor_save);
218         unHideMe(DOM.oils_rpt_report_editor_cancel);
219         hideMe(DOM.oils_rpt_report_editor_exit);
220
221     }
222
223     // avoid showing save-as-new for new reports, since the
224     // regular save button acts as save-as-new
225     if (rpt && !this.readonly) {
226         unHideMe(DOM.oils_rpt_report_editor_save_new);
227     } else {
228         hideMe(DOM.oils_rpt_report_editor_save_new);
229     }
230
231         this.paramEditor = new oilsRptParamEditor(
232                 rptObject, DOM.oils_rpt_param_editor_tbody, this.readonly);
233         this.paramEditor.draw();
234
235         removeChildren(DOM.oils_rpt_report_editor_selected_folder);
236         removeChildren(DOM.oils_rpt_output_selected_folder);
237
238         var obj = this;
239         oilsRptBuildFolder(
240                 'report',
241                 DOM.oils_rpt_report_editor_dest_folder,
242                 'oilsRptReportEditorFolderTree',
243                 rpt_strings.REPORT_EDITOR_REPORT_FOLDERS,
244                 function(node) { 
245                         appendClear(DOM.oils_rpt_report_editor_selected_folder, node.folder.name());
246                         obj.selectedFolder = node; 
247         },
248         null,
249         function(node) {
250             // apply the previously selected report folder
251             if (rpt && rpt.folder() == node.folder.id()) {
252                             appendClear(DOM.oils_rpt_report_editor_selected_folder, node.folder.name());
253                             obj.selectedFolder = node; 
254             }
255         }
256     );
257
258         oilsRptBuildFolder(
259                 'output',
260                 DOM.oils_rpt_output_dest_folder,
261                 'oilsRptReportEditorOutputTree',
262                 rpt_strings.REPORT_EDITOR_OUTPUT_FOLDERS,
263                 function(node) { 
264                         appendClear(DOM.oils_rpt_output_selected_folder, node.folder.name());
265                         obj.selectedOutputFolder = node; 
266         },
267         null,
268         function(node) {
269             // apply the previously selected output folder
270             if (obj.last_run && obj.last_run.folder() == node.folder.id()) {
271                             appendClear(DOM.oils_rpt_output_selected_folder, node.folder.name());
272                             obj.selectedOutputFolder = node; 
273             }
274         }
275     );
276
277
278         var obj = this;
279         DOM.oils_rpt_report_editor_save.onclick = function(){obj.save();}
280         DOM.oils_rpt_report_editor_save_new.onclick = function(){obj.save({save_new : true});}
281         DOM.oils_rpt_report_editor_exit.onclick = function(){obj.exit();}
282         DOM.oils_rpt_report_editor_cancel.onclick = function(){obj.exit();}
283
284         DOM.oils_rpt_param_editor_sched_email.value = 
285         this.last_run ? this.last_run.email() : USER.email();
286
287         _debug("fleshing template:\n" + tmpl.name() + '\n' + formatJSON(tmpl.data()));
288 }
289
290
291 // options.save_new : save as a new report, even if we
292 // were editing an exitingn report.
293 //
294 // options.modify_schedule : update the pending schedule
295 // object instead of creating a new one.
296 oilsRptReportEditor.prototype.save = function(options) {
297     if (!options) options = {};
298
299         if(!this.selectedFolder) 
300                 return alert(rpt_strings.REPORT_EDITOR_PROVIDE_FOLDER_ALERT);
301
302         if(!DOM.oils_rpt_report_editor_name.value)
303                 return alert(rpt_strings.REPORT_EDITOR_ENTER_NAME_ALERT);
304
305         if(!this.selectedOutputFolder) 
306                 return alert(rpt_strings.REPORT_EDITOR_PROVIDE_OUTPUT_ALERT);
307
308         var report = this.report;
309
310     if (report && options.save_new) {
311         // user is saving an existing report as a new report.
312         // The new report must have a different name.
313         if (DOM.oils_rpt_report_editor_name.value == this.orig_rpt_name) 
314             return alert(rpt_strings.REPORT_EDITOR_ENTER_NEW_NAME_ALERT);
315
316         report = null;
317     }
318
319     if (!report) {
320         report = new rr();
321         report.isnew(true);
322             report.owner( USER.id() );
323             report.template( this.template.id() );
324     }
325
326         report.folder( this.selectedFolder.folder.id() );
327         report.name( DOM.oils_rpt_report_editor_name.value );
328         report.description( DOM.oils_rpt_report_editor_desc.value );
329         report.recur(this.paramEditor.recur());
330         report.recurrence(this.paramEditor.recurInterval());
331
332         /* collect the param data */
333         var data = {};
334         for( var p in this.paramEditor.params ) {
335                 var par = this.paramEditor.params[p];
336                 _debug("adding report param "+par.key+" to report data");
337                 var val = par.widget.getValue();
338
339                 if(!val || val.length == 0 )
340                         return alertId('oils_rpt_empty_param');
341
342                 if( typeof val == 'object') {
343                         for( var i = i; i < val.length; i++ ) {
344                                 _debug("looking at widget value" + val[i]);
345                                 if( val[i] == '' || val[i] == null ) 
346                                         return alertId('oils_rpt_empty_param');
347                         }
348                 }
349
350                 data[par.key] = val;
351         }
352
353     if(getSelectorVal(DOM.oils_rpt_editor_pivot_data)) {
354         data.__pivot_label = getSelectorVal(DOM.oils_rpt_editor_pivot_label);
355         data.__pivot_data = getSelectorVal(DOM.oils_rpt_editor_pivot_data);
356     }
357     data.__do_rollup = DOM.oils_rpt_editor_do_rollup.checked ? '1' : '0';
358
359         data = js2JSON(data);
360         _debug("complete report data = "+data);
361         report.data(data);
362
363         _debug("Built report:\n"+js2JSON(report));
364
365
366         var time;
367         if( DOM.oils_rpt_report_editor_run_now.checked ) {
368                 time = 'now';
369
370         } else {
371
372                 var dt = DOM.oils_rpt_param_editor_sched_start_date.value;
373                 if(!dt || !dt.match(/^\d{4}-\d{2}-\d{2}$/) ) {
374                         /* for now.. make this better in the future */
375                         alert(rpt_strings.REPORT_EDITOR_INVALID_DATE_ALERT);
376                         return;
377                 }
378                 var hour = getSelectorVal(DOM.oils_rpt_param_editor_sched_start_hour);
379                 time = dt +'T'+hour+':00';
380                 _debug("built run_time "+time);
381         }
382
383     // if the last run has yet to actually start, then we update it
384     // instead of creating a new one.
385     var schedule = options.save_new ? null : this.last_run;
386
387     if (schedule && !schedule.start_time()) {
388         if (!options.modify_schedule) {
389             // warn the user that this action will modify an existing
390             // schedule object if they continue
391             return this.showPendingScheduleDialog();
392         }
393     } else {
394         // no schedules exist or the most recent one has already
395         // started.  Create a new one.
396             schedule = new rs();
397         schedule.isnew(true);
398     }
399
400         schedule.folder(this.selectedOutputFolder.folder.id());
401         schedule.email(DOM.oils_rpt_param_editor_sched_email.value);
402         schedule.run_time(time);
403         schedule.runner(USER.id());
404
405         schedule.excel_format((DOM.oils_rpt_format_excel.checked) ? 't' : 'f');
406         schedule.html_format((DOM.oils_rpt_format_html.checked) ? 't' : 'f');
407         schedule.csv_format((DOM.oils_rpt_format_csv.checked) ? 't' : 'f');
408         //schedule.chart_pie((DOM.oils_rpt_format_chart_pie.checked) ? 't' : 'f');
409         schedule.chart_bar((DOM.oils_rpt_format_chart_bar.checked) ? 't' : 'f');
410         schedule.chart_line((DOM.oils_rpt_format_chart_line.checked) ? 't' : 'f');
411
412         debugFMObject(report);
413         debugFMObject(schedule);
414
415     if (report.isnew()) {
416         this.createReport(report, schedule);
417     } else {
418         this.updateReport(report, schedule);
419     }
420 }
421
422 // Modify an existing report.
423 // Modify or create the schedule depending on isnew()
424 oilsRptReportEditor.prototype.updateReport = function(report, schedule) {
425
426     var this_ = this;
427     function success() {
428         oilsRptAlertSuccess();
429         this_.exit();
430     }
431
432     oilsRptUpdateReport(report, function(ok) {
433         if (!ok) return oilsRptAlertFailure();
434
435         if (schedule.isnew()) {
436
437             var req = new Request(OILS_RPT_CREATE_SCHEDULE, SESSION, schedule);
438             req.callback(function(res) {
439                 if(checkILSEvent(res)) 
440                     return alertILSEvent(res);
441                 success();
442             });
443             req.send()
444
445         } else {
446
447             oilsRptUpdateSchedule(schedule, function(ok2) {
448                 if (ok2) return success();
449                 _debug("schedule update failed " + js2JSON(schedule));
450                 oilsRptAlertFailure();
451             });
452         }
453     });
454 }
455
456 oilsRptReportEditor.prototype.createReport = function(report, schedule) {
457         var obj = this;
458     var folderReq = new Request(OILS_RPT_REPORT_EXISTS, SESSION, report);
459     folderReq.callback(
460         function(r1) {
461             if(r1.getResultObject() == 1) {
462                 alertId('oils_rpt_report_exists');
463                 return;
464             } else {
465                 var req = new Request(OILS_RPT_CREATE_REPORT, SESSION, report, schedule );
466                 req.callback(
467                     function(r) {
468                         var res = r.getResultObject();
469                         if(checkILSEvent(res)) {
470                             alertILSEvent(res);
471                         } else {
472                             if( res && res != '0' ) {
473                                 oilsRptAlertSuccess();
474                                 obj.exit();
475                             }
476                         }
477                     }
478                 );
479                 req.send();
480             }
481         }
482     );
483     folderReq.send();
484 }
485
486 oilsRptReportEditor.prototype.showPendingScheduleDialog = function() {
487     hideMe(DOM.oils_rpt_editor_table);
488     unHideMe(DOM.oils_rpt_editor_sched_confirm);
489
490     function close() {
491         unHideMe(DOM.oils_rpt_editor_table);
492         hideMe(DOM.oils_rpt_editor_sched_confirm);
493     }
494
495     var this_ = this;
496     DOM.oils_rpt_report_editor_sched_apply.onclick = function() {
497         close();
498         this_.save({modify_schedule : true});
499     }
500
501     DOM.oils_rpt_report_editor_sched_asnew.onclick = function() {
502
503         if (DOM.oils_rpt_report_editor_name.value == this_.orig_rpt_name) {
504             // user is saving as new but has not yet modified the name
505             // Prompt for a new name, then udpate the name entry so save() 
506             // will see it.  Don't let them escape until they comply.
507             var new_name;
508             while (true) { 
509
510                 new_name = prompt(
511                     rpt_strings.REPORT_EDITOR_ENTER_NEW_NAME_ALERT, 
512                     this_.orig_rpt_name
513                 );
514                 
515                 if (new_name && new_name != this_.orig_rpt_name)
516                     break;
517             }
518
519             DOM.oils_rpt_report_editor_name.value = new_name;
520         } 
521
522         close();
523         this_.save({save_new : true})
524     }
525     DOM.oils_rpt_report_editor_sched_cancel.onclick = close;
526 }
527
528
529 oilsRptReportEditor.prototype.exit = function() {
530     unHideMe(DOM.oils_rpt_folder_window_contents_table);                   
531     unHideMe(DOM.oils_rpt_folder_table_right_td);
532     hideMe(DOM.oils_rpt_folder_table_alt_td);
533     hideMe(DOM.oils_rpt_editor_div);
534 }