You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

grid_report.js 9.8 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. //
  3. // MIT License (MIT)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a
  6. // copy of this software and associated documentation files (the "Software"),
  7. // to deal in the Software without restriction, including without limitation
  8. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. // and/or sell copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. wn.provide("wn.report_dump");
  23. $.extend(wn.report_dump, {
  24. data: {},
  25. with_data: function(doctypes, callback, progress_bar) {
  26. var missing = [];
  27. $.each(doctypes, function(i, v) {
  28. if(!wn.report_dump.data[v]) missing.push(v);
  29. })
  30. if(missing.length) {
  31. wn.call({
  32. method: "webnotes.widgets.report_dump.get_data",
  33. args: {doctypes: missing},
  34. callback: function(r) {
  35. // creating map of data from a list
  36. $.each(r.message, function(doctype, doctype_data) {
  37. var data = [];
  38. $.each(doctype_data.data, function(i, d) {
  39. var row = {};
  40. $.each(doctype_data.columns, function(idx, col) {
  41. row[col] = d[idx];
  42. });
  43. row.id = doctype + "-" + i;
  44. data.push(row);
  45. });
  46. wn.report_dump.data[doctype] = data;
  47. });
  48. callback();
  49. },
  50. progress_bar: progress_bar
  51. })
  52. } else {
  53. callback();
  54. }
  55. }
  56. });
  57. wn.provide("wn.views");
  58. wn.views.GridReport = Class.extend({
  59. init: function(opts) {
  60. this.filter_inputs = {};
  61. $.extend(this, opts);
  62. this.wrapper = $('<div>').appendTo(this.parent);
  63. if(this.filters) {
  64. this.make_filters();
  65. }
  66. this.make_waiting();
  67. this.import_slickgrid();
  68. var me = this;
  69. this.get_data();
  70. wn.cur_grid_report = this;
  71. },
  72. get_data: function() {
  73. var me = this;
  74. wn.report_dump.with_data(this.doctypes, function() {
  75. // setup filters
  76. $.each(me.filter_inputs, function(i, v) {
  77. var opts = v.get(0).opts;
  78. if (opts.fieldtype == "Select" && inList(me.doctypes, opts.options)) {
  79. $(v).add_options($.map(wn.report_dump.data[opts.options], function(d) {
  80. return d.name;
  81. }));
  82. }
  83. });
  84. me.setup();
  85. me.refresh();
  86. }, this.wrapper.find(".progress .bar"));
  87. },
  88. make_waiting: function() {
  89. this.waiting = $('<div class="well" style="width: 63%; margin: 30px auto;">\
  90. <p style="text-align: center;">Loading Report...</p>\
  91. <div class="progress progress-striped active">\
  92. <div class="bar" style="width: 10%"></div></div>')
  93. .appendTo(this.wrapper);
  94. },
  95. make_grid_wrapper: function() {
  96. $('<div style="text-align: right;"> \
  97. <a href="#" class="grid-report-print"><i class="icon icon-print"></i> Print</a> \
  98. <span style="color: #aaa; margin: 0px 10px;"> | </span> \
  99. <a href="#" class="grid-report-export"><i class="icon icon-download-alt"></i> Export</a> \
  100. </div>').appendTo(this.wrapper);
  101. this.wrapper.find(".grid-report-export").click(function() { return me.export(); });
  102. this.grid_wrapper = $("<div style='height: 500px; border: 1px solid #aaa; \
  103. background-color: #eee; margin-top: 15px;'>")
  104. .appendTo(this.wrapper);
  105. this.id = wn.dom.set_unique_id(this.grid_wrapper.get(0));
  106. var me = this;
  107. // bind show event to reset cur_report_grid
  108. // and refresh filters from url
  109. // this must be called after init
  110. // because "wn.container.page" will only be set
  111. // once "load" event is over.
  112. $(wn.container.page).bind('show', function() {
  113. // reapply filters on show
  114. wn.cur_grid_report = me;
  115. me.apply_filters_from_route();
  116. me.refresh();
  117. });
  118. this.apply_filters_from_route();
  119. },
  120. load_filters: function(callback) {
  121. // override
  122. callback();
  123. },
  124. make_filters: function() {
  125. var me = this;
  126. $.each(this.filters, function(i, v) {
  127. v.fieldname = v.fieldname || v.label.replace(/ /g, '_').toLowerCase();
  128. var input = null;
  129. if(v.fieldtype=='Select') {
  130. input = me.appframe.add_select(v.label, [v.default_value]);
  131. } else if(v.fieldtype=='Button') {
  132. input = me.appframe.add_button(v.label);
  133. if(v.icon) {
  134. $('<i class="icon '+ v.icon +'"></i>').prependTo(input);
  135. }
  136. } else if(v.fieldtype=='Date') {
  137. input = me.appframe.add_date(v.label);
  138. } else if(v.fieldtype=='Label') {
  139. input = me.appframe.add_label(v.label);
  140. } else if(v.fieldtype=='Data') {
  141. input = me.appframe.add_data(v.label);
  142. }
  143. if(v.cssClass) {
  144. input && input.addClass(v.cssClass);
  145. }
  146. input && (input.get(0).opts = v);
  147. me.filter_inputs[v.fieldname] = input;
  148. });
  149. },
  150. import_slickgrid: function() {
  151. wn.require('js/lib/slickgrid/slick.grid.css');
  152. wn.require('js/lib/slickgrid/slick-default-theme.css');
  153. wn.require('js/lib/slickgrid/jquery.event.drag.min.js');
  154. wn.require('js/lib/slickgrid/slick.core.js');
  155. wn.require('js/lib/slickgrid/slick.grid.js');
  156. wn.require('js/lib/slickgrid/slick.dataview.js');
  157. wn.dom.set_style('.slick-cell { font-size: 12px; }');
  158. },
  159. refresh: function() {
  160. this.render();
  161. },
  162. apply_filters_from_route: function() {
  163. var hash = window.location.hash;
  164. var me = this;
  165. if(hash.indexOf('/') != -1) {
  166. $.each(hash.split('/').splice(1).join('/').split('&'), function(i, f) {
  167. var f = f.split("=");
  168. me.filter_inputs[f[0]].val(decodeURIComponent(f[1]));
  169. });
  170. }
  171. },
  172. set_route: function() {
  173. wn.set_route(wn.container.page.page_name, $.map(this.filter_inputs, function(v) {
  174. var val = v.val();
  175. var opts = v.get(0).opts;
  176. if(val && val != opts.default_value)
  177. return encodeURIComponent(opts.fieldname)
  178. + '=' + encodeURIComponent(val);
  179. }).join('&'))
  180. },
  181. render: function() {
  182. // new slick grid
  183. this.waiting.toggle(false);
  184. if(!this.grid_wrapper) this.make_grid_wrapper();
  185. this.apply_link_formatters();
  186. this.prepare_data();
  187. this.grid = new Slick.Grid("#"+this.id, this.dataView, this.columns, this.options);
  188. // bind events
  189. this.dataView.onRowsChanged.subscribe(function (e, args) {
  190. grid.invalidateRows(args.rows);
  191. grid.render();
  192. });
  193. this.dataView.onRowCountChanged.subscribe(function (e, args) {
  194. grid.updateRowCount();
  195. grid.render();
  196. });
  197. },
  198. prepare_data_view: function(items) {
  199. // initialize the model
  200. this.dataView = new Slick.Data.DataView({ inlineFilters: true });
  201. this.dataView.beginUpdate();
  202. this.dataView.setItems(items);
  203. this.dataView.setFilter(this.dataview_filter);
  204. this.dataView.endUpdate();
  205. },
  206. export: function() {
  207. var me = this;
  208. var res = [$.map(this.columns, function(v) { return v.name; })];
  209. var col_map = $.map(this.columns, function(v) { return v.field; });
  210. for (var i=0, len=this.dataView.getLength(); i<len; i++) {
  211. var d = this.dataView.getItem(i);
  212. var row = $.map(col_map, function(col) {
  213. return d[col];
  214. });
  215. res.push(row);
  216. }
  217. wn.require("js/lib/downloadify/downloadify.min.js");
  218. wn.require("js/lib/downloadify/swfobject.js");
  219. var id = wn.dom.set_unique_id();
  220. var msgobj = msgprint('<p id="'+ id +'">You must have Flash 10 installed to download this file.</p>');
  221. Downloadify.create(id ,{
  222. filename: function(){
  223. return me.title + '.csv';
  224. },
  225. data: function(){
  226. return wn.to_csv(res);
  227. },
  228. swf: 'js/lib/downloadify/downloadify.swf',
  229. downloadImage: 'js/lib/downloadify/download.png',
  230. onComplete: function(){ msgobj.hide(); },
  231. onCancel: function(){ msgobj.hide(); },
  232. onError: function(){ msgobj.hide(); },
  233. width: 100,
  234. height: 30,
  235. transparent: true,
  236. append: false
  237. });
  238. return false;
  239. },
  240. options: {
  241. editable: false,
  242. enableColumnReorder: false
  243. },
  244. dataview_filter: function(item) {
  245. var filters = wn.cur_grid_report.filter_inputs;
  246. for (i in filters) {
  247. var filter = filters[i].get(0);
  248. if(filter.opts.filter && !filter.opts.filter($(filter).val(), item, filter.opts)) {
  249. return false;
  250. }
  251. }
  252. return true;
  253. },
  254. date_formatter: function(row, cell, value, columnDef, dataContext) {
  255. return dateutil.str_to_user(value);
  256. },
  257. currency_formatter: function(row, cell, value, columnDef, dataContext) {
  258. return "<div style='text-align: right;'>" + fmt_money(value) + "</div>";
  259. },
  260. text_formatter: function(row, cell, value, columnDef, dataContext) {
  261. return "<span title='" + value +"'>" + value + "</div>";
  262. },
  263. apply_link_formatters: function() {
  264. var me = this;
  265. $.each(this.columns, function(i, col) {
  266. if(col.link_formatter) {
  267. col.formatter = function(row, cell, value, columnDef, dataContext) {
  268. // added link and open button to links
  269. // link_formatter must have
  270. // filter_input, open_btn (true / false), doctype (will be eval'd)
  271. // make link to add a filter
  272. var link_formatter = wn.cur_grid_report.columns[cell].link_formatter;
  273. var html = repl('<a href="#" \
  274. onclick="wn.cur_grid_report.filter_inputs.%(col_name)s.val(\'%(value)s\'); \
  275. wn.cur_grid_report.set_route(); return false;">\
  276. %(value)s</a>', {
  277. value: value,
  278. col_name: link_formatter.filter_input,
  279. page_name: wn.container.page.page_name
  280. })
  281. // make icon to open form
  282. if(link_formatter.open_btn) {
  283. html += repl(' <i class="icon icon-share" style="cursor: pointer;"\
  284. onclick="wn.set_route(\'Form\', \'%(doctype)s\', \'%(value)s\');">\
  285. </i>', {
  286. value: value,
  287. doctype: eval(link_formatter.doctype)
  288. });
  289. }
  290. return html;
  291. }
  292. }
  293. })
  294. }
  295. })