From 7170a1bd78232c39dd67eea1291c430d4ba8b0d2 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 21 Jan 2026 15:30:55 +0530 Subject: [PATCH 1/2] fix: force user to enter batch or serial for serial/batch items --- .../stock_reconciliation/stock_reconciliation.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index acf7a5a937b..3816b3fe2ad 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -665,6 +665,16 @@ class StockReconciliation(StockController): validate_is_stock_item, ) + def validate_serial_batch_items(): + has_batch_no, has_serial_no = frappe.get_value( + "Item", item_code, ["has_batch_no", "has_serial_no"] + ) + if row.use_serial_batch_fields: + if has_batch_no and not row.batch_no: + raise frappe.ValidationError(_("Please enter Batch No")) + if has_serial_no and not row.serial_no: + raise frappe.ValidationError(_("Please enter Serial No")) + # using try except to catch all validation msgs and display together try: @@ -673,12 +683,13 @@ class StockReconciliation(StockController): # end of life and stock item validate_end_of_life(item_code, item.end_of_life, item.disabled) validate_is_stock_item(item_code, item.is_stock_item) + validate_serial_batch_items() # docstatus should be < 2 validate_cancelled_item(item_code, item.docstatus) except Exception as e: - self.validation_messages.append(_("Row #") + " " + ("%d: " % (row.idx)) + cstr(e)) + self.validation_messages.append(_("Row #") + ("%d: " % (row.idx)) + cstr(e)) def validate_reserved_stock(self) -> None: """Raises an exception if there is any reserved stock for the items in the Stock Reconciliation.""" From 035b3cb61e89ae6075597b451401f619b3084dd2 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 21 Jan 2026 15:58:46 +0530 Subject: [PATCH 2/2] fix: tests --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 2 +- .../doctype/stock_reconciliation/test_stock_reconciliation.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 3816b3fe2ad..22ca84f6e38 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -669,7 +669,7 @@ class StockReconciliation(StockController): has_batch_no, has_serial_no = frappe.get_value( "Item", item_code, ["has_batch_no", "has_serial_no"] ) - if row.use_serial_batch_fields: + if row.use_serial_batch_fields and self.purpose == "Stock Reconciliation": if has_batch_no and not row.batch_no: raise frappe.ValidationError(_("Please enter Batch No")) if has_serial_no and not row.serial_no: diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 54808ffe292..eb6f6ca1f2c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1449,6 +1449,7 @@ class TestStockReconciliation(IntegrationTestCase, StockTestMixin): qty=10, rate=100, use_serial_batch_fields=1, + purpose="Opening Stock", ) sr.reload() @@ -1591,6 +1592,7 @@ class TestStockReconciliation(IntegrationTestCase, StockTestMixin): qty=10, rate=80, use_serial_batch_fields=1, + purpose="Opening Stock", ) batch_no = get_batch_from_bundle(reco.items[0].serial_and_batch_bundle) @@ -1675,6 +1677,7 @@ class TestStockReconciliation(IntegrationTestCase, StockTestMixin): qty=10, rate=100, use_serial_batch_fields=1, + purpose="Opening Stock", ) sr.reload()