fix: merge conflict
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 2f86c6c..335e8a1 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -301,17 +301,21 @@
 	start_date = add_months(today(), -1)
 	end_date = add_days(today(), -1)
 
-	for record_type in ('Income', 'Expense'):
-		doc = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date=posting_date,
-			start_date=start_date,
-			end_date=end_date,
-			type=record_type
-		))
+	companies = frappe.get_all('Company')
 
-		doc.insert()
-		doc.submit()
+	for company in companies:
+		for record_type in ('Income', 'Expense'):
+			doc = frappe.get_doc(dict(
+				doctype='Process Deferred Accounting',
+				company=company.name,
+				posting_date=posting_date,
+				start_date=start_date,
+				end_date=end_date,
+				type=record_type
+			))
+
+			doc.insert()
+			doc.submit()
 
 def make_gl_entries(doc, credit_account, debit_account, against,
 	amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 5f110e2..ffc9d1c 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -51,7 +51,7 @@
 			self.import_file, self.google_sheets_url
 		)
 
-		if 'Bank Account' not in json.dumps(preview):
+		if 'Bank Account' not in json.dumps(preview['columns']):
 			frappe.throw(_("Please add the Bank Account column"))
 
 		from frappe.core.page.background_jobs.background_jobs import get_info
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index 3b764aa..4fd8413 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -13,7 +13,8 @@
 from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file
 
 class ChartofAccountsImporter(Document):
-	pass
+	def validate(self):
+		validate_accounts(self.import_file)
 
 @frappe.whitelist()
 def validate_company(company):
@@ -301,28 +302,27 @@
 		if account["parent_account"] and accounts_dict.get(account["parent_account"]):
 			accounts_dict[account["parent_account"]]["is_group"] = 1
 
-	message = validate_root(accounts_dict)
-	if message: return message
-	message = validate_account_types(accounts_dict)
-	if message: return message
+	validate_root(accounts_dict)
+
+	validate_account_types(accounts_dict)
 
 	return [True, len(accounts)]
 
 def validate_root(accounts):
 	roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
 	if len(roots) < 4:
-		return _("Number of root accounts cannot be less than 4")
+		frappe.throw(_("Number of root accounts cannot be less than 4"))
 
 	error_messages = []
 
 	for account in roots:
 		if not account.get("root_type") and account.get("account_name"):
-			error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
+			error_messages.append(_("Please enter Root Type for account- {0}").format(account.get("account_name")))
 		elif account.get("root_type") not in get_root_types() and account.get("account_name"):
-			error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
+			error_messages.append(_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(account.get("account_name")))
 
 	if error_messages:
-		return "<br>".join(error_messages)
+		frappe.throw("<br>".join(error_messages))
 
 def get_root_types():
 	return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
@@ -356,7 +356,7 @@
 
 	missing = list(set(account_types_for_ledger) - set(account_types))
 	if missing:
