From bc111497380cfdcfecbefa168055cfb1a90d6300 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 1 Oct 2012 18:01:58 +0530 Subject: [PATCH] updated query report and modified Report to include query reports --- core/doctype/report/report.txt | 11 +- core/page/query_report/query_report.js | 405 ++++++++++++++++++++++++- public/build.json | 1 - public/css/legacy/body.css | 4 + public/js/wn/ui/dialog.js | 4 + public/js/wn/views/query_report.js | 258 ---------------- webnotes/client.py | 6 +- webnotes/widgets/query_builder.py | 2 +- webnotes/widgets/query_report.py | 15 +- 9 files changed, 438 insertions(+), 268 deletions(-) delete mode 100644 public/js/wn/views/query_report.js diff --git a/core/doctype/report/report.txt b/core/doctype/report/report.txt index 79241c9033..6914995ad5 100644 --- a/core/doctype/report/report.txt +++ b/core/doctype/report/report.txt @@ -5,7 +5,7 @@ { u'creation': '2012-06-13 19:07:26', u'docstatus': 0, - u'modified': '2012-10-01 14:00:02', + u'modified': '2012-10-01 17:58:19', u'modified_by': u'Administrator', u'owner': u'Administrator' }, @@ -48,6 +48,15 @@ u'name': u'Report' }, + # DocField + { + u'doctype': u'DocField', + 'fieldname': u'is_standard', + 'fieldtype': u'Select', + 'label': u'Is Standard', + 'options': u'No\nYes' + }, + # DocField { u'doctype': u'DocField', diff --git a/core/page/query_report/query_report.js b/core/page/query_report/query_report.js index 688afcc7cf..6fe0182ec6 100644 --- a/core/page/query_report/query_report.js +++ b/core/page/query_report/query_report.js @@ -1,3 +1,25 @@ +// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) +// +// MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + wn.pages['query-report'].onload = function(wrapper) { wn.ui.make_app_page({ parent: wrapper, @@ -5,7 +27,384 @@ wn.pages['query-report'].onload = function(wrapper) { single_column: true }); - wn.test = new wn.views.QueryReport({ + wn.query_report = new wn.views.QueryReport({ parent: wrapper, - }) -} \ No newline at end of file + }); +} + +wn.pages['query-report'].show = function(wrapper) { + wn.query_report && wn.query_report.load() +} + +wn.provide("wn.views"); + +wn.views.QueryReport = Class.extend({ + init: function(opts) { + $.extend(this, opts); + // globalify for slickgrid + this.appframe = this.parent.appframe; + this.parent.query_report = this; + this.make(); + }, + slickgrid_options: { + enableColumnReorder: false, + showHeaderRow: true, + headerRowHeight: 30, + explicitInitialization: true, + multiColumnSort: true + }, + make: function() { + this.wrapper = $("
").appendTo($(this.parent).find(".layout-main")); + $('\ +
\ +


\ + For comparative filters, start with ">" or "<", e.g. >5 or >01-02-2012\ +
For ranges (values and dates) use ":", e.g. "5:10" (to filter values between 5 & 10)\ +

