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) {