From 62e6e23581d81d809b720051e3d564f1a6f3c48b Mon Sep 17 00:00:00 2001 From: Shllokkk <140623894+Shllokkk@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:14:36 +0530 Subject: [PATCH] fix: validate items against source Sales Order in Material Request (#58443) --- .../material_request/material_request.py | 17 +++++++++++++++++ .../material_request/test_material_request.py | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 4350f33c468..c59a7dfef71 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -112,6 +112,22 @@ class MaterialRequest(BuyingController): def check_if_already_pulled(self): pass + def validate_with_previous_doc(self): + super().validate_with_previous_doc( + { + "Sales Order": { + "ref_dn_field": "sales_order", + "compare_fields": [["company", "="]], + }, + "Sales Order Item": { + "ref_dn_field": "sales_order_item", + "compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]], + "is_child_table": True, + "allow_duplicate_prev_row_id": True, + }, + } + ) + def validate_qty_against_so(self): so_items = {} # Format --> {'SO/00001': {'Item/001': 120, 'Item/002': 24}} for d in self.get("items"): @@ -157,6 +173,7 @@ class MaterialRequest(BuyingController): self.validate_schedule_date() self.check_for_on_hold_or_closed_status("Sales Order", "sales_order") + self.validate_with_previous_doc() self.validate_uom_is_integer("uom", "qty") self.validate_material_request_type() diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 02eb150f51e..ef7bebc3edf 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -1066,6 +1066,18 @@ class TestMaterialRequest(ERPNextTestSuite): self.assertEqual(mr.items[0].qty, 5) self.assertEqual(mr.items[1].qty, 5) + def test_item_change_on_sales_order_row_is_blocked(self): + from erpnext.selling.doctype.sales_order.mapper import make_material_request + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + + other_item = create_item("_Test MR Item Swap").name + so = make_sales_order() + mr = make_material_request(so.name) + mr.material_request_type = "Purchase" + # swapping the fetched item would leave a stale link to the SO row + mr.items[0].item_code = other_item + self.assertRaises(frappe.ValidationError, mr.insert) + def test_pending_qty_in_pick_list(self): """Test for pick list mapped doc qty from partially received Material Request Transfer""" import json