').appendTo(this.wrapper); + + this.make_query_form(); + this.make_toolbar(); + this.import_slickgrid(); + }, + make_toolbar: function() { + var me = this; + this.appframe.add_button("Run", function() { + me.refresh(); + }).addClass("btn-success"); + + if(in_list(user_roles, "System Manager")) { + // Edit + this.appframe.add_button("Edit", function() { + me.wrapper.find(".query-edit").slideToggle(); + }); + } + }, + make_query_form: function() { + this.query_form = new wn.ui.FieldGroup({ + parent: $(this.wrapper).find(".query-form").get(0), + fields: [ + {label:"Report Name", reqd: 1, fieldname:"name"}, + {label:"Based on", fieldtype:"Link", options:"DocType", + fieldname: "ref_doctype", + reqd:1, description:"Permissions will be based on this DocType"}, + {label:"Query", fieldtype: "Text", reqd: 1}, + {label:"Save", fieldtype:"Button"} + ] + }); + + // text style + $(this.query_form.fields_dict.query.input).css({ + width: "100%", + height: "300px", + "font-weight": "Normal", + "font-family": "Monaco, Courier, Fixed" + }); + + // Save + var me = this; + $(this.query_form.fields_dict.save.input).click(function() { + var doc = me.query_form.get_values(); + if(!doc) return; + + // new report + if(!me.doc) { + doc.doctype = "Report"; + if(user=="Administrator") doc.is_standard="Yes"; + else doc.is_standard="No" + doc.__islocal = 1; + } else{ + doc = $.extend(copy_dict(me.doc), doc); + } + + + wn.call({ + method:"webnotes.client.save", + args: { doclist: [doc] }, + callback: function(r) { + wn.provide("locals.Report"); + me.doc = r.message[0] + locals["Report"][me.doc.name] = r.message[0]; + wn.set_route("query-report", me.doc.name); + } + }) + }); + }, + load: function() { + // load from route + var route = wn.get_route(); + var me = this; + this.doc = null; + if(route[1]) { + wn.model.with_doc("Report", route[1], function(doc) { + me.doc = locals["Report"] && locals["Report"][route[1]]; + if(!me.doc) { + msgprint("Bad Report Name"); + return; + } + me.appframe.title("Query Report: " + me.doc.name); + me.query_form.set_values(me.doc); + + // only administrator can edit standard reports + $(me.wrapper).find("query-form :input").attr('disabled', + (me.doc.is_standard=="Yes" && user!="Administrator") + ? "disabled" : null); + me.refresh(); + }) + } + }, + import_slickgrid: function() { + wn.require('lib/js/lib/slickgrid/slick.grid.css'); + wn.require('lib/js/lib/slickgrid/slick-default-theme.css'); + wn.require('lib/js/lib/slickgrid/jquery.event.drag.min.js'); + wn.require('lib/js/lib/slickgrid/slick.core.js'); + wn.require('lib/js/lib/slickgrid/slick.grid.js'); + wn.require('lib/js/lib/slickgrid/slick.dataview.js'); + wn.dom.set_style('.slick-cell { font-size: 12px; }\ + .slick-headerrow-column {\ + background: #87ceeb;\ + text-overflow: clip;\ + -moz-box-sizing: border-box;\ + box-sizing: border-box;\ + }\ + .slick-headerrow-column input {\ + margin: 0;\ + padding: 0;\ + width: 100%;\ + height: 100%;\ + -moz-box-sizing: border-box;\ + box-sizing: border-box;}'); + }, + refresh: function() { + // Run + var me =this; + wn.call({ + method: "webnotes.widgets.query_report.run", + args: { + doctype: me.query_form.get_value("ref_doctype"), + query: me.query_form.get_value("query") + }, + callback: function(r) { + me.make_results(r.message.result, r.message.columns); + } + }) + }, + make_results: function(result, columns) { + this.make_columns(columns); + this.make_data(result, columns); + this.render(result, columns); + }, + render: function(result, columns) { + this.columnFilters = {}; + this.make_dataview(); + this.id = wn.dom.set_unique_id($(this.wrapper.find(".result-area")).get(0)); + + this.grid = new Slick.Grid("#"+this.id, this.dataView, this.columns, + this.slickgrid_options); + this.setup_header_row(); + this.grid.init(); + this.setup_sort(); + }, + make_columns: function(columns) { + this.columns = [{id: "_id", field: "_id", name: "Sr No", width: 60}] + .concat($.map(columns, function(c) { + var col = {name:c, id: c, field: c, sortable: true, width: 80} + + if(c.indexOf(":")!=-1) { + var opts = c.split(":"); + + // link + if(opts[1].substr(0,4)=="Link") { + col.doctype = opts[1].split('/')[1]; + col.formatter = function(row, cell, value, columnDef, dataContext) { + return repl('%(name)s', { + doctype: columnDef.doctype, + name: value + }); + } + } else if(opts[1]=="Date") { + col.formatter = function(row, cell, value, columnDef, dataContext) { + return dateutil.str_to_user(value); + }; + } else if(opts[1]=="Currency") { + col.formatter = function(row, cell, value, columnDef, dataContext) { + return repl('
%(value)s
', { + value: fmt_money(value) + }); + }; + } else if(opts[1]=="Float") { + col.formatter = function(row, cell, value, columnDef, dataContext) { + return repl('
%(value)s
', { + value: value.toFixed(6) + }); + }; + } else if(opts[1]=="Int") { + col.formatter = function(row, cell, value, columnDef, dataContext) { + return repl('
%(value)s
', { + value: parseInt(value) + }); + }; + } + + col.name = col.id = col.field = opts[0]; + col.fieldtype = opts[1]; + + // width + if(opts[2]) { + col.width=parseInt(opts[2]); + } + } + col.name = toTitle(col.name.replace(/ /g, " ")) + return col + })); + }, + make_data: function(result, columns) { + var me = this; + this.data = $.map(result, function(row, row_idx) { + var newrow = {}; + for(var i=1, j=me.columns.length; i") { + filter = filter.substr(1); + cond = ">" + } else if(filter[0]=="<") { + filter = filter.substr(1); + cond = "<" + } + + + if(in_list(['Float', 'Currency', 'Int', 'Date'], columnDef.fieldtype)) { + // non strings + if(filter.indexOf(":")==-1) { + if(columnDef.fieldtype=="Date") { + filter = dateutil.user_to_str(filter); + } + out = eval("value" + cond + "filter"); + } else { + // range + filter = filter.split(":"); + if(columnDef.fieldtype=="Date") { + filter[0] = dateutil.user_to_str(filter[0]); + filter[1] = dateutil.user_to_str(filter[1]); + } + out = value >= filter[0] && value <= filter[1]; + } + } else { + // string + value = value + ""; + value = value.toLowerCase(); + filter = filter.toLowerCase(); + out = value.indexOf(filter) != -1; + } + + if(invert) + return !out; + else + return out; + }, + setup_header_row: function() { + var me = this; + + $(this.grid.getHeaderRow()).delegate(":input", "change keyup", function (e) { + var columnId = $(this).data("columnId"); + if (columnId != null) { + me.columnFilters[columnId] = $.trim($(this).val()); + me.dataView.refresh(); + } + }); + + this.grid.onHeaderRowCellRendered.subscribe(function(e, args) { + $(args.node).empty(); + $("") + .data("columnId", args.column.id) + .val(me.columnFilters[args.column.id]) + .appendTo(args.node); + }); + }, + setup_sort: function() { + var me = this; + this.grid.onSort.subscribe(function (e, args) { + var cols = args.sortCols; + + me.data.sort(function (dataRow1, dataRow2) { + for (var i = 0, l = cols.length; i < l; i++) { + var field = cols[i].sortCol.field; + var sign = cols[i].sortAsc ? 1 : -1; + var value1 = dataRow1[field], value2 = dataRow2[field]; + var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign; + if (result != 0) { + return result; + } + } + return 0; + }); + me.dataView.beginUpdate(); + me.dataView.setItems(me.data); + me.dataView.endUpdate(); + me.dataView.refresh(); + }); + } +}) \ No newline at end of file diff --git a/public/build.json b/public/build.json index 41ce185415..f30119b261 100644 --- a/public/build.json +++ b/public/build.json @@ -141,7 +141,6 @@ "lib/public/js/wn/views/formview.js", "lib/public/js/wn/views/reportview.js", "lib/public/js/wn/views/grid_report.js", - "lib/public/js/wn/views/query_report.js", "lib/public/js/legacy/widgets/dialog.js", "lib/public/js/legacy/widgets/layout.js", "lib/public/js/legacy/widgets/tabbedpage.js", diff --git a/public/css/legacy/body.css b/public/css/legacy/body.css index 0aa2fc045d..6288d98d94 100644 --- a/public/css/legacy/body.css +++ b/public/css/legacy/body.css @@ -305,4 +305,8 @@ div.std-footer-item { .markdown h3, .markdown h4 { margin-bottom: 5px; +} + +.clear { + clear: both; } \ No newline at end of file diff --git a/public/js/wn/ui/dialog.js b/public/js/wn/ui/dialog.js index 2c79f2d0c0..737b025fac 100644 --- a/public/js/wn/ui/dialog.js +++ b/public/js/wn/ui/dialog.js @@ -86,6 +86,10 @@ wn.ui.FieldGroup = Class.extend({ } return ret; }, + get_value: function(key) { + var f = this.fields_dict[key]; + return f && (f.get_value ? f.get_value() : null); + }, set_value: function(key, val){ var f = this.fields_dict[key]; if(f) { diff --git a/public/js/wn/views/query_report.js b/public/js/wn/views/query_report.js deleted file mode 100644 index 807a9992ba..0000000000 --- a/public/js/wn/views/query_report.js +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) -// -// MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -wn.provide("wn.views"); - -wn.views.QueryReport = Class.extend({ - init: function(opts) { - $.extend(this, opts); - // globalify for slickgrid - this.appframe = this.parent.appframe; - this.parent.query_report = this; - this.make(); - }, - slickgrid_options: { - enableColumnReorder: false, - showHeaderRow: true, - headerRowHeight: 30, - explicitInitialization: true, - multiColumnSort: true - }, - make: function() { - this.wrapper = $("
").appendTo($(this.parent).find(".layout-main")); - $('\ -
').appendTo(this.wrapper); - - this.make_query_form(); - this.make_toolbar(); - this.import_slickgrid(); - }, - make_toolbar: function() { - var me = this; - this.appframe.add_button("Run", function() { - // Run - wn.call({ - method: "webnotes.widgets.query_report.run", - args: { - query: $(me.wrapper).find("textarea").val() - }, - callback: function(r) { - me.refresh(r.message.result, r.message.columns); - } - }) - }).addClass("btn-success"); - - if(in_list(user_roles, "System Manager")) { - // Edit - this.appframe.add_button("Edit", function() { - me.wrapper.find(".query-edit").slideToggle(); - }); - - // Save - this.appframe.add_button("Save", function() { - var doc = me.query_form.get_values(); - if(!doc) return; - doc.doctype = "Report" - wn.call({ - method:"webnotes.client.save", - args: { - doclist: [doc], - }, - callback: function(r) { - //wn.set_route("query-form", doc.name); - } - }) - }); - } - }, - make_query_form: function() { - this.query_form = new wn.ui.FieldGroup({ - parent: $(this.wrapper).find(".query-form").get(0), - fields: [ - {label:"Report Name", reqd: 1, fieldname:"name"}, - {label:"Based on", fieldtype:"Link", options:"DocType", - fieldname: "ref_doctype", - reqd:1, description:"Permissions will be based on this DocType"}, - {label:"Query", fieldtype: "Text", reqd: 1} - ] - }); - - $(this.query_form.fields_dict.query.input).css({ - width: "100%", - height: "300px", - "font-weight": "Normal", - "font-family": "Monaco, Courier, Fixed" - }); - }, - import_slickgrid: function() { - wn.require('lib/js/lib/slickgrid/slick.grid.css'); - wn.require('lib/js/lib/slickgrid/slick-default-theme.css'); - wn.require('lib/js/lib/slickgrid/jquery.event.drag.min.js'); - wn.require('lib/js/lib/slickgrid/slick.core.js'); - wn.require('lib/js/lib/slickgrid/slick.grid.js'); - wn.require('lib/js/lib/slickgrid/slick.dataview.js'); - wn.dom.set_style('.slick-cell { font-size: 12px; }\ - .slick-headerrow-column {\ - background: #87ceeb;\ - text-overflow: clip;\ - -moz-box-sizing: border-box;\ - box-sizing: border-box;\ - }\ - .slick-headerrow-column input {\ - margin: 0;\ - padding: 0;\ - width: 100%;\ - height: 100%;\ - -moz-box-sizing: border-box;\ - box-sizing: border-box;}'); - }, - refresh: function(result, columns) { - this.make_data(result, columns); - this.make_columns(columns); - this.render(result, columns); - }, - render: function(result, columns) { - this.columnFilters = {}; - this.make_dataview(); - this.id = wn.dom.set_unique_id($(this.wrapper.find(".result-area")).get(0)); - - this.grid = new Slick.Grid("#"+this.id, this.dataView, this.columns, - this.slickgrid_options); - this.setup_header_row(); - this.grid.init(); - this.setup_sort(); - }, - make_columns: function(columns) { - this.columns = [{id: "_id", field: "_id", name: "Sr No", width: 60}] - .concat($.map(columns, function(c) { - return {id: c, field: c, sortable: true, - name: toTitle(c.replace(/_/g, " ")) } - })); - }, - make_data: function(result, columns) { - this.data = $.map(result, function(row, row_idx) { - var newrow = {}; - $.each(columns, function(i, col) { - newrow[col] = row[i]; - }); - newrow._id = row_idx + 1; - newrow.id = newrow.name ? newrow.name : ("_" + newrow._id); - return newrow; - }); - }, - make_dataview: function() { - // initialize the model - this.dataView = new Slick.Data.DataView({ inlineFilters: true }); - this.dataView.beginUpdate(); - this.dataView.setItems(this.data); - this.dataView.setFilter(this.inline_filter); - this.dataView.endUpdate(); - - var me = this; - this.dataView.onRowCountChanged.subscribe(function (e, args) { - me.grid.updateRowCount(); - me.grid.render(); - }); - - this.dataView.onRowsChanged.subscribe(function (e, args) { - me.grid.invalidateRows(args.rows); - me.grid.render(); - }); - }, - inline_filter: function (item) { - var me = wn.container.page.query_report; - for (var columnId in me.columnFilters) { - if (columnId !== undefined && me.columnFilters[columnId] !== "") { - var c = me.grid.getColumns()[me.grid.getColumnIndex(columnId)]; - if (!me.compare_values(item[c.field], me.columnFilters[columnId])) { - return false; - } - } - } - return true; - }, - compare_values: function(value, filter) { - value = value + ""; - value = value.toLowerCase(); - filter = filter.toLowerCase(); - - if(filter.length < value.length) { - return filter==value.substr(0, filter.length) - } - }, - setup_header_row: function() { - var me = this; - - $(this.grid.getHeaderRow()).delegate(":input", "change keyup", function (e) { - var columnId = $(this).data("columnId"); - if (columnId != null) { - me.columnFilters[columnId] = $.trim($(this).val()); - me.dataView.refresh(); - } - }); - - this.grid.onHeaderRowCellRendered.subscribe(function(e, args) { - $(args.node).empty(); - $("") - .data("columnId", args.column.id) - .val(me.columnFilters[args.column.id]) - .appendTo(args.node); - }); - }, - setup_sort: function() { - var me = this; - this.grid.onSort.subscribe(function (e, args) { - var cols = args.sortCols; - - me.data.sort(function (dataRow1, dataRow2) { - for (var i = 0, l = cols.length; i < l; i++) { - var field = cols[i].sortCol.field; - var sign = cols[i].sortAsc ? 1 : -1; - var value1 = dataRow1[field], value2 = dataRow2[field]; - var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign; - if (result != 0) { - return result; - } - } - return 0; - }); - me.dataView.beginUpdate(); - me.dataView.setItems(me.data); - me.dataView.endUpdate(); - me.dataView.refresh(); - }); - } -}) \ No newline at end of file diff --git a/webnotes/client.py b/webnotes/client.py index 353c3ec515..3563a84f18 100644 --- a/webnotes/client.py +++ b/webnotes/client.py @@ -33,8 +33,8 @@ def save(): if not webnotes.has_permission(doclist[0]["doctype"], "write"): webnotes.msgprint("No Write Permission", raise_exception=True) - + doclistobj = DocList(doclist) doclistobj.save() - - webntoes.msgprint("%s: '%s' saved" % form.doctype, form.name) \ No newline at end of file + + return [d.fields for d in doclist] \ No newline at end of file diff --git a/webnotes/widgets/query_builder.py b/webnotes/widgets/query_builder.py index 7e2bb8ef65..5e7fa02d73 100644 --- a/webnotes/widgets/query_builder.py +++ b/webnotes/widgets/query_builder.py @@ -259,7 +259,7 @@ def runquery(q='', ret=0, from_export=0): q = q.replace('__user', session['user']) q = q.replace('__today', webnotes.utils.nowdate()) - res = sql(q, as_list=1, formatted=formatted, debug=1) + res = sql(q, as_list=1, formatted=formatted) colnames, coltypes, coloptions, colwidths = build_description_standard(meta, tl) diff --git a/webnotes/widgets/query_report.py b/webnotes/widgets/query_report.py index 00cbc833cf..3d332b2385 100644 --- a/webnotes/widgets/query_report.py +++ b/webnotes/widgets/query_report.py @@ -25,7 +25,20 @@ import webnotes @webnotes.whitelist() def run(): - query = webnotes.form_dict.query + globals().update(webnotes.form_dict) + + if not doctype: + webnotes.msgprint("Must specify DocType for permissions.", + raise_exception=1) + + if not ("tab" + doctype.lower()) in query.lower().split("from")[1].split("where")[0]: + webnotes.msgprint("Specified DocType must appear in query.", + raise_exception=1) + + if not webnotes.has_permission(doctype, "read"): + webnotes.msgprint("Must have read permission to access this report.", + raise_exception=1) + if not query.lower().startswith("select"): webnotes.msgprint("Query must be a SELECT", raise_exception=True)