From 2c6054d51a4fd77ac277a5737f5bed946cc0f66c Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 3 Feb 2026 11:33:56 +0530 Subject: [PATCH] fix(asset category): validate depreciation accounts when category has active depreciable assets --- .../doctype/asset_category/asset_category.py | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 16e564ad1bd..2edf71099af 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -31,7 +31,7 @@ class AssetCategory(Document): self.validate_finance_books() self.validate_account_types() self.validate_account_currency() - self.valide_cwip_account() + self.validate_accounts() def validate_finance_books(self): for d in self.finance_books: @@ -97,11 +97,21 @@ class AssetCategory(Document): title=_("Invalid Account"), ) - def valide_cwip_account(self): + def validate_accounts(self): + self.validate_duplicate_rows() + self.validate_cwip_accounts() + self.validate_depreciation_accounts() + + def validate_duplicate_rows(self): + companies = {row.company_name for row in self.accounts} + if len(companies) != len(self.accounts): + frappe.throw(_("Cannot set multiple account rows for the same company")) + + def validate_cwip_accounts(self): if self.enable_cwip_accounting: missing_cwip_accounts_for_company = [] for d in self.accounts: - if not d.capital_work_in_progress_account and not frappe.db.get_value( + if not d.capital_work_in_progress_account and not frappe.get_cached_value( "Company", d.company_name, "capital_work_in_progress_account" ): missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name)) @@ -115,6 +125,40 @@ class AssetCategory(Document): ) frappe.throw(msg, title=_("Missing Account")) + def validate_depreciation_accounts(self): + depreciation_account_map = { + "accumulated_depreciation_account": "Accumulated Depreciation Account", + "depreciation_expense_account": "Depreciation Expense Account", + } + missing_depreciation_account_msg = [] + for row in self.accounts: + if not has_depreciable_asset(self.name, row.company_name): + continue + default_accounts = frappe.get_cached_value( + "Company", + row.company_name, + ["accumulated_depreciation_account", "depreciation_expense_account"], + as_dict=True, + ) + for fieldname, label in depreciation_account_map.items(): + if not row.get(fieldname) and not default_accounts.get(fieldname): + missing_depreciation_account_msg.append( + _("Row #{0}: Missing {1} for company {2}.").format( + row.idx, + label, + get_link_to_form("Company", row.company_name), + ) + ) + if missing_depreciation_account_msg: + msg = _( + "Since there are active depreciable assets under this category, the following accounts are required.

" + ) + msg += _( + "You can either configure default depreciation accounts in the Company or set the required accounts in the following rows:

" + ) + msg += "
".join(missing_depreciation_account_msg) + frappe.throw(msg, title=_("Missing Accounts")) + def get_asset_category_account( fieldname, item=None, asset=None, account=None, asset_category=None, company=None @@ -138,3 +182,17 @@ def get_asset_category_account( ) return account + + +def has_depreciable_asset(asset_category, company): + return bool( + frappe.db.count( + "Asset", + { + "calculate_depreciation": 1, + "asset_category": asset_category, + "company": company, + "status": ["in", ("Submitted", "Partially Depreciated")], + }, + ) + )