diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 5a22304f32..bf63afa5c5 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -3,7 +3,7 @@ import frappe, json, os import unittest -from frappe.desk.query_report import run, save_report +from frappe.desk.query_report import run, save_report, add_total_row from frappe.desk.reportview import delete_report, save_report as _save_report from frappe.custom.doctype.customize_form.customize_form import reset_customization from frappe.core.doctype.user_permission.test_user_permission import create_user @@ -282,3 +282,55 @@ result = [ # Set user back to administrator frappe.set_user('Administrator') + + def test_add_total_row_for_tree_reports(self): + report_settings = { + 'tree': True, + 'parent_field': 'parent_value' + } + + columns = [ + { + "fieldname": "parent_column", + "label": "Parent Column", + "fieldtype": "Data", + "width": 10 + }, + { + "fieldname": "column_1", + "label": "Column 1", + "fieldtype": "Float", + "width": 10 + }, + { + "fieldname": "column_2", + "label": "Column 2", + "fieldtype": "Float", + "width": 10 + } + ] + + result = [ + { + "parent_column": "Parent 1", + "column_1": 200, + "column_2": 150.50 + }, + { + "parent_column": "Child 1", + "column_1": 100, + "column_2": 75.25, + "parent_value": "Parent 1" + }, + { + "parent_column": "Child 2", + "column_1": 100, + "column_2": 75.25, + "parent_value": "Parent 1" + } + ] + + result = add_total_row(result, columns, meta=None, report_settings=report_settings) + self.assertEqual(result[-1][0], "Total") + self.assertEqual(result[-1][1], 200) + self.assertEqual(result[-1][2], 150.50) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 97bceeb725..9ed956e986 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -73,7 +73,7 @@ def get_report_result(report, filters): return res @frappe.read_only() -def generate_report_result(report, filters=None, user=None, custom_columns=None): +def generate_report_result(report, filters=None, user=None, custom_columns=None, report_settings=None): user = user or frappe.session.user filters = filters or [] @@ -108,7 +108,7 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None) result = get_filtered_data(report.ref_doctype, columns, result, user) if cint(report.add_total_row) and result and not skip_total_row: - result = add_total_row(result, columns) + result = add_total_row(result, columns, report_settings=report_settings) return { "result": result, @@ -210,7 +210,7 @@ def get_script(report_name): @frappe.whitelist() @frappe.read_only() -def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None): +def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None, report_settings=None): report = get_report_doc(report_name) if not user: user = frappe.session.user @@ -238,7 +238,7 @@ def run(report_name, filters=None, user=None, ignore_prepared_report=False, cust dn = "" result = get_prepared_report_result(report, filters, dn, user) else: - result = generate_report_result(report, filters, user, custom_columns) + result = generate_report_result(report, filters, user, custom_columns, report_settings) result["add_total_row"] = report.add_total_row and not result.get( "skip_total_row", False @@ -435,9 +435,19 @@ def build_xlsx_data(columns, data, visible_idx, include_indentation, ignore_visi return result, column_widths -def add_total_row(result, columns, meta=None): +def add_total_row(result, columns, meta=None, report_settings=None): total_row = [""] * len(columns) has_percent = [] + is_tree = False + parent_field = '' + + if report_settings: + if isinstance(report_settings, (str,)): + report_settings = json.loads(report_settings) + + is_tree = report_settings.get('tree') + parent_field = report_settings.get('parent_field') + for i, col in enumerate(columns): fieldtype, options, fieldname = None, None, None if isinstance(col, str): @@ -464,12 +474,12 @@ def add_total_row(result, columns, meta=None): for row in result: if i >= len(row): continue - cell = row.get(fieldname) if isinstance(row, dict) else row[i] if fieldtype in ["Currency", "Int", "Float", "Percent", "Duration"] and flt( cell ): - total_row[i] = flt(total_row[i]) + flt(cell) + if not (is_tree and row.get(parent_field)): + total_row[i] = flt(total_row[i]) + flt(cell) if fieldtype == "Percent" and i not in has_percent: has_percent.append(i) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 7ba0a0228f..c226a3a458 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -578,6 +578,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { args: { report_name: this.report_name, filters: filters, + report_settings: this.report_settings }, callback: resolve, always: () => this.page.btn_secondary.prop('disabled', false) @@ -834,7 +835,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { let data = this.data; let columns = this.columns.filter((col) => !col.hidden); - if (this.raw_data.add_total_row) { + if (this.raw_data.add_total_row && !this.report_settings.tree) { data = data.slice(); data.splice(-1, 1); } @@ -854,7 +855,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { treeView: this.tree_report, layout: 'fixed', cellHeight: 33, - showTotalRow: this.raw_data.add_total_row, + showTotalRow: this.raw_data.add_total_row && !this.report_settings.tree, direction: frappe.utils.is_rtl() ? 'rtl' : 'ltr', hooks: { columnTotal: frappe.utils.report_column_total