Merge pull request #49811 from frappe/mergify/bp/version-15-hotfix/pr-49750

fix: get unconsumed qty as per BOM required qty (backport #49750)
This commit is contained in:
rohitwaghchaure
2025-09-30 22:19:55 +05:30
committed by GitHub
2 changed files with 76 additions and 2 deletions

View File

@@ -2828,6 +2828,78 @@ class TestWorkOrder(FrappeTestCase):
wo.operations[3].planned_start_time, add_to_date(wo.operations[1].planned_end_time, minutes=10)
)
def test_req_qty_clamping_in_manufacture_entry(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import (
make_stock_entry as make_stock_entry_test_record,
)
fg_item = "Test Unconsumed RM FG Item"
rm_item_1 = "Test Unconsumed RM Item 1"
rm_item_2 = "Test Unconsumed RM Item 2"
source_warehouse = "_Test Warehouse - _TC"
wip_warehouse = "Stores - _TC"
fg_warehouse = create_warehouse("_Test Finished Goods Warehouse", company="_Test Company")
make_item(fg_item, {"is_stock_item": 1})
make_item(rm_item_1, {"is_stock_item": 1})
make_item(rm_item_2, {"is_stock_item": 1})
# create a BOM: 1 FG = 1 RM1 + 1 RM2
bom = make_bom(
item=fg_item,
source_warehouse=source_warehouse,
raw_materials=[rm_item_1, rm_item_2],
operating_cost_per_bom_quantity=1,
do_not_submit=True,
)
for row in bom.exploded_items:
make_stock_entry_test_record(
item_code=row.item_code,
target=source_warehouse,
qty=100,
basic_rate=100,
)
wo = make_wo_order_test_record(
item=fg_item,
qty=50,
source_warehouse=source_warehouse,
wip_warehouse=wip_warehouse,
)
wo.submit()
# first partial transfer & manufacture (6 units)
se_transfer_1 = frappe.get_doc(
make_stock_entry(wo.name, "Material Transfer for Manufacture", 6, wip_warehouse)
)
se_transfer_1.insert()
se_transfer_1.submit()
stock_entry_1 = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 6, fg_warehouse))
# remove rm_2 from the items to simulate unconsumed RM scenario
stock_entry_1.items = [row for row in stock_entry_1.items if row.item_code != rm_item_2]
stock_entry_1.save()
stock_entry_1.submit()
wo.reload()
se_transfer_2 = frappe.get_doc(
make_stock_entry(wo.name, "Material Transfer for Manufacture", 20, wip_warehouse)
)
se_transfer_2.insert()
se_transfer_2.submit()
stock_entry_2 = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 20, fg_warehouse))
# validate rm_item_2 quantity is clamped correctly (per-unit BOM = 1 → max 20)
for row in stock_entry_2.items:
if row.item_code == rm_item_2:
self.assertLessEqual(row.qty, 20)
self.assertGreaterEqual(row.qty, 0)
def make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehouse):
from erpnext.stock.doctype.stock_entry.test_stock_entry import (

View File

@@ -2279,10 +2279,12 @@ class StockEntry(StockController):
wo_item_qty = item.transferred_qty or item.required_qty
wo_qty_consumed = flt(wo_item_qty) - flt(item.consumed_qty)
wo_qty_unconsumed = flt(wo_item_qty) - flt(item.consumed_qty)
wo_qty_to_produce = flt(work_order_qty) - flt(wo.produced_qty)
bom_qty_per_unit = item.required_qty / wo.qty # per-unit BOM qty
req_qty_each = (wo_qty_consumed) / (wo_qty_to_produce or 1)
req_qty_each = (wo_qty_unconsumed) / (wo_qty_to_produce or 1)
req_qty_each = min(req_qty_each, bom_qty_per_unit)
qty = req_qty_each * flt(self.fg_completed_qty)