diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index aeb529e3f9..5fa87630aa 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -13,8 +13,8 @@ from frappe import _ def get(): args = get_form_params() args.save_list_settings = True - data = compress(execute(**args)) + data = compress(execute(**args)) return data def execute(doctype, *args, **kwargs): @@ -33,6 +33,7 @@ def get_form_params(): if isinstance(data.get("docstatus"), basestring): data["docstatus"] = json.loads(data["docstatus"]) + # queries must always be server side data.query = None @@ -54,7 +55,6 @@ def compress(data): "values": values } - @frappe.whitelist() def save_report(): """save report""" @@ -80,13 +80,22 @@ def export_query(): form_params["limit_page_length"] = None form_params["as_list"] = True doctype = form_params.doctype + add_totals_row = None + del form_params["doctype"] + if 'add_totals_row' in form_params and form_params['add_totals_row']=='1': + add_totals_row = 1 + del form_params["add_totals_row"] + frappe.permissions.can_export(doctype, raise_exception=True) db_query = DatabaseQuery(doctype) ret = db_query.execute(**form_params) + if add_totals_row: + ret = append_totals_row(ret) + data = [['Sr'] + get_labels(db_query.fields, doctype)] for i, row in enumerate(ret): data.append([i+1] + list(row)) @@ -106,6 +115,21 @@ def export_query(): frappe.response['type'] = 'csv' frappe.response['doctype'] = doctype +def append_totals_row(data): + if not data: + return data + data = list(data) + totals = [] + totals.extend([""]*len(data[0])) + + for row in data: + for i in xrange(len(row)): + if isinstance(row[i], (float, int)): + totals[i] = (totals[i] or 0) + row[i] + data.append(totals) + + return data + def get_labels(fields, doctype): """get column labels based on column names""" labels = [] diff --git a/frappe/public/js/frappe/views/reports/reportview.js b/frappe/public/js/frappe/views/reports/reportview.js index f054bc5c5d..cabf11c82c 100644 --- a/frappe/public/js/frappe/views/reports/reportview.js +++ b/frappe/public/js/frappe/views/reports/reportview.js @@ -23,6 +23,7 @@ frappe.views.ReportViewPage = Class.extend({ frappe.model.with_doctype(this.doctype, function() { me.make_report_view(); if(me.docname) { + frappe.model.with_doc('Report', me.docname, function(r) { me.parent.reportview.set_columns_and_filters( JSON.parse(frappe.get_doc("Report", me.docname).json)); @@ -87,7 +88,10 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ setup: function() { var me = this; + + this.add_totals_row = 0; this.page = this.parent.page; + this._body = $('
').appendTo(this.page.main); this.page_title = __('Report')+ ': ' + __(this.docname ? (this.doctype + ' - ' + this.docname) : this.doctype); this.page.set_title(this.page_title); this.init_list_settings(); @@ -96,15 +100,17 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ method: 'frappe.desk.reportview.get', save_list_settings: true, get_args: this.get_args, - parent: this.page.main, + parent: this._body, start: 0, show_filters: true, allow_delete: true, }); + this.make_new_and_refresh(); this.make_delete(); this.make_column_picker(); this.make_sorter(); + this.make_totals_row_button(); this.setup_print(); this.make_export(); this.setup_auto_email(); @@ -195,6 +201,8 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ // second sort if(opts.sort_by_next) this.sort_by_next_select.val(opts.sort_by_next); if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next); + + this.add_totals_row = opts.add_totals_row; }, set_route_filters: function(first_load) { @@ -233,7 +241,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ order_by: this.get_order_by(), filters: this.filter_list.get_filters(), save_list_settings_fields: 1, - with_childnames: 1 + with_childnames: 1, } }, @@ -331,6 +339,8 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ var me = this; var columns = this.get_columns(); + this.set_totals_row(); + // add sr in data $.each(this.data, function(i, v) { // add index @@ -497,9 +507,39 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ make_column_picker: function() { var me = this; this.column_picker = new frappe.ui.ColumnPicker(this); - this.page.add_menu_item(__('Pick Columns'), function() { + this.page.add_inner_button(__('Pick Columns'), function() { me.column_picker.show(me.columns); - }, true); + }); + }, + + make_totals_row_button: function() { + var me = this; + + this.page.add_inner_button(__('Show Totals'), function() { + me.add_totals_row = 1 - me.add_totals_row; + me.render_list(); + }); + }, + + set_totals_row: function() { + // remove existing totals row + if(this.data.length && this.data[this.data.length-1]._totals_row) { + this.data.pop(); + } + + if(this.add_totals_row) { + var totals_row = {_totals_row: 1}; + if(this.data.length) { + this.data.forEach(function(row, ri) { + $.each(row, function(key, value) { + if($.isNumeric(value)) { + totals_row[key] = (totals_row[key] || 0) + value; + } + }); + }); + } + this.data.push(totals_row); + } }, set_tag_and_status_filter: function() { @@ -560,9 +600,9 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ this.sort_order_next_select.val('desc'); // button actions - this.page.add_menu_item(__('Sort By'), function() { + this.page.add_inner_button(__('Set Sort'), function() { me.sort_dialog.show(); - }, true); + }); $(this.sort_dialog.body).find('.btn-primary').click(function() { me.sort_dialog.hide(); @@ -579,6 +619,9 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ var export_btn = this.page.add_menu_item(__('Export'), function() { var args = me.get_args(); args.cmd = 'frappe.desk.reportview.export_query' + if(me.add_totals_row) { + args.add_totals_row = 1; + } open_url_post(frappe.request.url, args); }, true); }, @@ -609,7 +652,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({ sort_by: me.sort_by_select.val(), sort_order: me.sort_order_select.val(), sort_by_next: me.sort_by_next_select.val(), - sort_order_next: me.sort_order_next_select.val() + sort_order_next: me.sort_order_next_select.val(), }) }, callback: function(r) {