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.

reportview.js 14 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. // Copyright 2013 Web Notes Technologies Pvt Ltd
  2. // License: MIT. See license.txt
  3. wn.views.ReportViewPage = Class.extend({
  4. init: function(doctype, docname) {
  5. if(!wn.model.can_get_report(doctype)) {
  6. wn.set_route("403");
  7. return;
  8. };
  9. this.doctype = doctype;
  10. this.docname = docname;
  11. this.page_name = wn.get_route_str();
  12. this.make_page();
  13. var me = this;
  14. wn.model.with_doctype(this.doctype, function() {
  15. me.make_report_view();
  16. if(me.docname) {
  17. wn.model.with_doc('Report', me.docname, function(r) {
  18. me.page.reportview.set_columns_and_filters(
  19. JSON.parse(wn.model.get("Report", me.docname)[0].json));
  20. me.page.reportview.run();
  21. });
  22. } else {
  23. me.page.reportview.run();
  24. }
  25. });
  26. },
  27. make_page: function() {
  28. this.page = wn.container.add_page(this.page_name);
  29. wn.ui.make_app_page({parent:this.page,
  30. single_column:true});
  31. wn.container.change_to(this.page_name);
  32. },
  33. make_report_view: function() {
  34. var module = locals.DocType[this.doctype].module;
  35. this.page.appframe.set_title(this.doctype);
  36. this.page.appframe.add_home_breadcrumb()
  37. this.page.appframe.add_module_breadcrumb(module)
  38. this.page.appframe.add_breadcrumb("icon-table");
  39. this.page.reportview = new wn.views.ReportView({
  40. doctype: this.doctype,
  41. docname: this.docname,
  42. page: this.page,
  43. wrapper: $(this.page).find(".layout-main")
  44. });
  45. }
  46. })
  47. wn.views.ReportView = wn.ui.Listing.extend({
  48. init: function(opts) {
  49. var me = this;
  50. $(this.page).find('.layout-main').html(wn._('Loading Report')+'...');
  51. $(this.page).find('.layout-main').empty();
  52. $.extend(this, opts);
  53. this.can_delete = wn.model.can_delete(me.doctype);
  54. this.tab_name = '`tab'+this.doctype+'`';
  55. this.setup();
  56. },
  57. set_init_columns: function() {
  58. // pre-select mandatory columns
  59. var columns = [['name'], ['owner']];
  60. $.each(wn.meta.docfield_list[this.doctype], function(i, df) {
  61. if(df.in_filter && df.fieldname!='naming_series' && df.fieldtype!='Table') {
  62. columns.push([df.fieldname]);
  63. }
  64. });
  65. this.columns = columns;
  66. },
  67. setup: function() {
  68. var me = this;
  69. this.page_title = wn._('Report')+ ': ' + wn._(this.docname ? (this.doctype + ' - ' + this.docname) : this.doctype);
  70. this.page.appframe.set_title(this.page_title)
  71. this.make({
  72. appframe: this.page.appframe,
  73. method: 'webnotes.widgets.reportview.get',
  74. get_args: this.get_args,
  75. parent: $(this.page).find('.layout-main'),
  76. start: 0,
  77. page_length: 20,
  78. show_filters: true,
  79. new_doctype: this.doctype,
  80. allow_delete: true,
  81. });
  82. this.make_delete();
  83. this.make_column_picker();
  84. this.make_sorter();
  85. this.make_export();
  86. this.set_init_columns();
  87. this.make_save();
  88. this.set_tag_and_status_filter();
  89. },
  90. set_init_columns: function() {
  91. // pre-select mandatory columns
  92. var columns = wn.user.get_default("_list_settings:" + this.doctype);
  93. if(!columns) {
  94. var columns = [['name', this.doctype],];
  95. $.each(wn.meta.docfield_list[this.doctype], function(i, df) {
  96. if(df.in_filter && df.fieldname!='naming_series'
  97. && !in_list(no_value_fields, df.fieldname)) {
  98. columns.push([df.fieldname, df.parent]);
  99. }
  100. });
  101. }
  102. this.columns = columns;
  103. },
  104. // preset columns and filters from saved info
  105. set_columns_and_filters: function(opts) {
  106. var me = this;
  107. if(opts.columns) this.columns = opts.columns;
  108. if(opts.filters) $.each(opts.filters, function(i, f) {
  109. // fieldname, condition, value
  110. me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
  111. });
  112. // first sort
  113. if(opts.sort_by) this.sort_by_select.val(opts.sort_by);
  114. if(opts.sort_order) this.sort_order_select.val(opts.sort_order);
  115. // second sort
  116. if(opts.sort_by_next) this.sort_by_next_select.val(opts.sort_by_next);
  117. if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next);
  118. },
  119. // build args for query
  120. get_args: function() {
  121. var me = this;
  122. return {
  123. doctype: this.doctype,
  124. fields: $.map(this.columns, function(v) { return me.get_full_column_name(v) }),
  125. order_by: this.get_order_by(),
  126. filters: this.filter_list.get_filters(),
  127. docstatus: ['0','1','2']
  128. }
  129. },
  130. get_order_by: function() {
  131. // first
  132. var order_by = this.get_selected_table_and_column(this.sort_by_select)
  133. + ' ' + this.sort_order_select.val();
  134. // second
  135. if(this.sort_by_next_select.val()) {
  136. order_by += ', ' + this.get_selected_table_and_column(this.sort_by_next_select)
  137. + ' ' + this.sort_order_next_select.val();
  138. }
  139. return order_by;
  140. },
  141. get_selected_table_and_column: function($select) {
  142. return this.get_full_column_name([$select.find('option:selected').attr('fieldname'),
  143. $select.find('option:selected').attr('table')])
  144. },
  145. // get table_name.column_name
  146. get_full_column_name: function(v) {
  147. return (v[1] ? ('`tab' + v[1] + '`') : this.tab_name) + '.' + v[0];
  148. },
  149. // build columns for slickgrid
  150. build_columns: function() {
  151. var me = this;
  152. return $.map(this.columns, function(c) {
  153. var docfield = wn.meta.docfield_map[c[1] || me.doctype][c[0]];
  154. if(!docfield) {
  155. var docfield = wn.model.get_std_field(c[0]);
  156. if(c[0]=="name") {
  157. docfield.options = me.doctype;
  158. }
  159. }
  160. coldef = {
  161. id: c[0],
  162. field: c[0],
  163. docfield: docfield,
  164. name: wn._(docfield ? docfield.label : toTitle(c[0])),
  165. width: (docfield ? cint(docfield.width) : 120) || 120,
  166. formatter: function(row, cell, value, columnDef, dataContext) {
  167. var docfield = columnDef.docfield;
  168. return wn.format(value, docfield);
  169. }
  170. }
  171. return coldef;
  172. });
  173. },
  174. // render data
  175. render_list: function() {
  176. var me = this;
  177. var columns = this.get_columns();
  178. // add sr in data
  179. $.each(this.data, function(i, v) {
  180. // add index
  181. v._idx = i+1;
  182. v.id = v._idx;
  183. });
  184. var options = {
  185. enableCellNavigation: true,
  186. enableColumnReorder: false,
  187. };
  188. if(this.slickgrid_options) {
  189. $.extend(options, this.slickgrid_options);
  190. }
  191. this.col_defs = columns;
  192. this.dataView = new Slick.Data.DataView();
  193. this.set_data(this.data);
  194. var grid_wrapper = this.$w.find('.result-list');
  195. // set height if not auto
  196. if(!options.autoHeight)
  197. grid_wrapper.css('height', '500px');
  198. this.grid = new Slick.Grid(grid_wrapper
  199. .css('border', '1px solid #ccc')
  200. .css('border-top', '0px')
  201. .get(0), this.dataView,
  202. columns, options);
  203. wn.slickgrid_tools.add_property_setter_on_resize(this.grid);
  204. if(this.start!=0 && !options.autoHeight) {
  205. this.grid.scrollRowIntoView(this.data.length-1);
  206. }
  207. },
  208. set_data: function() {
  209. this.dataView.beginUpdate();
  210. this.dataView.setItems(this.data);
  211. this.dataView.endUpdate();
  212. },
  213. get_columns: function() {
  214. var std_columns = [{id:'_idx', field:'_idx', name: 'Sr.', width: 40, maxWidth: 40}];
  215. if(this.can_delete) {
  216. std_columns = std_columns.concat([{
  217. id:'_check', field:'_check', name: "", width: 30, maxWidth: 30,
  218. formatter: function(row, cell, value, columnDef, dataContext) {
  219. return repl("<input type='checkbox' \
  220. data-row='%(row)s' %(checked)s>", {
  221. row: row,
  222. checked: (dataContext._checked ? "checked" : "")
  223. });
  224. }
  225. }])
  226. }
  227. return std_columns.concat(this.build_columns());
  228. },
  229. // setup column picker
  230. make_column_picker: function() {
  231. var me = this;
  232. this.column_picker = new wn.ui.ColumnPicker(this);
  233. this.page.appframe.add_button(wn._('Pick Columns'), function() {
  234. me.column_picker.show(me.columns);
  235. }, 'icon-th-list');
  236. },
  237. set_tag_and_status_filter: function() {
  238. var me = this;
  239. this.$w.find('.result-list').on("click", ".label-info", function() {
  240. if($(this).attr("data-label")) {
  241. me.set_filter("_user_tags", $(this).attr("data-label"));
  242. }
  243. });
  244. this.$w.find('.result-list').on("click", "[data-workflow-state]", function() {
  245. if($(this).attr("data-workflow-state")) {
  246. me.set_filter(me.state_fieldname,
  247. $(this).attr("data-workflow-state"));
  248. }
  249. });
  250. },
  251. // setup sorter
  252. make_sorter: function() {
  253. var me = this;
  254. this.sort_dialog = new wn.ui.Dialog({title:'Sorting Preferences'});
  255. $(this.sort_dialog.body).html('<p class="help">Sort By</p>\
  256. <div class="sort-column"></div>\
  257. <div><select class="sort-order" style="margin-top: 10px; width: 60%;">\
  258. <option value="asc">'+wn._('Ascending')+'</option>\
  259. <option value="desc">'+wn._('Descending')+'</option>\
  260. </select></div>\
  261. <hr><p class="help">'+wn._('Then By (optional)')+'</p>\
  262. <div class="sort-column-1"></div>\
  263. <div><select class="sort-order-1" style="margin-top: 10px; width: 60%;">\
  264. <option value="asc">'+wn._('Ascending')+'</option>\
  265. <option value="desc">'+wn._('Descending')+'</option>\
  266. </select></div><hr>\
  267. <div><button class="btn btn-info">'+wn._('Update')+'</div>');
  268. // first
  269. this.sort_by_select = new wn.ui.FieldSelect($(this.sort_dialog.body).find('.sort-column'),
  270. this.doctype).$select;
  271. this.sort_by_select.css('width', '60%');
  272. this.sort_order_select = $(this.sort_dialog.body).find('.sort-order');
  273. // second
  274. this.sort_by_next_select = new wn.ui.FieldSelect($(this.sort_dialog.body).find('.sort-column-1'),
  275. this.doctype, null, true).$select;
  276. this.sort_by_next_select.css('width', '60%');
  277. this.sort_order_next_select = $(this.sort_dialog.body).find('.sort-order-1');
  278. // initial values
  279. this.sort_by_select.val('modified');
  280. this.sort_order_select.val('desc');
  281. this.sort_by_next_select.val('');
  282. this.sort_order_next_select.val('desc');
  283. // button actions
  284. this.page.appframe.add_button(wn._('Sort By'), function() {
  285. me.sort_dialog.show();
  286. }, 'icon-arrow-down');
  287. $(this.sort_dialog.body).find('.btn-info').click(function() {
  288. me.sort_dialog.hide();
  289. me.run();
  290. });
  291. },
  292. // setup export
  293. make_export: function() {
  294. var me = this;
  295. var export_btn = this.page.appframe.add_button(wn._('Export'), function() {
  296. var args = me.get_args();
  297. args.cmd = 'webnotes.widgets.reportview.export_query'
  298. open_url_post(wn.request.url, args);
  299. }, 'icon-download-alt');
  300. wn.utils.disable_export_btn(export_btn);
  301. },
  302. // save
  303. make_save: function() {
  304. var me = this;
  305. if(wn.user.is_report_manager()) {
  306. this.page.appframe.add_button(wn._('Save'), function() {
  307. // name
  308. if(me.docname) {
  309. var name = me.docname
  310. } else {
  311. var name = prompt(wn._('Select Report Name'));
  312. if(!name) {
  313. return;
  314. }
  315. }
  316. // callback
  317. wn.call({
  318. method: 'webnotes.widgets.reportview.save_report',
  319. args: {
  320. name: name,
  321. doctype: me.doctype,
  322. json: JSON.stringify({
  323. filters: me.filter_list.get_filters(),
  324. columns: me.columns,
  325. sort_by: me.sort_by_select.val(),
  326. sort_order: me.sort_order_select.val(),
  327. sort_by_next: me.sort_by_next_select.val(),
  328. sort_order_next: me.sort_order_next_select.val()
  329. })
  330. },
  331. callback: function(r) {
  332. if(r.exc) {
  333. msgprint(wn._("Report was not saved (there were errors)"));
  334. return;
  335. }
  336. if(r.message != me.docname)
  337. wn.set_route('Report2', me.doctype, r.message);
  338. }
  339. });
  340. }, 'icon-upload');
  341. }
  342. },
  343. make_delete: function() {
  344. var me = this;
  345. if(this.can_delete) {
  346. $(this.page).on("click", "input[type='checkbox'][data-row]", function() {
  347. me.data[$(this).attr("data-row")]._checked
  348. = this.checked ? true : false;
  349. });
  350. this.page.appframe.add_button(wn._("Delete"), function() {
  351. var delete_list = []
  352. $.each(me.data, function(i, d) {
  353. if(d._checked) {
  354. if(d.name)
  355. delete_list.push(d.name);
  356. }
  357. });
  358. if(!delete_list.length)
  359. return;
  360. if(wn.confirm(wn._("This is PERMANENT action and you cannot undo. Continue?"),
  361. function() {
  362. wn.call({
  363. method: 'webnotes.widgets.reportview.delete_items',
  364. args: {
  365. items: delete_list,
  366. doctype: me.doctype
  367. },
  368. callback: function() {
  369. me.refresh();
  370. }
  371. });
  372. }));
  373. }, 'icon-remove');
  374. }
  375. },
  376. });
  377. wn.ui.ColumnPicker = Class.extend({
  378. init: function(list) {
  379. this.list = list;
  380. this.doctype = list.doctype;
  381. this.selects = {};
  382. },
  383. show: function(columns) {
  384. wn.require('lib/js/lib/jquery/jquery.ui.interactions.min.js');
  385. var me = this;
  386. if(!this.dialog) {
  387. this.dialog = new wn.ui.Dialog({
  388. title: wn._("Pick Columns"),
  389. width: '400'
  390. });
  391. }
  392. $(this.dialog.body).html('<div class="help">'+wn._("Drag to sort columns")+'</div>\
  393. <div class="column-list"></div>\
  394. <div><button class="btn btn-add"><i class="icon-plus"></i>\
  395. '+wn._("Add Column")+'</button></div>\
  396. <hr>\
  397. <div><button class="btn btn-info">'+wn._("Update")+'</div>');
  398. // show existing
  399. $.each(columns, function(i, c) {
  400. me.add_column(c);
  401. });
  402. $(this.dialog.body).find('.column-list').sortable();
  403. // add column
  404. $(this.dialog.body).find('.btn-add').click(function() {
  405. me.add_column(['name']);
  406. });
  407. // update
  408. $(this.dialog.body).find('.btn-info').click(function() {
  409. me.dialog.hide();
  410. // selected columns as list of [column_name, table_name]
  411. var columns = [];
  412. $(me.dialog.body).find('select').each(function() {
  413. var $selected = $(this).find('option:selected');
  414. columns.push([$selected.attr('fieldname'),
  415. $selected.attr('table')]);
  416. });
  417. wn.user.set_default("_list_settings:" + me.doctype, columns);
  418. me.list.columns = columns;
  419. me.list.run();
  420. });
  421. this.dialog.show();
  422. },
  423. add_column: function(c) {
  424. var w = $('<div style="padding: 5px; background-color: #eee; \
  425. width: 90%; margin-bottom: 10px; border-radius: 3px; cursor: move;">\
  426. <img src="lib/images/ui/drag-handle.png" style="margin-right: 10px;">\
  427. <a class="close" style="margin-top: 5px;">&times</a>\
  428. </div>')
  429. .appendTo($(this.dialog.body).find('.column-list'));
  430. var fieldselect = new wn.ui.FieldSelect(w, this.doctype);
  431. fieldselect.$select
  432. .css({width: '70%', 'margin-top':'5px'})
  433. .val((c[1] || this.doctype) + "." + c[0]);
  434. w.find('.close').click(function() {
  435. $(this).parent().remove();
  436. });
  437. }
  438. });