From 7c7c3c932fb8b8942d87b785f37e991c6c83818c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 27 Dec 2023 16:32:57 +0530 Subject: [PATCH 1/2] fix: incorrect total when Accumulating values (cherry picked from commit d54f8318fba2b60eaad4a93a811a5b569c3344dc) --- .../accounts/report/financial_statements.py | 20 ++++++++++++++---- .../profit_and_loss_statement.py | 21 ++++++++++++++----- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 7355c4b8a1..004a9299ea 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -211,7 +211,13 @@ def get_data( ignore_accumulated_values_for_fy, ) accumulate_values_into_parents(accounts, accounts_by_name, period_list) - out = prepare_data(accounts, balance_must_be, period_list, company_currency) + out = prepare_data( + accounts, + balance_must_be, + period_list, + company_currency, + accumulated_values=filters.accumulated_values, + ) out = filter_out_zero_value_rows(out, parent_children_map) if out and total: @@ -270,7 +276,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name, period_list): ) + d.get("opening_balance", 0.0) -def prepare_data(accounts, balance_must_be, period_list, company_currency): +def prepare_data(accounts, balance_must_be, period_list, company_currency, accumulated_values): data = [] year_start_date = period_list[0]["year_start_date"].strftime("%Y-%m-%d") year_end_date = period_list[-1]["year_end_date"].strftime("%Y-%m-%d") @@ -310,8 +316,14 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency): has_value = True total += flt(row[period.key]) - row["has_value"] = has_value - row["total"] = total + if accumulated_values: + # when 'accumulated_values' is enabled, periods have running balance. + # so, last period will have the net amount. + row["has_value"] = has_value + row["total"] = flt(d.get(period_list[-1].key, 0.0), 3) + else: + row["has_value"] = has_value + row["total"] = total data.append(row) return data diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 66353358a0..0b7ce51891 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -82,14 +82,25 @@ def get_report_summary( if filters.get("accumulated_in_group_company"): period_list = get_filtered_list_for_consolidated_report(filters, period_list) - for period in period_list: - key = period if consolidated else period.key + if filters.accumulated_values: + # when 'accumulated_values' is enabled, periods have running balance. + # so, last period will have the net amount. + key = period_list[-1].key if income: - net_income += income[-2].get(key) + net_income = income[-2].get(key) if expense: - net_expense += expense[-2].get(key) + net_expense = expense[-2].get(key) if net_profit_loss: - net_profit += net_profit_loss.get(key) + net_profit = net_profit_loss.get(key) + else: + for period in period_list: + key = period if consolidated else period.key + if income: + net_income += income[-2].get(key) + if expense: + net_expense += expense[-2].get(key) + if net_profit_loss: + net_profit += net_profit_loss.get(key) if len(period_list) == 1 and periodicity == "Yearly": profit_label = _("Profit This Year") From a3cecb892af373a846722ebd10fd61a451e4d6de Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 28 Dec 2023 11:39:55 +0530 Subject: [PATCH 2/2] test: profit and loss report output (cherry picked from commit 855561729544770d77106bc1e4e4a964884377ea) --- .../test_profit_and_loss_statement.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 erpnext/accounts/report/profit_and_loss_statement/test_profit_and_loss_statement.py diff --git a/erpnext/accounts/report/profit_and_loss_statement/test_profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/test_profit_and_loss_statement.py new file mode 100644 index 0000000000..b4423abc7f --- /dev/null +++ b/erpnext/accounts/report/profit_and_loss_statement/test_profit_and_loss_statement.py @@ -0,0 +1,94 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, getdate, today + +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import execute +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin + + +class TestProfitAndLossStatement(AccountsTestMixin, FrappeTestCase): + def setUp(self): + self.create_company() + self.create_customer() + self.create_item() + + def tearDown(self): + frappe.db.rollback() + + def create_sales_invoice(self, qty=1, rate=150, no_payment_schedule=False, do_not_submit=False): + frappe.set_user("Administrator") + si = create_sales_invoice( + item=self.item, + company=self.company, + customer=self.customer, + debit_to=self.debit_to, + posting_date=today(), + parent_cost_center=self.cost_center, + cost_center=self.cost_center, + rate=rate, + price_list_rate=rate, + qty=qty, + do_not_save=1, + ) + si = si.save() + if not do_not_submit: + si = si.submit() + return si + + def get_fiscal_year(self): + active_fy = frappe.db.get_all( + "Fiscal Year", + filters={"disabled": 0, "year_start_date": ("<=", today()), "year_end_date": (">=", today())}, + )[0] + return frappe.get_doc("Fiscal Year", active_fy.name) + + def get_report_filters(self): + fy = self.get_fiscal_year() + return frappe._dict( + company=self.company, + from_fiscal_year=fy.name, + to_fiscal_year=fy.name, + period_start_date=fy.year_start_date, + period_end_date=fy.year_end_date, + filter_based_on="Fiscal Year", + periodicity="Monthly", + accumulated_vallues=True, + ) + + def test_profit_and_loss_output_and_summary(self): + si = self.create_sales_invoice(qty=1, rate=150) + + filters = self.get_report_filters() + period_list = get_period_list( + filters.from_fiscal_year, + filters.to_fiscal_year, + filters.period_start_date, + filters.period_end_date, + filters.filter_based_on, + filters.periodicity, + company=filters.company, + ) + + result = execute(filters)[1] + current_period = [x for x in period_list if x.from_date <= getdate() and x.to_date >= getdate()][ + 0 + ] + current_period_key = current_period.key + without_current_period = [x for x in period_list if x.key != current_period.key] + # all period except current period(whence invoice was posted), should be '0' + for acc in result: + if acc: + with self.subTest(acc=acc): + for period in without_current_period: + self.assertEqual(acc[period.key], 0) + + for acc in result: + if acc: + with self.subTest(current_period_key=current_period_key): + self.assertEqual(acc[current_period_key], 150) + self.assertEqual(acc["total"], 150)