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.
 
 
 
 
 
 

1253 lines
35 KiB

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