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