diff --git a/erpnext/stock/doctype/stock_closing_entry/test_stock_closing_entry.py b/erpnext/stock/doctype/stock_closing_entry/test_stock_closing_entry.py index 3485e47030f..09670f0ff8c 100644 --- a/erpnext/stock/doctype/stock_closing_entry/test_stock_closing_entry.py +++ b/erpnext/stock/doctype/stock_closing_entry/test_stock_closing_entry.py @@ -1,6 +1,8 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt +from unittest.mock import patch + import frappe from frappe.utils import add_days, today @@ -57,3 +59,33 @@ class TestStockClosingEntry(ERPNextTestSuite): ).submit() self.last_closing_entry = entry.name return entry + + +class TestStockClosingEntryDuplicate(ERPNextTestSuite): + """validate_duplicate blocks a second submitted closing entry whose date range + overlaps an existing one for the same scope (company + warehouse/item filters).""" + + def make_closing(self, from_date, to_date, **fields): + doc = frappe.new_doc("Stock Closing Entry") + doc.company = COMPANY + doc.from_date = from_date + doc.to_date = to_date + doc.update(fields) + return doc + + def submit_closing(self, doc): + # the closing-balance build is enqueued on submit; skip it here + with patch("erpnext.stock.doctype.stock_closing_entry.stock_closing_entry.enqueue"): + doc.submit() + return doc + + def test_overlapping_range_is_rejected(self): + self.submit_closing(self.make_closing("2026-01-01", "2026-03-31")) + overlap = self.make_closing("2026-02-01", "2026-04-30") + self.assertRaises(frappe.ValidationError, overlap.insert) + + def test_non_overlapping_range_is_allowed(self): + self.submit_closing(self.make_closing("2026-01-01", "2026-03-31")) + later = self.make_closing("2026-04-01", "2026-06-30") + later.insert() + self.assertTrue(later.name)