Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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