From 10c6cda6db4bbfb0e8a9b276f41498a42caeb88d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 4 Jul 2026 17:47:22 +0530 Subject: [PATCH] fix: detect contained/enclosing date ranges in Stock Closing Entry duplicate check --- .../stock_closing_entry/stock_closing_entry.py | 9 ++++----- .../stock_closing_entry/test_stock_closing_entry.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) 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 00a3b0204c4..1f8450c87c4 100644 --- a/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py +++ b/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py @@ -59,11 +59,10 @@ class StockClosingEntry(Document): .where( (table.docstatus == 1) & (table.company == self.company) - & ( - (table.from_date.between(self.from_date, self.to_date)) - | (table.to_date.between(self.from_date, self.to_date)) - | ((self.from_date >= table.from_date) & (table.from_date >= self.to_date)) - ) + # two date ranges overlap when each starts on or before the other ends; + # this also catches one range being fully contained within the other + & (table.from_date <= self.to_date) + & (table.to_date >= self.from_date) ) ) 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 2f47551ff74..df5c22b6be5 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 @@ -84,6 +84,18 @@ class TestStockClosingEntryDuplicate(ERPNextTestSuite): overlap = self.make_closing("2026-02-01", "2026-04-30") self.assertRaises(frappe.ValidationError, overlap.insert) + def test_fully_contained_range_is_rejected(self): + # a range entirely inside an existing entry's range is still a duplicate + self.submit_closing(self.make_closing("2026-01-01", "2026-12-31")) + contained = self.make_closing("2026-03-01", "2026-03-31") + self.assertRaises(frappe.ValidationError, contained.insert) + + def test_enclosing_range_is_rejected(self): + # and so is a range that fully encloses an existing entry's range + self.submit_closing(self.make_closing("2026-03-01", "2026-03-31")) + enclosing = self.make_closing("2026-01-01", "2026-12-31") + self.assertRaises(frappe.ValidationError, enclosing.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")