Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

921 Zeilen
25 KiB

  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. wn.provide("wn.report_dump");
  4. $.extend(wn.report_dump, {
  5. data: {},
  6. last_modified: {},
  7. with_data: function(doctypes, callback, progress_bar) {
  8. var pre_loaded = keys(wn.report_dump.last_modified);
  9. return wn.call({
  10. method: "webnotes.widgets.report_dump.get_data",
  11. type: "GET",
  12. args: {
  13. doctypes: doctypes,
  14. last_modified: wn.report_dump.last_modified
  15. },
  16. callback: function(r) {
  17. // creating map of data from a list
  18. $.each(r.message, function(doctype, doctype_data) {
  19. wn.report_dump.set_data(doctype, doctype_data);
  20. });
  21. // reverse map names
  22. $.each(r.message, function(doctype, doctype_data) {
  23. // only if not pre-loaded
  24. if(!in_list(pre_loaded, doctype)) {
  25. if(doctype_data.links) {
  26. $.each(wn.report_dump.data[doctype], function(row_idx, row) {
  27. $.each(doctype_data.links, function(link_key, link) {
  28. if(wn.report_dump.data[link[0]][row[link_key]]) {
  29. row[link_key] = wn.report_dump.data[link[0]][row[link_key]][link[1]];
  30. } else {
  31. row[link_key] = null;
  32. }
  33. })
  34. })
  35. }
  36. }
  37. });
  38. callback();
  39. },
  40. progress_bar: progress_bar
  41. })
  42. },
  43. set_data: function(doctype, doctype_data) {
  44. var data = [];
  45. var replace_dict = {};
  46. var make_row = function(d) {
  47. var row = {};
  48. $.each(doctype_data.columns, function(idx, col) {
  49. row[col] = d[idx];
  50. });
  51. row.id = row.name;
  52. row.doctype = doctype;
  53. return row;
  54. }
  55. if(wn.report_dump.last_modified[doctype]) {
  56. // partial loading, make a name dict
  57. $.each(doctype_data.data, function(i, d) {
  58. var row = make_row(d);
  59. replace_dict[row.name] = row;
  60. });
  61. // replace old data
  62. $.each(wn.report_dump.data[doctype], function(i, d) {
  63. if(replace_dict[d.name]) {
  64. data.push(replace_dict[d.name]);
  65. delete replace_dict[d.name];
  66. } else if(doctype_data.modified_names.indexOf(d.name)!==-1) {
  67. // if modified but not in replace_dict, then assume it as cancelled
  68. // don't push in data
  69. } else {
  70. data.push(d);
  71. }
  72. });
  73. // add new records
  74. $.each(replace_dict, function(name, d) {
  75. data.push(d);
  76. })
  77. } else {
  78. // first loading
  79. $.each(doctype_data.data, function(i, d) {
  80. data.push(make_row(d));
  81. });
  82. }
  83. wn.report_dump.last_modified[doctype] = doctype_data.last_modified;
  84. wn.report_dump.data[doctype] = data;
  85. }
  86. });
  87. wn.provide("wn.views");
  88. wn.views.GridReport = Class.extend({
  89. init: function(opts) {
  90. wn.require("assets/js/slickgrid.min.js");
  91. this.filter_inputs = {};
  92. this.preset_checks = [];
  93. this.tree_grid = {show: false};
  94. var me = this;
  95. $.extend(this, opts);
  96. this.wrapper = $('<div>').appendTo(this.parent);
  97. if(this.filters) {
  98. this.make_filters();
  99. }
  100. this.make_waiting();
  101. this.get_data_and_refresh();
  102. },
  103. bind_show: function() {
  104. // bind show event to reset cur_report_grid
  105. // and refresh filters from url
  106. // this must be called after init
  107. // because "wn.container.page" will only be set
  108. // once "load" event is over.
  109. var me = this;
  110. $(this.page).bind('show', function() {
  111. // reapply filters on show
  112. wn.cur_grid_report = me;
  113. me.get_data_and_refresh();
  114. });
  115. },
  116. get_data_and_refresh: function() {
  117. var me = this;
  118. this.get_data(function() {
  119. me.apply_filters_from_route();
  120. me.refresh();
  121. });
  122. },
  123. get_data: function(callback) {
  124. var me = this;
  125. var progress_bar = null;
  126. if(!this.setup_filters_done)
  127. progress_bar = this.wrapper.find(".progress .progress-bar");
  128. wn.report_dump.with_data(this.doctypes, function() {
  129. if(!me.setup_filters_done) {
  130. me.setup_filters();
  131. me.setup_filters_done = true;
  132. }
  133. callback();
  134. }, progress_bar);
  135. },
  136. setup_filters: function() {
  137. var me = this;
  138. $.each(me.filter_inputs, function(i, v) {
  139. var opts = v.get(0).opts;
  140. if(opts.fieldtype == "Select" && inList(me.doctypes, opts.link)) {
  141. $(v).add_options($.map(wn.report_dump.data[opts.link],
  142. function(d) { return d.name; }));
  143. } else if(opts.fieldtype == "Link" && inList(me.doctypes, opts.link)) {
  144. opts.list = $.map(wn.report_dump.data[opts.link],
  145. function(d) { return d.name; });
  146. me.set_autocomplete(v, opts.list);
  147. }
  148. });
  149. // refresh
  150. this.filter_inputs.refresh && this.filter_inputs.refresh.click(function() {
  151. me.get_data(function() {
  152. me.refresh();
  153. });
  154. });
  155. // reset filters
  156. this.filter_inputs.reset_filters && this.filter_inputs.reset_filters.click(function() {
  157. me.init_filter_values();
  158. me.refresh();
  159. });
  160. // range
  161. this.filter_inputs.range && this.filter_inputs.range.on("change", function() {
  162. me.refresh();
  163. });
  164. // plot check
  165. if(this.setup_plot_check)
  166. this.setup_plot_check();
  167. },
  168. set_filter: function(key, value) {
  169. var filters = this.filter_inputs[key];
  170. if(filters) {
  171. var opts = filters.get(0).opts;
  172. if(opts.fieldtype === "Check") {
  173. filters.prop("checked", cint(value) ? true : false);
  174. } if(opts.fieldtype=="Date") {
  175. filters.val(wn.datetime.str_to_user(value));
  176. } else {
  177. filters.val(value);
  178. }
  179. } else {
  180. msgprint("Invalid Filter: " + key)
  181. }
  182. },
  183. set_autocomplete: function($filter, list) {
  184. var me = this;
  185. $filter.autocomplete({
  186. source: list,
  187. select: function(event, ui) {
  188. $filter.val(ui.item.value);
  189. me.refresh();
  190. }
  191. });
  192. },
  193. init_filter_values: function() {
  194. var me = this;
  195. $.each(this.filter_inputs, function(key, filter) {
  196. var opts = filter.get(0).opts;
  197. if(sys_defaults[key]) {
  198. filter.val(sys_defaults[key]);
  199. } else if(opts.fieldtype=='Select') {
  200. filter.get(0).selectedIndex = 0;
  201. } else if(opts.fieldtype=='Data') {
  202. filter.val("");
  203. } else if(opts.fieldtype=="Link") {
  204. filter.val("");
  205. }
  206. });
  207. this.set_default_values();
  208. },
  209. set_default_values: function() {
  210. var values = {
  211. from_date: dateutil.str_to_user(sys_defaults.year_start_date),
  212. to_date: dateutil.str_to_user(sys_defaults.year_end_date)
  213. }
  214. var me = this;
  215. $.each(values, function(i, v) {
  216. if(me.filter_inputs[i] && !me.filter_inputs[i].val())
  217. me.filter_inputs[i].val(v);
  218. })
  219. },
  220. make_filters: function() {
  221. var me = this;
  222. $.each(this.filters, function(i, v) {
  223. v.fieldname = v.fieldname || v.label.replace(/ /g, '_').toLowerCase();
  224. var input = null;
  225. if(v.fieldtype=='Select') {
  226. input = me.appframe.add_select(v.label, v.options || [v.default_value]);
  227. } else if(v.fieldtype=="Link") {
  228. input = me.appframe.add_data(v.label);
  229. input.autocomplete({
  230. source: v.list || [],
  231. });
  232. } else if(v.fieldtype=='Button') {
  233. input = me.appframe.add_primary_action(v.label, null, v.icon);
  234. } else if(v.fieldtype=='Date') {
  235. input = me.appframe.add_date(v.label);
  236. } else if(v.fieldtype=='Label') {
  237. input = me.appframe.add_label(v.label);
  238. } else if(v.fieldtype=='Data') {
  239. input = me.appframe.add_data(v.label);
  240. } else if(v.fieldtype=='Check') {
  241. input = me.appframe.add_check(v.label);
  242. }
  243. if(input) {
  244. input && (input.get(0).opts = v);
  245. if(v.cssClass) {
  246. input.addClass(v.cssClass);
  247. }
  248. input.keypress(function(e) {
  249. if(e.which==13) {
  250. me.refresh();
  251. }
  252. })
  253. }
  254. me.filter_inputs[v.fieldname] = input;
  255. });
  256. },
  257. make_waiting: function() {
  258. this.waiting = wn.messages.waiting(this.wrapper, wn._("Loading Report")+"...", '10');
  259. },
  260. load_filter_values: function() {
  261. var me = this;
  262. $.each(this.filter_inputs, function(i, f) {
  263. var opts = f.get(0).opts;
  264. if(opts.fieldtype=='Check') {
  265. me[opts.fieldname] = f.prop('checked') ? 1 : 0;
  266. } else if(opts.fieldtype!='Button') {
  267. me[opts.fieldname] = f.val();
  268. if(opts.fieldtype=="Date") {
  269. me[opts.fieldname] = dateutil.user_to_str(me[opts.fieldname]);
  270. } else if (opts.fieldtype == "Select") {
  271. me[opts.fieldname+'_default'] = opts.default_value;
  272. }
  273. }
  274. });
  275. if(this.filter_inputs.from_date && this.filter_inputs.to_date && (this.to_date < this.from_date)) {
  276. msgprint(wn._("From Date must be before To Date"));
  277. return;
  278. }
  279. },
  280. make_name_map: function(data, key) {
  281. var map = {};
  282. key = key || "name";
  283. $.each(data, function(i, v) {
  284. map[v[key]] = v;
  285. })
  286. return map;
  287. },
  288. reset_item_values: function(item) {
  289. var me = this;
  290. $.each(this.columns, function(i, col) {
  291. if (col.formatter==me.currency_formatter) {
  292. item[col.id] = 0.0;
  293. }
  294. });
  295. },
  296. round_item_values: function(item) {
  297. var me = this;
  298. $.each(this.columns, function(i, col) {
  299. if (col.formatter==me.currency_formatter) {
  300. item[col.id] = flt(item[col.id], wn.defaults.get_default("float_precision") || 3);
  301. }
  302. });
  303. },
  304. round_off_data: function() {
  305. var me = this;
  306. $.each(this.data, function(i, d) {
  307. me.round_item_values(d);
  308. });
  309. },
  310. refresh: function() {
  311. this.waiting.toggle(false);
  312. if(!this.grid_wrapper)
  313. this.make();
  314. this.show_zero = $('.show-zero input:checked').length;
  315. this.load_filter_values();
  316. this.setup_columns();
  317. this.setup_dataview_columns();
  318. this.apply_link_formatters();
  319. this.prepare_data();
  320. this.round_off_data();
  321. this.prepare_data_view();
  322. // plot might need prepared data
  323. this.wrapper.find(".processing").toggle(true);
  324. this.wrapper.find(".processing").delay(2000).fadeOut(300);
  325. this.render();
  326. this.render_plot && this.render_plot();
  327. },
  328. setup_dataview_columns: function() {
  329. this.dataview_columns = $.map(this.columns, function(col) {
  330. return !col.hidden ? col : null;
  331. });
  332. },
  333. make: function() {
  334. var me = this;
  335. // plot wrapper
  336. this.plot_area = $('<div class="plot" style="margin-bottom: 15px; display: none; \
  337. height: 300px; width: 100%;"></div>').appendTo(this.wrapper);
  338. // print / export
  339. $('<div style="text-align: right;"> \
  340. <div class="processing" style="background-color: #fec; display: none; \
  341. float: left; margin: 2px">Updated! </div> \
  342. <a href="#" class="grid-report-export"> \
  343. <i class="icon icon-download-alt"></i> Export</a> \
  344. </div>').appendTo(this.wrapper);
  345. this.wrapper.find(".grid-report-export").click(function() { return me.export(); });
  346. // grid wrapper
  347. this.grid_wrapper = $("<div style='height: 500px; border: 1px solid #aaa; \
  348. background-color: #eee; margin-top: 15px;'>")
  349. .appendTo(this.wrapper);
  350. this.id = wn.dom.set_unique_id(this.grid_wrapper.get(0));
  351. // zero-value check
  352. $('<div style="margin: 10px 0px; display: none" class="show-zero">\
  353. <input type="checkbox"> '+wn._('Show rows with zero values')
  354. +'</div>').appendTo(this.wrapper);
  355. this.bind_show();
  356. wn.cur_grid_report = this;
  357. $(this.wrapper).trigger('make');
  358. },
  359. apply_filters_from_route: function() {
  360. var me = this;
  361. if(wn.route_options) {
  362. $.each(wn.route_options, function(key, value) {
  363. me.set_filter(key, value);
  364. });
  365. wn.route_options = null;
  366. } else {
  367. this.init_filter_values();
  368. }
  369. this.set_default_values();
  370. $(this.wrapper).trigger('apply_filters_from_route');
  371. },
  372. options: {
  373. editable: false,
  374. enableColumnReorder: false
  375. },
  376. render: function() {
  377. // new slick grid
  378. this.grid = new Slick.Grid("#"+this.id, this.dataView, this.dataview_columns, this.options);
  379. var me = this;
  380. this.grid.setSelectionModel(new Slick.CellSelectionModel());
  381. this.grid.registerPlugin(new Slick.CellExternalCopyManager({
  382. dataItemColumnValueExtractor: function(item, columnDef, value) {
  383. return item[columnDef.field];
  384. }
  385. }));
  386. // bind events
  387. this.dataView.onRowsChanged.subscribe(function (e, args) {
  388. me.grid.invalidateRows(args.rows);
  389. me.grid.render();
  390. });
  391. this.dataView.onRowCountChanged.subscribe(function (e, args) {
  392. me.grid.updateRowCount();
  393. me.grid.render();
  394. });
  395. this.tree_grid.show && this.add_tree_grid_events();
  396. },
  397. prepare_data_view: function() {
  398. // initialize the model
  399. this.dataView = new Slick.Data.DataView({ inlineFilters: true });
  400. this.dataView.beginUpdate();
  401. this.dataView.setItems(this.data);
  402. if(this.dataview_filter) this.dataView.setFilter(this.dataview_filter);
  403. if(this.tree_grid.show) this.dataView.setFilter(this.tree_dataview_filter);
  404. this.dataView.endUpdate();
  405. },
  406. export: function() {
  407. wn.tools.downloadify(wn.slickgrid_tools.get_view_data(this.columns, this.dataView),
  408. ["Report Manager", "System Manager"], this);
  409. return false;
  410. },
  411. apply_filters: function(item) {
  412. // generic filter: apply filter functiions
  413. // from all filter_inputs
  414. var filters = this.filter_inputs;
  415. if(item._show) return true;
  416. for (i in filters) {
  417. if(!this.apply_filter(item, i)) {
  418. return false;
  419. }
  420. }
  421. return true;
  422. },
  423. apply_filter: function(item, fieldname) {
  424. var filter = this.filter_inputs[fieldname].get(0);
  425. if(filter.opts.filter) {
  426. if(!filter.opts.filter(this[filter.opts.fieldname], item, filter.opts, this)) {
  427. return false;
  428. }
  429. }
  430. return true;
  431. },
  432. apply_zero_filter: function(val, item, opts, me) {
  433. // show only non-zero values
  434. if(!me.show_zero) {
  435. for(var i=0, j=me.columns.length; i<j; i++) {
  436. var col = me.columns[i];
  437. if(col.formatter==me.currency_formatter && !col.hidden) {
  438. if(flt(item[col.field]) > 0.001 || flt(item[col.field]) < -0.001) {
  439. return true;
  440. }
  441. }
  442. }
  443. return false;
  444. }
  445. return true;
  446. },
  447. show_zero_check: function() {
  448. var me = this;
  449. this.wrapper.bind('make', function() {
  450. me.wrapper.find('.show-zero').toggle(true).find('input').click(function(){
  451. me.refresh();
  452. });
  453. });
  454. },
  455. is_default: function(fieldname) {
  456. return this[fieldname]==this[fieldname + "_default"];
  457. },
  458. date_formatter: function(row, cell, value, columnDef, dataContext) {
  459. return dateutil.str_to_user(value);
  460. },
  461. currency_formatter: function(row, cell, value, columnDef, dataContext) {
  462. return repl('<div style="text-align: right; %(_style)s">%(value)s</div>', {
  463. _style: dataContext._style || "",
  464. value: ((value==null || value==="") ? "" : format_number(value))
  465. });
  466. },
  467. text_formatter: function(row, cell, value, columnDef, dataContext) {
  468. return repl('<span style="%(_style)s" title="%(esc_value)s">%(value)s</span>', {
  469. _style: dataContext._style || "",
  470. esc_value: cstr(value).replace(/"/g, '\"'),
  471. value: cstr(value)
  472. });
  473. },
  474. check_formatter: function(row, cell, value, columnDef, dataContext) {
  475. return repl('<input type="checkbox" data-id="%(id)s" \
  476. class="plot-check" %(checked)s>', {
  477. "id": dataContext.id,
  478. "checked": dataContext.checked ? 'checked="checked"' : ""
  479. })
  480. },
  481. apply_link_formatters: function() {
  482. var me = this;
  483. $.each(this.dataview_columns, function(i, col) {
  484. if(col.link_formatter) {
  485. col.formatter = function(row, cell, value, columnDef, dataContext) {
  486. // added link and open button to links
  487. // link_formatter must have
  488. // filter_input, open_btn (true / false), doctype (will be eval'd)
  489. if(!value) return "";
  490. var me = wn.cur_grid_report;
  491. if(dataContext._show) {
  492. return repl('<span style="%(_style)s">%(value)s</span>', {
  493. _style: dataContext._style || "",
  494. value: value
  495. });
  496. }
  497. // make link to add a filter
  498. var link_formatter = me.dataview_columns[cell].link_formatter;
  499. if (link_formatter.filter_input) {
  500. var html = repl('<a href="#" \
  501. onclick="wn.cur_grid_report.set_filter(\'%(col_name)s\', \'%(value)s\'); \
  502. wn.cur_grid_report.refresh(); return false;">\
  503. %(value)s</a>', {
  504. value: value,
  505. col_name: link_formatter.filter_input,
  506. page_name: wn.container.page.page_name
  507. });
  508. } else {
  509. var html = value;
  510. }
  511. // make icon to open form
  512. if(link_formatter.open_btn) {
  513. var doctype = link_formatter.doctype
  514. ? eval(link_formatter.doctype)
  515. : dataContext.doctype;
  516. html += me.get_link_open_icon(doctype, value);
  517. }
  518. return html;
  519. }
  520. }
  521. })
  522. },
  523. get_link_open_icon: function(doctype, name) {
  524. return repl(' <a href="#Form/%(doctype)s/%(name)s">\
  525. <i class="icon icon-share" style="cursor: pointer;"></i></a>', {
  526. doctype: doctype,
  527. name: encodeURIComponent(name)
  528. });
  529. },
  530. make_date_range_columns: function() {
  531. this.columns = [];
  532. var me = this;
  533. var range = this.filter_inputs.range.val();
  534. this.from_date = dateutil.user_to_str(this.filter_inputs.from_date.val());
  535. this.to_date = dateutil.user_to_str(this.filter_inputs.to_date.val());
  536. var date_diff = dateutil.get_diff(this.to_date, this.from_date);
  537. me.column_map = {};
  538. var add_column = function(date) {
  539. me.columns.push({
  540. id: date,
  541. name: dateutil.str_to_user(date),
  542. field: date,
  543. formatter: me.currency_formatter,
  544. width: 100
  545. });
  546. }
  547. var build_columns = function(condition) {
  548. // add column for each date range
  549. for(var i=0; i <= date_diff; i++) {
  550. var date = dateutil.add_days(me.from_date, i);
  551. if(!condition) condition = function() { return true; }
  552. if(condition(date)) add_column(date);
  553. me.last_date = date;
  554. if(me.columns.length) {
  555. me.column_map[date] = me.columns[me.columns.length-1];
  556. }
  557. }
  558. }
  559. // make columns for all date ranges
  560. if(range=='Daily') {
  561. build_columns();
  562. } else if(range=='Weekly') {
  563. build_columns(function(date) {
  564. if(!me.last_date) return true;
  565. return !(dateutil.get_diff(date, me.from_date) % 7)
  566. });
  567. } else if(range=='Monthly') {
  568. build_columns(function(date) {
  569. if(!me.last_date) return true;
  570. return dateutil.str_to_obj(me.last_date).getMonth() != dateutil.str_to_obj(date).getMonth()
  571. });
  572. } else if(range=='Quarterly') {
  573. build_columns(function(date) {
  574. if(!me.last_date) return true;
  575. return dateutil.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], dateutil.str_to_obj(date).getMonth())
  576. });
  577. } else if(range=='Yearly') {
  578. build_columns(function(date) {
  579. if(!me.last_date) return true;
  580. return $.map(wn.report_dump.data['Fiscal Year'], function(v) {
  581. return date==v.year_start_date ? true : null;
  582. }).length;
  583. });
  584. }
  585. // set label as last date of period
  586. $.each(this.columns, function(i, col) {
  587. col.name = me.columns[i+1]
  588. ? dateutil.str_to_user(dateutil.add_days(me.columns[i+1].id, -1))
  589. : dateutil.str_to_user(me.to_date);
  590. });
  591. },
  592. trigger_refresh_on_change: function(filters) {
  593. var me = this;
  594. $.each(filters, function(i, f) {
  595. me.filter_inputs[f] && me.filter_inputs[f].on("change", function() {
  596. me.refresh();
  597. });
  598. });
  599. }
  600. });
  601. wn.views.GridReportWithPlot = wn.views.GridReport.extend({
  602. render_plot: function() {
  603. var plot_data = this.get_plot_data ? this.get_plot_data() : null;
  604. if(!plot_data) {
  605. this.plot_area.toggle(false);
  606. return;
  607. }
  608. wn.require('assets/webnotes/js/lib/flot/jquery.flot.js');
  609. wn.require('assets/webnotes/js/lib/flot/jquery.flot.downsample.js');
  610. this.plot = $.plot(this.plot_area.toggle(true), plot_data,
  611. this.get_plot_options());
  612. this.setup_plot_hover();
  613. },
  614. setup_plot_check: function() {
  615. var me = this;
  616. me.wrapper.bind('make', function() {
  617. me.wrapper.on("click", ".plot-check", function() {
  618. var checked = $(this).prop("checked");
  619. var id = $(this).attr("data-id");
  620. if(me.item_by_name) {
  621. if(me.item_by_name[id]) {
  622. me.item_by_name[id].checked = checked ? true : false;
  623. }
  624. } else {
  625. $.each(me.data, function(i, d) {
  626. if(d.id==id) d.checked = checked;
  627. });
  628. }
  629. me.render_plot();
  630. });
  631. });
  632. },
  633. setup_plot_hover: function() {
  634. var me = this;
  635. this.tooltip_id = wn.dom.set_unique_id();
  636. function showTooltip(x, y, contents) {
  637. $('<div id="' + me.tooltip_id + '">' + contents + '</div>').css( {
  638. position: 'absolute',
  639. display: 'none',
  640. top: y + 5,
  641. left: x + 5,
  642. border: '1px solid #fdd',
  643. padding: '2px',
  644. 'background-color': '#fee',
  645. opacity: 0.80
  646. }).appendTo("body").fadeIn(200);
  647. }
  648. this.previousPoint = null;
  649. this.wrapper.find('.plot').bind("plothover", function (event, pos, item) {
  650. if (item) {
  651. if (me.previousPoint != item.dataIndex) {
  652. me.previousPoint = item.dataIndex;
  653. $("#" + me.tooltip_id).remove();
  654. showTooltip(item.pageX, item.pageY,
  655. me.get_tooltip_text(item.series.label, item.datapoint[0], item.datapoint[1]));
  656. }
  657. }
  658. else {
  659. $("#" + me.tooltip_id).remove();
  660. me.previousPoint = null;
  661. }
  662. });
  663. },
  664. get_tooltip_text: function(label, x, y) {
  665. var date = dateutil.obj_to_user(new Date(x));
  666. var value = format_number(y);
  667. return value + " on " + date;
  668. },
  669. get_plot_data: function() {
  670. var data = [];
  671. var me = this;
  672. $.each(this.data, function(i, item) {
  673. if (item.checked) {
  674. data.push({
  675. label: item.name,
  676. data: $.map(me.columns, function(col, idx) {
  677. if(col.formatter==me.currency_formatter && !col.hidden && col.plot!==false) {
  678. return me.get_plot_points(item, col, idx)
  679. }
  680. }),
  681. points: {show: true},
  682. lines: {show: true, fill: true},
  683. });
  684. // prepend opening
  685. data[data.length-1].data = [[dateutil.str_to_obj(me.from_date).getTime(),
  686. item.opening]].concat(data[data.length-1].data);
  687. }
  688. });
  689. return data.length ? data : false;
  690. },
  691. get_plot_options: function() {
  692. return {
  693. grid: { hoverable: true, clickable: true },
  694. xaxis: { mode: "time",
  695. min: dateutil.str_to_obj(this.from_date).getTime(),
  696. max: dateutil.str_to_obj(this.to_date).getTime() },
  697. series: { downsample: { threshold: 1000 } }
  698. }
  699. }
  700. });
  701. wn.views.TreeGridReport = wn.views.GridReportWithPlot.extend({
  702. make_transaction_list: function(parent_doctype, doctype) {
  703. var me = this;
  704. var tmap = {};
  705. $.each(wn.report_dump.data[doctype], function(i, v) {
  706. if(!tmap[v.parent]) tmap[v.parent] = [];
  707. tmap[v.parent].push(v);
  708. });
  709. if (!this.tl) this.tl = {};
  710. if (!this.tl[parent_doctype]) this.tl[parent_doctype] = [];
  711. $.each(wn.report_dump.data[parent_doctype], function(i, parent) {
  712. if(tmap[parent.name]) {
  713. $.each(tmap[parent.name], function(i, d) {
  714. me.tl[parent_doctype].push($.extend(copy_dict(parent), d));
  715. });
  716. }
  717. });
  718. },
  719. add_tree_grid_events: function() {
  720. var me = this;
  721. this.grid.onClick.subscribe(function (e, args) {
  722. if ($(e.target).hasClass("toggle")) {
  723. var item = me.dataView.getItem(args.row);
  724. if (item) {
  725. if (!item._collapsed) {
  726. item._collapsed = true;
  727. } else {
  728. item._collapsed = false;
  729. }
  730. me.dataView.updateItem(item.id, item);
  731. }
  732. e.stopImmediatePropagation();
  733. }
  734. });
  735. },
  736. tree_formatter: function (row, cell, value, columnDef, dataContext) {
  737. var me = wn.cur_grid_report;
  738. value = value.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
  739. var data = me.data;
  740. var spacer = "<span style='display:inline-block;height:1px;width:" +
  741. (15 * dataContext["indent"]) + "px'></span>";
  742. var idx = me.dataView.getIdxById(dataContext.id);
  743. var link = me.tree_grid.formatter(dataContext);
  744. if(dataContext.doctype) {
  745. link += me.get_link_open_icon(dataContext.doctype, dataContext.name);
  746. }
  747. if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) {
  748. if (dataContext._collapsed) {
  749. return spacer + " <span class='toggle expand'></span>&nbsp;" + link;
  750. } else {
  751. return spacer + " <span class='toggle collapse'></span>&nbsp;" + link;
  752. }
  753. } else {
  754. return spacer + " <span class='toggle'></span>&nbsp;" + link;
  755. }
  756. },
  757. tree_dataview_filter: function(item) {
  758. var me = wn.cur_grid_report;
  759. if(!me.apply_filters(item)) return false;
  760. var parent = item[me.tree_grid.parent_field];
  761. while (parent) {
  762. if (me.item_by_name[parent]._collapsed) {
  763. return false;
  764. }
  765. parent = me.parent_map[parent];
  766. }
  767. return true;
  768. },
  769. prepare_tree: function(item_dt, group_dt) {
  770. var group_data = wn.report_dump.data[group_dt];
  771. var item_data = wn.report_dump.data[item_dt];
  772. // prepare map with child in respective group
  773. var me = this;
  774. var item_group_map = {};
  775. var group_ids = $.map(group_data, function(v) { return v.id; });
  776. $.each(item_data, function(i, item) {
  777. var parent = item[me.tree_grid.parent_field];
  778. if(!item_group_map[parent]) item_group_map[parent] = [];
  779. if(group_ids.indexOf(item.name)==-1) {
  780. item_group_map[parent].push(item);
  781. } else {
  782. msgprint("Ignoring Item "+ item.name.bold() +
  783. ", because a group exists with the same name!");
  784. }
  785. });
  786. // arrange items besides their parent item groups
  787. var items = [];
  788. $.each(group_data, function(i, group){
  789. group.is_group = true;
  790. items.push(group);
  791. items = items.concat(item_group_map[group.name] || []);
  792. });
  793. return items;
  794. },
  795. set_indent: function() {
  796. var me = this;
  797. $.each(this.data, function(i, d) {
  798. var indent = 0;
  799. var parent = me.parent_map[d.name];
  800. if(parent) {
  801. while(parent) {
  802. indent++;
  803. parent = me.parent_map[parent];
  804. }
  805. }
  806. d.indent = indent;
  807. });
  808. },
  809. export: function() {
  810. var msgbox = msgprint('<p>Select To Download:</p>\
  811. <p><input type="checkbox" name="with_groups" checked="checked"> With Groups</p>\
  812. <p><input type="checkbox" name="with_ledgers" checked="checked"> With Ledgers</p>\
  813. <p><button class="btn btn-info">Download</button>');
  814. var me = this;
  815. $(msgbox.body).find("button").click(function() {
  816. var with_groups = $(msgbox.body).find("[name='with_groups']").prop("checked");
  817. var with_ledgers = $(msgbox.body).find("[name='with_ledgers']").prop("checked");
  818. var data = wn.slickgrid_tools.get_view_data(me.columns, me.dataView,
  819. function(row, item) {
  820. if(with_groups) {
  821. // add row
  822. for(var i=0; i<item.indent; i++) row[0] = " " + row[0];
  823. }
  824. if(with_groups && (item.group_or_ledger == "Group" || item.is_group)) {
  825. return true;
  826. }
  827. if(with_ledgers && (item.group_or_ledger != "Group" && !item.is_group)) {
  828. return true;
  829. }
  830. return false;
  831. });
  832. wn.tools.downloadify(data, ["Report Manager", "System Manager"], me);
  833. return false;
  834. })
  835. return false;
  836. },
  837. });