Merge pull request #38455 from rohitwaghchaure/fixed-incorrect-requested-qty-in-mr-for-sco

fix: incorrect requested quantity for the subcontracting order
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 5f19657..595722d 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -221,6 +221,10 @@
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
 	def validate_with_previous_doc(self):
+		mri_compare_fields = [["project", "="], ["item_code", "="]]
+		if self.is_subcontracted:
+			mri_compare_fields = [["project", "="]]
+
 		super(PurchaseOrder, self).validate_with_previous_doc(
 			{
 				"Supplier Quotation": {
@@ -243,7 +247,7 @@
 				},
 				"Material Request Item": {
 					"ref_dn_field": "material_request_item",
-					"compare_fields": [["project", "="], ["item_code", "="]],
+					"compare_fields": mri_compare_fields,
 					"is_child_table": True,
 				},
 			}
@@ -417,23 +421,6 @@
 				check_list.append(d.material_request)
 				check_on_hold_or_closed_status("Material Request", d.material_request)
 
-	def update_requested_qty(self):
-		material_request_map = {}
-		for d in self.get("items"):
-			if d.material_request_item:
-				material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
-
-		for mr, mr_item_rows in material_request_map.items():
-			if mr and mr_item_rows:
-				mr_obj = frappe.get_doc("Material Request", mr)
-
-				if mr_obj.status in ["Stopped", "Cancelled"]:
-					frappe.throw(
-						_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError
-					)
-
-				mr_obj.update_requested_qty(mr_item_rows)
-
 	def update_ordered_qty(self, po_item_rows=None):
 		"""update requested qty (before ordered_qty is updated)"""
 		item_wh_list = []
@@ -475,7 +462,9 @@
 			self.update_status_updater()
 
 		self.update_prevdoc_status()
-		self.update_requested_qty()
+		if not self.is_subcontracted or self.is_old_subcontracting_flow:
+			self.update_requested_qty()
+
 		self.update_ordered_qty()
 		self.validate_budget()
 		self.update_reserved_qty_for_subcontract()
@@ -509,7 +498,9 @@
 
 		# Must be called after updating ordered qty in Material Request
 		# bin uses Material Request Items to recalculate & update
-		self.update_requested_qty()
+		if not self.is_subcontracted or self.is_old_subcontracting_flow:
+			self.update_requested_qty()
+
 		self.update_ordered_qty()
 
 		self.update_blanket_order()
@@ -875,6 +866,8 @@
 				"doctype": "Subcontracting Order Service Item",
 				"field_map": {
 					"name": "purchase_order_item",
+					"material_request": "material_request",
+					"material_request_item": "material_request_item",
 				},
 				"field_no_map": [],
 			},
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 3d55a08..b52fbad 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -952,6 +952,23 @@
 
 		return self._sub_contracted_items
 
+	def update_requested_qty(self):
+		material_request_map = {}
+		for d in self.get("items"):
+			if d.material_request_item:
+				material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
+
+		for mr, mr_item_rows in material_request_map.items():
+			if mr and mr_item_rows:
+				mr_obj = frappe.get_doc("Material Request", mr)
+
+				if mr_obj.status in ["Stopped", "Cancelled"]:
+					frappe.throw(
+						_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError
+					)
+
+				mr_obj.update_requested_qty(mr_item_rows)
+
 
 def get_item_details(items):
 	item = frappe.qb.DocType("Item")
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
index 68d98fc..6c187f8 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
@@ -80,6 +80,23 @@
 		transaction_date: DF.Date
 	# end: auto-generated types
 
+	def __init__(self, *args, **kwargs):
+		super(SubcontractingOrder, self).__init__(*args, **kwargs)
+
+		self.status_updater = [
+			{
+				"source_dt": "Subcontracting Order Item",
+				"target_dt": "Material Request Item",
+				"join_field": "material_request_item",
+				"target_field": "ordered_qty",
+				"target_parent_dt": "Material Request",
+				"target_parent_field": "per_ordered",
+				"target_ref_field": "stock_qty",
+				"source_field": "qty",
+				"percent_join_field": "material_request",
+			}
+		]
+
 	def before_validate(self):
 		super(SubcontractingOrder, self).before_validate()
 
@@ -93,11 +110,15 @@
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
 	def on_submit(self):
+		self.update_prevdoc_status()
+		self.update_requested_qty()
 		self.update_ordered_qty_for_subcontracting()
 		self.update_reserved_qty_for_subcontracting()
 		self.update_status()
 
 	def on_cancel(self):
+		self.update_prevdoc_status()
+		self.update_requested_qty()
 		self.update_ordered_qty_for_subcontracting()
 		self.update_reserved_qty_for_subcontracting()
 		self.update_status()
@@ -237,6 +258,8 @@
 						"stock_uom": item.stock_uom,
 						"bom": bom,
 						"purchase_order_item": si.purchase_order_item,
