From f6639db0e95d9769a8c13768e131379c7a1e3c8c Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhodawala <99460106+Abdeali099@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:34:37 +0530 Subject: [PATCH] feat: enhance account category with root type (#53190) --- erpnext/accounts/doctype/account/account.js | 22 +++++++++++-- .../account_category/account_category.json | 19 +++++++++-- .../account_category/account_category.py | 1 + .../account_categories.json | 31 +++++++++++++++++- erpnext/patches.txt | 1 + .../set_root_type_in_account_categories.py | 32 +++++++++++++++++++ 6 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 erpnext/patches/v16_0/set_root_type_in_account_categories.py diff --git a/erpnext/accounts/doctype/account/account.js b/erpnext/accounts/doctype/account/account.js index ff44723afee..ae5d0e35523 100644 --- a/erpnext/accounts/doctype/account/account.js +++ b/erpnext/accounts/doctype/account/account.js @@ -5,8 +5,7 @@ frappe.ui.form.on("Account", { setup: function (frm) { frm.add_fetch("parent_account", "report_type", "report_type"); frm.add_fetch("parent_account", "root_type", "root_type"); - }, - onload: function (frm) { + frm.set_query("parent_account", function (doc) { return { filters: { @@ -15,7 +14,18 @@ frappe.ui.form.on("Account", { }, }; }); + + frm.set_query("account_category", function () { + if (!frm.doc.root_type) return; + + return { + filters: { + root_type: ["in", [frm.doc.root_type, ""]], + }, + }; + }); }, + refresh: function (frm) { frm.toggle_display("account_name", frm.is_new()); @@ -58,12 +68,20 @@ frappe.ui.form.on("Account", { } } }, + account_type: function (frm) { if (frm.doc.is_group == 0) { frm.toggle_display(["tax_rate"], frm.doc.account_type == "Tax"); frm.toggle_display("warehouse", frm.doc.account_type == "Stock"); } }, + + root_type: function (frm) { + if (frm.doc.account_category) { + frm.set_value("account_category", ""); + } + }, + add_toolbar_buttons: function (frm) { frm.add_custom_button( __("Chart of Accounts"), diff --git a/erpnext/accounts/doctype/account_category/account_category.json b/erpnext/accounts/doctype/account_category/account_category.json index cc8f4103f21..694ac06b082 100644 --- a/erpnext/accounts/doctype/account_category/account_category.json +++ b/erpnext/accounts/doctype/account_category/account_category.json @@ -7,6 +7,8 @@ "engine": "InnoDB", "field_order": [ "account_category_name", + "root_type", + "column_break_qluu", "description" ], "fields": [ @@ -14,6 +16,7 @@ "fieldname": "account_category_name", "fieldtype": "Data", "in_list_view": 1, + "in_standard_filter": 1, "label": "Account Category Name", "reqd": 1, "unique": 1 @@ -22,6 +25,18 @@ "fieldname": "description", "fieldtype": "Small Text", "label": "Description" + }, + { + "fieldname": "column_break_qluu", + "fieldtype": "Column Break" + }, + { + "fieldname": "root_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Root Type", + "options": "\nAsset\nLiability\nIncome\nExpense\nEquity" } ], "grid_page_length": 50, @@ -32,7 +47,7 @@ "link_fieldname": "account_category" } ], - "modified": "2026-02-23 01:19:49.589393", + "modified": "2026-03-05 06:49:34.430723", "modified_by": "Administrator", "module": "Accounts", "name": "Account Category", @@ -69,7 +84,7 @@ } ], "row_format": "Dynamic", - "search_fields": "account_category_name, description", + "search_fields": "account_category_name, root_type", "sort_field": "creation", "sort_order": "DESC", "states": [] diff --git a/erpnext/accounts/doctype/account_category/account_category.py b/erpnext/accounts/doctype/account_category/account_category.py index 8be84d0f8e2..f11ac18969e 100644 --- a/erpnext/accounts/doctype/account_category/account_category.py +++ b/erpnext/accounts/doctype/account_category/account_category.py @@ -21,6 +21,7 @@ class AccountCategory(Document): account_category_name: DF.Data description: DF.SmallText | None + root_type: DF.Literal["", "Asset", "Liability", "Income", "Expense", "Equity"] # end: auto-generated types def after_rename(self, old_name, new_name, merge): diff --git a/erpnext/accounts/financial_report_template/account_categories.json b/erpnext/accounts/financial_report_template/account_categories.json index f9af2698f10..37eb7baf3cc 100644 --- a/erpnext/accounts/financial_report_template/account_categories.json +++ b/erpnext/accounts/financial_report_template/account_categories.json @@ -1,118 +1,147 @@ [ { "account_category_name": "Cash and Cash Equivalents", + "root_type": "Asset", "description": "Cash on hand, demand deposits, and short-term highly liquid investments readily convertible to cash with original maturities of three months or less. Examples: Cash in hand, bank current accounts, money market funds, treasury bills \u22643 months." }, { "account_category_name": "Cost of Goods Sold", + "root_type": "Expense", "description": "Direct costs attributable to cost of goods sold. Examples: Raw materials, stock in trade." }, { "account_category_name": "Current Tax Liabilities", + "root_type": "Liability", "description": "Income tax obligations for current and prior periods. Examples: Provision for income tax, advance tax paid, tax deducted at source." }, { "account_category_name": "Finance Costs", + "root_type": "Expense", "description": "Interest and financing-related expenses. Examples: Interest on borrowings, bank charges, lease interest, foreign exchange losses." }, { "account_category_name": "Intangible Assets", + "root_type": "Asset", "description": "Identifiable non-monetary assets without physical substance. Examples: Software, patents, trademarks, licenses, development costs." }, { "account_category_name": "Investment Income", + "root_type": "Income", "description": "Returns generated from financial investments and cash management. Examples: Interest income, dividend income, rental income, fair value gains." }, { "account_category_name": "Long-term Borrowings", + "root_type": "Liability", "description": "Interest-bearing debt obligations with maturity beyond one year. Examples: Term loans, bonds, debentures, mortgages." }, { "account_category_name": "Long-term Investments", + "root_type": "Asset", "description": "Investments held for strategic purposes or extended periods. Examples: Equity investments, bonds, associates, joint ventures, deposits." }, { "account_category_name": "Long-term Provisions", + "root_type": "Liability", "description": "Present obligations beyond one year with uncertain timing/amount. Examples: Asset retirement obligations, environmental remediation, legal settlements." }, { "account_category_name": "Operating Expenses", + "root_type": "Expense", "description": "Costs incurred in ordinary business operations excluding direct costs. Examples: Selling expenses, administrative costs, marketing, utilities, rent." }, { "account_category_name": "Other Current Assets", + "root_type": "Asset", "description": "Current assets not classified elsewhere including prepaid expenses and advances. Examples: Prepaid insurance, prepaid rent, advance to suppliers, security deposits recoverable within one year." }, { "account_category_name": "Other Current Liabilities", + "root_type": "Liability", "description": "Short-term obligations not classified elsewhere. Examples: Accrued expenses, statutory liabilities, employee payables." }, { "account_category_name": "Other Direct Costs", + "root_type": "Expense", "description": "Direct costs excluding cost of goods sold. Examples: Direct labor, manufacturing overhead, freight inward." }, { "account_category_name": "Other Non-current Assets", + "root_type": "Asset", "description": "Long-term assets not classified elsewhere. Examples: Security deposits, long-term prepayments, advances for capital goods." }, { "account_category_name": "Other Non-current Liabilities", + "root_type": "Liability", "description": "Long-term obligations not classified elsewhere. Examples: Long-term deposits, deferred income, government grants." }, { "account_category_name": "Other Operating Income", + "root_type": "Income", "description": "Incidental income related to business operations but not core revenue. Examples: Scrap sales, government grants, insurance claims, foreign exchange gains." }, { "account_category_name": "Other Payables", + "root_type": "Liability", "description": "Non-trade payables and obligations to parties other than suppliers. Examples: Employee payables, accrued expenses, customer advances, security deposits received." }, { "account_category_name": "Other Receivables", + "root_type": "Asset", "description": "Non-trade amounts due to the entity excluding financing arrangements. Examples: Employee advances, insurance claims, tax refunds, deposits recoverable." }, { "account_category_name": "Reserves and Surplus", + "root_type": "Equity", "description": "Accumulated profits and other reserves created from profits or share premium. Examples: General reserves, retained earnings, statutory reserves, share premium." }, { "account_category_name": "Revenue from Operations", + "root_type": "Income", "description": "Income from primary business activities in ordinary course. Examples: Sales of goods, service revenue, commission income, royalty income." }, { "account_category_name": "Share Capital", + "root_type": "Equity", "description": "Nominal value of issued and paid-up equity shares. Examples: Common stock, ordinary shares, preference shares." }, { "account_category_name": "Short-term Borrowings", + "root_type": "Liability", "description": "Interest-bearing debt obligations due within one year. Examples: Bank overdrafts, short-term loans, current portion of long-term debt." }, { "account_category_name": "Short-term Investments", + "root_type": "Asset", "description": "Financial instruments held for short-term investment purposes, readily convertible to cash. Examples: Marketable securities, fixed deposits >3 months, mutual funds." }, { "account_category_name": "Short-term Provisions", + "root_type": "Liability", "description": "Present obligations due within one year with uncertain timing or amount. Examples: Warranty provisions, legal claims, restructuring costs." }, { "account_category_name": "Stock Assets", + "root_type": "Asset", "description": "Inventory and stock-related assets including raw materials, work in progress, finished goods, and stock in trade. Examples: Raw materials, finished goods, trading merchandise, consumables." }, { "account_category_name": "Tangible Assets", + "root_type": "Asset", "description": "Physical assets used in business operations including property, plant, and equipment. Examples: Land, buildings, machinery, equipment, vehicles, furniture, capital work in progress." }, { "account_category_name": "Tax Expense", + "root_type": "Expense", "description": "Current and deferred income tax obligations. Examples: Current tax provision, deferred tax expense, withholding taxes." }, { "account_category_name": "Trade Payables", + "root_type": "Liability", "description": "Amounts owed to suppliers. Examples: Supplier invoices, accrued purchases, bills payable." }, { "account_category_name": "Trade Receivables", + "root_type": "Asset", "description": "Amounts due from customers for goods sold or services provided in ordinary course of business. Examples: Accounts receivable, notes receivable from customers, unbilled revenue." } -] \ No newline at end of file +] diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 125ade6ee73..54cacf764bd 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -477,3 +477,4 @@ erpnext.patches.v16_0.remove_payables_receivables_workspace erpnext.patches.v16_0.depends_on_inv_dimensions erpnext.patches.v16_0.uom_category erpnext.patches.v16_0.merge_repost_settings_to_accounts_settings +erpnext.patches.v16_0.set_root_type_in_account_categories diff --git a/erpnext/patches/v16_0/set_root_type_in_account_categories.py b/erpnext/patches/v16_0/set_root_type_in_account_categories.py new file mode 100644 index 00000000000..44eb6678d06 --- /dev/null +++ b/erpnext/patches/v16_0/set_root_type_in_account_categories.py @@ -0,0 +1,32 @@ +import json +from pathlib import Path + +import frappe + + +def execute(): + base_path = Path(frappe.get_app_path("erpnext", "accounts")).resolve() + categories_file = (base_path / "financial_report_template" / "account_categories.json").resolve() + + if not categories_file.exists(): + return + + categories = json.loads(frappe.read_file(str(categories_file))) + + valid_root_types = set(frappe.get_meta("Account Category").get_field("root_type").options.split("\n")) + + root_type_categories = {} + for category in categories: + if (root_type := category.get("root_type")) and root_type in valid_root_types: + root_type_categories.setdefault(root_type, []).append(category["account_category_name"]) + + if not root_type_categories: + return + + for root_type, category_names in root_type_categories.items(): + frappe.db.set_value( + "Account Category", + {"name": ["in", category_names], "root_type": ["is", "not set"]}, + "root_type", + root_type, + )