You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

report_builder.js 36 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. // ReportContainer Contains ReportBuilder objects for all DocTypes
  2. // - Only one ReportContainer exists
  3. // - Page header is als a part
  4. // - New ReportBuilder is made here
  5. _r.ReportContainer = function() {
  6. if(user=='Guest') {
  7. msgprint("Not Allowed");
  8. return;
  9. }
  10. this.wrapper = page_body.add_page("Report Builder", function() { });
  11. this.wrapper.className ='layout_wrapper';
  12. var head_div = $a(this.wrapper, 'div');
  13. this.rb_area = $a(this.wrapper, 'div');
  14. $dh(this.wrapper);
  15. var me = this;
  16. this.rb_dict = {};
  17. // tool bar
  18. this.page_head = new PageHeader(head_div);
  19. $y(this.page_head.wrapper, {marginBottom:'0px'});
  20. // buttons
  21. var run_fn = function() {
  22. if(me.cur_rb){
  23. me.cur_rb.dt.start_rec = 1;
  24. me.cur_rb.dt.run();
  25. }
  26. }
  27. var runbtn = this.page_head.add_button('Run', run_fn, 1, 'ui-icon-circle-triangle-e', 1);
  28. // new
  29. if(has_common(['Administrator', 'System Manager'], user_roles)) {
  30. // save
  31. var savebtn = this.page_head.add_button('Save', function() {if(me.cur_rb) me.cur_rb.save_criteria(); }, 0, 'ui-icon-disk');
  32. // advanced
  33. var fn = function() {
  34. if(me.cur_rb) {
  35. if(!me.cur_rb.current_loaded) {
  36. msgprint("error:You must save the report before you can set Advanced features");
  37. return;
  38. }
  39. loaddoc('Search Criteria', me.cur_rb.sc_dict[me.cur_rb.current_loaded]);
  40. }
  41. };
  42. var advancedbtn = this.page_head.add_button('Advanced Settings', fn);
  43. }
  44. // set a type
  45. this.set_dt = function(dt, onload) {
  46. my_onload = function(f) {
  47. if(!f.forbidden) {
  48. me.cur_rb = f;
  49. me.cur_rb.mytabs.items['Result'].expand();
  50. if(onload)onload(f);
  51. }
  52. }
  53. if(me.cur_rb)
  54. me.cur_rb.hide();
  55. if(me.rb_dict[dt]){
  56. me.rb_dict[dt].show(my_onload);
  57. } else {
  58. me.rb_dict[dt] = new _r.ReportBuilder(me.rb_area, dt, my_onload);
  59. }
  60. }
  61. }
  62. // ===================================================================================
  63. // + ReportBuilder
  64. // + Datatable (grid)
  65. // + ColumnPicker
  66. // + ReportFilter
  67. //
  68. // - Contains all methods relating to saving, loading and executing Search Criteria
  69. // - Contains ui objects of the report including tabs
  70. _r.ReportBuilder = function(parent, doctype, onload) {
  71. this.menuitems = {};
  72. this.has_primary_filters = false;
  73. this.doctype = doctype;
  74. this.forbidden = 0;
  75. this.filter_fields = [];
  76. this.filter_fields_dict = {};
  77. var me = this;
  78. this.fn_list = ['beforetableprint','beforerowprint','afterrowprint','aftertableprint','customize_filters','get_query'];
  79. this.wrapper = $a(parent, 'div', 'finder_wrapper');
  80. this.make_tabs();
  81. this.current_loaded = null;
  82. this.setup_doctype(onload);
  83. this.hide = function() {
  84. $dh(me.wrapper);
  85. }
  86. this.show = function(my_onload) {
  87. $ds(me.wrapper);
  88. // reset main title
  89. this.set_main_title('Report: ' + get_doctype_label(me.doctype));
  90. if(my_onload)my_onload(me);
  91. }
  92. }
  93. // -------------------------------------------------------------------------------------
  94. _r.ReportBuilder.prototype.make_tabs = function() {
  95. this.tab_wrapper = $a(this.wrapper, 'div', 'finder_tab_area');
  96. this.mytabs = new TabbedPage(this.tab_wrapper);
  97. this.mytabs.add_item('Result', null, null, 1);
  98. this.mytabs.add_item('More Filters', null, null, 1);
  99. this.mytabs.add_item('Select Columns', null, null, 1);
  100. this.mytabs.tabs = this.mytabs.items;
  101. }
  102. // -------------------------------------------------------------------------------------
  103. _r.ReportBuilder.prototype.make_body = function() {
  104. this.set_main_title('Report: ' + get_doctype_label(this.doctype));
  105. var me = this;
  106. this.make_save_criteria();
  107. this.column_picker = new _r.ReportColumnPicker(this);
  108. this.report_filters = new _r.ReportFilters(this);
  109. }
  110. //
  111. // Make list of all Criterias relating to this DocType
  112. // -------------------------------------------------------------------------------------
  113. // Search Criterias are loaded with the DocType - put them in a list and dict
  114. _r.ReportBuilder.prototype.make_save_criteria = function() {
  115. var me = this;
  116. // make_list
  117. // ---------
  118. this.sc_list = []; this.sc_dict = {};
  119. for(var n in locals['Search Criteria']) {
  120. var d = locals['Search Criteria'][n];
  121. if(d.doc_type==this.doctype) {
  122. this.sc_list[this.sc_list.length] = d.criteria_name;
  123. this.sc_dict[d.criteria_name] = n;
  124. }
  125. }
  126. }
  127. // Save Criteria
  128. // -------------------------------------------------------------------------------------
  129. _r.ReportBuilder.prototype.save_criteria = function(save_as) {
  130. var overwrite = 0;
  131. // is loaded?
  132. if(this.current_loaded && (!save_as)) {
  133. var overwrite = confirm('Do you want to overwrite the saved criteria "'+this.current_loaded+'"');
  134. if(overwrite) {
  135. var doc = locals['Search Criteria'][this.sc_dict[this.current_loaded]];
  136. var criteria_name = this.current_loaded;
  137. }
  138. }
  139. // new criteria
  140. if(!overwrite) {
  141. var criteria_name = prompt('Select a name for the criteria:', '');
  142. if(!criteria_name)
  143. return;
  144. var dn = createLocal('Search Criteria');
  145. var doc = locals['Search Criteria'][dn];
  146. doc.criteria_name = criteria_name;
  147. doc.doc_type = this.doctype;
  148. }
  149. var cl = []; var fl = {};
  150. // save columns
  151. var t = this.column_picker.get_selected();
  152. for(var i=0;i<t.length;i++)
  153. cl.push(t[i].parent + '\1' + t[i].label);
  154. // save filters
  155. for(var i=0;i<this.filter_fields.length;i++) {
  156. var t = this.filter_fields[i];
  157. var v = t.get_value?t.get_value():'';
  158. if(v) fl[t.df.parent + '\1' + t.df.label + (t.bound?('\1'+t.bound):'')] = v;
  159. }
  160. doc.columns = cl.join(',');
  161. doc.filters = JSON.stringify(fl);
  162. // sort by and sort order
  163. doc.sort_by = sel_val(this.dt.sort_sel);
  164. doc.sort_order = this.dt.sort_order;
  165. doc.page_len = this.dt.page_len;
  166. // save advanced
  167. if(this.parent_dt)
  168. doc.parent_doc_type = this.parent_dt
  169. // rename
  170. var me = this;
  171. var fn = function(r) {
  172. me.sc_dict[criteria_name] = r.main_doc_name;
  173. me.set_criteria_sel(criteria_name);
  174. }
  175. //if(this.current_loaded && overwrite) {
  176. // msgprint('Filters and Columns Synchronized. You must also "Save" the Search Criteria to update');
  177. // loaddoc('Search Criteria', this.sc_dict[this.current_loaded]);
  178. //} else {
  179. save_doclist(doc.doctype, doc.name, 'Save', fn); // server-side save
  180. //}
  181. }
  182. // -------------------------------------------------------------------------------------
  183. _r.ReportBuilder.prototype.hide_all_filters = function() {
  184. for(var i=0; i<this.filter_fields.length; i++) {
  185. this.filter_fields[i].df.filter_hide = 1;
  186. }
  187. }
  188. // -------------------------------------------------------------------------------------
  189. _r.ReportBuilder.prototype.run = function() {
  190. // Note: all client code is executed in datatable
  191. this.dt.run();
  192. }
  193. // Load Criteria
  194. // -------------------------------------------------------------------------------------
  195. _r.ReportBuilder.prototype.clear_criteria = function() {
  196. this.column_picker.clear();
  197. this.column_picker.set_defaults();
  198. // clear filters
  199. // -------------
  200. for(var i=0; i<this.filter_fields.length; i++) {
  201. // reset filters
  202. this.filter_fields[i].df.filter_hide = 0;
  203. this.filter_fields[i].df.ignore = 0;
  204. if(this.filter_fields[i].is_custom) {
  205. // hide+ignore customized filters
  206. this.filter_fields[i].df.filter_hide = 1;
  207. this.filter_fields[i].df.ignore = 1;
  208. }
  209. this.filter_fields[i].set_input(null);
  210. }
  211. this.set_sort_options();
  212. this.set_main_title('Report: ' + get_doctype_label(this.doctype));
  213. this.current_loaded = null;
  214. this.customized_filters = null;
  215. this.sc = null;
  216. this.has_index = 1; this.has_headings = 1;
  217. for(var i in this.fn_list) this[this.fn_list[i]] = null; // clear custom functions
  218. }
  219. // -------------------------------------------------------------------------------------
  220. _r.ReportBuilder.prototype.set_main_title = function(t, t1) {
  221. _r.rb_con.page_head.main_head.innerHTML = t;
  222. _r.rb_con.page_head.sub_head.innerHTML = (t1 ? t1 : '');
  223. set_title(t);
  224. }
  225. _r.ReportBuilder.prototype.select_column = function(dt, label, value) {
  226. if(value==null)value = 1;
  227. this.column_picker.set(dt, label, value);
  228. }
  229. // -------------------------------------------------------------------------------------
  230. _r.ReportBuilder.prototype.set_filter = function(dt, label, value) {
  231. if(this.filter_fields_dict[dt+'\1'+ label])
  232. this.filter_fields_dict[dt+'\1'+ label].set_input(value);
  233. }
  234. // -------------------------------------------------------------------------------------
  235. _r.ReportBuilder.prototype.load_criteria = function(criteria_name) {
  236. this.clear_criteria();
  237. if(!this.sc_dict[criteria_name]) {
  238. alert(criteria_name + ' could not be loaded. Please Refresh and try again');
  239. }
  240. this.sc = locals['Search Criteria'][this.sc_dict[criteria_name]];
  241. // eval the custom script
  242. var report = this;
  243. if(this.sc && this.sc.report_script) eval(this.sc.report_script);
  244. this.large_report = 0;
  245. // execute the customize_filters method from Search Criteria
  246. if(report.customize_filters) {
  247. try {
  248. report.customize_filters(this);
  249. } catch(err) {
  250. errprint('Error in "customize_filters":\n' + err);
  251. }
  252. }
  253. // refresh fiters
  254. this.report_filters.refresh();
  255. // set fields
  256. // ----------
  257. this.column_picker.clear();
  258. var cl = this.sc.columns ? this.sc.columns.split(',') : [];
  259. for(var c=0;c<cl.length;c++) {
  260. var key = cl[c].split('\1');
  261. this.select_column(key[0], key[1], 1);
  262. }
  263. // set filters
  264. // -----------
  265. try {
  266. var fl = JSON.parse(this.sc.filters);
  267. } catch(e) {
  268. eval('var fl = ' + this.sc.filters);
  269. }
  270. for(var n in fl) {
  271. if(fl[n]) {
  272. var key = n.split('\1');
  273. if(key[1]=='docstatus') { /* ? */ }
  274. this.set_filter(key[0], key[1], fl[n]);
  275. }
  276. }
  277. // refresh column picker
  278. this.set_criteria_sel(criteria_name);
  279. }
  280. // -------------------------------------------------------------------------------------
  281. // this method must be called after resetting the Search Criteria (or clearing)
  282. // to set Data table properties
  283. _r.ReportBuilder.prototype.set_criteria_sel = function(criteria_name) {
  284. // add additional columns
  285. var sc = locals['Search Criteria'][this.sc_dict[criteria_name]];
  286. if(sc && sc.add_col)
  287. var acl = sc.add_col.split('\n');
  288. else
  289. var acl = [];
  290. var new_sl = [];
  291. // update the label in datatable where the column name is specified in the query using AS
  292. for(var i=0; i<acl.length; i++) {
  293. var tmp = acl[i].split(' AS ');
  294. if(tmp[1]) {
  295. var t = eval(tmp[1]);
  296. new_sl[new_sl.length] = [t, "`"+t+"`"];
  297. }
  298. }
  299. // set sort
  300. this.set_sort_options(new_sl);
  301. if(sc && sc.sort_by) {
  302. this.dt.sort_sel.value = sc.sort_by;
  303. }
  304. if(sc && sc.sort_order) {
  305. sc.sort_order=='ASC' ? this.dt.set_asc() : this.dt.set_desc();
  306. }
  307. if(sc && sc.page_len) {
  308. this.dt.page_len_sel.inp.value = sc.page_len;
  309. }
  310. this.current_loaded = criteria_name;
  311. // load additional fields sort option
  312. this.set_main_title(criteria_name, sc.description);
  313. }
  314. //
  315. // Create the filter UI and column selection UI
  316. // -------------------------------------------------------------------------------------
  317. _r.ReportBuilder.prototype.setup_filters_and_cols = function() {
  318. // function checks where there is submit permission on the DocType or if the DocType
  319. // can be trashed
  320. function can_dt_be_submitted(dt) {
  321. if(locals.DocType && locals.DocType[dt] && locals.DocType[dt].allow_trash) return 1;
  322. var plist = getchildren('DocPerm', dt, 'permissions', 'DocType');
  323. for(var pidx in plist) {
  324. if(plist[pidx].submit) return 1;
  325. }
  326. return 0;
  327. }
  328. var me = this;
  329. var dt = me.parent_dt?me.parent_dt:me.doctype;
  330. // default filters
  331. var fl = [
  332. {'fieldtype':'Data', 'label':'ID', 'fieldname':'name', 'in_filter':1, 'parent':dt},
  333. {'fieldtype':'Data', 'label':'Owner', 'fieldname':'owner', 'in_filter':1, 'parent':dt},
  334. {'fieldtype':'Date', 'label':'Created on', 'fieldname':'creation', 'in_filter':0, 'parent':dt},
  335. {'fieldtype':'Date', 'label':'Last modified on', 'fieldname':'modified', 'in_filter':0, 'parent':dt},
  336. ];
  337. // can this be submitted?
  338. if(can_dt_be_submitted(dt)) {
  339. fl[fl.length] = {'fieldtype':'Check', 'label':'Saved', 'fieldname':'docstatus', 'search_index':1, 'in_filter':1, 'def_filter':1, 'parent':dt};
  340. fl[fl.length] = {'fieldtype':'Check', 'label':'Submitted', 'fieldname':'docstatus', 'search_index':1, 'in_filter':1, 'def_filter':1, 'parent':dt};
  341. fl[fl.length] = {'fieldtype':'Check', 'label':'Cancelled', 'fieldname':'docstatus', 'search_index':1, 'in_filter':1, 'parent':dt};
  342. }
  343. // make the datatable
  344. me.make_datatable();
  345. // Add columns and filters of parent doctype
  346. me.orig_sort_list = [];
  347. if(me.parent_dt) {
  348. me.setup_dt_filters_and_cols(fl, me.parent_dt);
  349. var fl = [];
  350. }
  351. // Add columns and filters of selected doctype
  352. me.setup_dt_filters_and_cols(fl, me.doctype);
  353. // hide primary filters blue box if there are no primary filters
  354. if(!this.has_primary_filters)
  355. $dh(this.report_filters.first_page_filter);
  356. this.column_picker.refresh();
  357. // show body
  358. $ds(me.body);
  359. }
  360. // -------------------------------------------------------------------------------------
  361. _r.ReportBuilder.prototype.add_filter = function(f) {
  362. if(this.filter_fields_dict[f.parent + '\1' + f.label]) {
  363. // exists
  364. this.filter_fields_dict[f.parent + '\1' + f.label].df = f; // reset properties
  365. } else {
  366. this.report_filters.add_field(f, f.parent, null, 1);
  367. }
  368. }
  369. // -------------------------------------------------------------------------------------
  370. // this is where the filters and columns are created for a particular doctype
  371. _r.ReportBuilder.prototype.setup_dt_filters_and_cols = function(fl, dt) {
  372. var me = this;
  373. // set section headings
  374. var lab = $a(me.filter_area,'div','filter_dt_head');
  375. lab.innerHTML = 'Filters for ' + get_doctype_label(dt);
  376. var lab = $a(me.picker_area,'div','builder_dt_head');
  377. lab.innerHTML = 'Select columns for ' + get_doctype_label(dt);
  378. // get fields
  379. var dt_fields = fields_list[dt];
  380. for(var i=0;i<dt_fields.length;i++) {
  381. fl[fl.length] = dt_fields[i];
  382. }
  383. // get "high priority" search fields
  384. // if the field is in search_field then it should be primary filter (i.e. on first page)
  385. var sf_list = locals.DocType[dt].search_fields ? locals.DocType[dt].search_fields.split(',') : [];
  386. for(var i in sf_list) sf_list[i] = strip(sf_list[i]);
  387. // make fields
  388. for(var i=0;i<fl.length;i++) {
  389. var f=fl[i];
  390. // add to filter
  391. if(f && cint(f.in_filter)) {
  392. me.report_filters.add_field(f, dt, in_list(sf_list, f.fieldname));
  393. }
  394. // add to column selector (builder)
  395. if(f && !in_list(no_value_fields, f.fieldtype) && f.fieldname != 'docstatus' && (!f.report_hide)) {
  396. me.column_picker.add_field(f);
  397. }
  398. }
  399. me.set_sort_options();
  400. }
  401. // -------------------------------------------------------------------------------------
  402. _r.ReportBuilder.prototype.set_sort_options = function(l) {
  403. var sl = this.orig_sort_list;
  404. empty_select(this.dt.sort_sel);
  405. if(l) sl = add_lists(l, this.orig_sort_list);
  406. // make new list if reqd
  407. if(!l) l = [];
  408. // no sorts, add one
  409. if(!l.length) {
  410. l.push(['ID', 'name'])
  411. }
  412. for(var i=0; i<sl.length; i++) {
  413. this.dt.add_sort_option(sl[i][0], sl[i][1]);
  414. }
  415. }
  416. // -------------------------------------------------------------------------------------
  417. _r.ReportBuilder.prototype.validate_permissions = function(onload) {
  418. this.perm = get_perm(this.parent_dt ? this.parent_dt : this.doctype);
  419. if(!this.perm[0][READ]) {
  420. this.forbidden = 1;
  421. if(user=='Guest') {
  422. msgprint('You must log in to view this page');
  423. } else {
  424. msgprint('No Read Permission');
  425. }
  426. nav_obj.show_last_open();
  427. return 0;
  428. }
  429. return 1;
  430. }
  431. // -------------------------------------------------------------------------------------
  432. _r.ReportBuilder.prototype.setup_doctype = function(onload) {
  433. // load doctype
  434. var me = this;
  435. if(!locals['DocType'][this.doctype]) {
  436. this.load_doctype_from_server(onload);
  437. } else {
  438. // find parent dt if required
  439. for(var key in locals.DocField) {
  440. var f = locals.DocField[key];
  441. if(f.fieldtype=='Table' && f.options==this.doctype)
  442. this.parent_dt = f.parent;
  443. }
  444. if(!me.validate_permissions())
  445. return;
  446. me.validate_permissions();
  447. me.make_body();
  448. me.setup_filters_and_cols();
  449. if(onload)onload(me);
  450. }
  451. }
  452. _r.ReportBuilder.prototype.load_doctype_from_server = function(onload) {
  453. var me = this;
  454. $c('webnotes.widgets.form.load.getdoctype', args = {'doctype': this.doctype, 'with_parent':1 },
  455. function(r,rt) {
  456. if(r.parent_dt)me.parent_dt = r.parent_dt;
  457. if(!me.validate_permissions())
  458. return;
  459. me.make_body();
  460. me.setup_filters_and_cols();
  461. if(onload)onload(me);
  462. }
  463. );
  464. }
  465. // -------------------------------------------------------------------------------------
  466. _r.ReportBuilder.prototype.reset_report = function() {
  467. this.clear_criteria();
  468. // show column picker if find
  469. this.mytabs.items['Select Columns'].show();
  470. this.mytabs.items['More Filters'].show();
  471. this.report_filters.refresh();
  472. this.column_picker.refresh();
  473. var dt = this.parent_dt?this.parent_dt: this.doctype;
  474. this.set_filter(dt, 'Saved', 1);
  475. this.set_filter(dt, 'Submitted', 1);
  476. this.set_filter(dt, 'Cancelled', 0);
  477. this.column_picker.set_defaults();
  478. this.dt.clear_all();
  479. this.dt.sort_sel.value = 'ID';
  480. this.dt.page_len_sel.inp.value = '50';
  481. this.dt.set_no_limit(0);
  482. this.dt.set_desc();
  483. }
  484. //
  485. // Make the SQL query
  486. // -------------------------------------------------------------------------------------
  487. _r.ReportBuilder.prototype.make_datatable = function() {
  488. var me = this;
  489. this.dt_area = $a(this.mytabs.items['Result'].body, 'div');
  490. var clear_area = $a(this.mytabs.items['Result'].body, 'div');
  491. clear_area.style.marginTop = '8px';
  492. clear_area.style.textAlign = 'right';
  493. this.clear_btn = $a($a(clear_area, 'span'), 'button');
  494. this.clear_btn.innerHTML = 'Clear Settings';
  495. this.clear_btn.onclick = function() {
  496. me.reset_report();
  497. }
  498. var d = $a(clear_area, 'span', '', {marginLeft:'16px'});
  499. d.innerHTML = '<span>Show Query: </span>';
  500. this.show_query = $a_input(d, 'checkbox');
  501. this.show_query.checked = false;
  502. this.dt = new _r.DataTable(this.dt_area, '');
  503. this.dt.finder = this;
  504. this.dt.make_query = function() {
  505. // attach report script functions
  506. var report = me;
  507. // get search criteria
  508. if(me.current_loaded && me.sc_dict[me.current_loaded]) {
  509. var sc = get_local('Search Criteria', me.sc_dict[me.current_loaded]);
  510. }
  511. if(sc) me.dt.search_criteria = sc;
  512. else me.dt.search_criteria = null;
  513. // reset no_limit
  514. //me.dt.set_no_limit(0);
  515. //load server script
  516. if(sc && sc.server_script) me.dt.server_script = sc.server_script;
  517. else me.dt.server_script = null;
  518. // load client scripts - attach all functions from ReportBuilder to Datatable
  519. // this is a bad way of doing things but since DataTable is a stable object
  520. // currently this is okay.... to change in future
  521. for(var i=0;i<me.fn_list.length;i++) {
  522. if(me[me.fn_list[i]]) me.dt[me.fn_list[i]] = me[me.fn_list[i]];
  523. else me.dt[me.fn_list[i]] = null;
  524. }
  525. var fl = []; // field list
  526. var docstatus_cl = [];
  527. var cl = []; // cond list
  528. // format table name
  529. var table_name = function(t) { return '`tab' + t + '`'; }
  530. // advanced - make list of diabled filters
  531. var dis_filters_list = [];
  532. if(sc && sc.dis_filters)
  533. var dis_filters_list = sc.dis_filters.split('\n');
  534. // make a list of selected columns from ColumnPicker in tableName.fieldName format
  535. var t = me.column_picker.get_selected();
  536. for(var i=0;i<t.length;i++) {
  537. fl.push(table_name(t[i].parent) + '.`'+t[i].fieldname+'` AS `'+t[i].parent +'.'+ t[i].fieldname+'`');
  538. }
  539. me.selected_fields = fl;
  540. // advanced - additional fields
  541. if(sc && sc.add_col) {
  542. var adv_fl = sc.add_col.split('\n');
  543. for(var i=0;i<adv_fl.length;i++) {
  544. fl[fl.length] = adv_fl[i];
  545. }
  546. }
  547. // build dictionary for filter values for server side
  548. me.dt.filter_vals = {}
  549. add_to_filter = function(k,v,is_select) {
  550. if(v==null)v='';
  551. if(!in_list(keys(me.dt.filter_vals), k)) {
  552. me.dt.filter_vals[k] = v;
  553. } else {
  554. if(is_select)
  555. me.dt.filter_vals[k] += '\n' + v;
  556. else
  557. me.dt.filter_vals[k+'1'] = v; // for date, numeric (from-to)
  558. }
  559. }
  560. // loop over the fields and construct the SQL query
  561. // ------------------------------------------------
  562. for(var i=0;i<me.filter_fields.length;i++) {
  563. var t = me.filter_fields[i];
  564. // add to "filter_values"
  565. var v = t.get_value?t.get_value():'';
  566. if(t.df.fieldtype=='Select') {
  567. if(t.input.multiple) {
  568. for(var sel_i=0;sel_i < v.length; sel_i++) {
  569. add_to_filter(t.df.fieldname, v[sel_i], 1);
  570. }
  571. // no values? atleast add key
  572. if(!v.length) add_to_filter(t.df.fieldname, "", 1);
  573. } else {
  574. add_to_filter(t.df.fieldname, v);
  575. }
  576. } else add_to_filter(t.df.fieldname, v);
  577. // if filter is not disabled
  578. if(!in_list(dis_filters_list, t.df.fieldname) && !t.df.ignore) {
  579. if(t.df.fieldname=='docstatus') {
  580. // work around for docstatus
  581. // -------------------------
  582. if(t.df.label=='Saved'){
  583. if(t.get_value()) docstatus_cl[docstatus_cl.length] = table_name(t.df.parent)+'.docstatus=0';
  584. else cl[cl.length] = table_name(t.df.parent)+'.docstatus!=0';
  585. }
  586. else if(t.df.label=='Submitted'){
  587. if(t.get_value()) docstatus_cl[docstatus_cl.length] = table_name(t.df.parent)+'.docstatus=1';
  588. else cl[cl.length] = table_name(t.df.parent)+'.docstatus!=1';
  589. }
  590. else if(t.df.label=='Cancelled'){
  591. if(t.get_value()) docstatus_cl[docstatus_cl.length] = table_name(t.df.parent)+'.docstatus=2';
  592. else cl[cl.length] = table_name(t.df.parent)+'.docstatus!=2';
  593. }
  594. } else {
  595. // normal
  596. // -------
  597. var fn = '`' + t.df.fieldname + '`';
  598. var v = t.get_value?t.get_value():'';
  599. if(v) {
  600. if(in_list(['Data','Link','Small Text','Text'],t.df.fieldtype)) {
  601. cl[cl.length] = table_name(t.df.parent) + '.' + fn + ' LIKE "' + v + '%"';
  602. } else if(t.df.fieldtype=='Select') {
  603. if(t.input.multiple) {
  604. // loop for multiple select
  605. var tmp_cl = [];
  606. for(var sel_i=0;sel_i < v.length; sel_i++) {
  607. if(v[sel_i]) {
  608. tmp_cl[tmp_cl.length] = table_name(t.df.parent) + '.' + fn + ' = "' + v[sel_i] + '"';
  609. }
  610. }
  611. // join multiple select conditions by OR
  612. if(tmp_cl.length)cl[cl.length] = '(' + tmp_cl.join(' OR ') + ')';
  613. } else {
  614. cl[cl.length] = table_name(t.df.parent) + '.' + fn + ' = "' + v + '"';
  615. }
  616. } else {
  617. var condition = '=';
  618. if(t.sql_condition) condition = t.sql_condition;
  619. cl[cl.length] = table_name(t.df.parent) + '.' + fn + condition + '"' + v + '"';
  620. }
  621. }
  622. }
  623. }
  624. }
  625. // standard filters
  626. me.dt.filter_vals.user = user;
  627. me.dt.filter_vals.user_email = user_email;
  628. me.filter_vals = me.dt.filter_vals; // in both dt and report
  629. // overloaded query - finish it here
  630. this.is_simple = 0;
  631. if(sc && sc.custom_query) {
  632. this.query = repl(sc.custom_query, me.dt.filter_vals);
  633. this.is_simple = 1;
  634. return
  635. }
  636. if(me.get_query) {
  637. // custom query method
  638. this.query = me.get_query();
  639. this.is_simple = 1;
  640. } else {
  641. // add docstatus conditions
  642. if(docstatus_cl.length)
  643. cl[cl.length] = '('+docstatus_cl.join(' OR ')+')';
  644. // advanced - additional conditions
  645. if(sc && sc.add_cond) {
  646. var adv_cl = sc.add_cond.split('\n');
  647. for(var i=0;i< adv_cl.length;i++) {
  648. cl[cl.length] = adv_cl[i];
  649. }
  650. }
  651. // atleast one field
  652. if(!fl.length) {
  653. alert('You must select atleast one column to view');
  654. this.query = '';
  655. return;
  656. }
  657. // join with parent in case of child
  658. var tn = table_name(me.doctype);
  659. if(me.parent_dt) {
  660. tn = tn + ',' + table_name(me.parent_dt);
  661. cl[cl.length] = table_name(me.doctype) + '.`parent` = ' + table_name(me.parent_dt) + '.`name`';
  662. }
  663. // advanced - additional tables
  664. if(sc && sc.add_tab) {
  665. var adv_tl = sc.add_tab.split('\n');
  666. tn = tn + ',' + adv_tl.join(',');
  667. }
  668. // make the query
  669. if(!cl.length)
  670. this.query = 'SELECT ' + fl.join(',\n') + ' FROM ' + tn
  671. else
  672. this.query = 'SELECT ' + fl.join(',') + ' FROM ' + tn + ' WHERE ' + cl.join('\n AND ');
  673. // advanced - group by
  674. if(sc && sc.group_by) {
  675. this.query += ' GROUP BY ' + sc.group_by;
  676. }
  677. // replace - in custom query if %(key)s is specified, then replace it by filter values
  678. this.query = repl(this.query, me.dt.filter_vals)
  679. }
  680. if(me.show_query.checked) {
  681. this.show_query = 1;
  682. }
  683. // report name - used as filename in export
  684. if(me.current_loaded) this.rep_name = me.current_loaded;
  685. else this.rep_name = me.doctype;
  686. }
  687. }
  688. // -------------------------------------------------------------------------------------
  689. _r.ReportBuilder.prototype.get_filter = function(dt, label) {
  690. return this.filter_fields_dict[dt + FILTER_SEP + label];
  691. }
  692. _r.ReportBuilder.prototype.set_filter_properties = function(dt, label, properties) {
  693. var f = this.filter_fields_dict[dt + FILTER_SEP + label];
  694. for(key in properties) {
  695. f.df[key]=properties[key];
  696. }
  697. }
  698. // Report Filter
  699. // ===================================================================================
  700. _r.ReportFilters = function(rb) {
  701. this.rb = rb;
  702. // filters broken into - primary - in searchfields and others
  703. this.first_page_filter = $a(rb.mytabs.items['Result'].body, 'div', 'finder_filter_area');
  704. this.filter_area = $a(rb.mytabs.items['More Filters'].body, 'div', 'finder_filter_area');
  705. // filter fields area
  706. this.filter_fields_area = $a(this.filter_area,'div');
  707. }
  708. // -------------------------------------------------------------------------------------
  709. _r.ReportFilters.prototype.refresh = function() {
  710. // called after customization
  711. var fl = this.rb.filter_fields
  712. for(var i=0; i<fl.length; i++) {
  713. var f = fl[i];
  714. // is hidden ?
  715. if(f.df.filter_hide) {
  716. $dh(f.wrapper);
  717. } else {
  718. $ds(f.wrapper);
  719. }
  720. // is bold?
  721. if(f.df.bold) {
  722. if(f.label_cell)
  723. $y(f.label_cell, {fontWeight:'bold'})
  724. } else {
  725. if(f.label_cell) $y(f.label_cell, {fontWeight:'normal'})
  726. }
  727. // set default value
  728. if(f.df['report_default'])
  729. f.set_input(f.df['report_default']);
  730. // show in first page?
  731. if(f.df.in_first_page && f.df.filter_cell) {
  732. f.df.filter_cell.parentNode.removeChild(f.df.filter_cell);
  733. this.first_page_filter.appendChild(f.df.filter_cell);
  734. this.rb.has_primary_filters = 1;
  735. $ds(this.first_page_filter);
  736. }
  737. // clear / hide all custom added filters
  738. }
  739. }
  740. // -------------------------------------------------------------------------------------
  741. _r.ReportFilters.prototype.add_date_field = function(cell, f, dt, is_custom) {
  742. var my_div = $a(cell,'div','',{});
  743. // from date
  744. var f1 = copy_dict(f);
  745. f1.label = 'From ' + f1.label;
  746. var tmp1 = this.make_field_obj(f1, dt, my_div, is_custom);
  747. tmp1.sql_condition = '>=';
  748. tmp1.bound = 'lower';
  749. // to date
  750. var f2 = copy_dict(f);
  751. f2.label = 'To ' + f2.label;
  752. var tmp2 = this.make_field_obj(f2, dt, my_div, is_custom);
  753. tmp2.sql_condition = '<=';
  754. tmp2.bound = 'upper';
  755. }
  756. // -------------------------------------------------------------------------------------
  757. _r.ReportFilters.prototype.add_numeric_field = function(cell, f, dt, is_custom) {
  758. var my_div = $a(cell,'div','',{});
  759. // from value
  760. var f1 = copy_dict(f);
  761. f1.label = f1.label + ' >=';
  762. var tmp1 = this.make_field_obj(f1, dt, my_div, is_custom);
  763. tmp1.sql_condition = '>=';
  764. tmp1.bound = 'lower';
  765. // to value
  766. var f2 = copy_dict(f);
  767. f2.label = f2.label + ' <=';
  768. var tmp2 = this.make_field_obj(f2, dt, my_div, is_custom);
  769. tmp2.sql_condition = '<=';
  770. tmp2.bound = 'upper';
  771. }
  772. // make a field object
  773. _r.ReportFilters.prototype.make_field_obj = function(f, dt, parent, is_custom) {
  774. var tmp = make_field(f, dt, parent, this.rb, false);
  775. tmp.not_in_form = 1;
  776. tmp.in_filter = 1;
  777. tmp.refresh();
  778. this.rb.filter_fields[this.rb.filter_fields.length] = tmp;
  779. this.rb.filter_fields_dict[f.parent + '\1' + f.label] = tmp;
  780. if(is_custom) tmp.is_custom = 1;
  781. return tmp;
  782. }
  783. // -------------------------------------------------------------------------------------
  784. _r.ReportFilters.prototype.add_field = function(f, dt, in_primary, is_custom) {
  785. var me = this;
  786. // insert in (parent element)
  787. if(f.in_first_page) in_primary = true;
  788. var fparent = this.filter_fields_area;
  789. if(in_primary) {
  790. fparent = this.first_page_filter;
  791. this.rb.has_primary_filters = 1;
  792. }
  793. // label
  794. // --- ability to insert
  795. if(f.on_top) {
  796. var cell = document.createElement('div');
  797. fparent.insertBefore(cell, fparent.firstChild);
  798. $y(cell,{width:'70%'});
  799. } else if(f.insert_before) {
  800. var cell = document.createElement('div');
  801. fparent.insertBefore(cell, fparent[f.df.insert_before].filter_cell);
  802. $y(cell,{width:'70%'});
  803. }
  804. else
  805. var cell = $a(fparent, 'div', '', {width:'70%'});
  806. f.filter_cell = cell;
  807. // make field
  808. if(f.fieldtype=='Date') {
  809. // date field
  810. this.add_date_field(cell, f, dt);
  811. } else if(in_list(['Currency', 'Int', 'Float'], f.fieldtype)) {
  812. // numeric
  813. this.add_numeric_field(cell, f, dt);
  814. } else if (!in_list(['Section Break', 'Column Break', 'Read Only', 'HTML', 'Table', 'Image', 'Button'], f.fieldtype)) {
  815. var tmp = this.make_field_obj(f, dt, cell, is_custom);
  816. }
  817. // add to datatable sort
  818. if(f.fieldname != 'docstatus')
  819. me.rb.orig_sort_list.push([f.label, '`tab' + f.parent + '`.`' + f.fieldname + '`']);
  820. // check saved
  821. if(f.def_filter)
  822. tmp.input.checked = true;
  823. }
  824. // Column Picker
  825. // ===================================================================================
  826. _r.ReportColumnPicker = function(rb) {
  827. this.rb = rb;
  828. this.picker_area = $a(this.rb.mytabs.items['Select Columns'].body, 'div', 'finder_picker_area');
  829. this.all_fields = [];
  830. this.sel_idx = 0;
  831. this.make_body();
  832. }
  833. // -------------------------------------------------------------------------------------
  834. _r.ReportColumnPicker.prototype.make_body = function() {
  835. var t = make_table(this.picker_area, 1, 3, '100%', ['35%','30%','35%'], {verticalAlign:'middle', textAlign:'center'});
  836. // all fields
  837. $a($td(t,0,0), 'h3', '', {marginBottom:'8px'}).innerHTML = 'Columns';
  838. this.unsel_fields = $a($td(t,0,0), 'select', '', {height:'200px', width:'100%', border:'1px solid #AAA'});
  839. this.unsel_fields.multiple = true;
  840. this.unsel_fields.onchange = function() { for(var i=0; i<this.options.length; i++) this.options[i].field.is_selected = this.options[i].selected; }
  841. // buttons
  842. var me = this;
  843. this.up_btn = $a($a($td(t,0,1), 'div'), 'button', '', {width:'70px'});
  844. this.up_btn.innerHTML = 'Up &uarr;';
  845. this.up_btn.onclick = function() { me.move_up(); }
  846. this.add_all = $a($a($td(t,0,1), 'div'), 'button', '', {width:'40px'});
  847. this.add_all.innerHTML = '&gt;&gt;';
  848. this.add_all.onclick = function() { me.move(me.unsel_fields, 'add', 1); }
  849. this.add_btn = $a($a($td(t,0,1), 'div'), 'button', '', {width:'110px'});
  850. this.add_btn.innerHTML = '<b>Add &gt;</b>';
  851. this.add_btn.onclick = function() { me.move(me.unsel_fields, 'add'); }
  852. this.remove_btn = $a($a($td(t,0,1), 'div'), 'button', '', {width:'110px'});
  853. this.remove_btn.innerHTML = '<b>&lt; Remove</b>';
  854. this.remove_btn.onclick = function() { me.move(me.sel_fields, 'remove'); }
  855. this.remove_all = $a($a($td(t,0,1), 'div'), 'button', '', {width:'40px'});
  856. this.remove_all.innerHTML = '&lt;&lt;';
  857. this.remove_all.onclick = function() { me.move(me.sel_fields, 'remove', 1); }
  858. this.dn_btn = $a($a($td(t,0,1), 'div'), 'button', '', {width:'70px'});
  859. this.dn_btn.innerHTML = 'Down &darr;';
  860. this.dn_btn.onclick = function() { me.move_down(); }
  861. // multiple fields
  862. $a($td(t,0,2), 'h3', '', {marginBottom:'8px'}).innerHTML = 'Selected Columns';
  863. this.sel_fields = $a($td(t,0,2), 'select', '', {height:'200px', width:'100%', border:'1px solid #AAA'});
  864. this.sel_fields.multiple = true;
  865. this.sel_fields.onchange = function() { for(var i=0; i<this.options.length; i++) this.options[i].field.is_selected = this.options[i].selected; }
  866. }
  867. // -------------------------------------------------------------------------------------
  868. _r.ReportColumnPicker.prototype.get_by_sel_idx = function(s, idx) {
  869. for(var j=0;j<s.options.length; j++) {
  870. if(s.options[j].field.sel_idx == idx)
  871. return s.options[j].field;
  872. }
  873. return {} // nothing
  874. }
  875. _r.ReportColumnPicker.prototype.move_up = function() {
  876. var s = this.sel_fields;
  877. for(var i=1;i<s.options.length; i++ ) {
  878. if(s.options[i].selected) {
  879. s.options[i].field.sel_idx--;
  880. this.get_by_sel_idx(s, i-1).sel_idx++;
  881. }
  882. }
  883. this.refresh();
  884. }
  885. _r.ReportColumnPicker.prototype.move_down = function() {
  886. var s = this.sel_fields;
  887. if(s.options.length<=1) return;
  888. for(var i=s.options.length-2;i>=0; i-- ) {
  889. if(s.options[i].selected) {
  890. this.get_by_sel_idx(s, i+1).sel_idx--;
  891. s.options[i].field.sel_idx++;
  892. }
  893. }
  894. this.refresh();
  895. }
  896. // -------------------------------------------------------------------------------------
  897. _r.ReportColumnPicker.prototype.move = function(s, type, all) {
  898. for(var i=0;i<s.options.length; i++ ) {
  899. if(s.options[i].selected || all) {
  900. if(type=='add') {
  901. s.options[i].field.selected = 1;
  902. s.options[i].field.sel_idx = this.sel_idx;
  903. this.sel_idx++;
  904. } else {
  905. s.options[i].field.selected = 0;
  906. s.options[i].field.sel_idx = 0;
  907. this.sel_idx--;
  908. }
  909. }
  910. }
  911. this.refresh();
  912. }
  913. // -------------------------------------------------------------------------------------
  914. _r.ReportColumnPicker.prototype.refresh = function() {
  915. // separate
  916. var ul = []; var sl=[];
  917. for(var i=0; i<this.all_fields.length; i++) {
  918. var o = this.all_fields[i];
  919. if(o.selected) {
  920. sl.push(o);
  921. // enable sort option
  922. if(this.rb.dt) this.rb.dt.set_sort_option_disabled(o.df.label, 0);
  923. } else {
  924. ul.push(o);
  925. // disable sort option
  926. if(this.rb.dt) this.rb.dt.set_sort_option_disabled(o.df.label, 1);
  927. }
  928. }
  929. // sort by field idx
  930. ul.sort(function(a,b){return (cint(a.df.idx)-cint(b.df.idx))});
  931. // sort by order in which they were selected
  932. sl.sort(function(a,b){return (cint(a.sel_idx)-cint(b.sel_idx))})
  933. // re-number selected
  934. for(var i=0; i<sl.length; i++) { sl[i].sel_idx = i; }
  935. // add options
  936. this.set_options(this.unsel_fields, ul);
  937. this.set_options(this.sel_fields, sl);
  938. }
  939. // -------------------------------------------------------------------------------------
  940. _r.ReportColumnPicker.prototype.set_options = function(s, l) {
  941. empty_select(s);
  942. for(var i=0; i<l.length; i++) {
  943. var v = l[i].df.parent + '.' + l[i].df.label;
  944. var v_label = get_doctype_label(l[i].df.parent) + '.' + l[i].df.label;
  945. var o = new Option (v_label, v, false, false);
  946. o.field = l[i];
  947. if(o.field.is_selected) o.selected = 1;
  948. s.options[s.options.length] = o;
  949. }
  950. }
  951. // -------------------------------------------------------------------------------------
  952. _r.ReportColumnPicker.prototype.clear = function() {
  953. this.sel_idx = 0;
  954. for(var i=0; i<this.all_fields.length; i++) {
  955. this.all_fields[i].selected = 0;
  956. }
  957. this.refresh();
  958. }
  959. // -------------------------------------------------------------------------------------
  960. _r.ReportColumnPicker.prototype.get_selected = function() {
  961. var sl = [];
  962. for(var i=0; i<this.all_fields.length; i++) {
  963. var o = this.all_fields[i];
  964. if(o.selected) {
  965. sl[sl.length] = o.df;
  966. o.df.sel_idx = o.sel_idx;
  967. }
  968. }
  969. return sl.sort(function(a,b){return (cint(a.sel_idx)-cint(b.sel_idx))});
  970. }
  971. // -------------------------------------------------------------------------------------
  972. _r.ReportColumnPicker.prototype.set_defaults = function() {
  973. for(var i=0; i<this.all_fields.length; i++) {
  974. if(this.all_fields[i].selected_by_default)
  975. this.all_fields[i].selected = 1;
  976. }
  977. }
  978. // -------------------------------------------------------------------------------------
  979. _r.ReportColumnPicker.prototype.add_field = function(f) {
  980. // column picker
  981. if(!f.label) return;
  982. var by_default = (f.in_filter) ? 1 : 0;
  983. this.all_fields.push({
  984. selected:by_default
  985. ,df:f
  986. ,sel_idx: (by_default ? this.sel_idx : 0)
  987. ,selected_by_default : by_default
  988. });
  989. this.sel_idx += by_default;
  990. }
  991. // -------------------------------------------------------------------------------------
  992. _r.ReportColumnPicker.prototype.set = function(dt, label, selected) {
  993. for(var i=0; i<this.all_fields.length; i++) {
  994. if(this.all_fields[i].df.parent == dt && this.all_fields[i].df.label==label) {
  995. this.all_fields[i].selected = selected;
  996. this.all_fields[i].sel_idx = this.sel_idx;
  997. this.sel_idx += cint(selected);
  998. this.refresh();
  999. return;
  1000. }
  1001. }
  1002. }