Merge branch 'develop' into prod-plan-status
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 2c15144..c801cfc 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -101,7 +101,7 @@
 				return
 			if not frappe.db.get_value("Account",
 				{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
-				frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
+				frappe.throw(_("Please add the account to root level Company - {}").format(ancestors[0]))
 		elif self.parent_account:
 			descendants = get_descendants_of('Company', self.company)
 			if not descendants: return
@@ -164,9 +164,19 @@
 
 	def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
 		for company in descendants:
+			company_bold = frappe.bold(company)
+			parent_acc_name_bold = frappe.bold(parent_acc_name)
 			if not parent_acc_name_map.get(company):
-				frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
-					.format(company, parent_acc_name))
+				frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
+					.format(company_bold, parent_acc_name_bold), title=_("Account Not Found"))
+
+			# validate if parent of child company account to be added is a group
+			if (frappe.db.get_value("Account", self.parent_account, "is_group")
+				and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")):
+				msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold)
+				msg += "<br><br>"
+				msg += _("Please convert the parent account in corresponding child company to a group account.")
+				frappe.throw(msg, title=_("Invalid Parent Account"))
 
 			filters = {
 				"account_name": self.account_name,
@@ -309,8 +319,9 @@
 				allow_child_account_creation = _("Allow Account Creation Against Child Company")
 
 				message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor))
-				message += "<br>" + _("Renaming it is only allowed via parent company {0}, \
-					to avoid mismatch.").format(frappe.bold(ancestor)) + "<br><br>"
+				message += "<br>"
+				message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(frappe.bold(ancestor))
+				message += "<br><br>"
 				message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company))
 
 				frappe.throw(message, title=_("Rename Not Allowed"))
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index b6a950b..0605d89 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -111,6 +111,17 @@
 		self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
 		self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
 
+	def test_add_account_to_a_group(self):
+		frappe.db.set_value("Account", "Office Rent - _TC3", "is_group", 1)
+
+		acc = frappe.new_doc("Account")
+		acc.account_name = "Test Group Account"
+		acc.parent_account = "Office Rent - _TC3"
+		acc.company = "_Test Company 3"
+		self.assertRaises(frappe.ValidationError, acc.insert)
+
+		frappe.db.set_value("Account", "Office Rent - _TC3", "is_group", 0)
+
 	def test_account_rename_sync(self):
 		frappe.local.flags.pop("ignore_root_company_validation", None)
 
@@ -160,6 +171,7 @@
 		for doc in to_delete:
 			frappe.delete_doc("Account", doc)
 
+
 def _make_test_records(verbose):
 	from frappe.test_runner import make_test_objects
 
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
index 3c12f85..9a6c389 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -7,7 +7,7 @@
 		frm.set_query('document_type', () => {
 			let invalid_doctypes = frappe.model.core_doctypes_list;
 			invalid_doctypes.push('Accounting Dimension', 'Project',
-				'Cost Center', 'Accounting Dimension Detail');
+				'Cost Center', 'Accounting Dimension Detail', 'Company');
 
 			return {
 				filters: {
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 8834385..f888d9e 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -19,7 +19,7 @@
 
 	def validate(self):
 		if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
-				'Cost Center', 'Accounting Dimension Detail') :
+				'Cost Center', 'Accounting Dimension Detail', 'Company') :
 
 			msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
 			frappe.throw(msg)
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index 0bcde64..52f7fe2 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -63,6 +63,7 @@
 		FROM
 			`tabPOS Invoice` p {from_sales_invoice_payment}
 		WHERE
+			p.docstatus = 1 and
 			{group_by_mop_condition}
 			{conditions}
 		ORDER BY
diff --git a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json
index bc2edc9..94debf1 100644
--- a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json
+++ b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json
@@ -9,9 +9,9 @@
  "filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}",
  "group_by_type": "Count",
  "idx": 0,
- "is_public": 0,
+ "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-23 13:53:33.211371",
+ "modified": "2020-10-28 23:15:58.432189",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Value Analytics",
diff --git a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json
index e79d2d7..78611da 100644
--- a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json
+++ b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json
@@ -8,9 +8,9 @@
  "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
  "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}",
  "idx": 0,
- "is_public": 0,
+ "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-23 13:39:32.429240",
+ "modified": "2020-10-28 23:16:16.939070",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Category-wise Asset Value",
diff --git a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json
index 481586e..848184c 100644
--- a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json
+++ b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json
@@ -8,9 +8,9 @@
  "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
  "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}",
  "idx": 0,
- "is_public": 0,
+ "is_public": 1,
  "is_standard": 1,
- "modified": "2020-07-23 13:42:44.912551",
+ "modified": "2020-10-28 23:16:07.883312",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Location-wise Asset Value",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 0cf3d24..0181ae7 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -19,6 +19,8 @@
 from erpnext.controllers.status_updater import OverAllowanceError
 from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
 
+from erpnext.stock.doctype.batch.test_batch import make_new_batch
+from erpnext.controllers.buying_controller import get_backflushed_subcontracted_raw_materials
 
 class TestPurchaseOrder(unittest.TestCase):
 	def test_make_purchase_receipt(self):
@@ -686,7 +688,7 @@
 
 	def test_exploded_items_in_subcontracted(self):
 		item_code = "_Test Subcontracted FG Item 1"
-		make_subcontracted_item(item_code)
+		make_subcontracted_item(item_code=item_code)
 
 		po = create_purchase_order(item_code=item_code, qty=1,
 			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=1)
@@ -708,7 +710,7 @@
 
 	def test_backflush_based_on_stock_entry(self):
 		item_code = "_Test Subcontracted FG Item 1"
-		make_subcontracted_item(item_code)
+		make_subcontracted_item(item_code=item_code)
 		make_item('Sub Contracted Raw Material 1', {
 			'is_stock_item': 1,
 			'is_sub_contracted_item': 1
@@ -767,6 +769,129 @@
 
 		update_backflush_based_on("BOM")
 
+	def test_backflushed_based_on_for_multiple_batches(self):
+		item_code = "_Test Subcontracted FG Item 2"
+		make_item('Sub Contracted Raw Material 2', {
+			'is_stock_item': 1,
+			'is_sub_contracted_item': 1
+		})
+
+		make_subcontracted_item(item_code=item_code, has_batch_no=1, create_new_batch=1,
+			raw_materials=["Sub Contracted Raw Material 2"])
+
+		update_backflush_based_on("Material Transferred for Subcontract")
+
+		order_qty = 500
+		po = create_purchase_order(item_code=item_code, qty=order_qty,
+			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		make_stock_entry(target="_Test Warehouse - _TC",
+			item_code = "Sub Contracted Raw Material 2", qty=552, basic_rate=100)
+
+		rm_items = [
+			{"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 2","item_name":"_Test Item",
+				"qty":552,"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.submit()
+
+		for batch in ["ABCD1", "ABCD2", "ABCD3", "ABCD4"]:
+			make_new_batch(batch_id=batch, item_code=item_code)
+
+		pr = make_purchase_receipt(po.name)
+
+		# partial receipt
+		pr.get('items')[0].qty = 30
+		pr.get('items')[0].batch_no = "ABCD1"
+
+		purchase_order = po.name
+		purchase_order_item = po.items[0].name
+
+		for batch_no, qty in {"ABCD2": 60, "ABCD3": 70, "ABCD4":40}.items():
+			pr.append("items", {
+				"item_code": pr.get('items')[0].item_code,
+				"item_name": pr.get('items')[0].item_name,
+				"uom": pr.get('items')[0].uom,
+				"stock_uom": pr.get('items')[0].stock_uom,
+				"warehouse": pr.get('items')[0].warehouse,
+				"conversion_factor": pr.get('items')[0].conversion_factor,
+				"cost_center": pr.get('items')[0].cost_center,
+				"rate": pr.get('items')[0].rate,
+				"qty": qty,
+				"batch_no": batch_no,
+				"purchase_order": purchase_order,
+				"purchase_order_item": purchase_order_item
+			})
+
+		pr.submit()
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.get('items')[0].qty = 300
+		pr1.get('items')[0].batch_no = "ABCD1"
+		pr1.save()
+
+		pr_key = ("Sub Contracted Raw Material 2", po.name)
+		consumed_qty = get_backflushed_subcontracted_raw_materials([po.name]).get(pr_key)
+
+		self.assertTrue(pr1.supplied_items[0].consumed_qty > 0)
+		self.assertTrue(pr1.supplied_items[0].consumed_qty,  flt(552.0) - flt(consumed_qty))
+
+		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="Yes", 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", "name": po.supplied_items[1].name
+			},
+		]
+
+		# 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()
+
+		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
 		frappe.db.set_value("Accounts Settings", "Accounts Settings",
@@ -839,27 +964,33 @@
 	pr.submit()
 	return pr
 
-def make_subcontracted_item(item_code):
+def make_subcontracted_item(**args):
 	from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 
-	if not frappe.db.exists('Item', item_code):
-		make_item(item_code, {
+	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
+			'is_sub_contracted_item': 1,
+			'has_batch_no': args.get("has_batch_no") or 0
 		})
 
-	if not frappe.db.exists('Item', "Test Extra Item 1"):
-		make_item("Test Extra Item 1", {
-			'is_stock_item': 1,
-		})
+	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,
-		})
+		if not frappe.db.exists('Item', "Test Extra Item 2"):
+			make_item("Test Extra Item 2", {
+				'is_stock_item': 1,
+			})
 
-	if not frappe.db.get_value('BOM', {'item': item_code}, 'name'):
-		make_bom(item = item_code, raw_materials = ['_Test FG Item', 'Test Extra 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')
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index e05c70e..f376836 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _, msgprint
 from frappe.utils import flt,cint, cstr, getdate
-
+from six import iteritems
 from erpnext.accounts.party import get_party_details
 from erpnext.stock.get_item_details import get_conversion_factor
 from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
@@ -112,8 +112,8 @@
 			"docstatus": 1
 		})]
 		if self.is_return and len(not_cancelled_asset):
-			frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.".format(self.return_against)),
-				title=_("Not Allowed"))
+			frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.")
+				.format(self.return_against), title=_("Not Allowed"))
 
 	def get_asset_items(self):
 		if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']:
