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

12 роки тому
12 роки тому
12 роки тому
12 роки тому
11 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. wn.provide("wn.views");
  4. wn.provide("wn.query_reports");
  5. wn.standard_pages["query-report"] = function() {
  6. var wrapper = wn.container.add_page('query-report');
  7. wn.require("assets/js/slickgrid.min.js");
  8. wn.ui.make_app_page({
  9. parent: wrapper,
  10. title: wn._('Query Report'),
  11. single_column: true
  12. });
  13. wn.query_report = new wn.views.QueryReport({
  14. parent: wrapper,
  15. });
  16. $(wrapper).bind("show", function() {
  17. wn.query_report.load();
  18. });
  19. }
  20. wn.views.QueryReport = Class.extend({
  21. init: function(opts) {
  22. $.extend(this, opts);
  23. // globalify for slickgrid
  24. this.appframe = this.parent.appframe;
  25. this.parent.query_report = this;
  26. this.make();
  27. this.load();
  28. },
  29. slickgrid_options: {
  30. enableColumnReorder: false,
  31. showHeaderRow: true,
  32. headerRowHeight: 30,
  33. explicitInitialization: true,
  34. multiColumnSort: true
  35. },
  36. make: function() {
  37. this.wrapper = $("<div>").appendTo($(this.parent).find(".layout-main"));
  38. $('<div class="waiting-area" style="display: none;"></div>\
  39. <div class="no-report-area well" style="display: none;">\
  40. </div>\
  41. <div class="results" style="display: none;">\
  42. <div class="result-area" style="height:400px; \
  43. border: 1px solid #aaa;"></div>\
  44. <p class="text-muted"><br>\
  45. '+wn._('For comparative filters, start with')+' ">" or "<", e.g. >5 or >01-02-2012\
  46. <br>'+wn._('For ranges')+' ('+wn._('values and dates')+') use ":", \
  47. e.g. "5:10" (to filter values between 5 & 10)</p>\
  48. </div>').appendTo(this.wrapper);
  49. this.make_toolbar();
  50. },
  51. make_toolbar: function() {
  52. var me = this;
  53. this.appframe.set_title_right(wn._('Refresh'), function() { me.refresh(); });
  54. // Edit
  55. var edit_btn = this.appframe.add_primary_action(wn._('Edit'), function() {
  56. if(!wn.user.is_report_manager()) {
  57. msgprint(wn._("You are not allowed to create / edit reports"));
  58. return false;
  59. }
  60. wn.set_route("Form", "Report", me.report_name);
  61. }, "icon-edit");
  62. this.appframe.add_primary_action(wn._('Export'), function() { me.export_report(); },
  63. "icon-download");
  64. if(wn.model.can_restrict("Report")) {
  65. this.appframe.add_primary_action(wn._("User Restrictions"), function() {
  66. wn.route_options = {
  67. property: "Report",
  68. restriction: me.report_name
  69. };
  70. wn.set_route("user-properties");
  71. }, "icon-shield");
  72. }
  73. },
  74. load: function() {
  75. // load from route
  76. var route = wn.get_route();
  77. var me = this;
  78. if(route[1]) {
  79. if(me.report_name!=route[1]) {
  80. me.report_name = route[1];
  81. this.wrapper.find(".no-report-area").toggle(false);
  82. me.appframe.set_title(wn._("Query Report")+": " + wn._(me.report_name));
  83. wn.model.with_doc("Report", me.report_name, function() {
  84. me.report_doc = wn.model.get_doc("Report", me.report_name);
  85. wn.model.with_doctype(me.report_doc.ref_doctype, function() {
  86. if(!wn.query_reports[me.report_name]) {
  87. return wn.call({
  88. method:"webnotes.widgets.query_report.get_script",
  89. args: {
  90. report_name: me.report_name
  91. },
  92. callback: function(r) {
  93. me.appframe.set_title(wn._("Query Report")+": " + wn._(me.report_name));
  94. wn.dom.eval(r.message || "");
  95. me.setup_filters();
  96. me.refresh();
  97. }
  98. });
  99. } else {
  100. me.setup_filters();
  101. me.refresh();
  102. }
  103. });
  104. });
  105. }
  106. } else {
  107. var msg = wn._("No Report Loaded. Please use query-report/[Report Name] to run a report.")
  108. this.wrapper.find(".no-report-area").html(msg).toggle(true);
  109. }
  110. },
  111. setup_filters: function() {
  112. this.clear_filters();
  113. var me = this;
  114. $.each(wn.query_reports[this.report_name].filters || [], function(i, df) {
  115. if(df.fieldtype==="Break") {
  116. me.appframe.add_break();
  117. } else {
  118. var f = me.appframe.add_field(df);
  119. $(f.wrapper).addClass("filters pull-left");
  120. me.filters.push(f);
  121. if(df["default"]) {
  122. f.set_input(df["default"]);
  123. }
  124. if(df.get_query) f.get_query = df.get_query;
  125. }
  126. });
  127. this.set_route_filters()
  128. this.set_filters_by_name();
  129. },
  130. clear_filters: function() {
  131. this.filters = [];
  132. this.appframe.parent.find('.appframe-form .filters').remove();
  133. },
  134. set_route_filters: function() {
  135. var me = this;
  136. if(wn.route_options) {
  137. $.each(this.filters || [], function(i, f) {
  138. if(wn.route_options[f.df.fieldname]!=null)
  139. f.set_input(wn.route_options[f.df.fieldname]);
  140. });
  141. }
  142. wn.route_options = null;
  143. },
  144. set_filters_by_name: function() {
  145. this.filters_by_name = {};
  146. for(var i in this.filters) {
  147. this.filters_by_name[this.filters[i].df.fieldname] = this.filters[i];
  148. }
  149. },
  150. refresh: function() {
  151. // Run
  152. var me =this;
  153. this.waiting = wn.messages.waiting(this.wrapper.find(".waiting-area").empty().toggle(true),
  154. "Loading Report...");
  155. this.wrapper.find(".results").toggle(false);
  156. var filters = {};
  157. $.each(this.filters || [], function(i, f) {
  158. filters[f.df.fieldname] = f.get_parsed_value();
  159. })
  160. return wn.call({
  161. method: "webnotes.widgets.query_report.run",
  162. type: "GET",
  163. args: {
  164. "report_name": me.report_name,
  165. filters: filters
  166. },
  167. callback: function(r) {
  168. me.report_ajax = undefined;
  169. me.make_results(r.message.result, r.message.columns);
  170. }
  171. });
  172. return this.report_ajax;
  173. },
  174. get_values: function() {
  175. var filters = {};
  176. var mandatory_fields = [];
  177. $.each(this.filters || [], function(i, f) {
  178. var v = f.get_parsed_value();
  179. if(v === '%') v = null;
  180. if(f.df.reqd && !v) mandatory_fields.push(f.df.label);
  181. if(v) filters[f.df.fieldname] = v;
  182. })
  183. if(mandatory_fields.length) {
  184. wn.throw(wn._("Mandatory filters required:\n") + wn._(mandatory_fields.join("\n")));
  185. }
  186. return filters
  187. },
  188. make_results: function(result, columns) {
  189. this.wrapper.find(".waiting-area").empty().toggle(false);
  190. this.wrapper.find(".results").toggle(true);
  191. this.make_columns(columns);
  192. this.make_data(result, columns);
  193. this.render(result, columns);
  194. },
  195. render: function(result, columns) {
  196. this.columnFilters = {};
  197. this.make_dataview();
  198. this.id = wn.dom.set_unique_id(this.wrapper.find(".result-area").get(0));
  199. this.grid = new Slick.Grid("#"+this.id, this.dataView, this.columns,
  200. this.slickgrid_options);
  201. this.grid.setSelectionModel(new Slick.CellSelectionModel());
  202. this.grid.registerPlugin(new Slick.CellExternalCopyManager({
  203. dataItemColumnValueExtractor: function(item, columnDef, value) {
  204. return item[columnDef.field];
  205. }
  206. }));
  207. this.setup_header_row();
  208. this.grid.init();
  209. this.setup_sort();
  210. },
  211. make_columns: function(columns) {
  212. this.columns = [{id: "_id", field: "_id", name: "Sr No", width: 60}]
  213. .concat($.map(columns, function(c) {
  214. var col = {name:c, id: c, field: c, sortable: true, width: 80}
  215. if(c.indexOf(":")!=-1) {
  216. var opts = c.split(":");
  217. var df = {
  218. label: opts.length<=2 ? opts[0] : opts.slice(0, opts.length - 2).join(":"),
  219. fieldtype: opts.length<=2 ? opts[1] : opts[opts.length - 2],
  220. width: opts.length<=2 ? opts[2] : opts[opts.length - 1]
  221. }
  222. if(!df.fieldtype)
  223. df.fieldtype="Data";
  224. if(df.fieldtype.indexOf("/")!=-1) {
  225. var tmp = df.fieldtype.split("/");
  226. df.fieldtype = tmp[0];
  227. df.options = tmp[1];
  228. }
  229. col.df = df;
  230. col.formatter = function(row, cell, value, columnDef, dataContext) {
  231. return wn.format(value, columnDef.df, null, dataContext);
  232. }
  233. // column parameters
  234. col.name = col.id = col.field = df.label;
  235. col.name = wn._(df.label);
  236. col.fieldtype = opts[1];
  237. // width
  238. if(df.width) {
  239. col.width=parseInt(df.width);
  240. }
  241. } else {
  242. col.df = {
  243. label: c,
  244. fieldtype: "Data"
  245. }
  246. }
  247. col.name = wn._(toTitle(col.name.replace(/_/g, " ")))
  248. return col
  249. }));
  250. },
  251. make_data: function(result, columns) {
  252. var me = this;
  253. this.data = [];
  254. for(var row_idx=0, l=result.length; row_idx < l; row_idx++) {
  255. var row = result[row_idx];
  256. var newrow = {};
  257. for(var i=1, j=this.columns.length; i<j; i++) {
  258. newrow[this.columns[i].field] = row[i-1];
  259. };
  260. newrow._id = row_idx + 1;
  261. newrow.id = newrow.name ? newrow.name : ("_" + newrow._id);
  262. this.data.push(newrow);
  263. }
  264. },
  265. make_dataview: function() {
  266. // initialize the model
  267. this.dataView = new Slick.Data.DataView({ inlineFilters: true });
  268. this.dataView.beginUpdate();
  269. this.dataView.setItems(this.data);
  270. this.dataView.setFilter(this.inline_filter);
  271. this.dataView.endUpdate();
  272. var me = this;
  273. this.dataView.onRowCountChanged.subscribe(function (e, args) {
  274. me.grid.updateRowCount();
  275. me.grid.render();
  276. });
  277. this.dataView.onRowsChanged.subscribe(function (e, args) {
  278. me.grid.invalidateRows(args.rows);
  279. me.grid.render();
  280. });
  281. },
  282. inline_filter: function (item) {
  283. var me = wn.container.page.query_report;
  284. for (var columnId in me.columnFilters) {
  285. if (columnId !== undefined && me.columnFilters[columnId] !== "") {
  286. var c = me.grid.getColumns()[me.grid.getColumnIndex(columnId)];
  287. if (!me.compare_values(item[c.field], me.columnFilters[columnId],
  288. me.columns[me.grid.getColumnIndex(columnId)])) {
  289. return false;
  290. }
  291. }
  292. }
  293. return true;
  294. },
  295. compare_values: function(value, filter, columnDef) {
  296. var invert = false;
  297. // check if invert
  298. if(filter[0]=="!") {
  299. invert = true;
  300. filter = filter.substr(1);
  301. }
  302. var out = false;
  303. var cond = "=="
  304. // parse condition
  305. if(filter[0]==">") {
  306. filter = filter.substr(1);
  307. cond = ">"
  308. } else if(filter[0]=="<") {
  309. filter = filter.substr(1);
  310. cond = "<"
  311. }
  312. if(in_list(['Float', 'Currency', 'Int', 'Date'], columnDef.df.fieldtype)) {
  313. // non strings
  314. if(filter.indexOf(":")==-1) {
  315. if(columnDef.df.fieldtype=="Date") {
  316. filter = dateutil.user_to_str(filter);
  317. }
  318. if(in_list(["Float", "Currency", "Int"], columnDef.df.fieldtype)) {
  319. value = flt(value);
  320. filter = flt(filter);
  321. }
  322. out = eval("value" + cond + "filter");
  323. } else {
  324. // range
  325. filter = filter.split(":");
  326. if(columnDef.df.fieldtype=="Date") {
  327. filter[0] = dateutil.user_to_str(filter[0]);
  328. filter[1] = dateutil.user_to_str(filter[1]);
  329. }
  330. if(in_list(["Float", "Currency", "Int"], columnDef.df.fieldtype)) {
  331. value = flt(value);
  332. filter[0] = flt(filter[0]);
  333. filter[1] = flt(filter[1]);
  334. }
  335. out = value >= filter[0] && value <= filter[1];
  336. }
  337. } else {
  338. // string
  339. value = value + "";
  340. value = value.toLowerCase();
  341. filter = filter.toLowerCase();
  342. out = value.indexOf(filter) != -1;
  343. }
  344. if(invert)
  345. return !out;
  346. else
  347. return out;
  348. },
  349. setup_header_row: function() {
  350. var me = this;
  351. $(this.grid.getHeaderRow()).delegate(":input", "change keyup", function (e) {
  352. var columnId = $(this).data("columnId");
  353. if (columnId != null) {
  354. me.columnFilters[columnId] = $.trim($(this).val());
  355. me.dataView.refresh();
  356. }
  357. });
  358. this.grid.onHeaderRowCellRendered.subscribe(function(e, args) {
  359. $(args.node).empty();
  360. $("<input type='text'>")
  361. .data("columnId", args.column.id)
  362. .val(me.columnFilters[args.column.id])
  363. .appendTo(args.node);
  364. });
  365. },
  366. setup_sort: function() {
  367. var me = this;
  368. this.grid.onSort.subscribe(function (e, args) {
  369. var cols = args.sortCols;
  370. me.data.sort(function (dataRow1, dataRow2) {
  371. for (var i = 0, l = cols.length; i < l; i++) {
  372. var field = cols[i].sortCol.field;
  373. var sign = cols[i].sortAsc ? 1 : -1;
  374. var value1 = dataRow1[field], value2 = dataRow2[field];
  375. var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
  376. if (result != 0) {
  377. return result;
  378. }
  379. }
  380. return 0;
  381. });
  382. me.dataView.beginUpdate();
  383. me.dataView.setItems(me.data);
  384. me.dataView.endUpdate();
  385. me.dataView.refresh();
  386. });
  387. },
  388. export_report: function() {
  389. if(!wn.model.can_export(this.report_doc.ref_doctype)) {
  390. msgprint(wn._("You are not allowed to export this report."));
  391. return false;
  392. }
  393. var result = $.map(wn.slickgrid_tools.get_view_data(this.columns, this.dataView),
  394. function(row) {
  395. return [row.splice(1)];
  396. });
  397. this.title = this.report_name;
  398. wn.tools.downloadify(result, null, this);
  399. return false;
  400. }
  401. })