-		return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
+		frappe.throw(_("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)))
 
 	account_types_for_group = ["Bank", "Cash", "Stock"]
 	# fix logic bug
@@ -364,7 +364,7 @@
 
 	missing = list(set(account_types_for_group) - set(account_groups))
 	if missing:
-		return _("Please identify/create Account (Group) for type - {0}").format(' , '.join(missing))
+		frappe.throw(_("Please identify/create Account (Group) for type - {0}").format(' , '.join(missing)))
 
 def unset_existing_data(company):
 	linked = frappe.db.sql('''select fieldname from tabDocField
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index c6c6892..1ef512a 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -25,7 +25,7 @@
 
 	def validate_amount(self):
 		amounts = calculate_interest_and_amount(
-			self.posting_date, self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
+			self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
 		if self.interest_amount != amounts.get('interest_amount'):
 			self.interest_amount = flt(amounts.get('interest_amount'), self.precision('interest_amount'))
 		if self.dunning_amount != amounts.get('dunning_amount'):
@@ -91,13 +91,13 @@
 			for dunning in dunnings:
 				frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved')
 
-def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
+def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
 	interest_amount = 0
-	grand_total = 0
+	grand_total = flt(outstanding_amount) + flt(dunning_fee)
 	if rate_of_interest:
 		interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100
 		interest_amount = (interest_per_year * cint(overdue_days)) / 365
-		grand_total = flt(outstanding_amount) + flt(interest_amount) + flt(dunning_fee)
+		grand_total += flt(interest_amount)
 	dunning_amount = flt(interest_amount) + flt(dunning_fee)
 	return {
 		'interest_amount': interest_amount,
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index e2d4d82..ed50f78 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -16,6 +16,7 @@
 	@classmethod
 	def setUpClass(self):
 		create_dunning_type()
+		create_dunning_type_with_zero_interest_rate()
 		unlink_payment_on_cancel_of_invoice()
 
 	@classmethod
@@ -25,11 +26,20 @@
 	def test_dunning(self):
 		dunning = create_dunning()
 		amounts = calculate_interest_and_amount(
-			dunning.posting_date, dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
+			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
 		self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44)
 		self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44)
 		self.assertEqual(round(amounts.get('grand_total'), 2), 120.44)
 
+	def test_dunning_with_zero_interest_rate(self):
+		dunning = create_dunning_with_zero_interest_rate()
+		amounts = calculate_interest_and_amount(
+			dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
+		self.assertEqual(round(amounts.get('interest_amount'), 2), 0)
+		self.assertEqual(round(amounts.get('dunning_amount'), 2), 20)
+		self.assertEqual(round(amounts.get('grand_total'), 2), 120)
+
+
 	def test_gl_entries(self):
 		dunning = create_dunning()
 		dunning.submit()
@@ -83,6 +93,27 @@
 	dunning.save()
 	return dunning
 
+def create_dunning_with_zero_interest_rate():
+	posting_date = add_days(today(), -20)
+	due_date = add_days(today(), -15)
+	sales_invoice = create_sales_invoice_against_cost_center(
+		posting_date=posting_date, due_date=due_date, status='Overdue')
+	dunning_type = frappe.get_doc("Dunning Type", 'First Notice with 0% Rate of Interest')
+	dunning = frappe.new_doc("Dunning")
+	dunning.sales_invoice = sales_invoice.name
+	dunning.customer_name = sales_invoice.customer_name
+	dunning.outstanding_amount = sales_invoice.outstanding_amount
+	dunning.debit_to = sales_invoice.debit_to
+	dunning.currency = sales_invoice.currency
+	dunning.company = sales_invoice.company
+	dunning.posting_date = nowdate()
+	dunning.due_date = sales_invoice.due_date
+	dunning.dunning_type = 'First Notice with 0% Rate of Interest'
+	dunning.rate_of_interest = dunning_type.rate_of_interest
+	dunning.dunning_fee = dunning_type.dunning_fee
+	dunning.save()
+	return dunning
+
 def create_dunning_type():
 	dunning_type = frappe.new_doc("Dunning Type")
 	dunning_type.dunning_type = 'First Notice'
@@ -98,3 +129,19 @@
 		}
 	)
 	dunning_type.save()
+
+def create_dunning_type_with_zero_interest_rate():
+	dunning_type = frappe.new_doc("Dunning Type")
+	dunning_type.dunning_type = 'First Notice with 0% Rate of Interest'
+	dunning_type.start_day = 10
+	dunning_type.end_day = 20
+	dunning_type.dunning_fee = 20
+	dunning_type.rate_of_interest = 0
+	dunning_type.append(
+		"dunning_letter_text", {
+			'language': 'en',
+			'body_text': 'We have still not received payment for our invoice ',
+			'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
+		}
+	)
+	dunning_type.save() 
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index adaf99a..0c21aae 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1318,9 +1318,9 @@
 
 	return frappe._dict({
 		"due_date": ref_doc.get("due_date"),
-		"total_amount": total_amount,
-		"outstanding_amount": outstanding_amount,
-		"exchange_rate": exchange_rate,
+		"total_amount": flt(total_amount),
+		"outstanding_amount": flt(outstanding_amount),
+		"exchange_rate": flt(exchange_rate),
 		"bill_no": bill_no
 	})
 
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 4641d6b..d1302f5 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -589,9 +589,9 @@
 		party_account_balance = get_balance_on(account=pe.paid_from, cost_center=pe.cost_center)
 
 		self.assertEqual(pe.cost_center, si.cost_center)
-		self.assertEqual(expected_account_balance, account_balance)
-		self.assertEqual(expected_party_balance, party_balance)
-		self.assertEqual(expected_party_account_balance, party_account_balance)
+		self.assertEqual(flt(expected_account_balance), account_balance)
+		self.assertEqual(flt(expected_party_balance), party_balance)
+		self.assertEqual(flt(expected_party_account_balance), party_account_balance)
 
 def create_payment_terms_template():
 
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 0b0ee90..500952e 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -207,10 +207,9 @@
 @frappe.whitelist()
 def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
 	billing_email = frappe.db.sql("""
-		SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent \
-		WHERE l.link_doctype='Customer' and l.link_name='""" + customer_name + """' and \
-		c.is_billing_contact=1 \
-		order by c.creation desc""")
+		SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent
+		WHERE l.link_doctype='Customer' and l.link_name=%s and c.is_billing_contact=1
+		order by c.creation desc""", customer_name)
 
 	if len(billing_email) == 0 or (billing_email[0][0] is None):
 		if billing_and_primary:
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 2f5d36c..311745d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1010,21 +1010,21 @@
 		# Check GLE for Purchase Invoice
 		# Zero net effect on final TDS Payable on invoice
 		expected_gle = [
-			['_Test Account Cost for Goods Sold - _TC', 30000, 0],
-			['_Test Account Excise Duty - _TC', 0, 3000],
-			['Creditors - _TC', 0, 27000],
-			['TDS Payable - _TC', 3000, 3000]
+			['_Test Account Cost for Goods Sold - _TC', 30000],
+			['_Test Account Excise Duty - _TC', -3000],
+			['Creditors - _TC', -27000],
+			['TDS Payable - _TC', 0]
 		]
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql("""select account, sum(debit - credit) as amount
 			from `tabGL Entry`
 			where voucher_type='Purchase Invoice' and voucher_no=%s
+			group by account
 			order by account asc""", (purchase_invoice.name), as_dict=1)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
-			self.assertEqual(expected_gle[i][1], gle.debit)
-			self.assertEqual(expected_gle[i][2], gle.credit)
+			self.assertEqual(expected_gle[i][1], gle.amount)
 
 def update_tax_witholding_category(company, account, date):
 	from erpnext.accounts.utils import get_fiscal_year
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 114b7d2..dbc7f86 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1957,6 +1957,33 @@
 		einvoice = make_einvoice(si)
 		validate_totals(einvoice)
 
+	def test_item_tax_net_range(self):
+		item = create_item("T Shirt")
+
+		item.set('taxes', [])
+		item.append("taxes", {
+			"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
+			"minimum_net_rate": 0,
+			"maximum_net_rate": 500
+		})
+
+		item.append("taxes", {
+			"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
+			"minimum_net_rate": 501,
+			"maximum_net_rate": 1000
+		})
+
+		item.save()
+
+		sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True)
+		self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
+
+		# Apply discount
+		sales_invoice.apply_discount_on = 'Net Total'
+		sales_invoice.discount_amount = 300
+		sales_invoice.save()
+		self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
+
 def get_sales_invoice_for_e_invoice():
 	si = make_sales_invoice_for_ewaybill()
 	si.naming_series = 'INV-2020-.#####'
@@ -1985,32 +2012,6 @@
 
 	return si
 
-	def test_item_tax_net_range(self):
-		item = create_item("T Shirt")
-
-		item.set('taxes', [])
-		item.append("taxes", {
-			"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
-			"minimum_net_rate": 0,
-			"maximum_net_rate": 500
-		})
-
-		item.append("taxes", {
-			"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
-			"minimum_net_rate": 501,
-			"maximum_net_rate": 1000
-		})
-
-		item.save()
-
-		sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True)
-		self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
-
-		# Apply discount
-		sales_invoice.apply_discount_on = 'Net Total'
-		sales_invoice.discount_amount = 300
-		sales_invoice.save()
-		self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
 
 def make_test_address_for_ewaybill():
 	if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
@@ -2087,9 +2088,9 @@
 	if not gst_account:
 		gst_settings.append("gst_accounts", {
 			"company": "_Test Company",
-			"cgst_account": "CGST - _TC",
-			"sgst_account": "SGST - _TC",
-			"igst_account": "IGST - _TC",
+			"cgst_account": "Output Tax CGST - _TC",
+			"sgst_account": "Output Tax SGST - _TC",
+			"igst_account": "Output Tax IGST - _TC",
 		})
 
 	gst_settings.save()
@@ -2106,7 +2107,7 @@
 
 	si.append("taxes", {
 		"charge_type": "On Net Total",
-		"account_head": "CGST - _TC",
+		"account_head": "Output Tax CGST - _TC",
 		"cost_center": "Main - _TC",
 		"description": "CGST @ 9.0",
 		"rate": 9
@@ -2114,7 +2115,7 @@
 
 	si.append("taxes", {
 		"charge_type": "On Net Total",
-		"account_head": "SGST - _TC",
+		"account_head": "Output Tax SGST - _TC",
 		"cost_center": "Main - _TC",
 		"description": "SGST @ 9.0",
 		"rate": 9
diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.js b/erpnext/accounts/doctype/tax_rule/tax_rule.js
index 370890e..bc49716 100644
--- a/erpnext/accounts/doctype/tax_rule/tax_rule.js
+++ b/erpnext/accounts/doctype/tax_rule/tax_rule.js
@@ -1,24 +1,6 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-cur_frm.add_fetch("customer", "customer_group", "customer_group" );
-cur_frm.add_fetch("supplier", "supplier_group_name", "supplier_group" );
-
-frappe.ui.form.on("Tax Rule", "tax_type", function(frm) {
-	frm.toggle_reqd("sales_tax_template", frm.doc.tax_type=="Sales");
-	frm.toggle_reqd("purchase_tax_template", frm.doc.tax_type=="Purchase");
-})
-
-frappe.ui.form.on("Tax Rule", "onload", function(frm) {
-	if(frm.doc.__islocal) {
-		frm.set_value("use_for_shopping_cart", 1);
-	}
-})
-
-frappe.ui.form.on("Tax Rule", "refresh", function(frm) {
-	frappe.ui.form.trigger("Tax Rule", "tax_type");
-})
-
 frappe.ui.form.on("Tax Rule", "customer", function(frm) {
 	if(frm.doc.customer) {
 		frappe.call({
diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.json b/erpnext/accounts/doctype/tax_rule/tax_rule.json
index ef15538..2746748 100644
--- a/erpnext/accounts/doctype/tax_rule/tax_rule.json
+++ b/erpnext/accounts/doctype/tax_rule/tax_rule.json
@@ -1,1103 +1,250 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 0, 
- "autoname": "ACC-TAX-RULE-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2015-08-07 02:33:52.670866", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 0, 
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "ACC-TAX-RULE-.YYYY.-.#####",
+ "creation": "2015-08-07 02:33:52.670866",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+  "tax_type",
+  "use_for_shopping_cart",
+  "column_break_1",
+  "sales_tax_template",
+  "purchase_tax_template",
+  "filters",
+  "customer",
+  "supplier",
+  "item",
+  "billing_city",
+  "billing_county",
+  "billing_state",
+  "billing_zipcode",
+  "billing_country",
+  "tax_category",
+  "column_break_2",
+  "customer_group",
+  "supplier_group",
+  "item_group",
+  "shipping_city",
+  "shipping_county",
+  "shipping_state",
+  "shipping_zipcode",
+  "shipping_country",
+  "section_break_4",
+  "from_date",
+  "column_break_7",
+  "to_date",
+  "section_break_6",
+  "priority",
+  "column_break_20",
+  "company"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "Sales", 
-   "fieldname": "tax_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Tax Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Sales\nPurchase", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "Sales",
+   "fieldname": "tax_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Tax Type",
+   "options": "Sales\nPurchase"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "use_for_shopping_cart", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Use for Shopping Cart", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "1",
+   "fieldname": "use_for_shopping_cart",
+   "fieldtype": "Check",
+   "label": "Use for Shopping Cart"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_1", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_1",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.tax_type==\"Sales\"", 
-   "fieldname": "sales_tax_template", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Sales Tax Template", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Sales Taxes and Charges Template", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.tax_type==\"Sales\"",
+   "fieldname": "sales_tax_template",
+   "fieldtype": "Link",
+   "label": "Sales Tax Template",
+   "options": "Sales Taxes and Charges Template"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.tax_type==\"Purchase\"", 
-   "fieldname": "purchase_tax_template", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Purchase Tax Template", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Purchase Taxes and Charges Template", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.tax_type==\"Purchase\"",
+   "fieldname": "purchase_tax_template",
+   "fieldtype": "Link",
+   "label": "Purchase Tax Template",
+   "options": "Purchase Taxes and Charges Template"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "filters", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Filters", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "filters",
+   "fieldtype": "Section Break",
+   "label": "Filters"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.tax_type==\"Sales\"", 
-   "fieldname": "customer", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Customer", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Customer", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.tax_type==\"Sales\"",
+   "fieldname": "customer",
+   "fieldtype": "Link",
+   "label": "Customer",
+   "options": "Customer"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.tax_type==\"Purchase\"", 
-   "fieldname": "supplier", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Supplier", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Supplier", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.tax_type==\"Purchase\"",
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "label": "Supplier",
+   "options": "Supplier"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_city", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing City", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_city",
+   "fieldtype": "Data",
+   "label": "Billing City"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_county", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing County", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_county",
+   "fieldtype": "Data",
+   "label": "Billing County"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_state", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing State", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_state",
+   "fieldtype": "Data",
+   "label": "Billing State"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_zipcode", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Zipcode", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_zipcode",
+   "fieldtype": "Data",
+   "label": "Billing Zipcode"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_country", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Country", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Country", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_country",
+   "fieldtype": "Link",
+   "label": "Billing Country",
+   "options": "Country"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "tax_category", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Tax Category", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Tax Category", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "tax_category",
+   "fieldtype": "Link",
+   "label": "Tax Category",
+   "options": "Tax Category"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.tax_type==\"Sales\"", 
-   "fieldname": "customer_group", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Customer Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Customer Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.tax_type==\"Sales\"",
+   "fetch_from": "customer.customer_group",
+   "fieldname": "customer_group",
+   "fieldtype": "Link",
+   "label": "Customer Group",
+   "options": "Customer Group"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.tax_type==\"Purchase\"", 
-   "fieldname": "supplier_group", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Supplier Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Supplier Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.tax_type==\"Purchase\"",
+   "fetch_from": "supplier.supplier_group",
+   "fieldname": "supplier_group",
+   "fieldtype": "Link",
+   "label": "Supplier Group",
+   "options": "Supplier Group"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_group", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item Group", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_group",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "shipping_city", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Shipping City", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "shipping_city",
+   "fieldtype": "Data",
+   "label": "Shipping City"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "shipping_county", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Shipping County", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "shipping_county",
+   "fieldtype": "Data",
+   "label": "Shipping County"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "shipping_state", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Shipping State", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "shipping_state",
+   "fieldtype": "Data",
+   "label": "Shipping State"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "shipping_zipcode", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Shipping Zipcode", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "shipping_zipcode",
+   "fieldtype": "Data",
+   "label": "Shipping Zipcode"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "shipping_country", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Shipping Country", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Country", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "shipping_country",
+   "fieldtype": "Link",
+   "label": "Shipping Country",
+   "options": "Country"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_4", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Validity", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "label": "Validity"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "from_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "From Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "from_date",
+   "fieldtype": "Date",
+   "label": "From Date"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_7", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "to_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "To Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "to_date",
+   "fieldtype": "Date",
+   "label": "To Date"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_6", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "1", 
-   "fieldname": "priority", 
-   "fieldtype": "Int", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Priority", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "1",
+   "fieldname": "priority",
+   "fieldtype": "Int",
+   "label": "Priority"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_20", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_20",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "company", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Company", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "remember_last_selected_value": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-12-27 01:22:17.721636", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Tax Rule", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "links": [],
+ "modified": "2021-06-04 23:14:27.186879",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Tax Rule",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 1, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
index ac1ffd9..cf72268 100644
--- a/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/test_tax_rule.py
@@ -50,7 +50,7 @@
 		tax_rule1 = make_tax_rule(customer_group= "All Customer Groups",
 			sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01")
 		tax_rule1.save()
-		self.assertEqual(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":0}),
+		self.assertEqual(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":1}),
 			"_Test Sales Taxes and Charges Template - _TC")
 
 	def test_conflict_with_overlapping_dates(self):
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index e025fc6..b97dc40 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -542,6 +542,7 @@
 		select company, sum(debit_in_account_currency) - sum(credit_in_account_currency)
 		from `tabGL Entry`
 		where party_type = %s and party=%s
+		and is_cancelled = 0
 		group by company""", (party_type, party)))
 
 	for d in companies:
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index 9c9ada8..f1b231b 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -397,6 +397,7 @@
 				{'name': 'Budget', 'chartType': 'bar', 'values': budget_values},
 				{'name': 'Actual Expense', 'chartType': 'bar', 'values': actual_values}
 			]
-		}
+		},
+		'type' : 'bar'
 	}
 
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index 7793af7..56a67bb 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -380,7 +380,7 @@
 		gl_entries = frappe.db.sql("""select gl.posting_date, gl.account, gl.debit, gl.credit, gl.is_opening, gl.company,
 			gl.fiscal_year, gl.debit_in_account_currency, gl.credit_in_account_currency, gl.account_currency,
 			acc.account_name, acc.account_number
-			from `tabGL Entry` gl, `tabAccount` acc where acc.name = gl.account and gl.company = %(company)s
+			from `tabGL Entry` gl, `tabAccount` acc where acc.name = gl.account and gl.company = %(company)s and gl.is_cancelled = 0
 			{additional_conditions} and gl.posting_date <= %(to_date)s and acc.lft >= %(lft)s and acc.rgt <= %(rgt)s
 			order by gl.account, gl.posting_date""".format(additional_conditions=additional_conditions),
 			{
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 744ada9..e724e9b 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -48,13 +48,12 @@
 
 	if not filters.get("from_date") and not filters.get("to_date"):
 		frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
-
-	for account in filters.account:
-		if not account_details.get(account):
-			frappe.throw(_("Account {0} does not exists").format(account))
 			
 	if filters.get('account'):
 		filters.account = frappe.parse_json(filters.get('account'))
+		for account in filters.account:
+			if not account_details.get(account):
+				frappe.throw(_("Account {0} does not exists").format(account))
 
 	if (filters.get("account") and filters.get("group_by") == _('Group by Account')
 		and account_details[filters.account].is_group == 0):
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
index 60e675f..48bd730 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py
@@ -168,21 +168,24 @@
 			"label": _("Income"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 305
+
 		},
 		{
 			"fieldname": "expense",
 			"label": _("Expense"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 305
+
 		},
 		{
 			"fieldname": "gross_profit_loss",
 			"label": _("Gross Profit / Loss"),
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 120
+			"width": 307
+
 		}
 	]
 
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 66a9b60..ed6e28d 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -784,7 +784,7 @@
 	return acc
 
 def create_payment_gateway_account(gateway, payment_channel="Email"):
-	from erpnext.setup.setup_wizard.operations.company_setup import create_bank_account
+	from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account
 
 	company = frappe.db.get_value("Global Defaults", None, "default_company")
 	if not company:
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 1dbd7c6..132dd17 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -97,6 +97,9 @@
   "is_fixed_asset",
   "item_tax_rate",
   "section_break_72",
+  "production_plan",
+  "production_plan_item",
+  "production_plan_sub_assembly_item",
   "page_break"
  ],
  "fields": [
@@ -803,13 +806,37 @@
    "options": "Company:company:default_currency",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "production_plan",
+   "fieldtype": "Link",
+   "label": "Production Plan",
+   "options": "Production Plan",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "production_plan_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Production Plan Item",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "production_plan_sub_assembly_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Production Plan Sub Assembly Item",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-03-22 11:46:12.357435",
+ "modified": "2021-06-28 19:22:22.715365",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 5f759b4..80ccc6d 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -99,9 +99,10 @@
 								frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
 									.format(d.idx, s, doc.doctype, doc.return_against))
 
-				if warehouse_mandatory and frappe.db.get_value("Item", d.item_code, "is_stock_item") \
-					and not d.get("warehouse"):
-						frappe.throw(_("Warehouse is mandatory"))
+				if (warehouse_mandatory and not d.get("warehouse") and
+					frappe.db.get_value("Item", d.item_code, "is_stock_item")
+				):
+					frappe.throw(_("Warehouse is mandatory"))
 
 			items_returned = True
 
@@ -462,4 +463,4 @@
 	for row in frappe.get_all(parent_doc.doctype, fields = fields, filters=filters):
 		serial_nos.extend(get_serial_nos(row.serial_no))
 
-	return serial_nos
\ No newline at end of file
+	return serial_nos
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 8196cff..2526e6d 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -356,42 +356,68 @@
 		}, update_modified)
 
 	def validate_inspection(self):
-		'''Checks if quality inspection is set for Items that require inspection.
-		On submit, throw an exception'''
-		inspection_required_fieldname = None
-		if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
-			inspection_required_fieldname = "inspection_required_before_purchase"
-		elif self.doctype in ["Delivery Note", "Sales Invoice"]:
-			inspection_required_fieldname = "inspection_required_before_delivery"
+		"""Checks if quality inspection is set/ is valid for Items that require inspection."""
+		inspection_fieldname_map = {
+			"Purchase Receipt": "inspection_required_before_purchase",
+			"Purchase Invoice": "inspection_required_before_purchase",
+			"Sales Invoice": "inspection_required_before_delivery",
+			"Delivery Note": "inspection_required_before_delivery"
+		}
+		inspection_required_fieldname = inspection_fieldname_map.get(self.doctype)
 
+		# return if inspection is not required on document level
 		if ((not inspection_required_fieldname and self.doctype != "Stock Entry") or
 			(self.doctype == "Stock Entry" and not self.inspection_required) or
 			(self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock)):
 				return
 
-		for d in self.get('items'):
-			qa_required = False
-			if (inspection_required_fieldname and not d.quality_inspection and
-				frappe.db.get_value("Item", d.item_code, inspection_required_fieldname)):
-				qa_required = True
-			elif self.doctype == "Stock Entry" and not d.quality_inspection and d.t_warehouse:
-				qa_required = True
-			if self.docstatus == 1 and d.quality_inspection:
-				qa_doc = frappe.get_doc("Quality Inspection", d.quality_inspection)
-				if qa_doc.docstatus == 0:
-					link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection)
-					frappe.throw(_("Quality Inspection: {0} is not submitted for the item: {1} in row {2}").format(link, d.item_code, d.idx), QualityInspectionNotSubmittedError)
+		for row in self.get('items'):
+			qi_required = False
+			if (inspection_required_fieldname and frappe.db.get_value("Item", row.item_code, inspection_required_fieldname)):
+				qi_required = True
+			elif self.doctype == "Stock Entry" and row.t_warehouse:
+				qi_required = True # inward stock needs inspection
 
-				if qa_doc.status != 'Accepted':
-					frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
-						.format(d.idx, d.item_code), QualityInspectionRejectedError)
-			elif qa_required :
-				action = frappe.get_doc('Stock Settings').action_if_quality_inspection_is_not_submitted
-				if self.docstatus==1 and action == 'Stop':
-					frappe.throw(_("Quality Inspection required for Item {0} to submit").format(frappe.bold(d.item_code)),
-						exc=QualityInspectionRequiredError)
-				else:
-					frappe.msgprint(_("Create Quality Inspection for Item {0}").format(frappe.bold(d.item_code)))
+			if qi_required: # validate row only if inspection is required on item level
+				self.validate_qi_presence(row)
+				if self.docstatus == 1:
+					self.validate_qi_submission(row)
+					self.validate_qi_rejection(row)
+
+	def validate_qi_presence(self, row):
+		"""Check if QI is present on row level. Warn on save and stop on submit if missing."""
+		if not row.quality_inspection:
+			msg = f"Row #{row.idx}: Quality Inspection is required for Item {frappe.bold(row.item_code)}"
+			if self.docstatus == 1:
+				frappe.throw(_(msg), title=_("Inspection Required"), exc=QualityInspectionRequiredError)
+			else:
+				frappe.msgprint(_(msg), title=_("Inspection Required"), indicator="blue")
+
+	def validate_qi_submission(self, row):
+		"""Check if QI is submitted on row level, during submission"""
+		action = frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_not_submitted")
+		qa_docstatus = frappe.db.get_value("Quality Inspection", row.quality_inspection, "docstatus")
+
+		if not qa_docstatus == 1:
+			link = frappe.utils.get_link_to_form('Quality Inspection', row.quality_inspection)
+			msg = f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
+			if action == "Stop":
+				frappe.throw(_(msg), title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
+			else:
+				frappe.msgprint(_(msg), alert=True, indicator="orange")
+
+	def validate_qi_rejection(self, row):
+		"""Check if QI is rejected on row level, during submission"""
+		action = frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_rejected")
+		qa_status = frappe.db.get_value("Quality Inspection", row.quality_inspection, "status")
+
+		if qa_status == "Rejected":
+			link = frappe.utils.get_link_to_form('Quality Inspection', row.quality_inspection)
+			msg = f"Row #{row.idx}: Quality Inspection {link} was rejected for item {row.item_code}"
+			if action == "Stop":
+				frappe.throw(_(msg), title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
+			else:
+				frappe.msgprint(_(msg), alert=True, indicator="orange")
 
 	def update_blanket_order(self):
 		blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))
diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json
index 8517dde..306be7f 100644
--- a/erpnext/crm/doctype/appointment/appointment.json
+++ b/erpnext/crm/doctype/appointment/appointment.json
@@ -102,7 +102,7 @@
   }
  ],
  "links": [],
- "modified": "2020-01-28 16:16:45.447213",
+ "modified": "2021-06-29 18:27:02.832979",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Appointment",
@@ -153,6 +153,18 @@
    "role": "Sales User",
    "share": 1,
    "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "write": 1
   }
  ],
  "quick_entry": 1,
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index d1d0968..ce3de40 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -168,12 +168,13 @@
 		if self.phone:
 			contact.append("phone_nos", {
 				"phone": self.phone,
-				"is_primary": 1
+				"is_primary_phone": 1
 			})
 
 		if self.mobile_no:
 			contact.append("phone_nos", {
-				"phone": self.mobile_no
+				"phone": self.mobile_no,
+				"is_primary_mobile_no":1
 			})
 
 		contact.insert(ignore_permissions=True)
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
index 9db8a4a..3070e6a 100644
--- a/erpnext/education/utils.py
+++ b/erpnext/education/utils.py
@@ -355,11 +355,11 @@
 	student = get_current_student()
 	course_enrollment = get_enrollment("course", course, student.name)
 	if not course_enrollment:
-		program_enrollment = get_enrollment('program', program, student.name)
+		program_enrollment = get_enrollment('program', program.name, student.name)
 		if not program_enrollment:
 			frappe.throw(_("You are not enrolled in program {0}").format(program))
 			return
-		return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program, student.name))
+		return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program.name, student.name))
 	else:
 		return frappe.get_doc('Course Enrollment', course_enrollment)
 
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
index 3c2e59a..b0e662d 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -7,16 +7,21 @@
 import unittest
 from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import process_balance_info, verify_transaction
 from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+from erpnext.erpnext_integrations.utils import create_mode_of_payment
 
 class TestMpesaSettings(unittest.TestCase):
+	def setUp(self):
+		# create payment gateway in setup
+		create_mpesa_settings(payment_gateway_name="_Test")
+		create_mpesa_settings(payment_gateway_name="_Account Balance")
+		create_mpesa_settings(payment_gateway_name="Payment")
+
 	def tearDown(self):
 		frappe.db.sql('delete from `tabMpesa Settings`')
 		frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"')
 
 	def test_creation_of_payment_gateway(self):
-		create_mpesa_settings(payment_gateway_name="_Test")
-
-		mode_of_payment = frappe.get_doc("Mode of Payment", "Mpesa-_Test")
+		mode_of_payment = create_mode_of_payment('Mpesa-_Test', payment_type="Phone")
 		self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"}))
 		self.assertTrue(mode_of_payment.name)
 		self.assertEqual(mode_of_payment.type, "Phone")
@@ -47,7 +52,6 @@
 		integration_request.delete()
 
 	def test_processing_of_callback_payload(self):
-		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
@@ -90,7 +94,6 @@
 		pos_invoice.delete()
 
 	def test_processing_of_multiple_callback_payload(self):
-		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
@@ -141,7 +144,6 @@
 		pos_invoice.delete()
 
 	def test_processing_of_only_one_succes_callback_payload(self):
-		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
@@ -202,6 +204,7 @@
 
 	doc = frappe.get_doc(dict( #nosec
 		doctype="Mpesa Settings",
+		sandbox=1,
 		payment_gateway_name=payment_gateway_name,
 		consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn",
 		consumer_secret="VI1oS3oBGPJfh3JyvLHw",
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 3840e78..a5e162f 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -52,7 +52,8 @@
 			"payment_gateway": gateway
 		}, ['payment_account'])
 
-	if not frappe.db.exists("Mode of Payment", gateway) and payment_gateway_account:
+	mode_of_payment = frappe.db.exists("Mode of Payment", gateway) 
+	if not mode_of_payment and payment_gateway_account:
 		mode_of_payment = frappe.get_doc({
 			"doctype": "Mode of Payment",
 			"mode_of_payment": gateway,
@@ -66,6 +67,10 @@
 		})
 		mode_of_payment.insert(ignore_permissions=True)
 
+		return mode_of_payment
+	elif mode_of_payment:
+		return frappe.get_doc("Mode of Payment", mode_of_payment)
+
 def get_tracking_url(carrier, tracking_number):
 	# Return the formatted Tracking URL.
 	tracking_url = ''
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 8ad77a1..52daec9 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -157,6 +157,7 @@
 			"parents": [{"label": _("Material Request"), "route": "material-requests"}]
 		}
 	},
+	{"from_route": "/project", "to_route": "Project"}
 ]
 
 standard_portal_menu_items = [
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 578eccf..96ea686 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -72,7 +72,8 @@
 	def test_expense_claim_gl_entry(self):
 		payable_account = get_payable_account(company_name)
 		taxes = generate_taxes()
-		expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes)
+		expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", 
+			do_not_submit=True, taxes=taxes)
 		expense_claim.submit()
 
 		gl_entries = frappe.db.sql("""select account, debit, credit
@@ -82,7 +83,7 @@
 		self.assertTrue(gl_entries)
 
 		expected_values = dict((d[0], d) for d in [
-			['CGST - _TC4',18.0, 0.0],
+			['Output Tax CGST - _TC4',18.0, 0.0],
 			[payable_account, 0.0, 218.0],
 			["Travel Expenses - _TC4", 200.0, 0.0]
 		])
@@ -145,7 +146,7 @@
 	parent_account = frappe.db.get_value('Account',
 		{'company': company_name, 'is_group':1, 'account_type': 'Tax'},
 		'name')
-	account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account)
+	account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
 	return {'taxes':[{
 		"account_head": account,
 		"rate": 0,
diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js
index b7d34b1..d5f6e5f 100644
--- a/erpnext/hr/doctype/training_event/training_event.js
+++ b/erpnext/hr/doctype/training_event/training_event.js
@@ -20,11 +20,10 @@
 				frappe.set_route("List", "Training Feedback");
 			});
 		}
-	}
-});
+		frm.events.set_employee_query(frm);
+	},
 
-frappe.ui.form.on("Training Event Employee", {
-	employee: function (frm) {
+	set_employee_query: function(frm) {
 		let emp = [];
 		for (let d in frm.doc.employees) {
 			if (frm.doc.employees[d].employee) {
@@ -34,9 +33,17 @@
 		frm.set_query("employee", "employees", function () {
 			return {
 				filters: {
-					name: ["NOT IN", emp]
+					name: ["NOT IN", emp],
+					status: "Active"
 				}
 			};
 		});
 	}
 });
+
+frappe.ui.form.on("Training Event Employee", {
+	employee: function(frm) {
+		frm.events.set_employee_query(frm);
+	}
+});
+
diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.json b/erpnext/hr/doctype/training_event_employee/training_event_employee.json
index 2d313e9..bcb7d5e 100644
--- a/erpnext/hr/doctype/training_event_employee/training_event_employee.json
+++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.json
@@ -19,6 +19,7 @@
    "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Employee",
+   "no_copy": 1,
    "options": "Employee"
   },
   {
@@ -68,7 +69,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-05-21 12:41:59.336237",
+ "modified": "2021-07-02 17:20:27.630176",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Training Event Employee",
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
index 18bd4ae..68bac8e 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
@@ -35,7 +35,9 @@
    "no_copy": 1,
    "options": "Loan Security Pledge",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fetch_from": "loan_application.applicant",
@@ -45,47 +47,63 @@
    "in_standard_filter": 1,
    "label": "Applicant",
    "options": "applicant_type",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "loan_security_details_section",
    "fieldtype": "Section Break",
-   "label": "Loan Security Details"
+   "label": "Loan Security Details",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "loan",
    "fieldtype": "Link",
    "label": "Loan",
-   "options": "Loan"
+   "options": "Loan",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "loan_application",
    "fieldtype": "Link",
    "label": "Loan Application",
-   "options": "Loan Application"
+   "options": "Loan Application",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "total_security_value",
    "fieldtype": "Currency",
    "label": "Total Security Value",
    "options": "Company:company:default_currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "maximum_loan_value",
    "fieldtype": "Currency",
    "label": "Maximum Loan Value",
    "options": "Company:company:default_currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "loan_details_section",
    "fieldtype": "Section Break",
-   "label": "Loan  Details"
+   "label": "Loan  Details",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "Requested",
@@ -94,37 +112,49 @@
    "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Status",
-   "options": "Requested\nUnpledged\nPledged\nPartially Pledged",
-   "read_only": 1
+   "options": "Requested\nUnpledged\nPledged\nPartially Pledged\nCancelled",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "pledge_time",
    "fieldtype": "Datetime",
    "label": "Pledge Time",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "securities",
    "fieldtype": "Table",
    "label": "Securities",
    "options": "Pledge",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "section_break_10",
    "fieldtype": "Section Break",
-   "label": "Totals"
+   "label": "Totals",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "company",
    "fieldtype": "Link",
    "label": "Company",
    "options": "Company",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fetch_from": "loan.applicant_type",
@@ -132,35 +162,45 @@
    "fieldtype": "Select",
    "label": "Applicant Type",
    "options": "Employee\nMember\nCustomer",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "more_information_section",
    "fieldtype": "Section Break",
-   "label": "More Information"
+   "label": "More Information",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "fieldname": "reference_no",
    "fieldtype": "Data",
-   "label": "Reference No"
+   "label": "Reference No",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_18",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "fieldname": "description",
    "fieldtype": "Text",
-   "label": "Description"
+   "label": "Description",
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-19 18:23:16.953305",
+ "modified": "2021-06-29 17:15:16.082256",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Security Pledge",
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
index cbc8376..c390b6c 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
@@ -23,6 +23,12 @@
 			update_shortfall_status(self.loan, self.total_security_value)
 			update_loan(self.loan, self.maximum_loan_value)
 
+	def on_cancel(self):
+		if self.loan:
+			self.db_set("status", "Cancelled")
+			self.db_set("pledge_time", None)
+			update_loan(self.loan, self.maximum_loan_value, cancel=1)
+
 	def validate_duplicate_securities(self):
 		security_list = []
 		for security in self.securities:
@@ -36,7 +42,7 @@
 		existing_pledge = ''
 
 		if self.loan:
-			existing_pledge = frappe.db.get_value('Loan Security Pledge', {'loan': self.loan}, ['name'])
+			existing_pledge = frappe.db.get_value('Loan Security Pledge', {'loan': self.loan, 'docstatus': 1}, ['name'])
 
 		if existing_pledge:
 			loan_security_type = frappe.db.get_value('Pledge', {'parent': existing_pledge}, ['loan_security_type'])
@@ -77,8 +83,12 @@
 		self.total_security_value = total_security_value
 		self.maximum_loan_value = maximum_loan_value
 
-def update_loan(loan, maximum_value_against_pledge):
+def update_loan(loan, maximum_value_against_pledge, cancel=0):
 	maximum_loan_value = frappe.db.get_value('Loan', {'name': loan}, ['maximum_loan_amount'])
 
-	frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
-		WHERE name=%s""", (maximum_loan_value + maximum_value_against_pledge, loan))
+	if cancel:
+		frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s
+			WHERE name=%s""", (maximum_loan_value - maximum_value_against_pledge, loan))
+	else:
+		frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
+			WHERE name=%s""", (maximum_loan_value + maximum_value_against_pledge, loan))
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
index 4c31bd0..f19a1b0 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
@@ -13,7 +13,7 @@
 
 	refresh: function(frm) {
 		erpnext.hide_company();
-		if (frm.doc.customer && frm.doc.docstatus === 1) {
+		if (frm.doc.customer && frm.doc.docstatus === 1 && frm.doc.to_date > frappe.datetime.get_today()) {
 			frm.add_custom_button(__("Sales Order"), function() {
 				frappe.model.open_mapped_doc({
 					method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
index 0330e5c..a63fc4d 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "naming_series:",
  "creation": "2018-05-24 07:18:08.256060",
  "doctype": "DocType",
@@ -79,6 +80,7 @@
    "reqd": 1
   },
   {
+   "allow_on_submit": 1,
    "fieldname": "to_date",
    "fieldtype": "Date",
    "label": "To Date",
@@ -129,8 +131,10 @@
    "label": "Terms and Conditions Details"
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
- "modified": "2019-11-18 19:37:37.151686",
+ "links": [],
+ "modified": "2021-06-29 00:30:30.621636",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Blanket Order",
diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json
index f38d1b9..7e53918 100644
--- a/erpnext/manufacturing/doctype/bom/bom.json
+++ b/erpnext/manufacturing/doctype/bom/bom.json
@@ -36,6 +36,9 @@
   "materials_section",
   "inspection_required",
   "quality_inspection_template",
+  "column_break_31",
+  "bom_level",
+  "section_break_33",
   "items",
   "scrap_section",
   "scrap_items",
@@ -513,6 +516,22 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_31",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "bom_level",
+   "fieldtype": "Int",
+   "label": "BOM Level",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_33",
+   "fieldtype": "Section Break",
+   "hide_border": 1
   }
  ],
  "icon": "fa fa-sitemap",
@@ -520,7 +539,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-16 12:25:09.081968",
+ "modified": "2021-05-16 12:25:09.081968",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index c58f017..c32a8a9 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -154,6 +154,7 @@
 		self.calculate_cost()
 		self.update_stock_qty()
 		self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate = False, save=False)
+		self.set_bom_level()
 
 	def get_context(self, context):
 		context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
@@ -676,6 +677,19 @@
 		"""Get a complete tree representation preserving order of child items."""
 		return BOMTree(self.name)
 
+	def set_bom_level(self, update=False):
+		levels = []
+
+		self.bom_level = 0
+		for row in self.items:
+			if row.bom_no:
+				levels.append(frappe.get_cached_value("BOM", row.bom_no, "bom_level") or 0)
+
+		if levels:
+			self.bom_level = max(levels) + 1
+
+		if update:
+			self.db_set("bom_level", self.bom_level)
 
 def get_bom_item_rate(args, bom_doc):
 	if bom_doc.rm_cost_as_per == 'Valuation Rate':
@@ -860,7 +874,7 @@
 		frappe.form_dict.parent = parent
 
 	if frappe.form_dict.parent:
-		bom_doc = frappe.get_doc("BOM", frappe.form_dict.parent)
+		bom_doc = frappe.get_cached_doc("BOM", frappe.form_dict.parent)
 		frappe.has_permission("BOM", doc=bom_doc, throw=True)
 
 		bom_items = frappe.get_all('BOM Item',
@@ -871,7 +885,7 @@
 		item_names = tuple(d.get('item_code') for d in bom_items)
 
 		items = frappe.get_list('Item',
-			fields=['image', 'description', 'name', 'stock_uom', 'item_name'],
+			fields=['image', 'description', 'name', 'stock_uom', 'item_name', 'is_sub_contracted_item'],
 			filters=[['name', 'in', item_names]]) # to get only required item dicts
 
 		for bom_item in bom_items:
@@ -884,6 +898,7 @@
 
 			bom_item.parent_bom_qty = bom_doc.quantity
 			bom_item.expandable = 0 if bom_item.value in ('', None)  else 1
+			bom_item.image = frappe.db.escape(bom_item.image)
 
 		return bom_items
 
@@ -1100,6 +1115,8 @@
 		},
 		'BOM Item': {
 			'doctype': 'BOM Item',
+			# stop get_mapped_doc copying parent bom_no to children
+			'field_no_map': ['bom_no'],
 			'condition': lambda doc: doc.has_variants == 0
 		},
 	}, target_doc, postprocess)
diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
index 6cd5f8c..6088e46 100644
--- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html
+++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
@@ -1,13 +1,31 @@
 <div style="padding: 15px;">
-	{% if data.image %}
-	<img class="responsive" src={{ data.image }}>
-	<hr style="margin: 15px -15px;">
-	{% endif %}
-	<h4>
-		{{ __("Description") }}
-	</h4>
-	<div style="padding-top: 10px;">
-		{{ data.description }}
+	<div class="row mb-5">
+		<div class="col-md-5" style="max-height: 500px">
+			{% if data.image %}
+				<div class="border image-field " style="overflow: hidden;border-color:#e6e6e6">
+					<img class="responsive" src={{ data.image }}>
+				</div>
+			{% endif %}
+		</div>
+		<div class="col-md-7 h-500">
+			<h4>
+				{{ __("Description") }}
+			</h4>
+			<div style="padding-top: 10px;">
+				{{ data.description }}
+			</div>
+			<hr style="margin: 15px -15px;">
+			<p>
+				{% if data.value %}
+				<a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="#Form/BOM/{{ data.value }}">
+					{{ __("Open BOM {0}", [data.value.bold()]) }}</a>
+				{% endif %}
+				{% if data.item_code %}
+				<a class="btn btn-default btn-xs" href="#Form/Item/{{ data.item_code }}">
+					{{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
+				{% endif %}
+			</p>
+		</div>
 	</div>
 	<hr style="margin: 15px -15px;">
 	<p>
diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js
index 185b9ed..60fb377 100644
--- a/erpnext/manufacturing/doctype/bom/bom_tree.js
+++ b/erpnext/manufacturing/doctype/bom/bom_tree.js
@@ -64,7 +64,7 @@
 		if(node.is_root && node.data.value!="BOM") {
 			frappe.model.with_doc("BOM", node.data.value, function() {
 				var bom = frappe.model.get_doc("BOM", node.data.value);
-				node.data.image = bom.image || "";
+				node.data.image = escape(bom.image) || "";
 				node.data.description = bom.description || "";
 			});
 		}
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 7f8f2ef..420bb00 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -192,15 +192,20 @@
 						"completed_qty": args.get("completed_qty") or 0.0
 					})
 		elif args.get("start_time"):
-			for name in employees:
-				self.append("time_logs", {
-					"from_time": get_datetime(args.get("start_time")),
-					"employee": name.get('employee'),
-					"operation": args.get("sub_operation"),
-					"completed_qty": 0.0
-				})
+			new_args = {
+				"from_time": get_datetime(args.get("start_time")),
+				"operation": args.get("sub_operation"),
+				"completed_qty": 0.0
+			}
 
-		if not self.employee:
+			if employees:
+				for name in employees:
+					new_args.employee = name.get('employee')
+					self.add_start_time_log(new_args)
+			else:
+				self.add_start_time_log(new_args)
+
+		if not self.employee and employees:
 			self.set_employees(employees)
 
 		if self.status == "On Hold":
@@ -208,6 +213,9 @@
 
 		self.save()
 
+	def add_start_time_log(self, args):
+		self.append("time_logs", args)
+
 	def set_employees(self, employees):
 		for name in employees:
 			self.append('employee', {
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 450aa04..d198a69 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -4,7 +4,7 @@
 frappe.ui.form.on('Production Plan', {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
-			'Work Order': 'Work Order',
+			'Work Order': 'Work Order / Subcontract PO',
 			'Material Request': 'Material Request',
 		};
 
@@ -68,17 +68,13 @@
 			frm.trigger("show_progress");
 
 			if (frm.doc.status !== "Completed") {
-				if (frm.doc.po_items && frm.doc.status !== "Closed") {
-					frm.add_custom_button(__("Work Order"), ()=> {
-						frm.trigger("make_work_order");
-					}, __('Create'));
-				}
+				frm.add_custom_button(__("Work Order Tree"), ()=> {
+					frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name});
+				}, __('View'));
 
-				if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
-					frm.add_custom_button(__("Material Request"), ()=> {
-						frm.trigger("make_material_request");
-					}, __('Create'));
-				}
+				frm.add_custom_button(__("Production Plan Summary"), ()=> {
+					frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
+				}, __('View'));
 
 				if  (frm.doc.status === "Closed") {
 					frm.add_custom_button(__("Re-open"), function() {
@@ -89,6 +85,18 @@
 						frm.events.close_open_production_plan(frm, true);
 					}, __("Status"));
 				}
+
+				if (frm.doc.po_items && frm.doc.status !== "Closed") {
+					frm.add_custom_button(__("Work Order / Subcontract PO"), ()=> {
+						frm.trigger("make_work_order");
+					}, __('Create'));
+				}
+
+				if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
+					frm.add_custom_button(__("Material Request"), ()=> {
+						frm.trigger("make_material_request");
+					}, __('Create'));
+				}
 			}
 		}
 
@@ -233,6 +241,17 @@
 		});
 	},
 
+	get_sub_assembly_items: function(frm) {
+		frappe.call({
+			method: "get_sub_assembly_items",
+			freeze: true,
+			doc: frm.doc,
+			callback: function() {
+				refresh_field("sub_assembly_items");
+			}
+		});
+	},
+
 	get_items_for_mr: function(frm) {
 		if (!frm.doc.for_warehouse) {
 			frappe.throw(__("Select warehouse for material requests"));
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 1c0dde2..8437895 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -32,6 +32,9 @@
   "po_items",
   "section_break_25",
   "prod_plan_references",
+  "section_break_24",
+  "get_sub_assembly_items",
+  "sub_assembly_items",
   "material_request_planning",
   "include_non_stock_items",
   "include_subcontracted_items",
@@ -187,7 +190,7 @@
    "depends_on": "get_items_from",
    "fieldname": "get_items",
    "fieldtype": "Button",
-   "label": "Get Items For Work Order"
+   "label": "Get Finished Goods for Manufacture"
   },
   {
    "fieldname": "po_items",
@@ -199,7 +202,7 @@
   {
    "fieldname": "material_request_planning",
    "fieldtype": "Section Break",
-   "label": "Material Request Planning"
+   "label": "Material Requirement Planning"
   },
   {
    "default": "1",
@@ -237,12 +240,13 @@
   },
   {
    "fieldname": "section_break_27",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "hide_border": 1
   },
   {
    "fieldname": "mr_items",
    "fieldtype": "Table",
-   "label": "Material Request Plan Item",
+   "label": "Raw Materials",
    "no_copy": 1,
    "options": "Material Request Plan Item"
   },
@@ -337,13 +341,30 @@
    "hidden": 1,
    "label": "Production Plan Item Reference",
    "options": "Production Plan Item Reference"
+  },
+  {
+   "fieldname": "section_break_24",
+   "fieldtype": "Section Break",
+   "hide_border": 1
+  },
+  {
+   "fieldname": "sub_assembly_items",
+   "fieldtype": "Table",
+   "label": "Sub Assembly Items",
+   "no_copy": 1,
+   "options": "Production Plan Sub Assembly Item"
+  },
+  {
+   "fieldname": "get_sub_assembly_items",
+   "fieldtype": "Button",
+   "label": "Get Sub Assembly Items"
   }
  ],
  "icon": "fa fa-calendar",
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-05-24 16:59:03.643211",
+ "modified": "2021-06-28 20:00:33.905114",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 0ede1bd..38a0ee7 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -5,10 +5,11 @@
 from __future__ import unicode_literals
 import frappe, json, copy
 from frappe import msgprint, _
-from six import string_types, iteritems
+from six import iteritems
 
 from frappe.model.document import Document
-from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime, ceil
+from frappe.utils import (flt, cint, nowdate, add_days, comma_and, now_datetime,
+	ceil, get_link_to_form, getdate)
 from frappe.utils.csvutils import build_csv_response
 from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_children
 from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
@@ -349,49 +350,88 @@
 
 	@frappe.whitelist()
 	def make_work_order(self):
-		wo_list = []
+		wo_list, po_list = [], []
+		subcontracted_po = {}
+
 		self.validate_data()
+		self.make_work_order_for_finished_goods(wo_list)
+		self.make_work_order_for_subassembly_items(wo_list, subcontracted_po)
+		self.make_subcontracted_purchase_order(subcontracted_po, po_list)
+		self.show_list_created_message('Work Order', wo_list)
+		self.show_list_created_message('Purchase Order', po_list)
+
+	def make_work_order_for_finished_goods(self, wo_list):
 		items_data = self.get_production_items()
 
 		for key, item in items_data.items():
+			if self.sub_assembly_items:
+				item['use_multi_level_bom'] = 0
+
 			work_order = self.create_work_order(item)
 			if work_order:
 				wo_list.append(work_order)
 
-			if item.get("make_work_order_for_sub_assembly_items"):
-				work_orders = self.make_work_order_for_sub_assembly_items(item)
-				wo_list.extend(work_orders)
+	def make_work_order_for_subassembly_items(self, wo_list, subcontracted_po):
+		for row in self.sub_assembly_items:
+			if row.type_of_manufacturing == 'Subcontract':
+				subcontracted_po.setdefault(row.supplier, []).append(row)
+				continue
+
+			args = {}
+			self.prepare_args_for_sub_assembly_items(row, args)
+			work_order = self.create_work_order(args)
+			if work_order:
+				wo_list.append(work_order)
+
+	def make_subcontracted_purchase_order(self, subcontracted_po, purchase_orders):
+		if not subcontracted_po:
+			return
+
+		for supplier, po_list in subcontracted_po.items():
+			po = frappe.new_doc('Purchase Order')
+			po.supplier = supplier
+			po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
+			po.is_subcontracted_item = 'Yes'
+			for row in po_list:
+				args = {
+					'item_code': row.production_item,
+					'warehouse': row.fg_warehouse,
+					'production_plan_sub_assembly_item': row.name,
+					'bom': row.bom_no,
+					'production_plan': self.name
+				}
+
+				for field in ['schedule_date', 'qty', 'uom', 'stock_uom', 'item_name',
+					'description', 'production_plan_item']:
+					args[field] = row.get(field)
+
+				po.append('items', args)
+
+			po.set_missing_values()
+			po.flags.ignore_mandatory = True
+			po.flags.ignore_validate = True
+			po.insert()
+			purchase_orders.append(po.name)
+
+	def show_list_created_message(self, doctype, doc_list=None):
+		if not doc_list:
+			return
 
 		frappe.flags.mute_messages = False
+		if doc_list:
+			doc_list = [get_link_to_form(doctype, p) for p in doc_list]
+			msgprint(_("{0} created").format(comma_and(doc_list)))
 
-		if wo_list:
-			wo_list = ["""<a href="/app/Form/Work Order/%s" target="_blank">%s</a>""" % \
-				(p, p) for p in wo_list]
-			msgprint(_("{0} created").format(comma_and(wo_list)))
-		else :
-			msgprint(_("No Work Orders created"))
+	def prepare_args_for_sub_assembly_items(self, row, args):
+		for field in ["production_item", "item_name", "qty", "fg_warehouse",
+			"description", "bom_no", "stock_uom", "bom_level", "production_plan_item"]:
+			args[field] = row.get(field)
 
-	def make_work_order_for_sub_assembly_items(self, item):
-		work_orders = []
-		bom_data = {}
-
-		get_sub_assembly_items(item.get("bom_no"), bom_data, item.get("qty"))
-
-		for key, data in bom_data.items():
-			data.update({
-				'qty': data.get("stock_qty"),
-				'production_plan': self.name,
-				'use_multi_level_bom': item.get("use_multi_level_bom"),
-				'company': self.company,
-				'fg_warehouse': item.get("fg_warehouse"),
-				'update_consumed_material_cost_in_project': 0
-			})
-
-			work_order = self.create_work_order(data)
-			if work_order:
-				work_orders.append(work_order)
-
-		return work_orders
+		args.update({
+			"use_multi_level_bom": 0,
+			"production_plan": self.name,
+			"production_plan_sub_assembly_item": row.name
+		})
 
 	def create_work_order(self, item):
 		from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError, get_default_warehouse
@@ -476,9 +516,32 @@
 		else :
 			msgprint(_("No material request created"))
 
+	@frappe.whitelist()
+	def get_sub_assembly_items(self, manufacturing_type=None):
+		self.sub_assembly_items = []
+		for row in self.po_items:
+			bom_data = []
+			get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
+			self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
+
+		self.save()
+
+	def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
+		bom_data = sorted(bom_data, key = lambda i: i.bom_level)
+
+		for data in bom_data:
+			data.qty = data.stock_qty
+			data.production_plan_item = row.name
+			data.fg_warehouse = row.warehouse
+			data.schedule_date = row.planned_start_date
+			data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
+				else "In House")
+
+			self.append("sub_assembly_items", data)
+
 @frappe.whitelist()
 def download_raw_materials(doc, warehouses=None):
-	if isinstance(doc, string_types):
+	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
 	item_list = [['Item Code', 'Description', 'Stock UOM', 'Warehouse', 'Required Qty as per BOM',
@@ -660,7 +723,7 @@
 
 @frappe.whitelist()
 def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
-	if isinstance(row, string_types):
+	if isinstance(row, str):
 		row = frappe._dict(json.loads(row))
 
 	company = frappe.db.escape(company)
@@ -684,8 +747,11 @@
 		group by item_code, warehouse
 	""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
 
-def get_warehouse_list(warehouses, warehouse_list=[]):
-	if isinstance(warehouses, string_types):
+def get_warehouse_list(warehouses, warehouse_list=None):
+	if not warehouse_list:
+		warehouse_list = []
+
+	if isinstance(warehouses, str):
 		warehouses = json.loads(warehouses)
 
 	for row in warehouses:
@@ -697,7 +763,7 @@
 
 @frappe.whitelist()
 def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
-	if isinstance(doc, string_types):
+	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
 	warehouse_list = []
@@ -726,6 +792,9 @@
 
 	so_item_details = frappe._dict()
 	for data in po_items:
+		if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
+			data["include_exploded_items"] = 1
+
 		planned_qty = data.get('required_qty') or data.get('planned_qty')
 		ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
 		warehouse = doc.get('for_warehouse')
@@ -857,23 +926,28 @@
 #		"description": item_details.get("description")
 	}
 
-def get_sub_assembly_items(bom_no, bom_data, to_produce_qty):
+def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
 	data = get_children('BOM', parent = bom_no)
 	for d in data:
 		if d.expandable:
-			key = (d.name, d.value)
-			if key not in bom_data:
-				bom_data.setdefault(key, {
-					'stock_qty': 0,
-					'description': d.description,
-					'production_item': d.item_code,
-					'item_name': d.item_name,
-					'stock_uom': d.stock_uom,
-					'uom': d.stock_uom,
-					'bom_no': d.value
-				})
+			parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
+			bom_level = (frappe.get_cached_value("BOM", d.value, "bom_level")
+				if d.value else 0)
 
-			bom_item = bom_data.get(key)
-			bom_item["stock_qty"] += (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
+			stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
+			bom_data.append(frappe._dict({
+				'parent_item_code': parent_item_code,
+				'description': d.description,
+				'production_item': d.item_code,
+				'item_name': d.item_name,
+				'stock_uom': d.stock_uom,
+				'uom': d.stock_uom,
+				'bom_no': d.value,
+				'is_sub_contracted_item': d.is_sub_contracted_item,
+				'bom_level': bom_level,
+				'indent': indent,
+				'stock_qty': stock_qty
+			}))
 
-			get_sub_assembly_items(bom_item.get("bom_no"), bom_data, bom_item["stock_qty"])
+			if d.value:
+				get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent+1)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
index 09ec24a..ca597f6 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
@@ -9,5 +9,9 @@
 				'label': _('Transactions'),
 				'items': ['Work Order', 'Material Request']
 			},
+			{
+				'label': _('Subcontract'),
+				'items': ['Purchase Order']
+			},
 		]
 	}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 768f99e..cce1bb6 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -169,7 +169,7 @@
 		pln.get_items()
 		pln.submit()
 
-		self.assertTrue(pln.po_items[0].planned_qty, 3)	
+		self.assertTrue(pln.po_items[0].planned_qty, 3)
 
 		pln.make_work_order()
 		work_order = frappe.db.get_value('Work Order', {
@@ -193,10 +193,10 @@
 		for so_item in so_items:
 			so_wo_qty = frappe.db.get_value('Sales Order Item', so_item, 'work_order_qty')
 			self.assertEqual(so_wo_qty, 0.0)
-		
+
 		latest_plan = frappe.get_doc('Production Plan', pln.name)
 		latest_plan.cancel()
-	
+
 	def test_pp_to_mr_customer_provided(self):
 		#Material Request from Production Plan for Customer Provided
 		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
@@ -236,10 +236,10 @@
 		pln.append("po_items", {
 			"item_code": item_code,
 			"bom_no": frappe.db.get_value('BOM', {'item': "Test BOM 1"}),
-			"planned_qty": 3,
-			"make_work_order_for_sub_assembly_items": 1
+			"planned_qty": 3
 		})
 
+		pln.get_sub_assembly_items('In House')
 		pln.submit()
 		pln.make_work_order()
 
diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
index 89ab7aa..f829d57 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
@@ -9,18 +9,17 @@
   "include_exploded_items",
   "item_code",
   "bom_no",
-  "planned_qty",
   "column_break_6",
-  "make_work_order_for_sub_assembly_items",
+  "planned_qty",
   "warehouse",
   "planned_start_date",
   "section_break_9",
   "pending_qty",
   "ordered_qty",
-  "produced_qty",
   "column_break_17",
   "description",
   "stock_uom",
+  "produced_qty",
   "reference_section",
   "sales_order",
   "sales_order_item",
@@ -32,11 +31,10 @@
  ],
  "fields": [
   {
-   "columns": 2,
-   "default": "0",
+   "columns": 1,
+   "default": "1",
    "fieldname": "include_exploded_items",
    "fieldtype": "Check",
-   "in_list_view": 1,
    "label": "Include Exploded Items"
   },
   {
@@ -81,13 +79,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "0",
-   "description": "If enabled, system will create the work order for the exploded items against which BOM is available.",
-   "fieldname": "make_work_order_for_sub_assembly_items",
-   "fieldtype": "Check",
-   "label": "Make Work Order for Sub Assembly Items"
-  },
-  {
    "fieldname": "warehouse",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -218,7 +209,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-04-28 19:14:57.772123",
+ "modified": "2021-06-28 18:31:06.822168",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan Item",
diff --git a/erpnext/accounts/accounts b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/__init__.py
similarity index 100%
copy from erpnext/accounts/accounts
copy to erpnext/manufacturing/doctype/production_plan_sub_assembly_item/__init__.py
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
new file mode 100644
index 0000000..657ee35
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
@@ -0,0 +1,202 @@
+{
+ "actions": [],
+ "creation": "2020-12-27 16:08:36.127199",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "production_item",
+  "item_name",
+  "fg_warehouse",
+  "parent_item_code",
+  "schedule_date",
+  "column_break_3",
+  "qty",
+  "bom_no",
+  "bom_level",
+  "type_of_manufacturing",
+  "supplier",
+  "work_order_details_section",
+  "work_order",
+  "purchase_order",
+  "production_plan_item",
+  "column_break_7",
+  "produced_qty",
+  "received_qty",
+  "indent",
+  "section_break_19",
+  "uom",
+  "stock_uom",
+  "column_break_22",
+  "description"
+ ],
+ "fields": [
+  {
+   "fetch_from": "sub_assembly_item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.type_of_manufacturing == \"In House\"",
+   "fieldname": "work_order_details_section",
+   "fieldtype": "Section Break",
+   "label": "Reference"
+  },
+  {
+   "fieldname": "work_order",
+   "fieldtype": "Link",
+   "label": "Work Order",
+   "options": "Work Order",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
+  {
+   "columns": 1,
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Required Qty",
+   "read_only": 1
+  },
+  {
+   "fieldname": "purchase_order",
+   "fieldtype": "Link",
+   "label": "Purchase Order",
+   "options": "Purchase Order",
+   "read_only": 1
+  },
+  {
+   "fieldname": "received_qty",
+   "fieldtype": "Float",
+   "label": "Received Qty"
+  },
+  {
+   "fieldname": "bom_no",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Bom No",
+   "options": "BOM"
+  },
+  {
+   "fieldname": "production_plan_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Production Plan Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "parent_item_code",
+   "fieldtype": "Link",
+   "label": "Finished Good",
+   "options": "Item",
+   "read_only": 1
+  },
+  {
+   "columns": 1,
+   "fetch_from": "bom_no.bom_level",
+   "fieldname": "bom_level",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Level (BOM)",
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_19",
+   "fieldtype": "Section Break",
+   "label": "Item Details"
+  },
+  {
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_22",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "description",
+   "read_only": 1
+  },
+  {
+   "fieldname": "production_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Sub Assembly Item Code",
+   "options": "Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "indent",
+   "fieldtype": "Int",
+   "label": "Indent"
+  },
+  {
+   "fieldname": "fg_warehouse",
+   "fieldtype": "Link",
+   "label": "Target Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "fieldname": "produced_qty",
+   "fieldtype": "Data",
+   "label": "Produced Quantity",
+   "read_only": 1
+  },
+  {
+   "default": "In House",
+   "fieldname": "type_of_manufacturing",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Manufacturing Type",
+   "options": "In House\nSubcontract"
+  },
+  {
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "label": "Supplier",
+   "mandatory_depends_on": "eval:doc.type_of_manufacturing == 'Subcontract'",
+   "options": "Supplier"
+  },
+  {
+   "fieldname": "schedule_date",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "Schedule Date"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-28 20:10:56.296410",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Sub Assembly Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py
new file mode 100644
index 0000000..6850a2e
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.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 ProductionPlanSubAssemblyItem(Document):
+	pass
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 44d76d2..3b56854 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -64,11 +64,16 @@
   "description",
   "stock_uom",
   "column_break2",
+  "references_section",
   "material_request",
   "material_request_item",
   "sales_order_item",
+  "column_break_61",
   "production_plan",
   "production_plan_item",
+  "production_plan_sub_assembly_item",
+  "parent_work_order",
+  "bom_level",
   "product_bundle_item",
   "amended_from"
  ],
@@ -546,17 +551,26 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
-  }
+  },
+  {
+    "fieldname": "production_plan_sub_assembly_item",
+    "fieldtype": "Data",
+    "label": "Production Plan Sub-assembly Item",
+    "no_copy": 1,
+    "print_hide": 1,
+    "read_only": 1
+   }
  ],
  "icon": "fa fa-cogs",
  "idx": 1,
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-20 15:19:14.902699",
+ "modified": "2021-06-28 16:19:14.902699",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
+ "nsm_parent_field": "parent_work_order",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 180815d..779ae42 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -483,7 +483,7 @@
 
 
 		self.set('operations', [])
-		if not self.bom_no:
+		if not self.bom_no or not frappe.get_cached_value('BOM', self.bom_no, 'with_operations'):
 			return
 
 		operations = []
@@ -590,6 +590,7 @@
 	def validate_operation_time(self):
 		for d in self.operations:
 			if not d.time_in_mins > 0:
+				print(self.bom_no, self.production_item)
 				frappe.throw(_("Operation Time must be greater than 0 for Operation {0}").format(d.operation))
 
 	def update_required_items(self):
diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
index 48907ad..858b554 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
@@ -20,17 +20,20 @@
 		fields= ['qty','bom_no','qty','scrap','item_code','item_name','description','uom'])
 
 	for item in exploded_items:
+		print(item.bom_no, indent)
 		item["indent"] = indent
 		data.append({
 			'item_code': item.item_code,
 			'item_name': item.item_name,
 			'indent': indent,
+			'bom_level': (frappe.get_cached_value("BOM", item.bom_no, "bom_level")
+				if item.bom_no else ""),
 			'bom': item.bom_no,
 			'qty': item.qty * qty,
 			'uom': item.uom,
 			'description': item.description,
 			'scrap': item.scrap
-			})
+		})
 		if item.bom_no:
 			get_exploded_items(item.bom_no, data, indent=indent+1, qty=item.qty)
 
@@ -69,6 +72,12 @@
 			"width": 100
 		},
 		{
+			"label": "BOM Level",
+			"fieldtype": "Data",
+			"fieldname": "bom_level",
+			"width": 100
+		},
+		{
 			"label": "Standard Description",
 			"fieldtype": "data",
 			"fieldname": "description",
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
index 1c6758e..ed8b939 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -70,12 +70,12 @@
 					ON bom_item.item_code = ledger.item_code
 				{conditions}
 			WHERE
-				bom_item.parent = '{bom}' and bom_item.parenttype='BOM'
+				bom_item.parent = {bom} and bom_item.parenttype='BOM'
 
 			GROUP BY bom_item.item_code""".format(
 				qty_field=qty_field,
 				table=table,
 				conditions=conditions,
-				bom=bom,
+				bom=frappe.db.escape(bom),
 				qty_to_produce=qty_to_produce or 1)
 			)
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
index bd68db1..cb771e4 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
@@ -68,6 +68,18 @@
 			get_data: function(txt) {
 				return frappe.db.get_link_options('Item', txt);
 			}
+		},
+		{
+			label: __("Workstation"),
+			fieldname: "workstation",
+			fieldtype: "Link",
+			options: "Workstation"
+		},
+		{
+			label: __("Operation"),
+			fieldname: "operation",
+			fieldtype: "Link",
+			options: "Operation"
 		}
 	]
 };
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.json b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json
index 9f08fc3..ecf2b74 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.json
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json
@@ -1,14 +1,16 @@
 {
- "add_total_row": 0,
+ "add_total_row": 1,
+ "columns": [],
  "creation": "2020-04-20 12:00:21.436619",
  "disable_prepared_report": 0,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Report",
+ "filters": [],
  "idx": 0,
  "is_standard": "Yes",
- "letter_head": "Gadgets International",
- "modified": "2020-04-20 12:00:21.436619",
+ "letter_head": "",
+ "modified": "2020-12-30 11:49:21.713561",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card Summary",
diff --git a/erpnext/accounts/accounts b/erpnext/manufacturing/report/production_plan_summary/__init__.py
similarity index 100%
rename from erpnext/accounts/accounts
rename to erpnext/manufacturing/report/production_plan_summary/__init__.py
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
new file mode 100644
index 0000000..59396fe
--- /dev/null
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
@@ -0,0 +1,32 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Production Plan Summary"] = {
+	"filters": [
+		{
+			fieldname: "production_plan",
+			label: __("Production Plan"),
+			fieldtype: "Link",
+			options: "Production Plan",
+			reqd: 1,
+			get_query: function() {
+				return {
+					filters: {
+						"docstatus": 1
+					}
+				};
+			}
+		}
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+
+		if (column.fieldname == "document_name") {
+			var color = data.pending_qty > 0 ? 'red': 'green';
+			value = `<a style='color:${color}' href="#Form/${data['document_type']}/${data['document_name']}" data-doctype="${data['document_type']}">${data['document_name']}</a>`;
+		}
+
+		return value;
+	},
+};
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.json b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.json
new file mode 100644
index 0000000..33aca21
--- /dev/null
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-12-27 11:43:39.781793",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-12-27 11:43:42.677584",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Production Plan",
+ "report_name": "Production Plan Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Manufacturing User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
new file mode 100644
index 0000000..81b1791
--- /dev/null
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -0,0 +1,136 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import flt
+
+def execute(filters=None):
+	columns, data = [], []
+	data = get_data(filters)
+	columns = get_column(filters)
+
+	return columns, data
+
+def get_data(filters):
+	data = []
+
+	order_details = {}
+	get_work_order_details(filters, order_details)
+	get_purchase_order_details(filters, order_details)
+	get_production_plan_item_details(filters, data, order_details)
+
+	return data
+
+def get_production_plan_item_details(filters, data, order_details):
+	itemwise_indent = {}
+
+	production_plan_doc = frappe.get_cached_doc("Production Plan", filters.get("production_plan"))
+	for row in production_plan_doc.po_items:
+		work_order = frappe.get_cached_value("Work Order", {"production_plan_item": row.name,
+			"bom_no": row.bom_no, "production_item": row.item_code}, "name")
+
+		if row.item_code not in itemwise_indent:
+			itemwise_indent.setdefault(row.item_code, {})
+
+		data.append({
+			"indent": 0,
+			"item_code": row.item_code,
+			"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
+			"qty": row.planned_qty,
+			"document_type": "Work Order",
+			"document_name": work_order,
+			"bom_level": frappe.get_cached_value("BOM", row.bom_no, "bom_level"),
+			"produced_qty": order_details.get((work_order, row.item_code)).get("produced_qty"),
+			"pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code)).get("produced_qty"))
+		})
+
+		get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details)
+
+def get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details):
+	for item in production_plan_doc.sub_assembly_items:
+		if row.name == item.production_plan_item:
+			subcontracted_item = (item.type_of_manufacturing == 'Subcontract')
+
+			if subcontracted_item:
+				docname = frappe.get_cached_value("Purchase Order Item",
+					{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "parent")
+			else:
+				docname = frappe.get_cached_value("Work Order",
+					{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "name")
+
+			data.append({
+				"indent": 1,
+				"item_code": item.production_item,
+				"item_name": item.item_name,
+				"qty": item.qty,
+				"document_type": "Work Order" if not subcontracted_item else "Purchase Order",
+				"document_name": docname,
+				"bom_level": item.bom_level,
+				"produced_qty": order_details.get((docname, item.production_item)).get("produced_qty"),
+				"pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item)).get("produced_qty"))
+			})
+
+def get_work_order_details(filters, order_details):
+	for row in frappe.get_all("Work Order", filters = {"production_plan": filters.get("production_plan")},
+		fields=["name", "produced_qty", "production_plan", "production_item"]):
+		order_details.setdefault((row.name, row.production_item), row)
+
+def get_purchase_order_details(filters, order_details):
+	for row in frappe.get_all("Purchase Order Item", filters = {"production_plan": filters.get("production_plan")},
+		fields=["parent", "received_qty as produced_qty", "item_code"]):
+		order_details.setdefault((row.parent, row.item_code), row)
+
+def get_column(filters):
+	return [
+		{
+			"label": "Finished Good",
+			"fieldtype": "Link",
+			"fieldname": "item_code",
+			"width": 300,
+			"options": "Item"
+		},
+		{
+			"label": "Item Name",
+			"fieldtype": "data",
+			"fieldname": "item_name",
+			"width": 100
+		},
+		{
+			"label": "Document Type",
+			"fieldtype": "Link",
+			"fieldname": "document_type",
+			"width": 150,
+			"options": "DocType"
+		},
+		{
+			"label": "Document Name",
+			"fieldtype": "Dynamic Link",
+			"fieldname": "document_name",
+			"width": 150
+		},
+		{
+			"label": "BOM Level",
+			"fieldtype": "Int",
+			"fieldname": "bom_level",
+			"width": 100
+		},
+		{
+			"label": "Order Qty",
+			"fieldtype": "Float",
+			"fieldname": "qty",
+			"width": 120
+		},
+		{
+			"label": "Received Qty",
+			"fieldtype": "Float",
+			"fieldname": "produced_qty",
+			"width": 160
+		},
+		{
+			"label": "Pending Qty",
+			"fieldtype": "Float",
+			"fieldname": "pending_qty",
+			"width": 110
+		}
+	]
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
index fb047b2..612dad0 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -19,7 +19,7 @@
 	return columns, data, None, chart_data
 
 def get_data(filters):
-	query_filters = {"docstatus": 1}
+	query_filters = {"docstatus": ("<", 2)}
 
 	fields = ["name", "status", "sales_order", "production_item", "qty", "produced_qty",
 		"planned_start_date", "planned_end_date", "actual_start_date", "actual_end_date", "lead_time"]
@@ -62,7 +62,8 @@
 		"Not Started": 0,
 		"In Process": 0,
 		"Stopped": 0,
-		"Completed": 0
+		"Completed": 0,
+		"Draft": 0
 	}
 
 	for d in data:
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 2b1fc43..29376f0 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -290,3 +290,4 @@
 erpnext.patches.v13_0.rename_issue_status_hold_to_on_hold
 erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice
 erpnext.patches.v13_0.update_job_card_details
+erpnext.patches.v13_0.update_level_in_bom #1234sswef
diff --git a/erpnext/patches/v13_0/update_level_in_bom.py b/erpnext/patches/v13_0/update_level_in_bom.py
new file mode 100644
index 0000000..0d03c42
--- /dev/null
+++ b/erpnext/patches/v13_0/update_level_in_bom.py
@@ -0,0 +1,30 @@
+# Copyright (c) 2020, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	for document in ["bom", "bom_item", "bom_explosion_item"]:
+		frappe.reload_doc('manufacturing', 'doctype', document)
+
+	frappe.db.sql(" update `tabBOM` set bom_level = 0 where docstatus = 1")
+
+	bom_list = frappe.db.sql_list("""select name from `tabBOM` bom
+		where docstatus=1 and is_active=1 and not exists(select bom_no from `tabBOM Item`
+		where parent=bom.name and ifnull(bom_no, '')!='')""")
+
+	count = 0
+	while(count < len(bom_list)):
+		for parent_bom in get_parent_boms(bom_list[count]):
+			bom_doc = frappe.get_cached_doc("BOM", parent_bom)
+			bom_doc.set_bom_level(update=True)
+			bom_list.append(parent_bom)
+		count += 1
+
+def get_parent_boms(bom_no):
+	return frappe.db.sql_list("""
+		select distinct bom_item.parent from `tabBOM Item` bom_item
+		where bom_item.bom_no = %s and bom_item.docstatus=1 and bom_item.parenttype='BOM'
+			and exists(select bom.name from `tabBOM` bom where bom.name=bom_item.parent and bom.is_active=1)
+	""", bom_no)
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 36e728f..13cc423 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -117,7 +117,6 @@
 			Creates salary slip for selected employees if already not created
 		"""
 		self.check_permission('write')
-		self.created = 1
 		employees = [emp.employee for emp in self.employees]
 		if employees:
 			args = frappe._dict({
@@ -686,7 +685,7 @@
 
 	if filters.start_date and filters.end_date:
 		employee_list = get_employee_list(filters)
-		emp = filters.get('employees')
+		emp = filters.get('employees') or []
 		include_employees = [employee.employee for employee in employee_list if employee.employee not in emp]
 		filters.pop('start_date')
 		filters.pop('end_date')
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index ef03b01..6f5d67c 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -147,7 +147,7 @@
 			}
 
 			// Validate bank name
-			if(me.values.bank_account){
+			if(me.values.bank_account) { 
 				frappe.call({
 					async: false,
 					method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.validate_bank_account",
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.js b/erpnext/regional/doctype/gst_settings/gst_settings.js
index 808f9bc..cd682c5 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.js
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.js
@@ -35,6 +35,7 @@
 			return {
 				filters: {
 					company: row.company,
+					account_type: "Tax",
 					is_group: 0
 				}
 			};
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.py b/erpnext/regional/doctype/gst_settings/gst_settings.py
index bc956e9..af3d92e 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.py
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.py
@@ -19,6 +19,21 @@
 			from tabAddress where country = "India" and ifnull(gstin, '')!='' ''')
 		self.set_onload('data', data)
 
+	def validate(self):
+		# Validate duplicate accounts
+		self.validate_duplicate_accounts()
+
+	def validate_duplicate_accounts(self):
+		account_list = []
+		for account in self.get('gst_accounts'):
+			for fieldname in ['cgst_account', 'sgst_account', 'igst_account', 'cess_account']:
+				if account.get(fieldname) in account_list:
+					frappe.throw(_("Account {0} appears multiple times").format(
+						frappe.bold(account.get(fieldname))))
+
+				if account.get(fieldname):
+					account_list.append(account.get(fieldname))
+
 @frappe.whitelist()
 def send_reminder():
 	frappe.has_permission('GST Settings', throw=True)
diff --git a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
index 3857ce1..065f80d 100644
--- a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
@@ -46,14 +46,14 @@
 		make_sales_invoice()
 		create_purchase_invoices()
 
-		if frappe.db.exists("GSTR 3B Report", "GSTR3B-March-2019-_Test Address-Billing"):
-			report = frappe.get_doc("GSTR 3B Report", "GSTR3B-March-2019-_Test Address-Billing")
+		if frappe.db.exists("GSTR 3B Report", "GSTR3B-March-2019-_Test Address GST-Billing"):
+			report = frappe.get_doc("GSTR 3B Report", "GSTR3B-March-2019-_Test Address GST-Billing")
 			report.save()
 		else:
 			report = frappe.get_doc({
 				"doctype": "GSTR 3B Report",
 				"company": "_Test Company GST",
-				"company_address": "_Test Address-Billing",
+				"company_address": "_Test Address GST-Billing",
 				"year": getdate().year,
 				"month": month_number_mapping.get(getdate().month)
 			}).insert()
@@ -89,7 +89,7 @@
 
 		si.append("taxes", {
 			"charge_type": "On Net Total",
-			"account_head": "IGST - _GST",
+			"account_head": "Output Tax IGST - _GST",
 			"cost_center": "Main - _GST",
 			"description": "IGST @ 18.0",
 			"rate": 18
@@ -117,7 +117,7 @@
 
 	si.append("taxes", {
 			"charge_type": "On Net Total",
-			"account_head": "IGST - _GST",
+			"account_head": "Output Tax IGST - _GST",
 			"cost_center": "Main - _GST",
 			"description": "IGST @ 18.0",
 			"rate": 18
@@ -138,7 +138,7 @@
 
 	si1.append("taxes", {
 			"charge_type": "On Net Total",
-			"account_head": "IGST - _GST",
+			"account_head": "Output Tax IGST - _GST",
 			"cost_center": "Main - _GST",
 			"description": "IGST @ 18.0",
 			"rate": 18
@@ -159,7 +159,7 @@
 
 	si2.append("taxes", {
 			"charge_type": "On Net Total",
-			"account_head": "IGST - _GST",
+			"account_head": "Output Tax IGST - _GST",
 			"cost_center": "Main - _GST",
 			"description": "IGST @ 18.0",
 			"rate": 18
@@ -195,7 +195,7 @@
 
 	pi.append("taxes", {
 			"charge_type": "On Net Total",
-			"account_head": "CGST - _GST",
+			"account_head": "Input Tax CGST - _GST",
 			"cost_center": "Main - _GST",
 			"description": "CGST @ 9.0",
 			"rate": 9
@@ -203,7 +203,7 @@
 
 	pi.append("taxes", {
 			"charge_type": "On Net Total",
-			"account_head": "SGST - _GST",
+			"account_head": "Input Tax SGST - _GST",
 			"cost_center": "Main - _GST",
 			"description": "SGST @ 9.0",
 			"rate": 9
@@ -410,10 +410,10 @@
 	company.country = "India"
 	company.insert()
 
-	if not frappe.db.exists('Address', '_Test Address-Billing'):
+	if not frappe.db.exists('Address', '_Test Address GST-Billing'):
 		address = frappe.get_doc({
+			"address_title": "_Test Address GST",
 			"address_line1": "_Test Address Line 1",
-			"address_title": "_Test Address",
 			"address_type": "Billing",
 			"city": "_Test City",
 			"state": "Test State",
@@ -444,9 +444,9 @@
 	if not gst_account:
 		gst_settings.append("gst_accounts", {
 			"company": "_Test Company GST",
-			"cgst_account": "CGST - _GST",
-			"sgst_account": "SGST - _GST",
-			"igst_account": "IGST - _GST",
+			"cgst_account": "Output Tax CGST - _GST",
+			"sgst_account": "Output Tax SGST - _GST",
+			"igst_account": "Output Tax IGST - _GST"
 		})
 
 		gst_settings.save()
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 23d4fe9..8ad30fa 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -1,6 +1,8 @@
 erpnext.setup_einvoice_actions = (doctype) => {
 	frappe.ui.form.on(doctype, {
 		async refresh(frm) {
+			if (frm.doc.docstatus == 2) return;
+
 			const res = await frappe.call({
 				method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility',
 				args: { doc: frm.doc }
@@ -111,7 +113,7 @@
 
 			if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
 				const action = () => {
-					let message = __('Cancellation of e-way bill is currently not supported. ');
+					let message = __('Cancellation of e-way bill is currently not supported.') + ' ';
 					message += '<br><br>';
 					message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.');
 
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 11ebef7..ea600d9 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -42,7 +42,10 @@
 	invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') })
 	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
 	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
-	no_taxes_applied = not doc.get('taxes')
+
+	# if export invoice, then taxes can be empty
+	# invoice can only be ineligible if no taxes applied and is not an export invoice
+	no_taxes_applied = not doc.get('taxes') and not doc.get('gst_category') == 'Overseas'
 	has_non_gst_item = any(d for d in doc.get('items', []) if d.get('is_non_gst'))
 
 	if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
@@ -188,9 +191,10 @@
 
 		item.qty = abs(item.qty)
 
-		item.unit_rate = abs((abs(item.taxable_value) - item.discount_amount)/ item.qty)
-		item.gross_amount = abs(item.taxable_value) + item.discount_amount
+		item.unit_rate = abs(item.taxable_value / item.qty)
+		item.gross_amount = abs(item.taxable_value)
 		item.taxable_value = abs(item.taxable_value)
+		item.discount_amount = 0
 
 		item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
 		item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 3e0b9b7..5f9d5ed 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -25,6 +25,7 @@
 	frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
 	create_gratuity_rule()
 	add_print_formats()
+	update_accounts_settings_for_taxes()
 
 def add_hsn_sac_codes():
 	if frappe.flags.in_test and frappe.flags.created_hsn_codes:
@@ -680,7 +681,7 @@
 
 def make_fixtures(company=None):
 	docs = []
-	company = company.name if company else frappe.db.get_value("Global Defaults", None, "default_company")
+	company = company or frappe.db.get_value("Global Defaults", None, "default_company")
 
 	set_salary_components(docs)
 	set_tds_account(docs, company)
@@ -698,6 +699,53 @@
 	# create records for Tax Withholding Category
 	set_tax_withholding_category(company)
 
+def update_regional_tax_settings(country, company):
+	# Will only add default GST accounts if present
+	input_account_names = ['Input Tax CGST', 'Input Tax SGST', 'Input Tax IGST']
+	output_account_names = ['Output Tax CGST', 'Output Tax SGST', 'Output Tax IGST']
+	rcm_accounts = ['Input Tax CGST RCM', 'Input Tax SGST RCM', 'Input Tax IGST RCM']
+	gst_settings = frappe.get_single('GST Settings')
+	existing_account_list = []
+
+	for account in gst_settings.get('gst_accounts'):
+		for key in ['cgst_account', 'sgst_account', 'igst_account']:
+			existing_account_list.append(account.get(key))
+
+	gst_accounts = frappe._dict(frappe.get_all("Account",
+		{'company': company, 'account_name': ('in', input_account_names +
+			output_account_names + rcm_accounts)}, ['account_name', 'name'], as_list=1))
+
+	add_accounts_in_gst_settings(company,  input_account_names, gst_accounts,
+		existing_account_list, gst_settings)
+	add_accounts_in_gst_settings(company, output_account_names, gst_accounts,
+		existing_account_list, gst_settings)
+	add_accounts_in_gst_settings(company, rcm_accounts, gst_accounts,
+		existing_account_list, gst_settings, is_reverse_charge=1)
+
+	gst_settings.save()
+
+def add_accounts_in_gst_settings(company, account_names, gst_accounts,
+	existing_account_list, gst_settings, is_reverse_charge=0):
+	accounts_not_added = 1
+
+	for account in account_names:
+		# Default Account Added does not exists
+		if not gst_accounts.get(account):
+			accounts_not_added = 0
+
+		# Check if already added in GST Settings
+		if gst_accounts.get(account) in existing_account_list:
+			accounts_not_added = 0
+
+	if accounts_not_added:
+		gst_settings.append('gst_accounts', {
+			'company': company,
+			'cgst_account': gst_accounts.get(account_names[0]),
+			'sgst_account': gst_accounts.get(account_names[1]),
+			'igst_account': gst_accounts.get(account_names[2]),
+			'is_reverse_charge_account': is_reverse_charge
+		})
+
 def set_salary_components(docs):
 	docs.extend([
 		{'doctype': 'Salary Component', 'salary_component': 'Professional Tax',
@@ -731,12 +779,13 @@
 	docs = get_tds_details(accounts, fiscal_year)
 
 	for d in docs:
-		try:
+		if not frappe.db.exists("Tax Withholding Category", d.get("name")):
 			doc = frappe.get_doc(d)
+			doc.flags.ignore_validate = True
 			doc.flags.ignore_permissions = True
 			doc.flags.ignore_mandatory = True
 			doc.insert()
-		except frappe.DuplicateEntryError:
+		else:
 			doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
 
 			if accounts:
@@ -749,11 +798,12 @@
 					doc.append("rates", d.get('rates')[0])
 
 			doc.flags.ignore_permissions = True
+			doc.flags.ignore_validate = True
 			doc.flags.ignore_mandatory = True
+			doc.flags.ignore_links = True
 			doc.save()
 
 def set_tds_account(docs, company):
-	abbr = frappe.get_value("Company", company, "abbr")
 	parent_account = frappe.db.get_value("Account", filters = {"account_name": "Duties and Taxes", "company": company})
 	if parent_account:
 		docs.extend([
@@ -912,7 +962,6 @@
 	]
 
 def create_gratuity_rule():
-
 	# Standard Indain Gratuity Rule
 	if not frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"):
 		rule = frappe.new_doc("Gratuity Rule")
@@ -930,3 +979,7 @@
 
 		rule.flags.ignore_mandatory = True
 		rule.save()
+
+def update_accounts_settings_for_taxes():
+	if frappe.db.count('Company') == 1:
+		frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)
\ No newline at end of file
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
index 4deb073..d0000ad 100644
--- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
@@ -11,7 +11,7 @@
  "is_standard": "Yes",
  "json": "{}",
  "letter_head": "Logo",
- "modified": "2021-03-12 12:36:48.689413",
+ "modified": "2021-03-13 12:36:48.689413",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "E-Invoice Summary",
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 7cae0e4..38508c2 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -472,12 +472,7 @@
 		const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? frm.doc.grand_total : frm.doc.rounded_total;
 		this.render_grand_total(grand_total);
 
-		const taxes = frm.doc.taxes.map(t => {
-			return {
-				description: t.description, rate: t.rate
-			};
-		});
-		this.render_taxes(frm.doc.total_taxes_and_charges, taxes);
+		this.render_taxes(frm.doc.taxes);
 	}
 
 	render_net_total(value) {
@@ -502,14 +497,14 @@
 		);
 	}
 
-	render_taxes(value, taxes) {
+	render_taxes(taxes) {
 		if (taxes.length) {
 			const currency = this.events.get_frm().doc.currency;
 			const taxes_html = taxes.map(t => {
 				const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
 				return `<div class="tax-row">
 					<div class="tax-label">${description}</div>
-					<div class="tax-value">${format_currency(value, currency)}</div>
+					<div class="tax-value">${format_currency(t.tax_amount_after_discount_amount, currency)}</div>
 				</div>`;
 			}).join('');
 			this.$totals_section.find('.taxes-container').css('display', 'flex').html(taxes_html);
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index c484873..f1a166b 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -56,7 +56,7 @@
 				);
 				let df_events = {
 					onchange: function() {
-						frm.set_value(this.df.fieldname, this.value);
+						frm.set_value(this.df.fieldname, this.get_value());
 					}
 				};
 				if (df.fieldtype == "Button") {
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 0427abe..915e6a4 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -110,7 +110,7 @@
 				self.create_default_warehouses()
 
 		if frappe.flags.country_change:
-			install_country_fixtures(self.name)
+			install_country_fixtures(self.name, self.country)
 			self.create_default_tax_template()
 
 		if not frappe.db.get_value("Department", {"company": self.name}):
@@ -440,16 +440,15 @@
 
 	return " - ".join(parts)
 
-def install_country_fixtures(company):
-	company_doc = frappe.get_doc("Company", company)
-	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country))
+def install_country_fixtures(company, country):
+	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
 	if os.path.exists(path.encode("utf-8")):
 		try:
-			module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country))
-			frappe.get_attr(module_name)(company_doc, False)
+			module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(country))
+			frappe.get_attr(module_name)(company, False)
 		except Exception as e:
 			frappe.log_error()
-			frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country)))
+			frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(country)))
 
 
 def update_company_current_month_sales(company):
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index daaa626..34af093 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -1164,33 +1164,292 @@
 	},
 
 	"India": {
+		"tax_categories": [
+			{
+				"title": "In-State",
+				"is_inter_state": 0,
+				"gst_state": ""
+			},
+			{
+				"title": "Out-State",
+				"is_inter_state": 1,
+				"gst_state": ""
+			},
+			{
+				"title": "Reverse Charge In-State",
+				"is_inter_state": 0,
+				"gst_state": ""
+			},
+			{
+				"title": "Reverse Charge Out-State",
+				"is_inter_state": 1,
+				"gst_state": ""
+			},
+			{
+				"title": "Registered Composition",
+				"is_inter_state": 0,
+				"gst_state": ""
+			}
+		],
 		"chart_of_accounts": {
 			"*": {
 				"item_tax_templates": [
 					{
-						"title": "In State GST",
+						"title": "GST 9%",
 						"taxes": [
 							{
 								"tax_type": {
-									"account_name": "SGST",
+									"account_name": "Output Tax SGST",
 									"tax_rate": 9.00
 								}
 							},
 							{
 								"tax_type": {
-									"account_name": "CGST",
+									"account_name": "Output Tax CGST",
 									"tax_rate": 9.00
 								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 18.00
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 18.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 18.00,
+									"root_type": "Asset"
+								}
 							}
 						]
 					},
 					{
-						"title": "Out of State GST",
+						"title": "GST 5%",
 						"taxes": [
 							{
 								"tax_type": {
-									"account_name": "IGST",
-									"tax_rate": 18.00
+									"account_name": "Output Tax SGST",
+									"tax_rate": 2.5
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax CGST",
+									"tax_rate": 2.5
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 5.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 2.5,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 2.5,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 5.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 2.50,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 2.50,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 5.00,
+									"root_type": "Asset"
+								}
+							}
+						]
+					},
+					{
+						"title": "GST 12%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Output Tax SGST",
+									"tax_rate": 6.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax CGST",
+									"tax_rate": 6.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 12.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 6.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 6.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 12.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 6.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 6.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 12.00,
+									"root_type": "Asset"
+								}
+							}
+						]
+					},
+					{
+						"title": "GST 28%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Output Tax SGST",
+									"tax_rate": 14.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax CGST",
+									"tax_rate": 14.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 28.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 14.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 14.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 28.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 14.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 14.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 28.00,
+									"root_type": "Asset"
 								}
 							}
 						]
@@ -1229,36 +1488,117 @@
 						]
 					}
 				],
-				"*": [
+				"sales_tax_templates": [
 					{
-						"title": "In State GST",
+						"title": "Output GST In-state",
 						"taxes": [
 							{
 								"account_head": {
-									"account_name": "SGST",
-									"tax_rate": 9.00
+									"account_name": "Output Tax SGST",
+									"tax_rate": 9.00,
+									"account_type": "Tax"
 								}
 							},
 							{
 								"account_head": {
-									"account_name": "CGST",
-									"tax_rate": 9.00
+									"account_name": "Output Tax CGST",
+									"tax_rate": 9.00,
+									"account_type": "Tax"
 								}
 							}
-						]
+						],
+						"tax_category": "In-State"
 					},
 					{
-						"title": "Out of State GST",
+						"title": "Output GST Out-state",
 						"taxes": [
 							{
 								"account_head": {
-									"account_name": "IGST",
-									"tax_rate": 18.00
+									"account_name": "Output Tax IGST",
+									"tax_rate": 18.00,
+									"account_type": "Tax"
 								}
 							}
-						]
+						],
+						"tax_category": "Out-State"
+					}
+				],
+				"purchase_tax_templates": [
+					{
+						"title": "Input GST In-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							},
+							{
+								"account_head": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "In-State"
 					},
 					{
+						"title": "Input GST Out-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 18.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "Out-State"
+					},
+					{
+						"title": "Input GST RCM In-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							},
+							{
+								"account_head": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "Reverse Charge In-State"
+					},
+					{
+						"title": "Input GST RCM Out-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 18.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "Reverse Charge Out-State"
+					}
+				],
+				"*": [
+					{
 						"title": "VAT 5%",
 						"taxes": [
 							{
@@ -1349,7 +1689,7 @@
 		"Italy VAT 4%":{
 			"account_name": "IVA 4%",
 			"tax_rate": 4.00
-		}		
+		}
 	},
 
 	"Ivory Coast": {
diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py
index 3f0bb14..4edf948 100644
--- a/erpnext/setup/setup_wizard/operations/company_setup.py
+++ b/erpnext/setup/setup_wizard/operations/company_setup.py
@@ -42,29 +42,6 @@
 		'quotation_series': "QTN-",
 	}).insert()
 
-def create_bank_account(args):
-	if args.get("bank_account"):
-		company_name = args.get('company_name')
-		bank_account_group =  frappe.db.get_value("Account",
-			{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
-				"company": company_name})
-		if bank_account_group:
-			bank_account = frappe.get_doc({
-				"doctype": "Account",
-				'account_name': args.get("bank_account"),
-				'parent_account': bank_account_group,
-				'is_group':0,
-				'company': company_name,
-				"account_type": "Bank",
-			})
-			try:
-				return bank_account.insert()
-			except RootNotEditable:
-				frappe.throw(_("Bank account cannot be named as {0}").format(args.get("bank_account")))
-			except frappe.DuplicateEntryError:
-				# bank account same as a CoA entry
-				pass
-
 def create_email_digest():
 	from frappe.utils.user import get_system_managers
 	system_managers = get_system_managers(only_name=True)
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 7ae81d7..cd49a18 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -448,6 +448,8 @@
 	set_active_domains(args)
 	update_stock_settings()
 	update_shopping_cart_settings(args)
+
+	args.update({"set_default": 1})
 	create_bank_account(args)
 
 def set_global_defaults(args):
@@ -479,17 +481,17 @@
 	stock_settings.save()
 
 def create_bank_account(args):
-	if not args.bank_account:
+	if not args.get('bank_account'):
 		return
 
-	company_name = args.company_name
+	company_name = args.get('company_name')
 	bank_account_group =  frappe.db.get_value("Account",
 		{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
 			"company": company_name})
 	if bank_account_group:
 		bank_account = frappe.get_doc({
 			"doctype": "Account",
-			'account_name': args.bank_account,
+			'account_name': args.get('bank_account'),
 			'parent_account': bank_account_group,
 			'is_group':0,
 			'company': company_name,
@@ -498,10 +500,13 @@
 		try:
 			doc = bank_account.insert()
 
-			frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False)
+			if args.get('set_default'):
+				frappe.db.set_value("Company", args.get('company_name'), "default_bank_account", bank_account.name, update_modified=False)
+
+			return doc
 
 		except RootNotEditable:
-			frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account))
+			frappe.throw(_("Bank account cannot be named as {0}").format(args.get('bank_account')))
 		except frappe.DuplicateEntryError:
 			# bank account same as a CoA entry
 			pass
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index f4fe18e..c7cc000 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -27,6 +27,7 @@
 		country_wise_tax = simple_to_detailed(country_wise_tax)
 
 	from_detailed_data(company_name, country_wise_tax)
+	update_regional_tax_settings(country, company_name)
 
 
 def simple_to_detailed(templates):
@@ -101,6 +102,17 @@
 			make_item_tax_template(company_name, template)
 
 
+def update_regional_tax_settings(country, company):
+	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
+	if os.path.exists(path.encode("utf-8")):
+		try:
+			module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(frappe.scrub(country))
+			frappe.get_attr(module_name)(country, company)
+		except Exception as e:
+			# Log error and ignore if failed to setup regional tax settings
+			frappe.log_error()
+			pass
+
 def make_taxes_and_charges_template(company_name, doctype, template):
 	template['company'] = company_name
 	template['doctype'] = doctype
@@ -130,8 +142,14 @@
 			if fieldname not in tax_row:
 				tax_row[fieldname] = default_value
 
-	return frappe.get_doc(template).insert(ignore_permissions=True)
+	doc = frappe.get_doc(template)
 
+	# Data in country wise json is already pre validated, hence validations can be ignored 
+	# Ingone validations to make doctypes faster
+	doc.flags.ignore_links = True
+	doc.flags.ignore_validate = True
+	doc.insert(ignore_permissions=True)
+	return doc
 
 def make_item_tax_template(company_name, template):
 	"""Create an Item Tax Template.
@@ -156,8 +174,24 @@
 			if 'tax_rate' not in tax_row:
 				tax_row['tax_rate'] = account_data.get('tax_rate')
 
-	return frappe.get_doc(template).insert(ignore_permissions=True)
+	doc = frappe.get_doc(template)
 
+	# Data in country wise json is already pre validated, hence validations can be ignored 
+	# Ingone validations to make doctypes faster
+	doc.flags.ignore_links = True
+	doc.flags.ignore_validate = True
+	doc.insert(ignore_permissions=True)
+	return doc
+
+def make_tax_category(tax_category):
+	""" Make tax category based on title if not already created """
+	doctype = 'Tax Category'
+	if not frappe.db.exists(doctype, tax_category['title']):
+		tax_category['doctype'] = doctype
+		doc = frappe.get_doc(tax_category)
+		doc.flags.ignore_links = True
+		doc.flags.ignore_validate = True
+		doc.insert(ignore_permissions=True)
 
 def get_or_create_account(company_name, account):
 	"""
@@ -175,8 +209,7 @@
 		or_filters={
 			'account_name': account.get('account_name'),
 			'account_number': account.get('account_number')
-		}
-	)
+		})
 
 	if existing_accounts:
 		return frappe.get_doc('Account', existing_accounts[0].name)
@@ -191,8 +224,11 @@
 	account['root_type'] = root_type
 	account['is_group'] = 0
 
-	return frappe.get_doc(account).insert(ignore_permissions=True, ignore_mandatory=True)
-
+	doc = frappe.get_doc(account)
+	doc.flags.ignore_links = True
+	doc.flags.ignore_validate = True
+	doc.insert(ignore_permissions=True, ignore_mandatory=True)
+	return doc
 
 def get_or_create_tax_group(company_name, root_type):
 	# Look for a group account of type 'Tax'
@@ -237,7 +273,11 @@
 		'account_type': 'Tax',
 		'account_name': account_name,
 		'parent_account': root_account.name
-	}).insert(ignore_permissions=True)
+	})
+
+	tax_group_account.flags.ignore_links = True
+	tax_group_account.flags.ignore_validate = True
+	tax_group_account.insert(ignore_permissions=True)
 
 	tax_group_name = tax_group_account.name
 
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index 014f409..6ca3d63 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -11,10 +11,11 @@
  "hide_custom": 0,
  "icon": "settings",
  "idx": 0,
+ "is_default": 0,
  "is_standard": 1,
  "label": "ERPNext Settings",
  "links": [],
- "modified": "2020-12-01 13:38:37.759596",
+ "modified": "2021-06-12 01:58:11.399566",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "ERPNext Settings",
@@ -109,6 +110,13 @@
    "label": "Domain Settings",
    "link_to": "Domain Settings",
    "type": "DocType"
+  },
+  {
+   "doc_view": "",
+   "icon": "retail",
+   "label": "Products Settings",
+   "link_to": "Products Settings",
+   "type": "DocType"
   }
  ]
-}
\ No newline at end of file
+}
diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py
index d96d803..6c92d96 100644
--- a/erpnext/shopping_cart/product_query.py
+++ b/erpnext/shopping_cart/product_query.py
@@ -71,7 +71,8 @@
 					],
 					or_filters=self.or_filters,
 					start=start,
-					limit=self.page_length
+					limit=self.page_length,
+					order_by="weightage desc"
 				)
 
 				items_dict = {item.name: item for item in items}