@@ -298,10 +298,10 @@
 					title=_("Limit Crossed"))
 
 			transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
-			backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
+			# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
 
 			for raw_material in transferred_raw_materials + non_stock_items:
-				rm_item_key = '{}{}'.format(raw_material.rm_item_code, item.purchase_order)
+				rm_item_key = (raw_material.rm_item_code, item.purchase_order)
 				raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
 
 				consumed_qty = raw_material_data.get('qty', 0)
@@ -330,8 +330,10 @@
 					set_serial_nos(raw_material, consumed_serial_nos, qty)
 
 				if raw_material.batch_nos:
+					backflushed_batch_qty_map = raw_material_data.get('consumed_batch', {})
+
 					batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code,
-						qty, transferred_batch_qty_map, backflushed_batch_qty_map)
+						qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order)
 					for batch_data in batches_qty:
 						qty = batch_data['qty']
 						raw_material.batch_no = batch_data['batch']
@@ -343,6 +345,10 @@
 		rm = self.append('supplied_items', {})
 		rm.update(raw_material_data)
 
+		if not rm.main_item_code:
+			rm.main_item_code = fg_item_doc.item_code
+
+		rm.reference_name = fg_item_doc.name
 		rm.required_qty = qty
 		rm.consumed_qty = qty
 
@@ -792,8 +798,8 @@
 							asset.set(field, None)
 							asset.supplier = None
 						if asset.docstatus == 1 and delete_asset:
-							frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\
-								Please cancel the it to continue.').format(frappe.utils.get_link_to_form('Asset', asset.name)))
+							frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}. Please cancel it to continue.')
+								.format(frappe.utils.get_link_to_form('Asset', asset.name)))
 
 					asset.flags.ignore_validate_update_after_submit = True
 					asset.flags.ignore_mandatory = True
@@ -873,7 +879,7 @@
 			AND se.purpose='Send to Subcontractor'
 			AND se.purchase_order = %s
 			AND IFNULL(sed.t_warehouse, '') != ''
