feat: New DocType "Subcontracting Order"
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index b393737..4e0d911 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -627,6 +627,17 @@
 
 		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_subcontracting_orders
+
+			if self.subcontracting_orders:
+				for sco in set(self.subcontracting_orders):
+					sco_doc = frappe.get_doc("Subcontracting Order", sco)
+					sco_doc.update_status()
+
 	@property
 	def sub_contracted_items(self):
 		if not hasattr(self, "_sub_contracted_items"):
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index b851795..5adb8b2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -136,6 +136,7 @@
 		self.update_work_order()
 		self.validate_subcontracting_order()
 		self.update_subcontracting_order_supplied_items()
+		self.update_subcontracting_order_status()
 
 		self.make_gl_entries()
 
@@ -155,6 +156,7 @@
 
 	def on_cancel(self):
 		self.update_subcontracting_order_supplied_items()
+		self.update_subcontracting_order_status()
 
 		if self.work_order and self.purpose == "Material Consumption for Manufacture":
 			self.validate_work_order_status()
@@ -2212,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)
+
 
 @frappe.whitelist()
 def move_sample_to_retention_warehouse(company, items):
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..80fe944
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
@@ -0,0 +1,322 @@
+// 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: "Yes"
+				}
+			};
+		});
+
+		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.subcontracting.doctype.subcontracting_order.subcontracting_order.get_materials_from_supplier',
+					freeze: true,
+					freeze_message: __('Creating Stock Entry'),
+					args: { subcontracting_order: frm.doc.name, sco_rm_details: sco_rm_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'));
+		}
+	}
+});
+
+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.subcontracting.doctype.subcontracting_order.subcontracting_order.make_rm_stock_entry',
+			args: {
+				subcontracting_order: cur_frm.doc.name,
+				rm_items: rm_items
+			},
+			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..d12c9e8
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
@@ -0,0 +1,372 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import json
+
+import frappe
+from frappe import _
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import flt
+
+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:
+			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.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"
+			elif self.docstatus == 2:
+				status = "Cancelled"
+
+		if status:
+			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
+
+
+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(subcontracting_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(item["item_code"] for item in rm_items_list))
+	else:
+		frappe.throw(_("No Items selected for transfer"))
+
+	if subcontracting_order:
+		subcontracting_order = frappe.get_doc("Subcontracting Order", subcontracting_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"
+		stock_entry.subcontracting_order = subcontracting_order.name
+		stock_entry.supplier = subcontracting_order.supplier
+		stock_entry.supplier_name = subcontracting_order.supplier_name
+		stock_entry.supplier_address = subcontracting_order.supplier_address
+		stock_entry.address_display = subcontracting_order.address_display
+		stock_entry.company = subcontracting_order.company
+		stock_entry.to_warehouse = subcontracting_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: {
+							"sco_rm_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)
+		return stock_entry.as_dict()
+	else:
+		frappe.throw(_("No Items selected for transfer"))
+	return subcontracting_order.name
+
+
+def add_items_in_ste(ste_doc, row, qty, sco_rm_details, batch_no=None):
+	item = ste_doc.append("items", row.item_details)
+
+	sco_rm_detail = list(set(row.sco_rm_details).intersection(sco_rm_details))
+	item.update(
+		{
+			"qty": qty,
+			"batch_no": batch_no,
+			"basic_rate": row.item_details["rate"],
+			"sco_rm_detail": sco_rm_detail[0] if sco_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, sco_doc, sco_rm_details):
+	ste_doc = frappe.new_doc("Stock Entry")
+	ste_doc.purpose = "Material Transfer"
+
+	ste_doc.subcontracting_order = sco_doc.name
+	ste_doc.company = sco_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, sco_rm_details, batch_no)
+		else:
+			add_items_in_ste(ste_doc, value, value.qty, sco_rm_details)
+
+	ste_doc.set_stock_entry_type()
+	ste_doc.calculate_rate_and_amount()
+
+	return ste_doc
+
+
+@frappe.whitelist()
+def get_materials_from_supplier(subcontracting_order, sco_rm_details):
+	if isinstance(sco_rm_details, str):
+		sco_rm_details = json.loads(sco_rm_details)
+
+	doc = frappe.get_cached_doc("Subcontracting Order", subcontracting_order)
+	doc.initialized_fields()
+	doc.subcontracting_orders = [doc.name]
+	doc.get_available_materials()
+
+	if not doc.available_materials:
+		frappe.throw(
+			_("Materials are already received against the Subcontracting Order {0}").format(
+				subcontracting_order
+			)
+		)
+
+	return make_return_stock_entry_for_subcontract(doc.available_materials, doc, sco_rm_details)
+
+
+@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..a2b7245
--- /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..f58c830
--- /dev/null
+++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+class TestSubcontractingOrder(FrappeTestCase):
+	pass
\ No newline at end of file