@@ -86,7 +87,8 @@
 				filters=self.filters,
 				or_filters=self.or_filters,
 				start=start,
-				limit=self.page_length
+				limit=self.page_length,
+				order_by="weightage desc"
 			)
 
 		# Combine results having context of website item groups into item results
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index e6d2e13..fc4cf1d 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -193,7 +193,7 @@
  "image_field": "image",
  "links": [],
  "max_attachments": 5,
- "modified": "2021-01-07 11:10:09.149170",
+ "modified": "2021-07-08 16:22:01.343105",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Batch",
@@ -217,5 +217,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
- "title_field": "batch_id"
+ "title_field": "batch_id",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index cd441b5..b6eef6c 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -230,9 +230,8 @@
 	"""Automatically select `batch_no` for outgoing items in item table"""
 	for d in doc.get(child_table):
 		qty = d.get('stock_qty') or d.get('transfer_qty') or d.get('qty') or 0
-		has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no')
 		warehouse = d.get(warehouse_field, None)
-		if has_batch_no and warehouse and qty > 0:
+		if warehouse and qty > 0 and frappe.db.get_value('Item', d.item_code, 'has_batch_no'):
 			if not d.batch_no:
 				d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw, d.serial_no)
 			else:
@@ -313,4 +312,4 @@
 def make_batch(args):
 	if frappe.db.get_value("Item", args.item, "has_batch_no"):
 		args.doctype = "Batch"
