Merge branch 'develop' into subcontracting
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 686db9d..ec861a2 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -572,9 +572,10 @@
 	},
 
 	is_subcontracted: function(frm) {
-		if (frm.doc.is_subcontracted) {
+		if (frm.doc.is_old_subcontracting_flow) {
 			erpnext.buying.get_default_bom(frm);
 		}
+
 		frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted);
 	},
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 9f87c5a..534b879 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -169,7 +169,8 @@
   "column_break_114",
   "auto_repeat",
   "update_auto_repeat_reference",
-  "per_received"
+  "per_received",
+  "is_old_subcontracting_flow"
  ],
  "fields": [
   {
@@ -547,7 +548,8 @@
    "fieldname": "is_subcontracted",
    "fieldtype": "Check",
    "label": "Is Subcontracted",
-   "print_hide": 1
+   "print_hide": 1,
+   "read_only": 1
   },
   {
    "fieldname": "items_section",
@@ -1365,7 +1367,7 @@
    "width": "50px"
   },
   {
-   "depends_on": "eval:doc.update_stock && doc.is_subcontracted",
+    "depends_on": "eval:doc.is_subcontracted",
    "fieldname": "supplier_warehouse",
    "fieldtype": "Link",
    "label": "Supplier Warehouse",
@@ -1416,13 +1418,21 @@
    "label": "Advance Tax",
    "options": "Advance Tax",
    "read_only": 1
-  }
+  },
+  {
+   "default": "0",
+   "fieldname": "is_old_subcontracting_flow",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Is Old Subcontracting Flow",
+   "read_only": 1
+   }
  ],
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-11-25 13:31:02.716727",
+ "modified": "2022-06-15 15:40:58.527065",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 4e0d1c9..775d255 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -502,7 +502,10 @@
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		if self.update_stock == 1:
 			self.update_stock_ledger()
-			self.set_consumed_qty_in_po()
+
+			if self.is_old_subcontracting_flow:
+				self.set_consumed_qty_in_subcontract_order()
+
 			from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
 
 			update_serial_nos_after_submit(self, "items")
@@ -1405,7 +1408,9 @@
 		if self.update_stock == 1:
 			self.update_stock_ledger()
 			self.delete_auto_created_batches()
-			self.set_consumed_qty_in_po()
+
+			if self.is_old_subcontracting_flow:
+				self.set_consumed_qty_in_subcontract_order()
 
 		self.make_gl_entries_on_cancel()
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 427e481..e55d3a7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -470,37 +470,6 @@
 			self.assertEqual(tax.tax_amount, expected_values[i][1])
 			self.assertEqual(tax.total, expected_values[i][2])
 
-	def test_purchase_invoice_with_subcontracted_item(self):
-		wrapper = frappe.copy_doc(test_records[0])
-		wrapper.get("items")[0].item_code = "_Test FG Item"
-		wrapper.insert()
-		wrapper.load_from_db()
-
-		expected_values = [["_Test FG Item", 90, 59], ["_Test Item Home Desktop 200", 135, 177]]
-		for i, item in enumerate(wrapper.get("items")):
-			self.assertEqual(item.item_code, expected_values[i][0])
-			self.assertEqual(item.item_tax_amount, expected_values[i][1])
-			self.assertEqual(item.valuation_rate, expected_values[i][2])
-
-		self.assertEqual(wrapper.base_net_total, 1250)
-
-		# tax amounts
-		expected_values = [
-			["_Test Account Shipping Charges - _TC", 100, 1350],
-			["_Test Account Customs Duty - _TC", 125, 1350],
-			["_Test Account Excise Duty - _TC", 140, 1490],
-			["_Test Account Education Cess - _TC", 2.8, 1492.8],
-			["_Test Account S&H Education Cess - _TC", 1.4, 1494.2],
-			["_Test Account CST - _TC", 29.88, 1524.08],
-			["_Test Account VAT - _TC", 156.25, 1680.33],
-			["_Test Account Discount - _TC", 168.03, 1512.30],
-		]
-
-		for i, tax in enumerate(wrapper.get("taxes")):
-			self.assertEqual(tax.account_head, expected_values[i][0])
-			self.assertEqual(tax.tax_amount, expected_values[i][1])
-			self.assertEqual(tax.total, expected_values[i][2])
-
 	def test_purchase_invoice_with_advance(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
 			test_records as jv_test_records,
@@ -961,30 +930,6 @@
 		pi.cancel()
 		self.assertEqual(actual_qty_0, get_qty_after_transaction())
 
-	def test_subcontracting_via_purchase_invoice(self):
-		from erpnext.buying.doctype.purchase_order.test_purchase_order import update_backflush_based_on
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-
-		update_backflush_based_on("BOM")
-		make_stock_entry(
-			item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
-		)
-		make_stock_entry(
-			item_code="_Test Item Home Desktop 100",
-			target="_Test Warehouse 1 - _TC",
-			qty=100,
-			basic_rate=100,
-		)
-
-		pi = make_purchase_invoice(
-			item_code="_Test FG Item", qty=10, rate=500, update_stock=1, is_subcontracted=1
-		)
-
-		self.assertEqual(len(pi.get("supplied_items")), 2)
-
-		rm_supp_cost = sum(d.amount for d in pi.get("supplied_items"))
-		self.assertEqual(flt(pi.get("items")[0].rm_supp_cost, 2), flt(rm_supp_cost, 2))
-
 	def test_rejected_serial_no(self):
 		pi = make_purchase_invoice(
 			item_code="_Test Serialized Item With Series",
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 1f79d47..7fa2fe2 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -619,10 +619,13 @@
    "search_index": 1
   },
   {
+   "depends_on": "eval:parent.is_old_subcontracting_flow",
    "fieldname": "bom",
    "fieldtype": "Link",
    "label": "BOM",
-   "options": "BOM"
+   "options": "BOM",
+   "read_only": 1,
+   "read_only_depends_on": "eval:!parent.is_old_subcontracting_flow"
   },
   {
    "default": "0",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 33dbe3f..fbb42fe 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -8,15 +8,17 @@
 frappe.ui.form.on("Purchase Order", {
 	setup: function(frm) {
 
-		frm.set_query("reserve_warehouse", "supplied_items", function() {
-			return {
-				filters: {
-					"company": frm.doc.company,
-					"name": ['!=', frm.doc.supplier_warehouse],
-					"is_group": 0
+		if (frm.doc.is_old_subcontracting_flow) {
+			frm.set_query("reserve_warehouse", "supplied_items", function() {
+				return {
+					filters: {
+						"company": frm.doc.company,
+						"name": ['!=', frm.doc.supplier_warehouse],
+						"is_group": 0
+					}
 				}
-			}
-		});
+			});
+		}
 
 		frm.set_indicator_formatter('item_code',
 			function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
@@ -28,12 +30,67 @@
 			}
 		});
 
+		frm.set_query("fg_item", "items", function() {
+			return {
+				filters: {
+					'is_sub_contracted_item': 1,
+					'default_bom': ['!=', '']
+				}
+			}
+		});
 	},
 
 	company: function(frm) {
 		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
+	refresh: function(frm) {
+		if(frm.doc.is_old_subcontracting_flow) {
+			frm.trigger('get_materials_from_supplier');
+
+			$('a.grey-link').each(function () {
+				var id = $(this).children(':first-child').attr('data-label');
+				if (id == 'Duplicate') {
+					$(this).remove();
+					return false;
+				}
+			});
+		}
+	},
+
+	get_materials_from_supplier: function(frm) {
+		let po_details = [];
+
+		if (frm.doc.supplied_items && (frm.doc.per_received == 100 || frm.doc.status === 'Closed')) {
+			frm.doc.supplied_items.forEach(d => {
+				if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
+					po_details.push(d.name)
+				}
+			});
+		}
+
+		if (po_details && po_details.length) {
+			frm.add_custom_button(__('Return of Components'), () => {
+				frm.call({
+					method: 'erpnext.controllers.subcontracting_controller.get_materials_from_supplier',
+					freeze: true,
+					freeze_message: __('Creating Stock Entry'),
+					args: {
+						subcontract_order: frm.doc.name,
+						rm_details: po_details,
+						order_doctype: cur_frm.doc.doctype
+					},
+					callback: function(r) {
+						if (r && r.message) {
+							const doc = frappe.model.sync(r.message);
+							frappe.set_route("Form", doc[0].doctype, doc[0].name);
+						}
+					}
+				});
+			}, __('Create'));
+		}
+	},
+
 	onload: function(frm) {
 		set_schedule_date(frm);
 		if (!frm.doc.transaction_date){
@@ -52,39 +109,6 @@
 			frm.set_value("tax_withholding_category", frm.supplier_tds);
 		}
 	},
-
-	refresh: function(frm) {
-		frm.trigger('get_materials_from_supplier');
-	},
-
-	get_materials_from_supplier: function(frm) {
-		let po_details = [];
-
-		if (frm.doc.supplied_items && (frm.doc.per_received == 100 || frm.doc.status === 'Closed')) {
-			frm.doc.supplied_items.forEach(d => {
-				if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
-					po_details.push(d.name)
-				}
-			});
-		}
-
-		if (po_details && po_details.length) {
-			frm.add_custom_button(__('Return of Components'), () => {
-				frm.call({
-					method: 'erpnext.buying.doctype.purchase_order.purchase_order.get_materials_from_supplier',
-					freeze: true,
-					freeze_message: __('Creating Stock Entry'),
-					args: { purchase_order: frm.doc.name, po_details: po_details },
-					callback: function(r) {
-						if (r && r.message) {
-							const doc = frappe.model.sync(r.message);
-							frappe.set_route("Form", doc[0].doctype, doc[0].name);
-						}
-					}
-				});
-			}, __('Create'));
-		}
-	}
 });
 
 frappe.ui.form.on("Purchase Order Item", {
@@ -97,6 +121,16 @@
 				set_schedule_date(frm);
 			}
 		}
+	},
+
+	qty: function(frm, cdt, cdn) {
+		if (frm.doc.is_subcontracted && !frm.doc.is_old_subcontracting_flow) {
+			var row = locals[cdt][cdn];
+
+			if (row.qty) {
+				row.fg_item_qty = row.qty;
+			}
+		}
 	}
 });
 
@@ -105,12 +139,12 @@
 		this.frm.custom_make_buttons = {
 			'Purchase Receipt': 'Purchase Receipt',
 			'Purchase Invoice': 'Purchase Invoice',
-			'Stock Entry': 'Material to Supplier',
 			'Payment Entry': 'Payment',
+			'Subcontracting Order': 'Subcontracting Order',
+			'Stock Entry': 'Material to Supplier'
 		}
 
 		super.setup();
-
 	}
 
 	refresh(doc, cdt, cdn) {
@@ -142,14 +176,17 @@
 
 			if(!in_list(["Closed", "Delivered"], doc.status)) {
 				if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) {
-					this.frm.add_custom_button(__('Update Items'), () => {
-						erpnext.utils.update_child_items({
-							frm: this.frm,
-							child_docname: "items",
-							child_doctype: "Purchase Order Detail",
-							cannot_add_row: false,
-						})
-					});
+					// Don't add Update Items button if the PO is following the new subcontracting flow.
+					if (!(this.frm.doc.is_subcontracted && !this.frm.doc.is_old_subcontracting_flow)) {
+						this.frm.add_custom_button(__('Update Items'), () => {
+							erpnext.utils.update_child_items({
+								frm: this.frm,
+								child_docname: "items",
+								child_doctype: "Purchase Order Detail",
+								cannot_add_row: false,
+							})
+						});
+					}
 				}
 				if (this.frm.has_perm("submit")) {
 					if(flt(doc.per_billed, 6) < 100 || flt(doc.per_received, 6) < 100) {
@@ -177,9 +214,15 @@
 				if (doc.status != "On Hold") {
 					if(flt(doc.per_received) < 100 && allow_receipt) {
 						cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create'));
-						if(doc.is_subcontracted && me.has_unsupplied_items()) {
-							cur_frm.add_custom_button(__('Material to Supplier'),
-								function() { me.make_stock_entry(); }, __("Transfer"));
+						if (doc.is_subcontracted) {
+							if (doc.is_old_subcontracting_flow) {
+								if (me.has_unsupplied_items()) {
+									cur_frm.add_custom_button(__('Material to Supplier'), function() { me.make_stock_entry(); }, __("Transfer"));
+								}
+							}
+							else {
+								cur_frm.add_custom_button(__('Subcontracting Order'), this.make_subcontracting_order, __('Create'));
+							}
 						}
 					}
 					if(flt(doc.per_billed) < 100)
@@ -370,10 +413,11 @@
 
 	_make_rm_stock_entry(rm_items) {
 		frappe.call({
-			method:"erpnext.buying.doctype.purchase_order.purchase_order.make_rm_stock_entry",
+			method:"erpnext.controllers.subcontracting_controller.make_rm_stock_entry",
 			args: {
-				purchase_order: cur_frm.doc.name,
-				rm_items: rm_items
+				subcontract_order: cur_frm.doc.name,
+				rm_items: rm_items,
+				order_doctype: cur_frm.doc.doctype
 			}
 			,
 			callback: function(r) {
@@ -405,6 +449,14 @@
 		})
 	}
 
+	make_subcontracting_order() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.buying.doctype.purchase_order.purchase_order.make_subcontracting_order",
+			frm: cur_frm,
+			freeze_message: __("Creating Subcontracting Order ...")
+		})
+	}
+
 	add_from_mappers() {
 		var me = this;
 		this.frm.add_custom_button(__('Material Request'),
@@ -613,15 +665,17 @@
 	}
 }
 
-cur_frm.fields_dict['items'].grid.get_field('bom').get_query = function(doc, cdt, cdn) {
-	var d = locals[cdt][cdn]
-	return {
-		filters: [
-			['BOM', 'item', '=', d.item_code],
-			['BOM', 'is_active', '=', '1'],
-			['BOM', 'docstatus', '=', '1'],
-			['BOM', 'company', '=', doc.company]
-		]
+if (cur_frm.doc.is_old_subcontracting_flow) {
+	cur_frm.fields_dict['items'].grid.get_field('bom').get_query = function(doc, cdt, cdn) {
+		var d = locals[cdt][cdn]
+		return {
+			filters: [
+				['BOM', 'item', '=', d.item_code],
+				['BOM', 'is_active', '=', '1'],
+				['BOM', 'docstatus', '=', '1'],
+				['BOM', 'company', '=', doc.company]
+			]
+		}
 	}
 }
 
@@ -634,7 +688,7 @@
 frappe.provide("erpnext.buying");
 
 frappe.ui.form.on("Purchase Order", "is_subcontracted", function(frm) {
-	if (frm.doc.is_subcontracted) {
+	if (frm.doc.is_old_subcontracting_flow) {
 		erpnext.buying.get_default_bom(frm);
 	}
-});
+});
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index b365a83..aa50487 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -16,6 +16,8 @@
   "supplier_name",
   "apply_tds",
   "tax_withholding_category",
+  "is_subcontracted",
+  "supplier_warehouse",
   "column_break1",
   "company",
   "transaction_date",
@@ -55,10 +57,7 @@
   "price_list_currency",
   "plc_conversion_rate",
   "ignore_pricing_rule",
-  "sec_warehouse",
-  "is_subcontracted",
-  "col_break_warehouse",
-  "supplier_warehouse",
+  "section_break_45",
   "before_items_section",
   "scan_barcode",
   "items_col_break",
@@ -142,7 +141,8 @@
   "party_account_currency",
   "is_internal_supplier",
   "represents_company",
-  "inter_company_order_reference"
+  "inter_company_order_reference",
+  "is_old_subcontracting_flow"
  ],
  "fields": [
   {
@@ -158,7 +158,8 @@
    "hidden": 1,
    "label": "Title",
    "no_copy": 1,
-   "print_hide": 1
+   "print_hide": 1,
+   "reqd": 1
   },
   {
    "fieldname": "naming_series",
@@ -444,11 +445,6 @@
    "print_hide": 1
   },
   {
-   "fieldname": "sec_warehouse",
-   "fieldtype": "Section Break",
-   "label": "Subcontracting"
-  },
-  {
    "description": "Sets 'Warehouse' in each row of the Items table.",
    "fieldname": "set_warehouse",
    "fieldtype": "Link",
@@ -457,14 +453,9 @@
    "print_hide": 1
   },
   {
-   "fieldname": "col_break_warehouse",
-   "fieldtype": "Column Break"
-  },
-  {
    "default": "0",
    "fieldname": "is_subcontracted",
    "fieldtype": "Check",
-   "in_standard_filter": 1,
    "label": "Is Subcontracted",
    "print_hide": 1
   },
@@ -1143,6 +1134,10 @@
    "options": "Tax Withholding Category"
   },
   {
+   "fieldname": "section_break_45",
+   "fieldtype": "Section Break"
+  },
+  {
    "collapsible": 1,
    "fieldname": "accounting_dimensions_section",
    "fieldtype": "Section Break",
@@ -1163,13 +1158,21 @@
    "fieldtype": "Link",
    "label": "Project",
    "options": "Project"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_old_subcontracting_flow",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Is Old Subcontracting Flow",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-04-26 12:16:38.694276",
+ "modified": "2022-06-15 15:40:58.527065",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 44426ba..cd58d25 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -69,8 +69,12 @@
 		self.validate_with_previous_doc()
 		self.validate_for_subcontracting()
 		self.validate_minimum_order_qty()
-		self.validate_bom_for_subcontracting_items()
-		self.create_raw_materials_supplied("supplied_items")
+
+		if self.is_old_subcontracting_flow:
+			self.validate_bom_for_subcontracting_items()
+			self.create_raw_materials_supplied()
+
+		self.validate_fg_item_for_subcontracting()
 		self.set_received_qty_for_drop_ship_items()
 		validate_inter_company_party(
 			self.doctype, self.supplier, self.company, self.inter_company_order_reference
@@ -194,12 +198,38 @@
 				)
 
 	def validate_bom_for_subcontracting_items(self):
-		if self.is_subcontracted:
+		for item in self.items:
+			if not item.bom:
+				frappe.throw(
+					_("Row #{0}: BOM is not specified for subcontracting item {0}").format(
+						item.idx, item.item_code
+					)
+				)
+
+	def validate_fg_item_for_subcontracting(self):
+		if self.is_subcontracted and not self.is_old_subcontracting_flow:
 			for item in self.items:
-				if not item.bom:
+				if not item.fg_item:
 					frappe.throw(
-						_("BOM is not specified for subcontracting item {0} at row {1}").format(
-							item.item_code, item.idx
+						_("Row #{0}: Finished Good Item is not specified for service item {1}").format(
+							item.idx, item.item_code
+						)
+					)
+				else:
+					if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
+						frappe.throw(
+							_(
+								"Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}"
+							).format(item.idx, item.fg_item, item.item_code)
+						)
+					elif not frappe.get_value("Item", item.fg_item, "default_bom"):
+						frappe.throw(
+							_("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
+						)
+				if not item.fg_item_qty:
+					frappe.throw(
+						_("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format(
+							item.idx, item.item_code
 						)
 					)
 
@@ -294,9 +324,7 @@
 		self.set_status(update=True, status=status)
 		self.update_requested_qty()
 		self.update_ordered_qty()
-		if self.is_subcontracted:
-			self.update_reserved_qty_for_subcontract()
-
+		self.update_reserved_qty_for_subcontract()
 		self.notify_update()
 		clear_doctype_notifications(self)
 
@@ -310,9 +338,7 @@
 		self.update_requested_qty()
 		self.update_ordered_qty()
 		self.validate_budget()
-
-		if self.is_subcontracted:
-			self.update_reserved_qty_for_subcontract()
+		self.update_reserved_qty_for_subcontract()
 
 		frappe.get_doc("Authorization Control").validate_approving_authority(
 			self.doctype, self.company, self.base_grand_total
@@ -332,9 +358,7 @@
 		if self.has_drop_ship_item():
 			self.update_delivered_qty_in_sales_order()
 
-		if self.is_subcontracted:
-			self.update_reserved_qty_for_subcontract()
-
+		self.update_reserved_qty_for_subcontract()
 		self.check_on_hold_or_closed_status()
 
 		frappe.db.set(self, "status", "Cancelled")
@@ -405,10 +429,11 @@
 				item.received_qty = item.qty
 
 	def update_reserved_qty_for_subcontract(self):
-		for d in self.supplied_items:
-			if d.rm_item_code:
-				stock_bin = get_bin(d.rm_item_code, d.reserve_warehouse)
-				stock_bin.update_reserved_qty_for_sub_contracting()
+		if self.is_old_subcontracting_flow:
+			for d in self.supplied_items:
+				if d.rm_item_code:
+					stock_bin = get_bin(d.rm_item_code, d.reserve_warehouse)
+					stock_bin.update_reserved_qty_for_sub_contracting(subcontract_doctype="Purchase Order")
 
 	def update_receiving_percentage(self):
 		total_qty, received_qty = 0.0, 0.0
@@ -587,80 +612,6 @@
 	return doc
 
 
-@frappe.whitelist()
-def make_rm_stock_entry(purchase_order, rm_items):
-	rm_items_list = rm_items
-
-	if isinstance(rm_items, str):
-		rm_items_list = json.loads(rm_items)
-	elif not rm_items:
-		frappe.throw(_("No Items available for transfer"))
-
-	if rm_items_list:
-		fg_items = list(set(d["item_code"] for d in rm_items_list))
-	else:
-		frappe.throw(_("No Items selected for transfer"))
-
-	if purchase_order:
-		purchase_order = frappe.get_doc("Purchase Order", purchase_order)
-
-	if fg_items:
-		items = tuple(set(d["rm_item_code"] for d in rm_items_list))
-		item_wh = get_item_details(items)
-
-		stock_entry = frappe.new_doc("Stock Entry")
-		stock_entry.purpose = "Send to Subcontractor"
-		stock_entry.purchase_order = purchase_order.name
-		stock_entry.supplier = purchase_order.supplier
-		stock_entry.supplier_name = purchase_order.supplier_name
-		stock_entry.supplier_address = purchase_order.supplier_address
-		stock_entry.address_display = purchase_order.address_display
-		stock_entry.company = purchase_order.company
-		stock_entry.to_warehouse = purchase_order.supplier_warehouse
-		stock_entry.set_stock_entry_type()
-
-		for item_code in fg_items:
-			for rm_item_data in rm_items_list:
-				if rm_item_data["item_code"] == item_code:
-					rm_item_code = rm_item_data["rm_item_code"]
-					items_dict = {
-						rm_item_code: {
-							"po_detail": rm_item_data.get("name"),
-							"item_name": rm_item_data["item_name"],
-							"description": item_wh.get(rm_item_code, {}).get("description", ""),
-							"qty": rm_item_data["qty"],
-							"from_warehouse": rm_item_data["warehouse"],
-							"stock_uom": rm_item_data["stock_uom"],
-							"serial_no": rm_item_data.get("serial_no"),
-							"batch_no": rm_item_data.get("batch_no"),
-							"main_item_code": rm_item_data["item_code"],
-							"allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"),
-						}
-					}
-					stock_entry.add_to_stock_entry_detail(items_dict)
-
-		stock_entry.set_missing_values()
-		return stock_entry.as_dict()
-	else:
-		frappe.throw(_("No Items selected for transfer"))
-	return purchase_order.name
-
-
-def get_item_details(items):
-	item_details = {}
-	for d in frappe.db.sql(
-		"""select item_code, description, allow_alternative_item from `tabItem`
-		where name in ({0})""".format(
-			", ".join(["%s"] * len(items))
-		),
-		items,
-		as_dict=1,
-	):
-		item_details[d.item_code] = d
-
-	return item_details
-
-
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
 
@@ -691,61 +642,61 @@
 
 
 @frappe.whitelist()
-def get_materials_from_supplier(purchase_order, po_details):
-	if isinstance(po_details, str):
-		po_details = json.loads(po_details)
-
-	doc = frappe.get_cached_doc("Purchase Order", purchase_order)
-	doc.initialized_fields()
-	doc.purchase_orders = [doc.name]
-	doc.get_available_materials()
-
-	if not doc.available_materials:
-		frappe.throw(
-			_("Materials are already received against the purchase order {0}").format(purchase_order)
-		)
-
-	return make_return_stock_entry_for_subcontract(doc.available_materials, doc, po_details)
+def make_subcontracting_order(source_name, target_doc=None):
+	return get_mapped_subcontracting_order(source_name, target_doc)
 
 
-def make_return_stock_entry_for_subcontract(available_materials, po_doc, po_details):
-	ste_doc = frappe.new_doc("Stock Entry")
-	ste_doc.purpose = "Material Transfer"
-	ste_doc.purchase_order = po_doc.name
-	ste_doc.company = po_doc.company
-	ste_doc.is_return = 1
+def get_mapped_subcontracting_order(source_name, target_doc=None):
 
-	for key, value in available_materials.items():
-		if not value.qty:
-			continue
+	if target_doc and isinstance(target_doc, str):
+		target_doc = json.loads(target_doc)
+		for key in ["service_items", "items", "supplied_items"]:
+			if key in target_doc:
+				del target_doc[key]
+		target_doc = json.dumps(target_doc)
 
-		if value.batch_no:
-			for batch_no, qty in value.batch_no.items():
-				if qty > 0:
-					add_items_in_ste(ste_doc, value, value.qty, po_details, batch_no)
-		else:
-			add_items_in_ste(ste_doc, value, value.qty, po_details)
-
-	ste_doc.set_stock_entry_type()
-	ste_doc.set_missing_values()
-
-	return ste_doc
-
-
-def add_items_in_ste(ste_doc, row, qty, po_details, batch_no=None):
-	item = ste_doc.append("items", row.item_details)
-
-	po_detail = list(set(row.po_details).intersection(po_details))
-	item.update(
+	target_doc = get_mapped_doc(
+		"Purchase Order",
+		source_name,
 		{
-			"qty": qty,
-			"batch_no": batch_no,
-			"basic_rate": row.item_details["rate"],
-			"po_detail": po_detail[0] if po_detail else "",
-			"s_warehouse": row.item_details["t_warehouse"],
-			"t_warehouse": row.item_details["s_warehouse"],
-			"item_code": row.item_details["rm_item_code"],
-			"subcontracted_item": row.item_details["main_item_code"],
-			"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
-		}
+			"Purchase Order": {
+				"doctype": "Subcontracting Order",
+				"field_map": {},
+				"field_no_map": ["total_qty", "total", "net_total"],
+				"validation": {
+					"docstatus": ["=", 1],
+				},
+			},
+			"Purchase Order Item": {
+				"doctype": "Subcontracting Order Service Item",
+				"field_map": {},
+				"field_no_map": [],
+			},
+		},
+		target_doc,
 	)
+
+	target_doc.populate_items_table()
+
+	if target_doc.set_warehouse:
+		for item in target_doc.items:
+			item.warehouse = target_doc.set_warehouse
+	else:
+		source_doc = frappe.get_doc("Purchase Order", source_name)
+		if source_doc.set_warehouse:
+			for item in target_doc.items:
+				item.warehouse = source_doc.set_warehouse
+		else:
+			for idx, item in enumerate(target_doc.items):
+				item.warehouse = source_doc.items[idx].warehouse
+
+	return target_doc
+
+
+@frappe.whitelist()
+def is_subcontracting_order_created(po_name) -> bool:
+	count = frappe.db.count(
+		"Subcontracting Order", {"purchase_order": po_name, "status": ["not in", ["Draft", "Cancelled"]]}
+	)
+
+	return True if count else False
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
index 81f2010..01b55c0 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py
@@ -22,6 +22,6 @@
 				"label": _("Reference"),
 				"items": ["Material Request", "Supplier Quotation", "Project", "Auto Repeat"],
 			},
-			{"label": _("Sub-contracting"), "items": ["Stock Entry"]},
+			{"label": _("Sub-contracting"), "items": ["Subcontracting Order", "Stock Entry"]},
 		],
 	}
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 5f84de6..bd7e4e8 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -13,9 +13,6 @@
 	make_purchase_invoice as make_pi_from_po,
 )
 from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
-from erpnext.buying.doctype.purchase_order.purchase_order import (
-	make_rm_stock_entry as make_subcontract_transfer_entry,
-)
 from erpnext.controllers.accounts_controller import update_child_qty_rate
 from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
 from erpnext.stock.doctype.item.test_item import make_item
@@ -24,7 +21,6 @@
 from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
 	make_purchase_invoice as make_pi_from_pr,
 )
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
 
 class TestPurchaseOrder(FrappeTestCase):
@@ -140,43 +136,6 @@
 		# ordered qty decreases as ordered qty is 0 (deleted row)
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty - 10)  # 0
 
-	def test_supplied_items_validations_on_po_update_after_submit(self):
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1, qty=5, rate=100)
-		item = po.items[0]
-
-		original_supplied_items = {po.name: po.required_qty for po in po.supplied_items}
-
-		# Just update rate
-		trans_item = [
-			{
-				"item_code": "_Test FG Item",
-				"rate": 20,
-				"qty": 5,
-				"conversion_factor": 1.0,
-				"docname": item.name,
-			}
-		]
-		update_child_qty_rate("Purchase Order", json.dumps(trans_item), po.name)
-		po.reload()
-
-		new_supplied_items = {po.name: po.required_qty for po in po.supplied_items}
-		self.assertEqual(set(original_supplied_items.keys()), set(new_supplied_items.keys()))
-
-		# Update qty to 2x
-		trans_item[0]["qty"] *= 2
-		update_child_qty_rate("Purchase Order", json.dumps(trans_item), po.name)
-		po.reload()
-
-		new_supplied_items = {po.name: po.required_qty for po in po.supplied_items}
-		self.assertEqual(2 * sum(original_supplied_items.values()), sum(new_supplied_items.values()))
-
-		# Set transfer qty and attempt to update qty, shouldn't be allowed
-		po.supplied_items[0].supplied_qty = 2
-		po.supplied_items[0].db_update()
-		trans_item[0]["qty"] *= 2
-		with self.assertRaises(frappe.ValidationError):
-			update_child_qty_rate("Purchase Order", json.dumps(trans_item), po.name)
-
 	def test_update_child(self):
 		mr = make_material_request(qty=10)
 		po = make_purchase_order(mr.name)
@@ -426,31 +385,6 @@
 		new_item_with_tax.delete()
 		frappe.get_doc("Item Tax Template", "Test Update Items Template - _TC").delete()
 
-	def test_update_child_uom_conv_factor_change(self):
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
-		total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
-
-		trans_item = json.dumps(
-			[
-				{
-					"item_code": po.get("items")[0].item_code,
-					"rate": po.get("items")[0].rate,
-					"qty": po.get("items")[0].qty,
-					"uom": "_Test UOM 1",
-					"conversion_factor": 2,
-					"docname": po.get("items")[0].name,
-				}
-			]
-		)
-		update_child_qty_rate("Purchase Order", trans_item, po.name)
-		po.reload()
-
-		total_reqd_qty_after_change = sum(
-			d.get("required_qty") for d in po.as_dict().get("supplied_items")
-		)
-
-		self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty)
-
 	def test_update_qty(self):
 		po = create_purchase_order()
 
@@ -609,10 +543,6 @@
 		)
 		automatically_fetch_payment_terms(enable=0)
 
-	def test_subcontracting(self):
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
-		self.assertEqual(len(po.get("supplied_items")), 2)
-
 	def test_warehouse_company_validation(self):
 		from erpnext.stock.utils import InvalidWarehouseCompany
 
@@ -777,379 +707,6 @@
 		pi.insert()
 		self.assertTrue(pi.get("payment_schedule"))
 
-	def test_reserved_qty_subcontract_po(self):
-		# Make stock available for raw materials
-		make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=30, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse 1 - _TC",
-			item_code="_Test Item Home Desktop 100",
-			qty=30,
-			basic_rate=100,
-		)
-
-		bin1 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"],
-			as_dict=1,
-		)
-
-		# Submit PO
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
-
-		bin2 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"],
-			as_dict=1,
-		)
-
-		self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
-		self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10)
-		self.assertNotEqual(bin1.modified, bin2.modified)
-
-		# Create stock transfer
-		rm_item = [
-			{
-				"item_code": "_Test FG Item",
-				"rm_item_code": "_Test Item",
-				"item_name": "_Test Item",
-				"qty": 6,
-				"warehouse": "_Test Warehouse - _TC",
-				"rate": 100,
-				"amount": 600,
-				"stock_uom": "Nos",
-			}
-		]
-		rm_item_string = json.dumps(rm_item)
-		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
-		se.to_warehouse = "_Test Warehouse 1 - _TC"
-		se.save()
-		se.submit()
-
-		bin3 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
-
-		# close PO
-		po.update_status("Closed")
-		bin4 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
-
-		# Re-open PO
-		po.update_status("Submitted")
-		bin5 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
-
-		make_stock_entry(
-			target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=40, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse 1 - _TC",
-			item_code="_Test Item Home Desktop 100",
-			qty=40,
-			basic_rate=100,
-		)
-
-		# make Purchase Receipt against PO
-		pr = make_purchase_receipt(po.name)
-		pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
-		pr.save()
-		pr.submit()
-
-		bin6 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
-
-		# Cancel PR
-		pr.cancel()
-		bin7 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
-
-		# Make Purchase Invoice
-		pi = make_pi_from_po(po.name)
-		pi.update_stock = 1
-		pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
-		pi.insert()
-		pi.submit()
-		bin8 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
-
-		# Cancel PR
-		pi.cancel()
-		bin9 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
-
-		# Cancel Stock Entry
-		se.cancel()
-		bin10 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
-
-		# Cancel PO
-		po.reload()
-		po.cancel()
-		bin11 = frappe.db.get_value(
-			"Bin",
-			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
-			fieldname="reserved_qty_for_sub_contract",
-			as_dict=1,
-		)
-
-		self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
-
-	def test_exploded_items_in_subcontracted(self):
-		item_code = "_Test Subcontracted FG Item 11"
-		make_subcontracted_item(item_code=item_code)
-
-		po = create_purchase_order(
-			item_code=item_code,
-			qty=1,
-			is_subcontracted=1,
-			supplier_warehouse="_Test Warehouse 1 - _TC",
-			include_exploded_items=1,
-		)
-
-		name = frappe.db.get_value("BOM", {"item": item_code}, "name")
-		bom = frappe.get_doc("BOM", name)
-
-		exploded_items = sorted(
-			[d.item_code for d in bom.exploded_items if not d.get("sourced_by_supplier")]
-		)
-		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
-		self.assertEqual(exploded_items, supplied_items)
-
-		po1 = create_purchase_order(
-			item_code=item_code,
-			qty=1,
-			is_subcontracted=1,
-			supplier_warehouse="_Test Warehouse 1 - _TC",
-			include_exploded_items=0,
-		)
-
-		supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items])
-		bom_items = sorted([d.item_code for d in bom.items if not d.get("sourced_by_supplier")])
-
-		self.assertEqual(supplied_items1, bom_items)
-
-	def test_backflush_based_on_stock_entry(self):
-		item_code = "_Test Subcontracted FG Item 1"
-		make_subcontracted_item(item_code=item_code)
-		make_item("Sub Contracted Raw Material 1", {"is_stock_item": 1, "is_sub_contracted_item": 1})
-
-		update_backflush_based_on("Material Transferred for Subcontract")
-
-		order_qty = 5
-		po = create_purchase_order(
-			item_code=item_code,
-			qty=order_qty,
-			is_subcontracted=1,
-			supplier_warehouse="_Test Warehouse 1 - _TC",
-		)
-
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=100, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=10, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code="Sub Contracted Raw Material 1",
-			qty=10,
-			basic_rate=100,
-		)
-
-		rm_items = [
-			{
-				"item_code": item_code,
-				"rm_item_code": "Sub Contracted Raw Material 1",
-				"item_name": "_Test Item",
-				"qty": 10,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-			},
-			{
-				"item_code": item_code,
-				"rm_item_code": "_Test Item Home Desktop 100",
-				"item_name": "_Test Item Home Desktop 100",
-				"qty": 20,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-			},
-			{
-				"item_code": item_code,
-				"rm_item_code": "Test Extra Item 1",
-				"item_name": "Test Extra Item 1",
-				"qty": 10,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-			},
-			{
-				"item_code": item_code,
-				"rm_item_code": "Test Extra Item 2",
-				"stock_uom": "Nos",
-				"qty": 10,
-				"warehouse": "_Test Warehouse - _TC",
-				"item_name": "Test Extra Item 2",
-			},
-		]
-
-		rm_item_string = json.dumps(rm_items)
-		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
-		se.submit()
-
-		pr = make_purchase_receipt(po.name)
-
-		received_qty = 2
-		# partial receipt
-		pr.get("items")[0].qty = received_qty
-		pr.save()
-		pr.submit()
-
-		transferred_items = sorted(
-			[d.item_code for d in se.get("items") if se.purchase_order == po.name]
-		)
-		issued_items = sorted([d.rm_item_code for d in pr.get("supplied_items")])
-
-		self.assertEqual(transferred_items, issued_items)
-		self.assertEqual(pr.get("items")[0].rm_supp_cost, 2000)
-
-		transferred_rm_map = frappe._dict()
-		for item in rm_items:
-			transferred_rm_map[item.get("rm_item_code")] = item
-
-		update_backflush_based_on("BOM")
-
-	def test_supplied_qty_against_subcontracted_po(self):
-		item_code = "_Test Subcontracted FG Item 5"
-		make_item("Sub Contracted Raw Material 4", {"is_stock_item": 1, "is_sub_contracted_item": 1})
-
-		make_subcontracted_item(item_code=item_code, raw_materials=["Sub Contracted Raw Material 4"])
-
-		update_backflush_based_on("Material Transferred for Subcontract")
-
-		order_qty = 250
-		po = create_purchase_order(
-			item_code=item_code,
-			qty=order_qty,
-			is_subcontracted=1,
-			supplier_warehouse="_Test Warehouse 1 - _TC",
-			do_not_save=True,
-		)
-
-		# Add same subcontracted items multiple times
-		po.append(
-			"items",
-			{
-				"item_code": item_code,
-				"qty": order_qty,
-				"schedule_date": add_days(nowdate(), 1),
-				"warehouse": "_Test Warehouse - _TC",
-			},
-		)
-
-		po.set_missing_values()
-		po.submit()
-
-		# Material receipt entry for the raw materials which will be send to supplier
-		make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code="Sub Contracted Raw Material 4",
-			qty=500,
-			basic_rate=100,
-		)
-
-		rm_items = [
-			{
-				"item_code": item_code,
-				"rm_item_code": "Sub Contracted Raw Material 4",
-				"item_name": "_Test Item",
-				"qty": 250,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-				"name": po.supplied_items[0].name,
-			},
-			{
-				"item_code": item_code,
-				"rm_item_code": "Sub Contracted Raw Material 4",
-				"item_name": "_Test Item",
-				"qty": 250,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-			},
-		]
-
-		# Raw Materials transfer entry from stores to supplier's warehouse
-		rm_item_string = json.dumps(rm_items)
-		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
-		se.submit()
-
-		# Test po_detail field has value or not
-		for item_row in se.items:
-			self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name)
-
-		po_doc = frappe.get_doc("Purchase Order", po.name)
-		for row in po_doc.supplied_items:
-			# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
-			self.assertEqual(row.supplied_qty, 250.0)
-
-		update_backflush_based_on("BOM")
-
 	def test_advance_payment_entry_unlink_against_purchase_order(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
 
@@ -1248,50 +805,6 @@
 	return pr
 
 
-def make_subcontracted_item(**args):
-	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-
-	args = frappe._dict(args)
-
-	if not frappe.db.exists("Item", args.item_code):
-		make_item(
-			args.item_code,
-			{
-				"is_stock_item": 1,
-				"is_sub_contracted_item": 1,
-				"has_batch_no": args.get("has_batch_no") or 0,
-			},
-		)
-
-	if not args.raw_materials:
-		if not frappe.db.exists("Item", "Test Extra Item 1"):
-			make_item(
-				"Test Extra Item 1",
-				{
-					"is_stock_item": 1,
-				},
-			)
-
-		if not frappe.db.exists("Item", "Test Extra Item 2"):
-			make_item(
-				"Test Extra Item 2",
-				{
-					"is_stock_item": 1,
-				},
-			)
-
-		args.raw_materials = ["_Test FG Item", "Test Extra Item 1"]
-
-	if not frappe.db.get_value("BOM", {"item": args.item_code}, "name"):
-		make_bom(item=args.item_code, raw_materials=args.get("raw_materials"))
-
-
-def update_backflush_based_on(based_on):
-	doc = frappe.get_doc("Buying Settings")
-	doc.backflush_raw_materials_of_subcontract_based_on = based_on
-	doc.save()
-
-
 def get_same_items():
 	return [
 		{
diff --git a/erpnext/buying/doctype/purchase_order/test_records.json b/erpnext/buying/doctype/purchase_order/test_records.json
index 896050c..4df994a 100644
--- a/erpnext/buying/doctype/purchase_order/test_records.json
+++ b/erpnext/buying/doctype/purchase_order/test_records.json
@@ -8,40 +8,6 @@
   "doctype": "Purchase Order", 
   "base_grand_total": 5000.0, 
   "grand_total": 5000.0, 
-  "is_subcontracted": 1, 
-  "naming_series": "_T-Purchase Order-", 
-  "base_net_total": 5000.0, 
-  "items": [
-   {
-    "base_amount": 5000.0, 
-    "conversion_factor": 1.0, 
-    "description": "_Test FG Item", 
-    "doctype": "Purchase Order Item", 
-    "item_code": "_Test FG Item", 
-    "item_name": "_Test FG Item", 
-    "parentfield": "items", 
-    "qty": 10.0, 
-    "rate": 500.0, 
-    "schedule_date": "2013-03-01", 
-    "stock_uom": "_Test UOM", 
-    "uom": "_Test UOM", 
-    "warehouse": "_Test Warehouse - _TC"
-   }
-  ], 
-  "supplier": "_Test Supplier", 
-  "supplier_name": "_Test Supplier", 
-  "transaction_date": "2013-02-12",
-  "schedule_date": "2013-02-13"
- },
- {
-  "advance_paid": 0.0,  
-  "buying_price_list": "_Test Price List", 
-  "company": "_Test Company", 
-  "conversion_rate": 1.0, 
-  "currency": "INR", 
-  "doctype": "Purchase Order", 
-  "base_grand_total": 5000.0, 
-  "grand_total": 5000.0, 
   "is_subcontracted": 0, 
   "naming_series": "_T-Purchase Order-", 
   "base_net_total": 5000.0, 
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 7994b08..1a98453 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -11,6 +11,8 @@
   "supplier_part_no",
   "item_name",
   "product_bundle",
+  "fg_item",
+  "fg_item_qty",
   "column_break_4",
   "schedule_date",
   "expected_delivery_date",
@@ -574,16 +576,18 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:parent.is_subcontracted",
+   "depends_on": "eval:parent.is_old_subcontracting_flow",
    "fieldname": "bom",
    "fieldtype": "Link",
    "label": "BOM",
    "options": "BOM",
-   "print_hide": 1
+   "print_hide": 1,
+   "read_only": 1,
+   "read_only_depends_on": "eval:!parent.is_old_subcontracting_flow"
   },
   {
    "default": "0",
-   "depends_on": "eval:parent.is_subcontracted",
+   "depends_on": "eval:parent.is_old_subcontracting_flow",
    "fieldname": "include_exploded_items",
    "fieldtype": "Check",
    "label": "Include Exploded Items",
@@ -848,6 +852,22 @@
    "label": "Sales Order Packed Item",
    "no_copy": 1,
    "print_hide": 1
+  },
+  {
+   "depends_on": "eval:parent.is_subcontracted && !parent.is_old_subcontracting_flow",
+   "fieldname": "fg_item",
+   "fieldtype": "Link",
+   "label": "Finished Good Item",
+   "mandatory_depends_on": "eval:parent.is_subcontracted && !parent.is_old_subcontracting_flow",
+   "options": "Item"
+  },
+  {
+   "default": "1",
+   "depends_on": "eval:parent.is_subcontracted && !parent.is_old_subcontracting_flow",
+   "fieldname": "fg_item_qty",
+   "fieldtype": "Float",
+   "label": "Finished Good Item Qty",
+   "mandatory_depends_on": "eval:parent.is_subcontracted && !parent.is_old_subcontracting_flow"
   }
  ],
  "idx": 1,
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
index 6889322..075671f 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
@@ -14,32 +14,29 @@
 		},
 		{
 			label: __("From Date"),
-			fieldname:"from_date",
+			fieldname: "from_date",
 			fieldtype: "Date",
 			default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
 			reqd: 1
 		},
 		{
 			label: __("To Date"),
-			fieldname:"to_date",
+			fieldname: "to_date",
 			fieldtype: "Date",
 			default: frappe.datetime.get_today(),
 			reqd: 1
 		},
 		{
-			label: __("Purchase Order"),
+			label: __("Order Type"),
+			fieldname: "order_type",
+			fieldtype: "Select",
+			options: ["Purchase Order", "Subcontracting Order"],
+			default: "Subcontracting Order"
+		},
+		{
+			label: __("Subcontract Order"),
 			fieldname: "name",
-			fieldtype: "Link",
-			options: "Purchase Order",
-			get_query: function() {
-				return {
-					filters: {
-						docstatus: 1,
-						is_subcontracted: 1,
-						company: frappe.query_report.get_filter_value('company')
-					}
-				}
-			}
+			fieldtype: "Data"
 		}
 	]
-};
+};
\ No newline at end of file
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json
index 526a8d8..7861e49 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json
@@ -15,7 +15,7 @@
  "name": "Subcontract Order Summary",
  "owner": "Administrator",
  "prepared_report": 0,
- "ref_doctype": "Purchase Order",
+ "ref_doctype": "Subcontracting Order",
  "report_name": "Subcontract Order Summary",
  "report_type": "Script Report",
  "roles": [
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
index 3d66637..0213051 100644
--- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
@@ -8,7 +8,7 @@
 
 def execute(filters=None):
 	columns, data = [], []
-	columns = get_columns()
+	columns = get_columns(filters)
 	data = get_data(filters)
 
 	return columns, data
@@ -20,43 +20,45 @@
 
 	if orders:
 		supplied_items = get_supplied_items(orders, report_filters)
-		po_details = prepare_subcontracted_data(orders, supplied_items)
-		get_subcontracted_data(po_details, data)
+		order_details = prepare_subcontracted_data(orders, supplied_items)
+		get_subcontracted_data(order_details, data)
 
 	return data
 
 
 def get_subcontracted_orders(report_filters):
 	fields = [
-		"`tabPurchase Order Item`.`parent` as po_id",
-		"`tabPurchase Order Item`.`item_code`",
-		"`tabPurchase Order Item`.`item_name`",
-		"`tabPurchase Order Item`.`qty`",
-		"`tabPurchase Order Item`.`name`",
-		"`tabPurchase Order Item`.`received_qty`",
-		"`tabPurchase Order`.`status`",
+		f"`tab{report_filters.order_type} Item`.`parent` as order_id",
+		f"`tab{report_filters.order_type} Item`.`item_code`",
+		f"`tab{report_filters.order_type} Item`.`item_name`",
+		f"`tab{report_filters.order_type} Item`.`qty`",
+		f"`tab{report_filters.order_type} Item`.`name`",
+		f"`tab{report_filters.order_type} Item`.`received_qty`",
+		f"`tab{report_filters.order_type}`.`status`",
 	]
 
 	filters = get_filters(report_filters)
 
-	return frappe.get_all("Purchase Order", fields=fields, filters=filters) or []
+	return frappe.get_all(report_filters.order_type, fields=fields, filters=filters) or []
 
 
 def get_filters(report_filters):
 	filters = [
-		["Purchase Order", "docstatus", "=", 1],
-		["Purchase Order", "is_subcontracted", "=", 1],
+		[report_filters.order_type, "docstatus", "=", 1],
 		[
-			"Purchase Order",
+			report_filters.order_type,
 			"transaction_date",
 			"between",
 			(report_filters.from_date, report_filters.to_date),
 		],
 	]
 
+	if report_filters.order_type == "Purchase Order":
+		filters.append(["Purchase Order", "is_old_subcontracting_flow", "=", 1])
+
 	for field in ["name", "company"]:
 		if report_filters.get(field):
-			filters.append(["Purchase Order", field, "=", report_filters.get(field)])
+			filters.append([report_filters.order_type, field, "=", report_filters.get(field)])
 
 	return filters
 
@@ -77,10 +79,15 @@
 		"reference_name",
 	]
 
-	filters = {"parent": ("in", [d.po_id for d in orders]), "docstatus": 1}
+	filters = {"parent": ("in", [d.order_id for d in orders]), "docstatus": 1}
 
 	supplied_items = {}
-	for row in frappe.get_all("Purchase Order Item Supplied", fields=fields, filters=filters):
+	supplied_items_table = (
+		"Purchase Order Item Supplied"
+		if report_filters.order_type == "Purchase Order"
+		else "Subcontracting Order Supplied Item"
+	)
+	for row in frappe.get_all(supplied_items_table, fields=fields, filters=filters):
 		new_key = (row.parent, row.reference_name, row.main_item_code)
 
 		supplied_items.setdefault(new_key, []).append(row)
@@ -89,24 +96,24 @@
 
 
 def prepare_subcontracted_data(orders, supplied_items):
-	po_details = {}
+	order_details = {}
 	for row in orders:
-		key = (row.po_id, row.name, row.item_code)
-		if key not in po_details:
-			po_details.setdefault(key, frappe._dict({"po_item": row, "supplied_items": []}))
+		key = (row.order_id, row.name, row.item_code)
+		if key not in order_details:
+			order_details.setdefault(key, frappe._dict({"order_item": row, "supplied_items": []}))
 
-		details = po_details[key]
+		details = order_details[key]
 
 		if supplied_items.get(key):
 			for supplied_item in supplied_items[key]:
 				details["supplied_items"].append(supplied_item)
 
-	return po_details
+	return order_details
 
 
-def get_subcontracted_data(po_details, data):
-	for key, details in po_details.items():
-		res = details.po_item
+def get_subcontracted_data(order_details, data):
+	for key, details in order_details.items():
+		res = details.order_item
 		for index, row in enumerate(details.supplied_items):
 			if index != 0:
 				res = {}
@@ -115,13 +122,13 @@
 			data.append(res)
 
 
-def get_columns():
+def get_columns(filters):
 	return [
 		{
-			"label": _("Purchase Order"),
-			"fieldname": "po_id",
+			"label": _("Subcontract Order"),
+			"fieldname": "order_id",
 			"fieldtype": "Link",
-			"options": "Purchase Order",
+			"options": filters.order_type,
 			"width": 100,
 		},
 		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 80},
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js
index fc58b6a..6304a09 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js
@@ -5,6 +5,13 @@
 frappe.query_reports["Subcontracted Item To Be Received"] = {
 	"filters": [
 		{
+			label: __("Order Type"),
+			fieldname: "order_type",
+			fieldtype: "Select",
+			options: ["Purchase Order", "Subcontracting Order"],
+			default: "Subcontracting Order"
+		},
+		{
 			fieldname: "supplier",
 			label: __("Supplier"),
 			fieldtype: "Link",
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.json b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.json
index fdf6cf7..f40b788 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.json
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.json
@@ -13,7 +13,7 @@
  "name": "Subcontracted Item To Be Received",
  "owner": "Administrator",
  "prepared_report": 0,
- "ref_doctype": "Purchase Order",
+ "ref_doctype": "Subcontracting Order",
  "report_name": "Subcontracted Item To Be Received",
  "report_type": "Script Report",
  "roles": [
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
index 2e90de6..135449b 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py
@@ -11,18 +11,18 @@
 		frappe.msgprint(_("To Date must be greater than From Date"))
 
 	data = []
-	columns = get_columns()
+	columns = get_columns(filters)
 	get_data(data, filters)
 	return columns, data
 
 
-def get_columns():
+def get_columns(filters):
 	return [
 		{
-			"label": _("Purchase Order"),
+			"label": _("Subcontract Order"),
 			"fieldtype": "Link",
-			"fieldname": "purchase_order",
-			"options": "Purchase Order",
+			"fieldname": "subcontract_order",
+			"options": filters.order_type,
 			"width": 150,
 		},
 		{"label": _("Date"), "fieldtype": "Date", "fieldname": "date", "hidden": 1, "width": 150},
@@ -57,14 +57,14 @@
 
 
 def get_data(data, filters):
-	po = get_po(filters)
-	po_name = [v.name for v in po]
-	sub_items = get_purchase_order_item_supplied(po_name)
-	for item in sub_items:
-		for order in po:
+	orders = get_subcontract_orders(filters)
+	orders_name = [order.name for order in orders]
+	subcontracted_items = get_subcontract_order_supplied_item(filters.order_type, orders_name)
+	for item in subcontracted_items:
+		for order in orders:
 			if order.name == item.parent and item.received_qty < item.qty:
 				row = {
-					"purchase_order": item.parent,
+					"subcontract_order": item.parent,
 					"date": order.transaction_date,
 					"supplier": order.supplier,
 					"fg_item_code": item.item_code,
@@ -76,22 +76,25 @@
 				data.append(row)
 
 
-def get_po(filters):
+def get_subcontract_orders(filters):
 	record_filters = [
-		["is_subcontracted", "=", 1],
 		["supplier", "=", filters.supplier],
 		["transaction_date", "<=", filters.to_date],
 		["transaction_date", ">=", filters.from_date],
 		["docstatus", "=", 1],
 	]
+
+	if filters.order_type == "Purchase Order":
+		record_filters.append(["is_old_subcontracting_flow", "=", 1])
+
 	return frappe.get_all(
-		"Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"]
+		filters.order_type, filters=record_filters, fields=["name", "transaction_date", "supplier"]
 	)
 
 
-def get_purchase_order_item_supplied(po):
+def get_subcontract_order_supplied_item(order_type, orders):
 	return frappe.get_all(
-		"Purchase Order Item",
-		filters=[("parent", "IN", po)],
+		f"{order_type} Item",
+		filters=[("parent", "IN", orders)],
 		fields=["parent", "item_code", "item_name", "qty", "received_qty"],
 	)
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
index 57f8741..c772c1a 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
@@ -7,18 +7,35 @@
 import frappe
 from frappe.tests.utils import FrappeTestCase
 
-from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
 from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_to_be_received import (
 	execute,
 )
+from erpnext.controllers.tests.test_subcontracting_controller import (
+	get_subcontracting_order,
+	make_service_item,
+)
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+	make_subcontracting_receipt,
+)
 
 
 class TestSubcontractedItemToBeReceived(FrappeTestCase):
 	def test_pending_and_received_qty(self):
-		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1)
-		transfer_param = []
+		make_service_item("Subcontracted Service Item 1")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 10,
+				"rate": 500,
+				"fg_item": "_Test FG Item",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(
+			service_items=service_items, supplier_warehouse="_Test Warehouse 1 - _TC"
+		)
 		make_stock_entry(
 			item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
 		)
@@ -28,28 +45,28 @@
 			qty=100,
 			basic_rate=100,
 		)
-		make_purchase_receipt_against_po(po.name)
-		po.reload()
+		make_subcontracting_receipt_against_sco(sco.name)
+		sco.reload()
 		col, data = execute(
 			filters=frappe._dict(
 				{
-					"supplier": po.supplier,
+					"order_type": "Subcontracting Order",
+					"supplier": sco.supplier,
 					"from_date": frappe.utils.get_datetime(
-						frappe.utils.add_to_date(po.transaction_date, days=-10)
+						frappe.utils.add_to_date(sco.transaction_date, days=-10)
 					),
-					"to_date": frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10)),
+					"to_date": frappe.utils.get_datetime(frappe.utils.add_to_date(sco.transaction_date, days=10)),
 				}
 			)
 		)
 		self.assertEqual(data[0]["pending_qty"], 5)
 		self.assertEqual(data[0]["received_qty"], 5)
-		self.assertEqual(data[0]["purchase_order"], po.name)
-		self.assertEqual(data[0]["supplier"], po.supplier)
+		self.assertEqual(data[0]["subcontract_order"], sco.name)
+		self.assertEqual(data[0]["supplier"], sco.supplier)
 
 
-def make_purchase_receipt_against_po(po, quantity=5):
-	pr = make_purchase_receipt(po)
-	pr.items[0].qty = quantity
-	pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
-	pr.insert()
-	pr.submit()
+def make_subcontracting_receipt_against_sco(sco, quantity=5):
+	scr = make_subcontracting_receipt(sco)
+	scr.items[0].qty = quantity
+	scr.insert()
+	scr.submit()
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js
index 0853afd..b6739fe 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js
@@ -5,6 +5,13 @@
 frappe.query_reports["Subcontracted Raw Materials To Be Transferred"] = {
 	"filters": [
 		{
+			label: __("Order Type"),
+			fieldname: "order_type",
+			fieldtype: "Select",
+			options: ["Purchase Order", "Subcontracting Order"],
+			default: "Subcontracting Order"
+		},
+		{
 			fieldname: "supplier",
 			label: __("Supplier"),
 			fieldtype: "Link",
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.json b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.json
index c7cee5e..f689fbc 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.json
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.json
@@ -13,7 +13,7 @@
  "name": "Subcontracted Raw Materials To Be Transferred",
  "owner": "Administrator",
  "prepared_report": 0,
- "ref_doctype": "Purchase Order",
+ "ref_doctype": "Subcontracting Order",
  "report_name": "Subcontracted Raw Materials To Be Transferred",
  "report_type": "Script Report",
  "roles": [
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
index 6b8a3b1..ef28eda 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
@@ -10,19 +10,19 @@
 	if filters.from_date >= filters.to_date:
 		frappe.msgprint(_("To Date must be greater than From Date"))
 
-	columns = get_columns()
+	columns = get_columns(filters)
 	data = get_data(filters)
 
 	return columns, data or []
 
 
-def get_columns():
+def get_columns(filters):
 	return [
 		{
-			"label": _("Purchase Order"),
+			"label": _("Subcontract Order"),
 			"fieldtype": "Link",
-			"fieldname": "purchase_order",
-			"options": "Purchase Order",
+			"fieldname": "subcontract_order",
+			"options": filters.order_type,
 			"width": 200,
 		},
 		{"label": _("Date"), "fieldtype": "Date", "fieldname": "date", "width": 150},
@@ -46,10 +46,10 @@
 
 
 def get_data(filters):
-	po_rm_item_details = get_po_items_to_supply(filters)
+	order_rm_item_details = get_order_items_to_supply(filters)
 
 	data = []
-	for row in po_rm_item_details:
+	for row in order_rm_item_details:
 		transferred_qty = row.get("transferred_qty") or 0
 		if transferred_qty < row.get("reqd_qty", 0):
 			pending_qty = frappe.utils.flt(row.get("reqd_qty", 0) - transferred_qty)
@@ -59,23 +59,33 @@
 	return data
 
 
-def get_po_items_to_supply(filters):
+def get_order_items_to_supply(filters):
+	supplied_items_table = (
+		"Purchase Order Item Supplied"
+		if filters.order_type == "Purchase Order"
+		else "Subcontracting Order Supplied Item"
+	)
+
+	record_filters = [
+		[filters.order_type, "per_received", "<", "100"],
+		[filters.order_type, "supplier", "=", filters.supplier],
+		[filters.order_type, "transaction_date", "<=", filters.to_date],
+		[filters.order_type, "transaction_date", ">=", filters.from_date],
+		[filters.order_type, "docstatus", "=", 1],
+	]
+
+	if filters.order_type == "Purchase Order":
+		record_filters.append([filters.order_type, "is_old_subcontracting_flow", "=", 1])
+
 	return frappe.db.get_all(
-		"Purchase Order",
+		filters.order_type,
 		fields=[
-			"name as purchase_order",
+			"name as subcontract_order",
 			"transaction_date as date",
 			"supplier as supplier",
-			"`tabPurchase Order Item Supplied`.rm_item_code as rm_item_code",
-			"`tabPurchase Order Item Supplied`.required_qty as reqd_qty",
-			"`tabPurchase Order Item Supplied`.supplied_qty as transferred_qty",
+			f"`tab{supplied_items_table}`.rm_item_code as rm_item_code",
+			f"`tab{supplied_items_table}`.required_qty as reqd_qty",
+			f"`tab{supplied_items_table}`.supplied_qty as transferred_qty",
 		],
-		filters=[
-			["Purchase Order", "per_received", "<", "100"],
-			["Purchase Order", "is_subcontracted", "=", 1],
-			["Purchase Order", "supplier", "=", filters.supplier],
-			["Purchase Order", "transaction_date", "<=", filters.to_date],
-			["Purchase Order", "transaction_date", ">=", filters.from_date],
-			["Purchase Order", "docstatus", "=", 1],
-		],
+		filters=record_filters,
 	)
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
index 2791a26..1602957 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
@@ -3,24 +3,34 @@
 # Compiled at: 2019-05-06 10:24:35
 # Decompiled by https://python-decompiler.com
 
-import json
-
 import frappe
 from frappe.tests.utils import FrappeTestCase
 
-from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
 from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import (
 	execute,
 )
+from erpnext.controllers.subcontracting_controller import make_rm_stock_entry
+from erpnext.controllers.tests.test_subcontracting_controller import (
+	get_subcontracting_order,
+	make_service_item,
+)
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
 
 class TestSubcontractedItemToBeTransferred(FrappeTestCase):
 	def test_pending_and_transferred_qty(self):
-		po = create_purchase_order(
-			item_code="_Test FG Item", is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
+		make_service_item("Subcontracted Service Item 1")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 10,
+				"rate": 500,
+				"fg_item": "_Test FG Item",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
 
 		# Material Receipt of RMs
 		make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=100, basic_rate=100)
@@ -28,50 +38,48 @@
 			item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=100, basic_rate=100
 		)
 
-		se = transfer_subcontracted_raw_materials(po)
+		transfer_subcontracted_raw_materials(sco)
 
 		col, data = execute(
 			filters=frappe._dict(
 				{
-					"supplier": po.supplier,
+					"order_type": "Subcontracting Order",
+					"supplier": sco.supplier,
 					"from_date": frappe.utils.get_datetime(
-						frappe.utils.add_to_date(po.transaction_date, days=-10)
+						frappe.utils.add_to_date(sco.transaction_date, days=-10)
 					),
-					"to_date": frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10)),
+					"to_date": frappe.utils.get_datetime(frappe.utils.add_to_date(sco.transaction_date, days=10)),
 				}
 			)
 		)
-		po.reload()
+		sco.reload()
 
-		po_data = [row for row in data if row.get("purchase_order") == po.name]
+		sco_data = [row for row in data if row.get("subcontract_order") == sco.name]
 		# Alphabetically sort to be certain of order
-		po_data = sorted(po_data, key=lambda i: i["rm_item_code"])
+		sco_data = sorted(sco_data, key=lambda i: i["rm_item_code"])
 
-		self.assertEqual(len(po_data), 2)
-		self.assertEqual(po_data[0]["purchase_order"], po.name)
+		self.assertEqual(len(sco_data), 2)
+		self.assertEqual(sco_data[0]["subcontract_order"], sco.name)
 
-		self.assertEqual(po_data[0]["rm_item_code"], "_Test Item")
-		self.assertEqual(po_data[0]["p_qty"], 8)
-		self.assertEqual(po_data[0]["transferred_qty"], 2)
+		self.assertEqual(sco_data[0]["rm_item_code"], "_Test Item")
+		self.assertEqual(sco_data[0]["p_qty"], 8)
+		self.assertEqual(sco_data[0]["transferred_qty"], 2)
 
-		self.assertEqual(po_data[1]["rm_item_code"], "_Test Item Home Desktop 100")
-		self.assertEqual(po_data[1]["p_qty"], 19)
-		self.assertEqual(po_data[1]["transferred_qty"], 1)
-
-		se.cancel()
-		po.cancel()
+		self.assertEqual(sco_data[1]["rm_item_code"], "_Test Item Home Desktop 100")
+		self.assertEqual(sco_data[1]["p_qty"], 19)
+		self.assertEqual(sco_data[1]["transferred_qty"], 1)
 
 
-def transfer_subcontracted_raw_materials(po):
-	# Order of supplied items fetched in PO is flaky
+def transfer_subcontracted_raw_materials(sco):
+	# Order of supplied items fetched in SCO is flaky
 	transfer_qty_map = {"_Test Item": 2, "_Test Item Home Desktop 100": 1}
 
-	item_1 = po.supplied_items[0].rm_item_code
-	item_2 = po.supplied_items[1].rm_item_code
+	item_1 = sco.supplied_items[0].rm_item_code
+	item_2 = sco.supplied_items[1].rm_item_code
 
-	rm_item = [
+	rm_items = [
 		{
-			"name": po.supplied_items[0].name,
+			"name": sco.supplied_items[0].name,
 			"item_code": item_1,
 			"rm_item_code": item_1,
 			"item_name": item_1,
@@ -82,7 +90,7 @@
 			"stock_uom": "Nos",
 		},
 		{
-			"name": po.supplied_items[1].name,
+			"name": sco.supplied_items[1].name,
 			"item_code": item_2,
 			"rm_item_code": item_2,
 			"item_name": item_2,
@@ -93,8 +101,7 @@
 			"stock_uom": "Nos",
 		},
 	]
-	rm_item_string = json.dumps(rm_item)
-	se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
+	se = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items))
 	se.from_warehouse = "_Test Warehouse - _TC"
 	se.to_warehouse = "_Test Warehouse - _TC"
 	se.stock_entry_type = "Send to Subcontractor"
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index ceac815..5528b4d 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2709,10 +2709,10 @@
 		parent.update_ordered_qty()
 		parent.update_ordered_and_reserved_qty()
 		parent.update_receiving_percentage()
-		if parent.is_subcontracted:
+		if parent.is_old_subcontracting_flow:
 			if should_update_supplied_items(parent):
 				parent.update_reserved_qty_for_subcontract()
-				parent.create_raw_materials_supplied("supplied_items")
+				parent.create_raw_materials_supplied()
 			parent.save()
 	else:  # Sales Order
 		parent.validate_warehouse()
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 8d5a042..036733c 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -11,8 +11,7 @@
 from erpnext.accounts.party import get_party_details
 from erpnext.buying.utils import update_last_purchase_rate, validate_for_items
 from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
-from erpnext.controllers.stock_controller import StockController
-from erpnext.controllers.subcontracting import Subcontracting
+from erpnext.controllers.subcontracting_controller import SubcontractingController
 from erpnext.stock.get_item_details import get_conversion_factor
 from erpnext.stock.utils import get_incoming_rate
 
@@ -21,7 +20,7 @@
 	pass
 
 
-class BuyingController(StockController, Subcontracting):
+class BuyingController(SubcontractingController):
 	def __setup__(self):
 		self.flags.ignore_permlevel_for_fields = ["buying_price_list", "price_list_currency"]
 
@@ -55,7 +54,8 @@
 
 			# sub-contracting
 			self.validate_for_subcontracting()
-			self.create_raw_materials_supplied("supplied_items")
+			if self.get("is_old_subcontracting_flow"):
+				self.create_raw_materials_supplied()
 			self.set_landed_cost_voucher_amount()
 
 		if self.doctype in ("Purchase Receipt", "Purchase Invoice"):
@@ -256,13 +256,18 @@
 					)
 
 				qty_in_stock_uom = flt(item.qty * item.conversion_factor)
-				item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate)
-				item.valuation_rate = (
-					item.base_net_amount
-					+ item.item_tax_amount
-					+ item.rm_supp_cost
-					+ flt(item.landed_cost_voucher_amount)
-				) / qty_in_stock_uom
+				if self.get("is_old_subcontracting_flow"):
+					item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate)
+					item.valuation_rate = (
+						item.base_net_amount
+						+ item.item_tax_amount
+						+ item.rm_supp_cost
+						+ flt(item.landed_cost_voucher_amount)
+					) / qty_in_stock_uom
+				else:
+					item.valuation_rate = (
+						item.base_net_amount + item.item_tax_amount + flt(item.landed_cost_voucher_amount)
+					) / qty_in_stock_uom
 			else:
 				item.valuation_rate = 0.0
 
@@ -317,76 +322,25 @@
 					d.discount_amount = 0.0
 					d.margin_rate_or_amount = 0.0
 
-	def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
-		supplied_items_cost = 0.0
-		for d in self.get("supplied_items"):
-			if d.reference_name == item_row_id:
-				if reset_outgoing_rate and frappe.get_cached_value("Item", d.rm_item_code, "is_stock_item"):
-					rate = get_incoming_rate(
-						{
-							"item_code": d.rm_item_code,
-							"warehouse": self.supplier_warehouse,
-							"posting_date": self.posting_date,
-							"posting_time": self.posting_time,
-							"qty": -1 * d.consumed_qty,
-							"serial_no": d.serial_no,
-							"batch_no": d.batch_no,
-						}
-					)
-
-					if rate > 0:
-						d.rate = rate
-
-				d.amount = flt(flt(d.consumed_qty) * flt(d.rate), d.precision("amount"))
-				supplied_items_cost += flt(d.amount)
-
-		return supplied_items_cost
-
 	def validate_for_subcontracting(self):
-		if self.is_subcontracted:
+		if self.is_subcontracted and self.get("is_old_subcontracting_flow"):
 			if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
 				frappe.throw(_("Supplier Warehouse mandatory for sub-contracted {0}").format(self.doctype))
 
 			for item in self.get("items"):
 				if item in self.sub_contracted_items and not item.bom:
 					frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))
-
 			if self.doctype != "Purchase Order":
 				return
-
 			for row in self.get("supplied_items"):
 				if not row.reserve_warehouse:
 					msg = f"Reserved Warehouse is mandatory for the Item {frappe.bold(row.rm_item_code)} in Raw Materials supplied"
 					frappe.throw(_(msg))
 		else:
 			for item in self.get("items"):
-				if item.bom:
+				if item.get("bom"):
 					item.bom = None
 
-	def create_raw_materials_supplied(self, raw_material_table):
-		if self.is_subcontracted:
-			self.set_materials_for_subcontracted_items(raw_material_table)
-
-		elif self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
-			for item in self.get("items"):
-				item.rm_supp_cost = 0.0
-
-		if not self.is_subcontracted and self.get("supplied_items"):
-			self.set("supplied_items", [])
-
-	@property
-	def sub_contracted_items(self):
-		if not hasattr(self, "_sub_contracted_items"):
-			self._sub_contracted_items = []
-			item_codes = list(set(item.item_code for item in self.get("items")))
-			if item_codes:
-				items = frappe.get_all(
-					"Item", filters={"name": ["in", item_codes], "is_sub_contracted_item": 1}
-				)
-				self._sub_contracted_items = [item.name for item in items]
-
-		return self._sub_contracted_items
-
 	def set_qty_as_per_stock_uom(self):
 		for d in self.get("items"):
 			if d.meta.get_field("stock_qty"):
@@ -510,7 +464,9 @@
 						sle.update(
 							{
 								"incoming_rate": incoming_rate,
-								"recalculate_rate": 1 if (self.is_subcontracted and d.bom) or d.from_warehouse else 0,
+								"recalculate_rate": 1
+								if (self.is_subcontracted and (d.bom or d.fg_item)) or d.from_warehouse
+								else 0,
 							}
 						)
 					sl_entries.append(sle)
@@ -538,7 +494,8 @@
 					)
 				)
 
-		self.make_sl_entries_for_supplier_warehouse(sl_entries)
+		if self.get("is_old_subcontracting_flow"):
+			self.make_sl_entries_for_supplier_warehouse(sl_entries)
 		self.make_sl_entries(
 			sl_entries,
 			allow_negative_stock=allow_negative_stock,
@@ -565,26 +522,9 @@
 					)
 
 				po_obj.update_ordered_qty(po_item_rows)
-				if self.is_subcontracted:
+				if self.get("is_old_subcontracting_flow"):
 					po_obj.update_reserved_qty_for_subcontract()
 
-	def make_sl_entries_for_supplier_warehouse(self, sl_entries):
-		if hasattr(self, "supplied_items"):
-			for d in self.get("supplied_items"):
-				# negative quantity is passed, as raw material qty has to be decreased
-				# when PR is submitted and it has to be increased when PR is cancelled
-				sl_entries.append(
-					self.get_sl_entries(
-						d,
-						{
-							"item_code": d.rm_item_code,
-							"warehouse": self.supplier_warehouse,
-							"actual_qty": -1 * flt(d.consumed_qty),
-							"dependant_sle_voucher_detail_no": d.reference_name,
-						},
-					)
-				)
-
 	def on_submit(self):
 		if self.get("is_return"):
 			return
@@ -808,7 +748,7 @@
 		if self.doctype == "Material Request":
 			return
 
-		if hasattr(self, "is_subcontracted") and self.is_subcontracted:
+		if self.get("is_old_subcontracting_flow"):
 			validate_item_type(self, "is_sub_contracted_item", "subcontracted")
 		else:
 			validate_item_type(self, "is_purchase_item", "purchase")
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index d24ac3f..04a0dfa 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -77,7 +77,7 @@
 	if doc.doctype != "Purchase Invoice":
 		select_fields += ",serial_no, batch_no"
 
-	if doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
+	if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
 		select_fields += ",rejected_qty, received_qty"
 
 	for d in frappe.db.sql(
@@ -161,7 +161,7 @@
 
 def validate_quantity(doc, args, ref, valid_items, already_returned_items):
 	fields = ["stock_qty"]
-	if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
+	if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
 		fields.extend(["received_qty", "rejected_qty"])
 
 	already_returned_data = already_returned_items.get(args.item_code) or {}
@@ -224,7 +224,7 @@
 	if ref_item_row.get("rate", 0) > item_dict["rate"]:
 		item_dict["rate"] = ref_item_row.get("rate", 0)
 
-	if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt"]:
+	if ref_item_row.parenttype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
 		item_dict["received_qty"] += ref_item_row.received_qty
 		item_dict["rejected_qty"] += ref_item_row.rejected_qty
 
@@ -239,7 +239,7 @@
 
 def get_already_returned_items(doc):
 	column = "child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty"
-	if doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
+	if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
 		column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
 			sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
 
@@ -281,17 +281,21 @@
 	child_doctype = doctype + " Item"
 	reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
 
-	if doctype in ("Purchase Receipt", "Purchase Invoice"):
+	if doctype in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
 		party_type = "supplier"
 	else:
 		party_type = "customer"
 
 	fields = [
 		"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
-		"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype),
 	]
 
-	if doctype in ("Purchase Receipt", "Purchase Invoice"):
+	if doctype != "Subcontracting Receipt":
+		fields += [
+			"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype),
+		]
+
+	if doctype in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
 		fields += [
 			"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
 			"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
@@ -342,7 +346,7 @@
 			# look for Print Heading "Debit Note"
 			doc.select_print_heading = frappe.db.get_value("Print Heading", _("Debit Note"))
 
-		for tax in doc.get("taxes"):
+		for tax in doc.get("taxes") or []:
 			if tax.charge_type == "Actual":
 				tax.tax_amount = -1 * tax.tax_amount
 
@@ -381,8 +385,11 @@
 			for d in doc.get("packed_items"):
 				d.qty = d.qty * -1
 
-		doc.discount_amount = -1 * source.discount_amount
-		doc.run_method("calculate_taxes_and_totals")
+		if doc.get("discount_amount"):
+			doc.discount_amount = -1 * source.discount_amount
+
+		if doctype != "Subcontracting Receipt":
+			doc.run_method("calculate_taxes_and_totals")
 
 	def update_item(source_doc, target_doc, source_parent):
 		target_doc.qty = -1 * source_doc.qty
@@ -393,7 +400,7 @@
 			if serial_nos:
 				target_doc.serial_no = "\n".join(serial_nos)
 
-		if doctype == "Purchase Receipt":
+		if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
 			returned_qty_map = get_returned_qty_map_for_row(
 				source_parent.name, source_parent.supplier, source_doc.name, doctype
 			)
@@ -405,15 +412,24 @@
 			)
 			target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get("qty") or 0))
 
-			target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0))
-			target_doc.received_stock_qty = -1 * flt(
-				source_doc.received_stock_qty - (returned_qty_map.get("received_stock_qty") or 0)
-			)
+			if hasattr(target_doc, "stock_qty"):
+				target_doc.stock_qty = -1 * flt(
+					source_doc.stock_qty - (returned_qty_map.get("stock_qty") or 0)
+				)
+				target_doc.received_stock_qty = -1 * flt(
+					source_doc.received_stock_qty - (returned_qty_map.get("received_stock_qty") or 0)
+				)
 
-			target_doc.purchase_order = source_doc.purchase_order
-			target_doc.purchase_order_item = source_doc.purchase_order_item
-			target_doc.rejected_warehouse = source_doc.rejected_warehouse
-			target_doc.purchase_receipt_item = source_doc.name
+			if doctype == "Subcontracting Receipt":
+				target_doc.subcontracting_order = source_doc.subcontracting_order
+				target_doc.subcontracting_order_item = source_doc.subcontracting_order_item
+				target_doc.rejected_warehouse = source_doc.rejected_warehouse
+				target_doc.subcontracting_receipt_item = source_doc.name
+			else:
+				target_doc.purchase_order = source_doc.purchase_order
+				target_doc.purchase_order_item = source_doc.purchase_order_item
+				target_doc.rejected_warehouse = source_doc.rejected_warehouse
+				target_doc.purchase_receipt_item = source_doc.name
 
 		elif doctype == "Purchase Invoice":
 			returned_qty_map = get_returned_qty_map_for_row(
@@ -525,7 +541,7 @@
 		item_row,
 	)
 
-	if voucher_type in ("Purchase Receipt", "Purchase Invoice"):
+	if voucher_type in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"):
 		select_field = "incoming_rate"
 	else:
 		select_field = "abs(stock_value_difference / actual_qty)"
@@ -560,6 +576,7 @@
 		"Purchase Invoice": "purchase_invoice_item",
 		"Delivery Note": "dn_detail",
 		"Sales Invoice": "sales_invoice_item",
+		"Subcontracting Receipt": "subcontracting_receipt_item",
 	}
 	return return_against_item_fields[voucher_type]
 
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
deleted file mode 100644
index 4bce06f..0000000
--- a/erpnext/controllers/subcontracting.py
+++ /dev/null
@@ -1,469 +0,0 @@
-import copy
-from collections import defaultdict
-
-import frappe
-from frappe import _
-from frappe.utils import cint, flt, get_link_to_form
-
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-
-
-class Subcontracting:
-	def set_materials_for_subcontracted_items(self, raw_material_table):
-		if self.doctype == "Purchase Invoice" and not self.update_stock:
-			return
-
-		self.raw_material_table = raw_material_table
-		self.__identify_change_in_item_table()
-		self.__prepare_supplied_items()
-		self.__validate_supplied_items()
-
-	def __prepare_supplied_items(self):
-		self.initialized_fields()
-		self.__get_purchase_orders()
-		self.__get_pending_qty_to_receive()
-		self.get_available_materials()
-		self.__remove_changed_rows()
-		self.__set_supplied_items()
-
-	def initialized_fields(self):
-		self.available_materials = frappe._dict()
-		self.__transferred_items = frappe._dict()
-		self.alternative_item_details = frappe._dict()
-		self.__get_backflush_based_on()
-
-	def __get_backflush_based_on(self):
-		self.backflush_based_on = frappe.db.get_single_value(
-			"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
-		)
-
-	def __get_purchase_orders(self):
-		self.purchase_orders = []
-
-		if self.doctype == "Purchase Order":
-			return
-
-		self.purchase_orders = [d.purchase_order for d in self.items if d.purchase_order]
-
-	def __identify_change_in_item_table(self):
-		self.__changed_name = []
-		self.__reference_name = []
-
-		if self.doctype == "Purchase Order" or self.is_new():
-			self.set(self.raw_material_table, [])
-			return
-
-		item_dict = self.__get_data_before_save()
-		if not item_dict:
-			return True
-
-		for n_row in self.items:
-			self.__reference_name.append(n_row.name)
-			if (n_row.name not in item_dict) or (n_row.item_code, n_row.qty) != item_dict[n_row.name]:
-				self.__changed_name.append(n_row.name)
-
-			if item_dict.get(n_row.name):
-				del item_dict[n_row.name]
-
-		self.__changed_name.extend(item_dict.keys())
-
-	def __get_data_before_save(self):
-		item_dict = {}
-		if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self._doc_before_save:
-			for row in self._doc_before_save.get("items"):
-				item_dict[row.name] = (row.item_code, row.qty)
-
-		return item_dict
-
-	def get_available_materials(self):
-		"""Get the available raw materials which has been transferred to the supplier.
-		available_materials = {
-		        (item_code, subcontracted_item, purchase_order): {
-		                'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details
-		        }
-		}
-		"""
-		if not self.purchase_orders:
-			return
-
-		for row in self.__get_transferred_items():
-			key = (row.rm_item_code, row.main_item_code, row.purchase_order)
-
-			if key not in self.available_materials:
-				self.available_materials.setdefault(
-					key,
-					frappe._dict(
-						{
-							"qty": 0,
-							"serial_no": [],
-							"batch_no": defaultdict(float),
-							"item_details": row,
-							"po_details": [],
-						}
-					),
-				)
-
-			details = self.available_materials[key]
-			details.qty += row.qty
-			details.po_details.append(row.po_detail)
-
-			if row.serial_no:
-				details.serial_no.extend(get_serial_nos(row.serial_no))
-
-			if row.batch_no:
-				details.batch_no[row.batch_no] += row.qty
-
-			self.__set_alternative_item_details(row)
-
-		self.__transferred_items = copy.deepcopy(self.available_materials)
-		for doctype in ["Purchase Receipt", "Purchase Invoice"]:
-			self.__update_consumed_materials(doctype)
-
-	def __update_consumed_materials(self, doctype, return_consumed_items=False):
-		"""Deduct the consumed materials from the available materials."""
-
-		pr_items = self.__get_received_items(doctype)
-		if not pr_items:
-			return ([], {}) if return_consumed_items else None
-
-		pr_items = {d.name: d.get(self.get("po_field") or "purchase_order") for d in pr_items}
-		consumed_materials = self.__get_consumed_items(doctype, pr_items.keys())
-
-		if return_consumed_items:
-			return (consumed_materials, pr_items)
-
-		for row in consumed_materials:
-			key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name))
-			if not self.available_materials.get(key):
-				continue
-
-			self.available_materials[key]["qty"] -= row.consumed_qty
-			if row.serial_no:
-				self.available_materials[key]["serial_no"] = list(
-					set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
-				)
-
-			if row.batch_no:
-				self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
-
-	def __get_transferred_items(self):
-		fields = ["`tabStock Entry`.`purchase_order`"]
-		alias_dict = {
-			"item_code": "rm_item_code",
-			"subcontracted_item": "main_item_code",
-			"basic_rate": "rate",
-		}
-
-		child_table_fields = [
-			"item_code",
-			"item_name",
-			"description",
-			"qty",
-			"basic_rate",
-			"amount",
-			"serial_no",
-			"uom",
-			"subcontracted_item",
-			"stock_uom",
-			"batch_no",
-			"conversion_factor",
-			"s_warehouse",
-			"t_warehouse",
-			"item_group",
-			"po_detail",
-		]
-
-		if self.backflush_based_on == "BOM":
-			child_table_fields.append("original_item")
-
-		for field in child_table_fields:
-			fields.append(f"`tabStock Entry Detail`.`{field}` As {alias_dict.get(field, field)}")
-
-		filters = [
-			["Stock Entry", "docstatus", "=", 1],
-			["Stock Entry", "purpose", "=", "Send to Subcontractor"],
-			["Stock Entry", "purchase_order", "in", self.purchase_orders],
-		]
-
-		return frappe.get_all("Stock Entry", fields=fields, filters=filters)
-
-	def __get_received_items(self, doctype):
-		fields = []
-		self.po_field = "purchase_order"
-
-		for field in ["name", self.po_field, "parent"]:
-			fields.append(f"`tab{doctype} Item`.`{field}`")
-
-		filters = [
-			[doctype, "docstatus", "=", 1],
-			[f"{doctype} Item", self.po_field, "in", self.purchase_orders],
-		]
-		if doctype == "Purchase Invoice":
-			filters.append(["Purchase Invoice", "update_stock", "=", 1])
-
-		return frappe.get_all(f"{doctype}", fields=fields, filters=filters)
-
-	def __get_consumed_items(self, doctype, pr_items):
-		return frappe.get_all(
-			"Purchase Receipt Item Supplied",
-			fields=[
-				"serial_no",
-				"rm_item_code",
-				"reference_name",
-				"batch_no",
-				"consumed_qty",
-				"main_item_code",
-			],
-			filters={"docstatus": 1, "reference_name": ("in", list(pr_items)), "parenttype": doctype},
-		)
-
-	def __set_alternative_item_details(self, row):
-		if row.get("original_item"):
-			self.alternative_item_details[row.get("original_item")] = row
-
-	def __get_pending_qty_to_receive(self):
-		"""Get qty to be received against the purchase order."""
-
-		self.qty_to_be_received = defaultdict(float)
-
-		if (
-			self.doctype != "Purchase Order" and self.backflush_based_on != "BOM" and self.purchase_orders
-		):
-			for row in frappe.get_all(
-				"Purchase Order Item",
-				fields=["item_code", "(qty - received_qty) as qty", "parent", "name"],
-				filters={"docstatus": 1, "parent": ("in", self.purchase_orders)},
-			):
-
-				self.qty_to_be_received[(row.item_code, row.parent)] += row.qty
-
-	def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
-		doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
-		fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
-
-		alias_dict = {
-			"item_code": "rm_item_code",
-			"name": "bom_detail_no",
-			"source_warehouse": "reserve_warehouse",
-		}
-		for field in [
-			"item_code",
-			"name",
-			"rate",
-			"stock_uom",
-			"source_warehouse",
-			"description",
-			"item_name",
-			"stock_uom",
-		]:
-			fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
-
-		filters = [
-			[doctype, "parent", "=", bom_no],
-			[doctype, "docstatus", "=", 1],
-			["BOM", "item", "=", item_code],
-			[doctype, "sourced_by_supplier", "=", 0],
-		]
-
-		return (
-			frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
-		)
-
-	def __remove_changed_rows(self):
-		if not self.__changed_name:
-			return
-
-		i = 1
-		self.set(self.raw_material_table, [])
-		for d in self._doc_before_save.supplied_items:
-			if d.reference_name in self.__changed_name:
-				continue
-
-			if d.reference_name not in self.__reference_name:
-				continue
-
-			d.idx = i
-			self.append("supplied_items", d)
-
-			i += 1
-
-	def __set_supplied_items(self):
-		self.bom_items = {}
-
-		has_supplied_items = True if self.get(self.raw_material_table) else False
-		for row in self.items:
-			if self.doctype != "Purchase Order" and (
-				(self.__changed_name and row.name not in self.__changed_name)
-				or (has_supplied_items and not self.__changed_name)
-			):
-				continue
-
-			if self.doctype == "Purchase Order" or self.backflush_based_on == "BOM":
-				for bom_item in self.__get_materials_from_bom(
-					row.item_code, row.bom, row.get("include_exploded_items")
-				):
-					qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor
-					bom_item.main_item_code = row.item_code
-					self.__update_reserve_warehouse(bom_item, row)
-					self.__set_alternative_item(bom_item)
-					self.__add_supplied_item(row, bom_item, qty)
-
-			elif self.backflush_based_on != "BOM":
-				for key, transfer_item in self.available_materials.items():
-					if (key[1], key[2]) == (row.item_code, row.purchase_order) and transfer_item.qty > 0:
-						qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
-						transfer_item.qty -= qty
-						self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
-
-				if self.qty_to_be_received:
-					self.qty_to_be_received[(row.item_code, row.purchase_order)] -= row.qty
-
-	def __update_reserve_warehouse(self, row, item):
-		if self.doctype == "Purchase Order":
-			row.reserve_warehouse = self.set_reserve_warehouse or item.warehouse
-
-	def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
-		key = (item_row.item_code, item_row.purchase_order)
-
-		if self.qty_to_be_received == item_row.qty:
-			return transfer_item.qty
-
-		if self.qty_to_be_received:
-			qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
-			transfer_item.item_details.required_qty = transfer_item.qty
-
-			if transfer_item.serial_no or frappe.get_cached_value(
-				"UOM", transfer_item.item_details.stock_uom, "must_be_whole_number"
-			):
-				return frappe.utils.ceil(qty)
-
-			return qty
-
-	def __set_alternative_item(self, bom_item):
-		if self.alternative_item_details.get(bom_item.rm_item_code):
-			bom_item.update(self.alternative_item_details[bom_item.rm_item_code])
-
-	def __add_supplied_item(self, item_row, bom_item, qty):
-		bom_item.conversion_factor = item_row.conversion_factor
-		rm_obj = self.append(self.raw_material_table, bom_item)
-		rm_obj.reference_name = item_row.name
-
-		if self.doctype == "Purchase Order":
-			rm_obj.required_qty = qty
-		else:
-			rm_obj.consumed_qty = 0
-			rm_obj.purchase_order = item_row.purchase_order
-			self.__set_batch_nos(bom_item, item_row, rm_obj, qty)
-
-	def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
-		key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order)
-
-		if self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
-			new_rm_obj = None
-			for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
-				if batch_qty >= qty:
-					self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
-					self.available_materials[key]["batch_no"][batch_no] -= qty
-					return
-
-				elif qty > 0 and batch_qty > 0:
-					qty -= batch_qty
-					new_rm_obj = self.append(self.raw_material_table, bom_item)
-					new_rm_obj.reference_name = item_row.name
-					self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty)
-					self.available_materials[key]["batch_no"][batch_no] = 0
-
-			if abs(qty) > 0 and not new_rm_obj:
-				self.__set_consumed_qty(rm_obj, qty)
-		else:
-			self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty)
-			self.__set_serial_nos(item_row, rm_obj)
-
-	def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
-		rm_obj.required_qty = required_qty
-		rm_obj.consumed_qty = consumed_qty
-
-	def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty):
-		rm_obj.update(
-			{
-				"consumed_qty": qty,
-				"batch_no": batch_no,
-				"required_qty": qty,
-				"purchase_order": item_row.purchase_order,
-			}
-		)
-
-		self.__set_serial_nos(item_row, rm_obj)
-
-	def __set_serial_nos(self, item_row, rm_obj):
-		key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order)
-		if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
-			used_serial_nos = self.available_materials[key]["serial_no"][0 : cint(rm_obj.consumed_qty)]
-			rm_obj.serial_no = "\n".join(used_serial_nos)
-
-			# Removed the used serial nos from the list
-			for sn in used_serial_nos:
-				self.available_materials[key]["serial_no"].remove(sn)
-
-	def set_consumed_qty_in_po(self):
-		# Update consumed qty back in the purchase order
-		if not self.is_subcontracted:
-			return
-
-		self.__get_purchase_orders()
-		itemwise_consumed_qty = defaultdict(float)
-		for doctype in ["Purchase Receipt", "Purchase Invoice"]:
-			consumed_items, pr_items = self.__update_consumed_materials(doctype, return_consumed_items=True)
-
-			for row in consumed_items:
-				key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name))
-				itemwise_consumed_qty[key] += row.consumed_qty
-
-		self.__update_consumed_qty_in_po(itemwise_consumed_qty)
-
-	def __update_consumed_qty_in_po(self, itemwise_consumed_qty):
-		fields = ["main_item_code", "rm_item_code", "parent", "supplied_qty", "name"]
-		filters = {"docstatus": 1, "parent": ("in", self.purchase_orders)}
-
-		for row in frappe.get_all(
-			"Purchase Order Item Supplied", fields=fields, filters=filters, order_by="idx"
-		):
-			key = (row.rm_item_code, row.main_item_code, row.parent)
-			consumed_qty = itemwise_consumed_qty.get(key, 0)
-
-			if row.supplied_qty < consumed_qty:
-				consumed_qty = row.supplied_qty
-
-			itemwise_consumed_qty[key] -= consumed_qty
-			frappe.db.set_value("Purchase Order Item Supplied", row.name, "consumed_qty", consumed_qty)
-
-	def __validate_supplied_items(self):
-		if self.doctype not in ["Purchase Invoice", "Purchase Receipt"]:
-			return
-
-		for row in self.get(self.raw_material_table):
-			key = (row.rm_item_code, row.main_item_code, row.purchase_order)
-			if not self.__transferred_items or not self.__transferred_items.get(key):
-				return
-
-			self.__validate_batch_no(row, key)
-			self.__validate_serial_no(row, key)
-
-	def __validate_batch_no(self, row, key):
-		if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get(
-			"batch_no"
-		):
-			link = get_link_to_form("Purchase Order", row.purchase_order)
-			msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the Purchase Order {link}'
-			frappe.throw(_(msg), title=_("Incorrect Batch Consumed"))
-
-	def __validate_serial_no(self, row, key):
-		if row.get("serial_no"):
-			serial_nos = get_serial_nos(row.get("serial_no"))
-			incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get("serial_no"))
-
-			if incorrect_sn:
-				incorrect_sn = "\n".join(incorrect_sn)
-				link = get_link_to_form("Purchase Order", row.purchase_order)
-				msg = f"The Serial Nos {incorrect_sn} has not supplied against the Purchase Order {link}"
-				frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed"))
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
new file mode 100644
index 0000000..2a2f8f5
--- /dev/null
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -0,0 +1,902 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import copy
+import json
+from collections import defaultdict
+
+import frappe
+from frappe import _
+from frappe.utils import cint, cstr, flt, get_link_to_form
+
+from erpnext.controllers.stock_controller import StockController
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.utils import get_incoming_rate
+
+
+class SubcontractingController(StockController):
+	def __init__(self, *args, **kwargs):
+		super(SubcontractingController, self).__init__(*args, **kwargs)
+		if self.get("is_old_subcontracting_flow"):
+			self.subcontract_data = frappe._dict(
+				{
+					"order_doctype": "Purchase Order",
+					"order_field": "purchase_order",
+					"rm_detail_field": "po_detail",
+					"receipt_supplied_items_field": "Purchase Receipt Item Supplied",
+					"order_supplied_items_field": "Purchase Order Item Supplied",
+				}
+			)
+		else:
+			self.subcontract_data = frappe._dict(
+				{
+					"order_doctype": "Subcontracting Order",
+					"order_field": "subcontracting_order",
+					"rm_detail_field": "sco_rm_detail",
+					"receipt_supplied_items_field": "Subcontracting Receipt Supplied Item",
+					"order_supplied_items_field": "Subcontracting Order Supplied Item",
+				}
+			)
+
+	def before_validate(self):
+		if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]:
+			self.remove_empty_rows()
+			self.set_items_conversion_factor()
+
+	def validate(self):
+		if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]:
+			self.validate_items()
+			self.create_raw_materials_supplied()
+		else:
+			super(SubcontractingController, self).validate()
+
+	def remove_empty_rows(self):
+		for key in ["service_items", "items", "supplied_items"]:
+			if self.get(key):
+				idx = 1
+				for item in self.get(key)[:]:
+					if not (item.get("item_code") or item.get("main_item_code")):
+						self.get(key).remove(item)
+					else:
+						item.idx = idx
+						idx += 1
+
+	def set_items_conversion_factor(self):
+		for item in self.get("items"):
+			if not item.conversion_factor:
+				item.conversion_factor = 1
+
+	def validate_items(self):
+		for item in self.items:
+			if not frappe.get_value("Item", item.item_code, "is_sub_contracted_item"):
+				msg = f"Item {item.item_name} must be a subcontracted item."
+				frappe.throw(_(msg))
+			if item.bom:
+				bom = frappe.get_doc("BOM", item.bom)
+				if not bom.is_active:
+					msg = f"Please select an active BOM for Item {item.item_name}."
+					frappe.throw(_(msg))
+				if bom.item != item.item_code:
+					msg = f"Please select an valid BOM for Item {item.item_name}."
+					frappe.throw(_(msg))
+
+	def __get_data_before_save(self):
+		item_dict = {}
+		if (
+			self.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]
+			and self._doc_before_save
+		):
+			for row in self._doc_before_save.get("items"):
+				item_dict[row.name] = (row.item_code, row.qty)
+
+		return item_dict
+
+	def __identify_change_in_item_table(self):
+		self.__changed_name = []
+		self.__reference_name = []
+
+		if self.doctype in ["Purchase Order", "Subcontracting Order"] or self.is_new():
+			self.set(self.raw_material_table, [])
+			return
+
+		item_dict = self.__get_data_before_save()
+		if not item_dict:
+			return True
+
+		for row in self.items:
+			self.__reference_name.append(row.name)
+			if (row.name not in item_dict) or (row.item_code, row.qty) != item_dict[row.name]:
+				self.__changed_name.append(row.name)
+
+			if item_dict.get(row.name):
+				del item_dict[row.name]
+
+		self.__changed_name.extend(item_dict.keys())
+
+	def __get_backflush_based_on(self):
+		self.backflush_based_on = frappe.db.get_single_value(
+			"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
+		)
+
+	def initialized_fields(self):
+		self.available_materials = frappe._dict()
+		self.__transferred_items = frappe._dict()
+		self.alternative_item_details = frappe._dict()
+		self.__get_backflush_based_on()
+
+	def __get_subcontract_orders(self):
+		self.subcontract_orders = []
+
+		if self.doctype in ["Purchase Order", "Subcontracting Order"]:
+			return
+
+		self.subcontract_orders = [
+			item.get(self.subcontract_data.order_field)
+			for item in self.items
+			if item.get(self.subcontract_data.order_field)
+		]
+
+	def __get_pending_qty_to_receive(self):
+		"""Get qty to be received against the subcontract order."""
+
+		self.qty_to_be_received = defaultdict(float)
+
+		if (
+			self.doctype != self.subcontract_data.order_doctype
+			and self.backflush_based_on != "BOM"
+			and self.subcontract_orders
+		):
+			for row in frappe.get_all(
+				f"{self.subcontract_data.order_doctype} Item",
+				fields=["item_code", "(qty - received_qty) as qty", "parent", "name"],
+				filters={"docstatus": 1, "parent": ("in", self.subcontract_orders)},
+			):
+
+				self.qty_to_be_received[(row.item_code, row.parent)] += row.qty
+
+	def __get_transferred_items(self):
+		fields = [f"`tabStock Entry`.`{self.subcontract_data.order_field}`"]
+		alias_dict = {
+			"item_code": "rm_item_code",
+			"subcontracted_item": "main_item_code",
+			"basic_rate": "rate",
+		}
+
+		child_table_fields = [
+			"item_code",
+			"item_name",
+			"description",
+			"qty",
+			"basic_rate",
+			"amount",
+			"serial_no",
+			"uom",
+			"subcontracted_item",
+			"stock_uom",
+			"batch_no",
+			"conversion_factor",
+			"s_warehouse",
+			"t_warehouse",
+			"item_group",
+			self.subcontract_data.rm_detail_field,
+		]
+
+		if self.backflush_based_on == "BOM":
+			child_table_fields.append("original_item")
+
+		for field in child_table_fields:
+			fields.append(f"`tabStock Entry Detail`.`{field}` As {alias_dict.get(field, field)}")
+
+		filters = [
+			["Stock Entry", "docstatus", "=", 1],
+			["Stock Entry", "purpose", "=", "Send to Subcontractor"],
+			["Stock Entry", self.subcontract_data.order_field, "in", self.subcontract_orders],
+		]
+
+		return frappe.get_all("Stock Entry", fields=fields, filters=filters)
+
+	def __set_alternative_item_details(self, row):
+		if row.get("original_item"):
+			self.alternative_item_details[row.get("original_item")] = row
+
+	def __get_received_items(self, doctype):
+		fields = []
+		for field in ["name", self.subcontract_data.order_field, "parent"]:
+			fields.append(f"`tab{doctype} Item`.`{field}`")
+
+		filters = [
+			[doctype, "docstatus", "=", 1],
+			[f"{doctype} Item", self.subcontract_data.order_field, "in", self.subcontract_orders],
+		]
+		if doctype == "Purchase Invoice":
+			filters.append(["Purchase Invoice", "update_stock", "=", 1])
+
+		return frappe.get_all(f"{doctype}", fields=fields, filters=filters)
+
+	def __get_consumed_items(self, doctype, receipt_items):
+		return frappe.get_all(
+			self.subcontract_data.receipt_supplied_items_field,
+			fields=[
+				"serial_no",
+				"rm_item_code",
+				"reference_name",
+				"batch_no",
+				"consumed_qty",
+				"main_item_code",
+			],
+			filters={"docstatus": 1, "reference_name": ("in", list(receipt_items)), "parenttype": doctype},
+		)
+
+	def __update_consumed_materials(self, doctype, return_consumed_items=False):
+		"""Deduct the consumed materials from the available materials."""
+
+		receipt_items = self.__get_received_items(doctype)
+		if not receipt_items:
+			return ([], {}) if return_consumed_items else None
+
+		receipt_items = {
+			item.name: item.get(self.subcontract_data.order_field) for item in receipt_items
+		}
+		consumed_materials = self.__get_consumed_items(doctype, receipt_items.keys())
+
+		if return_consumed_items:
+			return (consumed_materials, receipt_items)
+
+		for row in consumed_materials:
+			key = (row.rm_item_code, row.main_item_code, receipt_items.get(row.reference_name))
+			if not self.available_materials.get(key):
+				continue
+
+			self.available_materials[key]["qty"] -= row.consumed_qty
+			if row.serial_no:
+				self.available_materials[key]["serial_no"] = list(
+					set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
+				)
+
+			if row.batch_no:
+				self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
+
+	def get_available_materials(self):
+		"""Get the available raw materials which has been transferred to the supplier.
+		available_materials = {
+		        (item_code, subcontracted_item, subcontract_order): {
+		                'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details
+		        }
+		}
+		"""
+		if not self.subcontract_orders:
+			return
+
+		for row in self.__get_transferred_items():
+			key = (row.rm_item_code, row.main_item_code, row.get(self.subcontract_data.order_field))
+
+			if key not in self.available_materials:
+				self.available_materials.setdefault(
+					key,
+					frappe._dict(
+						{
+							"qty": 0,
+							"serial_no": [],
+							"batch_no": defaultdict(float),
+							"item_details": row,
+							f"{self.subcontract_data.rm_detail_field}s": [],
+						}
+					),
+				)
+
+			details = self.available_materials[key]
+			details.qty += row.qty
+			details[f"{self.subcontract_data.rm_detail_field}s"].append(
+				row.get(self.subcontract_data.rm_detail_field)
+			)
+
+			if row.serial_no:
+				details.serial_no.extend(get_serial_nos(row.serial_no))
+
+			if row.batch_no:
+				details.batch_no[row.batch_no] += row.qty
+
+			self.__set_alternative_item_details(row)
+
+		self.__transferred_items = copy.deepcopy(self.available_materials)
+		if self.get("is_old_subcontracting_flow"):
+			for doctype in ["Purchase Receipt", "Purchase Invoice"]:
+				self.__update_consumed_materials(doctype)
+		else:
+			self.__update_consumed_materials("Subcontracting Receipt")
+
+	def __remove_changed_rows(self):
+		if not self.__changed_name:
+			return
+
+		i = 1
+		self.set(self.raw_material_table, [])
+		for item in self._doc_before_save.supplied_items:
+			if item.reference_name in self.__changed_name:
+				continue
+
+			if item.reference_name not in self.__reference_name:
+				continue
+
+			item.idx = i
+			self.append("supplied_items", item)
+
+			i += 1
+
+	def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
+		doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
+		fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
+
+		alias_dict = {
+			"item_code": "rm_item_code",
+			"name": "bom_detail_no",
+			"source_warehouse": "reserve_warehouse",
+		}
+		for field in [
+			"item_code",
+			"name",
+			"rate",
+			"stock_uom",
+			"source_warehouse",
+			"description",
+			"item_name",
+			"stock_uom",
+		]:
+			fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
+
+		filters = [
+			[doctype, "parent", "=", bom_no],
+			[doctype, "docstatus", "=", 1],
+			["BOM", "item", "=", item_code],
+			[doctype, "sourced_by_supplier", "=", 0],
+		]
+
+		return (
+			frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
+		)
+
+	def __update_reserve_warehouse(self, row, item):
+		if self.doctype == self.subcontract_data.order_doctype:
+			row.reserve_warehouse = self.set_reserve_warehouse or item.warehouse
+
+	def __set_alternative_item(self, bom_item):
+		if self.alternative_item_details.get(bom_item.rm_item_code):
+			bom_item.update(self.alternative_item_details[bom_item.rm_item_code])
+
+	def __set_serial_nos(self, item_row, rm_obj):
+		key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
+		if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
+			used_serial_nos = self.available_materials[key]["serial_no"][0 : cint(rm_obj.consumed_qty)]
+			rm_obj.serial_no = "\n".join(used_serial_nos)
+
+			# Removed the used serial nos from the list
+			for sn in used_serial_nos:
+				self.available_materials[key]["serial_no"].remove(sn)
+
+	def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty):
+		rm_obj.update(
+			{
+				"consumed_qty": qty,
+				"batch_no": batch_no,
+				"required_qty": qty,
+				self.subcontract_data.order_field: item_row.get(self.subcontract_data.order_field),
+			}
+		)
+
+		self.__set_serial_nos(item_row, rm_obj)
+
+	def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
+		rm_obj.required_qty = required_qty
+		rm_obj.consumed_qty = consumed_qty
+
+	def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
+		key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
+
+		if self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
+			new_rm_obj = None
+			for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
+				if batch_qty >= qty:
+					self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
+					self.available_materials[key]["batch_no"][batch_no] -= qty
+					return
+
+				elif qty > 0 and batch_qty > 0:
+					qty -= batch_qty
+					new_rm_obj = self.append(self.raw_material_table, bom_item)
+					new_rm_obj.reference_name = item_row.name
+					self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty)
+					self.available_materials[key]["batch_no"][batch_no] = 0
+
+			if abs(qty) > 0 and not new_rm_obj:
+				self.__set_consumed_qty(rm_obj, qty)
+		else:
+			self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty)
+			self.__set_serial_nos(item_row, rm_obj)
+
+	def __add_supplied_item(self, item_row, bom_item, qty):
+		bom_item.conversion_factor = item_row.conversion_factor
+		rm_obj = self.append(self.raw_material_table, bom_item)
+		rm_obj.reference_name = item_row.name
+
+		if self.doctype == "Subcontracting Receipt":
+			args = frappe._dict(
+				{
+					"item_code": rm_obj.rm_item_code,
+					"warehouse": self.supplier_warehouse,
+					"posting_date": self.posting_date,
+					"posting_time": self.posting_time,
+					"qty": -1 * flt(rm_obj.consumed_qty),
+					"serial_no": rm_obj.serial_no,
+					"batch_no": rm_obj.batch_no,
+					"voucher_type": self.doctype,
+					"voucher_no": self.name,
+					"company": self.company,
+					"allow_zero_valuation": 1,
+				}
+			)
+			rm_obj.rate = get_incoming_rate(args)
+
+		if self.doctype == self.subcontract_data.order_doctype:
+			rm_obj.required_qty = qty
+			rm_obj.amount = rm_obj.required_qty * rm_obj.rate
+		else:
+			rm_obj.consumed_qty = 0
+			setattr(
+				rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field)
+			)
+			self.__set_batch_nos(bom_item, item_row, rm_obj, qty)
+
+	def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
+		key = (item_row.item_code, item_row.get(self.subcontract_data.order_field))
+
+		if self.qty_to_be_received == item_row.qty:
+			return transfer_item.qty
+
+		if self.qty_to_be_received:
+			qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
+			transfer_item.item_details.required_qty = transfer_item.qty
+
+			if transfer_item.serial_no or frappe.get_cached_value(
+				"UOM", transfer_item.item_details.stock_uom, "must_be_whole_number"
+			):
+				return frappe.utils.ceil(qty)
+
+			return qty
+
+	def __set_supplied_items(self):
+		self.bom_items = {}
+
+		has_supplied_items = True if self.get(self.raw_material_table) else False
+		for row in self.items:
+			if self.doctype != self.subcontract_data.order_doctype and (
+				(self.__changed_name and row.name not in self.__changed_name)
+				or (has_supplied_items and not self.__changed_name)
+			):
+				continue
+
+			if self.doctype == self.subcontract_data.order_doctype or self.backflush_based_on == "BOM":
+				for bom_item in self.__get_materials_from_bom(
+					row.item_code, row.bom, row.get("include_exploded_items")
+				):
+					qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor
+					bom_item.main_item_code = row.item_code
+					self.__update_reserve_warehouse(bom_item, row)
+					self.__set_alternative_item(bom_item)
+					self.__add_supplied_item(row, bom_item, qty)
+
+			elif self.backflush_based_on != "BOM":
+				for key, transfer_item in self.available_materials.items():
+					if (key[1], key[2]) == (
+						row.item_code,
+						row.get(self.subcontract_data.order_field),
+					) and transfer_item.qty > 0:
+						qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
+						transfer_item.qty -= qty
+						self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
+
+				if self.qty_to_be_received:
+					self.qty_to_be_received[
+						(row.item_code, row.get(self.subcontract_data.order_field))
+					] -= row.qty
+
+	def __prepare_supplied_items(self):
+		self.initialized_fields()
+		self.__get_subcontract_orders()
+		self.__get_pending_qty_to_receive()
+		self.get_available_materials()
+		self.__remove_changed_rows()
+		self.__set_supplied_items()
+
+	def __validate_batch_no(self, row, key):
+		if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get(
+			"batch_no"
+		):
+			link = get_link_to_form(
+				self.subcontract_data.order_doctype, row.get(self.subcontract_data.order_field)
+			)
+			msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the {self.subcontract_data.order_doctype} {link}'
+			frappe.throw(_(msg), title=_("Incorrect Batch Consumed"))
+
+	def __validate_serial_no(self, row, key):
+		if row.get("serial_no"):
+			serial_nos = get_serial_nos(row.get("serial_no"))
+			incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get("serial_no"))
+
+			if incorrect_sn:
+				incorrect_sn = "\n".join(incorrect_sn)
+				link = get_link_to_form(
+					self.subcontract_data.order_doctype, row.get(self.subcontract_data.order_field)
+				)
+				msg = f"The Serial Nos {incorrect_sn} has not supplied against the {self.subcontract_data.order_doctype} {link}"
+				frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed"))
+
+	def __validate_supplied_items(self):
+		if self.doctype not in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
+			return
+
+		for row in self.get(self.raw_material_table):
+			key = (row.rm_item_code, row.main_item_code, row.get(self.subcontract_data.order_field))
+			if not self.__transferred_items or not self.__transferred_items.get(key):
+				return
+
+			self.__validate_batch_no(row, key)
+			self.__validate_serial_no(row, key)
+
+	def set_materials_for_subcontracted_items(self, raw_material_table):
+		if self.doctype == "Purchase Invoice" and not self.update_stock:
+			return
+
+		self.raw_material_table = raw_material_table
+		self.__identify_change_in_item_table()
+		self.__prepare_supplied_items()
+		self.__validate_supplied_items()
+
+	def create_raw_materials_supplied(self, raw_material_table="supplied_items"):
+		self.set_materials_for_subcontracted_items(raw_material_table)
+
+		if self.doctype in ["Subcontracting Receipt", "Purchase Receipt", "Purchase Invoice"]:
+			for item in self.get("items"):
+				item.rm_supp_cost = 0.0
+
+	def __update_consumed_qty_in_subcontract_order(self, itemwise_consumed_qty):
+		fields = ["main_item_code", "rm_item_code", "parent", "supplied_qty", "name"]
+		filters = {"docstatus": 1, "parent": ("in", self.subcontract_orders)}
+
+		for row in frappe.get_all(
+			self.subcontract_data.order_supplied_items_field, fields=fields, filters=filters, order_by="idx"
+		):
+			key = (row.rm_item_code, row.main_item_code, row.parent)
+			consumed_qty = itemwise_consumed_qty.get(key, 0)
+
+			if row.supplied_qty < consumed_qty:
+				consumed_qty = row.supplied_qty
+
+			itemwise_consumed_qty[key] -= consumed_qty
+			frappe.db.set_value(
+				self.subcontract_data.order_supplied_items_field, row.name, "consumed_qty", consumed_qty
+			)
+
+	def set_consumed_qty_in_subcontract_order(self):
+		# Update consumed qty back in the subcontract order
+		if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"] or self.get(
+			"is_old_subcontracting_flow"
+		):
+			self.__get_subcontract_orders()
+			itemwise_consumed_qty = defaultdict(float)
+			if self.get("is_old_subcontracting_flow"):
+				doctypes = ["Purchase Receipt", "Purchase Invoice"]
+			else:
+				doctypes = ["Subcontracting Receipt"]
+
+			for doctype in doctypes:
+				consumed_items, receipt_items = self.__update_consumed_materials(
+					doctype, return_consumed_items=True
+				)
+
+				for row in consumed_items:
+					key = (row.rm_item_code, row.main_item_code, receipt_items.get(row.reference_name))
+					itemwise_consumed_qty[key] += row.consumed_qty
+
+			self.__update_consumed_qty_in_subcontract_order(itemwise_consumed_qty)
+
+	def update_ordered_and_reserved_qty(self):
+		sco_map = {}
+		for item in self.get("items"):
+			if self.doctype == "Subcontracting Receipt" and item.subcontracting_order:
+				sco_map.setdefault(item.subcontracting_order, []).append(item.subcontracting_order_item)
+
+		for sco, sco_item_rows in sco_map.items():
+			if sco and sco_item_rows:
+				sco_doc = frappe.get_doc("Subcontracting Order", sco)
+
+				if sco_doc.status in ["Closed", "Cancelled"]:
+					frappe.throw(
+						_("{0} {1} is cancelled or closed").format(_("Subcontracting Order"), sco),
+						frappe.InvalidStatusError,
+					)
+
+				sco_doc.update_ordered_qty_for_subcontracting(sco_item_rows)
+				sco_doc.update_reserved_qty_for_subcontracting()
+
+	def make_sl_entries_for_supplier_warehouse(self, sl_entries):
+		if hasattr(self, "supplied_items"):
+			for item in self.get("supplied_items"):
+				# negative quantity is passed, as raw material qty has to be decreased
+				# when SCR is submitted and it has to be increased when SCR is cancelled
+				sl_entries.append(
+					self.get_sl_entries(
+						item,
+						{
+							"item_code": item.rm_item_code,
+							"warehouse": self.supplier_warehouse,
+							"actual_qty": -1 * flt(item.consumed_qty),
+							"dependant_sle_voucher_detail_no": item.reference_name,
+						},
+					)
+				)
+
+	def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
+		self.update_ordered_and_reserved_qty()
+
+		sl_entries = []
+		stock_items = self.get_stock_items()
+
+		for item in self.get("items"):
+			if item.item_code in stock_items and item.warehouse:
+				scr_qty = flt(item.qty) * flt(item.conversion_factor)
+
+				if scr_qty:
+					sle = self.get_sl_entries(
+						item, {"actual_qty": flt(scr_qty), "serial_no": cstr(item.serial_no).strip()}
+					)
+					rate_db_precision = 6 if cint(self.precision("rate", item)) <= 6 else 9
+					incoming_rate = flt(item.rate, rate_db_precision)
+					sle.update(
+						{
+							"incoming_rate": incoming_rate,
+							"recalculate_rate": 1,
+						}
+					)
+					sl_entries.append(sle)
+
+				if flt(item.rejected_qty) != 0:
+					sl_entries.append(
+						self.get_sl_entries(
+							item,
+							{
+								"warehouse": item.rejected_warehouse,
+								"actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor),
+								"serial_no": cstr(item.rejected_serial_no).strip(),
+								"incoming_rate": 0.0,
+								"recalculate_rate": 1,
+							},
+						)
+					)
+
+		self.make_sl_entries_for_supplier_warehouse(sl_entries)
+		self.make_sl_entries(
+			sl_entries,
+			allow_negative_stock=allow_negative_stock,
+			via_landed_cost_voucher=via_landed_cost_voucher,
+		)
+
+	def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
+		supplied_items_cost = 0.0
+		for item in self.get("supplied_items"):
+			if item.reference_name == item_row_id:
+				if (
+					self.get("is_old_subcontracting_flow")
+					and reset_outgoing_rate
+					and frappe.get_cached_value("Item", item.rm_item_code, "is_stock_item")
+				):
+					rate = get_incoming_rate(
+						{
+							"item_code": item.rm_item_code,
+							"warehouse": self.supplier_warehouse,
+							"posting_date": self.posting_date,
+							"posting_time": self.posting_time,
+							"qty": -1 * item.consumed_qty,
+							"serial_no": item.serial_no,
+							"batch_no": item.batch_no,
+						}
+					)
+
+					if rate > 0:
+						item.rate = rate
+
+				item.amount = flt(flt(item.consumed_qty) * flt(item.rate), item.precision("amount"))
+				supplied_items_cost += item.amount
+
+		return supplied_items_cost
+
+	def set_subcontracting_order_status(self):
+		if self.doctype == "Subcontracting Order":
+			self.update_status()
+		elif self.doctype == "Subcontracting Receipt":
+			self.__get_subcontract_orders
+
+			if self.subcontract_orders:
+				for sco in set(self.subcontract_orders):
+					sco_doc = frappe.get_doc("Subcontracting Order", sco)
+					sco_doc.update_status()
+
+	@frappe.whitelist()
+	def get_current_stock(self):
+		if self.doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
+			for item in self.get("supplied_items"):
+				if self.supplier_warehouse:
+					actual_qty = frappe.db.get_value(
+						"Bin",
+						{"item_code": item.rm_item_code, "warehouse": self.supplier_warehouse},
+						"actual_qty",
+					)
+					item.current_stock = flt(actual_qty) or 0
+
+	@property
+	def sub_contracted_items(self):
+		if not hasattr(self, "_sub_contracted_items"):
+			self._sub_contracted_items = []
+			item_codes = list(set(item.item_code for item in self.get("items")))
+			if item_codes:
+				items = frappe.get_all(
+					"Item", filters={"name": ["in", item_codes], "is_sub_contracted_item": 1}
+				)
+				self._sub_contracted_items = [item.name for item in items]
+
+		return self._sub_contracted_items
+
+
+def get_item_details(items):
+	item = frappe.qb.DocType("Item")
+	item_list = (
+		frappe.qb.from_(item)
+		.select(item.item_code, item.description, item.allow_alternative_item)
+		.where(item.name.isin(items))
+		.run(as_dict=True)
+	)
+
+	item_details = {}
+	for item in item_list:
+		item_details[item.item_code] = item
+
+	return item_details
+
+
+@frappe.whitelist()
+def make_rm_stock_entry(subcontract_order, rm_items, order_doctype="Subcontracting Order"):
+	rm_items_list = rm_items
+
+	if isinstance(rm_items, str):
+		rm_items_list = json.loads(rm_items)
+	elif not rm_items:
+		frappe.throw(_("No Items available for transfer"))
+
+	if rm_items_list:
+		fg_items = list(set(item["item_code"] for item in rm_items_list))
+	else:
+		frappe.throw(_("No Items selected for transfer"))
+
+	if subcontract_order:
+		subcontract_order = frappe.get_doc(order_doctype, subcontract_order)
+
+	if fg_items:
+		items = tuple(set(item["rm_item_code"] for item in rm_items_list))
+		item_wh = get_item_details(items)
+
+		stock_entry = frappe.new_doc("Stock Entry")
+		stock_entry.purpose = "Send to Subcontractor"
+		if order_doctype == "Purchase Order":
+			stock_entry.purchase_order = subcontract_order.name
+		else:
+			stock_entry.subcontracting_order = subcontract_order.name
+		stock_entry.supplier = subcontract_order.supplier
+		stock_entry.supplier_name = subcontract_order.supplier_name
+		stock_entry.supplier_address = subcontract_order.supplier_address
+		stock_entry.address_display = subcontract_order.address_display
+		stock_entry.company = subcontract_order.company
+		stock_entry.to_warehouse = subcontract_order.supplier_warehouse
+		stock_entry.set_stock_entry_type()
+
+		if order_doctype == "Purchase Order":
+			rm_detail_field = "po_detail"
+		else:
+			rm_detail_field = "sco_rm_detail"
+
+		for item_code in fg_items:
+			for rm_item_data in rm_items_list:
+				if rm_item_data["item_code"] == item_code:
+					rm_item_code = rm_item_data["rm_item_code"]
+					items_dict = {
+						rm_item_code: {
+							rm_detail_field: rm_item_data.get("name"),
+							"item_name": rm_item_data["item_name"],
+							"description": item_wh.get(rm_item_code, {}).get("description", ""),
+							"qty": rm_item_data["qty"],
+							"from_warehouse": rm_item_data["warehouse"],
+							"stock_uom": rm_item_data["stock_uom"],
+							"serial_no": rm_item_data.get("serial_no"),
+							"batch_no": rm_item_data.get("batch_no"),
+							"main_item_code": rm_item_data["item_code"],
+							"allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"),
+						}
+					}
+					stock_entry.add_to_stock_entry_detail(items_dict)
+		return stock_entry.as_dict()
+	else:
+		frappe.throw(_("No Items selected for transfer"))
+	return subcontract_order.name
+
+
+def add_items_in_ste(
+	ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_detail", batch_no=None
+):
+	item = ste_doc.append("items", row.item_details)
+
+	rm_detail = list(set(row.get(f"{rm_detail_field}s")).intersection(rm_details))
+	item.update(
+		{
+			"qty": qty,
+			"batch_no": batch_no,
+			"basic_rate": row.item_details["rate"],
+			rm_detail_field: rm_detail[0] if rm_detail else "",
+			"s_warehouse": row.item_details["t_warehouse"],
+			"t_warehouse": row.item_details["s_warehouse"],
+			"item_code": row.item_details["rm_item_code"],
+			"subcontracted_item": row.item_details["main_item_code"],
+			"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
+		}
+	)
+
+
+def make_return_stock_entry_for_subcontract(
+	available_materials, order_doc, rm_details, order_doctype="Subcontracting Order"
+):
+	ste_doc = frappe.new_doc("Stock Entry")
+	ste_doc.purpose = "Material Transfer"
+
+	if order_doctype == "Purchase Order":
+		ste_doc.purchase_order = order_doc.name
+		rm_detail_field = "po_detail"
+	else:
+		ste_doc.subcontracting_order = order_doc.name
+		rm_detail_field = "sco_rm_detail"
+	ste_doc.company = order_doc.company
+	ste_doc.is_return = 1
+
+	for key, value in available_materials.items():
+		if not value.qty:
+			continue
+
+		if value.batch_no:
+			for batch_no, qty in value.batch_no.items():
+				if qty > 0:
+					add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field, batch_no)
+		else:
+			add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field)
+
+	ste_doc.set_stock_entry_type()
+	ste_doc.calculate_rate_and_amount()
+
+	return ste_doc
+
+
+@frappe.whitelist()
+def get_materials_from_supplier(
+	subcontract_order, rm_details, order_doctype="Subcontracting Order"
+):
+	if isinstance(rm_details, str):
+		rm_details = json.loads(rm_details)
+
+	doc = frappe.get_cached_doc(order_doctype, subcontract_order)
+	doc.initialized_fields()
+	doc.subcontract_orders = [doc.name]
+	doc.get_available_materials()
+
+	if not doc.available_materials:
+		frappe.throw(
+			_("Materials are already received against the {0} {1}").format(order_doctype, subcontract_order)
+		)
+
+	return make_return_stock_entry_for_subcontract(
+		doc.available_materials, doc, rm_details, order_doctype
+	)
diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py
new file mode 100644
index 0000000..4fab805
--- /dev/null
+++ b/erpnext/controllers/tests/test_subcontracting_controller.py
@@ -0,0 +1,1077 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import copy
+from collections import defaultdict
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import cint
+
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.controllers.subcontracting_controller import (
+	get_materials_from_supplier,
+	make_rm_stock_entry,
+)
+from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+	make_subcontracting_receipt,
+)
+
+
+class TestSubcontractingController(FrappeTestCase):
+	def setUp(self):
+		make_subcontracted_items()
+		make_raw_materials()
+		make_service_items()
+		make_bom_for_subcontracted_items()
+
+	def test_remove_empty_rows(self):
+		sco = get_subcontracting_order()
+		len_before = len(sco.service_items)
+		sco.service_items[0].item_code = None
+		sco.remove_empty_rows()
+		self.assertEqual((len_before - 1), len(sco.service_items))
+
+	def test_create_raw_materials_supplied(self):
+		sco = get_subcontracting_order()
+		sco.supplied_items = None
+		sco.create_raw_materials_supplied()
+		self.assertIsNotNone(sco.supplied_items)
+
+	def test_sco_with_bom(self):
+		"""
+		- Set backflush based on BOM.
+		- Create SCO for the item Subcontracted Item SA1 and add same item two times.
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Create SCR against the SCO and check serial nos and batch no.
+		"""
+
+		set_backflush_based_on("BOM")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA1",
+				"fg_item_qty": 5,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 6,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA1",
+				"fg_item_qty": 6,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		scr.submit()
+
+		for key, value in get_supplied_items(scr).items():
+			transferred_detais = itemwise_details.get(key)
+
+			for field in ["qty", "serial_no", "batch_no"]:
+				if value.get(field):
+					transfer, consumed = (transferred_detais.get(field), value.get(field))
+					if field == "serial_no":
+						transfer, consumed = (sorted(transfer), sorted(consumed))
+
+					self.assertEqual(transfer, consumed)
+
+	def test_sco_with_material_transfer(self):
+		"""
+		- Set backflush based on Material Transfer.
+		- Create SCO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
+		- Create partial SCR against the SCO and check serial nos and batch no.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA1",
+				"fg_item_qty": 5,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 5",
+				"qty": 6,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA5",
+				"fg_item_qty": 6,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		rm_items.append(
+			{
+				"main_item_code": "Subcontracted Item SA5",
+				"item_code": "Subcontracted SRM Item 4",
+				"qty": 6,
+			}
+		)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.remove(scr1.items[1])
+		scr1.save()
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			transferred_detais = itemwise_details.get(key)
+
+			for field in ["qty", "serial_no", "batch_no"]:
+				if value.get(field):
+					self.assertEqual(value.get(field), transferred_detais.get(field))
+
+		scr2 = make_subcontracting_receipt(sco.name)
+		scr2.save()
+		scr2.submit()
+
+		for key, value in get_supplied_items(scr2).items():
+			transferred_detais = itemwise_details.get(key)
+
+			for field in ["qty", "serial_no", "batch_no"]:
+				if value.get(field):
+					self.assertEqual(value.get(field), transferred_detais.get(field))
+
+	def test_subcontracting_with_same_components_different_fg(self):
+		"""
+		- Set backflush based on Material Transfer.
+		- Create SCO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
+		- Transfer the components from Stores to Supplier warehouse with serial nos.
+		- Transfer extra qty of components for the item Subcontracted Item SA2.
+		- Create partial SCR against the SCO and check serial nos.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 2",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA2",
+				"fg_item_qty": 5,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 3",
+				"qty": 6,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA3",
+				"fg_item_qty": 6,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		rm_items[0]["qty"] += 1
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.items[0].qty = 3
+		scr1.remove(scr1.items[1])
+		scr1.save()
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			transferred_detais = itemwise_details.get(key)
+
+			self.assertEqual(value.qty, 4)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:4]))
+
+		scr2 = make_subcontracting_receipt(sco.name)
+		scr2.items[0].qty = 2
+		scr2.remove(scr2.items[1])
+		scr2.save()
+		scr2.submit()
+
+		for key, value in get_supplied_items(scr2).items():
+			transferred_detais = itemwise_details.get(key)
+
+			self.assertEqual(value.qty, 2)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[4:6]))
+
+		scr3 = make_subcontracting_receipt(sco.name)
+		scr3.save()
+		scr3.submit()
+
+		for key, value in get_supplied_items(scr3).items():
+			transferred_detais = itemwise_details.get(key)
+
+			self.assertEqual(value.qty, 6)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12]))
+
+	def test_return_non_consumed_materials(self):
+		"""
+		- Set backflush based on Material Transfer.
+		- Create SCO for item Subcontracted Item SA2.
+		- Transfer the components from Stores to Supplier warehouse with serial nos.
+		- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
+		- Create SCR for full qty against the SCO and change the qty of raw material.
+		- After that return the non consumed material back to the store from supplier's warehouse.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 2",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA2",
+				"fg_item_qty": 5,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		rm_items[0]["qty"] += 1
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.save()
+		scr1.supplied_items[0].consumed_qty = 5
+		scr1.supplied_items[0].serial_no = "\n".join(
+			sorted(itemwise_details.get("Subcontracted SRM Item 2").get("serial_no")[0:5])
+		)
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			transferred_detais = itemwise_details.get(key)
+			self.assertEqual(value.qty, 5)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
+
+		sco.load_from_db()
+		self.assertEqual(sco.supplied_items[0].consumed_qty, 5)
+		doc = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items])
+		self.assertEqual(doc.items[0].qty, 1)
+		self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
+		self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
+		self.assertEqual(
+			get_serial_nos(doc.items[0].serial_no),
+			itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
+		)
+
+	def test_item_with_batch_based_on_bom(self):
+		"""
+		- Set backflush based on BOM.
+		- Create SCO for item Subcontracted Item SA4 (has batch no).
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer the components in multiple batches.
+		- Create the 3 SCR against the SCO and split Subcontracted Items into two batches.
+		- Keep the qty as 2 for Subcontracted Item in the SCR.
+		"""
+
+		set_backflush_based_on("BOM")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 4",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA4",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = [
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 1",
+				"qty": 10.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 2",
+				"qty": 10.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 1.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+		]
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.items[0].qty = 2
+		add_second_row_in_scr(scr1)
+		scr1.flags.ignore_mandatory = True
+		scr1.save()
+		scr1.set_missing_values()
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			self.assertEqual(value.qty, 4)
+
+		scr2 = make_subcontracting_receipt(sco.name)
+		scr2.items[0].qty = 2
+		add_second_row_in_scr(scr2)
+		scr2.flags.ignore_mandatory = True
+		scr2.save()
+		scr2.set_missing_values()
+		scr2.submit()
+
+		for key, value in get_supplied_items(scr2).items():
+			self.assertEqual(value.qty, 4)
+
+		scr3 = make_subcontracting_receipt(sco.name)
+		scr3.items[0].qty = 2
+		scr3.flags.ignore_mandatory = True
+		scr3.save()
+		scr3.set_missing_values()
+		scr3.submit()
+
+		for key, value in get_supplied_items(scr3).items():
+			self.assertEqual(value.qty, 2)
+
+	def test_item_with_batch_based_on_material_transfer(self):
+		"""
+		- Set backflush based on Material Transferred for Subcontract.
+		- Create SCO for item Subcontracted Item SA4 (has batch no).
+		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+		- Transfer the components in multiple batches with extra 2 qty for the batched item.
+		- Create the 3 SCR against the SCO and split Subcontracted Items into two batches.
+		- Keep the qty as 2 for Subcontracted Item in the SCR.
+		- In the first SCR the batched raw materials will be consumed 2 extra qty.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 4",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA4",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = [
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 1",
+				"qty": 10.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 2",
+				"qty": 10.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+			{
+				"main_item_code": "Subcontracted Item SA4",
+				"item_code": "Subcontracted SRM Item 3",
+				"qty": 3.0,
+				"rate": 100.0,
+				"stock_uom": "Nos",
+				"warehouse": "_Test Warehouse - _TC",
+			},
+		]
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.items[0].qty = 2
+		add_second_row_in_scr(scr1)
+		scr1.flags.ignore_mandatory = True
+		scr1.save()
+		scr1.set_missing_values()
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			qty = 4 if key != "Subcontracted SRM Item 3" else 6
+			self.assertEqual(value.qty, qty)
+
+		scr2 = make_subcontracting_receipt(sco.name)
+		scr2.items[0].qty = 2
+		add_second_row_in_scr(scr2)
+		scr2.flags.ignore_mandatory = True
+		scr2.save()
+		scr2.set_missing_values()
+		scr2.submit()
+
+		for key, value in get_supplied_items(scr2).items():
+			self.assertEqual(value.qty, 4)
+
+		scr3 = make_subcontracting_receipt(sco.name)
+		scr3.items[0].qty = 2
+		scr3.flags.ignore_mandatory = True
+		scr3.save()
+		scr3.set_missing_values()
+		scr3.submit()
+
+		for key, value in get_supplied_items(scr3).items():
+			self.assertEqual(value.qty, 1)
+
+	def test_partial_transfer_serial_no_components_based_on_material_transfer(self):
+		"""
+		- Set backflush based on Material Transferred for Subcontract.
+		- Create SCO for the item Subcontracted Item SA2.
+		- Transfer the partial components from Stores to Supplier warehouse with serial nos.
+		- Create partial SCR against the SCO and change the qty manually.
+		- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
+		- Create SCR for remaining qty against the SCO and change the qty manually.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 2",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA2",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		rm_items[0]["qty"] = 5
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.items[0].qty = 5
+		scr1.flags.ignore_mandatory = True
+		scr1.save()
+		scr1.set_missing_values()
+
+		for key, value in get_supplied_items(scr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, 3)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
+
+		scr1.load_from_db()
+		scr1.supplied_items[0].consumed_qty = 5
+		scr1.supplied_items[0].serial_no = "\n".join(
+			itemwise_details[scr1.supplied_items[0].rm_item_code]["serial_no"]
+		)
+		scr1.save()
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr2 = make_subcontracting_receipt(sco.name)
+		scr2.submit()
+
+		for key, value in get_supplied_items(scr2).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
+
+	def test_incorrect_serial_no_components_based_on_material_transfer(self):
+		"""
+		- Set backflush based on Material Transferred for Subcontract.
+		- Create SCO for the item Subcontracted Item SA2.
+		- Transfer the serialized componenets to the supplier.
+		- Create SCR and change the serial no which is not transferred.
+		- System should throw the error and not allowed to save the SCR.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 2",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA2",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.save()
+		scr1.supplied_items[0].serial_no = "ABCD"
+		self.assertRaises(frappe.ValidationError, scr1.save)
+		scr1.delete()
+
+	def test_partial_transfer_batch_based_on_material_transfer(self):
+		"""
+		- Set backflush based on Material Transferred for Subcontract.
+		- Create SCO for the item Subcontracted Item SA6.
+		- Transfer the partial components from Stores to Supplier warehouse with batch.
+		- Create partial SCR against the SCO and change the qty manually.
+		- Transfer the remaining components from Stores to Supplier warehouse with batch.
+		- Create SCR for remaining qty against the SCO and change the qty manually.
+		"""
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 6",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA6",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		rm_items[0]["qty"] = 5
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.items[0].qty = 5
+		scr1.save()
+
+		transferred_batch_no = ""
+		for key, value in get_supplied_items(scr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, 3)
+			transferred_batch_no = details.batch_no
+			self.assertEqual(value.batch_no, details.batch_no)
+
+		scr1.load_from_db()
+		scr1.supplied_items[0].consumed_qty = 5
+		scr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
+		scr1.save()
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(value.batch_no, details.batch_no)
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name
+
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.submit()
+
+		for key, value in get_supplied_items(scr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(value.batch_no, details.batch_no)
+
+	def test_sco_supplied_qty(self):
+		"""
+		Check if 'Supplied Qty' in SCO's Supplied Items table is reset on submit/cancel.
+		"""
+		set_backflush_based_on("Material Transferred for Subcontract")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA1",
+				"fg_item_qty": 5,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 5",
+				"qty": 6,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA5",
+				"fg_item_qty": 6,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = [
+			{"item_code": "Subcontracted SRM Item 1", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 2", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 3", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
+			{"item_code": "Subcontracted SRM Item 5", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
+			{"item_code": "Subcontracted SRM Item 4", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
+		]
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		for item in rm_items:
+			item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name
+
+		se = make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+
+		sco.reload()
+		for item in sco.get("supplied_items"):
+			self.assertIn(item.supplied_qty, [5.0, 6.0])
+
+		se.cancel()
+		sco.reload()
+		for item in sco.get("supplied_items"):
+			self.assertEqual(item.supplied_qty, 0.0)
+
+
+def add_second_row_in_scr(scr):
+	item_dict = {}
+	for column in [
+		"item_code",
+		"item_name",
+		"qty",
+		"uom",
+		"warehouse",
+		"stock_uom",
+		"subcontracting_order",
+		"subcontracting_order_finished_good_item",
+		"conversion_factor",
+		"rate",
+		"expense_account",
+		"sco_rm_detail",
+	]:
+		item_dict[column] = scr.items[0].get(column)
+
+	scr.append("items", item_dict)
+
+
+def get_supplied_items(scr_doc):
+	supplied_items = {}
+	for row in scr_doc.get("supplied_items"):
+		if row.rm_item_code not in supplied_items:
+			supplied_items.setdefault(
+				row.rm_item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
+			)
+
+		details = supplied_items[row.rm_item_code]
+		update_item_details(row, details)
+
+	return supplied_items
+
+
+def make_stock_in_entry(**args):
+	args = frappe._dict(args)
+
+	items = {}
+	for row in args.rm_items:
+		row = frappe._dict(row)
+
+		doc = make_stock_entry(
+			target=row.warehouse or "_Test Warehouse - _TC",
+			item_code=row.item_code,
+			qty=row.qty or 1,
+			basic_rate=row.rate or 100,
+		)
+
+		if row.item_code not in items:
+			items.setdefault(
+				row.item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
+			)
+
+		child_row = doc.items[0]
+		details = items[child_row.item_code]
+		update_item_details(child_row, details)
+
+	return items
+
+
+def update_item_details(child_row, details):
+	details.qty += (
+		child_row.get("qty")
+		if child_row.doctype == "Stock Entry Detail"
+		else child_row.get("consumed_qty")
+	)
+
+	if child_row.serial_no:
+		details.serial_no.extend(get_serial_nos(child_row.serial_no))
+
+	if child_row.batch_no:
+		details.batch_no[child_row.batch_no] += child_row.get("qty") or child_row.get("consumed_qty")
+
+
+def make_stock_transfer_entry(**args):
+	args = frappe._dict(args)
+
+	items = []
+	for row in args.rm_items:
+		row = frappe._dict(row)
+
+		item = {
+			"item_code": row.main_item_code or args.main_item_code,
+			"rm_item_code": row.item_code,
+			"qty": row.qty or 1,
+			"item_name": row.item_code,
+			"rate": row.rate or 100,
+			"stock_uom": row.stock_uom or "Nos",
+			"warehouse": row.warehuose or "_Test Warehouse - _TC",
+		}
+
+		item_details = args.itemwise_details.get(row.item_code)
+
+		if item_details and item_details.serial_no:
+			serial_nos = item_details.serial_no[0 : cint(row.qty)]
+			item["serial_no"] = "\n".join(serial_nos)
+			item_details.serial_no = list(set(item_details.serial_no) - set(serial_nos))
+
+		if item_details and item_details.batch_no:
+			for batch_no, batch_qty in item_details.batch_no.items():
+				if batch_qty >= row.qty:
+					item["batch_no"] = batch_no
+					item_details.batch_no[batch_no] -= row.qty
+					break
+
+		items.append(item)
+
+	ste_dict = make_rm_stock_entry(args.sco_no, items)
+	doc = frappe.get_doc(ste_dict)
+	doc.insert()
+	doc.submit()
+
+	return doc
+
+
+def make_subcontracted_items():
+	sub_contracted_items = {
+		"Subcontracted Item SA1": {},
+		"Subcontracted Item SA2": {},
+		"Subcontracted Item SA3": {},
+		"Subcontracted Item SA4": {
+			"has_batch_no": 1,
+			"create_new_batch": 1,
+			"batch_number_series": "SBAT.####",
+		},
+		"Subcontracted Item SA5": {},
+		"Subcontracted Item SA6": {},
+		"Subcontracted Item SA7": {},
+	}
+
+	for item, properties in sub_contracted_items.items():
+		if not frappe.db.exists("Item", item):
+			properties.update({"is_stock_item": 1, "is_sub_contracted_item": 1})
+			make_item(item, properties)
+
+
+def make_raw_materials():
+	raw_materials = {
+		"Subcontracted SRM Item 1": {},
+		"Subcontracted SRM Item 2": {"has_serial_no": 1, "serial_no_series": "SRI.####"},
+		"Subcontracted SRM Item 3": {
+			"has_batch_no": 1,
+			"create_new_batch": 1,
+			"batch_number_series": "BAT.####",
+		},
+		"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
+		"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
+	}
+
+	for item, properties in raw_materials.items():
+		if not frappe.db.exists("Item", item):
+			properties.update({"is_stock_item": 1})
+			make_item(item, properties)
+
+
+def make_service_item(item, properties={}):
+	if not frappe.db.exists("Item", item):
+		properties.update({"is_stock_item": 0})
+		make_item(item, properties)
+
+
+def make_service_items():
+	service_items = {
+		"Subcontracted Service Item 1": {},
+		"Subcontracted Service Item 2": {},
+		"Subcontracted Service Item 3": {},
+		"Subcontracted Service Item 4": {},
+		"Subcontracted Service Item 5": {},
+		"Subcontracted Service Item 6": {},
+		"Subcontracted Service Item 7": {},
+	}
+
+	for item, properties in service_items.items():
+		make_service_item(item, properties)
+
+
+def make_bom_for_subcontracted_items():
+	boms = {
+		"Subcontracted Item SA1": [
+			"Subcontracted SRM Item 1",
+			"Subcontracted SRM Item 2",
+			"Subcontracted SRM Item 3",
+		],
+		"Subcontracted Item SA2": ["Subcontracted SRM Item 2"],
+		"Subcontracted Item SA3": ["Subcontracted SRM Item 2"],
+		"Subcontracted Item SA4": [
+			"Subcontracted SRM Item 1",
+			"Subcontracted SRM Item 2",
+			"Subcontracted SRM Item 3",
+		],
+		"Subcontracted Item SA5": ["Subcontracted SRM Item 5"],
+		"Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
+		"Subcontracted Item SA7": ["Subcontracted SRM Item 1"],
+	}
+
+	for item_code, raw_materials in boms.items():
+		if not frappe.db.exists("BOM", {"item": item_code}):
+			make_bom(item=item_code, raw_materials=raw_materials, rate=100)
+
+
+def set_backflush_based_on(based_on):
+	frappe.db.set_value(
+		"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", based_on
+	)
+
+
+def get_subcontracting_order(**args):
+	from erpnext.subcontracting.doctype.subcontracting_order.test_subcontracting_order import (
+		create_subcontracting_order,
+	)
+
+	args = frappe._dict(args)
+
+	if args.get("po_name"):
+		po = frappe.get_doc("Purchase Order", args.get("po_name"))
+
+		if po.is_subcontracted:
+			return create_subcontracting_order(po_name=po.name, **args)
+
+	if not args.service_items:
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 7",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA7",
+				"fg_item_qty": 10,
+			},
+		]
+	else:
+		service_items = args.service_items
+
+	po = create_purchase_order(
+		rm_items=service_items,
+		is_subcontracted=1,
+		supplier_warehouse=args.supplier_warehouse or "_Test Warehouse 1 - _TC",
+	)
+
+	return create_subcontracting_order(po_name=po.name, **args)
+
+
+def get_rm_items(supplied_items):
+	rm_items = []
+
+	for item in supplied_items:
+		rm_items.append(
+			{
+				"main_item_code": item.main_item_code,
+				"item_code": item.rm_item_code,
+				"qty": item.required_qty,
+				"rate": item.rate,
+				"stock_uom": item.stock_uom,
+				"warehouse": item.reserve_warehouse,
+			}
+		)
+
+	return rm_items
+
+
+def make_subcontracted_item(**args):
+	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+	args = frappe._dict(args)
+
+	if not frappe.db.exists("Item", args.item_code):
+		make_item(
+			args.item_code,
+			{
+				"is_stock_item": 1,
+				"is_sub_contracted_item": 1,
+				"has_batch_no": args.get("has_batch_no") or 0,
+			},
+		)
+
+	if not args.raw_materials:
+		if not frappe.db.exists("Item", "Test Extra Item 1"):
+			make_item(
+				"Test Extra Item 1",
+				{
+					"is_stock_item": 1,
+				},
+			)
+
+		if not frappe.db.exists("Item", "Test Extra Item 2"):
+			make_item(
+				"Test Extra Item 2",
+				{
+					"is_stock_item": 1,
+				},
+			)
+
+		args.raw_materials = ["_Test FG Item", "Test Extra Item 1"]
+
+	if not frappe.db.get_value("BOM", {"item": args.item_code}, "name"):
+		make_bom(item=args.item_code, raw_materials=args.get("raw_materials"))
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 860512c..a190cc7 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -9,7 +9,7 @@
 from frappe.tests.utils import FrappeTestCase
 from frappe.utils import cstr, flt
 
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.controllers.tests.test_subcontracting_controller import set_backflush_based_on
 from erpnext.manufacturing.doctype.bom.bom import BOMRecursionError, item_query, make_variant_bom
 from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import (
 	update_cost_in_all_boms_in_test,
@@ -18,7 +18,6 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
-from erpnext.tests.test_subcontracting import set_backflush_based_on
 
 test_records = frappe.get_test_records("BOM")
 test_dependencies = ["Item", "Quality Inspection Template"]
@@ -256,12 +255,29 @@
 		bom.submit()
 		# test that sourced_by_supplier rate is zero even after updating cost
 		self.assertEqual(bom.items[2].rate, 0)
-		# test in Purchase Order sourced_by_supplier is not added to Supplied Item
-		po = create_purchase_order(
-			item_code=item_code, qty=1, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
+
+		from erpnext.controllers.tests.test_subcontracting_controller import (
+			get_subcontracting_order,
+			make_service_item,
+		)
+
+		make_service_item("Subcontracted Service Item 1")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 1,
+				"rate": 100,
+				"fg_item": item_code,
+				"fg_item_qty": 1,
+			},
+		]
+		# test in Subcontracting Order sourced_by_supplier is not added to Supplied Item
+		sco = get_subcontracting_order(
+			service_items=service_items, supplier_warehouse="_Test Warehouse 1 - _TC"
 		)
 		bom_items = sorted([d.item_code for d in bom.items if d.sourced_by_supplier != 1])
-		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
+		supplied_items = sorted([d.rm_item_code for d in sco.supplied_items])
 		self.assertEqual(bom_items, supplied_items)
 
 	def test_bom_tree_representation(self):
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index a73b9bc..70ccb78 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -508,7 +508,7 @@
 			po.is_subcontracted = 1
 			for row in po_list:
 				po_data = {
-					"item_code": row.production_item,
+					"fg_item": row.production_item,
 					"warehouse": row.fg_warehouse,
 					"production_plan_sub_assembly_item": row.name,
 					"bom": row.bom_no,
@@ -518,9 +518,6 @@
 				for field in [
 					"schedule_date",
 					"qty",
-					"uom",
-					"stock_uom",
-					"item_name",
 					"description",
 					"production_plan_item",
 				]:
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index 869166b..01ebed7 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -21,3 +21,4 @@
 Telephony
 Bulk Transaction
 E-commerce
+Subcontracting
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index aef80bc..9eaecbd 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -343,6 +343,7 @@
 execute:frappe.delete_doc("DocType", "Naming Series")
 erpnext.patches.v13_0.set_payroll_entry_status
 erpnext.patches.v13_0.job_card_status_on_hold
+erpnext.patches.v14_0.copy_is_subcontracted_value_to_is_old_subcontracting_flow
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
 erpnext.patches.v14_0.crm_ux_cleanup
 erpnext.patches.v14_0.remove_india_localisation
diff --git a/erpnext/patches/v13_0/add_bin_unique_constraint.py b/erpnext/patches/v13_0/add_bin_unique_constraint.py
index 38a8500..7ad2bec 100644
--- a/erpnext/patches/v13_0/add_bin_unique_constraint.py
+++ b/erpnext/patches/v13_0/add_bin_unique_constraint.py
@@ -64,4 +64,8 @@
 		bin.update(qty_dict)
 		bin.update_reserved_qty_for_production()
 		bin.update_reserved_qty_for_sub_contracting()
+		if frappe.db.count(
+			"Purchase Order", {"status": ["!=", "Completed"], "is_old_subcontracting_flow": 1}
+		):
+			bin.update_reserved_qty_for_sub_contracting(subcontract_doctype="Purchase Order")
 		bin.db_update()
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
index f6427ca..75a5477 100644
--- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -15,6 +15,8 @@
 		("accounts", "sales_invoice_item"),
 		("accounts", "purchase_invoice_item"),
 		("buying", "purchase_receipt_item_supplied"),
+		("subcontracting", "subcontracting_receipt_item"),
+		("subcontracting", "subcontracting_receipt_supplied_item"),
 	]
 
 	for module, doctype in doctypes_to_reload:
diff --git a/erpnext/patches/v14_0/copy_is_subcontracted_value_to_is_old_subcontracting_flow.py b/erpnext/patches/v14_0/copy_is_subcontracted_value_to_is_old_subcontracting_flow.py
new file mode 100644
index 0000000..607ef695
--- /dev/null
+++ b/erpnext/patches/v14_0/copy_is_subcontracted_value_to_is_old_subcontracting_flow.py
@@ -0,0 +1,12 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+
+
+def execute():
+	for doctype in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
+		tab = frappe.qb.DocType(doctype).as_("tab")
+		frappe.qb.update(tab).set(tab.is_old_subcontracting_flow, 1).where(
+			tab.is_subcontracted == 1
+		).run()
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index a5b7699..09779d8 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -83,9 +83,17 @@
 
 		this.frm.set_query("item_code", "items", function() {
 			if (me.frm.doc.is_subcontracted) {
+				var filters = {'supplier': me.frm.doc.supplier};
+				if (me.frm.doc.is_old_subcontracting_flow) {
+					filters["is_sub_contracted_item"] = 1;
+				}
+				else {
+					filters["is_stock_item"] = 0;
+				}
+
 				return{
 					query: "erpnext.controllers.queries.item_query",
-					filters:{ 'supplier': me.frm.doc.supplier, 'is_sub_contracted_item': 1 }
+					filters: filters
 				}
 			}
 			else {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 62b98ec..6b618b2 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -470,7 +470,8 @@
 							cost_center: item.cost_center,
 							tax_category: me.frm.doc.tax_category,
 							item_tax_template: item.item_tax_template,
-							child_docname: item.name
+							child_docname: item.name,
+							is_old_subcontracting_flow: me.frm.doc.is_old_subcontracting_flow,
 						}
 					},
 
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 096175a..62abb74 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -486,7 +486,11 @@
 				filters = {"is_sales_item": 1};
 			} else if (frm.doc.doctype == 'Purchase Order') {
 				if (frm.doc.is_subcontracted) {
-					filters = {"is_sub_contracted_item": 1};
+					if (frm.doc.is_old_subcontracting_flow) {
+						filters = {"is_sub_contracted_item": 1};
+					} else {
+						filters = {"is_stock_item": 0};
+					}
 				} else {
 					filters = {"is_purchase_item": 1};
 				}
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index bc235d9..548df31 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -40,25 +40,37 @@
 		self.db_set("reserved_qty_for_production", flt(self.reserved_qty_for_production))
 		self.db_set("projected_qty", self.projected_qty)
 
-	def update_reserved_qty_for_sub_contracting(self):
+	def update_reserved_qty_for_sub_contracting(self, subcontract_doctype="Subcontracting Order"):
 		# reserved qty
 
-		po = frappe.qb.DocType("Purchase Order")
-		supplied_item = frappe.qb.DocType("Purchase Order Item Supplied")
+		subcontract_order = frappe.qb.DocType(subcontract_doctype)
+		supplied_item = frappe.qb.DocType(
+			"Purchase Order Item Supplied"
+			if subcontract_doctype == "Purchase Order"
+			else "Subcontracting Order Supplied Item"
+		)
+
+		conditions = (
+			(supplied_item.rm_item_code == self.item_code)
+			& (subcontract_order.name == supplied_item.parent)
+			& (subcontract_order.per_received < 100)
+			& (supplied_item.reserve_warehouse == self.warehouse)
+			& (
+				(
+					(subcontract_order.is_old_subcontracting_flow == 1)
+					& (subcontract_order.status != "Closed")
+					& (subcontract_order.docstatus == 1)
+				)
+				if subcontract_doctype == "Purchase Order"
+				else (subcontract_order.docstatus == 1)
+			)
+		)
 
 		reserved_qty_for_sub_contract = (
-			frappe.qb.from_(po)
+			frappe.qb.from_(subcontract_order)
 			.from_(supplied_item)
 			.select(Sum(Coalesce(supplied_item.required_qty, 0)))
-			.where(
-				(supplied_item.rm_item_code == self.item_code)
-				& (po.name == supplied_item.parent)
-				& (po.docstatus == 1)
-				& (po.is_subcontracted)
-				& (po.status != "Closed")
-				& (po.per_received < 100)
-				& (supplied_item.reserve_warehouse == self.warehouse)
-			)
+			.where(conditions)
 		).run()[0][0] or 0.0
 
 		se = frappe.qb.DocType("Stock Entry")
@@ -71,23 +83,34 @@
 		else:
 			qty_field = se_item.transfer_qty
 
+		conditions = (
+			(se.docstatus == 1)
+			& (se.purpose == "Send to Subcontractor")
+			& ((se_item.item_code == self.item_code) | (se_item.original_item == self.item_code))
+			& (se.name == se_item.parent)
+			& (subcontract_order.docstatus == 1)
+			& (subcontract_order.per_received < 100)
+			& (
+				(
+					(Coalesce(se.purchase_order, "") != "")
+					& (subcontract_order.name == se.purchase_order)
+					& (subcontract_order.is_old_subcontracting_flow == 1)
+					& (subcontract_order.status != "Closed")
+				)
+				if subcontract_doctype == "Purchase Order"
+				else (
+					(Coalesce(se.subcontracting_order, "") != "")
+					& (subcontract_order.name == se.subcontracting_order)
+				)
+			)
+		)
+
 		materials_transferred = (
 			frappe.qb.from_(se)
 			.from_(se_item)
-			.from_(po)
+			.from_(subcontract_order)
 			.select(Sum(qty_field))
-			.where(
-				(se.docstatus == 1)
-				& (se.purpose == "Send to Subcontractor")
-				& (Coalesce(se.purchase_order, "") != "")
-				& ((se_item.item_code == self.item_code) | (se_item.original_item == self.item_code))
-				& (se.name == se_item.parent)
-				& (po.name == se.purchase_order)
-				& (po.docstatus == 1)
-				& (po.is_subcontracted == 1)
-				& (po.status != "Closed")
-				& (po.per_received < 100)
-			)
+			.where(conditions)
 		).run()[0][0] or 0.0
 
 		if reserved_qty_for_sub_contract > materials_transferred:
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index b8f4803..1996418 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -1,17 +1,16 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import json
-
 import frappe
 from frappe.tests.utils import FrappeTestCase
 from frappe.utils import flt
 
-from erpnext.buying.doctype.purchase_order.purchase_order import (
-	make_purchase_receipt,
-	make_rm_stock_entry,
+from erpnext.controllers.subcontracting_controller import make_rm_stock_entry
+from erpnext.controllers.tests.test_subcontracting_controller import (
+	get_subcontracting_order,
+	make_service_item,
+	set_backflush_based_on,
 )
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
 from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
@@ -22,6 +21,9 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
+from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+	make_subcontracting_receipt,
+)
 
 
 class TestItemAlternative(FrappeTestCase):
@@ -30,9 +32,7 @@
 		make_items()
 
 	def test_alternative_item_for_subcontract_rm(self):
-		frappe.db.set_value(
-			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
-		)
+		set_backflush_based_on("BOM")
 
 		create_stock_reconciliation(
 			item_code="Alternate Item For A RW 1", warehouse="_Test Warehouse - _TC", qty=5, rate=2000
@@ -42,15 +42,22 @@
 		)
 
 		supplier_warehouse = "Test Supplier Warehouse - _TC"
-		po = create_purchase_order(
-			item="Test Finished Goods - A",
-			is_subcontracted=1,
-			qty=5,
-			rate=3000,
-			supplier_warehouse=supplier_warehouse,
-		)
 
-		rm_item = [
+		make_service_item("Subcontracted Service Item 1")
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 5,
+				"rate": 3000,
+				"fg_item": "Test Finished Goods - A",
+				"fg_item_qty": 5,
+			},
+		]
+		sco = get_subcontracting_order(
+			service_items=service_items, supplier_warehouse=supplier_warehouse
+		)
+		rm_items = [
 			{
 				"item_code": "Test Finished Goods - A",
 				"rm_item_code": "Test FG A RW 1",
@@ -73,14 +80,13 @@
 			},
 		]
 
-		rm_item_string = json.dumps(rm_item)
 		reserved_qty_for_sub_contract = frappe.db.get_value(
 			"Bin",
 			{"item_code": "Test FG A RW 1", "warehouse": "_Test Warehouse - _TC"},
 			"reserved_qty_for_sub_contract",
 		)
 
-		se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
+		se = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items))
 		se.to_warehouse = supplier_warehouse
 		se.insert()
 
@@ -104,22 +110,17 @@
 			after_transfer_reserved_qty_for_sub_contract, flt(reserved_qty_for_sub_contract - 5)
 		)
 
-		pr = make_purchase_receipt(po.name)
-		pr.save()
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
 
-		pr = frappe.get_doc("Purchase Receipt", pr.name)
+		scr = frappe.get_doc("Subcontracting Receipt", scr.name)
 		status = False
-		for d in pr.supplied_items:
-			if d.rm_item_code == "Alternate Item For A RW 1":
+		for item in scr.supplied_items:
+			if item.rm_item_code == "Alternate Item For A RW 1":
 				status = True
 
 		self.assertEqual(status, True)
-		frappe.db.set_value(
-			"Buying Settings",
-			None,
-			"backflush_raw_materials_of_subcontract_based_on",
-			"Material Transferred for Subcontract",
-		)
+		set_backflush_based_on("Material Transferred for Subcontract")
 
 	def test_alternative_item_for_production_rm(self):
 		create_stock_reconciliation(
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 754404b..312c166 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -198,7 +198,7 @@
 			cur_frm.add_custom_button(__('Reopen'), this.reopen_purchase_receipt, __("Status"))
 		}
 
-		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
+		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_old_subcontracting_flow);
 	}
 
 	make_purchase_invoice() {
@@ -296,10 +296,11 @@
 frappe.provide("erpnext.buying");
 
 frappe.ui.form.on("Purchase Receipt", "is_subcontracted", function(frm) {
-	if (frm.doc.is_subcontracted) {
+	if (frm.doc.is_old_subcontracting_flow) {
 		erpnext.buying.get_default_bom(frm);
 	}
-	frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted);
+
+	frm.toggle_reqd("supplier_warehouse", frm.doc.is_old_subcontracting_flow);
 });
 
 frappe.ui.form.on('Purchase Receipt Item', {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 923ceb3..a70415d 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -133,7 +133,8 @@
   "transporter_name",
   "column_break5",
   "lr_no",
-  "lr_date"
+  "lr_date",
+  "is_old_subcontracting_flow"
  ],
  "fields": [
   {
@@ -442,7 +443,8 @@
    "label": "Is Subcontracted",
    "oldfieldname": "is_subcontracted",
    "oldfieldtype": "Select",
-   "print_hide": 1
+   "print_hide": 1,
+   "read_only": 1
   },
   {
    "depends_on": "eval:doc.is_subcontracted",
@@ -1142,13 +1144,21 @@
   {
    "fieldname": "dimension_col_break",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_old_subcontracting_flow",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Is Old Subcontracting Flow",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-truck",
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-05-27 15:59:18.550583",
+ "modified": "2022-06-15 15:43:40.664382",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 704e1fc..84da3cc 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -123,6 +123,7 @@
 		if getdate(self.posting_date) > getdate(nowdate()):
 			throw(_("Posting Date cannot be future date"))
 
+		self.get_current_stock()
 		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
 		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
@@ -234,7 +235,7 @@
 
 		self.make_gl_entries()
 		self.repost_future_sle_and_gle()
-		self.set_consumed_qty_in_po()
+		self.set_consumed_qty_in_subcontract_order()
 
 	def check_next_docstatus(self):
 		submit_rv = frappe.db.sql(
@@ -270,18 +271,7 @@
 		self.repost_future_sle_and_gle()
 		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
 		self.delete_auto_created_batches()
-		self.set_consumed_qty_in_po()
-
-	@frappe.whitelist()
-	def get_current_stock(self):
-		for d in self.get("supplied_items"):
-			if self.supplier_warehouse:
-				bin = frappe.db.sql(
-					"select actual_qty from `tabBin` where item_code = %s and warehouse = %s",
-					(d.rm_item_code, self.supplier_warehouse),
-					as_dict=1,
-				)
-				d.current_stock = bin and flt(bin[0]["actual_qty"]) or 0
+		self.set_consumed_qty_in_subcontract_order()
 
 	def get_gl_entries(self, warehouse_account=None):
 		from erpnext.accounts.general_ledger import process_gl_map
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index be4f274..d0d115d 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2,10 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 
 
-import json
-import unittest
-from collections import defaultdict
-
 import frappe
 from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, cint, cstr, flt, today
@@ -311,142 +307,6 @@
 		pr.cancel()
 		self.assertTrue(get_gl_entries("Purchase Receipt", pr.name))
 
-	def test_subcontracting(self):
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-
-		frappe.db.set_value(
-			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
-		)
-
-		make_stock_entry(
-			item_code="_Test Item", qty=100, target="_Test Warehouse 1 - _TC", basic_rate=100
-		)
-		make_stock_entry(
-			item_code="_Test Item Home Desktop 100",
-			qty=100,
-			target="_Test Warehouse 1 - _TC",
-			basic_rate=100,
-		)
-		pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted=1)
-		self.assertEqual(len(pr.get("supplied_items")), 2)
-
-		rm_supp_cost = sum(d.amount for d in pr.get("supplied_items"))
-		self.assertEqual(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
-
-		pr.cancel()
-
-	def test_subcontracting_gle_fg_item_rate_zero(self):
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-
-		frappe.db.set_value(
-			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
-		)
-
-		se1 = make_stock_entry(
-			item_code="_Test Item",
-			target="Work In Progress - TCP1",
-			qty=100,
-			basic_rate=100,
-			company="_Test Company with perpetual inventory",
-		)
-
-		se2 = make_stock_entry(
-			item_code="_Test Item Home Desktop 100",
-			target="Work In Progress - TCP1",
-			qty=100,
-			basic_rate=100,
-			company="_Test Company with perpetual inventory",
-		)
-
-		pr = make_purchase_receipt(
-			item_code="_Test FG Item",
-			qty=10,
-			rate=0,
-			is_subcontracted=1,
-			company="_Test Company with perpetual inventory",
-			warehouse="Stores - TCP1",
-			supplier_warehouse="Work In Progress - TCP1",
-		)
-
-		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
-
-		self.assertFalse(gl_entries)
-
-		pr.cancel()
-		se1.cancel()
-		se2.cancel()
-
-	def test_subcontracting_over_receipt(self):
-		"""
-		Behaviour: Raise multiple PRs against one PO that in total
-		        receive more than the required qty in the PO.
-		Expected Result: Error Raised for Over Receipt against PO.
-		"""
-		from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
-		from erpnext.buying.doctype.purchase_order.purchase_order import (
-			make_rm_stock_entry as make_subcontract_transfer_entry,
-		)
-		from erpnext.buying.doctype.purchase_order.test_purchase_order import (
-			create_purchase_order,
-			make_subcontracted_item,
-			update_backflush_based_on,
-		)
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-
-		update_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "_Test Subcontracted FG Item 1"
-		make_subcontracted_item(item_code=item_code)
-
-		po = create_purchase_order(
-			item_code=item_code,
-			qty=1,
-			include_exploded_items=0,
-			is_subcontracted=1,
-			supplier_warehouse="_Test Warehouse 1 - _TC",
-		)
-
-		# stock raw materials in a warehouse before transfer
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=10, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="_Test FG Item", qty=1, basic_rate=100
-		)
-		make_stock_entry(
-			target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=1, basic_rate=100
-		)
-
-		rm_items = [
-			{
-				"item_code": item_code,
-				"rm_item_code": po.supplied_items[0].rm_item_code,
-				"item_name": "_Test FG Item",
-				"qty": po.supplied_items[0].required_qty,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-			},
-			{
-				"item_code": item_code,
-				"rm_item_code": po.supplied_items[1].rm_item_code,
-				"item_name": "Test Extra Item 1",
-				"qty": po.supplied_items[1].required_qty,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-			},
-		]
-		rm_item_string = json.dumps(rm_items)
-		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
-		se.to_warehouse = "_Test Warehouse 1 - _TC"
-		se.save()
-		se.submit()
-
-		pr1 = make_purchase_receipt(po.name)
-		pr2 = make_purchase_receipt(po.name)
-
-		pr1.submit()
-		self.assertRaises(frappe.ValidationError, pr2.submit)
-		frappe.db.rollback()
-
 	def test_serial_no_supplier(self):
 		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
 		pr_row_1_serial_no = pr.get("items")[0].serial_no
@@ -1133,103 +993,6 @@
 		pr.cancel()
 		pr1.cancel()
 
-	def test_subcontracted_pr_for_multi_transfer_batches(self):
-		from erpnext.buying.doctype.purchase_order.purchase_order import (
-			make_purchase_receipt,
-			make_rm_stock_entry,
-		)
-		from erpnext.buying.doctype.purchase_order.test_purchase_order import (
-			create_purchase_order,
-			update_backflush_based_on,
-		)
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-
-		update_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "_Test Subcontracted FG Item 3"
-
-		make_item(
-			"Sub Contracted Raw Material 3",
-			{"is_stock_item": 1, "is_sub_contracted_item": 1, "has_batch_no": 1, "create_new_batch": 1},
-		)
-
-		create_subcontracted_item(
-			item_code=item_code, has_batch_no=1, raw_materials=["Sub Contracted Raw Material 3"]
-		)
-
-		order_qty = 500
-		po = create_purchase_order(
-			item_code=item_code,
-			qty=order_qty,
-			is_subcontracted=1,
-			supplier_warehouse="_Test Warehouse 1 - _TC",
-		)
-
-		ste1 = make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code="Sub Contracted Raw Material 3",
-			qty=300,
-			basic_rate=100,
-		)
-		ste2 = make_stock_entry(
-			target="_Test Warehouse - _TC",
-			item_code="Sub Contracted Raw Material 3",
-			qty=200,
-			basic_rate=100,
-		)
-
-		transferred_batch = {ste1.items[0].batch_no: 300, ste2.items[0].batch_no: 200}
-
-		rm_items = [
-			{
-				"item_code": item_code,
-				"rm_item_code": "Sub Contracted Raw Material 3",
-				"item_name": "_Test Item",
-				"qty": 300,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-				"name": po.supplied_items[0].name,
-			},
-			{
-				"item_code": item_code,
-				"rm_item_code": "Sub Contracted Raw Material 3",
-				"item_name": "_Test Item",
-				"qty": 200,
-				"warehouse": "_Test Warehouse - _TC",
-				"stock_uom": "Nos",
-				"name": po.supplied_items[0].name,
-			},
-		]
-
-		rm_item_string = json.dumps(rm_items)
-		se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
-		self.assertEqual(len(se.items), 2)
-		se.items[0].batch_no = ste1.items[0].batch_no
-		se.items[1].batch_no = ste2.items[0].batch_no
-		se.submit()
-
-		supplied_qty = frappe.db.get_value(
-			"Purchase Order Item Supplied",
-			{"parent": po.name, "rm_item_code": "Sub Contracted Raw Material 3"},
-			"supplied_qty",
-		)
-
-		self.assertEqual(supplied_qty, 500.00)
-
-		pr = make_purchase_receipt(po.name)
-		pr.save()
-		self.assertEqual(len(pr.supplied_items), 2)
-
-		for row in pr.supplied_items:
-			self.assertEqual(transferred_batch.get(row.batch_no), row.consumed_qty)
-
-		update_backflush_based_on("BOM")
-
-		pr.delete()
-		se.cancel()
-		ste2.cancel()
-		ste1.cancel()
-		po.cancel()
-
 	def test_po_to_pi_and_po_to_pr_worflow_full(self):
 		"""Test following behaviour:
 		- Create PO
@@ -1568,43 +1331,5 @@
 	return pr
 
 
-def create_subcontracted_item(**args):
-	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-
-	args = frappe._dict(args)
-
-	if not frappe.db.exists("Item", args.item_code):
-		make_item(
-			args.item_code,
-			{
-				"is_stock_item": 1,
-				"is_sub_contracted_item": 1,
-				"has_batch_no": args.get("has_batch_no") or 0,
-			},
-		)
-
-	if not args.raw_materials:
-		if not frappe.db.exists("Item", "Test Extra Item 1"):
-			make_item(
-				"Test Extra Item 1",
-				{
-					"is_stock_item": 1,
-				},
-			)
-
-		if not frappe.db.exists("Item", "Test Extra Item 2"):
-			make_item(
-				"Test Extra Item 2",
-				{
-					"is_stock_item": 1,
-				},
-			)
-
-		args.raw_materials = ["_Test FG Item", "Test Extra Item 1"]
-
-	if not frappe.db.get_value("BOM", {"item": args.item_code}, "name"):
-		make_bom(item=args.item_code, raw_materials=args.get("raw_materials"))
-
-
 test_dependencies = ["BOM", "Item Price", "Location"]
 test_records = frappe.get_test_records("Purchase Receipt")
diff --git a/erpnext/stock/doctype/purchase_receipt/test_records.json b/erpnext/stock/doctype/purchase_receipt/test_records.json
index 990ad12..e7ea9af 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_records.json
+++ b/erpnext/stock/doctype/purchase_receipt/test_records.json
@@ -83,37 +83,5 @@
    }
   ],
   "supplier": "_Test Supplier"
- },
-
- {
-  "buying_price_list": "_Test Price List",
-  "company": "_Test Company",
-  "conversion_rate": 1.0,
-  "currency": "INR",
-  "doctype": "Purchase Receipt",
-  "base_grand_total": 5000.0,
-  "is_subcontracted": 1,
-  "base_net_total": 5000.0,
-  "items": [
-   {
-    "base_amount": 5000.0,
-    "conversion_factor": 1.0,
-    "description": "_Test FG Item",
-    "doctype": "Purchase Receipt Item",
-    "item_code": "_Test FG Item",
-    "item_name": "_Test FG Item",
-    "parentfield": "items",
-    "qty": 10.0,
-    "rate": 500.0,
-    "received_qty": 10.0,
-    "rejected_qty": 0.0,
-    "stock_uom": "_Test UOM",
-    "uom": "_Test UOM",
-    "warehouse": "_Test Warehouse - _TC",
-	"cost_center": "Main - _TC"
-   }
-  ],
-  "supplier": "_Test Supplier",
-  "supplier_warehouse": "_Test Warehouse - _TC"
  }
 ]
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index b45d663..c97dbee 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -645,12 +645,15 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval:parent.is_old_subcontracting_flow",
    "fieldname": "bom",
    "fieldtype": "Link",
    "label": "BOM",
    "no_copy": 1,
    "options": "BOM",
-   "print_hide": 1
+   "print_hide": 1,
+   "read_only": 1,
+   "read_only_depends_on": "eval:!parent.is_old_subcontracting_flow"
   },
   {
    "default": "0",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 7101190..6042ed4 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -687,7 +687,10 @@
 
 		update_rejected_serial_nos = (
 			True
-			if (controller.doctype in ("Purchase Receipt", "Purchase Invoice") and d.rejected_qty)
+			if (
+				controller.doctype in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt")
+				and d.rejected_qty
+			)
 			else False
 		)
 		accepted_serial_nos_updated = False
@@ -700,7 +703,11 @@
 			qty = d.stock_qty
 		else:
 			warehouse = d.warehouse
-			qty = d.qty if controller.doctype == "Stock Reconciliation" else d.stock_qty
+			qty = (
+				d.qty
+				if controller.doctype in ["Stock Reconciliation", "Subcontracting Receipt"]
+				else d.stock_qty
+			)
 		for sle in stock_ledger_entries:
 			if sle.voucher_detail_no == d.name:
 				if (
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 540ad18..1c514a9 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -613,7 +613,25 @@
 
 	apply_putaway_rule: function (frm) {
 		if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm, frm.doc.purpose);
-	}
+	},
+
+	purchase_order: (frm) => {
+		if (frm.doc.purchase_order) {
+			frm.set_value("subcontracting_order", "");
+		}
+	},
+
+	subcontracting_order: (frm) => {
+		if (frm.doc.subcontracting_order) {
+			frm.set_value("purchase_order", "");
+			erpnext.utils.map_current_doc({
+				method: 'erpnext.stock.doctype.stock_entry.stock_entry.get_items_from_subcontracting_order',
+				source_name: frm.doc.subcontracting_order,
+				target_doc: frm,
+				freeze: true,
+			});
+		}
+	},
 });
 
 frappe.ui.form.on('Stock Entry Detail', {
@@ -780,7 +798,16 @@
 			return {
 				"filters": {
 					"docstatus": 1,
-					"is_subcontracted": 1,
+					"is_old_subcontracting_flow": 1,
+					"company": me.frm.doc.company
+				}
+			};
+		});
+
+		this.frm.set_query("subcontracting_order", function() {
+			return {
+				"filters": {
+					"docstatus": 1,
 					"company": me.frm.doc.company
 				}
 			};
@@ -801,7 +828,12 @@
 			}
 		}
 
-		this.frm.add_fetch("purchase_order", "supplier", "supplier");
+		if (me.frm.doc.purchase_order) {
+			this.frm.add_fetch("purchase_order", "supplier", "supplier");
+		}
+		else {
+			this.frm.add_fetch("subcontracting_order", "supplier", "supplier");
+		}
 
 		frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' }
 		this.frm.set_query("supplier_address", erpnext.queries.address_query)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index f56e059..abe98e2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -15,6 +15,7 @@
   "add_to_transit",
   "work_order",
   "purchase_order",
+  "subcontracting_order",
   "delivery_note_no",
   "sales_invoice_no",
   "pick_list",
@@ -147,13 +148,20 @@
    "search_index": 1
   },
   {
-   "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
-   "fieldname": "purchase_order",
-   "fieldtype": "Link",
-   "label": "Purchase Order",
-   "options": "Purchase Order"
+    "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
+    "fieldname": "purchase_order",
+    "fieldtype": "Link",
+    "label": "Purchase Order",
+    "options": "Purchase Order"
   },
   {
+    "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
+    "fieldname": "subcontracting_order",
+    "fieldtype": "Link",
+    "label": "Subcontracting Order",
+    "options": "Subcontracting Order"
+   },
+  {
    "depends_on": "eval:doc.purpose==\"Sales Return\"",
    "fieldname": "delivery_note_no",
    "fieldtype": "Link",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 46a1e70..9c49408 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -62,6 +62,27 @@
 
 
 class StockEntry(StockController):
+	def __init__(self, *args, **kwargs):
+		super(StockEntry, self).__init__(*args, **kwargs)
+		if self.purchase_order:
+			self.subcontract_data = frappe._dict(
+				{
+					"order_doctype": "Purchase Order",
+					"order_field": "purchase_order",
+					"rm_detail_field": "po_detail",
+					"order_supplied_items_field": "Purchase Order Item Supplied",
+				}
+			)
+		else:
+			self.subcontract_data = frappe._dict(
+				{
+					"order_doctype": "Subcontracting Order",
+					"order_field": "subcontracting_order",
+					"rm_detail_field": "sco_rm_detail",
+					"order_supplied_items_field": "Subcontracting Order Supplied Item",
+				}
+			)
+
 	def get_feed(self):
 		return self.stock_entry_type
 
@@ -134,8 +155,9 @@
 
 		update_serial_nos_after_submit(self, "items")
 		self.update_work_order()
-		self.validate_purchase_order()
-		self.update_purchase_order_supplied_items()
+		self.validate_subcontract_order()
+		self.update_subcontract_order_supplied_items()
+		self.update_subcontracting_order_status()
 
 		self.make_gl_entries()
 
@@ -154,7 +176,8 @@
 			self.set_material_request_transfer_status("Completed")
 
 	def on_cancel(self):
-		self.update_purchase_order_supplied_items()
+		self.update_subcontract_order_supplied_items()
+		self.update_subcontracting_order_status()
 
 		if self.work_order and self.purpose == "Material Consumption for Manufacture":
 			self.validate_work_order_status()
@@ -792,8 +815,8 @@
 
 					serial_nos.append(sn)
 
-	def validate_purchase_order(self):
-		"""Throw exception if more raw material is transferred against Purchase Order than in
+	def validate_subcontract_order(self):
+		"""Throw exception if more raw material is transferred against Subcontract Order than in
 		the raw materials supplied table"""
 		backflush_raw_materials_based_on = frappe.db.get_single_value(
 			"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
@@ -801,24 +824,29 @@
 
 		qty_allowance = flt(frappe.db.get_single_value("Buying Settings", "over_transfer_allowance"))
 
-		if not (self.purpose == "Send to Subcontractor" and self.purchase_order):
+		if not (self.purpose == "Send to Subcontractor" and self.get(self.subcontract_data.order_field)):
 			return
 
 		if backflush_raw_materials_based_on == "BOM":
-			purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
+			subcontract_order = frappe.get_doc(
+				self.subcontract_data.order_doctype, self.get(self.subcontract_data.order_field)
+			)
 			for se_item in self.items:
 				item_code = se_item.original_item or se_item.item_code
 				precision = cint(frappe.db.get_default("float_precision")) or 3
 				required_qty = sum(
-					[flt(d.required_qty) for d in purchase_order.supplied_items if d.rm_item_code == item_code]
+					[flt(d.required_qty) for d in subcontract_order.supplied_items if d.rm_item_code == item_code]
 				)
 
 				total_allowed = required_qty + (required_qty * (qty_allowance / 100))
 
 				if not required_qty:
 					bom_no = frappe.db.get_value(
-						"Purchase Order Item",
-						{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
+						f"{self.subcontract_data.order_doctype} Item",
+						{
+							"parent": self.get(self.subcontract_data.order_field),
+							"item_code": se_item.subcontracted_item,
+						},
 						"bom",
 					)
 
@@ -830,7 +858,7 @@
 						required_qty = sum(
 							[
 								flt(d.required_qty)
-								for d in purchase_order.supplied_items
+								for d in subcontract_order.supplied_items
 								if d.rm_item_code == original_item_code
 							]
 						)
@@ -839,26 +867,57 @@
 
 				if not required_qty:
 					frappe.throw(
-						_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}").format(
-							se_item.item_code, self.purchase_order
+						_("Item {0} not found in 'Raw Materials Supplied' table in {1} {2}").format(
+							se_item.item_code,
+							self.subcontract_data.order_doctype,
+							self.get(self.subcontract_data.order_field),
 						)
 					)
-				total_supplied = frappe.db.sql(
-					"""select sum(transfer_qty)
-					from `tabStock Entry Detail`, `tabStock Entry`
-					where `tabStock Entry`.purchase_order = %s
-						and `tabStock Entry`.docstatus = 1
-						and `tabStock Entry Detail`.item_code = %s
-						and `tabStock Entry Detail`.parent = `tabStock Entry`.name""",
-					(self.purchase_order, se_item.item_code),
-				)[0][0]
+
+				parent = frappe.qb.DocType("Stock Entry")
+				child = frappe.qb.DocType("Stock Entry Detail")
+
+				conditions = (
+					(parent.docstatus == 1)
+					& (child.item_code == se_item.item_code)
+					& (
+						(parent.purchase_order == self.purchase_order)
+						if self.subcontract_data.order_doctype == "Purchase Order"
+						else (parent.subcontracting_order == self.subcontracting_order)
+					)
+				)
+
+				total_supplied = (
+					frappe.qb.from_(parent)
+					.inner_join(child)
+					.on(parent.name == child.parent)
+					.select(Sum(child.transfer_qty))
+					.where(conditions)
+				).run()[0][0]
 
 				if flt(total_supplied, precision) > flt(total_allowed, precision):
 					frappe.throw(
-						_("Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3}").format(
-							se_item.idx, se_item.item_code, total_allowed, self.purchase_order
+						_("Row {0}# Item {1} cannot be transferred more than {2} against {3} {4}").format(
+							se_item.idx,
+							se_item.item_code,
+							total_allowed,
+							self.subcontract_data.order_doctype,
+							self.get(self.subcontract_data.order_field),
 						)
 					)
+				elif not se_item.get(self.subcontract_data.rm_detail_field):
+					filters = {
+						"parent": self.get(self.subcontract_data.order_field),
+						"docstatus": 1,
+						"rm_item_code": se_item.item_code,
+						"main_item_code": se_item.subcontracted_item,
+					}
+
+					order_rm_detail = frappe.db.get_value(
+						self.subcontract_data.order_supplied_items_field, filters, "name"
+					)
+					if order_rm_detail:
+						se_item.db_set(self.subcontract_data.rm_detail_field, order_rm_detail)
 		elif backflush_raw_materials_based_on == "Material Transferred for Subcontract":
 			for row in self.items:
 				if not row.subcontracted_item:
@@ -867,17 +926,19 @@
 							row.idx, frappe.bold(row.item_code)
 						)
 					)
-				elif not row.po_detail:
+				elif not row.get(self.subcontract_data.rm_detail_field):
 					filters = {
-						"parent": self.purchase_order,
+						"parent": self.get(self.subcontract_data.order_field),
 						"docstatus": 1,
 						"rm_item_code": row.item_code,
 						"main_item_code": row.subcontracted_item,
 					}
 
-					po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name")
-					if po_detail:
-						row.db_set("po_detail", po_detail)
+					order_rm_detail = frappe.db.get_value(
+						self.subcontract_data.order_supplied_items_field, filters, "name"
+					)
+					if order_rm_detail:
+						row.db_set(self.subcontract_data.rm_detail_field, order_rm_detail)
 
 	def validate_bom(self):
 		for d in self.get("items"):
@@ -1224,11 +1285,13 @@
 			args.batch_no = get_batch_no(args["item_code"], args["s_warehouse"], args["qty"])
 
 		if (
-			self.purpose == "Send to Subcontractor" and self.get("purchase_order") and args.get("item_code")
+			self.purpose == "Send to Subcontractor"
+			and self.get(self.subcontract_data.order_field)
+			and args.get("item_code")
 		):
 			subcontract_items = frappe.get_all(
-				"Purchase Order Item Supplied",
-				{"parent": self.purchase_order, "rm_item_code": args.get("item_code")},
+				self.subcontract_data.order_supplied_items_field,
+				{"parent": self.get(self.subcontract_data.order_field), "rm_item_code": args.get("item_code")},
 				"main_item_code",
 			)
 
@@ -1322,27 +1385,27 @@
 
 					item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
 
-					# Get PO Supplied Items Details
-					if self.purchase_order and self.purpose == "Send to Subcontractor":
-						# Get PO Supplied Items Details
-						item_wh = frappe._dict(
-							frappe.db.sql(
-								"""
-							SELECT
-								rm_item_code, reserve_warehouse
-							FROM
-								`tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
-							WHERE
-								po.name = poitemsup.parent and po.name = %s """,
-								self.purchase_order,
-							)
-						)
+					# Get Subcontract Order Supplied Items Details
+					if self.get(self.subcontract_data.order_field) and self.purpose == "Send to Subcontractor":
+						# Get Subcontract Order Supplied Items Details
+						parent = frappe.qb.DocType(self.subcontract_data.order_doctype)
+						child = frappe.qb.DocType(self.subcontract_data.order_supplied_items_field)
+
+						item_wh = (
+							frappe.qb.from_(parent)
+							.inner_join(child)
+							.on(parent.name == child.parent)
+							.select(child.rm_item_code, child.reserve_warehouse)
+							.where(parent.name == self.get(self.subcontract_data.order_field))
+						).run(as_list=True)
+
+						item_wh = frappe._dict(item_wh)
 
 					for item in item_dict.values():
 						if self.pro_doc and cint(self.pro_doc.from_wip_warehouse):
 							item["from_warehouse"] = self.pro_doc.wip_warehouse
-						# Get Reserve Warehouse from PO
-						if self.purchase_order and self.purpose == "Send to Subcontractor":
+						# Get Reserve Warehouse from Subcontract Order
+						if self.get(self.subcontract_data.order_field) and self.purpose == "Send to Subcontractor":
 							item["from_warehouse"] = item_wh.get(item.item_code)
 						item["to_warehouse"] = self.to_warehouse if self.purpose == "Send to Subcontractor" else ""
 
@@ -1478,7 +1541,9 @@
 			fetch_qty_in_stock_uom=False,
 		)
 
-		used_alternative_items = get_used_alternative_items(work_order=self.work_order)
+		used_alternative_items = get_used_alternative_items(
+			subcontract_order_field=self.subcontract_data.order_field, work_order=self.work_order
+		)
 		for item in item_dict.values():
 			# if source warehouse presents in BOM set from_warehouse as bom source_warehouse
 			if item["allow_alternative_item"]:
@@ -1844,7 +1909,7 @@
 			se_child.is_process_loss = item_row.get("is_process_loss", 0)
 
 			for field in [
-				"po_detail",
+				self.subcontract_data.rm_detail_field,
 				"original_item",
 				"expense_account",
 				"description",
@@ -1918,33 +1983,37 @@
 					else:
 						frappe.throw(_("Batch {0} of Item {1} is disabled.").format(item.batch_no, item.item_code))
 
-	def update_purchase_order_supplied_items(self):
-		if self.purchase_order and (
+	def update_subcontract_order_supplied_items(self):
+		if self.get(self.subcontract_data.order_field) and (
 			self.purpose in ["Send to Subcontractor", "Material Transfer"] or self.is_return
 		):
 
-			# Get PO Supplied Items Details
-			po_supplied_items = frappe.db.get_all(
-				"Purchase Order Item Supplied",
-				filters={"parent": self.purchase_order},
+			# Get Subcontract Order Supplied Items Details
+			order_supplied_items = frappe.db.get_all(
+				self.subcontract_data.order_supplied_items_field,
+				filters={"parent": self.get(self.subcontract_data.order_field)},
 				fields=["name", "rm_item_code", "reserve_warehouse"],
 			)
 
-			# Get Items Supplied in Stock Entries against PO
-			supplied_items = get_supplied_items(self.purchase_order)
+			# Get Items Supplied in Stock Entries against Subcontract Order
+			supplied_items = get_supplied_items(
+				self.get(self.subcontract_data.order_field),
+				self.subcontract_data.rm_detail_field,
+				self.subcontract_data.order_field,
+			)
 
-			for row in po_supplied_items:
+			for row in order_supplied_items:
 				key, item = row.name, {}
 				if not supplied_items.get(key):
-					# no stock transferred against PO Supplied Items row
+					# no stock transferred against Subcontract Order Supplied Items row
 					item = {"supplied_qty": 0, "returned_qty": 0, "total_supplied_qty": 0}
 				else:
 					item = supplied_items.get(key)
 
-				frappe.db.set_value("Purchase Order Item Supplied", row.name, item)
+				frappe.db.set_value(self.subcontract_data.order_supplied_items_field, row.name, item)
 
 			# RM Item-Reserve Warehouse Dict
-			item_wh = {x.get("rm_item_code"): x.get("reserve_warehouse") for x in po_supplied_items}
+			item_wh = {x.get("rm_item_code"): x.get("reserve_warehouse") for x in order_supplied_items}
 
 			for d in self.get("items"):
 				# Update reserved sub contracted quantity in bin based on Supplied Item Details and
@@ -2145,6 +2214,14 @@
 
 		return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos)))
 
+	def update_subcontracting_order_status(self):
+		if self.subcontracting_order and self.purpose == "Send to Subcontractor":
+			from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+				update_subcontracting_order_status,
+			)
+
+			update_subcontracting_order_status(self.subcontracting_order)
+
 	def set_missing_values(self):
 		"Updates rate and availability of all the items of mapped doc."
 		self.set_transfer_qty()
@@ -2293,13 +2370,13 @@
 	return operating_cost_per_unit
 
 
-def get_used_alternative_items(purchase_order=None, work_order=None):
+def get_used_alternative_items(
+	subcontract_order=None, subcontract_order_field="subcontracting_order", work_order=None
+):
 	cond = ""
 
-	if purchase_order:
-		cond = "and ste.purpose = 'Send to Subcontractor' and ste.purchase_order = '{0}'".format(
-			purchase_order
-		)
+	if subcontract_order:
+		cond = f"and ste.purpose = 'Send to Subcontractor' and ste.{subcontract_order_field} = '{subcontract_order}'"
 	elif work_order:
 		cond = "and ste.purpose = 'Material Transfer for Manufacture' and ste.work_order = '{0}'".format(
 			work_order
@@ -2352,7 +2429,6 @@
 @frappe.whitelist()
 def get_uom_details(item_code, uom, qty):
 	"""Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}`
-
 	:param args: dict with `item_code`, `uom` and `qty`"""
 	conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor")
 
@@ -2436,25 +2512,27 @@
 	return sample_quantity
 
 
-def get_supplied_items(purchase_order):
+def get_supplied_items(
+	subcontract_order, rm_detail_field="sco_rm_detail", subcontract_order_field="subcontracting_order"
+):
 	fields = [
 		"`tabStock Entry Detail`.`transfer_qty`",
 		"`tabStock Entry`.`is_return`",
-		"`tabStock Entry Detail`.`po_detail`",
+		f"`tabStock Entry Detail`.`{rm_detail_field}`",
 		"`tabStock Entry Detail`.`item_code`",
 	]
 
 	filters = [
 		["Stock Entry", "docstatus", "=", 1],
-		["Stock Entry", "purchase_order", "=", purchase_order],
+		["Stock Entry", subcontract_order_field, "=", subcontract_order],
 	]
 
 	supplied_item_details = {}
 	for row in frappe.get_all("Stock Entry", fields=fields, filters=filters):
-		if not row.po_detail:
+		if not row.get(rm_detail_field):
 			continue
 
-		key = row.po_detail
+		key = row.get(rm_detail_field)
 		if key not in supplied_item_details:
 			supplied_item_details.setdefault(
 				key, frappe._dict({"supplied_qty": 0, "returned_qty": 0, "total_supplied_qty": 0})
@@ -2474,6 +2552,39 @@
 	return supplied_item_details
 
 
+@frappe.whitelist()
+def get_items_from_subcontracting_order(source_name, target_doc=None):
+	sco = frappe.get_doc("Subcontracting Order", source_name)
+
+	if sco.docstatus == 1:
+		if target_doc and isinstance(target_doc, str):
+			target_doc = frappe.get_doc(json.loads(target_doc))
+
+		if target_doc.items:
+			target_doc.items = []
+
+		warehouses = {}
+		for item in sco.items:
+			warehouses[item.name] = item.warehouse
+
+		for item in sco.supplied_items:
+			target_doc.append(
+				"items",
+				{
+					"s_warehouse": warehouses.get(item.reference_name),
+					"t_warehouse": sco.supplier_warehouse,
+					"item_code": item.rm_item_code,
+					"qty": item.required_qty,
+					"transfer_qty": item.required_qty,
+					"uom": item.stock_uom,
+					"stock_uom": item.stock_uom,
+					"conversion_factor": 1,
+				},
+			)
+
+	return target_doc
+
+
 def get_available_materials(work_order) -> dict:
 	data = get_stock_entry_data(work_order)
 
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index d758c8a..5fe11a2 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -68,6 +68,7 @@
   "against_stock_entry",
   "ste_detail",
   "po_detail",
+  "sco_rm_detail",
   "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
@@ -497,6 +498,15 @@
    "read_only": 1
   },
   {
+    "fieldname": "sco_rm_detail",
+    "fieldtype": "Data",
+    "hidden": 1,
+    "label": "SCO Supplied Item",
+    "no_copy": 1,
+    "print_hide": 1,
+    "read_only": 1
+   },
+  {
    "default": "0",
    "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse",
    "fieldname": "set_basic_rate_manually",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index f669e90..1410da5 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -409,61 +409,6 @@
 		lcv.cancel()
 		pr.cancel()
 
-	def test_sub_contracted_item_costing(self):
-		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-
-		company = "_Test Company"
-		rm_item_code = "_Test Item for Reposting"
-		subcontracted_item = "_Test Subcontracted Item for Reposting"
-
-		frappe.db.set_value(
-			"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM"
-		)
-		make_bom(item=subcontracted_item, raw_materials=[rm_item_code], currency="INR")
-
-		# Purchase raw materials on supplier warehouse: Qty = 50, Rate = 100
-		pr = make_purchase_receipt(
-			company=company,
-			posting_date="2020-04-10",
-			warehouse="Stores - _TC",
-			item_code=rm_item_code,
-			qty=10,
-			rate=100,
-		)
-
-		# Purchase Receipt for subcontracted item
-		pr1 = make_purchase_receipt(
-			company=company,
-			posting_date="2020-04-20",
-			warehouse="Finished Goods - _TC",
-			supplier_warehouse="Stores - _TC",
-			item_code=subcontracted_item,
-			qty=10,
-			rate=20,
-			is_subcontracted=1,
-		)
-
-		self.assertEqual(pr1.items[0].valuation_rate, 120)
-
-		# Update raw material's valuation via LCV, Additional cost = 50
-		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
-
-		pr1.reload()
-		self.assertEqual(pr1.items[0].valuation_rate, 125)
-
-		# check outgoing_rate for DN after reposting
-		incoming_rate = frappe.db.get_value(
-			"Stock Ledger Entry",
-			{"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "item_code": subcontracted_item},
-			"incoming_rate",
-		)
-		self.assertEqual(incoming_rate, 125)
-
-		# cleanup data
-		pr1.cancel()
-		lcv.cancel()
-		pr.cancel()
-
 	def test_back_dated_entry_not_allowed(self):
 		# Back dated stock transactions are only allowed to stock managers
 		frappe.db.set_value(
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 38ad662..e83182f 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -238,8 +238,13 @@
 		throw(_("Item {0} is a template, please select one of its variants").format(item.name))
 
 	elif args.transaction_type == "buying" and args.doctype != "Material Request":
-		if args.get("is_subcontracted") and item.is_sub_contracted_item != 1:
-			throw(_("Item {0} must be a Sub-contracted Item").format(item.name))
+		if args.get("is_subcontracted"):
+			if args.get("is_old_subcontracting_flow"):
+				if item.is_sub_contracted_item != 1:
+					throw(_("Item {0} must be a Sub-contracted Item").format(item.name))
+			else:
+				if item.is_stock_item:
+					throw(_("Item {0} must be a Non-Stock Item").format(item.name))
 
 
 def get_basic_details(args, item, overwrite_warehouse=True):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 01c5aa9..b1842e7 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -630,6 +630,7 @@
 			"Purchase Invoice",
 			"Delivery Note",
 			"Sales Invoice",
+			"Subcontracting Receipt",
 		):
 			if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_return"):
 				from erpnext.controllers.sales_and_purchase_return import (
@@ -646,6 +647,8 @@
 			else:
 				if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
 					rate_field = "valuation_rate"
+				elif sle.voucher_type == "Subcontracting Receipt":
+					rate_field = "rate"
 				else:
 					rate_field = "incoming_rate"
 
@@ -659,6 +662,8 @@
 				else:
 					if sle.voucher_type in ("Delivery Note", "Sales Invoice"):
 						ref_doctype = "Packed Item"
+					elif sle == "Subcontracting Receipt":
+						ref_doctype = "Subcontracting Receipt Supplied Item"
 					else:
 						ref_doctype = "Purchase Receipt Item Supplied"
 
@@ -684,6 +689,8 @@
 				self.update_rate_on_delivery_and_sales_return(sle, outgoing_rate)
 			elif flt(sle.actual_qty) < 0 and sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
 				self.update_rate_on_purchase_receipt(sle, outgoing_rate)
+			elif flt(sle.actual_qty) < 0 and sle.voucher_type == "Subcontracting Receipt":
+				self.update_rate_on_subcontracting_receipt(sle, outgoing_rate)
 
 	def update_rate_on_stock_entry(self, sle, outgoing_rate):
 		frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate)
@@ -732,6 +739,14 @@
 			for d in doc.items + doc.supplied_items:
 				d.db_update()
 
+	def update_rate_on_subcontracting_receipt(self, sle, outgoing_rate):
+		if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no):
+			frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "rate", outgoing_rate)
+		else:
+			frappe.db.set_value(
+				"Subcontracting Receipt Supplied Item", sle.voucher_detail_no, "rate", outgoing_rate
+			)
+
 	def get_serialized_values(self, sle):
 		incoming_rate = flt(sle.incoming_rate)
 		actual_qty = flt(sle.actual_qty)
diff --git a/erpnext/subcontracting/__init__.py b/erpnext/subcontracting/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/__init__.py
diff --git a/erpnext/subcontracting/doctype/__init__.py b/erpnext/subcontracting/doctype/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/__init__.py b/erpnext/subcontracting/doctype/subcontracting_order/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
new file mode 100644
index 0000000..dbd337a
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
@@ -0,0 +1,328 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.provide('erpnext.buying');
+
+frappe.ui.form.on('Subcontracting Order', {
+	setup: (frm) => {
+		frm.get_field("items").grid.cannot_add_rows = true;
+		frm.get_field("items").grid.only_sortable();
+
+		frm.set_indicator_formatter('item_code',
+			(doc) => (doc.qty <= doc.received_qty) ? 'green' : 'orange');
+
+		frm.set_query('supplier_warehouse', () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0
+				}
+			};
+		});
+
+		frm.set_query('purchase_order', () => {
+			return {
+				filters: {
+					docstatus: 1,
+					is_subcontracted: 1,
+					is_old_subcontracting_flow: 0
+				}
+			};
+		});
+
+		frm.set_query('set_warehouse', () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0
+				}
+			};
+		});
+
+		frm.set_query('warehouse', 'items', () => ({
+			filters: {
+				company: frm.doc.company,
+				is_group: 0
+			}
+		}));
+
+		frm.set_query('expense_account', 'items', () => ({
+			query: 'erpnext.controllers.queries.get_expense_account',
+			filters: {
+				company: frm.doc.company
+			}
+		}));
+
+		frm.set_query('bom', 'items', (doc, cdt, cdn) => {
+			let d = locals[cdt][cdn];
+			return {
+				filters: {
+					item: d.item_code,
+					is_active: 1,
+					docstatus: 1,
+					company: frm.doc.company
+				}
+			};
+		});
+
+		frm.set_query('set_reserve_warehouse', () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					name: ['!=', frm.doc.supplier_warehouse],
+					is_group: 0
+				}
+			};
+		});
+	},
+
+	onload: (frm) => {
+		if (!frm.doc.transaction_date) {
+			frm.set_value('transaction_date', frappe.datetime.get_today());
+		}
+	},
+
+	purchase_order: (frm) => {
+		frm.set_value('service_items', null);
+		frm.set_value('items', null);
+		frm.set_value('supplied_items', null);
+
+		if (frm.doc.purchase_order) {
+			erpnext.utils.map_current_doc({
+				method: 'erpnext.buying.doctype.purchase_order.purchase_order.make_subcontracting_order',
+				source_name: frm.doc.purchase_order,
+				target_doc: frm,
+				freeze: true,
+				freeze_message: __('Mapping Subcontracting Order ...'),
+			});
+		}
+	},
+
+	refresh: function (frm) {
+		frm.trigger('get_materials_from_supplier');
+	},
+
+	get_materials_from_supplier: function (frm) {
+		let sco_rm_details = [];
+
+		if (frm.doc.supplied_items && (frm.doc.per_received == 100)) {
+			frm.doc.supplied_items.forEach(d => {
+				if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
+					sco_rm_details.push(d.name);
+				}
+			});
+		}
+
+		if (sco_rm_details && sco_rm_details.length) {
+			frm.add_custom_button(__('Return of Components'), () => {
+				frm.call({
+					method: 'erpnext.controllers.subcontracting_controller.get_materials_from_supplier',
+					freeze: true,
+					freeze_message: __('Creating Stock Entry'),
+					args: {
+						subcontract_order: frm.doc.name,
+						rm_details: sco_rm_details,
+						order_doctype: cur_frm.doc.doctype
+					},
+					callback: function (r) {
+						if (r && r.message) {
+							const doc = frappe.model.sync(r.message);
+							frappe.set_route("Form", doc[0].doctype, doc[0].name);
+						}
+					}
+				});
+			}, __('Create'));
+		}
+	}
+});
+
+erpnext.buying.SubcontractingOrderController = class SubcontractingOrderController {
+	setup() {
+		this.frm.custom_make_buttons = {
+			'Subcontracting Receipt': 'Subcontracting Receipt',
+			'Stock Entry': 'Material to Supplier',
+		};
+	}
+
+	refresh(doc) {
+		var me = this;
+
+		if (doc.docstatus == 1) {
+			if (doc.status != 'Completed') {
+				if (flt(doc.per_received) < 100) {
+					cur_frm.add_custom_button(__('Subcontracting Receipt'), this.make_subcontracting_receipt, __('Create'));
+					if (me.has_unsupplied_items()) {
+						cur_frm.add_custom_button(__('Material to Supplier'),
+							() => {
+								me.make_stock_entry();
+							}, __('Transfer'));
+					}
+				}
+				cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
+			}
+		}
+	}
+
+	items_add(doc, cdt, cdn) {
+		if (doc.set_warehouse) {
+			var row = frappe.get_doc(cdt, cdn);
+			row.warehouse = doc.set_warehouse;
+		}
+	}
+
+	set_warehouse(doc) {
+		this.set_warehouse_in_children(doc.items, "warehouse", doc.set_warehouse);
+	}
+
+	set_reserve_warehouse(doc) {
+		this.set_warehouse_in_children(doc.supplied_items, "reserve_warehouse", doc.set_reserve_warehouse);
+	}
+
+	set_warehouse_in_children(child_table, warehouse_field, warehouse) {
+		let transaction_controller = new erpnext.TransactionController();
+		transaction_controller.autofill_warehouse(child_table, warehouse_field, warehouse);
+	}
+
+	make_stock_entry() {
+		var items = $.map(cur_frm.doc.items, (d) => d.bom ? d.item_code : false);
+		var me = this;
+
+		if (items.length >= 1) {
+			me.raw_material_data = [];
+			me.show_dialog = 1;
+			let title = __('Transfer Material to Supplier');
+			let fields = [
+				{ fieldtype: 'Section Break', label: __('Raw Materials') },
+				{
+					fieldname: 'sub_con_rm_items', fieldtype: 'Table', label: __('Items'),
+					fields: [
+						{
+							fieldtype: 'Data',
+							fieldname: 'item_code',
+							label: __('Item'),
+							read_only: 1,
+							in_list_view: 1
+						},
+						{
+							fieldtype: 'Data',
+							fieldname: 'rm_item_code',
+							label: __('Raw Material'),
+							read_only: 1,
+							in_list_view: 1
+						},
+						{
+							fieldtype: 'Float',
+							read_only: 1,
+							fieldname: 'qty',
+							label: __('Quantity'),
+							in_list_view: 1
+						},
+						{
+							fieldtype: 'Data',
+							read_only: 1,
+							fieldname: 'warehouse',
+							label: __('Reserve Warehouse'),
+							in_list_view: 1
+						},
+						{
+							fieldtype: 'Float',
+							read_only: 1,
+							fieldname: 'rate',
+							label: __('Rate'),
+							hidden: 1
+						},
+						{
+							fieldtype: 'Float',
+							read_only: 1,
+							fieldname: 'amount',
+							label: __('Amount'),
+							hidden: 1
+						},
+						{
+							fieldtype: 'Link',
+							read_only: 1,
+							fieldname: 'uom',
+							label: __('UOM'),
+							hidden: 1
+						}
+					],
+					data: me.raw_material_data,
+					get_data: () => me.raw_material_data
+				}
+			];
+
+			me.dialog = new frappe.ui.Dialog({
+				title: title, fields: fields
+			});
+
+			if (me.frm.doc['supplied_items']) {
+				me.frm.doc['supplied_items'].forEach((item) => {
+					if (item.rm_item_code && item.main_item_code && item.required_qty - item.supplied_qty != 0) {
+						me.raw_material_data.push({
+							'name': item.name,
+							'item_code': item.main_item_code,
+							'rm_item_code': item.rm_item_code,
+							'item_name': item.rm_item_code,
+							'qty': item.required_qty - item.supplied_qty,
+							'warehouse': item.reserve_warehouse,
+							'rate': item.rate,
+							'amount': item.amount,
+							'stock_uom': item.stock_uom
+						});
+						me.dialog.fields_dict.sub_con_rm_items.grid.refresh();
+					}
+				});
+			}
+
+			me.dialog.get_field('sub_con_rm_items').check_all_rows();
+
+			me.dialog.show();
+			this.dialog.set_primary_action(__('Transfer'), () => {
+				me.values = me.dialog.get_values();
+				if (me.values) {
+					me.values.sub_con_rm_items.map((row, i) => {
+						if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) {
+							let row_id = i + 1;
+							frappe.throw(__('Item Code, warehouse and quantity are required on row {0}', [row_id]));
+						}
+					});
+					me.make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children());
+					me.dialog.hide();
+				}
+			});
+		}
+
+		me.dialog.get_close_btn().on('click', () => {
+			me.dialog.hide();
+		});
+	}
+
+	has_unsupplied_items() {
+		return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty);
+	}
+
+	make_subcontracting_receipt() {
+		frappe.model.open_mapped_doc({
+			method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt',
+			frm: cur_frm,
+			freeze_message: __('Creating Subcontracting Receipt ...')
+		});
+	}
+
+	make_rm_stock_entry(rm_items) {
+		frappe.call({
+			method: 'erpnext.controllers.subcontracting_controller.make_rm_stock_entry',
+			args: {
+				subcontract_order: cur_frm.doc.name,
+				rm_items: rm_items,
+				order_doctype: cur_frm.doc.doctype
+			},
+			callback: (r) => {
+				var doclist = frappe.model.sync(r.message);
+				frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
+			}
+		});
+	}
+};
+
+extend_cscript(cur_frm.cscript, new erpnext.buying.SubcontractingOrderController({ frm: cur_frm }));
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
new file mode 100644
index 0000000..c6e76c7
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
@@ -0,0 +1,485 @@
+{
+    "actions": [],
+    "allow_auto_repeat": 1,
+    "allow_import": 1,
+    "autoname": "naming_series:",
+    "creation": "2022-04-01 22:39:17.662819",
+    "doctype": "DocType",
+    "document_type": "Document",
+    "engine": "InnoDB",
+    "field_order": [
+        "title",
+        "naming_series",
+        "purchase_order",
+        "supplier",
+        "supplier_name",
+        "supplier_warehouse",
+        "column_break_7",
+        "company",
+        "transaction_date",
+        "schedule_date",
+        "amended_from",
+        "address_and_contact_section",
+        "supplier_address",
+        "address_display",
+        "contact_person",
+        "contact_display",
+        "contact_mobile",
+        "contact_email",
+        "column_break_19",
+        "shipping_address",
+        "shipping_address_display",
+        "billing_address",
+        "billing_address_display",
+        "section_break_24",
+        "column_break_25",
+        "set_warehouse",
+        "items",
+        "section_break_32",
+        "total_qty",
+        "column_break_29",
+        "total",
+        "service_items_section",
+        "service_items",
+        "raw_materials_supplied_section",
+        "set_reserve_warehouse",
+        "supplied_items",
+        "additional_costs_section",
+        "distribute_additional_costs_based_on",
+        "additional_costs",
+        "total_additional_costs",
+        "order_status_section",
+        "status",
+        "column_break_39",
+        "per_received",
+        "printing_settings_section",
+        "select_print_heading",
+        "column_break_43",
+        "letter_head"
+    ],
+    "fields": [
+        {
+            "allow_on_submit": 1,
+            "default": "{supplier_name}",
+            "fieldname": "title",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "label": "Title",
+            "no_copy": 1,
+            "print_hide": 1
+        },
+        {
+            "fieldname": "naming_series",
+            "fieldtype": "Select",
+            "label": "Series",
+            "no_copy": 1,
+            "options": "SC-ORD-.YYYY.-",
+            "print_hide": 1,
+            "reqd": 1,
+            "set_only_once": 1
+        },
+        {
+            "fieldname": "purchase_order",
+            "fieldtype": "Link",
+            "label": "Subcontracting Purchase Order",
+            "options": "Purchase Order",
+            "reqd": 1
+        },
+        {
+            "bold": 1,
+            "fieldname": "supplier",
+            "fieldtype": "Link",
+            "in_global_search": 1,
+            "in_standard_filter": 1,
+            "label": "Supplier",
+            "options": "Supplier",
+            "print_hide": 1,
+            "reqd": 1,
+            "search_index": 1
+        },
+        {
+            "bold": 1,
+            "fetch_from": "supplier.supplier_name",
+            "fieldname": "supplier_name",
+            "fieldtype": "Data",
+            "in_global_search": 1,
+            "label": "Supplier Name",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "depends_on": "supplier",
+            "fieldname": "supplier_warehouse",
+            "fieldtype": "Link",
+            "label": "Supplier Warehouse",
+            "options": "Warehouse",
+            "reqd": 1
+        },
+        {
+            "fieldname": "column_break_7",
+            "fieldtype": "Column Break",
+            "print_width": "50%",
+            "width": "50%"
+        },
+        {
+            "fieldname": "company",
+            "fieldtype": "Link",
+            "in_standard_filter": 1,
+            "label": "Company",
+            "options": "Company",
+            "print_hide": 1,
+            "remember_last_selected_value": 1,
+            "reqd": 1
+        },
+        {
+            "default": "Today",
+            "fetch_from": "purchase_order.transaction_date",
+            "fetch_if_empty": 1,
+            "fieldname": "transaction_date",
+            "fieldtype": "Date",
+            "in_list_view": 1,
+            "label": "Date",
+            "reqd": 1,
+            "search_index": 1
+        },
+        {
+            "allow_on_submit": 1,
+            "fetch_from": "purchase_order.schedule_date",
+            "fetch_if_empty": 1,
+            "fieldname": "schedule_date",
+            "fieldtype": "Date",
+            "label": "Required By",
+            "read_only": 1
+        },
+        {
+            "fieldname": "amended_from",
+            "fieldtype": "Link",
+            "ignore_user_permissions": 1,
+            "label": "Amended From",
+            "no_copy": 1,
+            "options": "Subcontracting Order",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "address_and_contact_section",
+            "fieldtype": "Section Break",
+            "label": "Address and Contact"
+        },
+        {
+            "fetch_from": "supplier.supplier_primary_address",
+            "fetch_if_empty": 1,
+            "fieldname": "supplier_address",
+            "fieldtype": "Link",
+            "label": "Supplier Address",
+            "options": "Address",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "address_display",
+            "fieldtype": "Small Text",
+            "label": "Supplier Address Details",
+            "read_only": 1
+        },
+        {
+            "fetch_from": "supplier.supplier_primary_contact",
+            "fetch_if_empty": 1,
+            "fieldname": "contact_person",
+            "fieldtype": "Link",
+            "label": "Supplier Contact",
+            "options": "Contact",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "contact_display",
+            "fieldtype": "Small Text",
+            "in_global_search": 1,
+            "label": "Contact Name",
+            "read_only": 1
+        },
+        {
+            "fieldname": "contact_mobile",
+            "fieldtype": "Small Text",
+            "label": "Contact Mobile No",
+            "read_only": 1
+        },
+        {
+            "fieldname": "contact_email",
+            "fieldtype": "Small Text",
+            "label": "Contact Email",
+            "options": "Email",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_19",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "shipping_address",
+            "fieldtype": "Link",
+            "label": "Company Shipping Address",
+            "options": "Address",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "shipping_address_display",
+            "fieldtype": "Small Text",
+            "label": "Shipping Address Details",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "billing_address",
+            "fieldtype": "Link",
+            "label": "Company Billing Address",
+            "options": "Address"
+        },
+        {
+            "fieldname": "billing_address_display",
+            "fieldtype": "Small Text",
+            "label": "Billing Address Details",
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_24",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "column_break_25",
+            "fieldtype": "Column Break"
+        },
+        {
+            "depends_on": "purchase_order",
+            "description": "Sets 'Warehouse' in each row of the Items table.",
+            "fieldname": "set_warehouse",
+            "fieldtype": "Link",
+            "label": "Set Target Warehouse",
+            "options": "Warehouse",
+            "print_hide": 1
+        },
+        {
+            "allow_bulk_edit": 1,
+            "depends_on": "purchase_order",
+            "fieldname": "items",
+            "fieldtype": "Table",
+            "label": "Items",
+            "options": "Subcontracting Order Item",
+            "reqd": 1
+        },
+        {
+            "fieldname": "section_break_32",
+            "fieldtype": "Section Break"
+        },
+        {
+            "depends_on": "purchase_order",
+            "fieldname": "total_qty",
+            "fieldtype": "Float",
+            "label": "Total Quantity",
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_29",
+            "fieldtype": "Column Break"
+        },
+        {
+            "depends_on": "purchase_order",
+            "fieldname": "total",
+            "fieldtype": "Currency",
+            "label": "Total",
+            "options": "currency",
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "depends_on": "purchase_order",
+            "fieldname": "service_items_section",
+            "fieldtype": "Section Break",
+            "label": "Service Items"
+        },
+        {
+            "fieldname": "service_items",
+            "fieldtype": "Table",
+            "label": "Service Items",
+            "options": "Subcontracting Order Service Item",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "collapsible": 1,
+            "collapsible_depends_on": "supplied_items",
+            "depends_on": "supplied_items",
+            "fieldname": "raw_materials_supplied_section",
+            "fieldtype": "Section Break",
+            "label": "Raw Materials Supplied"
+        },
+        {
+            "depends_on": "supplied_items",
+            "description": "Sets 'Reserve Warehouse' in each row of the Supplied Items table.",
+            "fieldname": "set_reserve_warehouse",
+            "fieldtype": "Link",
+            "label": "Set Reserve Warehouse",
+            "options": "Warehouse"
+        },
+        {
+            "fieldname": "supplied_items",
+            "fieldtype": "Table",
+            "label": "Supplied Items",
+            "no_copy": 1,
+            "options": "Subcontracting Order Supplied Item",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "collapsible_depends_on": "total_additional_costs",
+            "depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
+            "fieldname": "additional_costs_section",
+            "fieldtype": "Section Break",
+            "label": "Additional Costs"
+        },
+        {
+            "fieldname": "additional_costs",
+            "fieldtype": "Table",
+            "label": "Additional Costs",
+            "options": "Landed Cost Taxes and Charges"
+        },
+        {
+            "fieldname": "total_additional_costs",
+            "fieldtype": "Currency",
+            "label": "Total Additional Costs",
+            "print_hide_if_no_value": 1,
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "order_status_section",
+            "fieldtype": "Section Break",
+            "label": "Order Status"
+        },
+        {
+            "default": "Draft",
+            "fieldname": "status",
+            "fieldtype": "Select",
+            "in_standard_filter": 1,
+            "label": "Status",
+            "no_copy": 1,
+            "options": "Draft\nOpen\nPartially Received\nCompleted\nMaterial Transferred\nPartial Material Transferred\nCancelled",
+            "print_hide": 1,
+            "read_only": 1,
+            "reqd": 1,
+            "search_index": 1
+        },
+        {
+            "fieldname": "column_break_39",
+            "fieldtype": "Column Break"
+        },
+        {
+            "depends_on": "eval:!doc.__islocal",
+            "fieldname": "per_received",
+            "fieldtype": "Percent",
+            "in_list_view": 1,
+            "label": "% Received",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "printing_settings_section",
+            "fieldtype": "Section Break",
+            "label": "Printing Settings",
+            "print_hide": 1,
+            "print_width": "50%",
+            "width": "50%"
+        },
+        {
+            "allow_on_submit": 1,
+            "fieldname": "select_print_heading",
+            "fieldtype": "Link",
+            "label": "Print Heading",
+            "no_copy": 1,
+            "options": "Print Heading",
+            "print_hide": 1,
+            "report_hide": 1
+        },
+        {
+            "fieldname": "column_break_43",
+            "fieldtype": "Column Break"
+        },
+        {
+            "allow_on_submit": 1,
+            "fieldname": "letter_head",
+            "fieldtype": "Link",
+            "label": "Letter Head",
+            "options": "Letter Head",
+            "print_hide": 1
+        },
+        {
+            "default": "Qty",
+            "fieldname": "distribute_additional_costs_based_on",
+            "fieldtype": "Select",
+            "label": "Distribute Additional Costs Based On ",
+            "options": "Qty\nAmount"
+        }
+    ],
+    "icon": "fa fa-file-text",
+    "is_submittable": 1,
+    "links": [],
+    "modified": "2022-04-11 21:02:44.097841",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Order",
+    "naming_rule": "By \"Naming Series\" field",
+    "owner": "Administrator",
+    "permissions": [
+        {
+            "read": 1,
+            "report": 1,
+            "role": "Stock User"
+        },
+        {
+            "amend": 1,
+            "cancel": 1,
+            "create": 1,
+            "delete": 1,
+            "email": 1,
+            "print": 1,
+            "read": 1,
+            "report": 1,
+            "role": "Purchase Manager",
+            "share": 1,
+            "submit": 1,
+            "write": 1
+        },
+        {
+            "amend": 1,
+            "cancel": 1,
+            "create": 1,
+            "delete": 1,
+            "email": 1,
+            "print": 1,
+            "read": 1,
+            "report": 1,
+            "role": "Purchase User",
+            "share": 1,
+            "submit": 1,
+            "write": 1
+        },
+        {
+            "permlevel": 1,
+            "read": 1,
+            "role": "Purchase Manager",
+            "write": 1
+        }
+    ],
+    "search_fields": "status, transaction_date, supplier",
+    "show_name_in_global_search": 1,
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "states": [],
+    "timeline_field": "supplier",
+    "title_field": "supplier_name",
+    "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
new file mode 100644
index 0000000..71cdc94
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
@@ -0,0 +1,246 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import flt
+
+from erpnext.buying.doctype.purchase_order.purchase_order import is_subcontracting_order_created
+from erpnext.controllers.subcontracting_controller import SubcontractingController
+from erpnext.stock.stock_balance import get_ordered_qty, update_bin_qty
+from erpnext.stock.utils import get_bin
+
+
+class SubcontractingOrder(SubcontractingController):
+	def before_validate(self):
+		super(SubcontractingOrder, self).before_validate()
+
+	def validate(self):
+		super(SubcontractingOrder, self).validate()
+		self.validate_purchase_order_for_subcontracting()
+		self.validate_items()
+		self.validate_service_items()
+		self.validate_supplied_items()
+		self.set_missing_values()
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+
+	def on_submit(self):
+		self.update_ordered_qty_for_subcontracting()
+		self.update_reserved_qty_for_subcontracting()
+		self.update_status()
+
+	def on_cancel(self):
+		self.update_ordered_qty_for_subcontracting()
+		self.update_reserved_qty_for_subcontracting()
+		self.update_status()
+
+	def validate_purchase_order_for_subcontracting(self):
+		if self.purchase_order:
+			if is_subcontracting_order_created(self.purchase_order):
+				frappe.throw(
+					_(
+						"Only one Subcontracting Order can be created against a Purchase Order, cancel the existing Subcontracting Order to create a new one."
+					)
+				)
+
+			po = frappe.get_doc("Purchase Order", self.purchase_order)
+
+			if not po.is_subcontracted:
+				frappe.throw(_("Please select a valid Purchase Order that is configured for Subcontracting."))
+
+			if po.is_old_subcontracting_flow:
+				frappe.throw(_("Please select a valid Purchase Order that has Service Items."))
+
+			if po.docstatus != 1:
+				msg = f"Please submit Purchase Order {po.name} before proceeding."
+				frappe.throw(_(msg))
+
+			if po.per_received == 100:
+				msg = f"Cannot create more Subcontracting Orders against the Purchase Order {po.name}."
+				frappe.throw(_(msg))
+		else:
+			self.service_items = self.items = self.supplied_items = None
+			frappe.throw(_("Please select a Subcontracting Purchase Order."))
+
+	def validate_service_items(self):
+		for item in self.service_items:
+			if frappe.get_value("Item", item.item_code, "is_stock_item"):
+				msg = f"Service Item {item.item_name} must be a non-stock item."
+				frappe.throw(_(msg))
+
+	def validate_supplied_items(self):
+		if self.supplier_warehouse:
+			for item in self.supplied_items:
+				if self.supplier_warehouse == item.reserve_warehouse:
+					msg = f"Reserve Warehouse must be different from Supplier Warehouse for Supplied Item {item.main_item_code}."
+					frappe.throw(_(msg))
+
+	def set_missing_values(self):
+		self.set_missing_values_in_additional_costs()
+		self.set_missing_values_in_service_items()
+		self.set_missing_values_in_supplied_items()
+		self.set_missing_values_in_items()
+
+	def set_missing_values_in_additional_costs(self):
+		if self.get("additional_costs"):
+			self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
+
+			if self.total_additional_costs:
+				if self.distribute_additional_costs_based_on == "Amount":
+					total_amt = sum(flt(item.amount) for item in self.get("items"))
+					for item in self.items:
+						item.additional_cost_per_qty = (
+							(item.amount * self.total_additional_costs) / total_amt
+						) / item.qty
+				else:
+					total_qty = sum(flt(item.qty) for item in self.get("items"))
+					additional_cost_per_qty = self.total_additional_costs / total_qty
+					for item in self.items:
+						item.additional_cost_per_qty = additional_cost_per_qty
+		else:
+			self.total_additional_costs = 0
+
+	def set_missing_values_in_service_items(self):
+		for idx, item in enumerate(self.get("service_items")):
+			self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty
+
+	def set_missing_values_in_supplied_items(self):
+		for item in self.get("items"):
+			bom = frappe.get_doc("BOM", item.bom)
+			rm_cost = sum(flt(rm_item.amount) for rm_item in bom.items)
+			item.rm_cost_per_qty = rm_cost / flt(bom.quantity)
+
+	def set_missing_values_in_items(self):
+		total_qty = total = 0
+		for item in self.items:
+			item.rate = (
+				item.rm_cost_per_qty + item.service_cost_per_qty + (item.additional_cost_per_qty or 0)
+			)
+			item.amount = item.qty * item.rate
+			total_qty += flt(item.qty)
+			total += flt(item.amount)
+		else:
+			self.total_qty = total_qty
+			self.total = total
+
+	def update_ordered_qty_for_subcontracting(self, sco_item_rows=None):
+		item_wh_list = []
+		for item in self.get("items"):
+			if (
+				(not sco_item_rows or item.name in sco_item_rows)
+				and [item.item_code, item.warehouse] not in item_wh_list
+				and frappe.get_cached_value("Item", item.item_code, "is_stock_item")
+				and item.warehouse
+			):
+				item_wh_list.append([item.item_code, item.warehouse])
+		for item_code, warehouse in item_wh_list:
+			update_bin_qty(item_code, warehouse, {"ordered_qty": get_ordered_qty(item_code, warehouse)})
+
+	def update_reserved_qty_for_subcontracting(self):
+		for item in self.supplied_items:
+			if item.rm_item_code:
+				stock_bin = get_bin(item.rm_item_code, item.reserve_warehouse)
+				stock_bin.update_reserved_qty_for_sub_contracting()
+
+	def populate_items_table(self):
+		items = []
+
+		for si in self.service_items:
+			if si.fg_item:
+				item = frappe.get_doc("Item", si.fg_item)
+				bom = frappe.db.get_value("BOM", {"item": item.item_code, "is_active": 1, "is_default": 1})
+
+				items.append(
+					{
+						"item_code": item.item_code,
+						"item_name": item.item_name,
+						"schedule_date": self.schedule_date,
+						"description": item.description,
+						"qty": si.fg_item_qty,
+						"stock_uom": item.stock_uom,
+						"bom": bom,
+					},
+				)
+			else:
+				frappe.throw(
+					_("Please select Finished Good Item for Service Item {0}").format(
+						si.item_name or si.item_code
+					)
+				)
+		else:
+			for item in items:
+				self.append("items", item)
+			else:
+				self.set_missing_values()
+
+	def update_status(self, status=None, update_modified=False):
+		if self.docstatus >= 1 and not status:
+			if self.docstatus == 1:
+				if self.status == "Draft":
+					status = "Open"
+				elif self.per_received >= 100:
+					status = "Completed"
+				elif self.per_received > 0 and self.per_received < 100:
+					status = "Partially Received"
+				else:
+					total_required_qty = total_supplied_qty = 0
+					for item in self.supplied_items:
+						total_required_qty += item.required_qty
+						total_supplied_qty += item.supplied_qty or 0
+					if total_supplied_qty:
+						status = "Partial Material Transferred"
+						if total_supplied_qty >= total_required_qty:
+							status = "Material Transferred"
+					else:
+						status = "Open"
+			elif self.docstatus == 2:
+				status = "Cancelled"
+
+			frappe.db.set_value("Subcontracting Order", self.name, "status", status, update_modified)
+
+
+@frappe.whitelist()
+def make_subcontracting_receipt(source_name, target_doc=None):
+	return get_mapped_subcontracting_receipt(source_name, target_doc)
+
+
+def get_mapped_subcontracting_receipt(source_name, target_doc=None):
+	def update_item(obj, target, source_parent):
+		target.qty = flt(obj.qty) - flt(obj.received_qty)
+		target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate)
+
+	target_doc = get_mapped_doc(
+		"Subcontracting Order",
+		source_name,
+		{
+			"Subcontracting Order": {
+				"doctype": "Subcontracting Receipt",
+				"field_map": {"supplier_warehouse": "supplier_warehouse"},
+				"validation": {
+					"docstatus": ["=", 1],
+				},
+			},
+			"Subcontracting Order Item": {
+				"doctype": "Subcontracting Receipt Item",
+				"field_map": {
+					"name": "subcontracting_order_item",
+					"parent": "subcontracting_order",
+					"bom": "bom",
+				},
+				"postprocess": update_item,
+				"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty),
+			},
+		},
+		target_doc,
+	)
+
+	return target_doc
+
+
+@frappe.whitelist()
+def update_subcontracting_order_status(sco):
+	if isinstance(sco, str):
+		sco = frappe.get_doc("Subcontracting Order", sco)
+
+	sco.update_status()
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_dashboard.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_dashboard.py
new file mode 100644
index 0000000..f17d8cd
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_dashboard.py
@@ -0,0 +1,8 @@
+from frappe import _
+
+
+def get_data():
+	return {
+		"fieldname": "subcontracting_order",
+		"transactions": [{"label": _("Reference"), "items": ["Subcontracting Receipt", "Stock Entry"]}],
+	}
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js
new file mode 100644
index 0000000..650419c
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.listview_settings['Subcontracting Order'] = {
+	get_indicator: function (doc) {
+		const status_colors = {
+			"Draft": "grey",
+			"Open": "orange",
+			"Partially Received": "yellow",
+			"Completed": "green",
+			"Partial Material Transferred": "purple",
+			"Material Transferred": "blue",
+		};
+		return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
+	},
+};
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
new file mode 100644
index 0000000..94bb38e
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
@@ -0,0 +1,536 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import copy
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+
+from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_subcontracting_order
+from erpnext.controllers.subcontracting_controller import make_rm_stock_entry
+from erpnext.controllers.tests.test_subcontracting_controller import (
+	get_rm_items,
+	get_subcontracting_order,
+	make_bom_for_subcontracted_items,
+	make_raw_materials,
+	make_service_items,
+	make_stock_in_entry,
+	make_stock_transfer_entry,
+	make_subcontracted_item,
+	make_subcontracted_items,
+	set_backflush_based_on,
+)
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+	make_subcontracting_receipt,
+)
+
+
+class TestSubcontractingOrder(FrappeTestCase):
+	def setUp(self):
+		make_subcontracted_items()
+		make_raw_materials()
+		make_service_items()
+		make_bom_for_subcontracted_items()
+
+	def test_populate_items_table(self):
+		sco = get_subcontracting_order()
+		sco.items = None
+		sco.populate_items_table()
+		self.assertEqual(len(sco.service_items), len(sco.items))
+
+	def test_set_missing_values(self):
+		sco = get_subcontracting_order()
+		before = {sco.total_qty, sco.total, sco.total_additional_costs}
+		sco.total_qty = sco.total = sco.total_additional_costs = 0
+		sco.set_missing_values()
+		after = {sco.total_qty, sco.total, sco.total_additional_costs}
+		self.assertSetEqual(before, after)
+
+	def test_update_status(self):
+		# Draft
+		sco = get_subcontracting_order(do_not_submit=1)
+		self.assertEqual(sco.status, "Draft")
+
+		# Open
+		sco.submit()
+		sco.load_from_db()
+		self.assertEqual(sco.status, "Open")
+
+		# Partial Material Transferred
+		rm_items = get_rm_items(sco.supplied_items)
+		rm_items[0]["qty"] -= 1
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		sco.load_from_db()
+		self.assertEqual(sco.status, "Partial Material Transferred")
+
+		# Material Transferred
+		rm_items[0]["qty"] = 1
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		sco.load_from_db()
+		self.assertEqual(sco.status, "Material Transferred")
+
+		# Partially Received
+		scr = make_subcontracting_receipt(sco.name)
+		scr.items[0].qty -= 1
+		scr.save()
+		scr.submit()
+		sco.load_from_db()
+		self.assertEqual(sco.status, "Partially Received")
+
+		# Completed
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		scr.submit()
+		sco.load_from_db()
+		self.assertEqual(sco.status, "Completed")
+
+		# Partially Received (scr cancelled)
+		scr.load_from_db()
+		scr.cancel()
+		sco.load_from_db()
+		self.assertEqual(sco.status, "Partially Received")
+
+	def test_make_rm_stock_entry(self):
+		sco = get_subcontracting_order()
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		ste = make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		self.assertEqual(len(ste.items), len(rm_items))
+
+	def test_make_rm_stock_entry_for_serial_items(self):
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 2",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA2",
+				"fg_item_qty": 5,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 5",
+				"qty": 6,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA5",
+				"fg_item_qty": 6,
+			},
+		]
+
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		ste = make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		self.assertEqual(len(ste.items), len(rm_items))
+
+	def test_make_rm_stock_entry_for_batch_items(self):
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 4",
+				"qty": 5,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA4",
+				"fg_item_qty": 5,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 6",
+				"qty": 6,
+				"rate": 100,
+				"fg_item": "Subcontracted Item SA6",
+				"fg_item_qty": 6,
+			},
+		]
+
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		ste = make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		self.assertEqual(len(ste.items), len(rm_items))
+
+	def test_update_reserved_qty_for_subcontracting(self):
+		# Make stock available for raw materials
+		make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=30, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC",
+			item_code="_Test Item Home Desktop 100",
+			qty=30,
+			basic_rate=100,
+		)
+
+		bin1 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"],
+			as_dict=1,
+		)
+
+		# Create SCO
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "_Test FG Item",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+
+		bin2 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"],
+			as_dict=1,
+		)
+
+		self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
+		self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10)
+		self.assertNotEqual(bin1.modified, bin2.modified)
+
+		# Create stock transfer
+		rm_items = [
+			{
+				"item_code": "_Test FG Item",
+				"rm_item_code": "_Test Item",
+				"item_name": "_Test Item",
+				"qty": 6,
+				"warehouse": "_Test Warehouse - _TC",
+				"rate": 100,
+				"amount": 600,
+				"stock_uom": "Nos",
+			}
+		]
+		ste = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items))
+		ste.to_warehouse = "_Test Warehouse 1 - _TC"
+		ste.save()
+		ste.submit()
+
+		bin3 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
+
+		self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=40, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse 1 - _TC",
+			item_code="_Test Item Home Desktop 100",
+			qty=40,
+			basic_rate=100,
+		)
+
+		# Make SCR against the SCO
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		scr.submit()
+
+		bin4 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
+
+		self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
+
+		# Cancel SCR
+		scr.reload()
+		scr.cancel()
+		bin5 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
+
+		self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+
+		# Cancel Stock Entry
+		ste.cancel()
+		bin6 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
+
+		self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
+
+		# Cancel PO
+		sco.reload()
+		sco.cancel()
+		bin7 = frappe.db.get_value(
+			"Bin",
+			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
+			fieldname="reserved_qty_for_sub_contract",
+			as_dict=1,
+		)
+
+		self.assertEqual(bin7.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
+
+	def test_exploded_items(self):
+		item_code = "_Test Subcontracted FG Item 11"
+		make_subcontracted_item(item_code=item_code)
+
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 1,
+				"rate": 100,
+				"fg_item": item_code,
+				"fg_item_qty": 1,
+			},
+		]
+
+		sco1 = get_subcontracting_order(service_items=service_items, include_exploded_items=1)
+		item_name = frappe.db.get_value("BOM", {"item": item_code}, "name")
+		bom = frappe.get_doc("BOM", item_name)
+		exploded_items = sorted([item.item_code for item in bom.exploded_items])
+		supplied_items = sorted([item.rm_item_code for item in sco1.supplied_items])
+		self.assertEqual(exploded_items, supplied_items)
+
+		sco2 = get_subcontracting_order(service_items=service_items, include_exploded_items=0)
+		supplied_items1 = sorted([item.rm_item_code for item in sco2.supplied_items])
+		bom_items = sorted([item.item_code for item in bom.items])
+		self.assertEqual(supplied_items1, bom_items)
+
+	def test_backflush_based_on_stock_entry(self):
+		item_code = "_Test Subcontracted FG Item 1"
+		make_subcontracted_item(item_code=item_code)
+		make_item("Sub Contracted Raw Material 1", {"is_stock_item": 1, "is_sub_contracted_item": 1})
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+
+		order_qty = 5
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": order_qty,
+				"rate": 100,
+				"fg_item": item_code,
+				"fg_item_qty": order_qty,
+			},
+		]
+
+		sco = get_subcontracting_order(service_items=service_items)
+
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=100, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=10, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 1",
+			qty=10,
+			basic_rate=100,
+		)
+
+		rm_items = [
+			{
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 1",
+				"item_name": "_Test Item",
+				"qty": 10,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "_Test Item Home Desktop 100",
+				"item_name": "_Test Item Home Desktop 100",
+				"qty": 20,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "Test Extra Item 1",
+				"item_name": "Test Extra Item 1",
+				"qty": 10,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "Test Extra Item 2",
+				"stock_uom": "Nos",
+				"qty": 10,
+				"warehouse": "_Test Warehouse - _TC",
+				"item_name": "Test Extra Item 2",
+			},
+		]
+
+		ste = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items))
+		ste.submit()
+
+		scr = make_subcontracting_receipt(sco.name)
+		received_qty = 2
+
+		# partial receipt
+		scr.get("items")[0].qty = received_qty
+		scr.save()
+		scr.submit()
+
+		transferred_items = sorted(
+			[item.item_code for item in ste.get("items") if ste.subcontracting_order == sco.name]
+		)
+		issued_items = sorted([item.rm_item_code for item in scr.get("supplied_items")])
+
+		self.assertEqual(transferred_items, issued_items)
+		self.assertEqual(scr.get_supplied_items_cost(scr.get("items")[0].name), 2000)
+
+		transferred_rm_map = frappe._dict()
+		for item in rm_items:
+			transferred_rm_map[item.get("rm_item_code")] = item
+
+		set_backflush_based_on("BOM")
+
+	def test_supplied_qty(self):
+		item_code = "_Test Subcontracted FG Item 5"
+		make_item("Sub Contracted Raw Material 4", {"is_stock_item": 1, "is_sub_contracted_item": 1})
+
+		make_subcontracted_item(item_code=item_code, raw_materials=["Sub Contracted Raw Material 4"])
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+
+		order_qty = 250
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": order_qty,
+				"rate": 100,
+				"fg_item": item_code,
+				"fg_item_qty": order_qty,
+			},
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": order_qty,
+				"rate": 100,
+				"fg_item": item_code,
+				"fg_item_qty": order_qty,
+			},
+		]
+
+		sco = get_subcontracting_order(service_items=service_items)
+
+		# Material receipt entry for the raw materials which will be send to supplier
+		make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 4",
+			qty=500,
+			basic_rate=100,
+		)
+
+		rm_items = [
+			{
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 4",
+				"item_name": "_Test Item",
+				"qty": 250,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+				"name": sco.supplied_items[0].name,
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 4",
+				"item_name": "_Test Item",
+				"qty": 250,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+		]
+
+		# Raw Materials transfer entry from stores to supplier's warehouse
+		ste = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items))
+		ste.submit()
+
+		# Test sco_rm_detail field has value or not
+		for item_row in ste.items:
+			self.assertEqual(item_row.sco_rm_detail, sco.supplied_items[item_row.idx - 1].name)
+
+		sco.load_from_db()
+		for row in sco.supplied_items:
+			# Valid that whether transferred quantity is matching with supplied qty or not in the subcontracting order
+			self.assertEqual(row.supplied_qty, 250.0)
+
+		set_backflush_based_on("BOM")
+
+
+def create_subcontracting_order(**args):
+	args = frappe._dict(args)
+	sco = get_mapped_subcontracting_order(source_name=args.po_name)
+
+	for item in sco.items:
+		item.include_exploded_items = args.get("include_exploded_items", 1)
+
+	if args.get("warehouse"):
+		for item in sco.items:
+			item.warehouse = args.warehouse
+	else:
+		warehouse = frappe.get_value("Purchase Order", args.po_name, "set_warehouse")
+		if warehouse:
+			for item in sco.items:
+				item.warehouse = warehouse
+		else:
+			po = frappe.get_doc("Purchase Order", args.po_name)
+			warehouses = []
+			for item in po.items:
+				warehouses.append(item.warehouse)
+			else:
+				for idx, val in enumerate(sco.items):
+					val.warehouse = warehouses[idx]
+
+	if not args.do_not_save:
+		sco.insert()
+		if not args.do_not_submit:
+			sco.submit()
+
+	return sco
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_order_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_item/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
new file mode 100644
index 0000000..291f47a
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
@@ -0,0 +1,326 @@
+{
+    "actions": [],
+    "autoname": "hash",
+    "creation": "2022-04-01 19:26:31.475015",
+    "doctype": "DocType",
+    "document_type": "Document",
+    "editable_grid": 1,
+    "engine": "InnoDB",
+    "field_order": [
+        "item_code",
+        "item_name",
+        "bom",
+        "include_exploded_items",
+        "column_break_3",
+        "schedule_date",
+        "expected_delivery_date",
+        "description_section",
+        "description",
+        "column_break_8",
+        "image",
+        "image_view",
+        "quantity_and_rate_section",
+        "qty",
+        "received_qty",
+        "returned_qty",
+        "column_break_13",
+        "stock_uom",
+        "conversion_factor",
+        "section_break_16",
+        "rate",
+        "amount",
+        "column_break_19",
+        "rm_cost_per_qty",
+        "service_cost_per_qty",
+        "additional_cost_per_qty",
+        "warehouse_section",
+        "warehouse",
+        "accounting_details_section",
+        "expense_account",
+        "manufacture_section",
+        "manufacturer",
+        "manufacturer_part_no",
+        "section_break_34",
+        "page_break"
+    ],
+    "fields": [
+        {
+            "bold": 1,
+            "columns": 2,
+            "fieldname": "item_code",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Item Code",
+            "options": "Item",
+            "read_only": 1,
+            "reqd": 1,
+            "search_index": 1
+        },
+        {
+            "fetch_from": "item_code.item_name",
+            "fetch_if_empty": 1,
+            "fieldname": "item_name",
+            "fieldtype": "Data",
+            "in_global_search": 1,
+            "label": "Item Name",
+            "print_hide": 1,
+            "reqd": 1
+        },
+        {
+            "fieldname": "column_break_3",
+            "fieldtype": "Column Break"
+        },
+        {
+            "bold": 1,
+            "columns": 2,
+            "fieldname": "schedule_date",
+            "fieldtype": "Date",
+            "label": "Required By",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "allow_on_submit": 1,
+            "bold": 1,
+            "fieldname": "expected_delivery_date",
+            "fieldtype": "Date",
+            "label": "Expected Delivery Date",
+            "search_index": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "description_section",
+            "fieldtype": "Section Break",
+            "label": "Description"
+        },
+        {
+            "fetch_from": "item_code.description",
+            "fetch_if_empty": 1,
+            "fieldname": "description",
+            "fieldtype": "Text Editor",
+            "label": "Description",
+            "print_width": "300px",
+            "reqd": 1,
+            "width": "300px"
+        },
+        {
+            "fieldname": "column_break_8",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "image",
+            "fieldtype": "Attach",
+            "hidden": 1,
+            "label": "Image"
+        },
+        {
+            "fieldname": "image_view",
+            "fieldtype": "Image",
+            "label": "Image View",
+            "options": "image",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "quantity_and_rate_section",
+            "fieldtype": "Section Break",
+            "label": "Quantity and Rate"
+        },
+        {
+            "bold": 1,
+            "columns": 1,
+            "default": "1",
+            "fieldname": "qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Quantity",
+            "print_width": "60px",
+            "read_only": 1,
+            "reqd": 1,
+            "width": "60px"
+        },
+        {
+            "fieldname": "column_break_13",
+            "fieldtype": "Column Break",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "stock_uom",
+            "fieldtype": "Link",
+            "label": "Stock UOM",
+            "options": "UOM",
+            "print_width": "100px",
+            "read_only": 1,
+            "reqd": 1,
+            "width": "100px"
+        },
+        {
+            "default": "1",
+            "fieldname": "conversion_factor",
+            "fieldtype": "Float",
+            "hidden": 1,
+            "label": "Conversion Factor",
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_16",
+            "fieldtype": "Section Break"
+        },
+        {
+            "bold": 1,
+            "columns": 2,
+            "fetch_from": "item_code.standard_rate",
+            "fetch_if_empty": 1,
+            "fieldname": "rate",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Rate",
+            "options": "currency",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "fieldname": "column_break_19",
+            "fieldtype": "Column Break"
+        },
+        {
+            "columns": 2,
+            "fieldname": "amount",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Amount",
+            "options": "currency",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "fieldname": "warehouse_section",
+            "fieldtype": "Section Break",
+            "label": "Warehouse Details"
+        },
+        {
+            "fieldname": "warehouse",
+            "fieldtype": "Link",
+            "label": "Warehouse",
+            "options": "Warehouse",
+            "print_hide": 1,
+            "reqd": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "accounting_details_section",
+            "fieldtype": "Section Break",
+            "label": "Accounting Details"
+        },
+        {
+            "fieldname": "expense_account",
+            "fieldtype": "Link",
+            "label": "Expense Account",
+            "options": "Account",
+            "print_hide": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "manufacture_section",
+            "fieldtype": "Section Break",
+            "label": "Manufacture"
+        },
+        {
+            "fieldname": "manufacturer",
+            "fieldtype": "Link",
+            "label": "Manufacturer",
+            "options": "Manufacturer"
+        },
+        {
+            "fieldname": "manufacturer_part_no",
+            "fieldtype": "Data",
+            "label": "Manufacturer Part Number"
+        },
+        {
+            "depends_on": "item_code",
+            "fetch_from": "item_code.default_bom",
+            "fieldname": "bom",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "BOM",
+            "options": "BOM",
+            "print_hide": 1,
+            "reqd": 1
+        },
+        {
+            "default": "0",
+            "fieldname": "include_exploded_items",
+            "fieldtype": "Check",
+            "label": "Include Exploded Items",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "service_cost_per_qty",
+            "fieldtype": "Currency",
+            "label": "Service Cost Per Qty",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "default": "0",
+            "fieldname": "additional_cost_per_qty",
+            "fieldtype": "Currency",
+            "label": "Additional Cost Per Qty",
+            "read_only": 1
+        },
+        {
+            "fieldname": "rm_cost_per_qty",
+            "fieldtype": "Currency",
+            "label": "Raw Material Cost Per Qty",
+            "no_copy": 1,
+            "read_only": 1
+        },
+        {
+            "allow_on_submit": 1,
+            "default": "0",
+            "fieldname": "page_break",
+            "fieldtype": "Check",
+            "label": "Page Break",
+            "no_copy": 1,
+            "print_hide": 1
+        },
+        {
+            "fieldname": "section_break_34",
+            "fieldtype": "Section Break"
+        },
+        {
+            "depends_on": "received_qty",
+            "fieldname": "received_qty",
+            "fieldtype": "Float",
+            "label": "Received Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "depends_on": "returned_qty",
+            "fieldname": "returned_qty",
+            "fieldtype": "Float",
+            "label": "Returned Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        }
+    ],
+    "idx": 1,
+    "index_web_pages_for_search": 1,
+    "istable": 1,
+    "links": [],
+    "modified": "2022-04-11 21:28:06.585338",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Order Item",
+    "naming_rule": "Random",
+    "owner": "Administrator",
+    "permissions": [],
+    "quick_entry": 1,
+    "search_fields": "item_name",
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "states": [],
+    "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py
new file mode 100644
index 0000000..174f5b2
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SubcontractingOrderItem(Document):
+	pass
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_service_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_order_service_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_service_item/__init__.py
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
new file mode 100644
index 0000000..f213313
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json
@@ -0,0 +1,131 @@
+{
+    "actions": [],
+    "autoname": "hash",
+    "creation": "2022-04-01 19:23:05.728354",
+    "doctype": "DocType",
+    "editable_grid": 1,
+    "engine": "InnoDB",
+    "field_order": [
+        "item_code",
+        "column_break_2",
+        "item_name",
+        "section_break_4",
+        "qty",
+        "column_break_6",
+        "rate",
+        "column_break_8",
+        "amount",
+        "section_break_10",
+        "fg_item",
+        "column_break_12",
+        "fg_item_qty"
+    ],
+    "fields": [
+        {
+            "bold": 1,
+            "columns": 2,
+            "fieldname": "item_code",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Item Code",
+            "options": "Item",
+            "reqd": 1,
+            "search_index": 1
+        },
+        {
+            "fetch_from": "item_code.item_name",
+            "fieldname": "item_name",
+            "fieldtype": "Data",
+            "in_global_search": 1,
+            "in_list_view": 1,
+            "label": "Item Name",
+            "print_hide": 1,
+            "reqd": 1
+        },
+        {
+            "bold": 1,
+            "columns": 1,
+            "fieldname": "qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Quantity",
+            "print_width": "60px",
+            "reqd": 1,
+            "width": "60px"
+        },
+        {
+            "bold": 1,
+            "columns": 2,
+            "fetch_from": "item_code.standard_rate",
+            "fetch_if_empty": 1,
+            "fieldname": "rate",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Rate",
+            "options": "currency",
+            "reqd": 1
+        },
+        {
+            "columns": 2,
+            "fieldname": "amount",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Amount",
+            "options": "currency",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "fieldname": "fg_item",
+            "fieldtype": "Link",
+            "label": "Finished Good Item",
+            "options": "Item",
+            "reqd": 1
+        },
+        {
+            "default": "1",
+            "fieldname": "fg_item_qty",
+            "fieldtype": "Float",
+            "label": "Finished Good Item Quantity",
+            "reqd": 1
+        },
+        {
+            "fieldname": "column_break_2",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "section_break_4",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "column_break_6",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "column_break_8",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "section_break_10",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "column_break_12",
+            "fieldtype": "Column Break"
+        }
+    ],
+    "istable": 1,
+    "links": [],
+    "modified": "2022-04-07 11:43:43.094867",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Order Service Item",
+    "naming_rule": "Random",
+    "owner": "Administrator",
+    "permissions": [],
+    "quick_entry": 1,
+    "search_fields": "item_name",
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "states": []
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.py b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.py
new file mode 100644
index 0000000..ad6289d
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SubcontractingOrderServiceItem(Document):
+	pass
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json
new file mode 100644
index 0000000..a206a21
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.json
@@ -0,0 +1,178 @@
+{
+    "actions": [],
+    "creation": "2022-04-01 19:29:30.923800",
+    "doctype": "DocType",
+    "editable_grid": 1,
+    "engine": "InnoDB",
+    "field_order": [
+        "main_item_code",
+        "rm_item_code",
+        "column_break_3",
+        "stock_uom",
+        "conversion_factor",
+        "reserve_warehouse",
+        "column_break_6",
+        "bom_detail_no",
+        "reference_name",
+        "section_break_9",
+        "rate",
+        "column_break_11",
+        "amount",
+        "section_break_13",
+        "required_qty",
+        "supplied_qty",
+        "column_break_16",
+        "consumed_qty",
+        "returned_qty",
+        "total_supplied_qty"
+    ],
+    "fields": [
+        {
+            "columns": 2,
+            "fieldname": "main_item_code",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Item Code",
+            "options": "Item",
+            "read_only": 1
+        },
+        {
+            "columns": 2,
+            "fieldname": "rm_item_code",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Raw Material Item Code",
+            "options": "Item",
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_3",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "stock_uom",
+            "fieldtype": "Link",
+            "label": "Stock Uom",
+            "options": "UOM",
+            "read_only": 1
+        },
+        {
+            "default": "1",
+            "fieldname": "conversion_factor",
+            "fieldtype": "Float",
+            "hidden": 1,
+            "label": "Conversion Factor",
+            "read_only": 1
+        },
+        {
+            "columns": 2,
+            "fieldname": "reserve_warehouse",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Reserve Warehouse",
+            "options": "Warehouse"
+        },
+        {
+            "fieldname": "column_break_6",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "bom_detail_no",
+            "fieldtype": "Data",
+            "label": "BOM Detail No",
+            "read_only": 1
+        },
+        {
+            "fieldname": "reference_name",
+            "fieldtype": "Data",
+            "label": "Reference Name",
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_9",
+            "fieldtype": "Section Break"
+        },
+        {
+            "columns": 2,
+            "fieldname": "rate",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Rate",
+            "options": "Company:company:default_currency"
+        },
+        {
+            "fieldname": "column_break_11",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "amount",
+            "fieldtype": "Currency",
+            "label": "Amount",
+            "options": "Company:company:default_currency",
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_13",
+            "fieldtype": "Section Break"
+        },
+        {
+            "columns": 2,
+            "fieldname": "required_qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Required Qty",
+            "read_only": 1
+        },
+        {
+            "fieldname": "supplied_qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Supplied Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_16",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "consumed_qty",
+            "fieldtype": "Float",
+            "label": "Consumed Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "returned_qty",
+            "fieldtype": "Float",
+            "label": "Returned Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1,
+            "hidden": 1
+        },
+        {
+            "fieldname": "total_supplied_qty",
+            "fieldtype": "Float",
+            "hidden": 1,
+            "label": "Total Supplied Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        }
+    ],
+    "hide_toolbar": 1,
+    "istable": 1,
+    "links": [],
+    "modified": "2022-04-07 12:58:28.208847",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Order Supplied Item",
+    "owner": "Administrator",
+    "permissions": [],
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "states": []
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.py b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.py
new file mode 100644
index 0000000..5619e3b
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order_supplied_item/subcontracting_order_supplied_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SubcontractingOrderSuppliedItem(Document):
+	pass
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/__init__.py b/erpnext/subcontracting/doctype/subcontracting_receipt/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
new file mode 100644
index 0000000..b2506cd
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -0,0 +1,157 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.provide('erpnext.buying');
+
+frappe.ui.form.on('Subcontracting Receipt', {
+	setup: (frm) => {
+		frm.get_field('supplied_items').grid.cannot_add_rows = true;
+		frm.get_field('supplied_items').grid.only_sortable();
+
+		frm.set_query('set_warehouse', () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0
+				}
+			};
+		});
+
+		frm.set_query('rejected_warehouse', () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0
+				}
+			};
+		});
+
+		frm.set_query('supplier_warehouse', () => {
+			return {
+				filters: {
+					company: frm.doc.company,
+					is_group: 0
+				}
+			};
+		});
+
+		frm.set_query('warehouse', 'items', () => ({
+			filters: {
+				company: frm.doc.company,
+				is_group: 0
+			}
+		}));
+
+		frm.set_query('rejected_warehouse', 'items', () => ({
+			filters: {
+				company: frm.doc.company,
+				is_group: 0
+			}
+		}));
+	},
+
+	refresh: (frm) => {
+		if (frm.doc.docstatus > 0) {
+			frm.add_custom_button(__("Stock Ledger"), function () {
+				frappe.route_options = {
+					voucher_no: frm.doc.name,
+					from_date: frm.doc.posting_date,
+					to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
+					company: frm.doc.company,
+					show_cancelled_entries: frm.doc.docstatus === 2
+				};
+				frappe.set_route("query-report", "Stock Ledger");
+			}, __("View"));
+
+			frm.add_custom_button(__('Accounting Ledger'), function () {
+				frappe.route_options = {
+					voucher_no: frm.doc.name,
+					from_date: frm.doc.posting_date,
+					to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
+					company: frm.doc.company,
+					group_by: "Group by Voucher (Consolidated)",
+					show_cancelled_entries: frm.doc.docstatus === 2
+				};
+				frappe.set_route("query-report", "General Ledger");
+			}, __("View"));
+		}
+
+		if (!frm.doc.is_return && frm.doc.docstatus == 1 && frm.doc.per_returned < 100) {
+			frm.add_custom_button(__('Subcontract Return'), function () {
+				frappe.model.open_mapped_doc({
+					method: 'erpnext.subcontracting.doctype.subcontracting_receipt.subcontracting_receipt.make_subcontract_return',
+					frm: frm
+				});
+			}, __('Create'));
+			frm.page.set_inner_btn_group_as_primary(__('Create'));
+		}
+
+		if (frm.doc.docstatus == 0) {
+			frm.add_custom_button(__('Subcontracting Order'), function () {
+				if (!frm.doc.supplier) {
+					frappe.throw({
+						title: __("Mandatory"),
+						message: __("Please Select a Supplier")
+					});
+				}
+
+				erpnext.utils.map_current_doc({
+					method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt',
+					source_doctype: "Subcontracting Order",
+					target: frm,
+					setters: {
+						supplier: frm.doc.supplier,
+					},
+					get_query_filters: {
+						docstatus: 1,
+						per_received: ["<", 100],
+						company: frm.doc.company
+					}
+				});
+			}, __("Get Items From"));
+		}
+	},
+
+	set_warehouse: (frm) => {
+		set_warehouse_in_children(frm.doc.items, 'warehouse', frm.doc.set_warehouse);
+	},
+
+	rejected_warehouse: (frm) => {
+		set_warehouse_in_children(frm.doc.items, 'rejected_warehouse', frm.doc.rejected_warehouse);
+	},
+});
+
+frappe.ui.form.on('Subcontracting Receipt Item', {
+	item_code(frm) {
+		set_missing_values(frm);
+	},
+
+	qty(frm) {
+		set_missing_values(frm);
+	},
+
+	rate(frm) {
+		set_missing_values(frm);
+	},
+});
+
+frappe.ui.form.on('Subcontracting Receipt Supplied Item', {
+	consumed_qty(frm) {
+		set_missing_values(frm);
+	},
+});
+
+let set_warehouse_in_children = (child_table, warehouse_field, warehouse) => {
+	let transaction_controller = new erpnext.TransactionController();
+	transaction_controller.autofill_warehouse(child_table, warehouse_field, warehouse);
+};
+
+let set_missing_values = (frm) => {
+	frappe.call({
+		doc: frm.doc,
+		method: 'set_missing_values',
+		callback: (r) => {
+			if (!r.exc) frm.refresh();
+		},
+	});
+};
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
new file mode 100644
index 0000000..e963814
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
@@ -0,0 +1,645 @@
+{
+    "actions": [],
+    "autoname": "naming_series:",
+    "creation": "2022-04-18 11:20:44.226738",
+    "doctype": "DocType",
+    "document_type": "Document",
+    "editable_grid": 1,
+    "engine": "InnoDB",
+    "field_order": [
+        "title",
+        "naming_series",
+        "supplier",
+        "supplier_name",
+        "column_break1",
+        "company",
+        "posting_date",
+        "posting_time",
+        "is_return",
+        "return_against",
+        "section_addresses",
+        "supplier_address",
+        "contact_person",
+        "address_display",
+        "contact_display",
+        "contact_mobile",
+        "contact_email",
+        "col_break_address",
+        "shipping_address",
+        "shipping_address_display",
+        "billing_address",
+        "billing_address_display",
+        "sec_warehouse",
+        "set_warehouse",
+        "rejected_warehouse",
+        "col_break_warehouse",
+        "supplier_warehouse",
+        "items_section",
+        "items",
+        "section_break0",
+        "total_qty",
+        "column_break_27",
+        "total",
+        "raw_material_details",
+        "get_current_stock",
+        "supplied_items",
+        "section_break_46",
+        "in_words",
+        "bill_no",
+        "bill_date",
+        "accounting_details_section",
+        "provisional_expense_account",
+        "more_info",
+        "status",
+        "column_break_39",
+        "per_returned",
+        "section_break_47",
+        "amended_from",
+        "range",
+        "column_break4",
+        "represents_company",
+        "subscription_detail",
+        "auto_repeat",
+        "printing_settings",
+        "letter_head",
+        "language",
+        "instructions",
+        "column_break_97",
+        "select_print_heading",
+        "other_details",
+        "remarks",
+        "transporter_info",
+        "transporter_name",
+        "column_break5",
+        "lr_no",
+        "lr_date"
+    ],
+    "fields": [
+        {
+            "allow_on_submit": 1,
+            "default": "{supplier_name}",
+            "fieldname": "title",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "label": "Title",
+            "no_copy": 1,
+            "print_hide": 1
+        },
+        {
+            "fieldname": "naming_series",
+            "fieldtype": "Select",
+            "label": "Series",
+            "no_copy": 1,
+            "options": "MAT-SCR-.YYYY.-\nMAT-SCR-RET-.YYYY.-",
+            "print_hide": 1,
+            "reqd": 1,
+            "set_only_once": 1
+        },
+        {
+            "bold": 1,
+            "fieldname": "supplier",
+            "fieldtype": "Link",
+            "in_global_search": 1,
+            "label": "Supplier",
+            "options": "Supplier",
+            "print_hide": 1,
+            "print_width": "150px",
+            "reqd": 1,
+            "search_index": 1,
+            "width": "150px"
+        },
+        {
+            "bold": 1,
+            "depends_on": "supplier",
+            "fetch_from": "supplier.supplier_name",
+            "fieldname": "supplier_name",
+            "fieldtype": "Data",
+            "in_global_search": 1,
+            "label": "Supplier Name",
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break1",
+            "fieldtype": "Column Break",
+            "print_width": "50%",
+            "width": "50%"
+        },
+        {
+            "default": "Today",
+            "fieldname": "posting_date",
+            "fieldtype": "Date",
+            "in_list_view": 1,
+            "label": "Date",
+            "no_copy": 1,
+            "print_width": "100px",
+            "reqd": 1,
+            "search_index": 1,
+            "width": "100px"
+        },
+        {
+            "description": "Time at which materials were received",
+            "fieldname": "posting_time",
+            "fieldtype": "Time",
+            "label": "Posting Time",
+            "no_copy": 1,
+            "print_hide": 1,
+            "print_width": "100px",
+            "reqd": 1,
+            "width": "100px"
+        },
+        {
+            "fieldname": "company",
+            "fieldtype": "Link",
+            "in_standard_filter": 1,
+            "label": "Company",
+            "options": "Company",
+            "print_hide": 1,
+            "print_width": "150px",
+            "remember_last_selected_value": 1,
+            "reqd": 1,
+            "width": "150px"
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "section_addresses",
+            "fieldtype": "Section Break",
+            "label": "Address and Contact"
+        },
+        {
+            "fieldname": "supplier_address",
+            "fieldtype": "Link",
+            "label": "Select Supplier Address",
+            "options": "Address",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "contact_person",
+            "fieldtype": "Link",
+            "label": "Contact Person",
+            "options": "Contact",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "address_display",
+            "fieldtype": "Small Text",
+            "label": "Address",
+            "read_only": 1
+        },
+        {
+            "fieldname": "contact_display",
+            "fieldtype": "Small Text",
+            "in_global_search": 1,
+            "label": "Contact",
+            "read_only": 1
+        },
+        {
+            "fieldname": "contact_mobile",
+            "fieldtype": "Small Text",
+            "label": "Mobile No",
+            "read_only": 1
+        },
+        {
+            "fieldname": "contact_email",
+            "fieldtype": "Small Text",
+            "label": "Contact Email",
+            "options": "Email",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "col_break_address",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "shipping_address",
+            "fieldtype": "Link",
+            "label": "Select Shipping Address",
+            "options": "Address",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "shipping_address_display",
+            "fieldtype": "Small Text",
+            "label": "Shipping Address",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "sec_warehouse",
+            "fieldtype": "Section Break"
+        },
+        {
+            "description": "Sets 'Accepted Warehouse' in each row of the Items table.",
+            "fieldname": "set_warehouse",
+            "fieldtype": "Link",
+            "label": "Accepted Warehouse",
+            "options": "Warehouse",
+            "print_hide": 1
+        },
+        {
+            "description": "Sets 'Rejected Warehouse' in each row of the Items table.",
+            "fieldname": "rejected_warehouse",
+            "fieldtype": "Link",
+            "label": "Rejected Warehouse",
+            "no_copy": 1,
+            "options": "Warehouse",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "col_break_warehouse",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "supplier_warehouse",
+            "fieldtype": "Link",
+            "label": "Supplier Warehouse",
+            "no_copy": 1,
+            "options": "Warehouse",
+            "print_hide": 1,
+            "print_width": "50px",
+            "width": "50px"
+        },
+        {
+            "fieldname": "items_section",
+            "fieldtype": "Section Break",
+            "options": "fa fa-shopping-cart"
+        },
+        {
+            "allow_bulk_edit": 1,
+            "fieldname": "items",
+            "fieldtype": "Table",
+            "label": "Items",
+            "options": "Subcontracting Receipt Item",
+            "reqd": 1
+        },
+        {
+            "depends_on": "supplied_items",
+            "fieldname": "get_current_stock",
+            "fieldtype": "Button",
+            "label": "Get Current Stock",
+            "options": "get_current_stock",
+            "print_hide": 1
+        },
+        {
+            "collapsible": 1,
+            "collapsible_depends_on": "supplied_items",
+            "depends_on": "supplied_items",
+            "fieldname": "raw_material_details",
+            "fieldtype": "Section Break",
+            "label": "Raw Materials Consumed",
+            "options": "fa fa-table",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "supplied_items",
+            "fieldtype": "Table",
+            "label": "Consumed Items",
+            "no_copy": 1,
+            "options": "Subcontracting Receipt Supplied Item",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "section_break0",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "total_qty",
+            "fieldtype": "Float",
+            "label": "Total Quantity",
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_27",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "total",
+            "fieldtype": "Currency",
+            "label": "Total",
+            "options": "currency",
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_46",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "in_words",
+            "fieldtype": "Data",
+            "label": "In Words",
+            "length": 240,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "bill_no",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "label": "Bill No",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "bill_date",
+            "fieldtype": "Date",
+            "hidden": 1,
+            "label": "Bill Date",
+            "print_hide": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "more_info",
+            "fieldtype": "Section Break",
+            "label": "More Information",
+            "options": "fa fa-file-text"
+        },
+        {
+            "default": "Draft",
+            "fieldname": "status",
+            "fieldtype": "Select",
+            "in_standard_filter": 1,
+            "label": "Status",
+            "no_copy": 1,
+            "options": "\nDraft\nCompleted\nReturn\nReturn Issued\nCancelled",
+            "print_hide": 1,
+            "print_width": "150px",
+            "read_only": 1,
+            "reqd": 1,
+            "search_index": 1,
+            "width": "150px"
+        },
+        {
+            "fieldname": "amended_from",
+            "fieldtype": "Link",
+            "hidden": 1,
+            "ignore_user_permissions": 1,
+            "label": "Amended From",
+            "no_copy": 1,
+            "options": "Subcontracting Receipt",
+            "print_hide": 1,
+            "print_width": "150px",
+            "read_only": 1,
+            "width": "150px"
+        },
+        {
+            "fieldname": "range",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "label": "Range",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "column_break4",
+            "fieldtype": "Column Break",
+            "print_hide": 1,
+            "print_width": "50%",
+            "width": "50%"
+        },
+        {
+            "fieldname": "subscription_detail",
+            "fieldtype": "Section Break",
+            "label": "Auto Repeat Detail"
+        },
+        {
+            "fieldname": "auto_repeat",
+            "fieldtype": "Link",
+            "label": "Auto Repeat",
+            "no_copy": 1,
+            "options": "Auto Repeat",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "printing_settings",
+            "fieldtype": "Section Break",
+            "label": "Printing Settings"
+        },
+        {
+            "allow_on_submit": 1,
+            "fieldname": "letter_head",
+            "fieldtype": "Link",
+            "label": "Letter Head",
+            "options": "Letter Head",
+            "print_hide": 1
+        },
+        {
+            "allow_on_submit": 1,
+            "fieldname": "select_print_heading",
+            "fieldtype": "Link",
+            "label": "Print Heading",
+            "no_copy": 1,
+            "options": "Print Heading",
+            "print_hide": 1,
+            "report_hide": 1
+        },
+        {
+            "fieldname": "language",
+            "fieldtype": "Data",
+            "label": "Print Language",
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_97",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "other_details",
+            "fieldtype": "HTML",
+            "hidden": 1,
+            "label": "Other Details",
+            "options": "<div class=\"columnHeading\">Other Details</div>",
+            "print_hide": 1,
+            "print_width": "30%",
+            "width": "30%"
+        },
+        {
+            "fieldname": "instructions",
+            "fieldtype": "Small Text",
+            "label": "Instructions"
+        },
+        {
+            "fieldname": "remarks",
+            "fieldtype": "Small Text",
+            "label": "Remarks",
+            "print_hide": 1
+        },
+        {
+            "collapsible": 1,
+            "collapsible_depends_on": "transporter_name",
+            "fieldname": "transporter_info",
+            "fieldtype": "Section Break",
+            "label": "Transporter Details",
+            "options": "fa fa-truck"
+        },
+        {
+            "fieldname": "transporter_name",
+            "fieldtype": "Data",
+            "label": "Transporter Name"
+        },
+        {
+            "fieldname": "column_break5",
+            "fieldtype": "Column Break",
+            "print_width": "50%",
+            "width": "50%"
+        },
+        {
+            "fieldname": "lr_no",
+            "fieldtype": "Data",
+            "label": "Vehicle Number",
+            "no_copy": 1,
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "fieldname": "lr_date",
+            "fieldtype": "Date",
+            "label": "Vehicle Date",
+            "no_copy": 1,
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "fieldname": "billing_address",
+            "fieldtype": "Link",
+            "label": "Select Billing Address",
+            "options": "Address"
+        },
+        {
+            "fieldname": "billing_address_display",
+            "fieldtype": "Small Text",
+            "label": "Billing Address",
+            "read_only": 1
+        },
+        {
+            "fetch_from": "supplier.represents_company",
+            "fieldname": "represents_company",
+            "fieldtype": "Link",
+            "ignore_user_permissions": 1,
+            "label": "Represents Company",
+            "options": "Company",
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "accounting_details_section",
+            "fieldtype": "Section Break",
+            "label": "Accounting Details"
+        },
+        {
+            "fieldname": "provisional_expense_account",
+            "fieldtype": "Link",
+            "hidden": 1,
+            "label": "Provisional Expense Account",
+            "options": "Account"
+        },
+        {
+            "default": "0",
+            "fieldname": "is_return",
+            "fieldtype": "Check",
+            "label": "Is Return",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "depends_on": "is_return",
+            "fieldname": "return_against",
+            "fieldtype": "Link",
+            "label": "Return Against Subcontracting Receipt",
+            "no_copy": 1,
+            "options": "Subcontracting Receipt",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_39",
+            "fieldtype": "Column Break"
+        },
+        {
+            "depends_on": "eval:(!doc.__islocal && doc.is_return==0)",
+            "fieldname": "per_returned",
+            "fieldtype": "Percent",
+            "in_list_view": 1,
+            "label": "% Returned",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_47",
+            "fieldtype": "Section Break"
+        }
+    ],
+    "is_submittable": 1,
+    "links": [],
+    "modified": "2022-04-18 13:15:12.011682",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Receipt",
+    "naming_rule": "By \"Naming Series\" field",
+    "owner": "Administrator",
+    "permissions": [
+        {
+            "amend": 1,
+            "cancel": 1,
+            "create": 1,
+            "delete": 1,
+            "email": 1,
+            "print": 1,
+            "read": 1,
+            "report": 1,
+            "role": "Stock Manager",
+            "share": 1,
+            "submit": 1,
+            "write": 1
+        },
+        {
+            "amend": 1,
+            "cancel": 1,
+            "create": 1,
+            "delete": 1,
+            "email": 1,
+            "print": 1,
+            "read": 1,
+            "report": 1,
+            "role": "Stock User",
+            "share": 1,
+            "submit": 1,
+            "write": 1
+        },
+        {
+            "amend": 1,
+            "cancel": 1,
+            "create": 1,
+            "delete": 1,
+            "email": 1,
+            "print": 1,
+            "read": 1,
+            "report": 1,
+            "role": "Purchase User",
+            "share": 1,
+            "submit": 1,
+            "write": 1
+        },
+        {
+            "read": 1,
+            "report": 1,
+            "role": "Accounts User"
+        },
+        {
+            "permlevel": 1,
+            "read": 1,
+            "role": "Stock Manager",
+            "write": 1
+        }
+    ],
+    "search_fields": "status, posting_date, supplier",
+    "show_name_in_global_search": 1,
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "states": [],
+    "timeline_field": "supplier",
+    "title_field": "title",
+    "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
new file mode 100644
index 0000000..0c4ec6f
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -0,0 +1,188 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.utils import cint, getdate, nowdate
+
+from erpnext.controllers.subcontracting_controller import SubcontractingController
+
+
+class SubcontractingReceipt(SubcontractingController):
+	def __init__(self, *args, **kwargs):
+		super(SubcontractingReceipt, self).__init__(*args, **kwargs)
+		self.status_updater = [
+			{
+				"target_dt": "Subcontracting Order Item",
+				"join_field": "subcontracting_order_item",
+				"target_field": "received_qty",
+				"target_parent_dt": "Subcontracting Order",
+				"target_parent_field": "per_received",
+				"target_ref_field": "qty",
+				"source_dt": "Subcontracting Receipt Item",
+				"source_field": "received_qty",
+				"percent_join_field": "subcontracting_order",
+				"overflow_type": "receipt",
+			},
+		]
+
+	def update_status_updater_args(self):
+		if cint(self.is_return):
+			self.status_updater.extend(
+				[
+					{
+						"source_dt": "Subcontracting Receipt Item",
+						"target_dt": "Subcontracting Order Item",
+						"join_field": "subcontracting_order_item",
+						"target_field": "returned_qty",
+						"source_field": "-1 * qty",
+						"extra_cond": """ and exists (select name from `tabSubcontracting Receipt`
+						where name=`tabSubcontracting Receipt Item`.parent and is_return=1)""",
+					},
+					{
+						"source_dt": "Subcontracting Receipt Item",
+						"target_dt": "Subcontracting Receipt Item",
+						"join_field": "subcontracting_receipt_item",
+						"target_field": "returned_qty",
+						"target_parent_dt": "Subcontracting Receipt",
+						"target_parent_field": "per_returned",
+						"target_ref_field": "received_qty",
+						"source_field": "-1 * received_qty",
+						"percent_join_field_parent": "return_against",
+					},
+				]
+			)
+
+	def before_validate(self):
+		super(SubcontractingReceipt, self).before_validate()
+		self.set_items_cost_center()
+		self.set_items_expense_account()
+
+	def validate(self):
+		super(SubcontractingReceipt, self).validate()
+		self.set_missing_values()
+		self.validate_posting_time()
+		self.validate_rejected_warehouse()
+
+		if self._action == "submit":
+			self.make_batches("warehouse")
+
+		if getdate(self.posting_date) > getdate(nowdate()):
+			frappe.throw(_("Posting Date cannot be future date"))
+
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
+		self.get_current_stock()
+
+	def on_submit(self):
+		self.update_status_updater_args()
+		self.update_prevdoc_status()
+		self.set_subcontracting_order_status()
+		self.set_consumed_qty_in_subcontract_order()
+		self.update_stock_ledger()
+
+		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+
+		update_serial_nos_after_submit(self, "items")
+
+		self.make_gl_entries()
+		self.repost_future_sle_and_gle()
+		self.update_status()
+
+	def on_cancel(self):
+		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
+		self.update_status_updater_args()
+		self.update_prevdoc_status()
+		self.update_stock_ledger()
+		self.make_gl_entries_on_cancel()
+		self.repost_future_sle_and_gle()
+		self.delete_auto_created_batches()
+		self.set_consumed_qty_in_subcontract_order()
+		self.set_subcontracting_order_status()
+		self.update_status()
+
+	@frappe.whitelist()
+	def set_missing_values(self):
+		self.set_missing_values_in_supplied_items()
+		self.set_missing_values_in_items()
+
+	def set_missing_values_in_supplied_items(self):
+		for item in self.get("supplied_items") or []:
+			item.amount = item.rate * item.consumed_qty
+
+	def set_missing_values_in_items(self):
+		rm_supp_cost = {}
+		for item in self.get("supplied_items") or []:
+			if item.reference_name in rm_supp_cost:
+				rm_supp_cost[item.reference_name] += item.amount
+			else:
+				rm_supp_cost[item.reference_name] = item.amount
+
+		total_qty = total_amount = 0
+		for item in self.items:
+			if item.name in rm_supp_cost:
+				item.rm_supp_cost = rm_supp_cost[item.name]
+				item.rm_cost_per_qty = item.rm_supp_cost / item.qty
+				rm_supp_cost.pop(item.name)
+
+			if self.is_new() and item.rm_supp_cost > 0:
+				item.rate = (
+					item.rm_cost_per_qty + (item.service_cost_per_qty or 0) + item.additional_cost_per_qty
+				)
+
+			item.received_qty = item.qty + (item.rejected_qty or 0)
+			item.amount = item.qty * item.rate
+			total_qty += item.qty
+			total_amount += item.amount
+		else:
+			self.total_qty = total_qty
+			self.total = total_amount
+
+	def validate_rejected_warehouse(self):
+		if not self.rejected_warehouse:
+			for item in self.items:
+				if item.rejected_qty:
+					frappe.throw(
+						_("Rejected Warehouse is mandatory against rejected Item {0}").format(item.item_code)
+					)
+
+	def set_items_cost_center(self):
+		if self.company:
+			cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
+
+			for item in self.items:
+				if not item.cost_center:
+					item.cost_center = cost_center
+
+	def set_items_expense_account(self):
+		if self.company:
+			expense_account = self.get_company_default("default_expense_account", ignore_validation=True)
+
+			for item in self.items:
+				if not item.expense_account:
+					item.expense_account = expense_account
+
+	def update_status(self, status=None, update_modified=False):
+		if self.docstatus >= 1 and not status:
+			if self.docstatus == 1:
+				if self.is_return:
+					status = "Return"
+					return_against = frappe.get_doc("Subcontracting Receipt", self.return_against)
+					return_against.run_method("update_status")
+				else:
+					if self.per_returned == 100:
+						status = "Return Issued"
+					elif self.status == "Draft":
+						status = "Completed"
+			elif self.docstatus == 2:
+				status = "Cancelled"
+
+		if status:
+			frappe.db.set_value("Subcontracting Receipt", self.name, "status", status, update_modified)
+
+
+@frappe.whitelist()
+def make_subcontract_return(source_name, target_doc=None):
+	from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+	return make_return_doc("Subcontracting Receipt", source_name, target_doc)
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py
new file mode 100644
index 0000000..a9e5193
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_dashboard.py
@@ -0,0 +1,15 @@
+from frappe import _
+
+
+def get_data():
+	return {
+		"fieldname": "subcontracting_receipt_no",
+		"internal_links": {
+			"Subcontracting Order": ["items", "subcontracting_order"],
+			"Project": ["items", "project"],
+			"Quality Inspection": ["items", "quality_inspection"],
+		},
+		"transactions": [
+			{"label": _("Reference"), "items": ["Subcontracting Order", "Quality Inspection", "Project"]},
+		],
+	}
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js
new file mode 100644
index 0000000..14a4e4a
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.listview_settings['Subcontracting Receipt'] = {
+	get_indicator: function (doc) {
+		const status_colors = {
+			"Draft": "grey",
+			"Return": "gray",
+			"Return Issued": "grey",
+			"Completed": "green",
+		};
+		return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
+	},
+};
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
new file mode 100644
index 0000000..763e768
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -0,0 +1,374 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+
+import copy
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import flt
+
+from erpnext.controllers.sales_and_purchase_return import make_return_doc
+from erpnext.controllers.tests.test_subcontracting_controller import (
+	get_rm_items,
+	get_subcontracting_order,
+	make_bom_for_subcontracted_items,
+	make_raw_materials,
+	make_service_items,
+	make_stock_in_entry,
+	make_stock_transfer_entry,
+	make_subcontracted_item,
+	make_subcontracted_items,
+	set_backflush_based_on,
+)
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+	make_subcontracting_receipt,
+)
+
+
+class TestSubcontractingReceipt(FrappeTestCase):
+	def setUp(self):
+		make_subcontracted_items()
+		make_raw_materials()
+		make_service_items()
+		make_bom_for_subcontracted_items()
+
+	def test_subcontracting(self):
+		set_backflush_based_on("BOM")
+		make_stock_entry(
+			item_code="_Test Item", qty=100, target="_Test Warehouse 1 - _TC", basic_rate=100
+		)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			qty=100,
+			target="_Test Warehouse 1 - _TC",
+			basic_rate=100,
+		)
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 10,
+				"rate": 100,
+				"fg_item": "_Test FG Item",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		scr.submit()
+		rm_supp_cost = sum(item.amount for item in scr.get("supplied_items"))
+		self.assertEqual(scr.get("items")[0].rm_supp_cost, flt(rm_supp_cost))
+
+	def test_subcontracting_gle_fg_item_rate_zero(self):
+		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
+
+		set_backflush_based_on("BOM")
+		make_stock_entry(
+			item_code="_Test Item",
+			target="Work In Progress - TCP1",
+			qty=100,
+			basic_rate=100,
+			company="_Test Company with perpetual inventory",
+		)
+		make_stock_entry(
+			item_code="_Test Item Home Desktop 100",
+			target="Work In Progress - TCP1",
+			qty=100,
+			basic_rate=100,
+			company="_Test Company with perpetual inventory",
+		)
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 10,
+				"rate": 0,
+				"fg_item": "_Test FG Item",
+				"fg_item_qty": 10,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		scr.submit()
+
+		gl_entries = get_gl_entries("Subcontracting Receipt", scr.name)
+		self.assertFalse(gl_entries)
+
+	def test_subcontracting_over_receipt(self):
+		"""
+		Behaviour: Raise multiple SCRs against one SCO that in total
+		        receive more than the required qty in the SCO.
+		Expected Result: Error Raised for Over Receipt against SCO.
+		"""
+		from erpnext.controllers.subcontracting_controller import (
+			make_rm_stock_entry as make_subcontract_transfer_entry,
+		)
+		from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+			make_subcontracting_receipt,
+		)
+		from erpnext.subcontracting.doctype.subcontracting_order.test_subcontracting_order import (
+			make_subcontracted_item,
+		)
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "_Test Subcontracted FG Item 1"
+		make_subcontracted_item(item_code=item_code)
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 1,
+				"rate": 100,
+				"fg_item": "_Test Subcontracted FG Item 1",
+				"fg_item_qty": 1,
+			},
+		]
+		sco = get_subcontracting_order(
+			service_items=service_items,
+			include_exploded_items=0,
+		)
+
+		# stock raw materials in a warehouse before transfer
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=10, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="_Test FG Item", qty=1, basic_rate=100
+		)
+		make_stock_entry(
+			target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=1, basic_rate=100
+		)
+
+		rm_items = [
+			{
+				"item_code": item_code,
+				"rm_item_code": sco.supplied_items[0].rm_item_code,
+				"item_name": "_Test FG Item",
+				"qty": sco.supplied_items[0].required_qty,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": sco.supplied_items[1].rm_item_code,
+				"item_name": "Test Extra Item 1",
+				"qty": sco.supplied_items[1].required_qty,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+			},
+		]
+		ste = frappe.get_doc(make_subcontract_transfer_entry(sco.name, rm_items))
+		ste.to_warehouse = "_Test Warehouse 1 - _TC"
+		ste.save()
+		ste.submit()
+
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr2 = make_subcontracting_receipt(sco.name)
+
+		scr1.submit()
+		self.assertRaises(frappe.ValidationError, scr2.submit)
+
+	def test_subcontracted_scr_for_multi_transfer_batches(self):
+		from erpnext.controllers.subcontracting_controller import make_rm_stock_entry
+		from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
+			make_subcontracting_receipt,
+		)
+
+		set_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "_Test Subcontracted FG Item 3"
+
+		make_item(
+			"Sub Contracted Raw Material 3",
+			{"is_stock_item": 1, "is_sub_contracted_item": 1, "has_batch_no": 1, "create_new_batch": 1},
+		)
+
+		make_subcontracted_item(
+			item_code=item_code, has_batch_no=1, raw_materials=["Sub Contracted Raw Material 3"]
+		)
+
+		order_qty = 500
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 3",
+				"qty": order_qty,
+				"rate": 100,
+				"fg_item": "_Test Subcontracted FG Item 3",
+				"fg_item_qty": order_qty,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+
+		ste1 = make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 3",
+			qty=300,
+			basic_rate=100,
+		)
+		ste2 = make_stock_entry(
+			target="_Test Warehouse - _TC",
+			item_code="Sub Contracted Raw Material 3",
+			qty=200,
+			basic_rate=100,
+		)
+
+		transferred_batch = {ste1.items[0].batch_no: 300, ste2.items[0].batch_no: 200}
+
+		rm_items = [
+			{
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 3",
+				"item_name": "_Test Item",
+				"qty": 300,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+				"name": sco.supplied_items[0].name,
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": "Sub Contracted Raw Material 3",
+				"item_name": "_Test Item",
+				"qty": 200,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos",
+				"name": sco.supplied_items[0].name,
+			},
+		]
+
+		se = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items))
+		self.assertEqual(len(se.items), 2)
+		se.items[0].batch_no = ste1.items[0].batch_no
+		se.items[1].batch_no = ste2.items[0].batch_no
+		se.submit()
+
+		supplied_qty = frappe.db.get_value(
+			"Subcontracting Order Supplied Item",
+			{"parent": sco.name, "rm_item_code": "Sub Contracted Raw Material 3"},
+			"supplied_qty",
+		)
+
+		self.assertEqual(supplied_qty, 500.00)
+
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		self.assertEqual(len(scr.supplied_items), 2)
+
+		for row in scr.supplied_items:
+			self.assertEqual(transferred_batch.get(row.batch_no), row.consumed_qty)
+
+	def test_subcontracting_order_partial_return(self):
+		sco = get_subcontracting_order()
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.save()
+		scr1.submit()
+
+		scr1_return = make_return_subcontracting_receipt(scr_name=scr1.name, qty=-3)
+		scr1.load_from_db()
+		self.assertEqual(scr1_return.status, "Return")
+		self.assertEqual(scr1.items[0].returned_qty, 3)
+
+		scr2_return = make_return_subcontracting_receipt(scr_name=scr1.name, qty=-7)
+		scr1.load_from_db()
+		self.assertEqual(scr2_return.status, "Return")
+		self.assertEqual(scr1.status, "Return Issued")
+		self.assertEqual(scr1.items[0].returned_qty, 10)
+
+	def test_subcontracting_order_over_return(self):
+		sco = get_subcontracting_order()
+		rm_items = get_rm_items(sco.supplied_items)
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		scr1 = make_subcontracting_receipt(sco.name)
+		scr1.save()
+		scr1.submit()
+
+		from erpnext.controllers.status_updater import OverAllowanceError
+
+		args = frappe._dict(scr_name=scr1.name, qty=-15)
+		self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args)
+
+
+def make_return_subcontracting_receipt(**args):
+	args = frappe._dict(args)
+	return_doc = make_return_doc("Subcontracting Receipt", args.scr_name)
+	return_doc.supplier_warehouse = (
+		args.supplier_warehouse or args.warehouse or "_Test Warehouse 1 - _TC"
+	)
+
+	if args.qty:
+		for item in return_doc.items:
+			item.qty = args.qty
+
+	if not args.do_not_save:
+		return_doc.save()
+		if not args.do_not_submit:
+			return_doc.submit()
+
+	return_doc.load_from_db()
+	return return_doc
+
+
+def get_items(**args):
+	args = frappe._dict(args)
+	return [
+		{
+			"conversion_factor": 1.0,
+			"description": "_Test Item",
+			"doctype": "Subcontracting Receipt Item",
+			"item_code": "_Test Item",
+			"item_name": "_Test Item",
+			"parentfield": "items",
+			"qty": 5.0,
+			"rate": 50.0,
+			"received_qty": 5.0,
+			"rejected_qty": 0.0,
+			"stock_uom": "_Test UOM",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"cost_center": args.cost_center or "Main - _TC",
+		},
+		{
+			"conversion_factor": 1.0,
+			"description": "_Test Item Home Desktop 100",
+			"doctype": "Subcontracting Receipt Item",
+			"item_code": "_Test Item Home Desktop 100",
+			"item_name": "_Test Item Home Desktop 100",
+			"parentfield": "items",
+			"qty": 5.0,
+			"rate": 50.0,
+			"received_qty": 5.0,
+			"rejected_qty": 0.0,
+			"stock_uom": "_Test UOM",
+			"warehouse": args.warehouse or "_Test Warehouse 1 - _TC",
+			"cost_center": args.cost_center or "Main - _TC",
+		},
+	]
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
new file mode 100644
index 0000000..e2785ce
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
@@ -0,0 +1,475 @@
+{
+    "actions": [],
+    "autoname": "hash",
+    "creation": "2022-04-13 16:05:55.395695",
+    "doctype": "DocType",
+    "document_type": "Document",
+    "editable_grid": 1,
+    "engine": "InnoDB",
+    "field_order": [
+        "item_code",
+        "column_break_2",
+        "item_name",
+        "section_break_4",
+        "description",
+        "brand",
+        "image_column",
+        "image",
+        "image_view",
+        "received_and_accepted",
+        "received_qty",
+        "qty",
+        "rejected_qty",
+        "returned_qty",
+        "col_break2",
+        "stock_uom",
+        "conversion_factor",
+        "tracking_section",
+        "col_break_tracking_section",
+        "rate_and_amount",
+        "rate",
+        "amount",
+        "column_break_19",
+        "rm_cost_per_qty",
+        "service_cost_per_qty",
+        "additional_cost_per_qty",
+        "rm_supp_cost",
+        "warehouse_and_reference",
+        "warehouse",
+        "rejected_warehouse",
+        "subcontracting_order",
+        "column_break_40",
+        "schedule_date",
+        "quality_inspection",
+        "subcontracting_order_item",
+        "subcontracting_receipt_item",
+        "section_break_45",
+        "bom",
+        "serial_no",
+        "col_break5",
+        "batch_no",
+        "rejected_serial_no",
+        "expense_account",
+        "manufacture_details",
+        "manufacturer",
+        "column_break_16",
+        "manufacturer_part_no",
+        "accounting_dimensions_section",
+        "project",
+        "dimension_col_break",
+        "cost_center",
+        "section_break_80",
+        "page_break"
+    ],
+    "fields": [
+        {
+            "bold": 1,
+            "columns": 3,
+            "fieldname": "item_code",
+            "fieldtype": "Link",
+            "in_global_search": 1,
+            "in_list_view": 1,
+            "label": "Item Code",
+            "options": "Item",
+            "print_width": "100px",
+            "reqd": 1,
+            "search_index": 1,
+            "width": "100px"
+        },
+        {
+            "fieldname": "column_break_2",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "item_name",
+            "fieldtype": "Data",
+            "in_global_search": 1,
+            "label": "Item Name",
+            "print_hide": 1,
+            "reqd": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "section_break_4",
+            "fieldtype": "Section Break",
+            "label": "Description"
+        },
+        {
+            "fieldname": "description",
+            "fieldtype": "Text Editor",
+            "label": "Description",
+            "print_width": "300px",
+            "reqd": 1,
+            "width": "300px"
+        },
+        {
+            "fieldname": "image",
+            "fieldtype": "Attach",
+            "hidden": 1,
+            "label": "Image"
+        },
+        {
+            "fieldname": "image_view",
+            "fieldtype": "Image",
+            "label": "Image View",
+            "options": "image",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "received_and_accepted",
+            "fieldtype": "Section Break",
+            "label": "Received and Accepted"
+        },
+        {
+            "bold": 1,
+            "default": "0",
+            "fieldname": "received_qty",
+            "fieldtype": "Float",
+            "label": "Received Quantity",
+            "no_copy": 1,
+            "print_hide": 1,
+            "print_width": "100px",
+            "read_only": 1,
+            "reqd": 1,
+            "width": "100px"
+        },
+        {
+            "columns": 2,
+            "fieldname": "qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Accepted Quantity",
+            "no_copy": 1,
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "columns": 1,
+            "fieldname": "rejected_qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Rejected Quantity",
+            "no_copy": 1,
+            "print_hide": 1,
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "fieldname": "col_break2",
+            "fieldtype": "Column Break",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "stock_uom",
+            "fieldtype": "Link",
+            "label": "Stock UOM",
+            "options": "UOM",
+            "print_hide": 1,
+            "print_width": "100px",
+            "read_only": 1,
+            "reqd": 1,
+            "width": "100px"
+        },
+        {
+            "default": "1",
+            "fieldname": "conversion_factor",
+            "fieldtype": "Float",
+            "hidden": 1,
+            "label": "Conversion Factor",
+            "read_only": 1
+        },
+        {
+            "fieldname": "rate_and_amount",
+            "fieldtype": "Section Break",
+            "label": "Rate and Amount"
+        },
+        {
+            "bold": 1,
+            "columns": 2,
+            "fieldname": "rate",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Rate",
+            "options": "currency",
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "fieldname": "amount",
+            "fieldtype": "Currency",
+            "in_list_view": 1,
+            "label": "Amount",
+            "options": "currency",
+            "read_only": 1
+        },
+        {
+            "fieldname": "column_break_19",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "rm_cost_per_qty",
+            "fieldtype": "Currency",
+            "label": "Raw Material Cost Per Qty",
+            "no_copy": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "service_cost_per_qty",
+            "fieldtype": "Currency",
+            "label": "Service Cost Per Qty",
+            "read_only": 1,
+            "reqd": 1
+        },
+        {
+            "default": "0",
+            "fieldname": "additional_cost_per_qty",
+            "fieldtype": "Currency",
+            "label": "Additional Cost Per Qty",
+            "read_only": 1
+        },
+        {
+            "fieldname": "warehouse_and_reference",
+            "fieldtype": "Section Break",
+            "label": "Warehouse and Reference"
+        },
+        {
+            "bold": 1,
+            "fieldname": "warehouse",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Accepted Warehouse",
+            "options": "Warehouse",
+            "print_hide": 1,
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "fieldname": "rejected_warehouse",
+            "fieldtype": "Link",
+            "label": "Rejected Warehouse",
+            "no_copy": 1,
+            "options": "Warehouse",
+            "print_hide": 1,
+            "print_width": "100px",
+            "width": "100px"
+        },
+        {
+            "depends_on": "eval:!doc.__islocal",
+            "fieldname": "quality_inspection",
+            "fieldtype": "Link",
+            "label": "Quality Inspection",
+            "no_copy": 1,
+            "options": "Quality Inspection",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "column_break_40",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "subcontracting_order",
+            "fieldtype": "Link",
+            "label": "Subcontracting Order",
+            "no_copy": 1,
+            "options": "Subcontracting Order",
+            "print_width": "150px",
+            "read_only": 1,
+            "search_index": 1,
+            "width": "150px"
+        },
+        {
+            "fieldname": "schedule_date",
+            "fieldtype": "Date",
+            "label": "Required By",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "section_break_45",
+            "fieldtype": "Section Break"
+        },
+        {
+            "depends_on": "eval:!doc.is_fixed_asset",
+            "fieldname": "serial_no",
+            "fieldtype": "Small Text",
+            "in_list_view": 1,
+            "label": "Serial No",
+            "no_copy": 1
+        },
+        {
+            "depends_on": "eval:!doc.is_fixed_asset",
+            "fieldname": "batch_no",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Batch No",
+            "no_copy": 1,
+            "options": "Batch",
+            "print_hide": 1
+        },
+        {
+            "depends_on": "eval:!doc.is_fixed_asset",
+            "fieldname": "rejected_serial_no",
+            "fieldtype": "Small Text",
+            "label": "Rejected Serial No",
+            "no_copy": 1,
+            "print_hide": 1
+        },
+        {
+            "fieldname": "subcontracting_order_item",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "label": "Subcontracting Order Item",
+            "no_copy": 1,
+            "print_hide": 1,
+            "print_width": "150px",
+            "read_only": 1,
+            "search_index": 1,
+            "width": "150px"
+        },
+        {
+            "fieldname": "col_break5",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "bom",
+            "fieldtype": "Link",
+            "label": "BOM",
+            "no_copy": 1,
+            "options": "BOM",
+            "print_hide": 1
+        },
+        {
+            "fetch_from": "item_code.brand",
+            "fieldname": "brand",
+            "fieldtype": "Link",
+            "hidden": 1,
+            "label": "Brand",
+            "options": "Brand",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "fieldname": "rm_supp_cost",
+            "fieldtype": "Currency",
+            "hidden": 1,
+            "label": "Raw Materials Supplied Cost",
+            "no_copy": 1,
+            "options": "Company:company:default_currency",
+            "print_hide": 1,
+            "print_width": "150px",
+            "read_only": 1,
+            "width": "150px"
+        },
+        {
+            "fieldname": "expense_account",
+            "fieldtype": "Link",
+            "hidden": 1,
+            "label": "Expense Account",
+            "options": "Account",
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "manufacture_details",
+            "fieldtype": "Section Break",
+            "label": "Manufacture"
+        },
+        {
+            "fieldname": "manufacturer",
+            "fieldtype": "Link",
+            "label": "Manufacturer",
+            "options": "Manufacturer"
+        },
+        {
+            "fieldname": "column_break_16",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "manufacturer_part_no",
+            "fieldtype": "Data",
+            "label": "Manufacturer Part Number"
+        },
+        {
+            "fieldname": "subcontracting_receipt_item",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "label": "Subcontracting Receipt Item",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "collapsible": 1,
+            "fieldname": "image_column",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "tracking_section",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "col_break_tracking_section",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "accounting_dimensions_section",
+            "fieldtype": "Section Break",
+            "label": "Accounting Dimensions"
+        },
+        {
+            "fieldname": "project",
+            "fieldtype": "Link",
+            "label": "Project",
+            "options": "Project",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "dimension_col_break",
+            "fieldtype": "Column Break"
+        },
+        {
+            "default": ":Company",
+            "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
+            "fieldname": "cost_center",
+            "fieldtype": "Link",
+            "label": "Cost Center",
+            "options": "Cost Center",
+            "print_hide": 1
+        },
+        {
+            "fieldname": "section_break_80",
+            "fieldtype": "Section Break"
+        },
+        {
+            "allow_on_submit": 1,
+            "default": "0",
+            "fieldname": "page_break",
+            "fieldtype": "Check",
+            "label": "Page Break",
+            "print_hide": 1
+        },
+        {
+            "depends_on": "returned_qty",
+            "fieldname": "returned_qty",
+            "fieldtype": "Float",
+            "label": "Returned Qty",
+            "no_copy": 1,
+            "print_hide": 1,
+            "read_only": 1
+        }
+    ],
+    "idx": 1,
+    "istable": 1,
+    "links": [],
+    "modified": "2022-04-21 12:07:55.899701",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Receipt Item",
+    "naming_rule": "Random",
+    "owner": "Administrator",
+    "permissions": [],
+    "quick_entry": 1,
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "states": []
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py
new file mode 100644
index 0000000..374f95b
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SubcontractingReceiptItem(Document):
+	pass
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/__init__.py b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/__init__.py
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
new file mode 100644
index 0000000..100a806
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
@@ -0,0 +1,198 @@
+{
+    "actions": [],
+    "creation": "2022-04-18 10:45:16.538479",
+    "doctype": "DocType",
+    "editable_grid": 1,
+    "engine": "InnoDB",
+    "field_order": [
+        "main_item_code",
+        "rm_item_code",
+        "item_name",
+        "bom_detail_no",
+        "col_break1",
+        "description",
+        "stock_uom",
+        "conversion_factor",
+        "reference_name",
+        "secbreak_1",
+        "rate",
+        "col_break2",
+        "amount",
+        "secbreak_2",
+        "required_qty",
+        "col_break3",
+        "consumed_qty",
+        "current_stock",
+        "secbreak_3",
+        "batch_no",
+        "col_break4",
+        "serial_no",
+        "subcontracting_order"
+    ],
+    "fields": [
+        {
+            "fieldname": "main_item_code",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Item Code",
+            "options": "Item",
+            "read_only": 1
+        },
+        {
+            "fieldname": "rm_item_code",
+            "fieldtype": "Link",
+            "in_list_view": 1,
+            "label": "Raw Material Item Code",
+            "options": "Item",
+            "read_only": 1
+        },
+        {
+            "fieldname": "description",
+            "fieldtype": "Text Editor",
+            "in_global_search": 1,
+            "label": "Description",
+            "print_width": "300px",
+            "read_only": 1,
+            "width": "300px"
+        },
+        {
+            "fieldname": "batch_no",
+            "fieldtype": "Link",
+            "label": "Batch No",
+            "no_copy": 1,
+            "options": "Batch"
+        },
+        {
+            "fieldname": "serial_no",
+            "fieldtype": "Text",
+            "label": "Serial No",
+            "no_copy": 1
+        },
+        {
+            "fieldname": "col_break1",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "required_qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Available Qty For Consumption",
+            "print_hide": 1,
+            "read_only": 1
+        },
+        {
+            "columns": 2,
+            "fieldname": "consumed_qty",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Qty to be Consumed",
+            "reqd": 1
+        },
+        {
+            "fieldname": "stock_uom",
+            "fieldtype": "Link",
+            "label": "Stock Uom",
+            "options": "UOM",
+            "read_only": 1
+        },
+        {
+            "fieldname": "rate",
+            "fieldtype": "Currency",
+            "label": "Rate",
+            "options": "Company:company:default_currency",
+            "read_only": 1
+        },
+        {
+            "fieldname": "amount",
+            "fieldtype": "Currency",
+            "label": "Amount",
+            "options": "Company:company:default_currency",
+            "read_only": 1
+        },
+        {
+            "default": "1",
+            "fieldname": "conversion_factor",
+            "fieldtype": "Float",
+            "hidden": 1,
+            "label": "Conversion Factor",
+            "read_only": 1
+        },
+        {
+            "fieldname": "current_stock",
+            "fieldtype": "Float",
+            "in_list_view": 1,
+            "label": "Current Stock",
+            "read_only": 1
+        },
+        {
+            "fieldname": "reference_name",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "in_list_view": 1,
+            "label": "Reference Name",
+            "read_only": 1
+        },
+        {
+            "fieldname": "bom_detail_no",
+            "fieldtype": "Data",
+            "hidden": 1,
+            "in_list_view": 1,
+            "label": "BOM Detail No",
+            "read_only": 1
+        },
+        {
+            "fieldname": "secbreak_1",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "col_break2",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "secbreak_2",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "col_break3",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "secbreak_3",
+            "fieldtype": "Section Break"
+        },
+        {
+            "fieldname": "col_break4",
+            "fieldtype": "Column Break"
+        },
+        {
+            "fieldname": "item_name",
+            "fieldtype": "Data",
+            "label": "Item Name",
+            "read_only": 1
+        },
+        {
+            "fieldname": "subcontracting_order",
+            "fieldtype": "Link",
+            "hidden": 1,
+            "label": "Subcontracting Order",
+            "no_copy": 1,
+            "options": "Subcontracting Order",
+            "print_hide": 1,
+            "read_only": 1
+        }
+    ],
+    "idx": 1,
+    "istable": 1,
+    "links": [],
+    "modified": "2022-04-18 10:45:16.538479",
+    "modified_by": "Administrator",
+    "module": "Subcontracting",
+    "name": "Subcontracting Receipt Supplied Item",
+    "naming_rule": "Autoincrement",
+    "owner": "Administrator",
+    "permissions": [],
+    "sort_field": "modified",
+    "sort_order": "DESC",
+    "track_changes": 1,
+    "states": []
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py
new file mode 100644
index 0000000..f4d2805
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class SubcontractingReceiptSuppliedItem(Document):
+	pass
diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py
deleted file mode 100644
index 93d1c8e..0000000
--- a/erpnext/tests/test_subcontracting.py
+++ /dev/null
@@ -1,1114 +0,0 @@
-import copy
-import unittest
-from collections import defaultdict
-
-import frappe
-from frappe.utils import cint
-
-from erpnext.buying.doctype.purchase_order.purchase_order import (
-	get_materials_from_supplier,
-	make_purchase_invoice,
-	make_purchase_receipt,
-	make_rm_stock_entry,
-)
-from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-
-
-class TestSubcontracting(unittest.TestCase):
-	def setUp(self):
-		make_subcontract_items()
-		make_raw_materials()
-		make_bom_for_subcontracted_items()
-
-	def test_po_with_bom(self):
-		"""
-		- Set backflush based on BOM
-		- Create subcontracted PO for the item Subcontracted Item SA1 and add same item two times.
-		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-		- Create purchase receipt against the PO and check serial nos and batch no.
-		"""
-
-		set_backflush_based_on("BOM")
-		item_code = "Subcontracted Item SA1"
-		items = [
-			{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 5, "rate": 100},
-			{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 6, "rate": 100},
-		]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 5},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 5},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 5},
-			{"item_code": "Subcontracted SRM Item 1", "qty": 6},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 6},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 6},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			transferred_detais = itemwise_details.get(key)
-
-			for field in ["qty", "serial_no", "batch_no"]:
-				if value.get(field):
-					transfer, consumed = (transferred_detais.get(field), value.get(field))
-					if field == "serial_no":
-						transfer, consumed = (sorted(transfer), sorted(consumed))
-
-					self.assertEqual(transfer, consumed)
-
-	def test_po_with_material_transfer(self):
-		"""
-		- Set backflush based on Material Transfer
-		- Create subcontracted PO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
-		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-		- Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
-		- Create partial purchase receipt against the PO and check serial nos and batch no.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		items = [
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA1",
-				"qty": 5,
-				"rate": 100,
-			},
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA5",
-				"qty": 6,
-				"rate": 100,
-			},
-		]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
-			{"item_code": "Subcontracted SRM Item 5", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
-			{"item_code": "Subcontracted SRM Item 4", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
-
-		make_stock_transfer_entry(
-			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.remove(pr1.items[1])
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			transferred_detais = itemwise_details.get(key)
-
-			for field in ["qty", "serial_no", "batch_no"]:
-				if value.get(field):
-					self.assertEqual(value.get(field), transferred_detais.get(field))
-
-		pr2 = make_purchase_receipt(po.name)
-		pr2.submit()
-
-		for key, value in get_supplied_items(pr2).items():
-			transferred_detais = itemwise_details.get(key)
-
-			for field in ["qty", "serial_no", "batch_no"]:
-				if value.get(field):
-					self.assertEqual(value.get(field), transferred_detais.get(field))
-
-	def test_subcontract_with_same_components_different_fg(self):
-		"""
-		- Set backflush based on Material Transfer
-		- Create subcontracted PO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
-		- Transfer the components from Stores to Supplier warehouse with serial nos.
-		- Transfer extra qty of components for the item Subcontracted Item SA2.
-		- Create partial purchase receipt against the PO and check serial nos and batch no.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		items = [
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA2",
-				"qty": 5,
-				"rate": 100,
-			},
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA3",
-				"qty": 6,
-				"rate": 100,
-			},
-		]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA2"},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA3"},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
-
-		make_stock_transfer_entry(
-			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 3
-		pr1.remove(pr1.items[1])
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			transferred_detais = itemwise_details.get(key)
-			self.assertEqual(value.qty, 4)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:4]))
-
-		pr2 = make_purchase_receipt(po.name)
-		pr2.items[0].qty = 2
-		pr2.remove(pr2.items[1])
-		pr2.submit()
-
-		for key, value in get_supplied_items(pr2).items():
-			transferred_detais = itemwise_details.get(key)
-
-			self.assertEqual(value.qty, 2)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[4:6]))
-
-		pr3 = make_purchase_receipt(po.name)
-		pr3.submit()
-		for key, value in get_supplied_items(pr3).items():
-			transferred_detais = itemwise_details.get(key)
-
-			self.assertEqual(value.qty, 6)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12]))
-
-	def test_return_non_consumed_materials(self):
-		"""
-		- Set backflush based on Material Transfer
-		- Create subcontracted PO for the item Subcontracted Item SA2.
-		- Transfer the components from Stores to Supplier warehouse with serial nos.
-		- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
-		- Create purchase receipt for full qty against the PO and change the qty of raw material.
-		- After that return the non consumed material back to the store from supplier's warehouse.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		items = [
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA2",
-				"qty": 5,
-				"rate": 100,
-			}
-		]
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 2", "qty": 6, "main_item_code": "Subcontracted Item SA2"}
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.save()
-		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].serial_no = "\n".join(
-			sorted(itemwise_details.get("Subcontracted SRM Item 2").get("serial_no")[0:5])
-		)
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			transferred_detais = itemwise_details.get(key)
-			self.assertEqual(value.qty, 5)
-			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
-
-		po.load_from_db()
-		self.assertEqual(po.supplied_items[0].consumed_qty, 5)
-		doc = get_materials_from_supplier(po.name, [d.name for d in po.supplied_items])
-		self.assertEqual(doc.items[0].qty, 1)
-		self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
-		self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
-		self.assertEqual(
-			get_serial_nos(doc.items[0].serial_no),
-			itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
-		)
-
-	def test_item_with_batch_based_on_bom(self):
-		"""
-		- Set backflush based on BOM
-		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-		- Transfer the components in multiple batches.
-		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-		"""
-
-		set_backflush_based_on("BOM")
-		item_code = "Subcontracted Item SA4"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 1},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 2
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 4)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 2
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 4)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 2
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 2)
-
-	def test_item_with_batch_based_on_material_transfer(self):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-		- Transfer the components in multiple batches with extra 2 qty for the batched item.
-		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-		- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA4"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 2
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			qty = 4 if key != "Subcontracted SRM Item 3" else 6
-			self.assertEqual(value.qty, qty)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 2
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 4)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 2
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 2)
-
-	def test_partial_transfer_serial_no_components_based_on_material_transfer(self):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA2.
-		- Transfer the partial components from Stores to Supplier warehouse with serial nos.
-		- Create partial purchase receipt against the PO and change the qty manually.
-		- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
-		- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA2"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 5}]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 5
-		pr1.save()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, 3)
-			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
-
-		pr1.load_from_db()
-		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].serial_no = "\n".join(
-			itemwise_details[pr1.supplied_items[0].rm_item_code]["serial_no"]
-		)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
-
-	def test_incorrect_serial_no_components_based_on_material_transfer(self):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA2.
-		- Transfer the serialized componenets to the supplier.
-		- Create purchase receipt and change the serial no which is not transferred.
-		- System should throw the error and not allowed to save the purchase receipt.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA2"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 10}]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.save()
-		pr1.supplied_items[0].serial_no = "ABCD"
-		self.assertRaises(frappe.ValidationError, pr1.save)
-		pr1.delete()
-
-	def test_partial_transfer_batch_based_on_material_transfer(self):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA6.
-		- Transfer the partial components from Stores to Supplier warehouse with batch.
-		- Create partial purchase receipt against the PO and change the qty manually.
-		- Transfer the remaining components from Stores to Supplier warehouse with batch.
-		- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA6"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [{"item_code": "Subcontracted SRM Item 3", "qty": 5}]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.items[0].qty = 5
-		pr1.save()
-
-		transferred_batch_no = ""
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, 3)
-			transferred_batch_no = details.batch_no
-			self.assertEqual(value.batch_no, details.batch_no)
-
-		pr1.load_from_db()
-		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(value.batch_no, details.batch_no)
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(value.batch_no, details.batch_no)
-
-	def test_item_with_batch_based_on_material_transfer_for_purchase_invoice(self):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-		- Transfer the components in multiple batches with extra 2 qty for the batched item.
-		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-		- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA4"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			qty = 4 if key != "Subcontracted SRM Item 3" else 6
-			self.assertEqual(value.qty, qty)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.items[0].qty = 2
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 4)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 2)
-
-	def test_partial_transfer_serial_no_components_based_on_material_transfer_for_purchase_invoice(
-		self,
-	):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA2.
-		- Transfer the partial components from Stores to Supplier warehouse with serial nos.
-		- Create partial purchase receipt against the PO and change the qty manually.
-		- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
-		- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA2"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [{"item_code": "Subcontracted SRM Item 2", "qty": 5}]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 5
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.save()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, 3)
-			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
-
-		pr1.load_from_db()
-		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].serial_no = "\n".join(
-			itemwise_details[pr1.supplied_items[0].rm_item_code]["serial_no"]
-		)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
-
-	def test_partial_transfer_batch_based_on_material_transfer_for_purchase_invoice(self):
-		"""
-		- Set backflush based on Material Transferred for Subcontract
-		- Create subcontracted PO for the item Subcontracted Item SA6.
-		- Transfer the partial components from Stores to Supplier warehouse with batch.
-		- Create partial purchase receipt against the PO and change the qty manually.
-		- Transfer the remaining components from Stores to Supplier warehouse with batch.
-		- Create purchase receipt for remaining qty against the PO and change the qty manually.
-		"""
-
-		set_backflush_based_on("Material Transferred for Subcontract")
-		item_code = "Subcontracted Item SA6"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [{"item_code": "Subcontracted SRM Item 3", "qty": 5}]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 5
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.save()
-
-		transferred_batch_no = ""
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, 3)
-			transferred_batch_no = details.batch_no
-			self.assertEqual(value.batch_no, details.batch_no)
-
-		pr1.load_from_db()
-		pr1.supplied_items[0].consumed_qty = 5
-		pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(value.batch_no, details.batch_no)
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			details = itemwise_details.get(key)
-			self.assertEqual(value.qty, details.qty)
-			self.assertEqual(value.batch_no, details.batch_no)
-
-	def test_item_with_batch_based_on_bom_for_purchase_invoice(self):
-		"""
-		- Set backflush based on BOM
-		- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
-		- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
-		- Transfer the components in multiple batches.
-		- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
-		- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
-		"""
-
-		set_backflush_based_on("BOM")
-		item_code = "Subcontracted Item SA4"
-		items = [{"warehouse": "_Test Warehouse - _TC", "item_code": item_code, "qty": 10, "rate": 100}]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 10},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 3},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 1},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name
-
-		make_stock_transfer_entry(
-			po_no=po.name,
-			main_item_code=item_code,
-			rm_items=rm_items,
-			itemwise_details=copy.deepcopy(itemwise_details),
-		)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 4)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		add_second_row_in_pr(pr1)
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 4)
-
-		pr1 = make_purchase_invoice(po.name)
-		pr1.update_stock = 1
-		pr1.items[0].qty = 2
-		pr1.items[0].expense_account = "Stock Adjustment - _TC"
-		pr1.save()
-		pr1.submit()
-
-		for key, value in get_supplied_items(pr1).items():
-			self.assertEqual(value.qty, 2)
-
-	def test_po_supplied_qty(self):
-		"""
-		Check if 'Supplied Qty' in PO's Supplied Items table is reset on submit/cancel.
-		"""
-		set_backflush_based_on("Material Transferred for Subcontract")
-		items = [
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA1",
-				"qty": 5,
-				"rate": 100,
-			},
-			{
-				"warehouse": "_Test Warehouse - _TC",
-				"item_code": "Subcontracted Item SA5",
-				"qty": 6,
-				"rate": 100,
-			},
-		]
-
-		rm_items = [
-			{"item_code": "Subcontracted SRM Item 1", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
-			{"item_code": "Subcontracted SRM Item 2", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
-			{"item_code": "Subcontracted SRM Item 3", "qty": 5, "main_item_code": "Subcontracted Item SA1"},
-			{"item_code": "Subcontracted SRM Item 5", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
-			{"item_code": "Subcontracted SRM Item 4", "qty": 6, "main_item_code": "Subcontracted Item SA5"},
-		]
-
-		itemwise_details = make_stock_in_entry(rm_items=rm_items)
-		po = create_purchase_order(
-			rm_items=items, is_subcontracted=1, supplier_warehouse="_Test Warehouse 1 - _TC"
-		)
-
-		for d in rm_items:
-			d["po_detail"] = po.items[0].name if d.get("qty") == 5 else po.items[1].name
-
-		se = make_stock_transfer_entry(
-			po_no=po.name, rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details)
-		)
-
-		po.reload()
-		for row in po.get("supplied_items"):
-			self.assertIn(row.supplied_qty, [5.0, 6.0])
-
-		se.cancel()
-		po.reload()
-		for row in po.get("supplied_items"):
-			self.assertEqual(row.supplied_qty, 0.0)
-
-
-def add_second_row_in_pr(pr):
-	item_dict = {}
-	for column in [
-		"item_code",
-		"item_name",
-		"qty",
-		"uom",
-		"warehouse",
-		"stock_uom",
-		"purchase_order",
-		"purchase_order_item",
-		"conversion_factor",
-		"rate",
-		"expense_account",
-		"po_detail",
-	]:
-		item_dict[column] = pr.items[0].get(column)
-
-	pr.append("items", item_dict)
-	pr.set_missing_values()
-
-
-def get_supplied_items(pr_doc):
-	supplied_items = {}
-	for row in pr_doc.get("supplied_items"):
-		if row.rm_item_code not in supplied_items:
-			supplied_items.setdefault(
-				row.rm_item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
-			)
-
-		details = supplied_items[row.rm_item_code]
-		update_item_details(row, details)
-
-	return supplied_items
-
-
-def make_stock_in_entry(**args):
-	args = frappe._dict(args)
-
-	items = {}
-	for row in args.rm_items:
-		row = frappe._dict(row)
-
-		doc = make_stock_entry(
-			target=row.warehouse or "_Test Warehouse - _TC",
-			item_code=row.item_code,
-			qty=row.qty or 1,
-			basic_rate=row.rate or 100,
-		)
-
-		if row.item_code not in items:
-			items.setdefault(
-				row.item_code, frappe._dict({"qty": 0, "serial_no": [], "batch_no": defaultdict(float)})
-			)
-
-		child_row = doc.items[0]
-		details = items[child_row.item_code]
-		update_item_details(child_row, details)
-
-	return items
-
-
-def update_item_details(child_row, details):
-	details.qty += (
-		child_row.get("qty")
-		if child_row.doctype == "Stock Entry Detail"
-		else child_row.get("consumed_qty")
-	)
-
-	if child_row.serial_no:
-		details.serial_no.extend(get_serial_nos(child_row.serial_no))
-
-	if child_row.batch_no:
-		details.batch_no[child_row.batch_no] += child_row.get("qty") or child_row.get("consumed_qty")
-
-
-def make_stock_transfer_entry(**args):
-	args = frappe._dict(args)
-
-	items = []
-	for row in args.rm_items:
-		row = frappe._dict(row)
-
-		item = {
-			"item_code": row.main_item_code or args.main_item_code,
-			"rm_item_code": row.item_code,
-			"qty": row.qty or 1,
-			"item_name": row.item_code,
-			"rate": row.rate or 100,
-			"stock_uom": row.stock_uom or "Nos",
-			"warehouse": row.warehuose or "_Test Warehouse - _TC",
-		}
-
-		item_details = args.itemwise_details.get(row.item_code)
-
-		if item_details and item_details.serial_no:
-			serial_nos = item_details.serial_no[0 : cint(row.qty)]
-			item["serial_no"] = "\n".join(serial_nos)
-			item_details.serial_no = list(set(item_details.serial_no) - set(serial_nos))
-
-		if item_details and item_details.batch_no:
-			for batch_no, batch_qty in item_details.batch_no.items():
-				if batch_qty >= row.qty:
-					item["batch_no"] = batch_no
-					item_details.batch_no[batch_no] -= row.qty
-					break
-
-		items.append(item)
-
-	ste_dict = make_rm_stock_entry(args.po_no, items)
-	doc = frappe.get_doc(ste_dict)
-	doc.insert()
-	doc.submit()
-
-	return doc
-
-
-def make_subcontract_items():
-	sub_contracted_items = {
-		"Subcontracted Item SA1": {},
-		"Subcontracted Item SA2": {},
-		"Subcontracted Item SA3": {},
-		"Subcontracted Item SA4": {
-			"has_batch_no": 1,
-			"create_new_batch": 1,
-			"batch_number_series": "SBAT.####",
-		},
-		"Subcontracted Item SA5": {},
-		"Subcontracted Item SA6": {},
-	}
-
-	for item, properties in sub_contracted_items.items():
-		if not frappe.db.exists("Item", item):
-			properties.update({"is_stock_item": 1, "is_sub_contracted_item": 1})
-			make_item(item, properties)
-
-
-def make_raw_materials():
-	raw_materials = {
-		"Subcontracted SRM Item 1": {},
-		"Subcontracted SRM Item 2": {"has_serial_no": 1, "serial_no_series": "SRI.####"},
-		"Subcontracted SRM Item 3": {
-			"has_batch_no": 1,
-			"create_new_batch": 1,
-			"batch_number_series": "BAT.####",
-		},
-		"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
-		"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
-	}
-
-	for item, properties in raw_materials.items():
-		if not frappe.db.exists("Item", item):
-			properties.update({"is_stock_item": 1})
-			make_item(item, properties)
-
-
-def make_bom_for_subcontracted_items():
-	boms = {
-		"Subcontracted Item SA1": [
-			"Subcontracted SRM Item 1",
-			"Subcontracted SRM Item 2",
-			"Subcontracted SRM Item 3",
-		],
-		"Subcontracted Item SA2": ["Subcontracted SRM Item 2"],
-		"Subcontracted Item SA3": ["Subcontracted SRM Item 2"],
-		"Subcontracted Item SA4": [
-			"Subcontracted SRM Item 1",
-			"Subcontracted SRM Item 2",
-			"Subcontracted SRM Item 3",
-		],
-		"Subcontracted Item SA5": ["Subcontracted SRM Item 5"],
-		"Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
-	}
-
-	for item_code, raw_materials in boms.items():
-		if not frappe.db.exists("BOM", {"item": item_code}):
-			make_bom(item=item_code, raw_materials=raw_materials, rate=100)
-
-
-def set_backflush_based_on(based_on):
-	frappe.db.set_value(
-		"Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", based_on
-	)