fix(stock): correct reservation and pick list quantities (#58613)

This commit is contained in:
Pandiyan P
2026-08-31 19:38:31 +05:30
committed by GitHub
parent 24209ae699
commit 7ecfa6b356
4 changed files with 104 additions and 5 deletions

View File

@@ -1310,9 +1310,9 @@ def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus)
# if extra quantity is available push current warehouse to available locations
if qty_diff > 0:
item_location.qty = qty_diff
if item_location.serial_no:
if item_location.serial_nos:
# set remaining serial numbers
item_location.serial_no = item_location.serial_no[-int(qty_diff) :]
item_location.serial_nos = item_location.serial_nos[-int(qty_diff) :]
available_locations = [item_location, *available_locations]
# update available locations for the item
@@ -1456,13 +1456,16 @@ def filter_locations_by_picked_materials(locations, picked_item_details) -> list
filterd_locations.append(row)
continue
if picked_qty > row.qty:
row.qty = 0
picked_item_details[key]["picked_qty"] -= row.qty
row.qty = 0
else:
row.qty -= picked_qty
picked_item_details[key]["picked_qty"] = 0.0
if row.serial_nos:
row.serial_nos = list(set(row.serial_nos) - set(picked_item_details[key].get("serial_no")))
picked_serial_nos = set(picked_item_details[key].get("serial_no") or [])
row.serial_nos = [
serial_no for serial_no in row.serial_nos if serial_no not in picked_serial_nos
]
if flt(row.qty, precision) > 0:
filterd_locations.append(row)

View File

@@ -29,6 +29,61 @@ from erpnext.tests.utils import ERPNextTestSuite
class TestPickList(ERPNextTestSuite):
def test_filter_locations_consumes_picked_qty_across_rows(self):
from erpnext.stock.doctype.pick_list.pick_list import filter_locations_by_picked_materials
key = ("Test Warehouse", "Test Batch")
locations = [
_dict(warehouse=key[0], batch_no=key[1], qty=5),
_dict(warehouse=key[0], batch_no=key[1], qty=5),
]
picked_item_details = {key: {"picked_qty": 7}}
filtered_locations = filter_locations_by_picked_materials(locations, picked_item_details)
self.assertEqual(len(filtered_locations), 1)
self.assertEqual(filtered_locations[0].qty, 3)
self.assertEqual(picked_item_details[key]["picked_qty"], 0)
def test_filter_locations_preserves_serial_order(self):
from erpnext.stock.doctype.pick_list.pick_list import filter_locations_by_picked_materials
warehouse = "Test Warehouse"
locations = [
_dict(
warehouse=warehouse,
batch_no=None,
qty=4,
serial_nos=["SN-1", "SN-2", "SN-3", "SN-4"],
)
]
picked_item_details = {warehouse: {"picked_qty": 2, "serial_no": ["SN-2", "SN-4"]}}
filtered_locations = filter_locations_by_picked_materials(locations, picked_item_details)
self.assertEqual(filtered_locations[0].serial_nos, ["SN-1", "SN-3"])
def test_get_items_with_location_trims_allocated_serial_nos(self):
from erpnext.stock.doctype.pick_list.pick_list import get_items_with_location_and_quantity
item = _dict(item_code="Test Serial Item", qty=2, stock_qty=2, conversion_factor=1, uom="Nos")
item_location_map = {
item.item_code: [
_dict(
warehouse="Test Warehouse",
batch_no=None,
qty=4,
serial_nos=["SN-1", "SN-2", "SN-3", "SN-4"],
)
]
}
first_locations = get_items_with_location_and_quantity(item, item_location_map, docstatus=0)
second_locations = get_items_with_location_and_quantity(item, item_location_map, docstatus=0)
self.assertEqual(first_locations[0].serial_no, "SN-1\nSN-2")
self.assertEqual(second_locations[0].serial_no, "SN-3\nSN-4")
def test_pick_list_allocation_takes_advisory_gate(self):
if frappe.db.db_type != "postgres":
return

View File

@@ -1971,7 +1971,7 @@ def update_serial_batch_delivered_qty(row, name, is_cancelled=False):
.where((doctype.parent == name) & (doctype.batch_no == batch_no))
)
query.run()
query.run()
def get_reserved_materials(voucher_no):

View File

@@ -1168,3 +1168,44 @@ class TestStockReservationEntryValidation(ERPNextTestSuite):
result = doc.get_serial_batch_entries()
self.assertEqual(result.serial_nos, ["SN1", "SN2"])
self.assertEqual(result.batches["B1"], 8)
def test_update_serial_batch_delivered_qty_updates_each_batch(self):
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
update_serial_batch_delivered_qty,
)
item = make_batch_item()
first_batch = frappe.get_doc(doctype="Batch", item=item.name).insert()
second_batch = frappe.get_doc(doctype="Batch", item=item.name).insert()
batches = {first_batch.name: 2, second_batch.name: 3}
sre = make_stock_reservation_entry(
item_code=item.name,
warehouse="_Test Warehouse - _TC",
reserved_qty=5,
ignore_validate=True,
do_not_submit=True,
)
sre.reservation_based_on = "Serial and Batch"
for batch_no, qty in batches.items():
sre.append("sb_entries", {"batch_no": batch_no, "qty": qty})
sre.save()
row = frappe._dict(serial_nos=[], batches=batches)
update_serial_batch_delivered_qty(row, sre.name)
delivered_qty_by_batch = {
d.batch_no: d.delivered_qty
for d in frappe.get_all(
"Serial and Batch Entry",
filters={"parent": sre.name},
fields=["batch_no", "delivered_qty"],
)
}
self.assertEqual(delivered_qty_by_batch, batches)
update_serial_batch_delivered_qty(row, sre.name, is_cancelled=True)
delivered_qty = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": sre.name},
pluck="delivered_qty",
)
self.assertEqual(delivered_qty, [0, 0])