test: WIP warehouse required for work orders tracking semi finished goods

(cherry picked from commit f61f6523b9)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/test_work_order.py
This commit is contained in:
Mihir Kandoi
2026-08-08 14:44:02 +05:30
committed by Mergify
parent 24cd5f22b5
commit e822efe6a1

View File

@@ -4839,6 +4839,71 @@ class TestWorkOrder(ERPNextTestSuite):
# generated qty (3.0 for 8 units) differs from the BOM-scaled qty (7.5 for 20 units)
self.assertEqual(flt(row.qty, 6), 3.0)
<<<<<<< HEAD
=======
def test_transferred_qty_not_misattributed_between_item_and_its_substitute(self):
"""When one item is transferred both for itself and as a substitute for another required item,
each transfer must be credited to the right required item.
_material_transfer_qty_by_item grouped Stock Entry Detail by item_code only and picked
Max(original_item); for item B transferred once for itself (original_item NULL) and once as a
substitute for A (original_item=A), Max picked A and credited B's whole transfer to A, leaving
B at 0. Grouping by (item_code, original_item) and accumulating into the keyed dict attributes
each transfer correctly, deterministically on MariaDB and Postgres.
"""
from erpnext.manufacturing.doctype.work_order.services.required_items import RequiredItemsService
source_warehouse = "Stores - _TC"
fg_item = make_item("Test WO SelfSub FG", {"is_stock_item": 1}).name
item_a = make_item("Test WO SelfSub RM A", {"is_stock_item": 1, "allow_alternative_item": 1}).name
item_b = make_item("Test WO SelfSub RM B", {"is_stock_item": 1, "allow_alternative_item": 1}).name
# B is a registered alternative for A
if not frappe.db.exists("Item Alternative", {"item_code": item_a, "alternative_item_code": item_b}):
frappe.get_doc(
{
"doctype": "Item Alternative",
"item_code": item_a,
"alternative_item_code": item_b,
"two_way": 1,
}
).insert()
# stock B generously (covers B-for-A plus B-for-itself)
for item, qty in ((item_a, 50), (item_b, 100)):
test_stock_entry.make_stock_entry(
item_code=item, target=source_warehouse, qty=qty, basic_rate=100
)
make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=[item_a, item_b])
wo = make_wo_order_test_record(item=fg_item, qty=10, source_warehouse=source_warehouse)
transfer = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
transfer.save()
# substitute B for the A line; the existing B line stays as B's own transfer
for d in transfer.items:
if d.item_code == item_a:
d.item_code = item_b
d.original_item = item_a
transfer.submit()
qty_by_item = RequiredItemsService(wo)._material_transfer_qty_by_item(is_return=0)
# B transferred as a substitute for A -> credited to A; B transferred for itself -> credited to B.
self.assertEqual(flt(qty_by_item.get(item_a)), 10.0)
self.assertEqual(flt(qty_by_item.get(item_b)), 10.0)
def test_wip_warehouse_required_when_tracking_semi_finished_goods(self):
wo = frappe.new_doc("Work Order")
wo.track_semi_finished_goods = 1
wo.skip_transfer = 0
wo.fg_warehouse = "_Test Warehouse 1 - _TC"
self.assertRaises(frappe.ValidationError, wo.validate_warehouse)
wo.wip_warehouse = "_Test Warehouse - _TC"
wo.validate_warehouse()
>>>>>>> f61f6523b9 (test: WIP warehouse required for work orders tracking semi finished goods)
def get_reserved_entries(voucher_no, warehouse=None):
doctype = frappe.qb.DocType("Stock Reservation Entry")