test: cover reserve stock gating on purchase receipt submit

This commit is contained in:
pandiyan
2026-07-02 16:56:44 +05:30
parent 71f02d412a
commit 820f5498e7

View File

@@ -2322,6 +2322,145 @@ class TestProductionPlan(ERPNextTestSuite):
self.assertEqual(len(reserved_entries), 0)
frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 0)
def test_no_stock_reservation_via_purchase_receipt_when_reserve_stock_disabled(self):
from erpnext.buying.doctype.purchase_order.mapper import make_purchase_receipt
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
from erpnext.stock.doctype.material_request.mapper import make_purchase_order
frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 1)
frappe.db.set_single_value("Stock Settings", "auto_reserve_stock", 0)
bom_tree = {"FG For SR No Auto Reserve": {"RM For SR No Auto Reserve": {}}}
parent_bom = create_nested_bom(bom_tree, prefix="")
warehouse = "_Test Warehouse - _TC"
# reserve_stock is deliberately left unset (defaults to 0): this is what happens when
# "Auto Reserve Stock" is off and nobody ticks "Reserve Stock" on the Production Plan by hand.
plan = create_production_plan(
item_code=parent_bom.item,
planned_qty=5,
ignore_existing_ordered_qty=1,
do_not_submit=1,
warehouse=warehouse,
for_warehouse=warehouse,
)
plan.get_sub_assembly_items()
plan.set("mr_items", [])
for d in get_items_for_material_requests(plan.as_dict()):
plan.append("mr_items", d)
plan.save()
self.assertEqual(plan.reserve_stock, 0)
plan.submit()
plan.submit_material_request = 1
plan.make_material_request()
material_requests = frappe.get_all(
"Material Request", filters={"production_plan": plan.name}, pluck="name"
)
self.assertGreater(len(material_requests), 0)
for mr_name in list(set(material_requests)):
po = make_purchase_order(mr_name)
po.supplier = "_Test Supplier"
po.submit()
pr = make_purchase_receipt(po.name)
pr.submit()
sre = StockReservation(plan)
reserved_entries = sre.get_reserved_entries("Production Plan", plan.name)
self.assertEqual(len(reserved_entries), 0)
frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 0)
def test_stock_reservation_ignores_production_plans_with_reserve_stock_off_on_shared_purchase_order(self):
from erpnext.buying.doctype.purchase_order.mapper import make_purchase_receipt
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 1)
frappe.db.set_single_value("Stock Settings", "auto_reserve_stock", 0)
warehouse = "_Test Warehouse - _TC"
bom_reserve = create_nested_bom({"FG SR Mixed Reserve": {"RM SR Mixed Reserve": {}}}, prefix="")
bom_skip = create_nested_bom({"FG SR Mixed Skip": {"RM SR Mixed Skip": {}}}, prefix="")
def make_submitted_plan(item_code, reserve_stock):
plan = create_production_plan(
item_code=item_code,
planned_qty=5,
ignore_existing_ordered_qty=1,
do_not_submit=1,
warehouse=warehouse,
for_warehouse=warehouse,
reserve_stock=reserve_stock,
)
plan.get_sub_assembly_items()
plan.set("mr_items", [])
for d in get_items_for_material_requests(plan.as_dict()):
plan.append("mr_items", d)
plan.save()
plan.submit()
plan.submit_material_request = 1
plan.make_material_request()
return plan
plan_reserve = make_submitted_plan(bom_reserve.item, reserve_stock=1)
plan_skip = make_submitted_plan(bom_skip.item, reserve_stock=0)
self.assertEqual(plan_reserve.reserve_stock, 1)
self.assertEqual(plan_skip.reserve_stock, 0)
mr_reserve = frappe.get_all(
"Material Request", filters={"production_plan": plan_reserve.name}, pluck="name"
)[0]
mr_skip = frappe.get_all(
"Material Request", filters={"production_plan": plan_skip.name}, pluck="name"
)[0]
# One Purchase Order pulling rows from both Material Requests, so the Purchase Receipt made
# from it has both a reservable and a non-reservable Production Plan reference in `doc.items`.
po = frappe.new_doc("Purchase Order")
po.supplier = "_Test Supplier"
po.company = plan_reserve.company
po.schedule_date = nowdate()
for mr_name in (mr_reserve, mr_skip):
mr = frappe.get_doc("Material Request", mr_name)
for item in mr.items:
po.append(
"items",
{
"item_code": item.item_code,
"qty": item.qty,
"rate": 100,
"schedule_date": nowdate(),
"warehouse": warehouse,
"material_request": mr.name,
"material_request_item": item.name,
},
)
po.submit()
pr = make_purchase_receipt(po.name)
pr.submit()
reserved_for_plan_reserve = StockReservation(plan_reserve).get_reserved_entries(
"Production Plan", plan_reserve.name
)
reserved_for_plan_skip = StockReservation(plan_skip).get_reserved_entries(
"Production Plan", plan_skip.name
)
self.assertGreater(len(reserved_for_plan_reserve), 0)
self.assertEqual(len(reserved_for_plan_skip), 0)
frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 0)
def test_stock_reservation_restored_on_work_order_cancel(self):
# Spec #5 (cancellation path): when a Work Order created from a Production Plan is cancelled,
# the reservation that was transferred PP -> WO must flow back to the still-open Production