Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

440 wiersze
12 KiB

  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. })