From 5537e41a223ddc2654d007dc0286a69c61edeee5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 14 Jun 2026 20:06:32 +0530 Subject: [PATCH] fix: use net fixed assets for Fixed Asset Turnover Ratio The Fixed Asset Turnover Ratio in the Financial Ratios report divided Net Sales by Total Assets (the root-level Asset group), which actually computes the Total Asset Turnover Ratio. Populate a `fixed_asset` balance from the asset account carrying the `Fixed Asset` account_type (mirroring how `current_asset` is derived for `Current Asset`) and use it as the denominator, so the ratio reflects Net Sales / Net Fixed Assets per the standard definition. Fixes #54529 (cherry picked from commit 986af3852c301cad2a618f4015f796dd682be07c) --- .../financial_ratios/financial_ratios.py | 8 +- .../financial_ratios/test_financial_ratios.py | 89 +++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 erpnext/accounts/report/financial_ratios/test_financial_ratios.py diff --git a/erpnext/accounts/report/financial_ratios/financial_ratios.py b/erpnext/accounts/report/financial_ratios/financial_ratios.py index 48047c81944..0ec7e4a0bc5 100644 --- a/erpnext/accounts/report/financial_ratios/financial_ratios.py +++ b/erpnext/accounts/report/financial_ratios/financial_ratios.py @@ -71,6 +71,7 @@ def get_ratios_data(filters, period_list, years): assets, liabilities, income, expense = get_gl_data(filters, period_list, years) current_asset, total_asset = {}, {} + fixed_asset = {} current_liability, total_liability = {}, {} net_sales, total_income = {}, {} cogs, total_expense = {}, {} @@ -93,6 +94,7 @@ def get_ratios_data(filters, period_list, years): quick_asset, total_quick_asset, ], + [fixed_asset, total_asset, "Fixed Asset", year, assets, "Asset", {}, 0], [ current_liability, total_liability, @@ -112,7 +114,7 @@ def get_ratios_data(filters, period_list, years): add_solvency_ratios( data, years, total_asset, total_liability, net_sales, cogs, total_income, total_expense ) - add_turnover_ratios(data, years, period_list, filters, total_asset, net_sales, cogs, direct_expense) + add_turnover_ratios(data, years, period_list, filters, fixed_asset, net_sales, cogs, direct_expense) return data @@ -193,7 +195,7 @@ def add_solvency_ratios( data.append(return_on_equity_ratio) -def add_turnover_ratios(data, years, period_list, filters, total_asset, net_sales, cogs, direct_expense): +def add_turnover_ratios(data, years, period_list, filters, fixed_asset, net_sales, cogs, direct_expense): precision = frappe.db.get_single_value("System Settings", "float_precision") data.append({"ratio": _("Turnover Ratios")}) @@ -208,7 +210,7 @@ def add_turnover_ratios(data, years, period_list, filters, total_asset, net_sale ) ratio_data = [ - [_("Fixed Asset Turnover Ratio"), net_sales, total_asset], + [_("Fixed Asset Turnover Ratio"), net_sales, fixed_asset], [_("Debtor Turnover Ratio"), net_sales, avg_debtors], [_("Creditor Turnover Ratio"), direct_expense, avg_creditors], [_("Inventory Turnover Ratio"), cogs, avg_stock], diff --git a/erpnext/accounts/report/financial_ratios/test_financial_ratios.py b/erpnext/accounts/report/financial_ratios/test_financial_ratios.py new file mode 100644 index 00000000000..0a34fd8409e --- /dev/null +++ b/erpnext/accounts/report/financial_ratios/test_financial_ratios.py @@ -0,0 +1,89 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe +from frappe.utils import today + +from erpnext.accounts.report.financial_ratios.financial_ratios import execute +from erpnext.tests.utils import ERPNextTestSuite + +COMPANY = "_Test Company Financial Ratios" +ABBR = "_TCFR" + + +class TestFinancialRatios(ERPNextTestSuite): + def setUp(self): + self.create_company() + # The report matches the group accounts by their account_type, which the + # standard chart of accounts does not set on group accounts by default. + self.set_account_type("Fixed Assets", "Fixed Asset") + self.set_account_type("Direct Income", "Direct Income") + + def create_company(self): + if frappe.db.exists("Company", COMPANY): + return + frappe.get_doc( + { + "doctype": "Company", + "company_name": COMPANY, + "abbr": ABBR, + "country": "India", + "default_currency": "INR", + "create_chart_of_accounts_based_on": "Standard Template", + "chart_of_accounts": "Standard", + } + ).insert() + + def set_account_type(self, account_name, account_type): + frappe.db.set_value("Account", f"{account_name} - {ABBR}", "account_type", account_type) + + def test_fixed_asset_turnover_uses_net_fixed_assets(self): + # Acquire a fixed asset worth 10,000 funded by equity. + self.make_journal_entry("Buildings", "Capital Stock", 10000) + # Book sales of 20,000 collected in cash. Total assets now = 30,000 + # (Buildings 10,000 + Cash 20,000), while net fixed assets stay at 10,000. + self.make_journal_entry("Cash", "Sales", 20000) + + columns, data = execute(self.get_report_filters()) + year_key = columns[1]["fieldname"] + ratio_row = next(row for row in data if row.get("ratio") == "Fixed Asset Turnover Ratio") + + # Net Sales / Net Fixed Assets = 20,000 / 10,000 = 2.0 + # (the old behaviour divided by total assets, giving 20,000 / 30,000 = 0.667) + self.assertEqual(ratio_row[year_key], 2.0) + + def get_report_filters(self): + active_fy = frappe.db.get_value( + "Fiscal Year", + {"disabled": 0, "year_start_date": ("<=", today()), "year_end_date": (">=", today())}, + ["name", "year_start_date", "year_end_date"], + as_dict=True, + ) + return frappe._dict( + company=COMPANY, + from_fiscal_year=active_fy.name, + to_fiscal_year=active_fy.name, + period_start_date=active_fy.year_start_date, + period_end_date=active_fy.year_end_date, + filter_based_on="Fiscal Year", + periodicity="Yearly", + ) + + def make_journal_entry(self, debit_account, credit_account, amount): + journal_entry = frappe.new_doc("Journal Entry") + journal_entry.posting_date = today() + journal_entry.company = COMPANY + for account, debit, credit in ( + (debit_account, amount, 0), + (credit_account, 0, amount), + ): + journal_entry.append( + "accounts", + { + "account": f"{account} - {ABBR}", + "debit_in_account_currency": debit, + "credit_in_account_currency": credit, + }, + ) + journal_entry.insert() + journal_entry.submit()