mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-12 11:25:09 +00:00
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com> fix(stock): add warehouse filter to pick work order raw materials (#53748)
This commit is contained in:
@@ -505,8 +505,26 @@ class PickList(TransactionBase):
|
|||||||
self.item_location_map = frappe._dict()
|
self.item_location_map = frappe._dict()
|
||||||
|
|
||||||
from_warehouses = [self.parent_warehouse] if self.parent_warehouse else []
|
from_warehouses = [self.parent_warehouse] if self.parent_warehouse else []
|
||||||
if self.parent_warehouse:
|
|
||||||
from_warehouses.extend(get_descendants_of("Warehouse", self.parent_warehouse))
|
if self.work_order:
|
||||||
|
root_warehouse = frappe.db.get_value(
|
||||||
|
"Warehouse", {"company": self.company, "parent_warehouse": ["IS", "NOT SET"], "is_group": 1}
|
||||||
|
)
|
||||||
|
|
||||||
|
from_warehouses = [root_warehouse]
|
||||||
|
|
||||||
|
if from_warehouses:
|
||||||
|
from_warehouses.extend(get_descendants_of("Warehouse", from_warehouses[0]))
|
||||||
|
|
||||||
|
item_warehouse_dict = frappe._dict()
|
||||||
|
if self.work_order:
|
||||||
|
item_warehouse_list = frappe.get_all(
|
||||||
|
"Work Order Item",
|
||||||
|
filters={"parent": self.work_order},
|
||||||
|
fields=["item_code", "source_warehouse"],
|
||||||
|
)
|
||||||
|
if item_warehouse_list:
|
||||||
|
item_warehouse_dict = {item.item_code: item.source_warehouse for item in item_warehouse_list}
|
||||||
|
|
||||||
# Create replica before resetting, to handle empty table on update after submit.
|
# Create replica before resetting, to handle empty table on update after submit.
|
||||||
locations_replica = self.get("locations")
|
locations_replica = self.get("locations")
|
||||||
@@ -524,6 +542,13 @@ class PickList(TransactionBase):
|
|||||||
len_idx = len(self.get("locations")) or 0
|
len_idx = len(self.get("locations")) or 0
|
||||||
for item_doc in items:
|
for item_doc in items:
|
||||||
item_code = item_doc.item_code
|
item_code = item_doc.item_code
|
||||||
|
priority_warehouses = []
|
||||||
|
|
||||||
|
if self.work_order and item_warehouse_dict.get(item_code):
|
||||||
|
source_warehouse = item_warehouse_dict.get(item_code)
|
||||||
|
priority_warehouses = [source_warehouse]
|
||||||
|
priority_warehouses.extend(get_descendants_of("Warehouse", source_warehouse))
|
||||||
|
from_warehouses = list(dict.fromkeys(priority_warehouses + from_warehouses))
|
||||||
|
|
||||||
self.item_location_map.setdefault(
|
self.item_location_map.setdefault(
|
||||||
item_code,
|
item_code,
|
||||||
@@ -534,6 +559,7 @@ class PickList(TransactionBase):
|
|||||||
self.company,
|
self.company,
|
||||||
picked_item_details=picked_items_details.get(item_code),
|
picked_item_details=picked_items_details.get(item_code),
|
||||||
consider_rejected_warehouses=self.consider_rejected_warehouses,
|
consider_rejected_warehouses=self.consider_rejected_warehouses,
|
||||||
|
priority_warehouses=priority_warehouses,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -959,6 +985,7 @@ def get_available_item_locations(
|
|||||||
ignore_validation=False,
|
ignore_validation=False,
|
||||||
picked_item_details=None,
|
picked_item_details=None,
|
||||||
consider_rejected_warehouses=False,
|
consider_rejected_warehouses=False,
|
||||||
|
priority_warehouses=None,
|
||||||
):
|
):
|
||||||
locations = []
|
locations = []
|
||||||
|
|
||||||
@@ -999,7 +1026,7 @@ def get_available_item_locations(
|
|||||||
locations = filter_locations_by_picked_materials(locations, picked_item_details)
|
locations = filter_locations_by_picked_materials(locations, picked_item_details)
|
||||||
|
|
||||||
if locations:
|
if locations:
|
||||||
locations = get_locations_based_on_required_qty(locations, required_qty)
|
locations = get_locations_based_on_required_qty(locations, required_qty, priority_warehouses)
|
||||||
|
|
||||||
if not ignore_validation:
|
if not ignore_validation:
|
||||||
validate_picked_materials(item_code, required_qty, locations, picked_item_details)
|
validate_picked_materials(item_code, required_qty, locations, picked_item_details)
|
||||||
@@ -1007,9 +1034,14 @@ def get_available_item_locations(
|
|||||||
return locations
|
return locations
|
||||||
|
|
||||||
|
|
||||||
def get_locations_based_on_required_qty(locations, required_qty):
|
def get_locations_based_on_required_qty(locations, required_qty, priority_warehouses):
|
||||||
filtered_locations = []
|
filtered_locations = []
|
||||||
|
|
||||||
|
if priority_warehouses:
|
||||||
|
priority_locations = [loc for loc in locations if loc.warehouse in priority_warehouses]
|
||||||
|
fallback_locations = [loc for loc in locations if loc.warehouse not in priority_warehouses]
|
||||||
|
locations = priority_locations + fallback_locations
|
||||||
|
|
||||||
for location in locations:
|
for location in locations:
|
||||||
if location.qty >= required_qty:
|
if location.qty >= required_qty:
|
||||||
location.qty = required_qty
|
location.qty = required_qty
|
||||||
|
|||||||
@@ -1041,6 +1041,53 @@ class TestPickList(FrappeTestCase):
|
|||||||
pl = create_pick_list(so.name)
|
pl = create_pick_list(so.name)
|
||||||
self.assertFalse(pl.locations)
|
self.assertFalse(pl.locations)
|
||||||
|
|
||||||
|
def test_pick_list_warehouse_for_work_order(self):
|
||||||
|
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||||
|
from erpnext.manufacturing.doctype.work_order.work_order import create_pick_list, make_work_order
|
||||||
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
|
|
||||||
|
# Create Warehouses for Work Order
|
||||||
|
source_warehouse = create_warehouse("_Test WO Warehouse")
|
||||||
|
wip_warehouse = create_warehouse("_Test WIP Warehouse", company="_Test Company")
|
||||||
|
fg_warehouse = create_warehouse("_Test Finished Goods Warehouse", company="_Test Company")
|
||||||
|
|
||||||
|
# Create Finished Good Item
|
||||||
|
fg_item = make_item("Test Work Order Finished Good Item", properties={"is_stock_item": 1}).name
|
||||||
|
|
||||||
|
# Create Raw Material Item
|
||||||
|
rm_item = make_item("Test Work Order Raw Material Item", properties={"is_stock_item": 1}).name
|
||||||
|
|
||||||
|
# Create BOM
|
||||||
|
bom = make_bom(item=fg_item, rate=100, raw_materials=[rm_item])
|
||||||
|
|
||||||
|
# Create Inward entry for Raw Material
|
||||||
|
make_stock_entry(item=rm_item, to_warehouse=wip_warehouse, qty=10)
|
||||||
|
make_stock_entry(item=rm_item, to_warehouse=source_warehouse, qty=10)
|
||||||
|
|
||||||
|
# Create Work Order
|
||||||
|
wo = make_work_order(item=fg_item, qty=5, bom_no=bom.name, company="_Test Company")
|
||||||
|
wo.required_items[0].source_warehouse = source_warehouse
|
||||||
|
wo.fg_warehouse = fg_warehouse
|
||||||
|
wo.skip_transfer = True
|
||||||
|
wo.submit()
|
||||||
|
|
||||||
|
# Create Pick List
|
||||||
|
pl = create_pick_list(wo.name, for_qty=wo.qty)
|
||||||
|
|
||||||
|
# System prioritises the Source Warehouse
|
||||||
|
self.assertEqual(pl.locations[0].warehouse, source_warehouse)
|
||||||
|
self.assertEqual(pl.locations[0].item_code, rm_item)
|
||||||
|
self.assertEqual(pl.locations[0].qty, 5)
|
||||||
|
|
||||||
|
# Create Outward Entry from Source Warehouse
|
||||||
|
make_stock_entry(item=rm_item, from_warehouse=source_warehouse, qty=10)
|
||||||
|
pl.set_item_locations()
|
||||||
|
|
||||||
|
# System should pick other available warehouses
|
||||||
|
self.assertEqual(pl.locations[0].warehouse, wip_warehouse)
|
||||||
|
self.assertEqual(pl.locations[0].item_code, rm_item)
|
||||||
|
self.assertEqual(pl.locations[0].qty, 5)
|
||||||
|
|
||||||
def test_pick_list_validation_for_serial_no(self):
|
def test_pick_list_validation_for_serial_no(self):
|
||||||
warehouse = "_Test Warehouse - _TC"
|
warehouse = "_Test Warehouse - _TC"
|
||||||
item = make_item(
|
item = make_item(
|
||||||
|
|||||||
Reference in New Issue
Block a user