mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-01 15:47:06 +00:00
fix(stock): correct reservation and pick list quantities (#58613)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user