+						"material_request": si.material_request,
+						"material_request_item": si.material_request_item,
 					}
 				)
 			else:
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
index 3557858..37dabf1 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
+++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
@@ -628,6 +628,62 @@
 
 		self.assertEqual(ordered_qty + 10, new_ordered_qty)
 
+	def test_requested_qty_for_subcontracting_order(self):
+		from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+		from erpnext.stock.doctype.material_request.test_material_request import make_material_request
+
+		requested_qty = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Item SA8"},
+			fieldname="indented_qty",
+		)
+		requested_qty = flt(requested_qty)
+
+		mr = make_material_request(
+			item_code="Subcontracted Item SA8",
+			material_request_type="Purchase",
+			qty=10,
+		)
+
+		self.assertTrue(mr.docstatus == 1)
+
+		new_requested_qty = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Item SA8"},
+			fieldname="indented_qty",
+		)
+		new_requested_qty = flt(new_requested_qty)
+
+		self.assertEqual(requested_qty + 10, new_requested_qty)
+
+		po = make_purchase_order(mr.name)
+		po.is_subcontracted = 1
+		po.supplier = "_Test Supplier"
+		po.items[0].fg_item = "Subcontracted Item SA8"
+		po.items[0].fg_item_qty = 10
+		po.items[0].item_code = "Subcontracted Service Item 8"
+		po.items[0].item_name = "Subcontracted Service Item 8"
+		po.items[0].qty = 10
+		po.supplier_warehouse = "_Test Warehouse 1 - _TC"
+		po.save()
+		po.submit()
+
+		self.assertTrue(po.items[0].material_request)
+		self.assertTrue(po.items[0].material_request_item)
+
+		sco = create_subcontracting_order(po_name=po.name)
+		self.assertTrue(sco.items[0].material_request)
+		self.assertTrue(sco.items[0].material_request_item)
+
+		new_requested_qty = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Item SA8"},
+			fieldname="indented_qty",
+		)
+		new_requested_qty = flt(new_requested_qty)
+
+		self.assertEqual(requested_qty, new_requested_qty)
+
 
 def create_subcontracting_order(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
index 911e903..1ca90c3 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
@@ -40,13 +40,18 @@
   "manufacture_section",
   "manufacturer",
   "manufacturer_part_no",
+  "column_break_impp",
+  "reference_section",
+  "material_request",
+  "column_break_fpyl",
+  "material_request_item",
   "accounting_dimensions_section",
   "cost_center",
   "dimension_col_break",
   "project",
   "section_break_34",
-  "page_break",
-  "purchase_order_item"
+  "purchase_order_item",
+  "page_break"
  ],
  "fields": [
   {
@@ -335,6 +340,37 @@
    "options": "Project"
   },
   {
+   "fieldname": "column_break_impp",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "material_request",
+   "fieldtype": "Link",
+   "label": "Material Request",
+   "no_copy": 1,
+   "options": "Material Request",
+   "read_only": 1,
+   "search_index": 1
+  },
+  {
+   "fieldname": "material_request_item",
+   "fieldtype": "Data",
+   "label": "Material Request Item",
+   "no_copy": 1,
+   "read_only": 1,
+   "search_index": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "reference_section",
+   "fieldtype": "Section Break",
+   "label": "Reference"
+  },
+  {
+   "fieldname": "column_break_fpyl",
+   "fieldtype": "Column Break"
+  },
+  {
    "fieldname": "purchase_order_item",
    "fieldtype": "Data",
    "hidden": 1,
@@ -348,7 +384,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-23 16:56:22.182698",
+ "modified": "2023-11-30 15:29:43.744618",
  "modified_by": "Administrator",
  "module": "Subcontracting",
  "name": "Subcontracting Order Item",
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json
index dc18543..f1e94e1 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json
@@ -19,7 +19,11 @@
   "fg_item",
   "column_break_12",
   "fg_item_qty",
-  "purchase_order_item"
+  "purchase_order_item",
+  "section_break_kphn",
+  "material_request",
+  "column_break_piqi",
+  "material_request_item"
  ],
  "fields": [
   {
@@ -122,11 +126,36 @@
    "no_copy": 1,
    "read_only": 1,
    "search_index": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_kphn",
+   "fieldtype": "Section Break",
+   "label": "Reference"
+  },
+  {
+   "fieldname": "material_request",
+   "fieldtype": "Link",
+   "label": "Material Request",
+   "no_copy": 1,
+   "options": "Material Request",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_piqi",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "material_request_item",
+   "fieldtype": "Data",
+   "label": "Material Request Item",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-11-23 17:05:04.561948",
+ "modified": "2023-11-30 13:29:31.017440",
  "modified_by": "Administrator",
  "module": "Subcontracting",
  "name": "Subcontracting Order Service Item",