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.
 
 
 
 
 
 

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