-		frappe.get_doc(args).insert().name
\ No newline at end of file
+		frappe.get_doc(args).insert().name
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index fcdb5f3..4808e94 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -182,9 +182,8 @@
 		super(DeliveryNote, self).validate_warehouse()
 
 		for d in self.get_item_list():
-			if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
-				if not d['warehouse']:
-					frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
+			if not d['warehouse'] and frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
+				frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
 
 
 	def update_current_stock(self):
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 8aec893..b55374b 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -93,7 +93,7 @@
 
 		erpnext.item.edit_prices_button(frm);
 		erpnext.item.toggle_attributes(frm);
-		
+
 		if (!frm.doc.is_fixed_asset) {
 			erpnext.item.make_dashboard(frm);
 		}
@@ -381,7 +381,8 @@
 		// Show Stock Levels only if is_stock_item
 		if (frm.doc.is_stock_item) {
 			frappe.require('assets/js/item-dashboard.min.js', function() {
-				const section = frm.dashboard.add_section('', __("Stock Levels"));
+				frm.dashboard.parent.find('.stock-levels').remove();
+				const section = frm.dashboard.add_section('', __("Stock Levels"), 'stock-levels');
 				erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({
 					parent: section,
 					item_code: frm.doc.name,
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 5df4d87..bf969f9 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -41,7 +41,7 @@
 
 	def validate(self):
 		self.check_mandatory()
-		self.validate_purchase_receipts()
+		self.validate_receipt_documents()
 		init_landed_taxes_and_totals(self)
 		self.set_total_taxes_and_charges()
 		if not self.get("items"):
@@ -56,14 +56,23 @@
 			frappe.throw(_("Please enter Receipt Document"))
 
 
-	def validate_purchase_receipts(self):
+	def validate_receipt_documents(self):
 		receipt_documents = []
 
 		for d in self.get("purchase_receipts"):
-			if frappe.db.get_value(d.receipt_document_type, d.receipt_document, "docstatus") != 1:
-				frappe.throw(_("Receipt document must be submitted"))
-			else:
-				receipt_documents.append(d.receipt_document)
+			docstatus = frappe.db.get_value(d.receipt_document_type, d.receipt_document, "docstatus")
+			if docstatus != 1:
+				msg = f"Row {d.idx}: {d.receipt_document_type} {frappe.bold(d.receipt_document)} must be submitted"
+				frappe.throw(_(msg), title=_("Invalid Document"))
+
+			if d.receipt_document_type == "Purchase Invoice":
+				update_stock = frappe.db.get_value(d.receipt_document_type, d.receipt_document, "update_stock")
+				if not update_stock:
+					msg = _("Row {0}: Purchase Invoice {1} has no stock impact.").format(d.idx, frappe.bold(d.receipt_document))
+					msg += "<br>" + _("Please create Landed Cost Vouchers against Invoices that have 'Update Stock' enabled.")
+					frappe.throw(msg, title=_("Incorrect Invoice"))
+
+			receipt_documents.append(d.receipt_document)
 
 		for item in self.get("items"):
 			if not item.receipt_document:
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 335175f..3ad9909 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -189,7 +189,7 @@
 		item_wh_list = []
 		for d in self.get("items"):
 			if (not mr_item_rows or d.name in mr_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
-					and frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and d.warehouse:
+					and d.warehouse and frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 :
 				item_wh_list.append([d.item_code, d.warehouse])
 
 		for item_code, warehouse in item_wh_list:
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index c01388d..2146793 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -184,4 +184,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 6ab68e2..e795742 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -17,6 +17,9 @@
 # TODO: Prioritize SO or WO group warehouse
 
 class PickList(Document):
+	def validate(self):
+		self.validate_for_qty()
+
 	def before_save(self):
 		self.set_item_locations()
 
@@ -35,6 +38,7 @@
 
 	@frappe.whitelist()
 	def set_item_locations(self, save=False):
+		self.validate_for_qty()
 		items = self.aggregate_item_qty()
 		self.item_location_map = frappe._dict()
 
@@ -107,6 +111,11 @@
 
 		return item_map.values()
 
+	def validate_for_qty(self):
+		if self.purpose == "Material Transfer for Manufacture" \
+				and (self.for_qty is None or self.for_qty == 0):
+			frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
+
 
 def validate_item_locations(pick_list):
 	if not pick_list.locations:
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index c4da05a..84566b8 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -37,6 +37,7 @@
 			'company': '_Test Company',
 			'customer': '_Test Customer',
 			'items_based_on': 'Sales Order',
+			'purpose': 'Delivery',
 			'locations': [{
 				'item_code': '_Test Item',
 				'qty': 5,
@@ -90,6 +91,7 @@
 			'company': '_Test Company',
 			'customer': '_Test Customer',
 			'items_based_on': 'Sales Order',
+			'purpose': 'Delivery',
 			'locations': [{
 				'item_code': '_Test Item Warehouse Group Wise Reorder',
 				'qty': 1000,
@@ -135,6 +137,7 @@
 			'company': '_Test Company',
 			'customer': '_Test Customer',
 			'items_based_on': 'Sales Order',
+			'purpose': 'Delivery',
 			'locations': [{
 				'item_code': '_Test Serialized Item',
 				'qty': 1000,
@@ -264,6 +267,7 @@
 			'company': '_Test Company',
 			'customer': '_Test Customer',
 			'items_based_on': 'Sales Order',
+			'purpose': 'Delivery',
 			'locations': [{
 				'item_code': '_Test Item',
 				'qty': 5,
@@ -319,6 +323,7 @@
 			'company': '_Test Company',
 			'customer': '_Test Customer',
 			'items_based_on': 'Sales Order',
+			'purpose': 'Delivery',
 			'locations': [{
 				'item_code': '_Test Item',
 				'qty': 1,
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
index ea26cac..0f50bcd 100644
--- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -97,7 +97,7 @@
 		at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse)
 
 		if not rules:
-			warehouse = source_warehouse or item.warehouse
+			warehouse = source_warehouse or item.get('warehouse')
 			if at_capacity:
 				# rules available, but no free space
 				items_not_accomodated.append([item_code, pending_qty])
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index 7f3d701..f5d076a 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -14,7 +14,7 @@
 )
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
 # test_records = frappe.get_test_records('Quality Inspection')
 
@@ -159,6 +159,47 @@
 			frappe.delete_doc("Quality Inspection", qi)
 		dn.delete()
 
+	def test_rejected_qi_validation(self):
+		"""Test if rejected QI blocks Stock Entry as per Stock Settings."""
+		se = make_stock_entry(
+			item_code="_Test Item with QA",
+			target="_Test Warehouse - _TC",
+			qty=1,
+			basic_rate=100,
+			inspection_required=True,
+			do_not_submit=True
+		)
+
+		readings = [
+			{
+				"specification": "Iron Content",
+				"min_value": 0.1,
+				"max_value": 0.9,
+				"reading_1": "0.4"
+			}
+		]
+
+		qa = create_quality_inspection(
+			reference_type="Stock Entry",
+			reference_name=se.name,
+			readings=readings,
+			status="Rejected"
+		)
+
+		frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
+		se.reload()
+		self.assertRaises(QualityInspectionRejectedError, se.submit) # when blocked in Stock settings, block rejected QI
+
+		frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Warn")
+		se.reload()
+		se.submit() # when allowed in Stock settings, allow rejected QI
+
+		# teardown
+		qa.reload()
+		qa.cancel()
+		se.reload()
+		se.cancel()
+		frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
 
 def create_quality_inspection(**args):
 	args = frappe._dict(args)
@@ -175,12 +216,11 @@
 	if not args.readings:
 		create_quality_inspection_parameter("Size")
 		readings = {"specification": "Size", "min_value": 0, "max_value": 10}
+		if args.status == "Rejected":
+			readings["reading_1"] = "12"  # status is auto set in child on save
 	else:
 		readings = args.readings
 
-	if args.status == "Rejected":
-		readings["reading_1"] = "12"  # status is auto set in child on save
-
 	if isinstance(readings, list):
 		for entry in readings:
 			create_quality_inspection_parameter(entry["specification"])
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index b12a854..563fcb0 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -45,6 +45,8 @@
 		s.posting_date = args.posting_date
 	if args.posting_time:
 		s.posting_time = args.posting_time
+	if args.inspection_required:
+		s.inspection_required = args.inspection_required
 
 	# map names
 	if args.from_warehouse:
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 a178283..22f412a 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -307,6 +307,7 @@
    "fieldname": "quality_inspection",
    "fieldtype": "Link",
    "label": "Quality Inspection",
+   "no_copy": 1,
    "options": "Quality Inspection"
   },
   {
@@ -548,7 +549,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-04-22 20:08:23.799715",
+ "modified": "2021-06-21 16:03:18.834880",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index ba31ad7..af2ada8 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -54,7 +54,7 @@
 		)
 
 		# _Test Item for Reposting transferred from Stores to FG warehouse on 30-04-2020
-		make_stock_entry(
+		se = make_stock_entry(
 			item_code="_Test Item for Reposting",
 			source="Stores - _TC",
 			target="Finished Goods - _TC",
@@ -64,29 +64,29 @@
 			posting_date='2020-04-30',
 			posting_time='14:00'
 		)
-		target_wh_sle = get_previous_sle({
+		target_wh_sle = frappe.db.get_value('Stock Ledger Entry', {
 			"item_code": "_Test Item for Reposting",
 			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-04-30',
-			"posting_time": '14:00'
-		})
+			"voucher_type": "Stock Entry",
+			"voucher_no": se.name
+		}, ["valuation_rate"], as_dict=1)
 
 		self.assertEqual(target_wh_sle.get("valuation_rate"), 150)
 
 		# Repack entry on 5-5-2020
 		repack = create_repack_entry(company=company, posting_date='2020-05-05', posting_time='14:00')
 
-		finished_item_sle = get_previous_sle({
+		finished_item_sle = frappe.db.get_value('Stock Ledger Entry', {
 			"item_code": "_Test Finished Item for Reposting",
 			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-05-05',
-			"posting_time": '14:00'
-		})
+			"voucher_type": "Stock Entry",
+			"voucher_no": repack.name
+		}, ["incoming_rate", "valuation_rate"], as_dict=1)
 		self.assertEqual(finished_item_sle.get("incoming_rate"), 540)
 		self.assertEqual(finished_item_sle.get("valuation_rate"), 540)
 
 		# Reconciliation for _Test Item for Reposting at Stores on 12-04-2020: Qty = 50, Rate = 150
-		create_stock_reconciliation(
+		sr = create_stock_reconciliation(
 			item_code="_Test Item for Reposting",
 			warehouse="Stores - _TC",
 			qty=50,
@@ -109,12 +109,12 @@
 		self.assertEqual(target_wh_sle.get("valuation_rate"), 175)
 
 		# Check valuation rate of repacked item after back-dated entry at Stores
-		finished_item_sle = get_previous_sle({
+		finished_item_sle = frappe.db.get_value('Stock Ledger Entry', {
 			"item_code": "_Test Finished Item for Reposting",
 			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-05-05',
-			"posting_time": '14:00'
-		})
+			"voucher_type": "Stock Entry",
+			"voucher_no": repack.name
+		}, ["incoming_rate", "valuation_rate"], as_dict=1)
 		self.assertEqual(finished_item_sle.get("incoming_rate"), 790)
 		self.assertEqual(finished_item_sle.get("valuation_rate"), 790)
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index e646600..2e09286 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -357,6 +357,7 @@
 			if row.current_qty:
 				data.actual_qty = -1 * row.current_qty
 				data.qty_after_transaction = flt(row.current_qty)
+				data.previous_qty_after_transaction = flt(row.qty)
 				data.valuation_rate = flt(row.current_valuation_rate)
 				data.stock_value = data.qty_after_transaction * data.valuation_rate
 				data.stock_value_difference = -1 * flt(row.amount_difference)
@@ -404,17 +405,18 @@
 
 			key = (d.item_code, d.warehouse)
 			if key not in merge_similar_entries:
+				d.total_amount = (d.actual_qty * d.valuation_rate)
 				merge_similar_entries[key] = d
 			elif d.serial_no:
 				data = merge_similar_entries[key]
 				data.actual_qty += d.actual_qty
 				data.qty_after_transaction += d.qty_after_transaction
 
-				data.valuation_rate = (data.valuation_rate + d.valuation_rate) / data.actual_qty
+				data.total_amount += (d.actual_qty * d.valuation_rate)
+				data.valuation_rate = (data.total_amount) / data.actual_qty
 				data.serial_no += '\n' + d.serial_no
 
-				if data.incoming_rate:
-					data.incoming_rate = (data.incoming_rate + d.incoming_rate) / data.actual_qty
+				data.incoming_rate = (data.total_amount) / data.actual_qty
 
 		for key, value in merge_similar_entries.items():
 			new_sl_entries.append(value)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 36380b8..84cdc49 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -6,7 +6,7 @@
 
 from __future__ import unicode_literals
 import frappe, unittest
-from frappe.utils import flt, nowdate, nowtime
+from frappe.utils import flt, nowdate, nowtime, random_string, add_days
 from erpnext.accounts.utils import get_stock_and_account_balance
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
@@ -14,6 +14,7 @@
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 
 class TestStockReconciliation(unittest.TestCase):
 	@classmethod
@@ -150,6 +151,42 @@
 			stock_doc = frappe.get_doc("Stock Reconciliation", d)
 			stock_doc.cancel()
 
+
+	def test_stock_reco_for_merge_serialized_item(self):
+		to_delete_records = []
+
+		# Add new serial nos
+		serial_item_code = "Stock-Reco-Serial-Item-2"
+		serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
+
+		sr = create_stock_reconciliation(item_code=serial_item_code, serial_no=random_string(6),
+			warehouse = serial_warehouse, qty=1, rate=100, do_not_submit=True, purpose='Opening Stock')
+
+		for i in range(3):
+			sr.append('items', {
+				'item_code': serial_item_code,
+				'warehouse': serial_warehouse,
+				'qty': 1,
+				'valuation_rate': 100,
+				'serial_no': random_string(6)
+			})
+
+		sr.save()
+		sr.submit()
+
+		sle_entries = frappe.get_all('Stock Ledger Entry', filters= {'voucher_no': sr.name},
+			fields = ['name', 'incoming_rate'])
+
+		self.assertEqual(len(sle_entries), 1)
+		self.assertEqual(sle_entries[0].incoming_rate, 100)
+
+		to_delete_records.append(sr.name)
+		to_delete_records.reverse()
+
+		for d in to_delete_records:
+			stock_doc = frappe.get_doc("Stock Reconciliation", d)
+			stock_doc.cancel()
+
 	def test_stock_reco_for_batch_item(self):
 		to_delete_records = []
 		to_delete_serial_nos = []
@@ -204,6 +241,117 @@
 		self.assertEqual(sr.get("items")[0].valuation_rate, 0)
 		self.assertEqual(sr.get("items")[0].amount, 0)
 
+	def test_backdated_stock_reco_qty_reposting(self):
+		"""
+			Test if a backdated stock reco recalculates future qty until next reco.
+			-------------------------------------------
+			Var		| Doc	|	Qty	| Balance
+			-------------------------------------------
+			SR5		| Reco	|	0	|	8	(posting date: today-4) [backdated]
+			PR1		| PR	|	10	|	18	(posting date: today-3)
+			PR2		| PR	|	1	|	19	(posting date: today-2)
+			SR4		| Reco	|	0	|	6	(posting date: today-1) [backdated]
+			PR3		| PR	|	1	|	7	(posting date: today) # can't post future PR
+		"""
+		item_code = "Backdated-Reco-Item"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code)
+
+		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
+			posting_date=add_days(nowdate(), -3))
+		pr2 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
+			posting_date=add_days(nowdate(), -2))
+		pr3 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
+			posting_date=nowdate())
+
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		pr3_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr3.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 10)
+		self.assertEqual(pr3_balance, 12)
+
+		# post backdated stock reco in between
+		sr4 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=6, rate=100,
+			posting_date=add_days(nowdate(), -1))
+		pr3_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr3.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr3_balance, 7)
+
+		# post backdated stock reco at the start
+		sr5 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=8, rate=100,
+			posting_date=add_days(nowdate(), -4))
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		pr2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr2.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		sr4_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": sr4.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 18)
+		self.assertEqual(pr2_balance, 19)
+		self.assertEqual(sr4_balance, 6) # check if future stock reco is unaffected
+
+		# cancel backdated stock reco and check future impact
+		sr5.cancel()
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		pr2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr2.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		sr4_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": sr4.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 10)
+		self.assertEqual(pr2_balance, 11)
+		self.assertEqual(sr4_balance, 6) # check if future stock reco is unaffected
+
+		# teardown
+		sr4.cancel()
+		pr3.cancel()
+		pr2.cancel()
+		pr1.cancel()
+
+	def test_backdated_stock_reco_future_negative_stock(self):
+		"""
+			Test if a backdated stock reco causes future negative stock and is blocked.
+			-------------------------------------------
+			Var		| Doc	|	Qty	| Balance
+			-------------------------------------------
+			PR1		| PR	|	10	|	10		(posting date: today-2)
+			SR3		| Reco	|	0	|	1		(posting date: today-1) [backdated & blocked]
+			DN2		| DN	|	-2	|	8(-1)	(posting date: today)
+		"""
+		from erpnext.stock.stock_ledger import NegativeStockError
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		item_code = "Backdated-Reco-Item"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code)
+
+		negative_stock_setting = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
+		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 0)
+
+		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
+			posting_date=add_days(nowdate(), -2))
+		dn2 = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=2, rate=120,
+			posting_date=nowdate())
+
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		dn2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": dn2.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 10)
+		self.assertEqual(dn2_balance, 8)
+
+		# check if stock reco is blocked
+		sr3 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
+			posting_date=add_days(nowdate(), -1), do_not_submit=True)
+		self.assertRaises(NegativeStockError, sr3.submit)
+
+		# teardown
+		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", negative_stock_setting)
+		sr3.cancel()
+		dn2.cancel()
+		pr1.cancel()
+
 def insert_existing_sle(warehouse):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
@@ -231,6 +379,12 @@
 		serial_item_doc.serial_no_series = "SRSI.####"
 		serial_item_doc.save(ignore_permissions=True)
 
+	serial_item_doc = create_item("Stock-Reco-Serial-Item-2", is_stock_item=1)
+	if not serial_item_doc.has_serial_no:
+		serial_item_doc.has_serial_no = 1
+		serial_item_doc.serial_no_series = "SRSII.####"
+		serial_item_doc.save(ignore_permissions=True)
+
 	batch_item_doc = create_item("Stock-Reco-batch-Item-1", is_stock_item=1)
 	if not batch_item_doc.has_batch_no:
 		batch_item_doc.has_batch_no = 1
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index cf5d98d..2a9dcfb 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -23,7 +23,10 @@
   "allow_negative_stock",
   "show_barcode_field",
   "clean_description_html",
+  "quality_inspection_settings_section",
   "action_if_quality_inspection_is_not_submitted",
+  "column_break_21",
+  "action_if_quality_inspection_is_rejected",
   "section_break_7",
   "automatically_set_serial_nos_based_on_fifo",
   "set_qty_in_transactions_based_on_serial_no_input",
@@ -264,6 +267,22 @@
   {
    "fieldname": "column_break_31",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "quality_inspection_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Quality Inspection Settings"
+  },
+  {
+   "fieldname": "column_break_21",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "Stop",
+   "fieldname": "action_if_quality_inspection_is_rejected",
+   "fieldtype": "Select",
+   "label": "Action If Quality Inspection Is Rejected",
+   "options": "Stop\nWarn"
   }
  ],
  "icon": "icon-cog",
@@ -271,7 +290,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-04-30 17:27:42.709231",
+ "modified": "2021-07-10 16:17:42.159829",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 9fe89c3..4e9c768 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -55,6 +55,11 @@
 				sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
 
 			args = sle_doc.as_dict()
+
+			if sle.get("voucher_type") == "Stock Reconciliation":
+				# preserve previous_qty_after_transaction for qty reposting
+				args.previous_qty_after_transaction = sle.get("previous_qty_after_transaction")
+
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
 def get_args_for_future_sle(row):
@@ -215,7 +220,7 @@
 		"""
 		self.data.setdefault(args.warehouse, frappe._dict())
 		warehouse_dict = self.data[args.warehouse]
-		previous_sle = self.get_previous_sle_of_current_voucher(args)
+		previous_sle = get_previous_sle_of_current_voucher(args)
 		warehouse_dict.previous_sle = previous_sle
 
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
@@ -227,29 +232,6 @@
 			"stock_value_difference": 0.0
 		})
 
-	def get_previous_sle_of_current_voucher(self, args):
-		"""get stock ledger entries filtered by specific posting datetime conditions"""
-
-		args['time_format'] = '%H:%i:%s'
-		if not args.get("posting_date"):
-			args["posting_date"] = "1900-01-01"
-		if not args.get("posting_time"):
-			args["posting_time"] = "00:00"
-
-		sle = frappe.db.sql("""
-			select *, timestamp(posting_date, posting_time) as "timestamp"
-			from `tabStock Ledger Entry`
-			where item_code = %(item_code)s
-				and warehouse = %(warehouse)s
-				and is_cancelled = 0
-				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
-			order by timestamp(posting_date, posting_time) desc, creation desc
-			limit 1
-			for update""", args, as_dict=1)
-
-		return sle[0] if sle else frappe._dict()
-
-
 	def build(self):
 		from erpnext.controllers.stock_controller import future_sle_exists
 
@@ -734,6 +716,35 @@
 			bin_doc.flags.via_stock_ledger_entry = True
 			bin_doc.save(ignore_permissions=True)
 
+
+def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
+	"""get stock ledger entries filtered by specific posting datetime conditions"""
+
+	args['time_format'] = '%H:%i:%s'
+	if not args.get("posting_date"):
+		args["posting_date"] = "1900-01-01"
+	if not args.get("posting_time"):
+		args["posting_time"] = "00:00"
+
+	voucher_condition = ""
+	if exclude_current_voucher:
+		voucher_no = args.get("voucher_no")
+		voucher_condition = f"and voucher_no != '{voucher_no}'"
+
+	sle = frappe.db.sql("""
+		select *, timestamp(posting_date, posting_time) as "timestamp"
+		from `tabStock Ledger Entry`
+		where item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and is_cancelled = 0
+			{voucher_condition}
+			and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+		order by timestamp(posting_date, posting_time) desc, creation desc
+		limit 1
+		for update""".format(voucher_condition=voucher_condition), args, as_dict=1)
+
+	return sle[0] if sle else frappe._dict()
+
 def get_previous_sle(args, for_update=False):
 	"""
 		get the last sle on or before the current time-bucket,
@@ -862,9 +873,24 @@
 	return valuation_rate
 
 def update_qty_in_future_sle(args, allow_negative_stock=None):
+	"""Recalculate Qty after Transaction in future SLEs based on current SLE."""
+	datetime_limit_condition = ""
+	qty_shift = args.actual_qty
+
+	# find difference/shift in qty caused by stock reconciliation
+	if args.voucher_type == "Stock Reconciliation":
+		qty_shift = get_stock_reco_qty_shift(args)
+
+	# find the next nearest stock reco so that we only recalculate SLEs till that point
+	next_stock_reco_detail = get_next_stock_reco(args)
+	if next_stock_reco_detail:
+		detail = next_stock_reco_detail[0]
+		# add condition to update SLEs before this date & time
+		datetime_limit_condition = get_datetime_limit_condition(detail)
+
 	frappe.db.sql("""
 		update `tabStock Ledger Entry`
-		set qty_after_transaction = qty_after_transaction + {qty}
+		set qty_after_transaction = qty_after_transaction + {qty_shift}
 		where
 			item_code = %(item_code)s
 			and warehouse = %(warehouse)s
@@ -876,15 +902,70 @@
 					and creation > %(creation)s
 				)
 			)
-	""".format(qty=args.actual_qty), args)
+		{datetime_limit_condition}
+		""".format(qty_shift=qty_shift, datetime_limit_condition=datetime_limit_condition), args)
 
 	validate_negative_qty_in_future_sle(args, allow_negative_stock)
 
+def get_stock_reco_qty_shift(args):
+	stock_reco_qty_shift = 0
+	if args.get("is_cancelled"):
+		if args.get("previous_qty_after_transaction"):
+			# get qty (balance) that was set at submission
+			last_balance = args.get("previous_qty_after_transaction")
+			stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance)
+		else:
+			stock_reco_qty_shift = flt(args.actual_qty)
+	else:
+		# reco is being submitted
+		last_balance = get_previous_sle_of_current_voucher(args,
+			exclude_current_voucher=True).get("qty_after_transaction")
+
+		if last_balance is not None:
+			stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance)
+		else:
+			stock_reco_qty_shift = args.qty_after_transaction
+
+	return stock_reco_qty_shift
+
+def get_next_stock_reco(args):
+	"""Returns next nearest stock reconciliaton's details."""
+
+	return frappe.db.sql("""
+		select
+			name, posting_date, posting_time, creation, voucher_no
+		from
+			`tabStock Ledger Entry`
+		where
+			item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and voucher_type = 'Stock Reconciliation'
+			and voucher_no != %(voucher_no)s
+			and is_cancelled = 0
+			and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
+				or (
+					timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
+					and creation > %(creation)s
+				)
+			)
+		limit 1
+	""", args, as_dict=1)
+
+def get_datetime_limit_condition(detail):
+	return f"""
+		and
+		(timestamp(posting_date, posting_time) < timestamp('{detail.posting_date}', '{detail.posting_time}')
+			or (
+				timestamp(posting_date, posting_time) = timestamp('{detail.posting_date}', '{detail.posting_time}')
+				and creation < '{detail.creation}'
+			)
+		)"""
+
 def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
 	allow_negative_stock = allow_negative_stock \
 		or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
 
-	if args.actual_qty < 0 and not allow_negative_stock:
+	if (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation") and not allow_negative_stock:
 		sle = get_future_sle_with_negative_qty(args)
 		if sle:
 			message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html
index 4c8c40d..bfd6349 100644
--- a/erpnext/templates/includes/projects/project_row.html
+++ b/erpnext/templates/includes/projects/project_row.html
@@ -1,28 +1,54 @@
-{% if doc.status=="Open" %}
-<div class="web-list-item">
-	<a class="no-decoration" href="/projects?project={{ doc.name | urlencode }}">
-		<div class="row">
-			<div class="col-xs-6">
-
-				{{ doc.name }}
-			</div>
-			<div class="col-xs-3">
-				{% if doc.percent_complete %}
-					<div class="progress" style="margin-bottom: 0!important; margin-top: 10px!important; height:5px;">
-					  <div class="progress-bar progress-bar-{{ "warning" if doc.percent_complete|round < 100 else "success"}}" role="progressbar"
-					  	aria-valuenow="{{ doc.percent_complete|round|int }}"
-					  	aria-valuemin="0" aria-valuemax="100" style="width:{{ doc.percent_complete|round|int }}%;">
-					  </div>
-					</div>
-				{% else %}
-					<span class="indicator {{ "red" if doc.status=="Open" else "gray"  }}">
-						{{ doc.status }}</span>
-				{% endif %}
-			</div>
-			<div class="col-xs-3 text-right small text-muted">
-				{{ frappe.utils.pretty_date(doc.modified) }}
-			</div>
-		</div>
-	</a>
-</div>
+{% if doc.status == "Open" %}
+  <div class="web-list-item transaction-list-item">
+    <div class="row">
+      <div class="col-xs-2">
+        <a class="transaction-item-link" href="/projects?project={{ doc.name | urlencode }}">Link</a>
+        {{ doc.name }}
+      </div>
+      <div class="col-xs-2">
+        {{ doc.project_name }}
+      </div>
+      <div class="col-xs-3 text-center">
+        {% if doc.percent_complete %}
+          {% set pill_class = "green" if doc.percent_complete | round == 100 else
+            "orange" %}
+          <div class="ellipsis">
+            <span class="indicator-pill {{ pill_class }} filterable ellipsis">
+              <span>{{ frappe.utils.cint(doc.percent_complete) }}
+                %</span>
+            </span>
+          </div>
+        {% else %}
+          <span class="indicator-pill {{ " red" if doc.status=="Open" else " darkgrey" }}">
+            {{ doc.status }}</span>
+        {% endif %}
+      </div>
+      {% if doc["_assign"] %}
+        {% set assigned_users = json.loads(doc["_assign"])%}
+        <div class="col-xs-2">
+          {% for user in assigned_users %}
+            {% set user_details = frappe
+              .db
+              .get_value("User", user, [
+                "full_name", "user_image"
+              ], as_dict = True) %}
+            {% if user_details.user_image %}
+              <span class="avatar avatar-small" style="width:32px; height:32px;" title="{{ user_details.full_name }}">
+                <img src="{{ user_details.user_image }}">
+              </span>
+            {% else %}
+              <span class="avatar avatar-small" style="width:32px; height:32px;" title="{{ user_details.full_name }}">
+                <div class='standard-image' style="background-color: #F5F4F4; color: #000;">
+                  {{ frappe.utils.get_abbr(user_details.full_name) }}
+                </div>
+              </span>
+            {% endif %}
+          {% endfor %}
+        </div>
+      {% endif %}
+      <div class="col-xs-3 text-right small text-muted">
+        {{ frappe.utils.pretty_date(doc.modified) }}
+      </div>
+    </div>
+  </div>
 {% endif %}
diff --git a/erpnext/templates/includes/projects/project_tasks.html b/erpnext/templates/includes/projects/project_tasks.html
index 50b9f4b..2b07a5f 100644
--- a/erpnext/templates/includes/projects/project_tasks.html
+++ b/erpnext/templates/includes/projects/project_tasks.html
@@ -1,32 +1,5 @@
 {% for task in doc.tasks %}
-	<div class='task'>
-		<a class="no-decoration task-link {{ task.css_seen }}" href="/tasks?name={{ task.name }}">
-		<div class='row project-item'>
-			<div class='col-xs-9'>
-				<span class="indicator {{ "red" if task.status=="Open" else "green" if task.status=="Closed" else "gray" }}" title="{{ task.status }}"  > {{ task.subject }}</span>
-	 				<div class="small text-muted item-timestamp"
-	 					title="{{ frappe.utils.pretty_date(task.modified) }}">
-						{{ _("modified") }} {{ frappe.utils.pretty_date(task.modified) }}
-	 				</div>
-			</div>
-			<div class='col-xs-1'>{% if task.todo %}
-					{% if task.todo.user_image %}
-						<span class="avatar avatar-small" title="{{ task.todo.owner }}">
-							<img src="{{ task.todo.user_image }}">
-						</span>
-					{% else %}
-						<span class="avatar avatar-small standard-image" title="Assigned to {{ task.todo.owner }}">
-
-						</span>
-					{% endif %}
-				{% endif %}	 </div>
-			<div class='col-xs-2'>
-				<span class="pull-right list-comment-count small {{ "text-extra-muted" if task.comment_count==0 else "text-muted" }}">
-					<i class="octicon octicon-comment-discussion"></i>
-						{{ task.comment_count }}
-				</span>
-			</div>
-		</div>
-		</a>
-	</div>
+  <div class="web-list-item transaction-list-item">
+    {{ task_row(task, 0) }}
+  </div>
 {% endfor %}
diff --git a/erpnext/templates/includes/projects/project_timesheets.html b/erpnext/templates/includes/projects/project_timesheets.html
index 05a07c1..850c5e9 100644
--- a/erpnext/templates/includes/projects/project_timesheets.html
+++ b/erpnext/templates/includes/projects/project_timesheets.html
@@ -1,23 +1,33 @@
 {% for timesheet in doc.timesheets %}
-<div class='timesheet'>
-	<a class="no-decoration timesheet-link {{ timesheet.css_seen }}" href="/timesheet/{{ timesheet.info.name}}">
-		<div class='row project-item'>
-			<div class='col-xs-10'>
-				<span class="indicator {{ "blue" if timesheet.info.status=="Submitted" else "red" if timesheet.info.status=="Draft" else "gray" }}" title="{{ timesheet.info.status }}"  > {{ timesheet.info.name }} </span>
-				<div class="small text-muted item-timestamp">
-				{{ _("From") }} {{ frappe.format_date(timesheet.from_time) }} {{ _("to") }} {{ frappe.format_date(timesheet.to_time) }}
-			</div>
-			</div>
-				<div class='col-xs-1' style="margin-right:-30px;">
-				<span class="avatar avatar-small" title="{{ timesheet.info.modified_by }}"> <img src="{{ timesheet.info.user_image }}" style="display:flex;"></span>
-			</div>
-			<div class='col-xs-1'>
-				<span class="pull-right list-comment-count small {{ "text-extra-muted" if timesheet.comment_count==0 else "text-muted" }}">
-				<i class="octicon octicon-comment-discussion"></i>
-				{{ timesheet.info.comment_count }}
-				</span>
-			</div>
-		</div>
-	</a>
-</div>
-{% endfor %}
\ No newline at end of file
+  <div class="web-list-item transaction-list-item">
+    <div class="row">
+      <div class="col-xs-2">{{ timesheet.name }}</div>
+      <a class="transaction-item-link" href="/timesheet/{{ timesheet.name}}">Link</a>
+      <div class="col-xs-2">{{ timesheet.status }}</div>
+      <div class="col-xs-2">{{ frappe.utils.format_date(timesheet.from_time, "medium") }}</div>
+      <div class="col-xs-2">{{ frappe.utils.format_date(timesheet.to_time, "medium") }}</div>
+      <div class="col-xs-2">
+        {% set user_details = frappe
+          .db
+          .get_value("User", timesheet.modified_by, [
+            "full_name", "user_image"
+          ], as_dict = True)
+ 		%}
+        {% if user_details.user_image %}
+          <span class="avatar avatar-small" style="width:32px; height:32px;" title="{{ user_details.full_name }}">
+            <img src="{{ user_details.user_image }}">
+          </span>
+        {% else %}
+          <span class="avatar avatar-small" style="width:32px; height:32px;" title="{{ user_details.full_name }}">
+            <div class='standard-image' style='background-color: #F5F4F4; color: #000;'>
+              {{ frappe.utils.get_abbr(user_details.full_name) }}
+            </div>
+          </span>
+        {% endif %}
+      </div>
+      <div class="col-xs-2 text-right">
+        {{ frappe.utils.pretty_date(timesheet.modified) }}
+      </div>
+    </div>
+  </div>
+{% endfor %}
diff --git a/erpnext/templates/pages/projects.html b/erpnext/templates/pages/projects.html
index 7e294e0..76eaf75 100644
--- a/erpnext/templates/pages/projects.html
+++ b/erpnext/templates/pages/projects.html
@@ -1,90 +1,173 @@
 {% extends "templates/web.html" %}
 
-{% block title %}{{ doc.project_name }}{% endblock %}
+{% block title %}
+  {{ doc.project_name }}
+{% endblock %}
+
+{% block head_include %}
+  <link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
+{% endblock %}
 
 {% block header %}
-	<h1>{{ doc.project_name }}</h1>
+  <h1>{{ doc.project_name }}</h1>
 {% endblock %}
 
 {% block style %}
-	<style>
-		{% include "templates/includes/projects.css" %}
-	</style>
+  <style>
+    {
+      % include "templates/includes/projects.css"%
+    }
+  </style>
 {% endblock %}
 
-
 {% block page_content %}
-{% if doc.percent_complete %}
-<div class="progress progress-hg">
-	<div class="progress-bar progress-bar-{{ "warning" if doc.percent_complete|round < 100 else "success" }} active" 				role="progressbar" aria-valuenow="{{ doc.percent_complete|round|int }}"
-	aria-valuemin="0" aria-valuemax="100" style="width:{{ doc.percent_complete|round|int }}%;">
-	</div>
-</div>
-{% endif %}
 
-<div class="clearfix">
-  <h4 style="float: left;">{{ _("Tasks") }}</h4>
-  <a class="btn btn-secondary btn-light btn-sm" style="float: right; position: relative; top: 10px;" href='/tasks?new=1&project={{ doc.project_name }}'>{{ _("New task") }}</a>
-</div>
+  {{ progress_bar(doc.percent_complete) }}
 
-<p>
-<!-- <a class='small underline task-status-switch' data-status='Open'>{{ _("Show closed") }}</a> -->
-</p>
+  <div class="d-flex mt-5 mb-5 justify-content-between">
+    <h4>Status:</h4>
+    <h4>Progress:
+      <span>{{ doc.percent_complete }}
+        %</span>
+    </h4>
+    <h4>Hours Spent:
+      <span>{{ doc.actual_time }}</span>
+    </h4>
+  </div>
 
-{% if doc.tasks %}
-	<div class='project-task-section'>
-		<div class='project-task'>
-		{% include "erpnext/templates/includes/projects/project_tasks.html" %}
-		</div>
-		<p><a id= 'more-task' style='display: none;' class='more-tasks small underline'>{{ _("More") }}</a><p>
-	</div>
-{% else %}
-	<p class="text-muted">{{ _("No tasks") }}</p>
-{% endif %}
+  {{ progress_bar(doc.percent_complete) }}
 
+  {% if doc.tasks %}
+    <div class="website-list">
+      <div class="result">
+        <div class="web-list-item transaction-list-item">
+          <div class="row">
+            <h3 class="col-xs-4">Tasks</h3>
+            <h3 class="col-xs-2">Status</h3>
+            <h3 class="col-xs-2">End Date</h3>
+            <h3 class="col-xs-2">Assigned To</h3>
+            <div class="col-xs-2 text-right">
+              <a class="btn btn-secondary btn-light btn-sm" href='/tasks?new=1&project={{ doc.project_name }}'>{{ _("New task") }}</a>
+            </div>
+          </div>
+        </div>
+        {% include "erpnext/templates/includes/projects/project_tasks.html" %}
+      </div>
+    </div>
+  {% else %}
+    <p class="font-weight-bold">{{ _("No Tasks") }}</p>
+  {% endif %}
 
-<div class='padding'></div>
+  {% if doc.timesheets %}
+    <div class="website-list">
+      <div class="result">
+        <div class="web-list-item transaction-list-item">
+          <div class="row">
+            <h3 class="col-xs-2">Timesheets</h3>
+            <h3 class="col-xs-2">Status</h3>
+            <h3 class="col-xs-2">From</h3>
+            <h3 class="col-xs-2">To</h3>
+            <h3 class="col-xs-2">Modified By</h3>
+            <h3 class="col-xs-2 text-right">Modified On</h3>
+          </div>
+        </div>
+        {% include "erpnext/templates/includes/projects/project_timesheets.html" %}
+      </div>
+    </div>
+  {% else %}
+    <p class="font-weight-bold mt-5">{{ _("No Timesheets") }}</p>
+  {% endif %}
 
-<h4>{{ _("Timesheets") }}</h4>
+  {% if doc.attachments %}
+    <div class='padding'></div>
 
-{% if doc.timesheets %}
-	<div class='project-timelogs'>
-	{% include "erpnext/templates/includes/projects/project_timesheets.html" %}
-	</div>
-	{% if doc.timesheets|length > 9 %}
-		<p><a class='more-timelogs small underline'>{{ _("More") }}</a><p>
-	{% endif %}
-{% else %}
-	<p class="text-muted">{{ _("No time sheets") }}</p>
-{% endif %}
-
-{% if doc.attachments %}
-<div class='padding'></div>
-
-<h4>{{ _("Attachments") }}</h4>
-	<div class="project-attachments">
-		{% for attachment in doc.attachments %}
-		<div class="attachment">
-			<a class="no-decoration attachment-link" href="{{ attachment.file_url }}" target="blank">
-				<div class="row">
-					<div class="col-xs-9">
-						<span class="indicator red file-name"> {{ attachment.file_name }}</span>
-					</div>
-					<div class="col-xs-3">
-						<span class="pull-right file-size">{{ attachment.file_size }}</span>
-					</div>
-				</div>
-			</a>
-		</div>
-		{% endfor %}
-	</div>
-{% endif %}
+    <h4>{{ _("Attachments") }}</h4>
+    <div class="project-attachments">
+      {% for attachment in doc.attachments %}
+        <div class="attachment">
+          <a class="no-decoration attachment-link" href="{{ attachment.file_url }}" target="blank">
+            <div class="row">
+              <div class="col-xs-9">
+                <span class="indicator red file-name">
+                  {{ attachment.file_name }}</span>
+              </div>
+              <div class="col-xs-3">
+                <span class="pull-right file-size">{{ attachment.file_size }}</span>
+              </div>
+            </div>
+          </a>
+        </div>
+      {% endfor %}
+    </div>
+  {% endif %}
 
 </div>
 
 <script>
-	{% include "frappe/public/js/frappe/provide.js" %}
-	{% include "frappe/public/js/frappe/form/formatters.js" %}
+  { % include "frappe/public/js/frappe/provide.js" %
+  } { % include "frappe/public/js/frappe/form/formatters.js" %
+  }
 </script>
 
 {% endblock %}
+
+{% macro progress_bar(percent_complete) %}
+{% if percent_complete %}
+  <div class="progress progress-hg" style="height: 5px;">
+    <div class="progress-bar progress-bar-{{ 'warning' if percent_complete|round < 100 else 'success' }} active" role="progressbar" aria-valuenow="{{ percent_complete|round|int }}" aria-valuemin="0" aria-valuemax="100" style="width:{{ percent_complete|round|int }}%;"></div>
+  </div>
+{% else %}
+  <hr>
+{% endif %}
+{% endmacro %}
+
+{% macro task_row(task, indent) %}
+<div class="row mt-5 {% if task.children %} font-weight-bold {% endif %}">
+  <div class="col-xs-4">
+    <a class="nav-link " style="color: inherit; {% if task.parent_task %} margin-left: {{ indent }}px {% endif %}" href="/tasks?name={{ task.name | urlencode }}">
+      {% if task.parent_task %}
+        <span class="">
+          <i class="fa fa-level-up fa-rotate-90"></i>
+        </span>
+      {% endif %}
+      {{ task.subject }}</a>
+  </div>
+  <div class="col-xs-2">{{ task.status }}</div>
+  <div class="col-xs-2">
+    {% if task.exp_end_date %}
+      {{ task.exp_end_date }}
+    {% else %}
+      --
+    {% endif %}
+  </div>
+  <div class="col-xs-2">
+    {% if task["_assign"] %}
+      {% set assigned_users = json.loads(task["_assign"])%}
+      {% for user in assigned_users %}
+        {% set user_details = frappe.db.get_value("User", user,
+		["full_name", "user_image"],
+		as_dict = True)%}
+        {% if user_details.user_image %}
+          <span class="avatar avatar-small" style="width:32px; height:32px;" title="{{ user_details.full_name }}">
+            <img src="{{ user_details.user_image }}">
+          </span>
+        {% else %}
+          <span class="avatar avatar-small" style="width:32px; height:32px;" title="{{ user_details.full_name }}">
+            <div class='standard-image' style='background-color: #F5F4F4; color: #000;'>
+              {{ frappe.utils.get_abbr(user_details.full_name) }}
+            </div>
+          </span>
+        {% endif %}
+      {% endfor %}
+    {% endif %}
+  </div>
+  <div class="col-xs-2 text-right">
+    {{ frappe.utils.pretty_date(task.modified) }}
+  </div>
+</div>
+{% if task.children %}
+  {% for child in task.children %}
+    {{ task_row(child, indent + 30) }}
+  {% endfor %}
+{% endif %}
+{% endmacro %}
diff --git a/erpnext/templates/pages/projects.py b/erpnext/templates/pages/projects.py
index d23fed9..b369cb6 100644
--- a/erpnext/templates/pages/projects.py
+++ b/erpnext/templates/pages/projects.py
@@ -35,26 +35,16 @@
 	# if item_status:
 # 		filters["status"] = item_status
 	tasks = frappe.get_all("Task", filters=filters,
-		fields=["name", "subject", "status", "_seen", "_comments", "modified", "description"],
+		fields=["name", "subject", "status", "modified", "_assign", "exp_end_date", "is_group", "parent_task"],
 		limit_start=start, limit_page_length=10)
-
+	task_nest = []
 	for task in tasks:
-		task.todo = frappe.get_all('ToDo',filters={'reference_name':task.name, 'reference_type':'Task'},
-		fields=["assigned_by", "owner", "modified", "modified_by"])
-
-		if task.todo:
-			task.todo=task.todo[0]
-			task.todo.user_image = frappe.db.get_value('User', task.todo.owner, 'user_image')
-
-
-		task.comment_count = len(json.loads(task._comments or "[]"))
-
-		task.css_seen = ''
-		if task._seen:
-			if frappe.session.user in json.loads(task._seen):
-				task.css_seen = 'seen'
-
-	return tasks
+		if task.is_group:
+			child_tasks = list(filter(lambda x: x.parent_task == task.name, tasks))
+			if len(child_tasks):
+				task.children = child_tasks
+		task_nest.append(task)
+	return list(filter(lambda x: not x.parent_task, tasks))
 
 @frappe.whitelist()
 def get_task_html(project, start=0, item_status=None):
@@ -74,19 +64,12 @@
 	fields=['project','activity_type','from_time','to_time','parent'],
 	limit_start=start, limit_page_length=10)
 	for timesheet in timesheets:
-		timesheet.infos = frappe.get_all('Timesheet', filters={"name": timesheet.parent},
-			fields=['name','_comments','_seen','status','modified','modified_by'],
+		info = frappe.get_all('Timesheet', filters={"name": timesheet.parent},
+			fields=['name','status','modified','modified_by'],
 			limit_start=start, limit_page_length=10)
 
-		for timesheet.info in timesheet.infos:
-			timesheet.info.user_image = frappe.db.get_value('User', timesheet.info.modified_by, 'user_image')
-
-			timesheet.info.comment_count = len(json.loads(timesheet.info._comments or "[]"))
-
-			timesheet.info.css_seen = ''
-			if timesheet.info._seen:
-				if frappe.session.user in json.loads(timesheet.info._seen):
-					timesheet.info.css_seen = 'seen'
+		if len(info):
+			timesheet.update(info[0])
 	return timesheets
 
 @frappe.whitelist()
diff --git a/erpnext/www/book_appointment/index.py b/erpnext/www/book_appointment/index.py
index 7bfac89..ccfa97b 100644
--- a/erpnext/www/book_appointment/index.py
+++ b/erpnext/www/book_appointment/index.py
@@ -2,6 +2,7 @@
 import datetime
 import json
 import pytz
+from frappe import _
 
 
 WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
@@ -14,7 +15,8 @@
 	if is_enabled:
 		return context
 	else:
-		frappe.local.flags.redirect_location = '/404'
+		frappe.redirect_to_message(_("Appointment Scheduling Disabled"), _("Appointment Scheduling has been disabled for this site"),
+			http_status_code=302, indicator_color="red")
 		raise frappe.Redirect
 
 @frappe.whitelist(allow_guest=True)