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"));
+ $('
\
+
\
+
\
+
Column Rules: "label:datatype:width" \
+
\
+ "datatype" and "width" are optional.\
+ "datatype" can be "Link", "Date", "Float", "Currency".\
+ For Links, use define linked Doctype as "Link/Customer". \
+ Example: "Customer:Link/Customer:120"\
+ \
+
\
+
\
+
\
+
\
+
\
+ 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"));
- $('
\
-
\
-
\
-
Query Rules: \
-
\
- Define column names as JSON objects to specifiy types and links:\
- Labels are automatically defined from column headings.\
- To define for doctype, set `{"label": "Customer", "type": "Link", "options":"Customer"}`\
- For DataTypes, use suffix: eg. `{"label":"Qty", "type":"Float"}`\
- For Links, use suffix "link": eg. `customer:float`\
- \
-
\
-
\
-
\
-
').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)