-			AND sed.subcontracted_item = %s
+			AND IFNULL(sed.subcontracted_item, '') in ('', %s)
 		GROUP BY sed.item_code, sed.subcontracted_item
 	"""
 	raw_materials = frappe.db.multisql({
@@ -890,39 +896,49 @@
 	return raw_materials
 
 def get_backflushed_subcontracted_raw_materials(purchase_orders):
-	common_query = """
-		SELECT
-			CONCAT(prsi.rm_item_code, pri.purchase_order) AS item_key,
-			SUM(prsi.consumed_qty) AS qty,
-			{serial_no_concat_syntax} AS serial_nos,
-			{batch_no_concat_syntax} AS batch_nos
-		FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` prsi
-		WHERE
-			pr.name = pri.parent
-			AND pr.name = prsi.parent
-			AND pri.purchase_order IN %s
-			AND pri.item_code = prsi.main_item_code
-			AND pr.docstatus = 1
-		GROUP BY prsi.rm_item_code, pri.purchase_order
-	"""
+	purchase_receipts = frappe.get_all("Purchase Receipt Item",
+		fields = ["purchase_order", "item_code", "name", "parent"],
+		filters={"docstatus": 1, "purchase_order": ("in", list(purchase_orders))})
 
-	backflushed_raw_materials = frappe.db.multisql({
-		'mariadb': common_query.format(
-			serial_no_concat_syntax="GROUP_CONCAT(prsi.serial_no)",
-			batch_no_concat_syntax="GROUP_CONCAT(prsi.batch_no)"
-		),
-		'postgres': common_query.format(
-			serial_no_concat_syntax="STRING_AGG(prsi.serial_no, ',')",
-			batch_no_concat_syntax="STRING_AGG(prsi.batch_no, ',')"
-		)
-	}, (purchase_orders, ), as_dict=1)
+	distinct_purchase_receipts = {}
+	for pr in purchase_receipts:
+		key = (pr.purchase_order, pr.item_code, pr.parent)
+		distinct_purchase_receipts.setdefault(key, []).append(pr.name)
 
 	backflushed_raw_materials_map = frappe._dict()
-	for item in backflushed_raw_materials:
-		backflushed_raw_materials_map.setdefault(item.item_key, item)
+	for args, references in iteritems(distinct_purchase_receipts):
+		purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
+
+		for data in purchase_receipt_supplied_items:
+			pr_key = (data.rm_item_code, args[0])
+			if pr_key not in backflushed_raw_materials_map:
+				backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
+					"qty": 0.0,
+					"serial_no": [],
+					"batch_no": [],
+					"consumed_batch": {}
+				}))
+
+			row = backflushed_raw_materials_map.get(pr_key)
+			row.qty += data.consumed_qty
+
+			for field in ["serial_no", "batch_no"]:
+				if data.get(field):
+					row[field].append(data.get(field))
+
+			if data.get("batch_no"):
+				if data.get("batch_no") in row.consumed_batch:
+					row.consumed_batch[data.get("batch_no")] += data.consumed_qty
+				else:
+					row.consumed_batch[data.get("batch_no")] = data.consumed_qty
 
 	return backflushed_raw_materials_map
 
+def get_supplied_items(item_code, purchase_receipt, references):
+	return frappe.get_all("Purchase Receipt Item Supplied",
+		fields=["rm_item_code", "consumed_qty", "serial_no", "batch_no"],
+		filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
+
 def get_asset_item_details(asset_items):
 	asset_items_data = {}
 	for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"],
@@ -1004,14 +1020,15 @@
 		SELECT
 			sed.batch_no,
 			SUM(sed.qty) AS qty,
-			sed.item_code
+			sed.item_code,
+			sed.subcontracted_item
 		FROM `tabStock Entry` se,`tabStock Entry Detail` sed
 		WHERE
 			se.name = sed.parent
 			AND se.docstatus=1
 			AND se.purpose='Send to Subcontractor'
 			AND se.purchase_order = %s
-			AND sed.subcontracted_item = %s
+			AND ifnull(sed.subcontracted_item, '') in ('', %s)
 			AND sed.batch_no IS NOT NULL
 		GROUP BY
 			sed.batch_no,
@@ -1019,8 +1036,10 @@
 	""", (purchase_order, fg_item), as_dict=1)
 
 	for batch_data in transferred_batches:
-		transferred_batch_qty_map.setdefault((batch_data.item_code, fg_item), {})
-		transferred_batch_qty_map[(batch_data.item_code, fg_item)][batch_data.batch_no] = batch_data.qty
+		key = ((batch_data.item_code, fg_item)
+			if batch_data.subcontracted_item else (batch_data.item_code, purchase_order))
+		transferred_batch_qty_map.setdefault(key, {})
+		transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty
 
 	return transferred_batch_qty_map
 
@@ -1057,10 +1076,11 @@
 
 	return backflushed_batch_qty_map
 
-def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batch_qty_map):
+def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batches, po):
 	# Returns available batches to be backflushed based on requirements
 	transferred_batches = transferred_batch_qty_map.get((item_code, fg_item), {})
-	backflushed_batches = backflushed_batch_qty_map.get((item_code, fg_item), {})
+	if not transferred_batches:
+		transferred_batches = transferred_batch_qty_map.get((item_code, po), {})
 
 	available_batches = []
 
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
index 5bd6596..96a6362 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -53,7 +53,7 @@
 	# renamed reports from "Minutes to First Response for Issues" to "First Response Time for Issues". Same for Opportunity
 	for report in ['Minutes to First Response for Issues', 'Minutes to First Response for Opportunity']:
 		if frappe.db.exists('Report', report):
-			frappe.delete_doc('Report', report)
+			frappe.delete_doc('Report', report, ignore_permissions=True)
 
 
 def convert_to_seconds(value, unit):
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 23705a8..02b1dc0 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -651,7 +651,7 @@
 					let child = frappe.model.add_child(me.frm.doc, "taxes");
 					child.charge_type = "On Net Total";
 					child.account_head = tax;
-					child.rate = rate;
+					child.rate = 0;
 				}
 			});
 		}
diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json
index 5ee7000..7a049b2 100644
--- a/erpnext/quality_management/desk_page/quality/quality.json
+++ b/erpnext/quality_management/desk_page/quality/quality.json
@@ -18,7 +18,7 @@
   {
    "hidden": 0,
    "label": "Review and Action",
-   "links": "[\n    {\n        \"description\": \"Quality Review\",\n        \"label\": \"Quality Review\",\n        \"name\": \"Quality Review\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Quality Action\",\n        \"label\": \"Quality Action\",\n        \"name\": \"Quality Action\",\n        \"type\": \"doctype\"\n    }\n]"
+   "links": "[\n    {\n        \"description\": \"Non Conformance\",\n        \"label\": \"Non Conformance\",\n        \"name\": \"Non Conformance\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Quality Review\",\n        \"label\": \"Quality Review\",\n        \"name\": \"Quality Review\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Quality Action\",\n        \"label\": \"Quality Action\",\n        \"name\": \"Quality Action\",\n        \"type\": \"doctype\"\n    }\n]"
   }
  ],
  "category": "Modules",
@@ -29,11 +29,11 @@
  "docstatus": 0,
  "doctype": "Desk Page",
  "extends_another_page": 0,
- "icon": "",
+ "hide_custom": 0,
  "idx": 0,
  "is_standard": 1,
  "label": "Quality",
- "modified": "2020-04-01 11:28:51.095012",
+ "modified": "2020-10-27 16:28:54.138055",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality",
@@ -47,6 +47,7 @@
    "type": "DocType"
   },
   {
+   "doc_view": "Tree",
    "label": "Quality Procedure",
    "link_to": "Quality Procedure",
    "type": "DocType"
@@ -55,6 +56,33 @@
    "label": "Quality Inspection",
    "link_to": "Quality Inspection",
    "type": "DocType"
+  },
+  {
+   "color": "#ff8989",
+   "doc_view": "",
+   "format": "{} Open",
+   "label": "Quality Review",
+   "link_to": "Quality Review",
+   "stats_filter": "{\"status\": \"Open\"}",
+   "type": "DocType"
+  },
+  {
+   "color": "#ff8989",
+   "doc_view": "",
+   "format": "{} Open",
+   "label": "Quality Action",
+   "link_to": "Quality Action",
+   "stats_filter": "{\"status\": \"Open\"}",
+   "type": "DocType"
+  },
+  {
+   "color": "#ff8989",
+   "doc_view": "",
+   "format": "{} Open",
+   "label": "Non Conformance",
+   "link_to": "Non Conformance",
+   "stats_filter": "{\"status\": \"Open\"}",
+   "type": "DocType"
   }
  ]
 }
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/non_conformance/__init__.py b/erpnext/quality_management/doctype/non_conformance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/__init__.py
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.js b/erpnext/quality_management/doctype/non_conformance/non_conformance.js
new file mode 100644
index 0000000..e7f5eee
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Non Conformance', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.json b/erpnext/quality_management/doctype/non_conformance/non_conformance.json
new file mode 100644
index 0000000..bfeb96b
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.json
@@ -0,0 +1,118 @@
+{
+ "actions": [],
+ "autoname": "format:QA-NC-{#####}",
+ "creation": "2020-10-21 14:49:50.350136",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "subject",
+  "procedure",
+  "process_owner",
+  "full_name",
+  "column_break_4",
+  "status",
+  "section_break_4",
+  "details",
+  "corrective_action",
+  "preventive_action"
+ ],
+ "fields": [
+  {
+   "fieldname": "subject",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Subject",
+   "reqd": 1
+  },
+  {
+   "fieldname": "procedure",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Procedure",
+   "options": "Quality Procedure",
+   "reqd": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Status",
+   "options": "Open\nResolved\nCancelled",
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "details",
+   "fieldtype": "Text Editor",
+   "label": "Details"
+  },
+  {
+   "fetch_from": "procedure.process_owner",
+   "fieldname": "process_owner",
+   "fieldtype": "Data",
+   "label": "Process Owner",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "process_owner.full_name",
+   "fieldname": "full_name",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Full Name"
+  },
+  {
+   "fieldname": "corrective_action",
+   "fieldtype": "Text",
+   "label": "Corrective Action"
+  },
+  {
+   "fieldname": "preventive_action",
+   "fieldtype": "Text",
+   "label": "Preventive Action"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-10-26 15:27:47.247814",
+ "modified_by": "Administrator",
+ "module": "Quality Management",
+ "name": "Non Conformance",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.py b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
new file mode 100644
index 0000000..d4e8cc7
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class NonConformance(Document):
+	pass
diff --git a/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
new file mode 100644
index 0000000..54f8b58
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestNonConformance(unittest.TestCase):
+	pass
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.js b/erpnext/quality_management/doctype/quality_action/quality_action.js
index 7078247..e216a75 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.js
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.js
@@ -2,32 +2,5 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Quality Action', {
-	onload: function(frm) {
-		frm.set_value("date", frappe.datetime.get_today());
-		frm.refresh();
-	},
-	document_name: function(frm){
-		frappe.call({
-			"method": "frappe.client.get",
-			args: {
-				doctype: frm.doc.document_type,
-				name: frm.doc.document_name
-			},
-			callback: function(data){
-				frm.fields_dict.resolutions.grid.remove_all();
-				let objectives = [];
 
-				if(frm.doc.document_type === "Quality Review"){
-					for(let i in data.message.reviews) objectives.push(data.message.reviews[i].review);
-				} else {
-					for(let j in data.message.parameters) objectives.push(data.message.parameters[j].feedback);
-				}
-				for (var objective in objectives){
-					frm.add_child("resolutions");
-					frm.fields_dict.resolutions.get_value()[objective].problem = objectives[objective];
-				}
-				frm.refresh();
-			}
-		});
-	},
 });
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.json b/erpnext/quality_management/doctype/quality_action/quality_action.json
index 8835b47..0cc2a98 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.json
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.json
@@ -1,32 +1,34 @@
 {
- "autoname": "format:ACTN-{#####}",
+ "actions": [],
+ "autoname": "format:QA-ACT-{#####}",
  "creation": "2018-10-02 11:40:43.666100",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "corrective_preventive",
-  "document_type",
-  "goal",
+  "review",
+  "feedback",
+  "status",
   "cb_00",
   "date",
-  "document_name",
+  "goal",
   "procedure",
-  "status",
   "sb_00",
   "resolutions"
  ],
  "fields": [
   {
-   "depends_on": "eval:doc.type == 'Quality Review'",
    "fetch_from": "review.goal",
    "fieldname": "goal",
    "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
    "label": "Goal",
-   "options": "Quality Goal",
-   "read_only": 1
+   "options": "Quality Goal"
   },
   {
+   "default": "Today",
    "fieldname": "date",
    "fieldtype": "Date",
    "in_list_view": 1,
@@ -34,34 +36,20 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:doc.type == 'Quality Review'",
    "fieldname": "procedure",
    "fieldtype": "Link",
    "label": "Procedure",
-   "options": "Quality Procedure",
-   "read_only": 1
+   "options": "Quality Procedure"
   },
   {
    "default": "Open",
    "fieldname": "status",
    "fieldtype": "Select",
    "in_list_view": 1,
+   "in_standard_filter": 1,
    "label": "Status",
-   "options": "Open\nClosed"
-  },
-  {
-   "fieldname": "document_name",
-   "fieldtype": "Dynamic Link",
-   "label": "Document Name",
-   "options": "document_type"
-  },
-  {
-   "fieldname": "document_type",
-   "fieldtype": "Select",
-   "in_list_view": 1,
-   "label": "Document Type",
-   "options": "Quality Review\nQuality Feedback",
-   "reqd": 1
+   "options": "Open\nCompleted",
+   "read_only": 1
   },
   {
    "default": "Corrective",
@@ -86,9 +74,24 @@
    "fieldtype": "Table",
    "label": "Resolutions",
    "options": "Quality Action Resolution"
+  },
+  {
+   "fieldname": "review",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Review",
+   "options": "Quality Review"
+  },
+  {
+   "fieldname": "feedback",
+   "fieldtype": "Link",
+   "label": "Feedback",
+   "options": "Quality Feedback"
   }
  ],
- "modified": "2019-05-28 13:10:44.092497",
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-10-27 16:21:59.533937",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Action",
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py
index 88d4bd8..d6fa505 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.py
@@ -7,4 +7,5 @@
 from frappe.model.document import Document
 
 class QualityAction(Document):
-	pass
\ No newline at end of file
+	def validate(self):
+		self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed'
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.js b/erpnext/quality_management/doctype/quality_action/test_quality_action.js
deleted file mode 100644
index 34a8c86..0000000
--- a/erpnext/quality_management/doctype/quality_action/test_quality_action.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Action", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quality Actions
-		() => frappe.tests.make('Quality Actions', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.py b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
index 51178d6..24b97ca 100644
--- a/erpnext/quality_management/doctype/quality_action/test_quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
@@ -5,42 +5,7 @@
 
 import frappe
 import unittest
-from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_unit
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal
-from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review
 
 class TestQualityAction(unittest.TestCase):
-
-	def test_quality_action(self):
-		create_procedure()
-		create_unit()
-		create_goal()
-		create_review()
-		test_create_action = create_action()
-		test_get_action = get_action()
-
-		self.assertEquals(test_create_action, test_get_action)
-
-def create_action():
-	review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
-	action = frappe.get_doc({
-		"doctype": "Quality Action",
-		"action": "Corrective",
-		"document_type": "Quality Review",
-		"document_name": review,
-		"date": frappe.utils.nowdate(),
-		"goal": "GOAL-_Test Quality Goal",
-		"procedure": "PRC-_Test Quality Procedure"
-	})
-	action_exist = frappe.db.exists("Quality Action", {"review": review})
-
-	if not action_exist:
-		action.insert()
-		return action.name
-	else:
-		return action_exist
-
-def get_action():
-	review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
-	return frappe.db.exists("Quality Action", {"document_name": review})
\ No newline at end of file
+	# quality action has no code
+	pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
index a4e6aed..993274b 100644
--- a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
+++ b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
@@ -1,33 +1,54 @@
 {
+ "actions": [],
  "creation": "2019-05-26 20:36:44.337186",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "problem",
-  "sb_00",
-  "resolution"
+  "resolution",
+  "status",
+  "responsible",
+  "completion_by"
  ],
  "fields": [
   {
    "fieldname": "problem",
    "fieldtype": "Long Text",
    "in_list_view": 1,
-   "label": "Review"
-  },
-  {
-   "fieldname": "sb_00",
-   "fieldtype": "Section Break"
+   "label": "Problem"
   },
   {
    "fieldname": "resolution",
    "fieldtype": "Text Editor",
    "in_list_view": 1,
    "label": "Resolution"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Status",
+   "options": "Open\nCompleted"
+  },
+  {
+   "fieldname": "responsible",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Responsible",
+   "options": "User"
+  },
+  {
+   "fieldname": "completion_by",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Completion By"
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-05-28 13:09:50.435323",
+ "links": [],
+ "modified": "2020-10-21 12:59:25.566682",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Action Resolution",
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js
index dac6ac4..6fb3267 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js
@@ -2,31 +2,9 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Quality Feedback', {
-	refresh: function(frm) {
-		frm.set_value("date", frappe.datetime.get_today());
-	},
-
 	template: function(frm) {
 		if (frm.doc.template) {
-			frappe.call({
-				"method": "frappe.client.get",
-				args: {
-					doctype: "Quality Feedback Template",
-					name: frm.doc.template
-				},
-				callback: function(data) {
-					if (data && data.message) {
-						frm.fields_dict.parameters.grid.remove_all();
-
-						// fetch parameters from template and autofill
-						for (let template_parameter of data.message.parameters) {
-							let row = frm.add_child("parameters");
-							row.parameter = template_parameter.parameter;
-						}
-						frm.refresh();
-					}
-				}
-			});
+			frm.call('set_parameters');
 		}
 	}
 });
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
index ab9084f..f3bd0dd 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
@@ -1,16 +1,15 @@
 {
  "actions": [],
- "autoname": "format:FDBK-{#####}",
+ "autoname": "format:QA-FB-{#####}",
  "creation": "2019-05-26 21:23:05.308379",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "document_type",
   "template",
   "cb_00",
+  "document_type",
   "document_name",
-  "date",
   "sb_00",
   "parameters"
  ],
@@ -18,6 +17,7 @@
   {
    "fieldname": "template",
    "fieldtype": "Link",
+   "in_list_view": 1,
    "label": "Template",
    "options": "Quality Feedback Template",
    "reqd": 1
@@ -27,13 +27,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "fieldname": "date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "Date",
-   "read_only": 1
-  },
-  {
    "fieldname": "sb_00",
    "fieldtype": "Section Break"
   },
@@ -47,6 +40,7 @@
   {
    "fieldname": "document_type",
    "fieldtype": "Select",
+   "in_list_view": 1,
    "label": "Type",
    "options": "User\nCustomer",
    "reqd": 1
@@ -54,13 +48,20 @@
   {
    "fieldname": "document_name",
    "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
    "label": "Feedback By",
    "options": "document_type",
    "reqd": 1
   }
  ],
- "links": [],
- "modified": "2020-07-03 15:50:58.589302",
+ "links": [
+  {
+   "group": "Actions",
+   "link_doctype": "Quality Action",
+   "link_fieldname": "feedback"
+  }
+ ],
+ "modified": "2020-10-27 16:20:10.918544",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Feedback",
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
index 9894181..bf82cc0 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
@@ -7,4 +7,17 @@
 from frappe.model.document import Document
 
 class QualityFeedback(Document):
-	pass
\ No newline at end of file
+	def set_parameters(self):
+		if self.template and not getattr(self, 'parameters', []):
+			for d in frappe.get_doc('Quality Feedback Template', self.template).parameters:
+				self.append('parameters', dict(
+					parameter = d.parameter,
+					rating = 1
+				))
+
+	def validate(self):
+		if not self.document_name:
+			self.document_type ='User'
+			self.document_name = frappe.session.user
+		self.set_parameters()
+
diff --git a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
index 3be1eb2..5a8bd5c 100644
--- a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
@@ -5,49 +5,27 @@
 
 import frappe
 import unittest
-from erpnext.quality_management.doctype.quality_feedback_template.test_quality_feedback_template import create_template
+
 
 class TestQualityFeedback(unittest.TestCase):
-
 	def test_quality_feedback(self):
-		create_template()
-		test_create_feedback = create_feedback()
-		test_get_feedback = get_feedback()
+		template = frappe.get_doc(dict(
+			doctype = 'Quality Feedback Template',
+			template = 'Test Template',
+			parameters = [
+				dict(parameter='Test Parameter 1'),
+				dict(parameter='Test Parameter 2')
+			]
+		)).insert()
 
-		self.assertEqual(test_create_feedback, test_get_feedback)
+		feedback = frappe.get_doc(dict(
+			doctype = 'Quality Feedback',
+			template = template.name,
+			document_type = 'User',
+			document_name = frappe.session.user
+		)).insert()
 
-def create_feedback():
-	create_customer()
+		self.assertEqual(template.parameters[0].parameter, feedback.parameters[0].parameter)
 
-	feedabck = frappe.get_doc({
-		"doctype": "Quality Feedback",
-		"template": "TMPL-_Test Feedback Template",
-		"document_type": "Customer",
-		"document_name": "Quality Feedback Customer",
-		"date": frappe.utils.nowdate(),
-		"parameters": [
-			{
-				"parameter": "Test Parameter",
-				"rating": 3,
-				"feedback": "Test Feedback"
-			}
-		]
-	})
-
-	feedback_exists = frappe.db.exists("Quality Feedback", {"template": "TMPL-_Test Feedback Template"})
-
-	if not feedback_exists:
-		feedabck.insert()
-		return feedabck.name
-	else:
-		return feedback_exists
-
-def get_feedback():
-	return frappe.db.exists("Quality Feedback", {"template": "TMPL-_Test Feedback Template"})
-
-def create_customer():
-	if not frappe.db.exists("Customer", {"customer_name": "Quality Feedback Customer"}):
-		customer = frappe.get_doc({
-				"doctype": "Customer",
-				"customer_name": "Quality Feedback Customer"
-			}).insert(ignore_permissions=True)
\ No newline at end of file
+		feedback.delete()
+		template.delete()
diff --git a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
index 5bd8920..ce5d4cf 100644
--- a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
+++ b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2019-05-26 21:25:01.715807",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -39,12 +40,13 @@
    "fieldname": "feedback",
    "fieldtype": "Text Editor",
    "in_list_view": 1,
-   "label": "Feedback",
-   "reqd": 1
+   "label": "Feedback"
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-07-13 19:58:08.966141",
+ "links": [],
+ "modified": "2020-10-27 17:28:12.033145",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Feedback Parameter",
diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
index bdc9dba..1696470 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
+++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
@@ -1,13 +1,12 @@
 {
  "actions": [],
- "autoname": "format:TMPL-{template}",
+ "autoname": "field:template",
  "creation": "2019-05-26 21:17:24.283061",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "template",
-  "cb_00",
   "sb_00",
   "parameters"
  ],
@@ -16,12 +15,9 @@
    "fieldname": "template",
    "fieldtype": "Data",
    "in_list_view": 1,
-   "label": "Template",
-   "reqd": 1
-  },
-  {
-   "fieldname": "cb_00",
-   "fieldtype": "Column Break"
+   "label": "Template Name",
+   "reqd": 1,
+   "unique": 1
   },
   {
    "fieldname": "sb_00",
@@ -35,8 +31,14 @@
    "reqd": 1
   }
  ],
- "links": [],
- "modified": "2020-07-03 16:06:03.749415",
+ "links": [
+  {
+   "group": "Records",
+   "link_doctype": "Quality Feedback",
+   "link_fieldname": "template"
+  }
+ ],
+ "modified": "2020-10-27 16:18:53.579688",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Feedback Template",
diff --git a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
index 36dbe13..b3eed10 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
+++ b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
@@ -7,31 +7,4 @@
 import unittest
 
 class TestQualityFeedbackTemplate(unittest.TestCase):
-
-	def test_quality_feedback_template(self):
-		test_create_template = create_template()
-		test_get_template = get_template()
-
-		self.assertEqual(test_create_template, test_get_template)
-
-def create_template():
-	template = frappe.get_doc({
-		"doctype": "Quality Feedback Template",
-		"template": "_Test Feedback Template",
-		"parameters": [
-			{
-				"parameter": "Test Parameter"
-			}
-		]
-	})
-
-	template_exists = frappe.db.exists("Quality Feedback Template", {"template": "_Test Feedback Template"})
-
-	if not template_exists:
-		template.insert()
-		return template.name
-	else:
-		return template_exists
-
-def get_template():
-	return frappe.db.exists("Quality Feedback Template", {"template": "_Test Feedback Template"})
\ No newline at end of file
+	pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.js b/erpnext/quality_management/doctype/quality_goal/quality_goal.js
index ff58c5a..40cb4d9 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.js
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.js
@@ -2,7 +2,6 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Quality Goal', {
-	refresh: function(frm) {
-		frm.doc.created_by = frappe.session.user;
-	}
+	// refresh: function(frm) {
+	// }
 });
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.json b/erpnext/quality_management/doctype/quality_goal/quality_goal.json
index c326109..2680255 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.json
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.json
@@ -1,5 +1,6 @@
 {
- "autoname": "format:GOAL-{goal}",
+ "actions": [],
+ "autoname": "field:goal",
  "creation": "2018-10-02 12:17:41.727541",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -7,28 +8,15 @@
  "field_order": [
   "goal",
   "frequency",
-  "created_by",
   "cb_00",
   "procedure",
   "weekday",
-  "quarter",
   "date",
-  "sb_00",
-  "revision",
-  "cb_01",
-  "revised_on",
   "sb_01",
   "objectives"
  ],
  "fields": [
   {
-   "fieldname": "created_by",
-   "fieldtype": "Link",
-   "label": "Created By",
-   "options": "User",
-   "read_only": 1
-  },
-  {
    "default": "None",
    "fieldname": "frequency",
    "fieldtype": "Select",
@@ -51,20 +39,6 @@
    "options": "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30"
   },
   {
-   "default": "0",
-   "fieldname": "revision",
-   "fieldtype": "Int",
-   "label": "Revision",
-   "read_only": 1
-  },
-  {
-   "fieldname": "revised_on",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "Revised On",
-   "read_only": 1
-  },
-  {
    "depends_on": "eval:doc.frequency == 'Weekly';",
    "fieldname": "weekday",
    "fieldtype": "Select",
@@ -76,15 +50,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "fieldname": "sb_00",
-   "fieldtype": "Section Break",
-   "label": "Revision and Revised On"
-  },
-  {
-   "fieldname": "cb_01",
-   "fieldtype": "Column Break"
-  },
-  {
    "fieldname": "sb_01",
    "fieldtype": "Section Break",
    "label": "Objectives"
@@ -101,18 +66,17 @@
    "label": "Goal",
    "reqd": 1,
    "unique": 1
-  },
-  {
-   "default": "January-April-July-October",
-   "depends_on": "eval:doc.frequency == 'Quarterly';",
-   "fieldname": "quarter",
-   "fieldtype": "Select",
-   "label": "Quarter",
-   "options": "January-April-July-October",
-   "read_only": 1
   }
  ],
- "modified": "2019-05-28 14:49:12.768863",
+ "index_web_pages_for_search": 1,
+ "links": [
+  {
+   "group": "Review",
+   "link_doctype": "Quality Review",
+   "link_fieldname": "goal"
+  }
+ ],
+ "modified": "2020-10-27 15:57:59.368605",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Goal",
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.py b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
index 4ae015e..f3fe986 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
@@ -8,7 +8,5 @@
 from frappe.model.document import Document
 
 class QualityGoal(Document):
-
 	def validate(self):
-		self.revision += 1
-		self.revised_on = frappe.utils.today()
+		pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py b/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py
deleted file mode 100644
index 22af3c0..0000000
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from frappe import _
-
-def get_data():
-	return {
-		'fieldname': 'goal',
-		'transactions': [
-			{
-				'label': _('Review'),
-				'items': ['Quality Review']
-			}
-		]
-	}
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js
deleted file mode 100644
index f8afe54..0000000
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Goal", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quality Goal
-		() => frappe.tests.make('Quality Goal', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
index d77187a..f61d6e5 100644
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
@@ -8,44 +8,18 @@
 from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
 
 class TestQualityGoal(unittest.TestCase):
-
 	def test_quality_goal(self):
-		create_procedure()
-		create_unit()
-		test_create_goal = create_goal()
-		test_get_goal = get_goal()
+		# no code, just a basic sanity check
+		goal = get_quality_goal()
+		self.assertTrue(goal)
+		goal.delete()
 
-		self.assertEquals(test_create_goal, test_get_goal)
-
-def create_goal():
-	goal = frappe.get_doc({
-		"doctype": "Quality Goal",
-		"goal": "_Test Quality Goal",
-		"procedure": "PRC-_Test Quality Procedure",
-		"objectives": [
-			{
-				"objective": "_Test Quality Objective",
-				"target": "4",
-				"uom": "_Test UOM"
-			}
+def get_quality_goal():
+	return frappe.get_doc(dict(
+		doctype = 'Quality Goal',
+		goal = 'Test Quality Module',
+		frequency = 'Daily',
+		objectives = [
+			dict(objective = 'Check test cases', target='100', uom='Percent')
 		]
-	})
-	goal_exist = frappe.db.exists("Quality Goal", {"goal": goal.goal})
-	if not goal_exist:
-		goal.insert()
-		return goal.name
-	else:
-		return goal_exist
-
-def get_goal():
-	goal = frappe.db.exists("Quality Goal", "GOAL-_Test Quality Goal")
-	return goal
-
-def create_unit():
-	unit = frappe.get_doc({
-		"doctype": "UOM",
-		"uom_name": "_Test UOM",
-	})
-	unit_exist = frappe.db.exists("UOM", unit.uom_name)
-	if not unit_exist:
-		unit.insert()
+	)).insert()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js
index 32c7c33..eb7a8c3 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js
@@ -2,8 +2,5 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Quality Meeting', {
-	onload: function(frm){
-		frm.set_value("date", frappe.datetime.get_today());
-		frm.refresh();
-	}
+
 });
diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
index 7691fe3..ead403d 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
@@ -1,15 +1,13 @@
 {
  "actions": [],
- "autoname": "naming_series:",
+ "autoname": "format:QA-MEET-{YY}-{MM}-{DD}",
  "creation": "2018-10-15 16:25:41.548432",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "naming_series",
-  "date",
-  "cb_00",
   "status",
+  "cb_00",
   "sb_00",
   "agenda",
   "sb_01",
@@ -17,13 +15,6 @@
  ],
  "fields": [
   {
-   "fieldname": "date",
-   "fieldtype": "Date",
-   "in_list_view": 1,
-   "label": "Date",
-   "read_only": 1
-  },
-  {
    "default": "Open",
    "fieldname": "status",
    "fieldtype": "Select",
@@ -55,16 +46,11 @@
    "fieldname": "sb_01",
    "fieldtype": "Section Break",
    "label": "Minutes"
-  },
-  {
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "label": "Naming Series",
-   "options": "MTNG-.YYYY.-.MM.-.DD.-"
   }
  ],
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-05-19 13:18:59.821740",
+ "modified": "2020-10-27 16:36:45.657883",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Meeting",
diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js
deleted file mode 100644
index 196cc85..0000000
--- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Meeting", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quality Meeting
-		() => frappe.tests.make('Quality Meeting', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
index e61b5df..754bccb 100644
--- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
+++ b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
@@ -5,41 +5,7 @@
 
 import frappe
 import unittest
-from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review
 
 class TestQualityMeeting(unittest.TestCase):
-	def test_quality_meeting(self):
-		create_review()
-		test_create_meeting = create_meeting()
-		test_get_meeting = get_meeting()
-		self.assertEquals(test_create_meeting, test_get_meeting)
-
-def create_meeting():
-	meeting = frappe.get_doc({
-		"doctype": "Quality Meeting",
-		"status": "Open",
-		"date": frappe.utils.nowdate(),
-		"agenda": [
-			{
-				"agenda": "Test Agenda"
-			}
-		],
-		"minutes": [
-			{
-				"document_type": "Quality Review",
-				"document_name": frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}),
-				"minute": "Test Minute"
-			}
-		]
-	})
-	meeting_exist = frappe.db.exists("Quality Meeting", {"date": frappe.utils.nowdate(), "status": "Open"})
-
-	if not meeting_exist:
-		meeting.insert()
-		return meeting.name
-	else:
-		return meeting_exist
-
-def get_meeting():
-	meeting = frappe.db.exists("Quality Meeting", {"date": frappe.utils.nowdate(), "status": "Open"})
-	return meeting
\ No newline at end of file
+	# nothing to test
+	pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
index 1ed921c..f588f9a 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
@@ -1,19 +1,22 @@
 {
  "actions": [],
  "allow_rename": 1,
- "autoname": "format:PRC-{quality_procedure_name}",
+ "autoname": "field:quality_procedure_name",
  "creation": "2018-10-06 00:06:29.756804",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "quality_procedure_name",
+  "process_owner",
+  "process_owner_full_name",
+  "section_break_3",
+  "processes",
+  "sb_00",
   "parent_quality_procedure",
   "is_group",
-  "sb_00",
-  "processes",
-  "lft",
   "rgt",
+  "lft",
   "old_parent"
  ],
  "fields": [
@@ -34,14 +37,14 @@
    "fieldname": "lft",
    "fieldtype": "Int",
    "hidden": 1,
-   "label": "Lft",
+   "label": "Left Index",
    "read_only": 1
   },
   {
    "fieldname": "rgt",
    "fieldtype": "Int",
    "hidden": 1,
-   "label": "Rgt",
+   "label": "Right Index",
    "read_only": 1
   },
   {
@@ -54,7 +57,7 @@
   {
    "fieldname": "sb_00",
    "fieldtype": "Section Break",
-   "label": "Processes"
+   "label": "Parent"
   },
   {
    "fieldname": "processes",
@@ -67,12 +70,52 @@
    "fieldtype": "Data",
    "in_list_view": 1,
    "label": "Quality Procedure",
-   "reqd": 1
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "process_owner",
+   "fieldtype": "Link",
+   "label": "Process Owner",
+   "options": "User"
+  },
+  {
+   "fieldname": "section_break_3",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fetch_from": "process_owner.full_name",
+   "fieldname": "process_owner_full_name",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Process Owner Full Name",
+   "print_hide": 1
   }
  ],
  "is_tree": 1,
- "links": [],
- "modified": "2020-10-13 11:46:07.744194",
+ "links": [
+  {
+   "group": "Reviews",
+   "link_doctype": "Quality Review",
+   "link_fieldname": "procedure"
+  },
+  {
+   "group": "Goals",
+   "link_doctype": "Quality Goal",
+   "link_fieldname": "procedure"
+  },
+  {
+   "group": "Actions",
+   "link_doctype": "Quality Action",
+   "link_fieldname": "procedure"
+  },
+  {
+   "group": "Actions",
+   "link_doctype": "Non Conformance",
+   "link_fieldname": "procedure"
+  }
+ ],
+ "modified": "2020-10-26 15:25:39.316088",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Procedure",
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index 797c26b..53f4e6c 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -14,69 +14,58 @@
 		self.check_for_incorrect_child()
 
 	def on_update(self):
+		NestedSet.on_update(self)
 		self.set_parent()
 
 	def after_insert(self):
 		self.set_parent()
-		#if Child is Added through Tree View.
+
+		# add child to parent if missing
 		if self.parent_quality_procedure:
-			parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
-			parent_quality_procedure.append("processes", {"procedure": self.name})
-			parent_quality_procedure.save()
+			parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
+			if not [d for d in parent.processes if d.procedure == self.name]:
+				parent.append("processes", {"procedure": self.name, "process_description": self.name})
+				parent.save()
 
 	def on_trash(self):
-		if self.parent_quality_procedure:
-			doc = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
-			for process in doc.processes:
-				if process.procedure == self.name:
-					doc.processes.remove(process)
-					doc.save(ignore_permissions=True)
-
-			flag_is_group = 0
-			doc.load_from_db()
-
-			for process in doc.processes:
-				flag_is_group = 1 if process.procedure else 0
-
-			doc.is_group = 0 if flag_is_group == 0 else 1
-			doc.save(ignore_permissions=True)
+		# clear from child table (sub procedures)
+		frappe.db.sql('''update `tabQuality Procedure Process`
+			set `procedure`='' where `procedure`=%s''', self.name)
+		NestedSet.on_trash(self, allow_root_deletion=True)
 
 	def set_parent(self):
-		rebuild_tree('Quality Procedure', 'parent_quality_procedure')
-
 		for process in self.processes:
 			# Set parent for only those children who don't have a parent
-			parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
-			if not parent_quality_procedure and process.procedure:
+			has_parent = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
+			if not has_parent and process.procedure:
 				frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name)
 
 	def check_for_incorrect_child(self):
 		for process in self.processes:
 			if process.procedure:
+				self.is_group = 1
 				# Check if any child process belongs to another parent.
 				parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
 				if parent_quality_procedure and parent_quality_procedure != self.name:
-					frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))),
+					frappe.throw(_("{0} already has a Parent Procedure {1}.").format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure)),
 						title=_("Invalid Child Procedure"))
-				self.is_group = 1
 
 @frappe.whitelist()
 def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
 	if parent is None or parent == "All Quality Procedures":
 		parent = ""
 
-	return frappe.db.sql("""
-		select
-			name as value,
-			is_group as expandable
-		from
-			`tab{doctype}`
-		where
-			ifnull(parent_quality_procedure, "")={parent}
-		""".format(
-			doctype = doctype,
-			parent=frappe.db.escape(parent)
-		), as_dict=1)
+	if parent:
+		parent_procedure = frappe.get_doc('Quality Procedure', parent)
+		# return the list in order
+		return [dict(
+				value=d.procedure,
+				expandable=frappe.db.get_value('Quality Procedure', d.procedure, 'is_group'))
+				for d in parent_procedure.processes if d.procedure
+			]
+	else:
+		return frappe.get_all(doctype, fields=['name as value', 'is_group as expandable'],
+			filters = dict(parent_quality_procedure = parent), order_by='name asc')
 
 @frappe.whitelist()
 def add_node():
@@ -88,4 +77,4 @@
 	if args.parent_quality_procedure == 'All Quality Procedures':
 		args.parent_quality_procedure = None
 
-	frappe.get_doc(args).insert()
\ No newline at end of file
+	return frappe.get_doc(args).insert()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py
deleted file mode 100644
index 407028b..0000000
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from frappe import _
-
-def get_data():
-	return {
-		'fieldname': 'procedure',
-		'transactions': [
-			{
-				'label': _('Goal'),
-				'items': ['Quality Goal']
-			},
-			{
-				'label': _('Review'),
-				'items': ['Quality Review']
-			},
-			{
-				'label': _('Action'),
-				'items': ['Quality Action']
-			}
-		],
-	}
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
index ef48ab6..eeb4cf6 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
@@ -15,7 +15,7 @@
 			}
 		},
 	],
-	breadcrumb: "Setup",
+	breadcrumb: "Quality Management",
 	disable_add_node: true,
 	root_label: "All Quality Procedures",
 	get_tree_root: false,
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js
deleted file mode 100644
index 0a187eb..0000000
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Procedure", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Quality Procedure
-		() => frappe.tests.make('Quality Procedure', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
index 3289bb5..36bdf26 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -6,54 +6,45 @@
 import frappe
 import unittest
 
-class TestQualityProcedure(unittest.TestCase):
-	def test_quality_procedure(self):
-		test_create_procedure = create_procedure()
-		test_create_nested_procedure = create_nested_procedure()
-		test_get_procedure, test_get_nested_procedure = get_procedure()
+from .quality_procedure import add_node
 
-		self.assertEquals(test_create_procedure, test_get_procedure.get("name"))
-		self.assertEquals(test_create_nested_procedure, test_get_nested_procedure.get("name"))
+class TestQualityProcedure(unittest.TestCase):
+	def test_add_node(self):
+		try:
+			procedure = frappe.get_doc(dict(
+				doctype = 'Quality Procedure',
+				quality_procedure_name = 'Test Procedure 1',
+				processes = [
+					dict(process_description = 'Test Step 1')
+				]
+			)).insert()
+
+			frappe.form_dict = dict(doctype = 'Quality Procedure', quality_procedure_name = 'Test Child 1',
+				parent_quality_procedure = procedure.name, cmd='test', is_root='false')
+			node = add_node()
+
+			procedure.reload()
+
+			self.assertEqual(procedure.is_group, 1)
+
+			# child row created
+			self.assertTrue([d for d in procedure.processes if d.procedure == node.name])
+
+			node.delete()
+			procedure.reload()
+
+			# child unset
+			self.assertFalse([d for d in procedure.processes if d.name == node.name])
+
+		finally:
+			procedure.delete()
 
 def create_procedure():
-	procedure = frappe.get_doc({
-		"doctype": "Quality Procedure",
-		"quality_procedure_name": "_Test Quality Procedure",
-		"processes": [
-			{
-				"process_description": "_Test Quality Procedure Table",
-			}
+	return frappe.get_doc(dict(
+		doctype = 'Quality Procedure',
+		quality_procedure_name = 'Test Procedure 1',
+		is_group = 1,
+		processes = [
+			dict(process_description = 'Test Step 1')
 		]
-	})
-
-	procedure_exist = frappe.db.exists("Quality Procedure", "PRC-_Test Quality Procedure")
-
-	if not procedure_exist:
-		procedure.insert()
-		return procedure.name
-	else:
-		return procedure_exist
-
-def create_nested_procedure():
-	nested_procedure = frappe.get_doc({
-		"doctype": "Quality Procedure",
-		"quality_procedure_name": "_Test Nested Quality Procedure",
-		"processes": [
-			{
-				"procedure": "PRC-_Test Quality Procedure"
-			}
-		]
-	})
-
-	nested_procedure_exist = frappe.db.exists("Quality Procedure", "PRC-_Test Nested Quality Procedure")
-
-	if not nested_procedure_exist:
-		nested_procedure.insert()
-		return nested_procedure.name
-	else:
-		return nested_procedure_exist
-
-def get_procedure():
-	procedure = frappe.get_doc("Quality Procedure", "PRC-_Test Quality Procedure")
-	nested_procedure = frappe.get_doc("Quality Procedure",  "PRC-_Test Nested Quality Procedure")
-	return {"name": procedure.name}, {"name": nested_procedure.name, "parent_quality_procedure": nested_procedure.parent_quality_procedure}
\ No newline at end of file
+	)).insert()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
index 3925dbb..aeca6ff 100644
--- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
+++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
@@ -10,6 +10,7 @@
  ],
  "fields": [
   {
+   "columns": 8,
    "fieldname": "process_description",
    "fieldtype": "Text Editor",
    "in_list_view": 1,
@@ -20,13 +21,14 @@
    "fieldname": "procedure",
    "fieldtype": "Link",
    "in_list_view": 1,
-   "label": "Child Procedure",
+   "label": "Sub Procedure",
    "options": "Quality Procedure"
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-06-17 15:44:38.937915",
+ "modified": "2020-10-27 13:55:11.252945",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Procedure Process",
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.js b/erpnext/quality_management/doctype/quality_review/quality_review.js
index b624581..67371bf 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.js
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.js
@@ -2,9 +2,6 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Quality Review', {
-	onload: function(frm){
-		frm.set_value("date", frappe.datetime.get_today());
-	},
 	goal: function(frm) {
 		frappe.call({
 			"method": "frappe.client.get",
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.json b/erpnext/quality_management/doctype/quality_review/quality_review.json
index 76714ce..31ad341 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.json
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.json
@@ -1,6 +1,6 @@
 {
  "actions": [],
- "autoname": "format:REV-{#####}",
+ "autoname": "format:QA-REV-{#####}",
  "creation": "2018-10-02 11:45:16.301955",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -18,6 +18,7 @@
  ],
  "fields": [
   {
+   "default": "Today",
    "fieldname": "date",
    "fieldtype": "Date",
    "in_list_view": 1,
@@ -50,7 +51,7 @@
    "collapsible": 1,
    "fieldname": "sb_01",
    "fieldtype": "Section Break",
-   "label": "Additional Information"
+   "label": "Notes"
   },
   {
    "fieldname": "reviews",
@@ -63,7 +64,8 @@
    "fieldname": "status",
    "fieldtype": "Select",
    "label": "Status",
-   "options": "Open\nClosed"
+   "options": "Open\nPassed\nFailed",
+   "read_only": 1
   },
   {
    "fieldname": "goal",
@@ -74,8 +76,15 @@
    "reqd": 1
   }
  ],
- "links": [],
- "modified": "2020-02-01 10:59:38.933115",
+ "index_web_pages_for_search": 1,
+ "links": [
+  {
+   "group": "Review",
+   "link_doctype": "Quality Action",
+   "link_fieldname": "review"
+  }
+ ],
+ "modified": "2020-10-21 12:56:47.046172",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Review",
@@ -120,5 +129,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "title_field": "goal",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py
index 2bc8867..e3a8b07 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.py
@@ -7,7 +7,26 @@
 from frappe.model.document import Document
 
 class QualityReview(Document):
-	pass
+	def validate(self):
+		# fetch targets from goal
+		if not self.reviews:
+			for d in frappe.get_doc('Quality Goal', self.goal).objectives:
+				self.append('reviews', dict(
+					objective = d.objective,
+					target = d.target,
+					uom = d.uom
+				))
+
+		self.set_status()
+
+	def set_status(self):
+		# if any child item is failed, fail the parent
+		if not len(self.reviews or []) or any([d.status=='Open' for d in self.reviews]):
+			self.status = 'Open'
+		elif any([d.status=='Failed' for d in self.reviews]):
+			self.status = 'Failed'
+		else:
+			self.status = 'Passed'
 
 def review():
 	day = frappe.utils.getdate().day
@@ -24,7 +43,7 @@
 		elif goal.frequency == 'Monthly' and goal.date == str(day):
 			create_review(goal.name)
 
-		elif goal.frequency == 'Quarterly' and goal.data == str(day) and get_quarter(month):
+		elif goal.frequency == 'Quarterly' and day==1 and get_quarter(month):
 			create_review(goal.name)
 
 def create_review(goal):
@@ -36,15 +55,6 @@
 		"date": frappe.utils.getdate()
 	})
 
-	for objective in goal.objectives:
-		review.append("reviews",
-			{
-				"objective": objective.objective,
-				"target": objective.target,
-				"uom": objective.uom
-			}
-		)
-
 	review.insert(ignore_permissions=True)
 
 def get_quarter(month):
diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.js b/erpnext/quality_management/doctype/quality_review/test_quality_review.js
deleted file mode 100644
index cf910b2..0000000
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Performance Monitoring", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Performance Monitoring
-		() => frappe.tests.make('Performance Monitoring', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.py b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
index 8add6db..a7d92da 100644
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
@@ -5,42 +5,18 @@
 
 import frappe
 import unittest
-from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_unit
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal
+
+from ..quality_goal.test_quality_goal import get_quality_goal
+from .quality_review import review
 
 class TestQualityReview(unittest.TestCase):
+	def test_review_creation(self):
+		quality_goal = get_quality_goal()
+		review()
 
-	def test_quality_review(self):
-		create_procedure()
-		create_unit()
-		create_goal()
-		test_create_review = create_review()
-		test_get_review = get_review()
-		self.assertEquals(test_create_review, test_get_review)
+		# check if review exists
+		quality_review = frappe.get_doc('Quality Review', dict(goal = quality_goal.name))
+		self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target)
+		quality_review.delete()
 
-def create_review():
-	review = frappe.get_doc({
-		"doctype": "Quality Review",
-		"goal": "GOAL-_Test Quality Goal",
-		"procedure": "PRC-_Test Quality Procedure",
-		"date": frappe.utils.nowdate(),
-		"reviews": [
-			{
-				"objective": "_Test Quality Objective",
-				"target": "100",
-				"uom": "_Test UOM",
-				"review": "Test Review"
-			}
-		]
-	})
-	review_exist = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
-	if not review_exist:
-		review.insert(ignore_permissions=True)
-		return review.name
-	else:
-		return review_exist
-
-def get_review():
-	review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
-	return review
\ No newline at end of file
+		quality_goal.delete()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
index 91f7bc0..3a750c2 100644
--- a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
+++ b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2019-05-26 15:17:44.796958",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -9,10 +10,12 @@
   "target",
   "uom",
   "sb_00",
+  "status",
   "review"
  ],
  "fields": [
   {
+   "columns": 3,
    "fieldname": "objective",
    "fieldtype": "Text",
    "in_list_view": 1,
@@ -20,6 +23,7 @@
    "read_only": 1
   },
   {
+   "columns": 2,
    "fieldname": "target",
    "fieldtype": "Data",
    "in_list_view": 1,
@@ -27,6 +31,7 @@
    "read_only": 1
   },
   {
+   "columns": 1,
    "fetch_from": "target_unit",
    "fieldname": "uom",
    "fieldtype": "Link",
@@ -49,10 +54,20 @@
   {
    "fieldname": "cb_00",
    "fieldtype": "Column Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Status",
+   "options": "Open\nPassed\nFailed"
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-05-26 16:14:12.586128",
+ "links": [],
+ "modified": "2020-10-27 16:28:20.908637",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Review Objective",
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 9e25ed0..a33d401 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -441,6 +441,7 @@
 	def test_update_child_qty_rate_with_workflow(self):
 		from frappe.model.workflow import apply_workflow
 
+		frappe.set_user("Administrator")
 		workflow = make_sales_order_workflow()
 		so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
 		apply_workflow(so, 'Approve')
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 1fce504..c2a3d3c 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -256,3 +256,18 @@
 			batch.insert()
 
 		return batch
+
+def make_new_batch(**args):
+	args = frappe._dict(args)
+
+	try:
+		batch = frappe.get_doc({
+			"doctype": "Batch",
+			"batch_id": args.batch_id,
+			"item": args.item_code,
+		}).insert()
+
+	except frappe.DuplicateEntryError:
+		batch = frappe.get_doc("Batch", args.batch_id)
+
+	return batch
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 12c8906..a1e01bf 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -174,7 +174,7 @@
 
 		update_backflush_based_on("Material Transferred for Subcontract")
 		item_code = "_Test Subcontracted FG Item 1"
-		make_subcontracted_item(item_code)
+		make_subcontracted_item(item_code=item_code)
 
 		po = create_purchase_order(item_code=item_code, qty=1,
 			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
@@ -717,6 +717,66 @@
 		# Allowed to submit for other company's PR
 		self.assertEqual(pr.docstatus, 1)
 
+	def test_subcontracted_pr_for_multi_transfer_batches(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+		from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry, make_purchase_receipt
+		from erpnext.buying.doctype.purchase_order.test_purchase_order import (update_backflush_based_on,
+			create_purchase_order)
+
+		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="Yes", 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")
 
 def get_sl_entries(voucher_type, voucher_no):
 	return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
@@ -858,6 +918,33 @@
 			pr.submit()
 	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/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index 3643174..dd95075 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -236,7 +236,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-12 16:11:31.910508",
+ "modified": "2020-10-21 13:03:11.938072",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection",
@@ -257,7 +257,6 @@
    "write": 1
   }
  ],
- "quick_entry": 1,
  "search_fields": "item_code, report_date, reference_name",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index dec4fe2..295149e 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -343,10 +343,11 @@
 
 def validate_so_serial_no(sr, sales_order):
 	if not sr.sales_order or sr.sales_order!= sales_order:
-		msg = _("Sales Order {0} has reservation for item {1}")
-		msg += _(", you can only deliver reserved {1} against {0}.")
-		msg += _(" Serial No {2} cannot be delivered")
-		frappe.throw(msg.format(sales_order, sr.item_code, sr.name))
+		msg = (_("Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}.")
+			.format(sales_order, sr.item_code))
+
+		frappe.throw(_("""{0} Serial No {1} cannot be delivered""")
+			.format(msg, sr.name))
 
 def has_duplicate_serial_no(sn, sle):
 	if (sn.warehouse and not sle.skip_serial_no_validaiton
@@ -449,6 +450,9 @@
 		from tabItem where name=%s""", item_code, as_dict=True)[0]
 
 def get_serial_nos(serial_no):
+	if isinstance(serial_no, list):
+		return serial_no
+
 	return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
 		if s.strip()]
 
@@ -572,8 +576,8 @@
 
 	pos_transacted_sr_nos = frappe.db.sql("""select item.serial_no as serial_no
 		from `tabPOS Invoice` p, `tabPOS Invoice Item` item
-		where p.name = item.parent 
-		and p.consolidated_invoice is NULL 
+		where p.name = item.parent
+		and p.consolidated_invoice is NULL
 		and p.docstatus = 1
 		and item.docstatus = 1
 		and item.item_code = %(item_code)s
@@ -605,7 +609,7 @@
 		SELECT sr.name FROM `tabSerial No` sr {batch_join_selection}
 		WHERE
 			sr.name not in ({excluded_sr_nos}) AND
-			sr.item_code = %(item_code)s AND 
+			sr.item_code = %(item_code)s AND
 			sr.warehouse = %(warehouse)s AND
 			ifnull(sr.sales_invoice,'') = '' AND
 			ifnull(sr.delivery_document_no, '') = ''
@@ -620,5 +624,5 @@
 				batch_join_selection=batch_join_selection,
 				batch_no_condition=batch_no_condition
 			), filters, as_dict=1)
-		
+
 	return serial_numbers
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9ad3694..4583c51 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -571,8 +571,9 @@
 		qty_allowance = flt(frappe.db.get_single_value("Buying Settings",
 			"over_transfer_allowance"))
 
-		if (self.purpose == "Send to Subcontractor" and self.purchase_order and
-			backflush_raw_materials_based_on == 'BOM'):
+		if not (self.purpose == "Send to Subcontractor" and self.purchase_order): return
+
+		if (backflush_raw_materials_based_on == 'BOM'):
 			purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
 			for se_item in self.items:
 				item_code = se_item.original_item or se_item.item_code
@@ -609,6 +610,11 @@
 				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))
+		elif backflush_raw_materials_based_on == "Material Transferred for Subcontract":
+			for row in self.items:
+				if not row.subcontracted_item:
+					frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
+						.format(row.idx, frappe.bold(row.item_code)))
 
 	def validate_bom(self):
 		for d in self.get('items'):
@@ -817,6 +823,13 @@
 			ret.get('has_batch_no') and not args.get('batch_no')):
 			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'):
+			subcontract_items = frappe.get_all("Purchase Order Item Supplied",
+				{"parent": self.purchase_order, "rm_item_code": args.get('item_code')}, "main_item_code")
+
+			if subcontract_items and len(subcontract_items) == 1:
+				ret["subcontracted_item"] = subcontract_items[0].main_item_code
+
 		return ret
 
 	def set_items_for_stock_in(self):
@@ -1288,9 +1301,16 @@
 		#Update Supplied Qty in PO Supplied Items
 
 		frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos
-			SET pos.supplied_qty = (SELECT ifnull(sum(transfer_qty), 0) FROM `tabStock Entry Detail` sed
-			WHERE pos.name = sed.po_detail and sed.docstatus = 1)
-			WHERE pos.docstatus = 1 and pos.parent = %s""", self.purchase_order)
+			SET
+				pos.supplied_qty = IFNULL((SELECT ifnull(sum(transfer_qty), 0)
+					FROM
+						`tabStock Entry Detail` sed, `tabStock Entry` se
+					WHERE
+						pos.name = sed.po_detail AND pos.rm_item_code = sed.item_code
+						AND pos.parent = se.purchase_order AND sed.docstatus = 1
+						AND se.name = sed.parent and se.purchase_order = %(po)s
+				), 0)
+			WHERE pos.docstatus = 1 and pos.parent = %(po)s""", {"po": self.purchase_order})
 
 		#Update reserved sub contracted quantity in bin based on Supplied Item Details and
 		for d in self.get("items"):
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 ae2e3a1..79e8f9a 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -1,5 +1,4 @@
 {
- "actions": [],
  "autoname": "hash",
  "creation": "2013-03-29 18:22:12",
  "doctype": "DocType",
@@ -16,6 +15,7 @@
   "item_code",
   "col_break2",
   "item_name",
+  "subcontracted_item",
   "section_break_8",
   "description",
   "column_break_10",
@@ -57,7 +57,6 @@
   "material_request",
   "material_request_item",
   "original_item",
-  "subcontracted_item",
   "reference_section",
   "against_stock_entry",
   "ste_detail",
@@ -415,6 +414,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:parent.purpose == 'Send to Subcontractor'",
    "fieldname": "subcontracted_item",
    "fieldtype": "Link",
    "label": "Subcontracted Item",
@@ -504,7 +504,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-22 17:55:03.384138",
+ "modified": "2020-09-23 17:55:03.384138",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
index 55f041c..78e95df 100644
--- a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
+++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
@@ -6,10 +6,17 @@
 from frappe import _
 
 def execute(filters=None):
+	validate_warehouse(filters)
 	columns = get_columns()
 	data = get_data(filters.warehouse)
 	return columns, data
 
+def validate_warehouse(filters):
+	company = filters.company
+	warehouse = filters.warehouse
+	if not frappe.db.exists("Warehouse", {"name": warehouse, "company": company}):
+		frappe.throw(_("Warehouse: {0} does not belong to {1}").format(warehouse, company))
+
 def get_columns():
 	columns = [
 		{
diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html
index 7ce3521..7b239ac 100644
--- a/erpnext/www/lms/index.html
+++ b/erpnext/www/lms/index.html
@@ -45,7 +45,7 @@
 		<p class='lead'>{{ education_settings.description }}</p>
 		<p class="mt-4">
 			{% if frappe.session.user == 'Guest' %}
-				<a class="btn btn-primary btn-lg" href="'/login#signup'">{{_('Sign Up')}}</a>
+				<a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
 			{% endif %}
 		</p>
 	</div>
@@ -62,4 +62,4 @@
 		</div>
 	</div>
 </section>
-{% endblock %}
\ No newline at end of file
+{% endblock %}