mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-16 08:05:00 +00:00
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:
@@ -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 (
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user