diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 5dc862cc4cd..cdb16c89164 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -119,6 +119,7 @@ class Account(NestedSet): self.validate_account_currency() self.validate_root_company_and_sync_account_to_children() self.validate_receivable_payable_account_type() + self.validate_stock_account_type_change() def validate_parent_child_account_type(self): if self.parent_account: @@ -207,6 +208,36 @@ class Account(NestedSet): frappe.msgprint(msg) self.add_comment("Comment", msg) + def validate_stock_account_type_change(self): + doc_before_save = self.get_doc_before_save() + if not (doc_before_save and doc_before_save.account_type == "Stock"): + return + + if self.account_type == "Stock": + return + + if self.stock_ledger_entry_exists(): + frappe.throw( + _( + "The account type of {0} cannot be changed from {1} because stock ledger entries exist against it." + ).format(frappe.bold(self.name), frappe.bold(_("Stock"))) + ) + + def stock_ledger_entry_exists(self): + from erpnext.stock import get_warehouse_account_map + + warehouse_account = get_warehouse_account_map(self.company) + warehouses = [wh for wh, details in warehouse_account.items() if details.account == self.name] + if not warehouses: + return False + + return bool( + frappe.db.count( + "Stock Ledger Entry", + filters={"warehouse": ("in", warehouses), "is_cancelled": 0}, + ) + ) + def validate_root_details(self): doc_before_save = self.get_doc_before_save() diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 4cce4d2ee28..8106e1f7c5b 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -313,6 +313,31 @@ class TestAccount(unittest.TestCase): acc.account_currency = "USD" self.assertRaises(frappe.ValidationError, acc.save) + def test_stock_account_type_change_with_ledger_entries(self): + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + company = "_Test Company with perpetual inventory" + warehouse = "Stores - TCP1" + stock_account = get_warehouse_account(frappe.get_doc("Warehouse", warehouse)) + + make_stock_entry( + item_code="_Test Item", + target=warehouse, + company=company, + qty=5, + basic_rate=100, + ) + + account = frappe.get_doc("Account", stock_account) + self.assertEqual(account.account_type, "Stock") + + account.account_type = "" + self.assertRaises(frappe.ValidationError, account.save) + + account.reload() + account.account_name = f"{account.account_name} Updated" + account.save() # non-type change stays allowed + def test_account_balance(self): from erpnext.accounts.utils import get_balance_on