From 000135a3d4ed833c0d1363d2514294ca249f2d0e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 3 Sep 2025 15:58:30 +0530 Subject: [PATCH] fix: incorrect batch qty --- .../test_stock_reconciliation.py | 74 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 14 +++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 4974fbb5063..98e369a01a8 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1591,6 +1591,80 @@ class TestStockReconciliation(IntegrationTestCase, StockTestMixin): self.assertFalse(status == "Active") + def test_stock_reconciliation_for_batch_with_backward(self): + # Make stock inward for 10 -> Stock Reco for 20 after two days + # Make backdated delivery note for 10 qty between stock inward and stock reco + # Check the state of the current serial and batch bundle in the stock reco + # The state should be cancelled + + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item_code = "Test Stock Reco for Batch with Backward" + + self.make_item( + item_code, {"has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "BCN-CB.#####"} + ) + + warehouse = "_Test Warehouse - _TC" + + se = make_stock_entry( + posting_date=add_days(nowdate(), -2), + posting_time="02:00", + item_code=item_code, + target=warehouse, + qty=10, + basic_rate=100, + ) + + batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle) + + sr = create_stock_reconciliation( + item_code=item_code, + warehouse=warehouse, + qty=20, + rate=200, + use_serial_batch_fields=1, + batch_no=batch_no, + posting_date=nowdate(), + posting_time="03:00", + ) + + current_sabb = sr.items[0].current_serial_and_batch_bundle + + self.assertTrue(frappe.db.get_value("Serial and Batch Bundle", current_sabb, "docstatus") == 1) + + self.assertTrue( + frappe.db.get_value( + "Stock Ledger Entry", {"serial_and_batch_bundle": current_sabb, "is_cancelled": 0}, "name" + ) + ) + self.assertTrue(sr.items[0].current_serial_and_batch_bundle) + self.assertTrue(sr.items[0].current_qty) + self.assertTrue(sr.items[0].current_qty == 10) + + se = make_stock_entry( + posting_date=add_days(nowdate(), -1), + posting_time="02:00", + item_code=item_code, + source=warehouse, + qty=10, + basic_rate=100, + use_serial_batch_fields=1, + batch_no=batch_no, + ) + + sr.reload() + self.assertFalse(sr.items[0].current_serial_and_batch_bundle) + self.assertTrue(sr.items[0].current_qty == 0) + + self.assertFalse(frappe.db.get_value("Serial and Batch Bundle", current_sabb, "docstatus") == 1) + + self.assertFalse( + frappe.db.get_value( + "Stock Ledger Entry", {"serial_and_batch_bundle": current_sabb, "is_cancelled": 0}, "name" + ) + ) + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index fbb74cd8f24..985ef099c6a 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -978,10 +978,12 @@ class update_entries_after: self.wh_data.valuation_rate = self.get_fallback_rate(sle) def reset_actual_qty_for_stock_reco(self, sle): - doc = frappe.get_cached_doc("Stock Reconciliation", sle.voucher_no) + doc = frappe.get_doc("Stock Reconciliation", sle.voucher_no) doc.recalculate_current_qty(sle.voucher_detail_no, sle.creation, sle.actual_qty > 0) if sle.actual_qty < 0: + doc.reload() + sle.actual_qty = ( flt(frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "current_qty")) * -1 @@ -990,6 +992,16 @@ class update_entries_after: if abs(sle.actual_qty) == 0.0: sle.is_cancelled = 1 + if sle.serial_and_batch_bundle: + for row in doc.items: + if row.name == sle.voucher_detail_no: + row.db_set("current_serial_and_batch_bundle", "") + + sabb_doc = frappe.get_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle) + sabb_doc.voucher_detail_no = None + sabb_doc.voucher_no = None + sabb_doc.cancel() + if sle.serial_and_batch_bundle and frappe.get_cached_value("Item", sle.item_code, "has_serial_no"): self.update_serial_no_status(sle)