Merge pull request #38969 from ruthra-kumar/bug_in_p_l_report
fix: incorrect net profit in Profit and Loss Statement
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 7355c4b..004a929 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -211,7 +211,13 @@
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 @@
) + 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 @@
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 002c05c..12a8554 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 @@
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")
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 0000000..b4423ab
--- /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)