Compare commits

...

2 Commits

Author SHA1 Message Date
Nabin Hait
d0d75a2ab5 fix: minor fix
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
(cherry picked from commit 0e7d45b1af)
2026-06-16 09:44:14 +00:00
Nabin Hait
5537e41a22 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 986af3852c)
2026-06-16 09:44:13 +00:00
2 changed files with 95 additions and 3 deletions

View File

@@ -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],

View File

@@ -0,0 +1,90 @@
# 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"), None)
self.assertIsNotNone(ratio_row, "Fixed Asset Turnover Ratio row not found in report output")
# 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()