From 08ec31174c85d02af5d1e47c44a3d26776c24b55 Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Fri, 28 Aug 2026 16:08:51 +0530 Subject: [PATCH 1/5] fix: filter fully ordered items when creating RFQ from Material Request (cherry picked from commit 9b1d907b6a59101070810b670be45ab6ae0aa242) --- .../doctype/material_request/material_request.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 4bf88564df7..8b56cd8e28a 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -555,6 +555,15 @@ def make_purchase_order(source_name, target_doc=None, args=None): @frappe.whitelist() def make_request_for_quotation(source_name, target_doc=None): + def update_item(obj, target, source_parent): + qty = obj.ordered_qty or obj.received_qty + target.qty = flt(flt(obj.stock_qty) - flt(qty)) / target.conversion_factor + target.stock_qty = target.qty * target.conversion_factor + + def select_item(d): + qty = d.ordered_qty or d.received_qty + return qty < d.stock_qty + doclist = get_mapped_doc( "Material Request", source_name, @@ -570,6 +579,8 @@ def make_request_for_quotation(source_name, target_doc=None): ["parent", "material_request"], ["project", "project_name"], ], + "postprocess": update_item, + "condition": select_item, }, }, target_doc, From 3d563a2e0889cd351b0b14b5855856ba882d47fb Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Fri, 28 Aug 2026 16:32:53 +0530 Subject: [PATCH 2/5] test: RFQ from Material Request skips fully ordered items (cherry picked from commit 5adcb28b3ce12e327c902644f12bf1e30c698ff3) # Conflicts: # erpnext/stock/doctype/material_request/test_material_request.py --- .../material_request/test_material_request.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 4c753c867a2..1e6c3c10db2 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -15,6 +15,7 @@ from erpnext.stock.doctype.material_request.material_request import ( create_pick_list, make_in_transit_stock_entry, make_purchase_order, + make_request_for_quotation, make_stock_entry, make_supplier_quotation, raise_work_orders, @@ -47,6 +48,76 @@ class TestMaterialRequest(FrappeTestCase): self.assertEqual(po.doctype, "Purchase Order") self.assertEqual(len(po.get("items")), len(mr.get("items"))) +<<<<<<< HEAD +======= + def test_make_request_for_quotation_skips_ordered_items(self): + mr = frappe.copy_doc(self.globalTestRecords["Material Request"][0]).insert() + mr = frappe.get_doc("Material Request", mr.name) + mr.submit() + + # fully order the first item, leave the second pending + po = make_purchase_order(mr.name) + po.supplier = "_Test Supplier" + po.items = [po.items[0]] + po.insert() + po.submit() + + rfq = make_request_for_quotation(mr.name) + + self.assertEqual(len(rfq.get("items")), 1) + self.assertEqual(rfq.items[0].material_request_item, mr.items[1].name) + self.assertEqual(rfq.items[0].qty, mr.items[1].qty) + + def test_make_subcontracted_purchase_order(self): + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + from erpnext.stock.doctype.item.test_item import create_item, make_item + from erpnext.subcontracting.doctype.subcontracting_bom.test_subcontracting_bom import ( + create_subcontracting_bom, + ) + + mr = frappe.copy_doc(self.globalTestRecords["Material Request"][0]).insert() + mr.material_request_type = "Subcontracting" + mr.submit() + + frappe.db.set_value("Item", mr.items[0].item_code, "is_sub_contracted_item", 1) + + raw_materials = ["Raw Material Item 1", "Raw Material Item 2"] + for item in raw_materials: + create_item(item) + + frappe.new_doc("UOM").update({"uom_name": "Test UOM"}).save() + service_item = make_item( + properties={"is_stock_item": 0}, uoms=[{"uom": "Test UOM", "conversion_factor": 3}] + ) + + mr.items[0].default_bom = make_bom(item=mr.items[0].item_code, raw_materials=raw_materials) + mr.reload() + + create_subcontracting_bom( + finished_good=mr.items[0].item_code, + service_item=service_item.name, + finished_good_qty=2, + service_item_qty=1, + service_item_uom="Test UOM", + ) + + po = make_purchase_order(mr.name) + po.supplier = "_Test Supplier" + po.items[0].schedule_date = today() + po.items.pop(1) + + # Test 1 - Test if items stock qty, qty and finished good qty are calculated correctly based on provided UOMs + self.assertEqual(po.items[0].stock_qty, 81) + self.assertEqual(po.items[0].qty, 27) + self.assertEqual(po.items[0].fg_item_qty, 54) + + po.submit() + mr.reload() + + # Test 2 - MR items ordered qty should be updated based on PO items qty when submitted + self.assertEqual(mr.items[0].ordered_qty, 54) + +>>>>>>> 5adcb28 (test: RFQ from Material Request skips fully ordered items) def test_make_supplier_quotation(self): mr = frappe.copy_doc(test_records[0]).insert() From 567730e38e82a0f7b721dd69a715c791fe1526ed Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Fri, 28 Aug 2026 20:02:59 +0530 Subject: [PATCH 3/5] fix: add type hints to make_request_for_quotation (cherry picked from commit 4435f166ea982add6cab56f5b6e2a9424f708d78) --- erpnext/stock/doctype/material_request/material_request.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 8b56cd8e28a..e5d8dc0d474 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -10,6 +10,7 @@ import json import frappe import frappe.defaults from frappe import _, msgprint +from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc from frappe.query_builder import Order from frappe.query_builder.functions import Sum @@ -554,7 +555,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): @frappe.whitelist() -def make_request_for_quotation(source_name, target_doc=None): +def make_request_for_quotation(source_name: str, target_doc: str | dict | Document | None = None): def update_item(obj, target, source_parent): qty = obj.ordered_qty or obj.received_qty target.qty = flt(flt(obj.stock_qty) - flt(qty)) / target.conversion_factor From b439deda888dc03bd7bb05207824c9c5a4be5db8 Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Fri, 28 Aug 2026 20:02:59 +0530 Subject: [PATCH 4/5] test: set schedule date on PO before insert (cherry picked from commit 5af60cf2e399657781bf4e27e8976da4aa74c7fd) --- erpnext/stock/doctype/material_request/test_material_request.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 1e6c3c10db2..14916a6a4fd 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -58,7 +58,9 @@ class TestMaterialRequest(FrappeTestCase): # fully order the first item, leave the second pending po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" + po.schedule_date = today() po.items = [po.items[0]] + po.items[0].schedule_date = today() po.insert() po.submit() From 4a4e1908e3893fb64f732453d57295384797f3b5 Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Wed, 2 Sep 2026 11:50:21 +0530 Subject: [PATCH 5/5] chore: resolve backport conflict --- .../material_request/test_material_request.py | 54 +------------------ 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 14916a6a4fd..9a8a87d2fd5 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -48,10 +48,8 @@ class TestMaterialRequest(FrappeTestCase): self.assertEqual(po.doctype, "Purchase Order") self.assertEqual(len(po.get("items")), len(mr.get("items"))) -<<<<<<< HEAD -======= def test_make_request_for_quotation_skips_ordered_items(self): - mr = frappe.copy_doc(self.globalTestRecords["Material Request"][0]).insert() + mr = frappe.copy_doc(test_records[0]).insert() mr = frappe.get_doc("Material Request", mr.name) mr.submit() @@ -70,56 +68,6 @@ class TestMaterialRequest(FrappeTestCase): self.assertEqual(rfq.items[0].material_request_item, mr.items[1].name) self.assertEqual(rfq.items[0].qty, mr.items[1].qty) - def test_make_subcontracted_purchase_order(self): - from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom - from erpnext.stock.doctype.item.test_item import create_item, make_item - from erpnext.subcontracting.doctype.subcontracting_bom.test_subcontracting_bom import ( - create_subcontracting_bom, - ) - - mr = frappe.copy_doc(self.globalTestRecords["Material Request"][0]).insert() - mr.material_request_type = "Subcontracting" - mr.submit() - - frappe.db.set_value("Item", mr.items[0].item_code, "is_sub_contracted_item", 1) - - raw_materials = ["Raw Material Item 1", "Raw Material Item 2"] - for item in raw_materials: - create_item(item) - - frappe.new_doc("UOM").update({"uom_name": "Test UOM"}).save() - service_item = make_item( - properties={"is_stock_item": 0}, uoms=[{"uom": "Test UOM", "conversion_factor": 3}] - ) - - mr.items[0].default_bom = make_bom(item=mr.items[0].item_code, raw_materials=raw_materials) - mr.reload() - - create_subcontracting_bom( - finished_good=mr.items[0].item_code, - service_item=service_item.name, - finished_good_qty=2, - service_item_qty=1, - service_item_uom="Test UOM", - ) - - po = make_purchase_order(mr.name) - po.supplier = "_Test Supplier" - po.items[0].schedule_date = today() - po.items.pop(1) - - # Test 1 - Test if items stock qty, qty and finished good qty are calculated correctly based on provided UOMs - self.assertEqual(po.items[0].stock_qty, 81) - self.assertEqual(po.items[0].qty, 27) - self.assertEqual(po.items[0].fg_item_qty, 54) - - po.submit() - mr.reload() - - # Test 2 - MR items ordered qty should be updated based on PO items qty when submitted - self.assertEqual(mr.items[0].ordered_qty, 54) - ->>>>>>> 5adcb28 (test: RFQ from Material Request skips fully ordered items) def test_make_supplier_quotation(self): mr = frappe.copy_doc(test_records[0]).insert()