Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

914 lignes
25 KiB

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