From 359a347be2333d58570db4ac90f026dbbbaab608 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 5 Aug 2026 14:24:44 +0530 Subject: [PATCH] fix: do not accept scoped stock closing entries as period closing prerequisite --- .../period_closing_voucher.py | 21 ++++++++++++----- .../test_period_closing_voucher.py | 23 +++++++++++++++++++ .../stock_closing_entry.py | 4 +++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index c3965676eb1..f31fe980db0 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -19,6 +19,9 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( from erpnext.accounts.general_ledger import check_freezing_date, is_immutable_ledger_enabled from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.controllers.accounts_controller import AccountsController +from erpnext.stock.doctype.stock_closing_entry.stock_closing_entry import ( + SCOPE_FIELDS as STOCK_CLOSING_SCOPE_FIELDS, +) from erpnext.stock.utils import get_stock_value_on @@ -216,11 +219,7 @@ class PeriodClosingVoucher(AccountsController): return flt(balance[0][0]) if balance else 0.0 def validate_stock_closing_entry(self): - status = frappe.db.get_value( - "Stock Closing Entry", - {"company": self.company, "to_date": self.period_end_date, "docstatus": 1}, - "status", - ) + status = frappe.db.get_value("Stock Closing Entry", self.get_stock_closing_entry_filters(), "status") if status == "Completed": return @@ -235,11 +234,21 @@ class PeriodClosingVoucher(AccountsController): frappe.throw( _( - "Create a Stock Closing Entry with To Date as {0} before submitting the Period Closing Voucher." + "Create a Stock Closing Entry for the entire company with To Date as {0} before submitting the Period Closing Voucher." ).format(frappe.bold(formatdate(self.period_end_date))), title=_("Stock Closing Entry Required"), ) + def get_stock_closing_entry_filters(self): + filters = {"company": self.company, "to_date": self.period_end_date, "docstatus": 1} + + meta = frappe.get_meta("Stock Closing Entry") + for fieldname in STOCK_CLOSING_SCOPE_FIELDS: + if meta.has_field(fieldname): + filters[fieldname] = ("is", "not set") + + return filters + def on_submit(self): self.db_set("gle_processing_status", "In Progress") if frappe.get_single_value("Accounts Settings", "use_legacy_controller_for_pcv"): diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index 1ab598e2a1e..b7a9f9668c3 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -389,9 +389,24 @@ class TestPeriodClosingVoucher(ERPNextTestSuite): def test_stock_validations_before_period_closing(self): from unittest.mock import patch + from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + create_custom_fields( + { + "Stock Closing Entry": [ + { + "fieldname": "warehouse", + "label": "Warehouse", + "fieldtype": "Link", + "options": "Warehouse", + } + ] + } + ) + item = make_item("Test PCV Stock Item", {"is_stock_item": 1}) se = make_stock_entry( item_code=item.name, @@ -411,12 +426,20 @@ class TestPeriodClosingVoucher(ERPNextTestSuite): "company": "Test PCV Company", "from_date": pcv.period_start_date, "to_date": pcv.period_end_date, + "warehouse": "Stores - TPC", } ).insert() with patch("erpnext.stock.doctype.stock_closing_entry.stock_closing_entry.enqueue"): sce.submit() + sce.db_set("status", "Completed") + + pcv.reload() + self.assertRaisesRegex(frappe.ValidationError, "Create a Stock Closing Entry", pcv.submit) + + frappe.db.set_value("Stock Closing Entry", sce.name, {"warehouse": None, "status": "In Progress"}) + pcv.reload() self.assertRaisesRegex(frappe.ValidationError, "is not completed yet", pcv.submit) diff --git a/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py b/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py index 1f8450c87c4..019bec684a5 100644 --- a/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py +++ b/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py @@ -12,6 +12,8 @@ from frappe.utils.background_jobs import enqueue from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions +SCOPE_FIELDS = ("warehouse", "item_code", "item_group", "warehouse_type") + class StockClosingEntry(Document): # begin: auto-generated types @@ -66,7 +68,7 @@ class StockClosingEntry(Document): ) ) - for fieldname in ["warehouse", "item_code", "item_group", "warehouse_type"]: + for fieldname in SCOPE_FIELDS: if self.get(fieldname): query = query.where(table[fieldname] == self.get(fieldname))