Merge pull request #26222 from 18alantom/teabox-cogs-by-item-group

feat: report to show cogs by item groups
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..be425ec
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,12 @@
+# Since version 2.23 (released in August 2019), git-blame has a feature
+# to ignore or bypass certain commits.
+#
+# This file contains a list of commits that are not likely what you
+# are looking for in a blame, such as mass reformatting or renaming.
+# You can set this file as a default ignore file for blame by running
+# the following command.
+#
+# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
+
+# This commit just changes spaces to tabs for indentation in some files
+5f473611bd6ed57703716244a054d3fb5ba9cd23
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index 7c9e027..b96a3d6 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -66,4 +66,8 @@
         run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
 
       - name: Run Patch Tests
-        run: cd ~/frappe-bench/ && wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz &&  bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz && bench --site test_site migrate
+        run: |
+          cd ~/frappe-bench/
+          wget https://erpnext.com/files/v10-erpnext.sql.gz
+          bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz
+          bench --site test_site migrate
diff --git a/erpnext/accounts/accounts b/erpnext/accounts/accounts
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/accounts
+++ /dev/null
diff --git a/erpnext/accounts/custom/address.py b/erpnext/accounts/custom/address.py
index 5e76403..c417a49 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -33,6 +33,8 @@
 	if address and frappe.db.get_value('Dynamic Link',
 		{'parent': address, 'link_name': company}):
 		filters.append(["Address", "name", "=", address])
+	if not address:
+		filters.append(["Address", "is_shipping_address", "=", 1])
 
 	address = frappe.get_all("Address", filters=filters, fields=fields) or {}
 
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index dd346bc..335e8a1 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -263,6 +263,9 @@
 			amount, base_amount = calculate_amount(doc, item, last_gl_entry,
 				total_days, total_booking_days, account_currency)
 
+		if not amount:
+			return
+
 		if via_journal_entry:
 			book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
 				base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
@@ -298,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/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 7cd1e77..fac28c9 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -19,7 +19,7 @@
 
 	def validate(self):
 		if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
-				'Cost Center', 'Accounting Dimension Detail', 'Company') :
+				'Cost Center', 'Accounting Dimension Detail', 'Company', 'Account') :
 
 			msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
 			frappe.throw(msg)
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 781f94e..703e93c 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -18,6 +18,7 @@
   "delete_linked_ledger_entries",
   "book_asset_depreciation_entry_automatically",
   "unlink_advance_payment_on_cancelation_of_order",
+  "post_change_gl_entries",
   "tax_settings_section",
   "determine_address_tax_category_from",
   "column_break_19",
@@ -253,6 +254,13 @@
   {
    "fieldname": "column_break_19",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "1",
+   "description": "If enabled, ledger entries will be posted for change amount in POS transactions",
+   "fieldname": "post_change_gl_entries",
+   "fieldtype": "Check",
+   "label": "Create Ledger Entries for Change Amount"
   }
  ],
  "icon": "icon-cog",
@@ -260,7 +268,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-04-30 15:25:10.381008",
+ "modified": "2021-06-17 20:26:03.721202",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/accounts/doctype/advance_taxes_and_charges/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/accounts/doctype/advance_taxes_and_charges/__init__.py
diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json
new file mode 100644
index 0000000..4d63499
--- /dev/null
+++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.json
@@ -0,0 +1,197 @@
+{
+ "actions": [],
+ "creation": "2020-09-12 22:26:19.594367",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "add_deduct_tax",
+  "charge_type",
+  "row_id",
+  "account_head",
+  "col_break_1",
+  "description",
+  "included_in_paid_amount",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "section_break_8",
+  "rate",
+  "section_break_9",
+  "currency",
+  "tax_amount",
+  "total",
+  "allocated_amount",
+  "column_break_13",
+  "base_tax_amount",
+  "base_total",
+  "base_allocated_amount"
+ ],
+ "fields": [
+  {
+   "columns": 2,
+   "fieldname": "charge_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Type",
+   "oldfieldname": "charge_type",
+   "oldfieldtype": "Select",
+   "options": "\nActual\nOn Paid Amount\nOn Previous Row Amount\nOn Previous Row Total",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1",
+   "fieldname": "row_id",
+   "fieldtype": "Data",
+   "label": "Reference Row #",
+   "oldfieldname": "row_id",
+   "oldfieldtype": "Data"
+  },
+  {
+   "columns": 2,
+   "fieldname": "account_head",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account Head",
+   "oldfieldname": "account_head",
+   "oldfieldtype": "Link",
+   "options": "Account",
+   "reqd": 1,
+   "search_index": 1
+  },
+  {
+   "fieldname": "col_break_1",
+   "fieldtype": "Column Break",
+   "width": "50%"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description",
+   "oldfieldname": "description",
+   "oldfieldtype": "Small Text",
+   "print_width": "300px",
+   "reqd": 1,
+   "width": "300px"
+  },
+  {
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "default": ":Company",
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "oldfieldname": "cost_center_other_charges",
+   "oldfieldtype": "Link",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_8",
+   "fieldtype": "Section Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "rate",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Rate",
+   "oldfieldname": "rate",
+   "oldfieldtype": "Currency"
+  },
+  {
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "tax_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "options": "currency"
+  },
+  {
+   "columns": 2,
+   "fieldname": "total",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Total",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "base_tax_amount",
+   "fieldtype": "Currency",
+   "label": "Amount (Company Currency)",
+   "oldfieldname": "tax_amount",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_total",
+   "fieldtype": "Currency",
+   "label": "Total (Company Currency)",
+   "oldfieldname": "total",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "add_deduct_tax",
+   "fieldtype": "Select",
+   "label": "Add Or Deduct",
+   "options": "Add\nDeduct",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "included_in_paid_amount",
+   "fieldtype": "Check",
+   "label": "Considered In Paid Amount"
+  },
+  {
+   "fieldname": "allocated_amount",
+   "fieldtype": "Currency",
+   "label": "Allocated Amount",
+   "options": "currency"
+  },
+  {
+   "fieldname": "base_allocated_amount",
+   "fieldtype": "Currency",
+   "label": "Allocated Amount (Company Currency)",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "fetch_from": "account_head.account_currency",
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Account Currency",
+   "options": "Currency",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-09 11:46:58.373170",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Advance Taxes and Charges",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "ASC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.py
new file mode 100644
index 0000000..597d2cc
--- /dev/null
+++ b/erpnext/accounts/doctype/advance_taxes_and_charges/advance_taxes_and_charges.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 AdvanceTaxesandCharges(Document):
+	pass
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/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py
index fd86ed4..cfe28f3 100644
--- a/erpnext/accounts/doctype/c_form/c_form.py
+++ b/erpnext/accounts/doctype/c_form/c_form.py
@@ -54,7 +54,7 @@
 			frappe.throw(_("Please enter atleast 1 invoice in the table"))
 
 	def set_total_invoiced_amount(self):
-		total = sum([flt(d.grand_total) for d in self.get('invoices')])
+		total = sum(flt(d.grand_total) for d in self.get('invoices'))
 		frappe.db.set(self, 'total_invoiced_amount', total)
 
 	@frappe.whitelist()
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/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py
index 7829c93..55c1193 100644
--- a/erpnext/accounts/doctype/coupon_code/coupon_code.py
+++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py
@@ -14,7 +14,7 @@
 
 		if not self.coupon_code:
 			if self.coupon_type == "Promotional":
-				self.coupon_code =''.join([i for i in self.coupon_name if not i.isdigit()])[0:8].upper()
+				self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
 			elif self.coupon_type == "Gift Card":
 				self.coupon_code = frappe.generate_hash()[:10].upper()
 		
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index 1a6dbed..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'):
@@ -86,18 +86,18 @@
 	for reference in doc.references:
 		if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0:
 			dunnings = frappe.get_list('Dunning', filters={
-				'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')})
+				'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}, ignore_permissions=True)
 
 			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)
+		interest_amount = (interest_per_year * cint(overdue_days)) / 365
+		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/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 948c513..11465b7 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -121,8 +121,7 @@
 
 	def check_pl_account(self):
 		if self.is_opening=='Yes' and \
-				frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss" and \
-				self.voucher_type not in ['Purchase Invoice', 'Sales Invoice']:
+				frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss":
 			frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry")
 				.format(self.voucher_type, self.voucher_no, self.account))
 
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 7b62b61..b73d8bf 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -42,18 +42,18 @@
 					record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)))
 
 	def calculate_total_amount(self):
-		self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])
+		self.total_amount = sum(flt(d.outstanding_amount) for d in self.invoices)
 
 	def on_submit(self):
 		self.update_sales_invoice()
 		self.make_gl_entries()
 
 	def on_cancel(self):
-		self.set_status()
+		self.set_status(cancel=1)
 		self.update_sales_invoice()
 		self.make_gl_entries()
 
-	def set_status(self, status=None):
+	def set_status(self, status=None, cancel=0):
 		if status:
 			self.status = status
 			self.db_set("status", status)
@@ -66,6 +66,9 @@
 			elif self.docstatus == 2:
 				self.status = "Cancelled"
 
+		if cancel:
+			self.db_set('status', self.status, update_modified = True)
+
 	def update_sales_invoice(self):
 		for d in self.invoices:
 			if self.docstatus == 1:
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index ed1bd28..937597b 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -196,8 +196,8 @@
 					frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account))
 
 	def check_credit_limit(self):
-		customers = list(set([d.party for d in self.get("accounts")
-			if d.party_type=="Customer" and d.party and flt(d.debit) > 0]))
+		customers = list(set(d.party for d in self.get("accounts")
+			if d.party_type=="Customer" and d.party and flt(d.debit) > 0))
 		if customers:
 			from erpnext.selling.doctype.customer.customer import check_credit_limit
 			for customer in customers:
diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
index 88667d7..bff6422 100644
--- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
+++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py
@@ -21,7 +21,7 @@
 			idx += 1
 
 	def validate(self):
-		total = sum([flt(d.percentage_allocation) for d in self.get("percentages")])
+		total = sum(flt(d.percentage_allocation) for d in self.get("percentages"))
 
 		if flt(total, 2) != 100.0:
 			frappe.throw(_("Percentage Allocation should be equal to 100%") + \
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index b80e8ad..d3ac3a6 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -3,6 +3,8 @@
 {% include "erpnext/public/js/controllers/accounts.js" %}
 frappe.provide("erpnext.accounts.dimensions");
 
+cur_frm.cscript.tax_table = "Advance Taxes and Charges";
+
 frappe.ui.form.on('Payment Entry', {
 	onload: function(frm) {
 		if(frm.doc.__islocal) {
@@ -91,6 +93,16 @@
 			}
 		});
 
+		frm.set_query("advance_tax_account", function() {
+			return {
+				filters: {
+					"company": frm.doc.company,
+					"root_type": ["in", ["Asset", "Liability"]],
+					"is_group": 0
+				}
+			}
+		});
+
 		frm.set_query("reference_doctype", "references", function() {
 			if (frm.doc.party_type == "Customer") {
 				var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
@@ -182,6 +194,8 @@
 			frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
 
 		frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
+		frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
+			(frm.doc.paid_from_account_currency != company_currency));
 
 		frm.toggle_display("base_received_amount", (
 			frm.doc.paid_to_account_currency != company_currency
@@ -216,7 +230,7 @@
 		var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
 
 		frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
-			"difference_amount"], company_currency);
+			"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax"], company_currency);
 
 		frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency);
 		frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency);
@@ -224,11 +238,13 @@
 		var party_account_currency = frm.doc.payment_type=="Receive" ?
 			frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
 
-		frm.set_currency_labels(["total_allocated_amount", "unallocated_amount"], party_account_currency);
+		frm.set_currency_labels(["total_allocated_amount", "unallocated_amount",
+			"total_taxes_and_charges"], party_account_currency);
 
 		var currency_field = (frm.doc.payment_type=="Receive") ? "paid_from_account_currency" : "paid_to_account_currency"
 		frm.set_df_property("total_allocated_amount", "options", currency_field);
 		frm.set_df_property("unallocated_amount", "options", currency_field);
+		frm.set_df_property("total_taxes_and_charges", "options", currency_field);
 		frm.set_df_property("party_balance", "options", currency_field);
 
 		frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"],
@@ -364,6 +380,16 @@
 		}
 	},
 
+	apply_tax_withholding_amount: function(frm) {
+		if (!frm.doc.apply_tax_withholding_amount) {
+			frm.set_value("tax_withholding_category", '');
+		} else {
+			frappe.db.get_value('Supplier', frm.doc.party, 'tax_withholding_category', (values) => {
+				frm.set_value("tax_withholding_category", values.tax_withholding_category);
+			});
+		}
+	},
+
 	paid_from: function(frm) {
 		if(frm.set_party_account_based_on_party) return;
 
@@ -843,12 +869,12 @@
 			if(frm.doc.payment_type == "Receive"
 				&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
 				&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
-					unallocated_amount = (frm.doc.base_received_amount + total_deductions
-						- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
+					unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
+						+ frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
 			} else if (frm.doc.payment_type == "Pay"
 				&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
 				&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
-					unallocated_amount = (frm.doc.base_paid_amount - (total_deductions
+					unallocated_amount = (frm.doc.base_paid_amount + frm.doc.base_total_taxes_and_charges - (total_deductions
 						+ frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate;
 			}
 		}
@@ -874,7 +900,8 @@
 		var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
 			function(d) { return flt(d.amount) }));
 
-		frm.set_value("difference_amount", difference_amount - total_deductions);
+		frm.set_value("difference_amount", difference_amount - total_deductions +
+			frm.doc.base_total_taxes_and_charges);
 
 		frm.events.hide_unhide_fields(frm);
 	},
@@ -1002,7 +1029,266 @@
 				}
 			});
 		}
-	}
+	},
+
+	sales_taxes_and_charges_template: function(frm) {
+		frm.trigger('fetch_taxes_from_template');
+	},
+
+	purchase_taxes_and_charges_template: function(frm) {
+		frm.trigger('fetch_taxes_from_template');
+	},
+
+	fetch_taxes_from_template: function(frm) {
+		let master_doctype = '';
+		let taxes_and_charges = '';
+
+		if (frm.doc.party_type == 'Supplier') {
+			master_doctype = 'Purchase Taxes and Charges Template';
+			taxes_and_charges = frm.doc.purchase_taxes_and_charges_template;
+		} else if (frm.doc.party_type == 'Customer') {
+			master_doctype = 'Sales Taxes and Charges Template';
+			taxes_and_charges = frm.doc.sales_taxes_and_charges_template;
+		}
+
+		if (!taxes_and_charges) {
+			return;
+		}
+
+		frappe.call({
+			method: "erpnext.controllers.accounts_controller.get_taxes_and_charges",
+			args: {
+				"master_doctype": master_doctype,
+				"master_name": taxes_and_charges
+			},
+			callback: function(r) {
+				if(!r.exc && r.message) {
+					// set taxes table
+					if(r.message) {
+						for (let tax of r.message) {
+							if (tax.charge_type === 'On Net Total') {
+								tax.charge_type = 'On Paid Amount';
+							}
+							me.frm.add_child("taxes", tax);
+						}
+						frm.events.apply_taxes(frm);
+						frm.events.set_unallocated_amount(frm);
+					}
+				}
+			}
+		});
+	},
+
+	apply_taxes: function(frm) {
+		frm.events.initialize_taxes(frm);
+		frm.events.determine_exclusive_rate(frm);
+		frm.events.calculate_taxes(frm);
+	},
+
+	initialize_taxes: function(frm) {
+		$.each(frm.doc["taxes"] || [], function(i, tax) {
+			frm.events.validate_taxes_and_charges(tax);
+			frm.events.validate_inclusive_tax(tax);
+			tax.item_wise_tax_detail = {};
+			let tax_fields = ["total",  "tax_fraction_for_current_item",
+				"grand_total_fraction_for_current_item"];
+
+			if (cstr(tax.charge_type) != "Actual") {
+				tax_fields.push("tax_amount");
+			}
+
+			$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
+
+			frm.doc.paid_amount_after_tax = frm.doc.paid_amount;
+		});
+	},
+
+	validate_taxes_and_charges: function(d) {
+		let msg = "";
+
+		if (d.account_head && !d.description) {
+			// set description from account head
+			d.description = d.account_head.split(' - ').slice(0, -1).join(' - ');
+		}
+
+		if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
+			msg = __("Please select Charge Type first");
+			d.row_id = "";
+			d.rate = d.tax_amount = 0.0;
+		} else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) {
+			msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'");
+			d.row_id = "";
+		} else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
+			if (d.idx == 1) {
+				msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row");
+				d.charge_type = '';
+			} else if (!d.row_id) {
+				msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]);
+				d.row_id = "";
+			} else if (d.row_id && d.row_id >= d.idx) {
+				msg = __("Cannot refer row number greater than or equal to current row number for this Charge type");
+				d.row_id = "";
+			}
+		}
+		if (msg) {
+			frappe.validated = false;
+			refresh_field("taxes");
+			frappe.throw(msg);
+		}
+
+	},
+
+	validate_inclusive_tax: function(tax) {
+		let actual_type_error = function() {
+			let msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
+			frappe.throw(msg);
+		};
+
+		let on_previous_row_error = function(row_range) {
+			let msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
+				[tax.idx, __(tax.doctype), tax.charge_type, row_range])
+			frappe.throw(msg);
+		};
+
+		if(cint(tax.included_in_paid_amount)) {
+			if(tax.charge_type == "Actual") {
+				// inclusive tax cannot be of type Actual
+				actual_type_error();
+			} else if(tax.charge_type == "On Previous Row Amount" &&
+				!cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_paid_amount)
+			) {
+				// referred row should also be an inclusive tax
+				on_previous_row_error(tax.row_id);
+			} else if(tax.charge_type == "On Previous Row Total") {
+				let taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id),
+					function(t) { return cint(t.included_in_paid_amount) ? null : t; });
+				if(taxes_not_included.length > 0) {
+					// all rows above this tax should be inclusive
+					on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id);
+				}
+			}
+		}
+	},
+
+	determine_exclusive_rate: function(frm) {
+		let has_inclusive_tax = false;
+		$.each(frm.doc["taxes"] || [], function(i, row) {
+			if(cint(row.included_in_paid_amount)) has_inclusive_tax = true;
+		});
+		if(has_inclusive_tax==false) return;
+
+		let cumulated_tax_fraction = 0.0;
+		$.each(frm.doc["taxes"] || [], function(i, tax) {
+			tax.tax_fraction_for_current_item = frm.events.get_current_tax_fraction(frm, tax);
+
+			if(i==0) {
+				tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
+			} else {
+				tax.grand_total_fraction_for_current_item =
+					me.frm.doc["taxes"][i-1].grand_total_fraction_for_current_item +
+					tax.tax_fraction_for_current_item;
+			}
+
+			cumulated_tax_fraction += tax.tax_fraction_for_current_item;
+			frm.doc.paid_amount_after_tax = flt(frm.doc.paid_amount/(1+cumulated_tax_fraction))
+		});
+	},
+
+	get_current_tax_fraction: function(frm, tax) {
+		let current_tax_fraction = 0.0;
+
+		if(cint(tax.included_in_paid_amount)) {
+			let tax_rate = tax.rate;
+
+			if(tax.charge_type == "On Paid Amount") {
+				current_tax_fraction = (tax_rate / 100.0);
+			} else if(tax.charge_type == "On Previous Row Amount") {
+				current_tax_fraction = (tax_rate / 100.0) *
+					frm.doc["taxes"][cint(tax.row_id) - 1].tax_fraction_for_current_item;
+			} else if(tax.charge_type == "On Previous Row Total") {
+				current_tax_fraction = (tax_rate / 100.0) *
+					frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item;
+			}
+		}
+
+		if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
+			current_tax_fraction *= -1;
+		}
+		return current_tax_fraction;
+	},
+
+
+	calculate_taxes: function(frm) {
+		frm.doc.total_taxes_and_charges = 0.0;
+		frm.doc.base_total_taxes_and_charges = 0.0;
+
+		let actual_tax_dict = {};
+
+		// maintain actual tax rate based on idx
+		$.each(frm.doc["taxes"] || [], function(i, tax) {
+			if (tax.charge_type == "Actual") {
+				actual_tax_dict[tax.idx] = flt(tax.tax_amount, precision("tax_amount", tax));
+			}
+		});
+
+		$.each(me.frm.doc["taxes"] || [], function(i, tax) {
+			let current_tax_amount = frm.events.get_current_tax_amount(frm, tax);
+
+			// Adjust divisional loss to the last item
+			if (tax.charge_type == "Actual") {
+				actual_tax_dict[tax.idx] -= current_tax_amount;
+				if (i == frm.doc["taxes"].length - 1) {
+					current_tax_amount += actual_tax_dict[tax.idx];
+				}
+			}
+
+			tax.tax_amount = current_tax_amount;
+			tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate;
+			current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
+
+			if(i==0) {
+				tax.total = flt(frm.doc.paid_amount_after_tax + current_tax_amount, precision("total", tax));
+			} else {
+				tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax));
+			}
+
+			tax.base_total = tax.total * frm.doc.source_exchange_rate;
+			frm.doc.total_taxes_and_charges += current_tax_amount;
+			frm.doc.base_total_taxes_and_charges += current_tax_amount * frm.doc.source_exchange_rate;
+
+			frm.refresh_field('taxes');
+			frm.refresh_field('total_taxes_and_charges');
+			frm.refresh_field('base_total_taxes_and_charges');
+		});
+	},
+
+	get_current_tax_amount: function(frm, tax) {
+		let tax_rate = tax.rate;
+		let current_tax_amount = 0.0;
+
+		// To set row_id by default as previous row.
+		if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) {
+			if (tax.idx === 1) {
+				frappe.throw(
+					__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"));
+			}
+		}
+
+		if(tax.charge_type == "Actual") {
+			current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax))
+		} else if(tax.charge_type == "On Paid Amount") {
+			current_tax_amount = flt((tax_rate / 100.0) * frm.doc.paid_amount_after_tax);
+		} else if(tax.charge_type == "On Previous Row Amount") {
+			current_tax_amount = flt((tax_rate / 100.0) *
+				frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount);
+
+		} else if(tax.charge_type == "On Previous Row Total") {
+			current_tax_amount = flt((tax_rate / 100.0) *
+				frm.doc["taxes"][cint(tax.row_id) - 1].total);
+		}
+
+		return current_tax_amount;
+	},
 });
 
 
@@ -1049,6 +1335,38 @@
 	}
 })
 
+frappe.ui.form.on('Advance Taxes and Charges', {
+	rate: function(frm) {
+		frm.events.apply_taxes(frm);
+		frm.events.set_unallocated_amount(frm);
+	},
+
+	tax_amount : function(frm) {
+		frm.events.apply_taxes(frm);
+		frm.events.set_unallocated_amount(frm);
+	},
+
+	row_id: function(frm) {
+		frm.events.apply_taxes(frm);
+		frm.events.set_unallocated_amount(frm);
+	},
+
+	taxes_remove: function(frm) {
+		frm.events.apply_taxes(frm);
+		frm.events.set_unallocated_amount(frm);
+	},
+
+	included_in_paid_amount: function(frm) {
+		frm.events.apply_taxes(frm);
+		frm.events.set_unallocated_amount(frm);
+	},
+
+	charge_type: function(frm) {
+		frm.events.apply_taxes(frm);
+		frm.events.set_unallocated_amount(frm);
+	}
+})
+
 frappe.ui.form.on('Payment Entry Deduction', {
 	amount: function(frm) {
 		frm.events.set_unallocated_amount(frm);
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 328584a..51f18a5 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -35,12 +35,16 @@
   "paid_to_account_balance",
   "payment_amounts_section",
   "paid_amount",
+  "paid_amount_after_tax",
   "source_exchange_rate",
   "base_paid_amount",
+  "base_paid_amount_after_tax",
   "column_break_21",
   "received_amount",
+  "received_amount_after_tax",
   "target_exchange_rate",
   "base_received_amount",
+  "base_received_amount_after_tax",
   "section_break_14",
   "get_outstanding_invoice",
   "references",
@@ -52,6 +56,17 @@
   "unallocated_amount",
   "difference_amount",
   "write_off_difference_amount",
+  "taxes_and_charges_section",
+  "purchase_taxes_and_charges_template",
+  "sales_taxes_and_charges_template",
+  "advance_tax_account",
+  "column_break_55",
+  "apply_tax_withholding_amount",
+  "tax_withholding_category",
+  "section_break_56",
+  "taxes",
+  "base_total_taxes_and_charges",
+  "total_taxes_and_charges",
   "deductions_or_loss_section",
   "deductions",
   "transaction_references",
@@ -320,6 +335,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "doc.received_amount",
    "fieldname": "base_received_amount",
    "fieldtype": "Currency",
    "label": "Received Amount (Company Currency)",
@@ -584,12 +600,114 @@
    "fieldname": "custom_remarks",
    "fieldtype": "Check",
    "label": "Custom Remarks"
+  },
+  {
+   "depends_on": "eval:doc.apply_tax_withholding_amount",
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "label": "Tax Withholding Category",
+   "mandatory_depends_on": "eval:doc.apply_tax_withholding_amount",
+   "options": "Tax Withholding Category"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.party_type == 'Supplier'",
+   "fieldname": "apply_tax_withholding_amount",
+   "fieldtype": "Check",
+   "label": "Apply Tax Withholding Amount"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "taxes_and_charges_section",
+   "fieldtype": "Section Break",
+   "label": "Taxes and Charges"
+  },
+  {
+   "depends_on": "eval:doc.party_type == 'Supplier'",
+   "fieldname": "purchase_taxes_and_charges_template",
+   "fieldtype": "Link",
+   "label": "Taxes and Charges Template",
+   "options": "Purchase Taxes and Charges Template"
+  },
+  {
+   "depends_on": "eval: doc.party_type == 'Customer'",
+   "fieldname": "sales_taxes_and_charges_template",
+   "fieldtype": "Link",
+   "label": "Taxes and Charges Template",
+   "options": "Sales Taxes and Charges Template"
+  },
+  {
+   "depends_on": "eval: doc.party_type == 'Supplier' || doc.party_type == 'Customer'",
+   "fieldname": "taxes",
+   "fieldtype": "Table",
+   "label": "Advance Taxes and Charges",
+   "options": "Advance Taxes and Charges"
+  },
+  {
+   "fieldname": "base_total_taxes_and_charges",
+   "fieldtype": "Currency",
+   "label": "Total Taxes and Charges (Company Currency)",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_taxes_and_charges",
+   "fieldtype": "Currency",
+   "label": "Total Taxes and Charges",
+   "read_only": 1
+  },
+  {
+   "fieldname": "paid_amount_after_tax",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Paid Amount After Tax",
+   "options": "paid_from_account_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_paid_amount_after_tax",
+   "fieldtype": "Currency",
+   "label": "Paid Amount After Tax (Company Currency)",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_55",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_56",
+   "fieldtype": "Section Break",
+   "hide_border": 1
+  },
+  {
+   "depends_on": "eval:doc.apply_tax_withholding_amount",
+   "description": "Provisional tax account for advance tax. Taxes are parked in this account until payments are allocated to invoices",
+   "fieldname": "advance_tax_account",
+   "fieldtype": "Link",
+   "label": "Advance Tax Account",
+   "mandatory_depends_on": "eval:doc.apply_tax_withholding_amount",
+   "options": "Account"
+  },
+  {
+   "depends_on": "eval:doc.received_amount && doc.payment_type != 'Internal Transfer'",
+   "fieldname": "received_amount_after_tax",
+   "fieldtype": "Currency",
+   "label": "Received Amount After Tax",
+   "options": "paid_to_account_currency"
+  },
+  {
+   "depends_on": "doc.received_amount",
+   "fieldname": "base_received_amount_after_tax",
+   "fieldtype": "Currency",
+   "label": "Received Amount After Tax (Company Currency)",
+   "options": "Company:company:default_currency"
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-08 13:05:16.958866",
+ "modified": "2021-06-22 20:37:06.154206",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
@@ -633,4 +751,4 @@
  "sort_order": "DESC",
  "title_field": "title",
  "track_changes": 1
-}
+}
\ 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 62ab76c..ff00fde 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -4,8 +4,8 @@
 
 from __future__ import unicode_literals
 import frappe, erpnext, json
-from frappe import _, scrub, ValidationError
-from frappe.utils import flt, comma_or, nowdate, getdate
+from frappe import _, scrub, ValidationError, throw
+from frappe.utils import flt, comma_or, nowdate, getdate, cint
 from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
 from erpnext.accounts.party import get_party_account
 from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
@@ -15,9 +15,11 @@
 from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details
 from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
 from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
-
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 from six import string_types, iteritems
 
+from erpnext.controllers.accounts_controller import validate_taxes_and_charges
+
 class InvalidPaymentEntry(ValidationError):
 	pass
 
@@ -52,6 +54,8 @@
 		self.set_exchange_rate()
 		self.validate_mandatory()
 		self.validate_reference_documents()
+		self.set_tax_withholding()
+		self.apply_taxes()
 		self.set_amounts()
 		self.clear_unallocated_reference_document_rows()
 		self.validate_payment_against_negative_invoice()
@@ -65,7 +69,6 @@
 		self.set_status()
 
 	def on_submit(self):
-		self.setup_party_account_field()
 		if self.difference_amount:
 			frappe.throw(_("Difference Amount must be zero"))
 		self.make_gl_entries()
@@ -78,7 +81,6 @@
 
 	def on_cancel(self):
 		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
-		self.setup_party_account_field()
 		self.make_gl_entries(cancel=1)
 		self.update_outstanding_amounts()
 		self.update_advance_paid()
@@ -122,6 +124,11 @@
 				if flt(d.allocated_amount) > flt(d.outstanding_amount):
 					frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
 
+			# Check for negative outstanding invoices as well
+			if flt(d.allocated_amount) < 0:
+				if flt(d.allocated_amount) < flt(d.outstanding_amount):
+					frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
+
 	def delink_advance_entry_references(self):
 		for reference in self.references:
 			if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
@@ -176,8 +183,15 @@
 					d.reference_name, self.party_account_currency)
 
 				for field, value in iteritems(ref_details):
+					if d.exchange_gain_loss:
+						# for cases where gain/loss is booked into invoice
+						# exchange_gain_loss is calculated from invoice & populated 
+						# and row.exchange_rate is already set to payment entry's exchange rate
+						# refer -> `update_reference_in_payment_entry()` in utils.py
+						continue
+
 					if field == 'exchange_rate' or not d.get(field) or force:
-						d.set(field, value)
+						d.db_set(field, value)
 
 	def validate_payment_type(self):
 		if self.payment_type not in ("Receive", "Pay", "Internal Transfer"):
@@ -303,11 +317,10 @@
 		for k, v in no_oustanding_refs.items():
 			frappe.msgprint(
 				_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.")
-					.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount"))
+					.format(k, frappe.bold(", ".join(d.reference_name for d in v)), frappe.bold("negative outstanding amount"))
 				+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
 				title=_("Warning"), indicator="orange")
 
-
 	def validate_journal_entry(self):
 		for d in self.get("references"):
 			if d.allocated_amount and d.reference_doctype == "Journal Entry":
@@ -386,12 +399,98 @@
 		else:
 			self.status = 'Draft'
 
+		self.db_set('status', self.status, update_modified = True)
+
+	def set_tax_withholding(self):
+		if not self.party_type == 'Supplier':
+			return
+
+		if not self.apply_tax_withholding_amount:
+			return
+
+		if not self.advance_tax_account:
+			frappe.throw(_("Advance TDS account is mandatory for advance TDS deduction"))
+
+		reference_doclist = []
+		net_total = self.paid_amount
+		included_in_paid_amount = 0
+
+		# Adding args as purchase invoice to get TDS amount
+		args = frappe._dict({
+			'company': self.company,
+			'doctype': 'Purchase Invoice',
+			'supplier': self.party,
+			'posting_date': self.posting_date,
+			'net_total': net_total
+		})
+
+		tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category)
+
+		if not tax_withholding_details:
+			return
+
+		tax_withholding_details.update({
+			'included_in_paid_amount': included_in_paid_amount,
+			'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company)
+		})
+
+		accounts = []
+		for d in self.taxes:
+			if d.account_head == tax_withholding_details.get("account_head"):
+
+				# Preserve user updated included in paid amount
+				if d.included_in_paid_amount:
+					tax_withholding_details.update({'included_in_paid_amount': d.included_in_paid_amount})
+
+				d.update(tax_withholding_details)
+			accounts.append(d.account_head)
+
+		if not accounts or tax_withholding_details.get("account_head") not in accounts:
+			self.append("taxes", tax_withholding_details)
+
+		to_remove = [d for d in self.taxes
+			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
+
+		for d in to_remove:
+			self.remove(d)
+
+	def apply_taxes(self):
+		self.initialize_taxes()
+		self.determine_exclusive_rate()
+		self.calculate_taxes()
+
 	def set_amounts(self):
+		self.set_received_amount()
 		self.set_amounts_in_company_currency()
+		self.set_amounts_after_tax()
 		self.set_total_allocated_amount()
 		self.set_unallocated_amount()
 		self.set_difference_amount()
 
+	def set_received_amount(self):
+		self.base_received_amount = self.base_paid_amount
+
+	def set_amounts_after_tax(self):
+		applicable_tax = 0
+		base_applicable_tax = 0
+		for tax in self.get('taxes'):
+			if not tax.included_in_paid_amount:
+				amount = -1 * tax.tax_amount if tax.add_deduct_tax == 'Deduct' else tax.tax_amount
+				base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == 'Deduct' else tax.base_tax_amount
+
+				applicable_tax += amount
+				base_applicable_tax += base_amount
+
+		self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(applicable_tax),
+			self.precision("paid_amount_after_tax"))
+		self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
+			self.precision("base_paid_amount_after_tax"))
+
+		self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax),
+			self.precision("paid_amount_after_tax"))
+		self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.target_exchange_rate),
+			self.precision("base_paid_amount_after_tax"))
+
 	def set_amounts_in_company_currency(self):
 		self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
 		if self.paid_amount:
@@ -419,17 +518,17 @@
 	def set_unallocated_amount(self):
 		self.unallocated_amount = 0
 		if self.party:
-			total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
+			total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
 			if self.payment_type == "Receive" \
-				and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
-				and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
-					self.unallocated_amount = (self.base_received_amount + total_deductions -
-						self.base_total_allocated_amount) / self.source_exchange_rate
+				and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \
+				and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate):
+				self.unallocated_amount = (self.received_amount_after_tax + total_deductions -
+					self.base_total_allocated_amount) / self.source_exchange_rate
 			elif self.payment_type == "Pay" \
-				and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
-				and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
-					self.unallocated_amount = (self.base_paid_amount - (total_deductions +
-						self.base_total_allocated_amount)) / self.target_exchange_rate
+				and self.base_total_allocated_amount < (self.base_paid_amount_after_tax - total_deductions) \
+				and self.total_allocated_amount < self.received_amount_after_tax + (total_deductions / self.target_exchange_rate):
+				self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions +
+					self.base_total_allocated_amount)) / self.target_exchange_rate
 
 	def set_difference_amount(self):
 		base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
@@ -438,13 +537,13 @@
 		base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
 
 		if self.payment_type == "Receive":
-			self.difference_amount = base_party_amount - self.base_received_amount
+			self.difference_amount = base_party_amount - self.base_received_amount_after_tax
 		elif self.payment_type == "Pay":
-			self.difference_amount = self.base_paid_amount - base_party_amount
+			self.difference_amount = self.base_paid_amount_after_tax - base_party_amount
 		else:
-			self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
+			self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax)
 
-		total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
+		total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
 
 		self.difference_amount = flt(self.difference_amount - total_deductions,
 			self.precision("difference_amount"))
@@ -460,8 +559,8 @@
 		if ((self.payment_type=="Pay" and self.party_type=="Customer")
 				or (self.payment_type=="Receive" and self.party_type=="Supplier")):
 
-			total_negative_outstanding = sum([abs(flt(d.outstanding_amount))
-				for d in self.get("references") if flt(d.outstanding_amount) < 0])
+			total_negative_outstanding = sum(abs(flt(d.outstanding_amount))
+				for d in self.get("references") if flt(d.outstanding_amount) < 0)
 
 			paid_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
 			additional_charges = sum([flt(d.amount) for d in self.deductions])
@@ -532,6 +631,7 @@
 		self.add_party_gl_entries(gl_entries)
 		self.add_bank_gl_entries(gl_entries)
 		self.add_deductions_gl_entries(gl_entries)
+		self.add_tax_gl_entries(gl_entries)
 
 		make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
 
@@ -571,8 +671,8 @@
 				gl_entries.append(gle)
 
 			if self.unallocated_amount:
-				base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \
-					(self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
+				exchange_rate = self.get_exchange_rate()
+				base_unallocated_amount = (self.unallocated_amount * exchange_rate)
 
 				gle = party_gl_dict.copy()
 
@@ -590,8 +690,8 @@
 					"account": self.paid_from,
 					"account_currency": self.paid_from_account_currency,
 					"against": self.party if self.payment_type=="Pay" else self.paid_to,
-					"credit_in_account_currency": self.paid_amount,
-					"credit": self.base_paid_amount,
+					"credit_in_account_currency": self.paid_amount_after_tax,
+					"credit": self.base_paid_amount_after_tax,
 					"cost_center": self.cost_center
 				}, item=self)
 			)
@@ -601,12 +701,50 @@
 					"account": self.paid_to,
 					"account_currency": self.paid_to_account_currency,
 					"against": self.party if self.payment_type=="Receive" else self.paid_from,
-					"debit_in_account_currency": self.received_amount,
-					"debit": self.base_received_amount,
+					"debit_in_account_currency": self.received_amount_after_tax,
+					"debit": self.base_received_amount_after_tax,
 					"cost_center": self.cost_center
 				}, item=self)
 			)
 
+	def add_tax_gl_entries(self, gl_entries):
+		for d in self.get('taxes'):
+			account_currency = get_account_currency(d.account_head)
+			if account_currency != self.company_currency:
+				frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency))
+
+			if self.payment_type in ('Pay', 'Internal Transfer'):
+				dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
+			elif self.payment_type == 'Receive':
+				dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
+
+			payment_or_advance_account = self.get_party_account_for_taxes()
+
+			gl_entries.append(
+				self.get_gl_dict({
+					"account": d.account_head,
+					"against": self.party if self.payment_type=="Receive" else self.paid_from,
+					dr_or_cr: d.base_tax_amount,
+					dr_or_cr + "_in_account_currency": d.base_tax_amount
+					if account_currency==self.company_currency
+					else d.tax_amount,
+					"cost_center": d.cost_center
+				}, account_currency, item=d))
+
+			#Intentionally use -1 to get net values in party account
+			gl_entries.append(
+				self.get_gl_dict({
+					"account": payment_or_advance_account,
+					"against": self.party if self.payment_type=="Receive" else self.paid_from,
+					dr_or_cr: -1 * d.base_tax_amount,
+					dr_or_cr + "_in_account_currency": -1*d.base_tax_amount
+					if account_currency==self.company_currency
+					else d.tax_amount,
+					"cost_center": self.cost_center,
+					"party_type": self.party_type,
+					"party": self.party
+				}, account_currency, item=d))
+
 	def add_deductions_gl_entries(self, gl_entries):
 		for d in self.get("deductions"):
 			if d.amount:
@@ -625,6 +763,14 @@
 					}, item=d)
 				)
 
+	def get_party_account_for_taxes(self):
+		if self.advance_tax_account:
+			return self.advance_tax_account
+		elif self.payment_type == 'Receive':
+			return self.paid_from
+		elif self.payment_type in ('Pay', 'Internal Transfer'):
+			return self.paid_to
+
 	def update_advance_paid(self):
 		if self.payment_type in ("Receive", "Pay") and self.party:
 			for d in self.get("references"):
@@ -667,10 +813,150 @@
 
 		if account_details:
 			row.update(account_details)
+		
+		if not row.get('amount'):
+			# if no difference amount
+			return
 
 		self.append('deductions', row)
 		self.set_unallocated_amount()
 
+	def get_exchange_rate(self):
+		return self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate
+
+	def initialize_taxes(self):
+		for tax in self.get("taxes"):
+			validate_taxes_and_charges(tax)
+			validate_inclusive_tax(tax, self)
+
+			tax_fields = ["total", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
+
+			if tax.charge_type != "Actual":
+				tax_fields.append("tax_amount")
+
+			for fieldname in tax_fields:
+				tax.set(fieldname, 0.0)
+
+		self.paid_amount_after_tax = self.paid_amount
+
+	def determine_exclusive_rate(self):
+		if not any((cint(tax.included_in_paid_amount) for tax in self.get("taxes"))):
+			return
+
+		cumulated_tax_fraction = 0
+		for i, tax in enumerate(self.get("taxes")):
+			tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax)
+			if i==0:
+				tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
+			else:
+				tax.grand_total_fraction_for_current_item = \
+					self.get("taxes")[i-1].grand_total_fraction_for_current_item \
+					+ tax.tax_fraction_for_current_item
+
+			cumulated_tax_fraction += tax.tax_fraction_for_current_item
+
+		self.paid_amount_after_tax = flt(self.paid_amount/(1+cumulated_tax_fraction))
+
+	def calculate_taxes(self):
+		self.total_taxes_and_charges = 0.0
+		self.base_total_taxes_and_charges = 0.0
+
+		actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
+			for tax in self.get("taxes") if tax.charge_type == "Actual"])
+
+		for i, tax in enumerate(self.get('taxes')):
+			current_tax_amount = self.get_current_tax_amount(tax)
+
+			if tax.charge_type == "Actual":
+				actual_tax_dict[tax.idx] -= current_tax_amount
+				if i == len(self.get("taxes")) - 1:
+					current_tax_amount += actual_tax_dict[tax.idx]
+
+			tax.tax_amount = current_tax_amount
+			tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate
+
+			if tax.add_deduct_tax == "Deduct":
+				current_tax_amount *= -1.0
+			else:
+				current_tax_amount *= 1.0
+
+			if i == 0:
+				tax.total = flt(self.paid_amount_after_tax + current_tax_amount, self.precision("total", tax))
+			else:
+				tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax))
+
+			tax.base_total = tax.total * self.source_exchange_rate
+
+			self.total_taxes_and_charges += current_tax_amount
+			self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate
+
+		if self.get('taxes'):
+			self.paid_amount_after_tax = self.get('taxes')[-1].base_total
+
+	def get_current_tax_amount(self, tax):
+		tax_rate = tax.rate
+
+		# To set row_id by default as previous row.
+		if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]:
+			if tax.idx == 1:
+				frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"))
+
+			if not tax.row_id:
+				tax.row_id = tax.idx - 1
+
+		if tax.charge_type == "Actual":
+			current_tax_amount = flt(tax.tax_amount, self.precision("tax_amount", tax))
+		elif tax.charge_type == "On Paid Amount":
+			current_tax_amount = (tax_rate / 100.0) * self.paid_amount_after_tax
+		elif tax.charge_type == "On Previous Row Amount":
+			current_tax_amount = (tax_rate / 100.0) * \
+				self.get('taxes')[cint(tax.row_id) - 1].tax_amount
+
+		elif tax.charge_type == "On Previous Row Total":
+			current_tax_amount = (tax_rate / 100.0) * \
+				self.get('taxes')[cint(tax.row_id) - 1].total
+
+		return current_tax_amount
+
+	def get_current_tax_fraction(self, tax):
+		current_tax_fraction = 0
+
+		if cint(tax.included_in_paid_amount):
+			tax_rate = tax.rate
+
+			if tax.charge_type == "On Paid Amount":
+				current_tax_fraction = tax_rate / 100.0
+			elif tax.charge_type == "On Previous Row Amount":
+				current_tax_fraction = (tax_rate / 100.0) * \
+					self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
+			elif tax.charge_type == "On Previous Row Total":
+				current_tax_fraction = (tax_rate / 100.0) * \
+					self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
+
+		if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
+			current_tax_fraction *= -1.0
+
+		return current_tax_fraction
+
+def validate_inclusive_tax(tax, doc):
+	def _on_previous_row_error(row_range):
+		throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
+
+	if cint(getattr(tax, "included_in_paid_amount", None)):
+		if tax.charge_type == "Actual":
+			# inclusive tax cannot be of type Actual
+			throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
+		elif tax.charge_type == "On Previous Row Amount" and \
+				not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount):
+			# referred row should also be inclusive
+			_on_previous_row_error(tax.row_id)
+		elif tax.charge_type == "On Previous Row Total" and \
+				not all([cint(t.included_in_paid_amount for t in doc.get("taxes")[:cint(tax.row_id) - 1])]):
+			# all rows about the referred tax should be inclusive
+			_on_previous_row_error("1 - %d" % (cint(tax.row_id),))
+		elif tax.get("category") == "Valuation":
+			frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
+
 @frappe.whitelist()
 def get_outstanding_reference_documents(args):
 
@@ -791,7 +1077,7 @@
 
 			outstanding_invoices.pop(idx - 1)
 			outstanding_invoices += invoice_ref_based_on_payment_terms[idx]
-	
+
 	return outstanding_invoices
 
 def get_orders_to_be_billed(posting_date, party_type, party,
@@ -989,6 +1275,7 @@
 		outstanding_amount = ref_doc.get("outstanding_amount")
 	elif reference_doctype == "Donation":
 		total_amount = ref_doc.get("amount")
+		outstanding_amount = total_amount
 		exchange_rate = 1
 	elif reference_doctype == "Dunning":
 		total_amount = ref_doc.get("dunning_amount")
@@ -1045,9 +1332,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
 	})
 
@@ -1235,6 +1522,13 @@
 			})
 			pe.set_difference_amount()
 
+	if doc.doctype == 'Purchase Order' and doc.apply_tds:
+		pe.apply_tax_withholding_amount = 1
+		pe.tax_withholding_category = doc.tax_withholding_category
+
+		if not pe.advance_tax_account:
+			pe.advance_tax_account = frappe.db.get_value('Company', pe.company, 'unrealized_profit_loss_account')
+
 	return pe
 
 def get_bank_cash_account(doc, bank_account):
@@ -1353,6 +1647,13 @@
 			paid_amount = received_amount * doc.get('conversion_rate', 1)
 			if dt == "Employee Advance":
 				paid_amount = received_amount * doc.get('exchange_rate', 1)
+
+	if dt == "Purchase Order" and doc.apply_tds:
+		if party_account_currency == bank.account_currency:
+			paid_amount = received_amount = doc.base_net_total
+		else:
+			paid_amount = received_amount = doc.base_net_total * doc.get('exchange_rate', 1)
+
 	return paid_amount, received_amount
 
 def apply_early_payment_discount(paid_amount, received_amount, doc):
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/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
index 7060d11..61a1462 100644
--- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
@@ -1,140 +1,70 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-06-15 15:56:30.815503", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2016-06-15 15:56:30.815503",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "field_order": [
+  "account",
+  "cost_center",
+  "amount",
+  "column_break_2",
+  "description"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "cost_center", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Cost Center", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Cost Center", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Cost Center",
+   "options": "Cost Center",
+   "print_hide": 1,
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Amount", 
-   "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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description",
+   "show_days": 1,
+   "show_seconds": 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": 1, 
- "max_attachments": 0, 
- "modified": "2019-01-07 16:52:07.040146", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Payment Entry Deduction", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-09-12 20:38:08.110674",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry Deduction",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
index 912ad09..43eb0b6 100644
--- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
@@ -14,7 +14,8 @@
   "total_amount",
   "outstanding_amount",
   "allocated_amount",
-  "exchange_rate"
+  "exchange_rate",
+  "exchange_gain_loss"
  ],
  "fields": [
   {
@@ -90,12 +91,19 @@
    "fieldtype": "Link",
    "label": "Payment Term",
    "options": "Payment Term"
+  },
+  {
+   "fieldname": "exchange_gain_loss",
+   "fieldtype": "Currency",
+   "label": "Exchange Gain/Loss",
+   "options": "Company:company:default_currency",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-10 11:25:47.144392",
+ "modified": "2021-04-21 13:30:11.605388",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry Reference",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 53ac996..438951d 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -101,7 +101,7 @@
 
 		controller.validate_transaction_currency(self.currency)
 		controller.request_for_payment(**payment_record)
-	
+
 	def get_request_amount(self):
 		data_of_completed_requests = frappe.get_all("Integration Request", filters={
 			'reference_doctype': self.doctype,
@@ -112,7 +112,7 @@
 		if not data_of_completed_requests:
 			return self.grand_total
 
-		request_amounts = sum([json.loads(d).get('request_amount') for d in data_of_completed_requests])
+		request_amounts = sum(json.loads(d).get('request_amount') for d in data_of_completed_requests)
 		return request_amounts
 
 	def on_cancel(self):
@@ -492,7 +492,6 @@
 					status = 'Requested'
 
 			pay_req_doc.db_set('status', status)
-			frappe.db.commit()
 
 def get_dummy_message(doc):
 	return frappe.render_template("""{% if doc.contact_person -%}
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
index 80e3348..39627eb 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
@@ -26,7 +26,7 @@
 	def check_duplicate_terms(self):
 		terms = []
 		for term in self.terms:
-			term_info = (term.credit_days, term.credit_months, term.due_date_based_on)
+			term_info = (term.payment_term, term.credit_days, term.credit_months, term.due_date_based_on)
 			if term_info in terms:
 				frappe.msgprint(
 					_('The Payment Term at row {0} is possibly a duplicate.').format(term.idx),
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
index 47546c0..84c941e 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
@@ -1,350 +1,138 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "ACC-PCV-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2013-01-10 16:34:07", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "actions": [],
+ "autoname": "ACC-PCV-.YYYY.-.#####",
+ "creation": "2013-01-10 16:34:07",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "transaction_date",
+  "posting_date",
+  "fiscal_year",
+  "amended_from",
+  "company",
+  "cost_center_wise_pnl",
+  "column_break1",
+  "closing_account_head",
+  "remarks"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "transaction_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": "Transaction Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "transaction_date", 
-   "oldfieldtype": "Date", 
-   "permlevel": 0, 
-   "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": "transaction_date",
+   "fieldtype": "Date",
+   "label": "Transaction Date",
+   "oldfieldname": "transaction_date",
+   "oldfieldtype": "Date"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "posting_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": "Posting Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "posting_date", 
-   "oldfieldtype": "Date", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "label": "Posting Date",
+   "oldfieldname": "posting_date",
+   "oldfieldtype": "Date",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "fiscal_year", 
-   "fieldtype": "Link", 
-   "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": "Closing Fiscal Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "fiscal_year", 
-   "oldfieldtype": "Select", 
-   "options": "Fiscal Year", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "fiscal_year",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Closing Fiscal Year",
+   "oldfieldname": "fiscal_year",
+   "oldfieldtype": "Select",
+   "options": "Fiscal Year",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amended_from", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amended From", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "amended_from", 
-   "oldfieldtype": "Data", 
-   "options": "Period Closing Voucher", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "label": "Amended From",
+   "no_copy": 1,
+   "oldfieldname": "amended_from",
+   "oldfieldtype": "Data",
+   "options": "Period Closing Voucher",
+   "read_only": 1
+  },
   {
-   "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, 
-   "oldfieldname": "company", 
-   "oldfieldtype": "Select", 
-   "options": "Company", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "oldfieldname": "company",
+   "oldfieldtype": "Select",
+   "options": "Company",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break1", 
-   "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, 
-   "oldfieldtype": "Column Break", 
-   "permlevel": 0, 
-   "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_break1",
+   "fieldtype": "Column Break",
+   "oldfieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "The account head under Liability or Equity, in which Profit/Loss will be booked", 
-   "fieldname": "closing_account_head", 
-   "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": "Closing Account Head", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "closing_account_head", 
-   "oldfieldtype": "Link", 
-   "options": "Account", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "description": "The account head under Liability or Equity, in which Profit/Loss will be booked",
+   "fieldname": "closing_account_head",
+   "fieldtype": "Link",
+   "label": "Closing Account Head",
+   "oldfieldname": "closing_account_head",
+   "oldfieldtype": "Link",
+   "options": "Account",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "remarks", 
-   "fieldtype": "Small Text", 
-   "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": "Remarks", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "remarks", 
-   "oldfieldtype": "Small Text", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "remarks",
+   "fieldtype": "Small Text",
+   "label": "Remarks",
+   "oldfieldname": "remarks",
+   "oldfieldtype": "Small Text",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "cost_center_wise_pnl",
+   "fieldtype": "Check",
+   "label": "Book Cost Center Wise Profit/Loss"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-file-text", 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 1, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2020-09-18 17:26:09.703215", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Period Closing Voucher", 
- "owner": "Administrator", 
+ ],
+ "icon": "fa fa-file-text",
+ "idx": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-05-20 15:27:37.210458",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Period Closing Voucher",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
    "write": 1
-  }, 
+  },
   {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1,
+   "submit": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "search_fields": "posting_date, fiscal_year", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "closing_account_head", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "search_fields": "posting_date, fiscal_year",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "closing_account_head"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index a74fa06..b0a5b04 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -51,63 +51,96 @@
 
 	def make_gl_entries(self):
 		gl_entries = []
-		net_pl_balance = 0
-		dimension_fields = ['t1.cost_center']
+		net_pl_balance = 0 
 
-		accounting_dimensions = get_accounting_dimensions()
-		for dimension in accounting_dimensions:
-			dimension_fields.append('t1.{0}'.format(dimension))
-
-		dimension_filters, default_dimensions = get_dimensions()
-
-		pl_accounts = self.get_pl_balances(dimension_fields)
+		pl_accounts = self.get_pl_balances()
 
 		for acc in pl_accounts:
-			if flt(acc.balance_in_company_currency):
+			if flt(acc.bal_in_company_currency):
 				gl_entries.append(self.get_gl_dict({
 					"account": acc.account,
 					"cost_center": acc.cost_center,
 					"account_currency": acc.account_currency,
-					"debit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
-						if flt(acc.balance_in_account_currency) < 0 else 0,
-					"debit": abs(flt(acc.balance_in_company_currency)) \
-						if flt(acc.balance_in_company_currency) < 0 else 0,
-					"credit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
-						if flt(acc.balance_in_account_currency) > 0 else 0,
-					"credit": abs(flt(acc.balance_in_company_currency)) \
-						if flt(acc.balance_in_company_currency) > 0 else 0
+					"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
+					"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
+					"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
+					"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
 				}, item=acc))
 
-				net_pl_balance += flt(acc.balance_in_company_currency)
+				net_pl_balance += flt(acc.bal_in_company_currency)
 
 		if net_pl_balance:
-			cost_center = frappe.db.get_value("Company", self.company, "cost_center")
-			gl_entry = self.get_gl_dict({
-				"account": self.closing_account_head,
-				"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
-				"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
-				"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
-				"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
-				"cost_center": cost_center
-			})
-
-			for dimension in accounting_dimensions:
-				gl_entry.update({
-					dimension: default_dimensions.get(self.company, {}).get(dimension)
-				})
-
-			gl_entries.append(gl_entry)
+			if self.cost_center_wise_pnl:
+				costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts)
+				gl_entries += costcenter_wise_gl_entries
+			else:
+				gl_entry = self.get_pnl_gl_entry(net_pl_balance)
+				gl_entries.append(gl_entry)
 
 		from erpnext.accounts.general_ledger import make_gl_entries
 		make_gl_entries(gl_entries)
+	
+	def get_pnl_gl_entry(self, net_pl_balance):
+		cost_center = frappe.db.get_value("Company", self.company, "cost_center")
+		gl_entry = self.get_gl_dict({
+			"account": self.closing_account_head,
+			"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
+			"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
+			"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
+			"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
+			"cost_center": cost_center
+		})
 
-	def get_pl_balances(self, dimension_fields):
-		"""Get balance for pl accounts"""
+		self.update_default_dimensions(gl_entry)
+
+		return gl_entry
+
+	def get_costcenter_wise_pnl_gl_entries(self, pl_accounts):
+		company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
+		gl_entries = []
+
+		for acc in pl_accounts:
+			if flt(acc.bal_in_company_currency):
+				gl_entry = self.get_gl_dict({
+					"account": self.closing_account_head,
+					"cost_center": acc.cost_center or company_cost_center,
+					"account_currency": acc.account_currency,
+					"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
+					"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
+					"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
+					"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0
+				}, item=acc)
+
+				self.update_default_dimensions(gl_entry)
+
+				gl_entries.append(gl_entry)
+
+		return gl_entries
+
+	def update_default_dimensions(self, gl_entry):
+		if not self.accounting_dimensions:
+			self.accounting_dimensions = get_accounting_dimensions()
+
+		_, default_dimensions = get_dimensions()
+		for dimension in self.accounting_dimensions:
+			gl_entry.update({
+				dimension: default_dimensions.get(self.company, {}).get(dimension)
+			})
+
+	def get_pl_balances(self):
+		"""Get balance for dimension-wise pl accounts"""
+
+		dimension_fields = ['t1.cost_center']
+
+		self.accounting_dimensions = get_accounting_dimensions()
+		for dimension in self.accounting_dimensions:
+			dimension_fields.append('t1.{0}'.format(dimension))
+
 		return frappe.db.sql("""
 			select
 				t1.account, t2.account_currency, {dimension_fields},
-				sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
-				sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
+				sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
+				sum(t1.debit) - sum(t1.credit) as bal_in_company_currency
 			from `tabGL Entry` t1, `tabAccount` t2
 			where t1.account = t2.name and t2.report_type = 'Profit and Loss'
 			and t2.docstatus < 2 and t2.company = %s
diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index eb02d97..2f29372 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -8,6 +8,7 @@
 from frappe.utils import flt, today
 from erpnext.accounts.utils import get_fiscal_year, now
 from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 
 class TestPeriodClosingVoucher(unittest.TestCase):
 	def test_closing_entry(self):
@@ -65,6 +66,58 @@
 			self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
 				-1*random_expense_account[0].balance_in_account_currency)
 
+	def test_cost_center_wise_posting(self):
+		frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
+
+		company = create_company()
+		surplus_account = create_account()
+
+		cost_center1 = create_cost_center("Test Cost Center 1")
+		cost_center2 = create_cost_center("Test Cost Center 2")
+
+		create_sales_invoice(
+			company=company,
+			cost_center=cost_center1,
+			income_account="Sales - TPC",
+			expense_account="Cost of Goods Sold - TPC",
+			rate=400,
+			debit_to="Debtors - TPC"
+		)
+		create_sales_invoice(
+			company=company,
+			cost_center=cost_center2,
+			income_account="Sales - TPC",
+			expense_account="Cost of Goods Sold - TPC",
+			rate=200,
+			debit_to="Debtors - TPC"
+		)
+
+		pcv = frappe.get_doc({
+			"transaction_date": today(),
+			"posting_date": today(),
+			"fiscal_year": get_fiscal_year(today())[0],
+			"company": "Test PCV Company",
+			"cost_center_wise_pnl": 1,
+			"closing_account_head": surplus_account,
+			"remarks": "Test",
+			"doctype": "Period Closing Voucher"
+		})
+		pcv.insert()
+		pcv.submit()
+
+		expected_gle = (
+			('Sales - TPC', 200.0, 0.0, cost_center2),
+			(surplus_account, 0.0, 200.0, cost_center2),
+			('Sales - TPC', 400.0, 0.0, cost_center1),
+			(surplus_account, 0.0, 400.0, cost_center1)
+		)
+
+		pcv_gle = frappe.db.sql("""
+			select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s
+		""", (pcv.name))
+
+		self.assertTrue(pcv_gle, expected_gle)
+
 	def make_period_closing_voucher(self):
 		pcv = frappe.get_doc({
 			"doctype": "Period Closing Voucher",
@@ -80,6 +133,38 @@
 
 		return pcv
 
+def create_company():
+	company = frappe.get_doc({
+		'doctype': 'Company',
+		'company_name': "Test PCV Company",
+		'country': 'United States',
+		'default_currency': 'USD'
+	})		
+	company.insert(ignore_if_duplicate = True)
+	return company.name
+
+def create_account():
+	account = frappe.get_doc({
+		"account_name": "Reserve and Surplus",
+		"is_group": 0,
+		"company": "Test PCV Company",
+		"root_type": "Liability",
+		"report_type": "Balance Sheet",
+		"account_currency": "USD",
+		"parent_account": "Current Liabilities - TPC",
+		"doctype": "Account"
+	}).insert(ignore_if_duplicate = True)
+	return account.name
+
+def create_cost_center(cc_name):
+	costcenter = frappe.get_doc({
+		"company": "Test PCV Company",
+		"cost_center_name": cc_name,
+		"doctype": "Cost Center",
+		"parent_cost_center": "Test PCV Company - TPC"
+	})
+	costcenter.insert(ignore_if_duplicate = True)
+	return costcenter.name
 
 test_dependencies = ["Customer", "Cost Center"]
 test_records = frappe.get_test_records("Period Closing Voucher")
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index d23b952..b54d0e7 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -20,9 +20,9 @@
 class MultiplePricingRuleConflict(frappe.ValidationError): pass
 
 apply_on_table = {
-    'Item Code': 'items',
-    'Item Group': 'item_groups',
-    'Brand': 'brands'
+	'Item Code': 'items',
+	'Item Group': 'item_groups',
+	'Brand': 'brands'
 }
 
 def get_pricing_rules(args, doc=None):
@@ -183,7 +183,7 @@
 			condition = "ifnull({table}.{field}, '') in ({parent_groups})".format(
 				table=table,
 				field=field,
-				parent_groups=", ".join([frappe.db.escape(d) for d in parent_groups])
+				parent_groups=", ".join(frappe.db.escape(d) for d in parent_groups)
 			)
 
 			frappe.flags.tree_conditions[key] = condition
@@ -264,7 +264,7 @@
 
 	# find pricing rule with highest priority
 	if pricing_rules:
-		max_priority = max([cint(p.priority) for p in pricing_rules])
+		max_priority = max(cint(p.priority) for p in pricing_rules)
 		if max_priority:
 			pricing_rules = list(filter(lambda x: cint(x.priority)==max_priority, pricing_rules))
 
@@ -272,14 +272,14 @@
 		pricing_rules = list(pricing_rules)
 
 	if len(pricing_rules) > 1:
-		rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules]))
+		rate_or_discount = list(set(d.rate_or_discount for d in pricing_rules))
 		if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage":
 			pricing_rules = list(filter(lambda x: x.for_price_list==args.price_list, pricing_rules)) \
 				or pricing_rules
 
 	if len(pricing_rules) > 1 and not args.for_shopping_cart:
 		frappe.throw(_("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}")
-			.format("\n".join([d.name for d in pricing_rules])), MultiplePricingRuleConflict)
+			.format("\n".join(d.name for d in pricing_rules)), MultiplePricingRuleConflict)
 	elif pricing_rules:
 		return pricing_rules[0]
 
@@ -541,7 +541,7 @@
 
 def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
 	if pricing_rule_args:
-		items = tuple([(d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item])
+		items = tuple((d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item)
 
 		for args in pricing_rule_args:
 			if not items or (args.get('item_code'), args.get('pricing_rules')) not in items:
@@ -589,4 +589,4 @@
 		elif transaction_type=='cancelled':
 			if coupon.used>0:
 				coupon.used=coupon.used-1
-				coupon.save(ignore_permissions=True)
\ No newline at end of file
+				coupon.save(ignore_permissions=True)
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/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index f58c8f4..dc9094c 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -27,10 +27,6 @@
 		});
 	},
 
-	company: function() {
-		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
-	},
-
 	onload: function() {
 		this._super();
 
@@ -569,5 +565,9 @@
 			frm: frm,
 			freeze_message: __("Creating Purchase Receipt ...")
 		})
-	}
+	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
 })
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index d3d3ffa..00ef7d5 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -592,11 +592,12 @@
    "label": "Raw Materials Supplied"
   },
   {
+   "depends_on": "update_stock",
    "fieldname": "supplied_items",
    "fieldtype": "Table",
    "label": "Supplied Items",
-   "options": "Purchase Receipt Item Supplied",
-   "read_only": 1
+   "no_copy": 1,
+   "options": "Purchase Receipt Item Supplied"
   },
   {
    "fieldname": "section_break_26",
@@ -837,6 +838,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "base_rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment (Company Currency)",
@@ -883,6 +885,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment",
@@ -1380,7 +1383,7 @@
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-30 22:45:58.334107",
+ "modified": "2021-06-15 18:20:56.806195",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 83e9f75..f799279 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -68,9 +68,6 @@
 
 		super(PurchaseInvoice, self).validate()
 
-		# apply tax withholding only if checked and applicable
-		self.set_tax_withholding()
-
 		if not self.is_return:
 			self.po_required()
 			self.pr_required()
@@ -251,11 +248,9 @@
 
 				if self.update_stock and (not item.from_warehouse):
 					if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
-						msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]))
-						msg += _("because account {} is not linked to warehouse {} ").format(frappe.bold(item.expense_account), frappe.bold(item.warehouse))
-						msg += _("or it is not the default inventory account")
+						msg = _("Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account").format(
+							item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), frappe.bold(item.expense_account), frappe.bold(item.warehouse))
 						frappe.msgprint(msg, title=_("Expense Head Changed"))
-
 					item.expense_account = warehouse_account[item.warehouse]["account"]
 				else:
 					# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
@@ -266,8 +261,8 @@
 
 						if negative_expense_booked_in_pr:
 							if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
-								msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account))
-								msg += _("because expense is booked against this account in Purchase Receipt {}").format(frappe.bold(item.purchase_receipt))
+								msg = _("Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}").format(
+									item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))
 								frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 							item.expense_account = stock_not_billed_account
@@ -275,8 +270,9 @@
 						# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
 						# This is done in cases when Purchase Invoice is created before Purchase Receipt
 						if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
-							msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account))
-							msg += _("as no Purchase Receipt is created against Item {}. ").format(frappe.bold(item.item_code))
+							msg = _("Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.").format(
+								item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))
+							msg += "<br>"
 							msg += _("This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice")
 							frappe.msgprint(msg, title=_("Expense Head Changed"))
 
@@ -308,8 +304,8 @@
 				if not d.purchase_order:
 					msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
 					msg += "<br><br>"
-					msg += _("To submit the invoice without purchase order please set {} ").format(frappe.bold(_('Purchase Order Required')))
-					msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
+					msg += _("To submit the invoice without purchase order please set {0} as {1} in {2}").format(
+						frappe.bold(_('Purchase Order Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
 					throw(msg, title=_("Mandatory Purchase Order"))
 
 	def pr_required(self):
@@ -323,8 +319,8 @@
 				if not d.purchase_receipt and d.item_code in stock_items:
 					msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code))
 					msg += "<br><br>"
-					msg += _("To submit the invoice without purchase receipt please set {} ").format(frappe.bold(_('Purchase Receipt Required')))
-					msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
+					msg += _("To submit the invoice without purchase receipt please set {0} as {1} in {2}").format(
+						frappe.bold(_('Purchase Receipt Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
 					throw(msg, title=_("Mandatory Purchase Receipt"))
 
 	def validate_write_off_account(self):
@@ -404,6 +400,7 @@
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		if self.update_stock == 1:
 			self.update_stock_ledger()
+			self.set_consumed_qty_in_po()
 			from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
 			update_serial_nos_after_submit(self, "items")
 
@@ -454,8 +451,11 @@
 			self.get_asset_gl_entry(gl_entries)
 
 		self.make_tax_gl_entries(gl_entries)
+		self.make_exchange_gain_loss_gl_entries(gl_entries)
 		self.make_internal_transfer_gl_entries(gl_entries)
 
+		self.allocate_advance_taxes(gl_entries)
+
 		gl_entries = make_regional_gl_entries(gl_entries, self)
 
 		gl_entries = merge_similar_entries(gl_entries)
@@ -1000,6 +1000,7 @@
 		if self.update_stock == 1:
 			self.update_stock_ledger()
 			self.delete_auto_created_batches()
+			self.set_consumed_qty_in_po()
 
 		self.make_gl_entries_on_cancel()
 
@@ -1090,6 +1091,7 @@
 		for d in self.taxes:
 			if d.account_head == tax_withholding_details.get("account_head"):
 				d.update(tax_withholding_details)
+
 			accounts.append(d.account_head)
 
 		if not accounts or tax_withholding_details.get("account_head") not in accounts:
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 53db689..c9384be 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -16,6 +16,7 @@
 from erpnext.projects.doctype.project.test_project import make_project
 from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
 from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.buying.doctype.supplier.test_supplier import create_supplier
 
 test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
 test_ignore = ["Serial No"]
@@ -620,8 +621,10 @@
 		self.assertEqual(actual_qty_0, get_qty_after_transaction())
 
 	def test_subcontracting_via_purchase_invoice(self):
+		from erpnext.buying.doctype.purchase_order.test_purchase_order import update_backflush_based_on
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
+		update_backflush_based_on('BOM')
 		make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
 		make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
 			qty=100, basic_rate=100)
@@ -631,7 +634,7 @@
 
 		self.assertEqual(len(pi.get("supplied_items")), 2)
 
-		rm_supp_cost = sum([d.amount for d in pi.get("supplied_items")])
+		rm_supp_cost = sum(d.amount for d in pi.get("supplied_items"))
 		self.assertEqual(flt(pi.get("items")[0].rm_supp_cost, 2), flt(rm_supp_cost, 2))
 
 	def test_rejected_serial_no(self):
@@ -950,6 +953,206 @@
 		acc_settings.submit_journal_entriessubmit_journal_entries = 0
 		acc_settings.save()
 
+	def test_gain_loss_with_advance_entry(self):
+		unlink_enabled = frappe.db.get_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice")
+		frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1)
+		pay = frappe.get_doc({
+			'doctype': 'Payment Entry',
+			'company': '_Test Company',
+			'payment_type': 'Pay',
+			'party_type': 'Supplier',
+			'party': '_Test Supplier USD',
+			'paid_to': '_Test Payable USD - _TC',
+			'paid_from': 'Cash - _TC',
+			'paid_amount': 70000,
+			'target_exchange_rate': 70,
+			'received_amount': 1000,
+		})
+		pay.insert()
+		pay.submit()
+
+		pi = make_purchase_invoice(supplier='_Test Supplier USD', currency="USD",
+			conversion_rate=75, rate=500, do_not_save=1, qty=1)
+		pi.cost_center = "_Test Cost Center - _TC"
+		pi.advances = []
+		pi.append("advances", {
+			"reference_type": "Payment Entry",
+			"reference_name": pay.name,
+			"advance_amount": 1000,
+			"remarks": pay.remarks,
+			"allocated_amount": 500,
+			"ref_exchange_rate": 70
+		})
+		pi.save()
+		pi.submit()
+
+		expected_gle = [
+			["_Test Account Cost for Goods Sold - _TC", 37500.0],
+			["_Test Payable USD - _TC", -40000.0],
+			["Exchange Gain/Loss - _TC", 2500.0]
+		]
+
+		gl_entries = frappe.db.sql("""
+			select account, sum(debit - credit) as balance from `tabGL Entry`
+			where voucher_no=%s
+			group by account order by account asc""", (pi.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.balance)
+
+		pi_2 = make_purchase_invoice(supplier='_Test Supplier USD', currency="USD",
+			conversion_rate=73, rate=500, do_not_save=1, qty=1)
+		pi_2.cost_center = "_Test Cost Center - _TC"
+		pi_2.advances = []
+		pi_2.append("advances", {
+			"reference_type": "Payment Entry",
+			"reference_name": pay.name,
+			"advance_amount": 500,
+			"remarks": pay.remarks,
+			"allocated_amount": 500,
+			"ref_exchange_rate": 70
+		})
+		pi_2.save()
+		pi_2.submit()
+
+		expected_gle = [
+			["_Test Account Cost for Goods Sold - _TC", 36500.0],
+			["_Test Payable USD - _TC", -38000.0],
+			["Exchange Gain/Loss - _TC", 1500.0]
+		]
+
+		gl_entries = frappe.db.sql("""
+			select account, sum(debit - credit) as balance from `tabGL Entry`
+			where voucher_no=%s
+			group by account order by account asc""", (pi_2.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.balance)
+
+		expected_gle = [
+			["_Test Payable USD - _TC", 70000.0],
+			["Cash - _TC", -70000.0]
+		]
+
+		gl_entries = frappe.db.sql("""
+			select account, sum(debit - credit) as balance from `tabGL Entry`
+			where voucher_no=%s and is_cancelled=0
+			group by account order by account asc""", (pay.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.balance)
+
+		pi.reload()
+		pi.cancel()
+
+		pi_2.reload()
+		pi_2.cancel()
+
+		pay.reload()
+		pay.cancel()
+
+		frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled)
+
+	def test_purchase_invoice_advance_taxes(self):
+		from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+		from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
+
+		# create a new supplier to test
+		supplier = create_supplier(supplier_name = '_Test TDS Advance Supplier',
+			tax_withholding_category = 'TDS - 194 - Dividends - Individual')
+
+		# Update tax withholding category with current fiscal year and rate details
+		update_tax_witholding_category('_Test Company', 'TDS Payable - _TC', nowdate())
+
+		# Create Purchase Order with TDS applied
+		po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000, item='_Test Non Stock Item')
+		po.apply_tds = 1
+		po.tax_withholding_category = 'TDS - 194 - Dividends - Individual'
+		po.save()
+		po.submit()
+
+		# Update Unrealized Profit / Loss Account which is used as default advance tax account
+		frappe.db.set_value('Company', '_Test Company', 'unrealized_profit_loss_account', '_Test Account Excise Duty - _TC')
+
+		# Create Payment Entry Against the order
+		payment_entry = get_payment_entry(dt='Purchase Order', dn=po.name)
+		payment_entry.paid_from = 'Cash - _TC'
+		payment_entry.save()
+		payment_entry.submit()
+
+		# Check GLE for Payment Entry
+		expected_gle = [
+			['_Test Account Excise Duty - _TC', 3000, 0],
+			['Cash - _TC', 0, 27000],
+			['Creditors - _TC', 27000, 0],
+			['TDS Payable - _TC', 0, 3000],
+		]
+
+		gl_entries = frappe.db.sql("""select account, debit, credit
+			from `tabGL Entry`
+			where voucher_type='Payment Entry' and voucher_no=%s
+			order by account asc""", (payment_entry.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)
+
+		# Create Purchase Invoice against Purchase Order
+		purchase_invoice = get_mapped_purchase_invoice(po.name)
+		purchase_invoice.allocate_advances_automatically = 1
+		purchase_invoice.items[0].item_code = '_Test Non Stock Item'
+		purchase_invoice.items[0].expense_account = '_Test Account Cost for Goods Sold - _TC'
+		purchase_invoice.save()
+		purchase_invoice.submit()
+
+		# Check GLE for Purchase Invoice
+		# Zero net effect on final TDS Payable on invoice
+		expected_gle = [
+			['_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, 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.amount)
+
+def update_tax_witholding_category(company, account, date):
+	from erpnext.accounts.utils import get_fiscal_year
+
+	fiscal_year = get_fiscal_year(date=date, company=company)
+
+	if not frappe.db.get_value('Tax Withholding Rate',
+		{'parent': 'TDS - 194 - Dividends - Individual', 'fiscal_year': fiscal_year[0]}):
+		tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual')
+		tds_category.append('rates', {
+			'fiscal_year': fiscal_year[0],
+			'tax_withholding_rate': 10,
+			'single_threshold': 2500,
+			'cumulative_threshold': 0
+		})
+		tds_category.save()
+
+	if not frappe.db.get_value('Tax Withholding Account',
+		{'parent': 'TDS - 194 - Dividends - Individual', 'account': account}):
+		tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual')
+		tds_category.append('accounts', {
+			'company': company,
+			'account': account
+		})
+		tds_category.save()
 
 def unlink_payment_on_cancel_of_invoice(enable=1):
 	accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
index 5801b17..63dfff8 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
@@ -1,235 +1,127 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-03-08 15:36:46", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2013-03-08 15:36:46",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "reference_type",
+  "reference_name",
+  "remarks",
+  "reference_row",
+  "col_break1",
+  "advance_amount",
+  "allocated_amount",
+  "exchange_gain_loss",
+  "ref_exchange_rate"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reference_type", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "label": "Reference Type", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "journal_voucher", 
-   "oldfieldtype": "Link", 
-   "options": "DocType", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "180px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "reference_type",
+   "fieldtype": "Link",
+   "label": "Reference Type",
+   "no_copy": 1,
+   "oldfieldname": "journal_voucher",
+   "oldfieldtype": "Link",
+   "options": "DocType",
+   "print_width": "180px",
+   "read_only": 1,
    "width": "180px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "reference_name", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Reference Name", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "reference_type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "columns": 3,
+   "fieldname": "reference_name",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Reference Name",
+   "no_copy": 1,
+   "options": "reference_type",
+   "read_only": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "remarks", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Remarks", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "remarks", 
-   "oldfieldtype": "Small Text", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "150px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "columns": 3,
+   "fieldname": "remarks",
+   "fieldtype": "Text",
+   "in_list_view": 1,
+   "label": "Remarks",
+   "no_copy": 1,
+   "oldfieldname": "remarks",
+   "oldfieldtype": "Small Text",
+   "print_width": "150px",
+   "read_only": 1,
    "width": "150px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reference_row", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "label": "Reference Row", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "jv_detail_no", 
-   "oldfieldtype": "Date", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "80px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "reference_row",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Reference Row",
+   "no_copy": 1,
+   "oldfieldname": "jv_detail_no",
+   "oldfieldtype": "Date",
+   "print_hide": 1,
+   "print_width": "80px",
+   "read_only": 1,
    "width": "80px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "col_break1", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "col_break1",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "advance_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Advance Amount", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "advance_amount", 
-   "oldfieldtype": "Currency", 
-   "options": "party_account_currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "100px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "columns": 2,
+   "fieldname": "advance_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Advance Amount",
+   "no_copy": 1,
+   "oldfieldname": "advance_amount",
+   "oldfieldtype": "Currency",
+   "options": "party_account_currency",
+   "print_width": "100px",
+   "read_only": 1,
    "width": "100px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "allocated_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Allocated Amount", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "allocated_amount", 
-   "oldfieldtype": "Currency", 
-   "options": "party_account_currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "100px", 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "columns": 2,
+   "fieldname": "allocated_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Allocated Amount",
+   "no_copy": 1,
+   "oldfieldname": "allocated_amount",
+   "oldfieldtype": "Currency",
+   "options": "party_account_currency",
+   "print_width": "100px",
    "width": "100px"
+  },
+  {
+   "fieldname": "exchange_gain_loss",
+   "fieldtype": "Currency",
+   "label": "Exchange Gain/Loss",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "ref_exchange_rate",
+   "fieldtype": "Float",
+   "label": "Reference Exchange Rate",
+   "non_negative": 1,
+   "read_only": 1
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2016-08-26 02:30:54.407138", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Purchase Invoice Advance", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_order": "DESC", 
- "track_seen": 0
+ ],
+ "idx": 1,
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-20 16:26:53.820530",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Advance",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 10e1c73..8a55ff8 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -272,7 +272,7 @@
    "fieldname": "rate",
    "fieldtype": "Currency",
    "in_list_view": 1,
-   "label": "Rate ",
+   "label": "Rate",
    "oldfieldname": "import_rate",
    "oldfieldtype": "Currency",
    "options": "currency",
@@ -854,7 +854,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-03-30 09:02:39.256602",
+ "modified": "2021-06-16 19:57:03.101571",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
index f9fdc4b..1fa68e0 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
@@ -12,6 +12,7 @@
   "charge_type",
   "row_id",
   "included_in_print_rate",
+  "included_in_paid_amount",
   "col_break1",
   "account_head",
   "description",
@@ -21,6 +22,7 @@
   "cost_center",
   "dimension_col_break",
   "section_break_9",
+  "currency",
   "tax_amount",
   "tax_amount_after_discount_amount",
   "total",
@@ -205,12 +207,28 @@
   {
    "fieldname": "dimension_col_break",
    "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "account_head.account_currency",
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Account Currency",
+   "options": "Currency",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
+   "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
+   "fieldname": "included_in_paid_amount",
+   "fieldtype": "Check",
+   "label": "Considered In Paid Amount"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-06-14 01:43:50.750455",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Taxes and Charges",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 1808005..f813425 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -17,7 +17,7 @@
 		var me = this;
 		this._super();
 
-		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet'];
+		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', 'POS Closing Entry'];
 		if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
 			// show debit_to in print format
 			this.frm.set_df_property("debit_to", "print_hide", 0);
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 023f4b0..6d1f624 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -531,7 +531,7 @@
 			# set pos values in items
 			for item in self.get("items"):
 				if item.get('item_code'):
-					profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos)
+					profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos, update_data=True)
 					for fname, val in iteritems(profile_details):
 						if (not for_validate) or (for_validate and not item.get(fname)):
 							item.set(fname, val)
@@ -840,8 +840,11 @@
 		self.make_customer_gl_entry(gl_entries)
 
 		self.make_tax_gl_entries(gl_entries)
+		self.make_exchange_gain_loss_gl_entries(gl_entries)
 		self.make_internal_transfer_gl_entries(gl_entries)
 
+		self.allocate_advance_taxes(gl_entries)
+
 		self.make_item_gl_entries(gl_entries)
 
 		# merge gl entries before adding pos entries
@@ -849,7 +852,6 @@
 
 		self.make_loyalty_point_redemption_gle(gl_entries)
 		self.make_pos_gl_entries(gl_entries)
-		self.make_gle_for_change_amount(gl_entries)
 
 		self.make_write_off_gl_entry(gl_entries)
 		self.make_gle_for_rounding_adjustment(gl_entries)
@@ -983,7 +985,13 @@
 
 	def make_pos_gl_entries(self, gl_entries):
 		if cint(self.is_pos):
+
+			skip_change_gl_entries = not cint(frappe.db.get_single_value('Accounts Settings', 'post_change_gl_entries'))
+
 			for payment_mode in self.payments:
+				if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount:
+					payment_mode.base_amount -= flt(self.change_amount)
+
 				if payment_mode.amount:
 					# POS, make payment entries
 					gl_entries.append(
@@ -1015,8 +1023,11 @@
 						}, payment_mode_account_currency, item=self)
 					)
 
+			if not skip_change_gl_entries:
+				self.make_gle_for_change_amount(gl_entries)
+
 	def make_gle_for_change_amount(self, gl_entries):
-		if cint(self.is_pos) and self.change_amount:
+		if self.change_amount:
 			if self.account_for_change_amount:
 				gl_entries.append(
 					self.get_gl_dict({
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index df6d483..dbc7f86 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -713,7 +713,7 @@
 		si.submit()
 		self.assertEqual(si.paid_amount, 100.0)
 
-		self.pos_gl_entry(si, pos, 50)
+		self.validate_pos_gl_entry(si, pos, 50)
 
 	def test_pos_returns_with_repayment(self):
 		from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
@@ -749,7 +749,7 @@
 		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
 			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
 
-		pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
+		make_purchase_receipt(company= "_Test Company with perpetual inventory",
 			item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
 
 		pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
@@ -770,7 +770,45 @@
 		self.assertEqual(pos.grand_total, 100.0)
 		self.assertEqual(pos.write_off_amount, -5)
 
-	def pos_gl_entry(self, si, pos, cash_amount):
+	def test_pos_with_no_gl_entry_for_change_amount(self):
+		frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 0)
+
+		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
+			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
+
+		make_purchase_receipt(company= "_Test Company with perpetual inventory",
+			item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
+
+		pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
+			debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
+			income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
+			cost_center = "Main - TCP1", do_not_save=True)
+
+		pos.is_pos = 1
+		pos.update_stock = 1
+
+		taxes = get_taxes_and_charges()
+		pos.taxes = []
+		for tax in taxes:
+			pos.append("taxes", tax)
+
+		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60})
+
+		pos.insert()
+		pos.submit()
+
+		self.assertEqual(pos.grand_total, 100.0)
+		self.assertEqual(pos.change_amount, 10)
+
+		self.validate_pos_gl_entry(pos, pos, 60, validate_without_change_gle=True)
+
+		frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 1)
+
+	def validate_pos_gl_entry(self, si, pos, cash_amount, validate_without_change_gle=False):
+		if validate_without_change_gle:
+			cash_amount -= pos.change_amount
+
 		# check stock ledger entries
 		sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
 			where voucher_type = 'Sales Invoice' and voucher_no = %s""",
@@ -1899,69 +1937,81 @@
 		frappe.flags.country = country
 
 	def test_einvoice_json(self):
-		from erpnext.regional.india.e_invoice.utils import make_einvoice
+		from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
 
-		si = make_sales_invoice_for_ewaybill()
-		si.naming_series = 'INV-2020-.#####'
-		si.items = []
-		si.append("items", {
-			"item_code": "_Test Item",
-			"uom": "Nos",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 2000,
-			"rate": 12,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-		})
-		si.append("items", {
-			"item_code": "_Test Item 2",
-			"uom": "Nos",
-			"warehouse": "_Test Warehouse - _TC",
-			"qty": 420,
-			"rate": 15,
-			"income_account": "Sales - _TC",
-			"expense_account": "Cost of Goods Sold - _TC",
-			"cost_center": "_Test Cost Center - _TC",
-		})
+		si = get_sales_invoice_for_e_invoice()
 		si.discount_amount = 100
 		si.save()
 
 		einvoice = make_einvoice(si)
-
-		total_item_ass_value = 0
-		total_item_cgst_value = 0
-		total_item_sgst_value = 0
-		total_item_igst_value = 0
-		total_item_value = 0
-
-		for item in einvoice['ItemList']:
-			total_item_ass_value += item['AssAmt']
-			total_item_cgst_value += item['CgstAmt']
-			total_item_sgst_value += item['SgstAmt']
-			total_item_igst_value += item['IgstAmt']
-			total_item_value += item['TotItemVal']
-
-			self.assertTrue(item['AssAmt'], item['TotAmt'] - item['Discount'])
-			self.assertTrue(item['TotItemVal'], item['AssAmt'] + item['CgstAmt'] + item['SgstAmt'] + item['IgstAmt'])
-
-		value_details = einvoice['ValDtls']
-
-		self.assertEqual(einvoice['Version'], '1.1')
-		self.assertEqual(value_details['AssVal'], total_item_ass_value)
-		self.assertEqual(value_details['CgstVal'], total_item_cgst_value)
-		self.assertEqual(value_details['SgstVal'], total_item_sgst_value)
-		self.assertEqual(value_details['IgstVal'], total_item_igst_value)
-
-		calculated_invoice_value = \
-			value_details['AssVal'] + value_details['CgstVal'] \
-			+ value_details['SgstVal'] + value_details['IgstVal'] \
-			+ value_details['OthChrg'] - value_details['Discount']
-
-		self.assertTrue(value_details['TotInvVal'] - calculated_invoice_value < 0.1)
-
-		self.assertEqual(value_details['TotInvVal'], si.base_grand_total)
 		self.assertTrue(einvoice['EwbDtls'])
+		validate_totals(einvoice)
+
+		si.apply_discount_on = 'Net Total'
+		si.save()
+		einvoice = make_einvoice(si)
+		validate_totals(einvoice)
+
+		[d.set('included_in_print_rate', 1) for d in si.taxes]
+		si.save()
+		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-.#####'
+	si.items = []
+	si.append("items", {
+		"item_code": "_Test Item",
+		"uom": "Nos",
+		"warehouse": "_Test Warehouse - _TC",
+		"qty": 2000,
+		"rate": 12,
+		"income_account": "Sales - _TC",
+		"expense_account": "Cost of Goods Sold - _TC",
+		"cost_center": "_Test Cost Center - _TC",
+	})
+
+	si.append("items", {
+		"item_code": "_Test Item 2",
+		"uom": "Nos",
+		"warehouse": "_Test Warehouse - _TC",
+		"qty": 420,
+		"rate": 15,
+		"income_account": "Sales - _TC",
+		"expense_account": "Cost of Goods Sold - _TC",
+		"cost_center": "_Test Cost Center - _TC",
+	})
+
+	return si
+
 
 def make_test_address_for_ewaybill():
 	if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
@@ -2038,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()
@@ -2057,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
@@ -2065,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
@@ -2085,27 +2135,6 @@
 		doc.assertEqual(expected_gle[i][2], gle.credit)
 		doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
-	def test_item_tax_validity(self):
-		item = frappe.get_doc("Item", "_Test Item 2")
-
-		if item.taxes:
-			item.taxes = []
-			item.save()
-
-		item.append("taxes", {
-			"item_tax_template": "_Test Item Tax Template 1 - _TC",
-			"valid_from": add_days(nowdate(), 1)
-		})
-
-		item.save()
-
-		sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
-		sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1 - _TC"
-		self.assertRaises(frappe.ValidationError, sales_invoice.save)
-
-		item.taxes = []
-		item.save()
-
 def create_sales_invoice(**args):
 	si = frappe.new_doc("Sales Invoice")
 	args = frappe._dict(args)
diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
index 14bf4d8..29422d6 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
+++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
@@ -1,235 +1,128 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-02-22 01:27:41", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2013-02-22 01:27:41",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "reference_type",
+  "reference_name",
+  "remarks",
+  "reference_row",
+  "col_break1",
+  "advance_amount",
+  "allocated_amount",
+  "exchange_gain_loss",
+  "ref_exchange_rate"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reference_type", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "label": "Reference Type", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "journal_voucher", 
-   "oldfieldtype": "Link", 
-   "options": "DocType", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "250px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "reference_type",
+   "fieldtype": "Link",
+   "label": "Reference Type",
+   "no_copy": 1,
+   "oldfieldname": "journal_voucher",
+   "oldfieldtype": "Link",
+   "options": "DocType",
+   "print_width": "250px",
+   "read_only": 1,
    "width": "250px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "reference_name", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Reference Name", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "reference_type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "columns": 3,
+   "fieldname": "reference_name",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Reference Name",
+   "no_copy": 1,
+   "options": "reference_type",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "remarks", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Remarks", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "remarks", 
-   "oldfieldtype": "Small Text", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "150px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "columns": 3,
+   "fieldname": "remarks",
+   "fieldtype": "Text",
+   "in_list_view": 1,
+   "label": "Remarks",
+   "no_copy": 1,
+   "oldfieldname": "remarks",
+   "oldfieldtype": "Small Text",
+   "print_width": "150px",
+   "read_only": 1,
    "width": "150px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reference_row", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "label": "Reference Row", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "jv_detail_no", 
-   "oldfieldtype": "Data", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "120px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "reference_row",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Reference Row",
+   "no_copy": 1,
+   "oldfieldname": "jv_detail_no",
+   "oldfieldtype": "Data",
+   "print_hide": 1,
+   "print_width": "120px",
+   "read_only": 1,
    "width": "120px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "col_break1", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "col_break1",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "advance_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Advance amount", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "advance_amount", 
-   "oldfieldtype": "Currency", 
-   "options": "party_account_currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "120px", 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "columns": 2,
+   "fieldname": "advance_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Advance amount",
+   "no_copy": 1,
+   "oldfieldname": "advance_amount",
+   "oldfieldtype": "Currency",
+   "options": "party_account_currency",
+   "print_width": "120px",
+   "read_only": 1,
    "width": "120px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "allocated_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Allocated amount", 
-   "length": 0, 
-   "no_copy": 1, 
-   "oldfieldname": "allocated_amount", 
-   "oldfieldtype": "Currency", 
-   "options": "party_account_currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "120px", 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "columns": 2,
+   "fieldname": "allocated_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Allocated amount",
+   "no_copy": 1,
+   "oldfieldname": "allocated_amount",
+   "oldfieldtype": "Currency",
+   "options": "party_account_currency",
+   "print_width": "120px",
    "width": "120px"
+  },
+  {
+   "fieldname": "exchange_gain_loss",
+   "fieldtype": "Currency",
+   "label": "Exchange Gain/Loss",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "ref_exchange_rate",
+   "fieldtype": "Float",
+   "label": "Reference Exchange Rate",
+   "non_negative": 1,
+   "read_only": 1
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2016-08-26 02:36:10.718057", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Sales Invoice Advance", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_order": "DESC", 
- "track_seen": 0
+ ],
+ "idx": 1,
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-04 20:25:49.832052",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Advance",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
index 3c8cb6b..1b7a0fe 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
@@ -1,8 +1,10 @@
 {
+ "actions": [],
  "creation": "2013-04-24 11:39:32",
  "doctype": "DocType",
  "document_type": "Setup",
  "editable_grid": 1,
+ "engine": "InnoDB",
  "field_order": [
   "charge_type",
   "row_id",
@@ -10,12 +12,14 @@
   "col_break_1",
   "description",
   "included_in_print_rate",
+  "included_in_paid_amount",
   "accounting_dimensions_section",
   "cost_center",
   "dimension_col_break",
   "section_break_8",
   "rate",
   "section_break_9",
+  "currency",
   "tax_amount",
   "total",
   "tax_amount_after_discount_amount",
@@ -23,8 +27,7 @@
   "base_tax_amount",
   "base_total",
   "base_tax_amount_after_discount_amount",
-  "item_wise_tax_detail",
-  "parenttype"
+  "item_wise_tax_detail"
  ],
  "fields": [
   {
@@ -174,17 +177,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "parenttype",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "in_filter": 1,
-   "label": "Parenttype",
-   "oldfieldname": "parenttype",
-   "oldfieldtype": "Data",
-   "print_hide": 1,
-   "search_index": 1
-  },
-  {
    "fieldname": "accounting_dimensions_section",
    "fieldtype": "Section Break",
    "label": "Accounting Dimensions"
@@ -192,15 +184,34 @@
   {
    "fieldname": "dimension_col_break",
    "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "account_head.account_currency",
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Account Currency",
+   "options": "Currency",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
+   "description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
+   "fieldname": "included_in_paid_amount",
+   "fieldtype": "Check",
+   "label": "Considered In Paid Amount"
   }
  ],
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-05-25 22:59:38.740883",
+ "links": [],
+ "modified": "2021-06-14 01:44:36.899147",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Taxes and Charges",
  "owner": "Administrator",
  "permissions": [],
+ "sort_field": "modified",
  "sort_order": "ASC"
 }
\ No newline at end of file
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/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 5c1cbaa..b9ee4a0 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -49,7 +49,7 @@
 	if not parties:
 		parties.append(party)
 
-	fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company)
+	fiscal_year = get_fiscal_year(inv.get('posting_date') or inv.get('transaction_date'), company=inv.company)
 	tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
 
 	if not tax_details:
@@ -154,7 +154,7 @@
 		tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
 
 	tax_amount = 0
-	posting_date = inv.posting_date
+	posting_date = inv.get('posting_date') or inv.get('transaction_date')
 	if party_type == 'Supplier':
 		ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
 		if tax_deducted:
@@ -257,7 +257,7 @@
 	if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
 		if ldc and is_valid_certificate(
 			ldc.valid_from, ldc.valid_upto,
-			inv.posting_date, tax_deducted,
+			inv.get('posting_date') or inv.get('transaction_date'), tax_deducted,
 			inv.net_total, ldc.certificate_limit
 		):
 			tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index 0cea761..dd26be7 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -112,7 +112,7 @@
 		si = create_sales_invoice(customer = "Test TCS Customer", rate=5000)
 		si.submit()
 
-		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+		tcs_charged = sum(d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC')
 		self.assertEqual(tcs_charged, 500)
 		invoices.append(si)
 
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index f1717c5..25d2cf1 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -101,7 +101,7 @@
 
 def check_if_in_list(gle, gl_map, dimensions=None):
 	account_head_fieldnames = ['party_type', 'party', 'against_voucher', 'against_voucher_type',
-		'cost_center', 'project']
+		'cost_center', 'project', 'voucher_detail_no']
 
 	if dimensions:
 		account_head_fieldnames = account_head_fieldnames + dimensions
@@ -143,7 +143,7 @@
 		validate_expense_against_budget(args)
 
 def validate_cwip_accounts(gl_map):
-	cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
+	cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting"))
 
 	if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
 			cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
@@ -185,10 +185,10 @@
 	for d in gl_map:
 		if d.account == round_off_account:
 			round_off_gle = d
-			if d.debit_in_account_currency:
-				debit_credit_diff -= flt(d.debit_in_account_currency)
+			if d.debit:
+				debit_credit_diff -= flt(d.debit)
 			else:
-				debit_credit_diff += flt(d.credit_in_account_currency)
+				debit_credit_diff += flt(d.credit)
 			round_off_account_exists = True
 
 	if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)):
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index e01cb6e..b97dc40 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -457,7 +457,7 @@
 					frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
 
 		elif party_type == "Employee":
-			if frappe.db.get_value("Employee", party_name, "status") == "Left":
+			if frappe.db.get_value("Employee", party_name, "status") != "Active":
 				frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), alert=True)
 
 def get_timeline_data(doctype, name):
@@ -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/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py
index 65e7d78..be64c32 100644
--- a/erpnext/accounts/report/account_balance/account_balance.py
+++ b/erpnext/accounts/report/account_balance/account_balance.py
@@ -58,11 +58,9 @@
 def get_data(filters):
 
 	data = []
-
 	conditions = get_conditions(filters)
-
 	accounts = frappe.db.get_all("Account", fields=["name", "account_currency"],
-		filters=conditions)
+		filters=conditions, order_by='name')
 
 	for d in accounts:
 		balance = get_balance_on(d.name, date=filters.report_date)
diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py
index b6ced31..14ddf4a 100644
--- a/erpnext/accounts/report/account_balance/test_account_balance.py
+++ b/erpnext/accounts/report/account_balance/test_account_balance.py
@@ -23,7 +23,7 @@
 
 		expected_data = [
 			{
-				"account": 'Sales - _TC2',
+				"account": 'Direct Income - _TC2',
 				"currency": 'EUR',
 				"balance": -100.0,
 			},
@@ -33,20 +33,20 @@
 				"balance": -100.0,
 			},
 			{
-				"account": 'Service - _TC2',
-				"currency": 'EUR',
-				"balance": 0.0,
-			},
-			{
-				"account": 'Direct Income - _TC2',
-				"currency": 'EUR',
-				"balance": -100.0,
-			},
-			{
 				"account": 'Indirect Income - _TC2',
 				"currency": 'EUR',
 				"balance": 0.0,
 			},
+			{
+				"account": 'Sales - _TC2',
+				"currency": 'EUR',
+				"balance": -100.0,
+			},
+			{
+				"account": 'Service - _TC2',
+				"currency": 'EUR',
+				"balance": 0.0,
+			}
 		]
 
 		self.assertEqual(expected_data, report[1])
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index db605f7..a11b77a 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -584,6 +584,7 @@
 				`tabGL Entry`
 			where
 				docstatus < 2
+				and is_cancelled = 0
 				and party_type=%s
 				and (party is not null and party != '')
 				{1} {2} {3}"""
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/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
index ff87276..c11c153 100644
--- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
@@ -32,7 +32,7 @@
 		join `tabCash Flow Mapping` cfm on cfma.parent=cfm.name
 		where cfma.parent in (%s)
 		order by cfm.is_working_capital
-	''', (', '.join(['"%s"' % d for d in mapping_names])))
+	''', (', '.join('"%s"' % d for d in mapping_names)))
 
 
 def setup_mappers(mappers):
@@ -83,8 +83,8 @@
 
 		account_types_labels = sorted(
 			set(
-				[(d['label'], d['is_working_capital'], d['is_income_tax_liability'], d['is_income_tax_expense'])
-					for d in account_types]
+				(d['label'], d['is_working_capital'], d['is_income_tax_liability'], d['is_income_tax_expense'])
+					for d in account_types
 			),
 			key=lambda x: x[1]
 		)
@@ -375,7 +375,7 @@
 	total = 0
 	for period in period_list:
 		start_date = get_start_date(period, accumulated_values, company)
-		accounts = ', '.join(['"%s"' % d for d in account_names])
+		accounts = ', '.join('"%s"' % d for d in account_names)
 
 		if opening_balances:
 			date_info = dict(date=start_date)
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/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
index 10b32fe..c79d740 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
@@ -145,7 +145,7 @@
 		out = []
 		for party, row in iteritems(self.party_data):
 			if row.opening_balance or row.invoiced_amount or row.paid_amount or row.return_amount or row.closing_amount:
-				total_party_adjustment = sum([amount for amount in itervalues(self.party_adjustment_details.get(party, {}))])
+				total_party_adjustment = sum(amount for amount in itervalues(self.party_adjustment_details.get(party, {})))
 				row.paid_amount -= total_party_adjustment
 
 				adjustments = self.party_adjustment_details.get(party, {})
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index d20ddbd..39ff804 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -369,7 +369,7 @@
 
 	if accounts:
 		additional_conditions += " and account in ({})"\
-			.format(", ".join([frappe.db.escape(d) for d in accounts]))
+			.format(", ".join(frappe.db.escape(d) for d in accounts))
 
 		gl_filters = {
 			"company": company,
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 84f7868..4a551b8 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -36,16 +36,12 @@
 		{
 			"fieldname":"account",
 			"label": __("Account"),
-			"fieldtype": "Link",
+			"fieldtype": "MultiSelectList",
 			"options": "Account",
-			"get_query": function() {
-				var company = frappe.query_report.get_filter_value('company');
-				return {
-					"doctype": "Account",
-					"filters": {
-						"company": company,
-					}
-				}
+			get_data: function(txt) {
+				return frappe.db.get_link_options('Account', txt, {
+					company: frappe.query_report.get_filter_value("company")
+				});
 			}
 		},
 		{
@@ -135,7 +131,9 @@
 			"label": __("Cost Center"),
 			"fieldtype": "MultiSelectList",
 			get_data: function(txt) {
-				return frappe.db.get_link_options('Cost Center', txt);
+				return frappe.db.get_link_options('Cost Center', txt, {
+					company: frappe.query_report.get_filter_value("company")
+				});
 			}
 		},
 		{
@@ -143,7 +141,9 @@
 			"label": __("Project"),
 			"fieldtype": "MultiSelectList",
 			get_data: function(txt) {
-				return frappe.db.get_link_options('Project', txt);
+				return frappe.db.get_link_options('Project', txt, {
+					company: frappe.query_report.get_filter_value("company")
+				});
 			}
 		},
 		{
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 562df4f..e724e9b 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -48,9 +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"))))
-
-	if filters.get("account") and not account_details.get(filters.account):
-		frappe.throw(_("Account {0} does not exists").format(filters.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):
@@ -87,7 +90,19 @@
 		account_currency = None
 
 		if filters.get("account"):
-			account_currency = get_account_currency(filters.account)
+			if len(filters.get("account")) == 1:	
+				account_currency = get_account_currency(filters.account[0])
+			else:
+				currency = get_account_currency(filters.account[0])
+				is_same_account_currency = True
+				for account in filters.get("account"):
+					if get_account_currency(account) != currency:
+						is_same_account_currency = False
+						break
+
+				if is_same_account_currency:
+					account_currency = currency
+
 		elif filters.get("party"):
 			gle_currency = frappe.db.get_value(
 				"GL Entry", {
@@ -205,10 +220,10 @@
 
 def get_conditions(filters):
 	conditions = []
-	if filters.get("account") and not filters.get("include_dimensions"):
-		lft, rgt = frappe.db.get_value("Account", filters["account"], ["lft", "rgt"])
-		conditions.append("""account in (select name from tabAccount
-			where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt))
+
+	if filters.get("account"):
+		filters.account = get_accounts_with_children(filters.account)
+		conditions.append("account in %(account)s")
 
 	if filters.get("cost_center"):
 		filters.cost_center = get_cost_centers_with_children(filters.cost_center)
@@ -266,6 +281,20 @@
 
 	return "and {}".format(" and ".join(conditions)) if conditions else ""
 
+def get_accounts_with_children(accounts):
+	if not isinstance(accounts, list):
+		accounts = [d.strip() for d in accounts.strip().split(',') if d]
+
+	all_accounts = []
+	for d in accounts:
+		if frappe.db.exists("Account", d):
+			lft, rgt = frappe.db.get_value("Account", d, ["lft", "rgt"])
+			children = frappe.get_all("Account", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
+			all_accounts += [c.name for c in children]
+		else:
+			frappe.throw(_("Account: {0} does not exist").format(d))
+
+	return list(set(all_accounts))
 
 def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
 	data = []
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index cb4d9b4..685419a 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -334,7 +334,7 @@
 
 def get_purchase_receipts_against_purchase_order(item_list):
 	po_pr_map = frappe._dict()
-	po_item_rows = list(set([d.po_detail for d in item_list]))
+	po_item_rows = list(set(d.po_detail for d in item_list))
 
 	if po_item_rows:
 		purchase_receipts = frappe.db.sql("""
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 928b373..2e794da 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -23,7 +23,7 @@
 	if item_list:
 		itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
 
-	mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list]))
+	mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list))
 	so_dn_map = get_delivery_notes_against_sales_order(item_list)
 
 	data = []
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index cfbd7fd..6a42bb4 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -77,14 +77,14 @@
 		), filters, as_dict=1)
 
 def concat_mode_of_payments(pos_entries):
-	mode_of_payments = get_mode_of_payments(set([d.pos_invoice for d in pos_entries]))
+	mode_of_payments = get_mode_of_payments(set(d.pos_invoice for d in pos_entries))
 	for entry in pos_entries:
 		if mode_of_payments.get(entry.pos_invoice):
 			entry.mode_of_payment = ", ".join(mode_of_payments.get(entry.pos_invoice, []))
 
 def add_subtotal_row(data, group_invoices, group_by_field, group_by_value):
-	grand_total = sum([d.grand_total for d in group_invoices])
-	paid_amount = sum([d.paid_amount for d in group_invoices])
+	grand_total = sum(d.grand_total for d in group_invoices)
+	paid_amount = sum(d.paid_amount for d in group_invoices)
 	data.append({
 		group_by_field: group_by_value,
 		"grand_total": grand_total,
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/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 8ac749d..10edd41 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -26,7 +26,7 @@
 	invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
 		invoice_expense_map, expense_accounts)
 	invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
-	suppliers = list(set([d.supplier for d in invoice_list]))
+	suppliers = list(set(d.supplier for d in invoice_list))
 	supplier_details = get_supplier_details(suppliers)
 
 	company_currency = frappe.get_cached_value('Company',  filters.company,  "default_currency")
@@ -120,13 +120,13 @@
 			and docstatus = 1 and (account_head is not null and account_head != '')
 			and category in ('Total', 'Valuation and Total')
 			and parent in (%s) order by account_head""" %
-			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
 
 		unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
 			from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
 			and ifnull(unrealized_profit_loss_account, '') != ''
 			order by unrealized_profit_loss_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
 
 	expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
 	unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts]
@@ -208,7 +208,7 @@
 		from `tabPurchase Invoice Item`
 		where parent in (%s)
 		group by parent, expense_account
-	""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+	""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_expense_map = {}
 	for d in expense_details:
@@ -221,7 +221,7 @@
 	unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
 		base_net_total as amount from `tabPurchase Invoice` where name in (%s)
 		and is_internal_supplier = 1 and company = represents_company""" %
-		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	internal_invoice_map = {}
 	for d in unrealized_amount_details:
@@ -238,7 +238,7 @@
 		where parent in (%s) and category in ('Total', 'Valuation and Total')
 			and base_tax_amount_after_discount_amount != 0
 		group by parent, account_head, add_deduct_tax
-	""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+	""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_tax_map = {}
 	for d in tax_details:
@@ -258,7 +258,7 @@
 		select parent, purchase_order, purchase_receipt, po_detail, project
 		from `tabPurchase Invoice Item`
 		where parent in (%s)
-	""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+	""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_po_pr_map = {}
 	for d in pi_items:
diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
index c234da0..ff77468 100644
--- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
+++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py
@@ -158,7 +158,7 @@
 def get_mode_of_payments(filters):
 	mode_of_payments = {}
 	invoice_list = get_invoices(filters)
-	invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list])
+	invoice_list_names = ",".join('"' + invoice['name'] + '"' for invoice in invoice_list)
 	if invoice_list:
 		inv_mop = frappe.db.sql("""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
 			from `tabSales Invoice` a, `tabSales Invoice Payment` b
@@ -197,7 +197,7 @@
 def get_mode_of_payment_details(filters):
 	mode_of_payment_details = {}
 	invoice_list = get_invoices(filters)
-	invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list])
+	invoice_list_names = ",".join('"' + invoice['name'] + '"' for invoice in invoice_list)
 	if invoice_list:
 		inv_mop_detail = frappe.db.sql("""select a.owner, a.posting_date,
 			ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index cb2c98b..9099593 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -248,19 +248,19 @@
 		income_accounts = frappe.db.sql_list("""select distinct income_account
 			from `tabSales Invoice Item` where docstatus = 1 and parent in (%s)
 			order by income_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
 
 		tax_accounts = 	frappe.db.sql_list("""select distinct account_head
 			from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
 			and docstatus = 1 and base_tax_amount_after_discount_amount != 0
 			and parent in (%s) order by account_head""" %
-			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
 
 		unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
 			from `tabSales Invoice` where docstatus = 1 and name in (%s)
 			and ifnull(unrealized_profit_loss_account, '') != ''
 			order by unrealized_profit_loss_account""" %
-			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+			', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
 
 	for account in income_accounts:
 		income_columns.append({
@@ -406,7 +406,7 @@
 def get_invoice_income_map(invoice_list):
 	income_details = frappe.db.sql("""select parent, income_account, sum(base_net_amount) as amount
 		from `tabSales Invoice Item` where parent in (%s) group by parent, income_account""" %
-		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_income_map = {}
 	for d in income_details:
@@ -419,7 +419,7 @@
 	unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
 		base_net_total as amount from `tabSales Invoice` where name in (%s)
 		and is_internal_customer = 1 and company = represents_company""" %
-		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	internal_invoice_map = {}
 	for d in unrealized_amount_details:
@@ -432,7 +432,7 @@
 	tax_details = frappe.db.sql("""select parent, account_head,
 		sum(base_tax_amount_after_discount_amount) as tax_amount
 		from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" %
-		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_tax_map = {}
 	for d in tax_details:
@@ -451,7 +451,7 @@
 	si_items = frappe.db.sql("""select parent, sales_order, delivery_note, so_detail
 		from `tabSales Invoice Item` where parent in (%s)
 		and (ifnull(sales_order, '') != '' or ifnull(delivery_note, '') != '')""" %
-		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_so_dn_map = {}
 	for d in si_items:
@@ -475,7 +475,7 @@
 	si_items = frappe.db.sql("""select parent, cost_center, warehouse
 		from `tabSales Invoice Item` where parent in (%s)
 		and (ifnull(cost_center, '') != '' or ifnull(warehouse, '') != '')""" %
-		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+		', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1)
 
 	invoice_cc_wh_map = {}
 	for d in si_items:
diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
index a8280c1..e15715d 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
@@ -78,7 +78,7 @@
 			and company=%s and posting_date between %s and %s
 	""", (supplier, company, from_date, to_date), as_dict=1)
 
-	supplier_credit_amount = flt(sum([d.credit for d in entries]))
+	supplier_credit_amount = flt(sum(d.credit for d in entries))
 
 	vouchers = [d.voucher_no for d in entries]
 	vouchers += get_advance_vouchers([supplier], company=company,
@@ -91,7 +91,7 @@
 			from `tabGL Entry`
 			where account=%s and posting_date between %s and %s
 				and company=%s and credit > 0 and voucher_no in ({0})
-		""".format(', '.join(["'%s'" % d for d in vouchers])),
+		""".format(', '.join("'%s'" % d for d in vouchers)),
 			(account, from_date, to_date, company))[0][0])
 
 	date_range_filter = [fiscal_year, from_date, to_date]
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
index 344539e..72de318 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js
@@ -55,6 +55,32 @@
 			}
 		},
 		{
+			"fieldname":"purchase_order",
+			"label": __("Purchase Order"),
+			"fieldtype": "Link",
+			"options": "Purchase Order",
+			"get_query": function() {
+				return {
+					"filters": {
+						"name": ["in", frappe.query_report.invoices]
+					}
+				}
+			},
+			on_change: function() {
+				let supplier = frappe.query_report.get_filter_value('supplier');
+				if(!supplier) return; // return if no supplier selected
+
+				// filter invoices based on selected supplier
+				let invoices = [];
+				frappe.query_report.invoice_data.map(d => {
+					if(d.supplier==supplier)
+						invoices.push(d.name)
+				});
+				frappe.query_report.invoices = invoices;
+				frappe.query_report.refresh();
+			}
+		},
+		{
 			"fieldname":"from_date",
 			"label": __("From Date"),
 			"fieldtype": "Date",
@@ -75,15 +101,17 @@
 	onload: function(report) {
 		// fetch all tds applied invoices
 		frappe.call({
-			"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices",
+			"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders",
 			callback: function(r) {
 				let invoices = [];
+
 				r.message.map(d => {
 					invoices.push(d.name);
 				});
 
-				report["invoice_data"] = r.message;
+				report["invoice_data"] = r.message.invoices;
 				report["invoices"] = invoices;
+
 			}
 		});
 	}
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
index a9fb237..ceefa31 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -11,11 +11,14 @@
 	validate_filters(filters)
 	set_filters(filters)
 
+	# TDS payment entries
+	payment_entries = get_payment_entires(filters)
+
 	columns = get_columns(filters)
-	if not filters["invoices"]:
+	if not filters.get("invoices"):
 		return columns, []
 
-	res = get_result(filters)
+	res = get_result(filters, payment_entries)
 
 	return columns, res
 
@@ -27,8 +30,9 @@
 def set_filters(filters):
 	invoices = []
 
-	if not filters["invoices"]:
-		filters["invoices"] = get_tds_invoices()
+	if not filters.get("invoices"):
+		filters["invoices"] = get_tds_invoices_and_orders()
+
 	if filters.supplier and filters.purchase_invoice:
 		for d in filters["invoices"]:
 			if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
@@ -41,13 +45,29 @@
 		for d in filters["invoices"]:
 			if d.name == filters.purchase_invoice:
 				invoices.append(d)
+	elif filters.supplier and filters.purchase_order:
+		for d in filters.get("invoices"):
+			if d.name == filters.purchase_order and d.supplier == filters.supplier:
+				invoices.append(d)
+	elif filters.supplier and not filters.purchase_order:
+		for d in filters.get("invoices"):
+			if d.supplier == filters.supplier:
+				invoices.append(d)
+	elif filters.purchase_order and not filters.supplier:
+		for d in filters.get("invoices"):
+			if d.name == filters.purchase_order:
+				invoices.append(d)
 
 	filters["invoices"] = invoices if invoices else filters["invoices"]
 	filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
 
-def get_result(filters):
-	supplier_map, tds_docs = get_supplier_map(filters)
-	gle_map = get_gle_map(filters)
+	#print(filters.get('invoices'))
+
+def get_result(filters, payment_entries):
+	supplier_map, tds_docs = get_supplier_map(filters, payment_entries)
+	documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries]
+
+	gle_map = get_gle_map(filters, documents)
 
 	out = []
 	for d in gle_map:
@@ -62,10 +82,11 @@
 
 		for k in gle_map[d]:
 			if k.party == supplier_map[d] and k.credit > 0:
-				total_amount_credited += k.credit
-			elif account_list and k.account == account and k.credit > 0:
-				tds_deducted = k.credit
-				total_amount_credited += k.credit
+				total_amount_credited += (k.credit - k.debit)
+			elif account_list and k.account == account and (k.credit - k.debit) > 0:
+				tds_deducted = (k.credit - k.debit)
+				total_amount_credited += (k.credit - k.debit)
+			voucher_type = k.voucher_type
 
 		rate = [i.tax_withholding_rate for i in tds_doc.rates
 			if i.fiscal_year == gle_map[d][0].fiscal_year]
@@ -73,32 +94,36 @@
 		if rate and len(rate) > 0 and tds_deducted:
 			rate = rate[0]
 
-			if getdate(filters.from_date) <= gle_map[d][0].posting_date \
-				and getdate(filters.to_date) >= gle_map[d][0].posting_date:
-				row = [supplier.pan, supplier.name]
+			row = [supplier.pan, supplier.name]
 
-				if filters.naming_series == 'Naming Series':
-					row.append(supplier.supplier_name)
+			if filters.naming_series == 'Naming Series':
+				row.append(supplier.supplier_name)
 
-				row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
-					tds_deducted, gle_map[d][0].posting_date, "Purchase Invoice", d])
-				out.append(row)
+			row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
+				tds_deducted, gle_map[d][0].posting_date, voucher_type, d])
+			out.append(row)
 
 	return out
 
-def get_supplier_map(filters):
+def get_supplier_map(filters, payment_entries):
 	# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
 	# pre-fetch all distinct applicable tds docs
 	supplier_map, tds_docs = {}, {}
 	pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
+	supplier_list = [d.supplier for d in filters["invoices"]]
+
 	supplier_detail = frappe.db.get_all('Supplier',
-		{"name": ["in", [d.supplier for d in filters["invoices"]]]},
+		{"name": ["in", supplier_list]},
 		["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
 
 	for d in filters["invoices"]:
 		supplier_map[d.get("name")] = [k for k in supplier_detail
 			if k.name == d.get("supplier")][0]
 
+	for d in payment_entries:
+		supplier_map[d.get("name")] = [k for k in supplier_detail
+			if k.name == d.get("supplier")][0]
+
 	for d in supplier_detail:
 		if d.get("tax_withholding_category") not in tds_docs:
 			tds_docs[d.get("tax_withholding_category")] = \
@@ -106,13 +131,19 @@
 
 	return supplier_map, tds_docs
 
-def get_gle_map(filters):
+def get_gle_map(filters, documents):
 	# create gle_map of the form
 	# {"purchase_invoice": list of dict of all gle created for this invoice}
 	gle_map = {}
-	gle = frappe.db.get_all('GL Entry',\
-		{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0},
-		["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"])
+
+	gle = frappe.db.get_all('GL Entry',
+		{
+			"voucher_no": ["in", documents],
+			'is_cancelled': 0,
+			'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
+		},
+		["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"],
+	)
 
 	for d in gle:
 		if not d.voucher_no in gle_map:
@@ -201,8 +232,26 @@
 
 	return columns
 
+def get_payment_entires(filters):
+	filter_dict = {
+		'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
+		'party_type': 'Supplier',
+		'apply_tax_withholding_amount': 1
+	}
+
+	if filters.get('purchase_invoice') or filters.get('purchase_order'):
+		parent = frappe.db.get_all('Payment Entry Reference',
+			{'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent'])
+
+		filter_dict.update({'name': ('in', [d.get('parent') for d in parent])})
+
+	payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'],
+		filters=filter_dict)
+
+	return payment_entries
+
 @frappe.whitelist()
-def get_tds_invoices():
+def get_tds_invoices_and_orders():
 	# fetch tds applicable supplier and fetch invoices for these suppliers
 	suppliers = [d.name for d in frappe.db.get_list("Supplier",
 		{"tax_withholding_category": ["!=", ""]}, ["name"])]
@@ -210,7 +259,12 @@
 	invoices = frappe.db.get_list("Purchase Invoice",
 		{"supplier": ["in", suppliers]}, ["name", "supplier"])
 
+	orders = frappe.db.get_list("Purchase Order",
+		{"supplier": ["in", suppliers]}, ["name", "supplier"])
+
+	invoices = invoices + orders
 	invoices = [d for d in invoices if d.supplier]
+
 	frappe.cache().hset("invoices", frappe.session.user, invoices)
 
 	return invoices
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index b020d0a..ba461ed 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -139,6 +139,6 @@
 	gross_profit_data = GrossProfitGenerator(filters)
 	result = gross_profit_data.grouped_data
 	if not with_item_data:
-		result = sum([d.gross_profit for d in result])
+		result = sum(d.gross_profit for d in result)
 
 	return result
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 5a64e27..1cdbd8d 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -472,7 +472,8 @@
 		"total_amount": d.grand_total,
 		"outstanding_amount": d.outstanding_amount,
 		"allocated_amount": d.allocated_amount,
-		"exchange_rate": d.exchange_rate
+		"exchange_rate": d.exchange_rate if not d.exchange_gain_loss else payment_entry.get_exchange_rate(),
+		"exchange_gain_loss": d.exchange_gain_loss # only populated from invoice in case of advance allocation
 	}
 
 	if d.voucher_detail_no:
@@ -498,12 +499,15 @@
 	payment_entry.set_amounts()
 
 	if d.difference_amount and d.difference_account:
-		payment_entry.set_gain_or_loss(account_details={
+		account_details = {
 			'account': d.difference_account,
 			'cost_center': payment_entry.cost_center or frappe.get_cached_value('Company',
-				payment_entry.company, "cost_center"),
-			'amount': d.difference_amount
-		})
+				payment_entry.company, "cost_center")
+		}
+		if d.difference_amount:
+			account_details['amount'] = d.difference_amount
+
+		payment_entry.set_gain_or_loss(account_details=account_details)
 
 	if not do_not_save:
 		payment_entry.save(ignore_permissions=True)
@@ -635,7 +639,7 @@
 			'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()',
 			as_dict=1
 		)
-		held_invoices = set([d['name'] for d in held_invoices])
+		held_invoices = set(d['name'] for d in held_invoices)
 
 	return held_invoices
 
@@ -784,7 +788,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/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index df68318..10a4001 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -445,15 +445,15 @@
    "type": "Link"
   },
   {
-    "dependencies": "GL Entry",
-    "hidden": 0,
-    "is_query_report": 1,
-    "label": "UAE VAT 201",
-    "link_to": "UAE VAT 201",
-    "link_type": "Report",
-    "onboard": 0,
-    "type": "Link"
-   },
+   "dependencies": "GL Entry",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "UAE VAT 201",
+   "link_to": "UAE VAT 201",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
   {
    "hidden": 0,
    "is_query_report": 0,
@@ -684,6 +684,7 @@
    "is_query_report": 0,
    "label": "Goods and Services Tax (GST India)",
    "onboard": 0,
+   "only_for": "India",
    "type": "Card Break"
   },
   {
@@ -694,6 +695,7 @@
    "link_to": "GST Settings",
    "link_type": "DocType",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -704,6 +706,7 @@
    "link_to": "GST HSN Code",
    "link_type": "DocType",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -714,6 +717,7 @@
    "link_to": "GSTR-1",
    "link_type": "Report",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -724,6 +728,7 @@
    "link_to": "GSTR-2",
    "link_type": "Report",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -734,6 +739,7 @@
    "link_to": "GSTR 3B Report",
    "link_type": "DocType",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -744,6 +750,7 @@
    "link_to": "GST Sales Register",
    "link_type": "Report",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -754,6 +761,7 @@
    "link_to": "GST Purchase Register",
    "link_type": "Report",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -764,6 +772,7 @@
    "link_to": "GST Itemised Sales Register",
    "link_type": "Report",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -774,6 +783,7 @@
    "link_to": "GST Itemised Purchase Register",
    "link_type": "Report",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -784,6 +794,7 @@
    "link_to": "C-Form",
    "link_type": "DocType",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -794,6 +805,7 @@
    "link_to": "Lower Deduction Certificate",
    "link_type": "DocType",
    "onboard": 0,
+   "only_for": "India",
    "type": "Link"
   },
   {
@@ -1052,7 +1064,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-05-12 11:48:01.905144",
+ "modified": "2021-06-10 03:17:31.427945",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
@@ -1107,4 +1119,4 @@
    "type": "Dashboard"
   }
  ]
-}
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 3cd4b80..8845f24 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -470,7 +470,7 @@
 		})
 		asset.insert()
 		accumulated_depreciation_after_full_schedule = \
-			max([d.accumulated_depreciation_amount for d in asset.get("schedules")])
+			max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
 
 		asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
 			flt(accumulated_depreciation_after_full_schedule))
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index 1430827..2f6b5ee 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -92,7 +92,7 @@
 			d.value_after_depreciation = asset_value
 
 			if d.depreciation_method in ("Straight Line", "Manual"):
-				end_date = max([s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx])
+				end_date = max(s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx)
 				total_days = date_diff(end_date, self.date)
 				rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
 				from_date = self.date
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index 630a1dc..b9c77d5 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -9,13 +9,14 @@
   "supp_master_name",
   "supplier_group",
   "buying_price_list",
+  "maintain_same_rate_action",
+  "role_to_override_stop_action",
   "column_break_3",
   "po_required",
   "pr_required",
   "maintain_same_rate",
-  "maintain_same_rate_action",
-  "role_to_override_stop_action",
   "allow_multiple_items",
+  "bill_for_rejected_quantity_in_purchase_invoice",
   "subcontract",
   "backflush_raw_materials_of_subcontract_based_on",
   "column_break_11",
@@ -108,6 +109,13 @@
    "fieldtype": "Link",
    "label": "Role Allowed to Override Stop Action",
    "options": "Role"
+  },
+  {
+   "default": "1",
+   "description": "If checked, Rejected Quantity will be included while making Purchase Invoice from Purchase Receipt.",
+   "fieldname": "bill_for_rejected_quantity_in_purchase_invoice",
+   "fieldtype": "Check",
+   "label": "Bill for Rejected Quantity in Purchase Invoice"
   }
  ],
  "icon": "fa fa-cog",
@@ -115,7 +123,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-04-04 20:01:44.087066",
+ "modified": "2021-06-24 10:38:28.934525",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying Settings",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index dd0f065..233a9c8 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -45,6 +45,47 @@
 		});
 
 		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+	},
+
+	apply_tds: function(frm) {
+		if (!frm.doc.apply_tds) {
+			frm.set_value("tax_withholding_category", '');
+		} else {
+			frm.set_value("tax_withholding_category", frm.supplier_tds);
+		}
+	},
+
+	refresh: function(frm) {
+		frm.trigger('get_materials_from_supplier');
+	},
+
+	get_materials_from_supplier: function(frm) {
+		let po_details = [];
+
+		if (frm.doc.supplied_items && (frm.doc.per_received == 100 || frm.doc.status === 'Closed')) {
+			frm.doc.supplied_items.forEach(d => {
+				if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
+					po_details.push(d.name)
+				}
+			});
+		}
+
+		if (po_details && po_details.length) {
+			frm.add_custom_button(__('Return of Components'), () => {
+				frm.call({
+					method: 'erpnext.buying.doctype.purchase_order.purchase_order.get_materials_from_supplier',
+					freeze: true,
+					freeze_message: __('Creating Stock Entry'),
+					args: { purchase_order: frm.doc.name, po_details: po_details },
+					callback: function(r) {
+						if (r && r.message) {
+							const doc = frappe.model.sync(r.message);
+							frappe.set_route("Form", doc[0].doctype, doc[0].name);
+						}
+					}
+				});
+			}, __('Create'));
+		}
 	}
 });
 
@@ -209,7 +250,7 @@
 	},
 
 	has_unsupplied_items: function() {
-		return this.frm.doc['supplied_items'].some(item => item.required_qty != item.supplied_qty)
+		return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty)
 	},
 
 	make_stock_entry: function() {
@@ -313,7 +354,8 @@
 			if(me.values) {
 				me.values.sub_con_rm_items.map((row,i) => {
 					if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) {
-						frappe.throw(__("Item Code, warehouse, quantity are required on row {0}", [i+1]));
+						let row_id = i+1;
+						frappe.throw(__("Item Code, warehouse and quantity are required on row {0}", [row_id]));
 					}
 				})
 				me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children())
@@ -504,12 +546,14 @@
 			],
 			primary_action: function() {
 				var data = d.get_values();
+				let reason_for_hold = 'Reason for hold: ' + data.reason_for_hold;
+
 				frappe.call({
 					method: "frappe.desk.form.utils.add_comment",
 					args: {
 						reference_doctype: me.frm.doctype,
 						reference_name: me.frm.docname,
-						content: __('Reason for hold: ')+data.reason_for_hold,
+						content: __(reason_for_hold),
 						comment_email: frappe.session.user,
 						comment_by: frappe.session.user_fullname
 					},
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index ee2beea..bb0ad60 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -14,6 +14,8 @@
   "supplier",
   "get_items_from_open_material_requests",
   "supplier_name",
+  "apply_tds",
+  "tax_withholding_category",
   "column_break1",
   "company",
   "transaction_date",
@@ -142,7 +144,9 @@
   {
    "fieldname": "supplier_section",
    "fieldtype": "Section Break",
-   "options": "fa fa-user"
+   "options": "fa fa-user",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
@@ -152,7 +156,9 @@
    "hidden": 1,
    "label": "Title",
    "no_copy": 1,
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "naming_series",
@@ -164,7 +170,9 @@
    "options": "PUR-ORD-.YYYY.-",
    "print_hide": 1,
    "reqd": 1,
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "bold": 1,
@@ -178,14 +186,18 @@
    "options": "Supplier",
    "print_hide": 1,
    "reqd": 1,
-   "search_index": 1
+   "search_index": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))",
    "description": "Fetch items based on Default Supplier.",
    "fieldname": "get_items_from_open_material_requests",
    "fieldtype": "Button",
-   "label": "Get Items from Open Material Requests"
+   "label": "Get Items from Open Material Requests",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "bold": 1,
@@ -194,7 +206,9 @@
    "fieldtype": "Data",
    "in_global_search": 1,
    "label": "Supplier Name",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "company",
@@ -206,13 +220,17 @@
    "options": "Company",
    "print_hide": 1,
    "remember_last_selected_value": 1,
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break1",
    "fieldtype": "Column Break",
    "oldfieldtype": "Column Break",
    "print_width": "50%",
+   "show_days": 1,
+   "show_seconds": 1,
    "width": "50%"
   },
   {
@@ -224,27 +242,35 @@
    "oldfieldname": "transaction_date",
    "oldfieldtype": "Date",
    "reqd": 1,
-   "search_index": 1
+   "search_index": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "fieldname": "schedule_date",
    "fieldtype": "Date",
-   "label": "Required By"
+   "label": "Required By",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "depends_on": "eval:doc.docstatus===1",
    "fieldname": "order_confirmation_no",
    "fieldtype": "Data",
-   "label": "Order Confirmation No"
+   "label": "Order Confirmation No",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "depends_on": "eval:doc.order_confirmation_no",
    "fieldname": "order_confirmation_date",
    "fieldtype": "Date",
-   "label": "Order Confirmation Date"
+   "label": "Order Confirmation Date",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "amended_from",
@@ -256,19 +282,25 @@
    "oldfieldtype": "Data",
    "options": "Purchase Order",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "drop_ship",
    "fieldtype": "Section Break",
-   "label": "Drop Ship"
+   "label": "Drop Ship",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "customer",
    "fieldtype": "Link",
    "label": "Customer",
    "options": "Customer",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "bold": 1,
@@ -276,31 +308,41 @@
    "fieldtype": "Data",
    "label": "Customer Name",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_19",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "customer_contact_person",
    "fieldtype": "Link",
    "label": "Customer Contact",
-   "options": "Contact"
+   "options": "Contact",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "customer_contact_display",
    "fieldtype": "Small Text",
    "hidden": 1,
    "label": "Customer Contact",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "customer_contact_mobile",
    "fieldtype": "Small Text",
    "hidden": 1,
    "label": "Customer Mobile No",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "customer_contact_email",
@@ -308,27 +350,35 @@
    "hidden": 1,
    "label": "Customer Contact Email",
    "options": "Email",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "section_addresses",
    "fieldtype": "Section Break",
-   "label": "Address and Contact"
+   "label": "Address and Contact",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "supplier_address",
    "fieldtype": "Link",
    "label": "Supplier Address",
    "options": "Address",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "contact_person",
    "fieldtype": "Link",
    "label": "Supplier Contact",
    "options": "Contact",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "address_display",
@@ -355,32 +405,42 @@
    "label": "Contact Email",
    "options": "Email",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "col_break_address",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "shipping_address",
    "fieldtype": "Link",
    "label": "Company Shipping Address",
    "options": "Address",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "shipping_address_display",
    "fieldtype": "Small Text",
    "label": "Shipping Address Details",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "currency_and_price_list",
    "fieldtype": "Section Break",
    "label": "Currency and Price List",
-   "options": "fa fa-tag"
+   "options": "fa fa-tag",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "currency",
@@ -390,7 +450,9 @@
    "oldfieldtype": "Select",
    "options": "Currency",
    "print_hide": 1,
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "conversion_rate",
@@ -400,18 +462,24 @@
    "oldfieldtype": "Currency",
    "precision": "9",
    "print_hide": 1,
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "cb_price_list",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "buying_price_list",
    "fieldtype": "Link",
    "label": "Price List",
    "options": "Price List",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "price_list_currency",
@@ -419,14 +487,18 @@
    "label": "Price List Currency",
    "options": "Currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "plc_conversion_rate",
    "fieldtype": "Float",
    "label": "Price List Exchange Rate",
    "precision": "9",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
@@ -435,7 +507,9 @@
    "label": "Ignore Pricing Rule",
    "no_copy": 1,
    "permlevel": 1,
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "sec_warehouse",
@@ -448,11 +522,15 @@
    "fieldtype": "Link",
    "label": "Set Target Warehouse",
    "options": "Warehouse",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "col_break_warehouse",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "No",
@@ -461,26 +539,34 @@
    "in_standard_filter": 1,
    "label": "Supply Raw Materials",
    "options": "No\nYes",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
    "fieldname": "supplier_warehouse",
    "fieldtype": "Link",
    "label": "Supplier Warehouse",
-   "options": "Warehouse"
+   "options": "Warehouse",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "items_section",
    "fieldtype": "Section Break",
    "hide_border": 1,
    "oldfieldtype": "Section Break",
-   "options": "fa fa-shopping-cart"
+   "options": "fa fa-shopping-cart",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "scan_barcode",
    "fieldtype": "Data",
-   "label": "Scan Barcode"
+   "label": "Scan Barcode",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_bulk_edit": 1,
@@ -490,46 +576,61 @@
    "oldfieldname": "po_details",
    "oldfieldtype": "Table",
    "options": "Purchase Order Item",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "section_break_48",
    "fieldtype": "Section Break",
-   "label": "Pricing Rules"
+   "label": "Pricing Rules",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "pricing_rules",
    "fieldtype": "Table",
    "label": "Purchase Order Pricing Rule",
    "options": "Pricing Rule Detail",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible_depends_on": "supplied_items",
    "fieldname": "raw_material_details",
    "fieldtype": "Section Break",
-   "label": "Raw Materials Supplied"
+   "label": "Raw Materials Supplied",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "supplied_items",
    "fieldtype": "Table",
    "label": "Supplied Items",
+   "no_copy": 1,
    "oldfieldname": "po_raw_material_details",
    "oldfieldtype": "Table",
    "options": "Purchase Order Item Supplied",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "sb_last_purchase",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "total_qty",
    "fieldtype": "Float",
    "label": "Total Quantity",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "base_total",
@@ -537,7 +638,9 @@
    "label": "Total (Company Currency)",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "base_net_total",
@@ -548,18 +651,24 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_26",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "total",
    "fieldtype": "Currency",
    "label": "Total",
    "options": "currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "net_total",
@@ -569,20 +678,26 @@
    "oldfieldtype": "Currency",
    "options": "currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "total_net_weight",
    "fieldtype": "Float",
    "label": "Total Net Weight",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "taxes_section",
    "fieldtype": "Section Break",
    "oldfieldtype": "Section Break",
-   "options": "fa fa-money"
+   "options": "fa fa-money",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "taxes_and_charges",
@@ -591,18 +706,24 @@
    "oldfieldname": "purchase_other_charges",
    "oldfieldtype": "Link",
    "options": "Purchase Taxes and Charges Template",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_50",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "shipping_rule",
    "fieldtype": "Link",
    "label": "Shipping Rule",
    "options": "Shipping Rule",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "section_break_52",
@@ -615,13 +736,17 @@
    "label": "Purchase Taxes and Charges",
    "oldfieldname": "purchase_tax_details",
    "oldfieldtype": "Table",
-   "options": "Purchase Taxes and Charges"
+   "options": "Purchase Taxes and Charges",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "sec_tax_breakup",
    "fieldtype": "Section Break",
-   "label": "Tax Breakup"
+   "label": "Tax Breakup",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "other_charges_calculation",
@@ -630,14 +755,18 @@
    "no_copy": 1,
    "oldfieldtype": "HTML",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "totals",
    "fieldtype": "Section Break",
    "label": "Taxes and Charges",
    "oldfieldtype": "Section Break",
-   "options": "fa fa-money"
+   "options": "fa fa-money",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "base_taxes_and_charges_added",
@@ -648,7 +777,9 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "base_taxes_and_charges_deducted",
@@ -659,7 +790,9 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "base_total_taxes_and_charges",
@@ -671,11 +804,15 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_39",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "taxes_and_charges_added",
@@ -686,7 +823,9 @@
    "oldfieldtype": "Currency",
    "options": "currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "taxes_and_charges_deducted",
@@ -697,7 +836,9 @@
    "oldfieldtype": "Currency",
    "options": "currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "total_taxes_and_charges",
@@ -706,14 +847,18 @@
    "label": "Total Taxes and Charges",
    "options": "currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "collapsible_depends_on": "apply_discount_on",
    "fieldname": "discount_section",
    "fieldtype": "Section Break",
-   "label": "Additional Discount"
+   "label": "Additional Discount",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "Grand Total",
@@ -721,7 +866,9 @@
    "fieldtype": "Select",
    "label": "Apply Additional Discount On",
    "options": "\nGrand Total\nNet Total",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "base_discount_amount",
@@ -729,24 +876,32 @@
    "label": "Additional Discount Amount (Company Currency)",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_45",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "additional_discount_percentage",
    "fieldtype": "Float",
    "label": "Additional Discount Percentage",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "discount_amount",
    "fieldtype": "Currency",
    "label": "Additional Discount Amount",
    "options": "currency",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "totals_section",
@@ -762,16 +917,21 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "base_rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment (Company Currency)",
    "no_copy": 1,
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "description": "In Words will be visible once you save the Purchase Order.",
@@ -782,7 +942,9 @@
    "oldfieldname": "in_words",
    "oldfieldtype": "Data",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "base_rounded_total",
@@ -792,12 +954,16 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break4",
    "fieldtype": "Column Break",
-   "oldfieldtype": "Column Break"
+   "oldfieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "grand_total",
@@ -807,29 +973,38 @@
    "oldfieldname": "grand_total_import",
    "oldfieldtype": "Currency",
    "options": "currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment",
    "no_copy": 1,
    "options": "currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "rounded_total",
    "fieldtype": "Currency",
    "label": "Rounded Total",
    "options": "currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "disable_rounded_total",
    "fieldtype": "Check",
-   "label": "Disable Rounded Total"
+   "label": "Disable Rounded Total",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "in_words",
@@ -839,7 +1014,9 @@
    "oldfieldname": "in_words_import",
    "oldfieldtype": "Data",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "advance_paid",
@@ -848,19 +1025,25 @@
    "no_copy": 1,
    "options": "party_account_currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "payment_schedule_section",
    "fieldtype": "Section Break",
-   "label": "Payment Terms"
+   "label": "Payment Terms",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "payment_terms_template",
    "fieldtype": "Link",
    "label": "Payment Terms Template",
-   "options": "Payment Terms Template"
+   "options": "Payment Terms Template",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "payment_schedule",
@@ -868,7 +1051,9 @@
    "label": "Payment Schedule",
    "no_copy": 1,
    "options": "Payment Schedule",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
@@ -877,7 +1062,9 @@
    "fieldtype": "Section Break",
    "label": "Terms and Conditions",
    "oldfieldtype": "Section Break",
-   "options": "fa fa-legal"
+   "options": "fa fa-legal",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "tc_name",
@@ -886,21 +1073,27 @@
    "oldfieldname": "tc_name",
    "oldfieldtype": "Link",
    "options": "Terms and Conditions",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "terms",
    "fieldtype": "Text Editor",
    "label": "Terms and Conditions",
    "oldfieldname": "terms",
-   "oldfieldtype": "Text Editor"
+   "oldfieldtype": "Text Editor",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "more_info",
    "fieldtype": "Section Break",
    "label": "More Information",
-   "oldfieldtype": "Section Break"
+   "oldfieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "Draft",
@@ -915,7 +1108,9 @@
    "print_hide": 1,
    "read_only": 1,
    "reqd": 1,
-   "search_index": 1
+   "search_index": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "ref_sq",
@@ -926,7 +1121,9 @@
    "oldfieldtype": "Data",
    "options": "Supplier Quotation",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "party_account_currency",
@@ -936,18 +1133,24 @@
    "no_copy": 1,
    "options": "Currency",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "inter_company_order_reference",
    "fieldtype": "Link",
    "label": "Inter Company Order Reference",
    "options": "Sales Order",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_74",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:!doc.__islocal",
@@ -957,7 +1160,9 @@
    "label": "% Received",
    "no_copy": 1,
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:!doc.__islocal",
@@ -967,7 +1172,9 @@
    "label": "% Billed",
    "no_copy": 1,
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
@@ -977,6 +1184,8 @@
    "oldfieldtype": "Column Break",
    "print_hide": 1,
    "print_width": "50%",
+   "show_days": 1,
+   "show_seconds": 1,
    "width": "50%"
   },
   {
@@ -987,7 +1196,9 @@
    "oldfieldname": "letter_head",
    "oldfieldtype": "Select",
    "options": "Letter Head",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
@@ -999,11 +1210,15 @@
    "oldfieldtype": "Link",
    "options": "Print Heading",
    "print_hide": 1,
-   "report_hide": 1
+   "report_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_86",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
@@ -1011,19 +1226,25 @@
    "fieldname": "group_same_items",
    "fieldtype": "Check",
    "label": "Group same items",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "language",
    "fieldtype": "Data",
    "label": "Print Language",
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
    "fieldname": "subscription_section",
    "fieldtype": "Section Break",
-   "label": "Subscription Section"
+   "label": "Subscription Section",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
@@ -1031,7 +1252,9 @@
    "fieldtype": "Date",
    "label": "From Date",
    "no_copy": 1,
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
@@ -1039,11 +1262,15 @@
    "fieldtype": "Date",
    "label": "To Date",
    "no_copy": 1,
-   "print_hide": 1
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_97",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "auto_repeat",
@@ -1052,27 +1279,35 @@
    "no_copy": 1,
    "options": "Auto Repeat",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "allow_on_submit": 1,
    "depends_on": "eval: doc.auto_repeat",
    "fieldname": "update_auto_repeat_reference",
    "fieldtype": "Button",
-   "label": "Update Auto Repeat Reference"
+   "label": "Update Auto Repeat Reference",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "tax_category",
    "fieldtype": "Link",
    "label": "Tax Category",
-   "options": "Tax Category"
+   "options": "Tax Category",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "supplied_items",
    "fieldname": "set_reserve_warehouse",
    "fieldtype": "Link",
    "label": "Set Reserve Warehouse",
-   "options": "Warehouse"
+   "options": "Warehouse",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "collapsible": 1,
@@ -1082,7 +1317,9 @@
   },
   {
    "fieldname": "column_break_75",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "billing_address",
@@ -1118,13 +1355,30 @@
    "label": "Represents Company",
    "options": "Company",
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "apply_tds",
+   "fieldtype": "Check",
+   "label": "Apply Tax Withholding Amount",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "eval: doc.apply_tds",
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "label": "Tax Withholding Category",
+   "options": "Tax Withholding Category",
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-01-20 22:07:23.487138",
+ "modified": "2021-05-30 15:17:53.663648",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index ef9372e..eaa502f 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -14,11 +14,11 @@
 from erpnext.buying.utils import validate_for_items, check_on_hold_or_closed_status
 from erpnext.stock.utils import get_bin
 from erpnext.accounts.party import get_party_account_currency
-from six import string_types
 from erpnext.stock.doctype.item.item import get_item_defaults
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
-from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
-	unlink_inter_company_doc
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_party,
+	update_linked_doc, unlink_inter_company_doc)
 
 form_grid_templates = {
 	"items": "templates/form_grid/item_grid.html"
@@ -39,11 +39,18 @@
 			'percent_join_field': 'material_request'
 		}]
 
+	def onload(self):
+		supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
+		self.set_onload("supplier_tds", supplier_tds)
+
 	def validate(self):
 		super(PurchaseOrder, self).validate()
 
 		self.set_status()
 
+		# apply tax withholding only if checked and applicable
+		self.set_tax_withholding()
+
 		self.validate_supplier()
 		self.validate_schedule_date()
 		validate_for_items(self)
@@ -87,6 +94,33 @@
 		if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
 			self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]])
 
+	def set_tax_withholding(self):
+		if not self.apply_tds:
+			return
+
+		tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
+
+		if not tax_withholding_details:
+			return
+
+		accounts = []
+		for d in self.taxes:
+			if d.account_head == tax_withholding_details.get("account_head"):
+				d.update(tax_withholding_details)
+			accounts.append(d.account_head)
+
+		if not accounts or tax_withholding_details.get("account_head") not in accounts:
+			self.append("taxes", tax_withholding_details)
+
+		to_remove = [d for d in self.taxes
+			if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
+
+		for d in to_remove:
+			self.remove(d)
+
+		# calculate totals again after applying TDS
+		self.calculate_taxes_and_totals()
+
 	def validate_supplier(self):
 		prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
 		if prevent_po:
@@ -104,7 +138,7 @@
 
 	def validate_minimum_order_qty(self):
 		if not self.get("items"): return
-		items = list(set([d.item_code for d in self.get("items")]))
+		items = list(set(d.item_code for d in self.get("items")))
 
 		itemwise_min_order_qty = frappe._dict(frappe.db.sql("""select name, min_order_qty
 			from tabItem where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
@@ -291,10 +325,10 @@
 			so.notify_update()
 
 	def has_drop_ship_item(self):
-		return any([d.delivered_by_supplier for d in self.items])
+		return any(d.delivered_by_supplier for d in self.items)
 
 	def is_against_so(self):
-		return any([d.sales_order for d in self.items if d.sales_order])
+		return any(d.sales_order for d in self.items if d.sales_order)
 
 	def set_received_qty_for_drop_ship_items(self):
 		for item in self.items:
@@ -468,9 +502,11 @@
 
 @frappe.whitelist()
 def make_rm_stock_entry(purchase_order, rm_items):
-	if isinstance(rm_items, string_types):
+	rm_items_list = rm_items
+
+	if isinstance(rm_items, str):
 		rm_items_list = json.loads(rm_items)
-	else:
+	elif not rm_items:
 		frappe.throw(_("No Items available for transfer"))
 
 	if rm_items_list:
@@ -508,6 +544,8 @@
 							'qty': rm_item_data["qty"],
 							'from_warehouse': rm_item_data["warehouse"],
 							'stock_uom': rm_item_data["stock_uom"],
+							'serial_no': rm_item_data.get('serial_no'),
+							'batch_no': rm_item_data.get('batch_no'),
 							'main_item_code': rm_item_data["item_code"],
 							'allow_alternative_item': item_wh.get(rm_item_code, {}).get('allow_alternative_item')
 						}
@@ -547,3 +585,58 @@
 def make_inter_company_sales_order(source_name, target_doc=None):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
 	return make_inter_company_transaction("Purchase Order", source_name, target_doc)
+
+@frappe.whitelist()
+def get_materials_from_supplier(purchase_order, po_details):
+	if isinstance(po_details, str):
+		po_details = json.loads(po_details)
+
+	doc = frappe.get_cached_doc('Purchase Order', purchase_order)
+	doc.initialized_fields()
+	doc.purchase_orders = [doc.name]
+	doc.get_available_materials()
+
+	if not doc.available_materials:
+		frappe.throw(_('Materials are already received against the purchase order {0}')
+			.format(purchase_order))
+
+	return make_return_stock_entry_for_subcontract(doc.available_materials, doc, po_details)
+
+def make_return_stock_entry_for_subcontract(available_materials, po_doc, po_details):
+	ste_doc = frappe.new_doc('Stock Entry')
+	ste_doc.purpose = 'Material Transfer'
+	ste_doc.purchase_order = po_doc.name
+	ste_doc.company = po_doc.company
+	ste_doc.is_return = 1
+
+	for key, value in available_materials.items():
+		if not value.qty:
+			continue
+
+		if value.batch_no:
+			for batch_no, qty in value.batch_no.items():
+				if qty > 0:
+					add_items_in_ste(ste_doc, value, value.qty, po_details, batch_no)
+		else:
+			add_items_in_ste(ste_doc, value, value.qty, po_details)
+
+	ste_doc.set_stock_entry_type()
+	ste_doc.calculate_rate_and_amount()
+
+	return ste_doc
+
+def add_items_in_ste(ste_doc, row, qty, po_details, batch_no=None):
+	item = ste_doc.append('items', row.item_details)
+
+	po_detail = list(set(row.po_details).intersection(po_details))
+	item.update({
+		'qty': qty,
+		'batch_no': batch_no,
+		'basic_rate': row.item_details['rate'],
+		'po_detail': po_detail[0] if po_detail else '',
+		's_warehouse': row.item_details['t_warehouse'],
+		't_warehouse': row.item_details['s_warehouse'],
+		'item_code': row.item_details['rm_item_code'],
+		'subcontracted_item': row.item_details['main_item_code'],
+		'serial_no': '\n'.join(row.serial_no) if row.serial_no else ''
+	})
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index aaa98f2..8563b97 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -20,7 +20,6 @@
 from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
 
 from erpnext.stock.doctype.batch.test_batch import make_new_batch
-from erpnext.controllers.buying_controller import get_backflushed_subcontracted_raw_materials
 
 class TestPurchaseOrder(unittest.TestCase):
 	def test_make_purchase_receipt(self):
@@ -359,7 +358,7 @@
 		update_child_qty_rate('Purchase Order', trans_item, po.name)
 		po.reload()
 
-		total_reqd_qty_after_change = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
+		total_reqd_qty_after_change = sum(d.get("required_qty") for d in po.as_dict().get("supplied_items"))
 
 		self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty)
 
@@ -771,7 +770,7 @@
 		self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 	def test_exploded_items_in_subcontracted(self):
-		item_code = "_Test Subcontracted FG Item 1"
+		item_code = "_Test Subcontracted FG Item 11"
 		make_subcontracted_item(item_code=item_code)
 
 		po = create_purchase_order(item_code=item_code, qty=1,
@@ -848,79 +847,6 @@
 		for item in rm_items:
 			transferred_rm_map[item.get('rm_item_code')] = item
 
-		for item in pr.get('supplied_items'):
-			self.assertEqual(item.get('required_qty'), (transferred_rm_map[item.get('rm_item_code')].get('qty') / order_qty) * received_qty)
-
-		update_backflush_based_on("BOM")
-
-	def test_backflushed_based_on_for_multiple_batches(self):
-		item_code = "_Test Subcontracted FG Item 2"
-		make_item('Sub Contracted Raw Material 2', {
-			'is_stock_item': 1,
-			'is_sub_contracted_item': 1
-		})
-
-		make_subcontracted_item(item_code=item_code, has_batch_no=1, create_new_batch=1,
-			raw_materials=["Sub Contracted Raw Material 2"])
-
-		update_backflush_based_on("Material Transferred for Subcontract")
-
-		order_qty = 500
-		po = create_purchase_order(item_code=item_code, qty=order_qty,
-			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
-
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Sub Contracted Raw Material 2", qty=552, basic_rate=100)
-
-		rm_items = [
-			{"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 2","item_name":"_Test Item",
-				"qty":552,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}]
-
-		rm_item_string = json.dumps(rm_items)
-		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
-		se.submit()
-
-		for batch in ["ABCD1", "ABCD2", "ABCD3", "ABCD4"]:
-			make_new_batch(batch_id=batch, item_code=item_code)
-
-		pr = make_purchase_receipt(po.name)
-
-		# partial receipt
-		pr.get('items')[0].qty = 30
-		pr.get('items')[0].batch_no = "ABCD1"
-
-		purchase_order = po.name
-		purchase_order_item = po.items[0].name
-
-		for batch_no, qty in {"ABCD2": 60, "ABCD3": 70, "ABCD4":40}.items():
-			pr.append("items", {
-				"item_code": pr.get('items')[0].item_code,
-				"item_name": pr.get('items')[0].item_name,
-				"uom": pr.get('items')[0].uom,
-				"stock_uom": pr.get('items')[0].stock_uom,
-				"warehouse": pr.get('items')[0].warehouse,
-				"conversion_factor": pr.get('items')[0].conversion_factor,
-				"cost_center": pr.get('items')[0].cost_center,
-				"rate": pr.get('items')[0].rate,
-				"qty": qty,
-				"batch_no": batch_no,
-				"purchase_order": purchase_order,
-				"purchase_order_item": purchase_order_item
-			})
-
-		pr.submit()
-
-		pr1 = make_purchase_receipt(po.name)
-		pr1.get('items')[0].qty = 300
-		pr1.get('items')[0].batch_no = "ABCD1"
-		pr1.save()
-
-		pr_key = ("Sub Contracted Raw Material 2", po.name)
-		consumed_qty = get_backflushed_subcontracted_raw_materials([po.name]).get(pr_key)
-
-		self.assertTrue(pr1.supplied_items[0].consumed_qty > 0)
-		self.assertTrue(pr1.supplied_items[0].consumed_qty,  flt(552.0) - flt(consumed_qty))
-
 		update_backflush_based_on("BOM")
 
 	def test_supplied_qty_against_subcontracted_po(self):
@@ -1111,28 +1037,35 @@
 
 	po.schedule_date = add_days(nowdate(), 1)
 	po.company = args.company or "_Test Company"
-	po.supplier = args.customer or "_Test Supplier"
+	po.supplier = args.supplier or "_Test Supplier"
 	po.is_subcontracted = args.is_subcontracted or "No"
 	po.currency = args.currency or frappe.get_cached_value('Company',  po.company,  "default_currency")
 	po.conversion_factor = args.conversion_factor or 1
 	po.supplier_warehouse = args.supplier_warehouse or None
 
-	po.append("items", {
-		"item_code": args.item or args.item_code or "_Test Item",
-		"warehouse": args.warehouse or "_Test Warehouse - _TC",
-		"qty": args.qty or 10,
-		"rate": args.rate or 500,
-		"schedule_date": add_days(nowdate(), 1),
-		"include_exploded_items": args.get('include_exploded_items', 1),
-		"against_blanket_order": args.against_blanket_order
-	})
+	if args.rm_items:
+		for row in args.rm_items:
+			po.append("items", row)
+	else:
+		po.append("items", {
+			"item_code": args.item or args.item_code or "_Test Item",
+			"warehouse": args.warehouse or "_Test Warehouse - _TC",
+			"qty": args.qty or 10,
+			"rate": args.rate or 500,
+			"schedule_date": add_days(nowdate(), 1),
+			"include_exploded_items": args.get('include_exploded_items', 1),
+			"against_blanket_order": args.against_blanket_order
+		})
+
+	po.set_missing_values()
 	if not args.do_not_save:
 		po.insert()
 		if not args.do_not_submit:
 			if po.is_subcontracted == "Yes":
 				supp_items = po.get("supplied_items")
 				for d in supp_items:
-					d.reserve_warehouse = args.warehouse or "_Test Warehouse - _TC"
+					if not d.reserve_warehouse:
+						d.reserve_warehouse = args.warehouse or "_Test Warehouse - _TC"
 			po.submit()
 
 	return po
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/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
index d7ea9c1..60247bd 100644
--- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
+++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
@@ -6,21 +6,25 @@
  "engine": "InnoDB",
  "field_order": [
   "main_item_code",
-  "bom_detail_no",
+  "rm_item_code",
+  "column_break_3",
   "stock_uom",
+  "reserve_warehouse",
   "conversion_factor",
   "column_break_6",
-  "rm_item_code",
+  "bom_detail_no",
   "reference_name",
-  "reserve_warehouse",
   "section_break2",
   "rate",
   "col_break2",
   "amount",
   "section_break1",
   "required_qty",
+  "supplied_qty",
   "col_break1",
-  "supplied_qty"
+  "consumed_qty",
+  "returned_qty",
+  "total_supplied_qty"
  ],
  "fields": [
   {
@@ -125,6 +129,8 @@
    "fieldtype": "Float",
    "in_list_view": 1,
    "label": "Supplied Qty",
+   "no_copy": 1,
+   "print_hide": 1,
    "read_only": 1
   },
   {
@@ -142,13 +148,42 @@
   {
    "fieldname": "col_break2",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "consumed_qty",
+   "fieldtype": "Float",
+   "label": "Consumed Qty",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "returned_qty",
+   "fieldtype": "Float",
+   "label": "Returned Qty",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_supplied_qty",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Total Supplied Qty",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
   }
  ],
  "hide_toolbar": 1,
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-06-09 15:17:58.128242",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item Supplied",
diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
index dc00bca..f9cd720 100644
--- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
+++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
@@ -6,10 +6,11 @@
  "engine": "InnoDB",
  "field_order": [
   "main_item_code",
-  "description",
+  "rm_item_code",
+  "item_name",
   "bom_detail_no",
   "col_break1",
-  "rm_item_code",
+  "description",
   "stock_uom",
   "conversion_factor",
   "reference_name",
@@ -25,7 +26,8 @@
   "secbreak_3",
   "batch_no",
   "col_break4",
-  "serial_no"
+  "serial_no",
+  "purchase_order"
  ],
  "fields": [
   {
@@ -52,7 +54,6 @@
    "fieldname": "description",
    "fieldtype": "Text Editor",
    "in_global_search": 1,
-   "in_list_view": 1,
    "label": "Description",
    "oldfieldname": "description",
    "oldfieldtype": "Data",
@@ -81,18 +82,20 @@
    "fieldname": "required_qty",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Required Qty",
+   "label": "Available Qty For Consumption",
    "oldfieldname": "required_qty",
    "oldfieldtype": "Currency",
+   "print_hide": 1,
    "read_only": 1
   },
   {
+   "columns": 2,
    "fieldname": "consumed_qty",
    "fieldtype": "Float",
-   "label": "Consumed Qty",
+   "in_list_view": 1,
+   "label": "Qty to Be Consumed",
    "oldfieldname": "consumed_qty",
    "oldfieldtype": "Currency",
-   "read_only": 1,
    "reqd": 1
   },
   {
@@ -183,12 +186,28 @@
   {
    "fieldname": "col_break4",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "purchase_order",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Purchase Order",
+   "no_copy": 1,
+   "options": "Purchase Order",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-06-19 19:33:04.431213",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Receipt Item Supplied",
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index 180ba93..a4ce84e 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -317,19 +317,21 @@
 			create_rfq_items(sq_doc, supplier, data)
 
 def create_rfq_items(sq_doc, supplier, data):
-	sq_doc.append('items', {
-		"item_code": data.item_code,
-		"item_name": data.item_name,
-		"description": data.description,
-		"qty": data.qty,
-		"rate": data.rate,
-		"conversion_factor": data.conversion_factor if data.conversion_factor else None,
-		"supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"),
-		"warehouse": data.warehouse or '',
+	args = {}
+
+	for field in ['item_code', 'item_name', 'description', 'qty', 'rate', 'conversion_factor',
+		'warehouse', 'material_request', 'material_request_item', 'stock_qty']:
+		args[field] = data.get(field)
+
+	args.update({
 		"request_for_quotation_item": data.name,
-		"request_for_quotation": data.parent
+		"request_for_quotation": data.parent,
+		"supplier_part_no": frappe.db.get_value("Item Supplier",
+			{'parent': data.item_code, 'supplier': supplier}, "supplier_part_no")
 	})
 
+	sq_doc.append('items', args)
+
 @frappe.whitelist()
 def get_pdf(doctype, name, supplier):
 	doc = get_rfq_doc(doctype, name, supplier)
@@ -391,7 +393,7 @@
 def get_supplier_tag():
 	if not frappe.cache().hget("Supplier", "Tags"):
 		filters = {"document_type": "Supplier"}
-		tags = list(set([tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag]))
+		tags = list(set(tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag))
 		frappe.cache().hset("Supplier", "Tags", tags)
 
 	return frappe.cache().hget("Supplier", "Tags")
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 40fbe2c..0a51a8e 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -576,6 +576,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "base_rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment (Company Currency",
@@ -620,6 +621,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment",
@@ -802,7 +804,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-03 15:18:29.073368",
+ "modified": "2021-04-19 00:58:20.995491",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/buying/report/subcontract_order_summary/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/buying/report/subcontract_order_summary/__init__.py
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
new file mode 100644
index 0000000..5ba52f1
--- /dev/null
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Subcontract Order Summary"] = {
+	"filters": [
+		{
+			label: __("Company"),
+			fieldname: "company",
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			label: __("From Date"),
+			fieldname:"from_date",
+			fieldtype: "Date",
+			default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+			reqd: 1
+		},
+		{
+			label: __("To Date"),
+			fieldname:"to_date",
+			fieldtype: "Date",
+			default: frappe.datetime.get_today(),
+			reqd: 1
+		},
+		{
+			label: __("Purchase Order"),
+			fieldname: "name",
+			fieldtype: "Link",
+			options: "Purchase Order",
+			get_query: function() {
+				return {
+					filters: {
+						docstatus: 1,
+						is_subcontracted: 'Yes',
+						company: frappe.query_report.get_filter_value('company')
+					}
+				}
+			}
+		}
+	]
+};
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json
new file mode 100644
index 0000000..526a8d8
--- /dev/null
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-05-31 14:43:32.417694",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-05-31 14:43:32.417694",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Subcontract Order Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Purchase Order",
+ "report_name": "Subcontract Order Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Purchase Manager"
+  },
+  {
+   "role": "Purchase User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
new file mode 100644
index 0000000..0c0d4f0
--- /dev/null
+++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py
@@ -0,0 +1,152 @@
+# 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 import _
+
+def execute(filters=None):
+	columns, data = [], []
+	columns = get_columns()
+	data = get_data(filters)
+
+	return columns, data
+
+def get_data(report_filters):
+	data = []
+	orders = get_subcontracted_orders(report_filters)
+
+	if orders:
+		supplied_items = get_supplied_items(orders, report_filters)
+		po_details = prepare_subcontracted_data(orders, supplied_items)
+		get_subcontracted_data(po_details, data)
+
+	return data
+
+def get_subcontracted_orders(report_filters):
+	fields = ['`tabPurchase Order Item`.`parent` as po_id', '`tabPurchase Order Item`.`item_code`',
+		'`tabPurchase Order Item`.`item_name`', '`tabPurchase Order Item`.`qty`', '`tabPurchase Order Item`.`name`',
+		'`tabPurchase Order Item`.`received_qty`', '`tabPurchase Order`.`status`']
+
+	filters = get_filters(report_filters)
+
+	return frappe.get_all('Purchase Order', fields = fields, filters=filters) or []
+
+def get_filters(report_filters):
+	filters = [['Purchase Order', 'docstatus', '=', 1], ['Purchase Order', 'is_subcontracted', '=', 'Yes'],
+		['Purchase Order', 'transaction_date', 'between', (report_filters.from_date, report_filters.to_date)]]
+
+	for field in ['name', 'company']:
+		if report_filters.get(field):
+			filters.append(['Purchase Order', field, '=', report_filters.get(field)])
+
+	return filters
+
+def get_supplied_items(orders, report_filters):
+	if not orders:
+		return []
+
+	fields = ['parent', 'main_item_code', 'rm_item_code', 'required_qty',
+		'supplied_qty', 'returned_qty', 'total_supplied_qty', 'consumed_qty', 'reference_name']
+
+	filters = {'parent': ('in', [d.po_id for d in orders]), 'docstatus': 1}
+
+	supplied_items = {}
+	for row in frappe.get_all('Purchase Order Item Supplied', fields = fields, filters=filters):
+		new_key = (row.parent, row.reference_name, row.main_item_code)
+
+		supplied_items.setdefault(new_key, []).append(row)
+
+	return supplied_items
+
+def prepare_subcontracted_data(orders, supplied_items):
+	po_details = {}
+	for row in orders:
+		key = (row.po_id, row.name, row.item_code)
+		if key not in po_details:
+			po_details.setdefault(key, frappe._dict({'po_item': row, 'supplied_items': []}))
+
+		details = po_details[key]
+
+		if supplied_items.get(key):
+			for supplied_item in supplied_items[key]:
+				details['supplied_items'].append(supplied_item)
+
+	return po_details
+
+def get_subcontracted_data(po_details, data):
+	for key, details in po_details.items():
+		res = details.po_item
+		for index, row in enumerate(details.supplied_items):
+			if index != 0:
+				res = {}
+
+			res.update(row)
+			data.append(res)
+
+def get_columns():
+	return [
+		{
+			"label": _("Purchase Order"),
+			"fieldname": "po_id",
+			"fieldtype": "Link",
+			"options": "Purchase Order",
+			"width": 100
+		},
+		{
+			"label": _("Status"),
+			"fieldname": "status",
+			"fieldtype": "Data",
+			"width": 80
+		},
+		{
+			"label": _("Subcontracted Item"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 160
+		},
+		{
+			"label": _("Order Qty"),
+			"fieldname": "qty",
+			"fieldtype": "Float",
+			"width": 90
+		},
+		{
+			"label": _("Received Qty"),
+			"fieldname": "received_qty",
+			"fieldtype": "Float",
+			"width": 110
+		},
+		{
+			"label": _("Supplied Item"),
+			"fieldname": "rm_item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 160
+		},
+		{
+			"label": _("Required Qty"),
+			"fieldname": "required_qty",
+			"fieldtype": "Float",
+			"width": 110
+		},
+		{
+			"label": _("Supplied Qty"),
+			"fieldname": "supplied_qty",
+			"fieldtype": "Float",
+			"width": 110
+		},
+		{
+			"label": _("Consumed Qty"),
+			"fieldname": "consumed_qty",
+			"fieldtype": "Float",
+			"width": 120
+		},
+		{
+			"label": _("Returned Qty"),
+			"fieldname": "returned_qty",
+			"fieldtype": "Float",
+			"width": 110
+		}
+	]
\ No newline at end of file
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
index de2ae8f..68426ab 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py
@@ -9,10 +9,10 @@
 	if filters.from_date >= filters.to_date:
 		frappe.msgprint(_("To Date must be greater than From Date"))
 
-	data = []
 	columns = get_columns()
-	get_data(data , filters)
-	return columns, data
+	data = get_data(filters)
+
+	return columns, data or []
 
 def get_columns():
 	return [
@@ -21,13 +21,12 @@
 			"fieldtype": "Link",
 			"fieldname": "purchase_order",
 			"options": "Purchase Order",
-			"width": 150
+			"width": 200
 		},
 		{
 			"label": _("Date"),
 			"fieldtype": "Date",
 			"fieldname": "date",
-			"hidden": 1,
 			"width": 150
 		},
 		{
@@ -41,97 +40,58 @@
 			"label": _("Item Code"),
 			"fieldtype": "Data",
 			"fieldname": "rm_item_code",
-			"width": 100
+			"width": 150
 		},
 		{
 			"label": _("Required Quantity"),
 			"fieldtype": "Float",
-			"fieldname": "r_qty",
-			"width": 100
+			"fieldname": "reqd_qty",
+			"width": 150
 		},
 		{
 			"label": _("Transferred Quantity"),
 			"fieldtype": "Float",
-			"fieldname": "t_qty",
-			"width": 100
+			"fieldname": "transferred_qty",
+			"width": 200
 		},
 		{
 			"label": _("Pending Quantity"),
 			"fieldtype": "Float",
 			"fieldname": "p_qty",
-			"width": 100
+			"width": 150
 		}
 	]
 
-def get_data(data, filters):
-	po = get_po(filters)
-	po_transferred_qty_map = frappe._dict(get_transferred_quantity([v.name for v in po]))
+def get_data(filters):
+	po_rm_item_details = get_po_items_to_supply(filters)
 
-	sub_items = get_purchase_order_item_supplied([v.name for v in po])
+	data = []
+	for row in po_rm_item_details:
+		transferred_qty = row.get("transferred_qty") or 0
+		if transferred_qty < row.get("reqd_qty", 0):
+			pending_qty = frappe.utils.flt(row.get("reqd_qty", 0) - transferred_qty)
+			row.p_qty = pending_qty if pending_qty > 0 else 0
+			data.append(row)
 
-	for order in po:
-		for item in sub_items:
-			if order.name == item.parent and order.name in po_transferred_qty_map and \
-				item.required_qty != po_transferred_qty_map.get(order.name).get(item.rm_item_code):
-				transferred_qty = po_transferred_qty_map.get(order.name).get(item.rm_item_code) \
-				if po_transferred_qty_map.get(order.name).get(item.rm_item_code) else 0
-				row ={
-					'purchase_order': item.parent,
-					'date': order.transaction_date,
-					'supplier': order.supplier,
-					'rm_item_code': item.rm_item_code,
-					'r_qty': item.required_qty,
-					't_qty':transferred_qty,
-					'p_qty':item.required_qty - transferred_qty
-				}
+	return data
 
-				data.append(row)
-
-	return(data)
-
-def get_po(filters):
-	record_filters = [
-			["is_subcontracted", "=", "Yes"],
-			["supplier", "=", filters.supplier],
-			["transaction_date", "<=", filters.to_date],
-			["transaction_date", ">=", filters.from_date],
-			["docstatus", "=", 1]
-		]
-	return frappe.get_all("Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"])
-
-def get_transferred_quantity(po_name):
-	stock_entries = get_stock_entry(po_name)
-	stock_entries_detail = get_stock_entry_detail([v.name for v in stock_entries])
-	po_transferred_qty_map = {}
-
-
-	for entry in stock_entries:
-		for details in stock_entries_detail:
-			if details.parent == entry.name:
-				details["Purchase_order"] = entry.purchase_order
-				if entry.purchase_order not in po_transferred_qty_map:
-					po_transferred_qty_map[entry.purchase_order] = {}
-					po_transferred_qty_map[entry.purchase_order][details.item_code] = details.qty
-				else:
-					po_transferred_qty_map[entry.purchase_order][details.item_code] = po_transferred_qty_map[entry.purchase_order].get(details.item_code, 0) + details.qty
-
-	return po_transferred_qty_map
-
-
-def get_stock_entry(po):
-	return frappe.get_all("Stock Entry", filters=[
-			('purchase_order', 'IN', po),
-			('stock_entry_type', '=', 'Send to Subcontractor'),
-			('docstatus', '=', 1)
-	], fields=["name", "purchase_order"])
-
-def get_stock_entry_detail(se):
-	return frappe.get_all("Stock Entry Detail", filters=[
-			["parent", "in", se]
+def get_po_items_to_supply(filters):
+	return frappe.db.get_all(
+		"Purchase Order",
+		fields=[
+			"name as purchase_order",
+			"transaction_date as date",
+			"supplier as supplier",
+			"`tabPurchase Order Item Supplied`.rm_item_code as rm_item_code",
+			"`tabPurchase Order Item Supplied`.required_qty as reqd_qty",
+			"`tabPurchase Order Item Supplied`.supplied_qty as transferred_qty"
 		],
-		fields=["parent", "item_code", "qty"])
-
-def get_purchase_order_item_supplied(po):
-	return frappe.get_all("Purchase Order Item Supplied", filters=[
-			('parent', 'IN', po)
-	], fields=['parent', 'rm_item_code', 'required_qty'])
+		filters = [
+			["Purchase Order", "per_received", "<", "100"],
+			["Purchase Order", "is_subcontracted", "=", "Yes"],
+			["Purchase Order", "supplier", "=", filters.supplier],
+			["Purchase Order", "transaction_date", "<=", filters.to_date],
+			["Purchase Order", "transaction_date", ">=", filters.from_date],
+			["Purchase Order", "docstatus", "=", 1]
+		]
+	)
\ No newline at end of file
diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
index c1fc6fb..2448e17 100644
--- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
+++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
@@ -12,34 +12,80 @@
 class TestSubcontractedItemToBeTransferred(unittest.TestCase):
 
 	def test_pending_and_transferred_qty(self):
-		po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
+		po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes', supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		# Material Receipt of RMs
 		make_stock_entry(item_code='_Test Item', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
 		make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
-		transfer_subcontracted_raw_materials(po.name)
-		col, data = execute(filters=frappe._dict({'supplier': po.supplier,
-		   'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
-		   'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))}))
-		self.assertEqual(data[0]['purchase_order'], po.name)
-		self.assertIn(data[0]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100'])
-		self.assertIn(data[0]['p_qty'], [9, 18])
-		self.assertIn(data[0]['t_qty'], [1, 2])
 
-		self.assertEqual(data[1]['purchase_order'], po.name)
-		self.assertIn(data[1]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100'])
-		self.assertIn(data[1]['p_qty'], [9, 18])
-		self.assertIn(data[1]['t_qty'], [1, 2])
+		se = transfer_subcontracted_raw_materials(po)
 
+		col, data = execute(filters=frappe._dict(
+			{
+				'supplier': po.supplier,
+				'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
+				'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))
+			}
+		))
+		po.reload()
+
+		po_data = [row for row in data if row.get('purchase_order') == po.name]
+		# Alphabetically sort to be certain of order
+		po_data = sorted(po_data, key = lambda i: i['rm_item_code'])
+
+		self.assertEqual(len(po_data), 2)
+		self.assertEqual(po_data[0]['purchase_order'], po.name)
+
+		self.assertEqual(po_data[0]['rm_item_code'], '_Test Item')
+		self.assertEqual(po_data[0]['p_qty'], 8)
+		self.assertEqual(po_data[0]['transferred_qty'], 2)
+
+		self.assertEqual(po_data[1]['rm_item_code'], '_Test Item Home Desktop 100')
+		self.assertEqual(po_data[1]['p_qty'], 19)
+		self.assertEqual(po_data[1]['transferred_qty'], 1)
+
+		se.cancel()
+		po.cancel()
 
 def transfer_subcontracted_raw_materials(po):
+	# Order of supplied items fetched in PO is flaky
+	transfer_qty_map = {
+		'_Test Item': 2,
+		'_Test Item Home Desktop 100': 1
+	}
+
+	item_1 = po.supplied_items[0].rm_item_code
+	item_2 = po.supplied_items[1].rm_item_code
+
 	rm_item = [
-	 {'item_code': '_Test Item', 'rm_item_code': '_Test Item', 'item_name': '_Test Item', 'qty': 1,
-		'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 100, 'stock_uom': 'Nos'},
-	 {'item_code': '_Test Item Home Desktop 100', 'rm_item_code': '_Test Item Home Desktop 100', 'item_name': '_Test Item Home Desktop 100', 'qty': 2,
-		'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}]
+		{
+			'name': po.supplied_items[0].name,
+			'item_code': item_1,
+			'rm_item_code': item_1,
+			'item_name': item_1,
+			'qty': transfer_qty_map[item_1],
+			'warehouse': '_Test Warehouse - _TC',
+			'rate': 100,
+			'amount': 100 * transfer_qty_map[item_1],
+			'stock_uom': 'Nos'
+		},
+		{
+			'name': po.supplied_items[1].name,
+			'item_code': item_2,
+			'rm_item_code': item_2,
+			'item_name': item_2,
+			'qty': transfer_qty_map[item_2],
+			'warehouse': '_Test Warehouse - _TC',
+			'rate': 100,
+			'amount': 100 * transfer_qty_map[item_2],
+			'stock_uom': 'Nos'
+		}
+	]
 	rm_item_string = json.dumps(rm_item)
-	se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string))
-	se.from_warehouse = '_Test Warehouse 1 - _TC'
-	se.to_warehouse = '_Test Warehouse 1 - _TC'
+	se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string))
+	se.from_warehouse = '_Test Warehouse - _TC'
+	se.to_warehouse = '_Test Warehouse - _TC'
 	se.stock_entry_type = 'Send to Subcontractor'
 	se.save()
 	se.submit()
+	return se
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index f88e8df..a9860ed 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -116,12 +116,16 @@
 
 		if self.doctype == 'Purchase Invoice':
 			self.calculate_paid_amount()
+			# apply tax withholding only if checked and applicable
+			self.set_tax_withholding()
 
 		if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
 			pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
 			if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
 				self.set_advances()
 
+			self.set_advance_gain_or_loss()
+
 			if self.is_return:
 				self.validate_qty()
 			else:
@@ -225,7 +229,7 @@
 
 	def validate_date_with_fiscal_year(self):
 		if self.meta.get_field("fiscal_year"):
-			date_field = ""
+			date_field = None
 			if self.meta.get_field("posting_date"):
 				date_field = "posting_date"
 			elif self.meta.get_field("transaction_date"):
@@ -582,15 +586,18 @@
 				allocated_amount = min(amount - advance_allocated, d.amount)
 			advance_allocated += flt(allocated_amount)
 
-			self.append("advances", {
+			advance_row = {
 				"doctype": self.doctype + " Advance",
 				"reference_type": d.reference_type,
 				"reference_name": d.reference_name,
 				"reference_row": d.reference_row,
 				"remarks": d.remarks,
 				"advance_amount": flt(d.amount),
-				"allocated_amount": allocated_amount
-			})
+				"allocated_amount": allocated_amount,
+				"ref_exchange_rate": flt(d.exchange_rate) # exchange_rate of advance entry
+			}
+
+			self.append("advances", advance_row)
 
 	def get_advance_entries(self, include_unallocated=True):
 		if self.doctype == "Sales Invoice":
@@ -608,8 +615,8 @@
 			order_field = "purchase_order"
 			order_doctype = "Purchase Order"
 
-		order_list = list(set([d.get(order_field)
-			for d in self.get("items") if d.get(order_field)]))
+		order_list = list(set(d.get(order_field)
+			for d in self.get("items") if d.get(order_field)))
 
 		journal_entries = get_advance_journal_entries(party_type, party, party_account,
 			amount_field, order_doctype, order_list, include_unallocated)
@@ -633,8 +640,8 @@
 
 	def validate_advance_entries(self):
 		order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
-		order_list = list(set([d.get(order_field)
-			for d in self.get("items") if d.get(order_field)]))
+		order_list = list(set(d.get(order_field)
+			for d in self.get("items") if d.get(order_field)))
 
 		if not order_list: return
 
@@ -648,6 +655,66 @@
 						"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
 							.format(d.reference_name, d.against_order))
 
+	def set_advance_gain_or_loss(self):
+		if not self.get("advances"):
+			return
+
+		for d in self.get("advances"):
+			advance_exchange_rate = d.ref_exchange_rate
+			if (d.allocated_amount and self.conversion_rate != 1
+				and self.conversion_rate != advance_exchange_rate):
+
+				base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount
+				base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount
+				difference = base_allocated_amount_in_ref_rate - base_allocated_amount_in_inv_rate
+
+				d.exchange_gain_loss = difference
+
+	def make_exchange_gain_loss_gl_entries(self, gl_entries):
+		if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']:
+			for d in self.get("advances"):
+				if d.exchange_gain_loss:
+					party = self.supplier if self.get('doctype') == 'Purchase Invoice' else self.customer
+					party_account = self.credit_to if self.get('doctype') == 'Purchase Invoice' else self.debit_to
+					party_type = "Supplier" if self.get('doctype') == 'Purchase Invoice' else "Customer"
+
+					gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
+					account_currency = get_account_currency(gain_loss_account)
+					if account_currency != self.company_currency:
+						frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
+
+					# for purchase
+					dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit'
+					# just reverse for sales?
+					dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
+
+					gl_entries.append(
+						self.get_gl_dict({
+							"account": gain_loss_account,
+							"account_currency": account_currency,
+							"against": party,
+							dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
+							dr_or_cr: abs(d.exchange_gain_loss),
+							"cost_center": self.cost_center,
+							"project": self.project
+						}, item=d)
+					)
+
+					dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
+
+					gl_entries.append(
+						self.get_gl_dict({
+							"account": party_account,
+							"party_type": party_type,
+							"party": party,
+							"against": gain_loss_account,
+							dr_or_cr + "_in_account_currency": flt(abs(d.exchange_gain_loss) / self.conversion_rate),
+							dr_or_cr: abs(d.exchange_gain_loss),
+							"cost_center": self.cost_center,
+							"project": self.project
+						}, self.party_account_currency, item=self)
+					)
+
 	def update_against_document_in_jv(self):
 		"""
 			Links invoice and advance voucher:
@@ -688,7 +755,9 @@
 						if self.party_account_currency != self.company_currency else 1),
 					'grand_total': (self.base_grand_total
 						if self.party_account_currency == self.company_currency else self.grand_total),
-					'outstanding_amount': self.outstanding_amount
+					'outstanding_amount': self.outstanding_amount,
+					'difference_account': frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account'),
+					'exchange_gain_loss': flt(d.get('exchange_gain_loss'))
 				})
 				lst.append(args)
 
@@ -700,6 +769,7 @@
 		from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
 
 		if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
+			self.update_allocated_advance_taxes_on_cancel()
 			if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
 				unlink_ref_doc_from_payment_entries(self)
 
@@ -707,6 +777,87 @@
 			if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
 				unlink_ref_doc_from_payment_entries(self)
 
+	def get_tax_map(self):
+		tax_map = {}
+		for tax in self.get('taxes'):
+			tax_map.setdefault(tax.account_head, 0.0)
+			tax_map[tax.account_head] += tax.tax_amount
+
+		return tax_map
+
+	def update_allocated_advance_taxes_on_cancel(self):
+		if self.get('advances'):
+			tax_accounts = [d.account_head for d in self.get('taxes')]
+			allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'],
+				filters={'voucher_no': self.name, 'account': ('in', tax_accounts)},
+				group_by='account', as_list=1))
+
+			tax_map = self.get_tax_map()
+
+			for pe in self.get('advances'):
+				if pe.reference_type == 'Payment Entry':
+					pe = frappe.get_doc('Payment Entry', pe.reference_name)
+					for tax in pe.get('taxes'):
+						allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head)
+						if allocated_amount > tax.tax_amount:
+							allocated_amount = tax.tax_amount
+
+						if allocated_amount:
+							frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount',
+								tax.allocated_amount - allocated_amount)
+							tax_map[tax.account_head] -= allocated_amount
+							allocated_tax_map[tax.account_head] -= allocated_amount
+
+	def allocate_advance_taxes(self, gl_entries):
+		tax_map = self.get_tax_map()
+		for pe in self.get("advances"):
+			if pe.reference_type == "Payment Entry" and \
+				frappe.db.get_value('Payment Entry', pe.reference_name, 'advance_tax_account'):
+				pe = frappe.get_doc("Payment Entry", pe.reference_name)
+				for tax in pe.get("taxes"):
+					account_currency = get_account_currency(tax.account_head)
+
+					if self.doctype == "Purchase Invoice":
+						dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
+						rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
+					else:
+						dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
+						rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
+
+					party = self.supplier if self.doctype == "Purchase Invoice" else self.customer
+					unallocated_amount = tax.tax_amount - tax.allocated_amount
+					if tax_map.get(tax.account_head):
+						amount = tax_map.get(tax.account_head)
+						if amount < unallocated_amount:
+							unallocated_amount = amount
+
+						gl_entries.append(
+							self.get_gl_dict({
+								"account": tax.account_head,
+								"against": party,
+								dr_or_cr: unallocated_amount,
+								dr_or_cr + "_in_account_currency": unallocated_amount
+								if account_currency==self.company_currency
+								else unallocated_amount,
+								"cost_center": tax.cost_center
+							}, account_currency, item=tax))
+
+						gl_entries.append(
+							self.get_gl_dict({
+								"account": pe.advance_tax_account,
+								"against": party,
+								rev_dr_cr: unallocated_amount,
+								rev_dr_cr + "_in_account_currency": unallocated_amount
+								if account_currency==self.company_currency
+								else unallocated_amount,
+								"cost_center": tax.cost_center
+							}, account_currency, item=tax))
+
+						frappe.db.set_value("Advance Taxes and Charges", tax.name, "allocated_amount",
+							tax.allocated_amount + unallocated_amount)
+
+						tax_map[tax.account_head] -= unallocated_amount
+
 	def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
 		from erpnext.controllers.status_updater import get_allowance_for
 		item_allowance = {}
@@ -744,8 +895,14 @@
 					role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
 
 					if total_billed_amt - max_allowed_amt > 0.01 and role_allowed_to_over_bill not in frappe.get_roles():
-						frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
-							.format(item.item_code, item.idx, max_allowed_amt))
+						if self.doctype != "Purchase Invoice":
+							self.throw_overbill_exception(item, max_allowed_amt)
+						elif not cint(frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice")):
+							self.throw_overbill_exception(item, max_allowed_amt)
+
+	def throw_overbill_exception(self, item, max_allowed_amt):
+		frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
+			.format(item.item_code, item.idx, max_allowed_amt))
 
 	def get_company_default(self, fieldname):
 		from erpnext.accounts.utils import get_company_default
@@ -1108,7 +1265,7 @@
 
 
 def validate_taxes_and_charges(tax):
-	if tax.charge_type in ['Actual', 'On Net Total'] and tax.row_id:
+	if tax.charge_type in ['Actual', 'On Net Total', 'On Paid Amount'] and tax.row_id:
 		frappe.throw(_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"))
 	elif tax.charge_type in ['On Previous Row Amount', 'On Previous Row Total']:
 		if cint(tax.idx) == 1:
@@ -1125,20 +1282,19 @@
 
 def validate_inclusive_tax(tax, doc):
 	def _on_previous_row_error(row_range):
-		throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx,
-																										  row_range))
+		throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
 
 	if cint(getattr(tax, "included_in_print_rate", None)):
 		if tax.charge_type == "Actual":
 			# inclusive tax cannot be of type Actual
-			throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate").format(tax.idx))
+			throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
 		elif tax.charge_type == "On Previous Row Amount" and \
 				not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate):
 			# referred row should also be inclusive
 			_on_previous_row_error(tax.row_id)
 		elif tax.charge_type == "On Previous Row Total" and \
 				not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]):
-			# all rows about the reffered tax should be inclusive
+			# all rows about the referred tax should be inclusive
 			_on_previous_row_error("1 - %d" % (tax.row_id,))
 		elif tax.get("category") == "Valuation":
 			frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
@@ -1200,6 +1356,8 @@
 	party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
 	currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
 	payment_type = "Receive" if party_type == "Customer" else "Pay"
+	exchange_rate_field = "source_exchange_rate" if payment_type == "Receive" else "target_exchange_rate"
+
 	payment_entries_against_order, unallocated_payment_entries = [], []
 	limit_cond = "limit %s" % limit if limit else ""
 
@@ -1216,31 +1374,31 @@
 				"Payment Entry" as reference_type, t1.name as reference_name,
 				t1.remarks, t2.allocated_amount as amount, t2.name as reference_row,
 				t2.reference_name as against_order, t1.posting_date,
-				t1.{0} as currency
+				t1.{0} as currency, t1.{4} as exchange_rate
 			from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
 			where
 				t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s
 				and t1.party_type = %s and t1.party = %s and t1.docstatus = 1
 				and t2.reference_doctype = %s {2}
 			order by t1.posting_date {3}
-		""".format(currency_field, party_account_field, reference_condition, limit_cond),
+		""".format(currency_field, party_account_field, reference_condition, limit_cond, exchange_rate_field),
 													  [party_account, payment_type, party_type, party,
 													   order_doctype] + order_list, as_dict=1)
 
 	if include_unallocated:
 		unallocated_payment_entries = frappe.db.sql("""
 				select "Payment Entry" as reference_type, name as reference_name,
-				remarks, unallocated_amount as amount
+				remarks, unallocated_amount as amount, {2} as exchange_rate
 				from `tabPayment Entry`
 				where
 					{0} = %s and party_type = %s and party = %s and payment_type = %s
 					and docstatus = 1 and unallocated_amount > 0
 				order by posting_date {1}
-			""".format(party_account_field, limit_cond), (party_account, party_type, party, payment_type), as_dict=1)
+			""".format(party_account_field, limit_cond, exchange_rate_field),
+			(party_account, party_type, party, payment_type), as_dict=1)
 
 	return list(payment_entries_against_order) + list(unallocated_payment_entries)
 
-
 def update_invoice_status():
 	# Daily update the status of the invoices
 
@@ -1449,6 +1607,7 @@
 	for d in deleted_children:
 		update_bin_on_delete(d, parent.doctype)
 
+
 @frappe.whitelist()
 def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
 	def check_doc_permissions(doc, perm_type='create'):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 3f2d339..6a550e0 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -11,16 +11,17 @@
 from erpnext.stock.get_item_details import get_conversion_factor
 from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
 from erpnext.stock.stock_ledger import get_valuation_rate
-from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items
 from erpnext.stock.doctype.serial_no.serial_no import get_auto_serial_nos, auto_make_serial_nos, get_serial_nos
 from frappe.contacts.doctype.address.address import get_address_display
 
 from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
-from erpnext.controllers.stock_controller import StockController
 from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
 from erpnext.stock.utils import get_incoming_rate
 
-class BuyingController(StockController):
+from erpnext.controllers.stock_controller import StockController
+from erpnext.controllers.subcontracting import Subcontracting
+
+class BuyingController(StockController, Subcontracting):
 
 	def get_feed(self):
 		if self.get("supplier_name"):
@@ -57,6 +58,11 @@
 		if self.doctype in ("Purchase Receipt", "Purchase Invoice"):
 			self.update_valuation_rate()
 
+	def onload(self):
+		super(BuyingController, self).onload()
+		self.set_onload("backflush_based_on", frappe.db.get_single_value('Buying Settings',
+			'backflush_raw_materials_of_subcontract_based_on'))
+
 	def set_missing_values(self, for_validate=False):
 		super(BuyingController, self).set_missing_values(for_validate)
 
@@ -171,18 +177,19 @@
 
 			TODO: rename item_tax_amount to valuation_tax_amount
 		"""
+		stock_and_asset_items = []
 		stock_and_asset_items = self.get_stock_items() + self.get_asset_items()
 
 		stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
 		last_item_idx = 1
 		for d in self.get("items"):
-			if d.item_code and d.item_code in stock_and_asset_items:
+			if (d.item_code and d.item_code in stock_and_asset_items):
 				stock_and_asset_items_qty += flt(d.qty)
 				stock_and_asset_items_amount += flt(d.base_net_amount)
 				last_item_idx = d.idx
 
-		total_valuation_amount = sum([flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes")
-			if d.category in ["Valuation", "Valuation and Total"]])
+		total_valuation_amount = sum(flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes")
+			if d.category in ["Valuation", "Valuation and Total"])
 
 		valuation_amount_adjustment = total_valuation_amount
 		for i, item in enumerate(self.get("items")):
@@ -255,7 +262,7 @@
 		supplied_items_cost = 0.0
 		for d in self.get("supplied_items"):
 			if d.reference_name == item_row_id:
-				if reset_outgoing_rate and frappe.db.get_value('Item', d.rm_item_code, 'is_stock_item'):
+				if reset_outgoing_rate and frappe.get_cached_value('Item', d.rm_item_code, 'is_stock_item'):
 					rate = get_incoming_rate({
 						"item_code": d.rm_item_code,
 						"warehouse": self.supplier_warehouse,
@@ -285,11 +292,13 @@
 				if item in self.sub_contracted_items and not item.bom:
 					frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))
 
-			if self.doctype == "Purchase Order":
-				for supplied_item in self.get("supplied_items"):
-					if not supplied_item.reserve_warehouse:
-						frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code)))
+			if self.doctype != "Purchase Order":
+				return
 
+			for row in self.get("supplied_items"):
+				if not row.reserve_warehouse:
+					msg = f"Reserved Warehouse is mandatory for the Item {frappe.bold(row.rm_item_code)} in Raw Materials supplied"
+					frappe.throw(_(msg))
 		else:
 			for item in self.get("items"):
 				if item.bom:
@@ -297,23 +306,7 @@
 
 	def create_raw_materials_supplied(self, raw_material_table):
 		if self.is_subcontracted=="Yes":
-			parent_items = []
-			backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings",
-				"backflush_raw_materials_of_subcontract_based_on")
-			if (self.doctype == 'Purchase Receipt' and
-				backflush_raw_materials_based_on != 'BOM'):
-				self.update_raw_materials_supplied_based_on_stock_entries()
-			else:
-				for item in self.get("items"):
-					if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
-						item.rm_supp_cost = 0.0
-					if item.bom and item.item_code in self.sub_contracted_items:
-						self.update_raw_materials_supplied_based_on_bom(item, raw_material_table)
-
-						if [item.item_code, item.name] not in parent_items:
-							parent_items.append([item.item_code, item.name])
-
-				self.cleanup_raw_materials_supplied(parent_items, raw_material_table)
+			self.set_materials_for_subcontracted_items(raw_material_table)
 
 		elif self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
 			for item in self.get("items"):
@@ -322,176 +315,6 @@
 		if self.is_subcontracted == "No" and self.get("supplied_items"):
 			self.set('supplied_items', [])
 
-	def update_raw_materials_supplied_based_on_stock_entries(self):
-		self.set('supplied_items', [])
-
-		purchase_orders = set([d.purchase_order for d in self.items])
-
-		# qty of raw materials backflushed (for each item per purchase order)
-		backflushed_raw_materials_map = get_backflushed_subcontracted_raw_materials(purchase_orders)
-
-		# qty of "finished good" item yet to be received
-		qty_to_be_received_map = get_qty_to_be_received(purchase_orders)
-
-		for item in self.get('items'):
-			if not item.purchase_order:
-				continue
-
-			# reset raw_material cost
-			item.rm_supp_cost = 0
-
-			# qty of raw materials transferred to the supplier
-			transferred_raw_materials = get_subcontracted_raw_materials_from_se(item.purchase_order, item.item_code)
-
-			non_stock_items = get_non_stock_items(item.purchase_order, item.item_code)
-
-			item_key = '{}{}'.format(item.item_code, item.purchase_order)
-
-			fg_yet_to_be_received = qty_to_be_received_map.get(item_key)
-
-			if not fg_yet_to_be_received:
-				frappe.throw(_("Row #{0}: Item {1} is already fully received in Purchase Order {2}")
-					.format(item.idx, frappe.bold(item.item_code),
-						frappe.utils.get_link_to_form("Purchase Order", item.purchase_order)),
-					title=_("Limit Crossed"))
-
-			transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
-			# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
-
-			for raw_material in transferred_raw_materials + non_stock_items:
-				rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
-				raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
-
-				consumed_qty = raw_material_data.get('qty', 0)
-				consumed_serial_nos = raw_material_data.get('serial_no', '')
-				consumed_batch_nos = raw_material_data.get('batch_nos', '')
-
-				transferred_qty = raw_material.qty
-
-				rm_qty_to_be_consumed = transferred_qty - consumed_qty
-
-				# backflush all remaining transferred qty in the last Purchase Receipt
-				if fg_yet_to_be_received == item.qty:
-					qty = rm_qty_to_be_consumed
-				else:
-					qty = (rm_qty_to_be_consumed / fg_yet_to_be_received) * item.qty
-
-					if frappe.get_cached_value('UOM', raw_material.stock_uom, 'must_be_whole_number'):
-						qty = frappe.utils.ceil(qty)
-
-				if qty > rm_qty_to_be_consumed:
-					qty = rm_qty_to_be_consumed
-
-				if not qty: continue
-
-				if raw_material.serial_nos:
-					set_serial_nos(raw_material, consumed_serial_nos, qty)
-
-				if raw_material.batch_nos:
-					backflushed_batch_qty_map = raw_material_data.get('consumed_batch', {})
-
-					batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code,
-						qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order)
-
-					for batch_data in batches_qty:
-						qty = batch_data['qty']
-						raw_material.batch_no = batch_data['batch']
-						if qty > 0:
-							self.append_raw_material_to_be_backflushed(item, raw_material, qty)
-				else:
-					self.append_raw_material_to_be_backflushed(item, raw_material, qty)
-
-	def append_raw_material_to_be_backflushed(self, fg_item_row, raw_material_data, qty):
-		rm = self.append('supplied_items', {})
-		rm.update(raw_material_data)
-
-		if not rm.main_item_code:
-			rm.main_item_code = fg_item_row.item_code
-
-		rm.reference_name = fg_item_row.name
-		rm.required_qty = qty
-		rm.consumed_qty = qty
-
-	def update_raw_materials_supplied_based_on_bom(self, item, raw_material_table):
-		exploded_item = 1
-		if hasattr(item, 'include_exploded_items'):
-			exploded_item = item.get('include_exploded_items')
-
-		bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item)
-
-		used_alternative_items = []
-		if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and item.purchase_order:
-			used_alternative_items = get_used_alternative_items(purchase_order = item.purchase_order)
-
-		raw_materials_cost = 0
-		items = list(set([d.item_code for d in bom_items]))
-		item_wh = frappe._dict(frappe.db.sql("""select i.item_code, id.default_warehouse
-			from `tabItem` i, `tabItem Default` id
-			where id.parent=i.name and id.company=%s and i.name in ({0})"""
-			.format(", ".join(["%s"] * len(items))), [self.company] + items))
-
-		for bom_item in bom_items:
-			if self.doctype == "Purchase Order":
-				reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code)
-				if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company:
-					reserve_warehouse = None
-
-			conversion_factor = item.conversion_factor
-			if (self.doctype in ["Purchase Receipt", "Purchase Invoice"] and item.purchase_order and
-				bom_item.item_code in used_alternative_items):
-				alternative_item_data = used_alternative_items.get(bom_item.item_code)
-				bom_item.item_code = alternative_item_data.item_code
-				bom_item.item_name = alternative_item_data.item_name
-				bom_item.stock_uom = alternative_item_data.stock_uom
-				conversion_factor = alternative_item_data.conversion_factor
-				bom_item.description = alternative_item_data.description
-
-			# check if exists
-			exists = 0
-			for d in self.get(raw_material_table):
-				if d.main_item_code == item.item_code and d.rm_item_code == bom_item.item_code \
-					and d.reference_name == item.name:
-						rm, exists = d, 1
-						break
-
-			if not exists:
-				rm = self.append(raw_material_table, {})
-
-			required_qty = flt(flt(bom_item.qty_consumed_per_unit) * (flt(item.qty) + getattr(item, 'rejected_qty', 0)) *
-				flt(conversion_factor), rm.precision("required_qty"))
-			rm.reference_name = item.name
-			rm.bom_detail_no = bom_item.name
-			rm.main_item_code = item.item_code
-			rm.rm_item_code = bom_item.item_code
-			rm.stock_uom = bom_item.stock_uom
-			rm.required_qty = required_qty
-			rm.rate = bom_item.rate
-			rm.conversion_factor = conversion_factor
-
-			if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
-				rm.consumed_qty = required_qty
-				rm.description = bom_item.description
-				if item.batch_no and frappe.db.get_value("Item", rm.rm_item_code, "has_batch_no") and not rm.batch_no:
-					rm.batch_no = item.batch_no
-			elif not rm.reserve_warehouse:
-				rm.reserve_warehouse = reserve_warehouse
-
-	def cleanup_raw_materials_supplied(self, parent_items, raw_material_table):
-		"""Remove all those child items which are no longer present in main item table"""
-		delete_list = []
-		for d in self.get(raw_material_table):
-			if [d.main_item_code, d.reference_name] not in parent_items:
-				# mark for deletion from doclist
-				delete_list.append(d)
-
-		# delete from doclist
-		if delete_list:
-			rm_supplied_details = self.get(raw_material_table)
-			self.set(raw_material_table, [])
-			for d in rm_supplied_details:
-				if d not in delete_list:
-					self.append(raw_material_table, d)
-
 	@property
 	def sub_contracted_items(self):
 		if not hasattr(self, "_sub_contracted_items"):
@@ -683,7 +506,8 @@
 			self.process_fixed_asset()
 			self.update_fixed_asset(field)
 
-		update_last_purchase_rate(self, is_submit = 1)
+		if self.doctype in ['Purchase Order', 'Purchase Receipt']:
+			update_last_purchase_rate(self, is_submit = 1)
 
 	def on_cancel(self):
 		super(BuyingController, self).on_cancel()
@@ -691,7 +515,9 @@
 		if self.get('is_return'):
 			return
 
-		update_last_purchase_rate(self, is_submit = 0)
+		if self.doctype in ['Purchase Order', 'Purchase Receipt']:
+			update_last_purchase_rate(self, is_submit = 0)
+
 		if self.doctype in ['Purchase Receipt', 'Purchase Invoice']:
 			field = 'purchase_invoice' if self.doctype == 'Purchase Invoice' else 'purchase_receipt'
 
@@ -863,104 +689,6 @@
 		else:
 			validate_item_type(self, "is_purchase_item", "purchase")
 
-
-def get_items_from_bom(item_code, bom, exploded_item=1):
-	doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
-
-	bom_items = frappe.db.sql("""select t2.item_code, t2.name,
-			t2.rate, t2.stock_uom, t2.source_warehouse, t2.description,
-			t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit
-		from
-			`tabBOM` t1, `tab{0}` t2, tabItem t3
-		where
-			t2.parent = t1.name and t1.item = %s
-			and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
-			and t2.sourced_by_supplier = 0
-			and t2.item_code = t3.name""".format(doctype),
-			(item_code, bom), as_dict=1)
-
-	if not bom_items:
-		msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1)
-
-	return bom_items
-
-def get_subcontracted_raw_materials_from_se(purchase_order, fg_item):
-	common_query = """
-		SELECT
-			sed.item_code AS rm_item_code,
-			SUM(sed.qty) AS qty,
-			sed.description,
-			sed.stock_uom,
-			sed.subcontracted_item AS main_item_code,
-			{serial_no_concat_syntax} AS serial_nos,
-			{batch_no_concat_syntax} AS batch_nos
-		FROM `tabStock Entry` se,`tabStock Entry Detail` sed
-		WHERE
-			se.name = sed.parent
-			AND se.docstatus=1
-			AND se.purpose='Send to Subcontractor'
-			AND se.purchase_order = %s
-			AND IFNULL(sed.t_warehouse, '') != ''
-			AND IFNULL(sed.subcontracted_item, '') in ('', %s)
-		GROUP BY sed.item_code, sed.subcontracted_item
-	"""
-	raw_materials = frappe.db.multisql({
-		'mariadb': common_query.format(
-			serial_no_concat_syntax="GROUP_CONCAT(sed.serial_no)",
-			batch_no_concat_syntax="GROUP_CONCAT(sed.batch_no)"
-		),
-		'postgres': common_query.format(
-			serial_no_concat_syntax="STRING_AGG(sed.serial_no, ',')",
-			batch_no_concat_syntax="STRING_AGG(sed.batch_no, ',')"
-		)
-	}, (purchase_order, fg_item), as_dict=1)
-
-	return raw_materials
-
-def get_backflushed_subcontracted_raw_materials(purchase_orders):
-	purchase_receipts = frappe.get_all("Purchase Receipt Item",
-		fields = ["purchase_order", "item_code", "name", "parent"],
-		filters={"docstatus": 1, "purchase_order": ("in", list(purchase_orders))})
-
-	distinct_purchase_receipts = {}
-	for pr in purchase_receipts:
-		key = (pr.purchase_order, pr.item_code, pr.parent)
-		distinct_purchase_receipts.setdefault(key, []).append(pr.name)
-
-	backflushed_raw_materials_map = frappe._dict()
-	for args, references in iteritems(distinct_purchase_receipts):
-		purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
-
-		for data in purchase_receipt_supplied_items:
-			pr_key = (data.rm_item_code, data.main_item_code, args[0])
-			if pr_key not in backflushed_raw_materials_map:
-				backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
-					"qty": 0.0,
-					"serial_no": [],
-					"batch_no": [],
-					"consumed_batch": {}
-				}))
-
-			row = backflushed_raw_materials_map.get(pr_key)
-			row.qty += data.consumed_qty
-
-			for field in ["serial_no", "batch_no"]:
-				if data.get(field):
-					row[field].append(data.get(field))
-
-			if data.get("batch_no"):
-				if data.get("batch_no") in row.consumed_batch:
-					row.consumed_batch[data.get("batch_no")] += data.consumed_qty
-				else:
-					row.consumed_batch[data.get("batch_no")] = data.consumed_qty
-
-	return backflushed_raw_materials_map
-
-def get_supplied_items(item_code, purchase_receipt, references):
-	return frappe.get_all("Purchase Receipt Item Supplied",
-		fields=["rm_item_code", "main_item_code", "consumed_qty", "serial_no", "batch_no"],
-		filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
-
 def get_asset_item_details(asset_items):
 	asset_items_data = {}
 	for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"],
@@ -992,135 +720,3 @@
 			error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message)
 
 		frappe.throw(error_message)
-
-def get_qty_to_be_received(purchase_orders):
-	return frappe._dict(frappe.db.sql("""
-		SELECT CONCAT(poi.`item_code`, poi.`parent`) AS item_key,
-		SUM(poi.`qty`) - SUM(poi.`received_qty`) AS qty_to_be_received
-		FROM `tabPurchase Order Item` poi
-		WHERE
-			poi.`parent` in %s
-		GROUP BY poi.`item_code`, poi.`parent`
-		HAVING SUM(poi.`qty`) > SUM(poi.`received_qty`)
-	""", (purchase_orders)))
-
-def get_non_stock_items(purchase_order, fg_item_code):
-	return frappe.db.sql("""
-		SELECT
-			pois.main_item_code,
-			pois.rm_item_code,
-			item.description,
-			pois.required_qty AS qty,
-			pois.rate,
-			1 as non_stock_item,
-			pois.stock_uom
-		FROM `tabPurchase Order Item Supplied` pois, `tabItem` item
-		WHERE
-			pois.`rm_item_code` = item.`name`
-			AND item.is_stock_item = 0
-			AND pois.`parent` = %s
-			AND pois.`main_item_code` = %s
-	""", (purchase_order, fg_item_code), as_dict=1)
-
-
-def set_serial_nos(raw_material, consumed_serial_nos, qty):
-	serial_nos = set(get_serial_nos(raw_material.serial_nos)) - \
-		set(get_serial_nos(consumed_serial_nos))
-	if serial_nos and qty <= len(serial_nos):
-		raw_material.serial_no = '\n'.join(list(serial_nos)[0:frappe.utils.cint(qty)])
-
-def get_transferred_batch_qty_map(purchase_order, fg_item):
-	# returns
-	# {
-	# 	(item_code, fg_code): {
-	# 		batch1: 10, # qty
-	# 		batch2: 16
-	# 	},
-	# }
-	transferred_batch_qty_map = {}
-	transferred_batches = frappe.db.sql("""
-		SELECT
-			sed.batch_no,
-			SUM(sed.qty) AS qty,
-			sed.item_code,
-			sed.subcontracted_item
-		FROM `tabStock Entry` se,`tabStock Entry Detail` sed
-		WHERE
-			se.name = sed.parent
-			AND se.docstatus=1
-			AND se.purpose='Send to Subcontractor'
-			AND se.purchase_order = %s
-			AND ifnull(sed.subcontracted_item, '') in ('', %s)
-			AND sed.batch_no IS NOT NULL
-		GROUP BY
-			sed.batch_no,
-			sed.item_code
-	""", (purchase_order, fg_item), as_dict=1)
-
-	for batch_data in transferred_batches:
-		key = ((batch_data.item_code, fg_item)
-			if batch_data.subcontracted_item else (batch_data.item_code, purchase_order))
-		transferred_batch_qty_map.setdefault(key, OrderedDict())
-		transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty
-
-	return transferred_batch_qty_map
-
-def get_backflushed_batch_qty_map(purchase_order, fg_item):
-	# returns
-	# {
-	# 	(item_code, fg_code): {
-	# 		batch1: 10, # qty
-	# 		batch2: 16
-	# 	},
-	# }
-	backflushed_batch_qty_map = {}
-	backflushed_batches = frappe.db.sql("""
-		SELECT
-			pris.batch_no,
-			SUM(pris.consumed_qty) AS qty,
-			pris.rm_item_code AS item_code
-		FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` pris
-		WHERE
-			pr.name = pri.parent
-			AND pri.parent = pris.parent
-			AND pri.purchase_order = %s
-			AND pri.item_code = pris.main_item_code
-			AND pr.docstatus = 1
-			AND pris.main_item_code = %s
-			AND pris.batch_no IS NOT NULL
-		GROUP BY
-			pris.rm_item_code, pris.batch_no
-	""", (purchase_order, fg_item), as_dict=1)
-
-	for batch_data in backflushed_batches:
-		backflushed_batch_qty_map.setdefault((batch_data.item_code, fg_item), {})
-		backflushed_batch_qty_map[(batch_data.item_code, fg_item)][batch_data.batch_no] = batch_data.qty
-
-	return backflushed_batch_qty_map
-
-def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batches, po):
-	# Returns available batches to be backflushed based on requirements
-	transferred_batches = transferred_batch_qty_map.get((item_code, fg_item), {})
-	if not transferred_batches:
-		transferred_batches = transferred_batch_qty_map.get((item_code, po), {})
-
-	available_batches = []
-
-	for (batch, transferred_qty) in transferred_batches.items():
-		backflushed_qty = backflushed_batches.get(batch, 0)
-		available_qty = transferred_qty - backflushed_qty
-
-		if available_qty >= required_qty:
-			available_batches.append({'batch': batch, 'qty': required_qty})
-			break
-		elif available_qty != 0:
-			available_batches.append({'batch': batch, 'qty': available_qty})
-			required_qty -= available_qty
-
-	for row in available_batches:
-		if backflushed_batches.get(row.get('batch'), 0) > 0:
-			backflushed_batches[row.get('batch')] += row.get('qty')
-		else:
-			backflushed_batches[row.get('batch')] = row.get('qty')
-
-	return available_batches
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 46301b7..2803193 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 import erpnext
+import json
 from frappe.desk.reportview import get_match_cond, get_filters_cond
 from frappe.utils import nowdate, getdate
 from collections import defaultdict
@@ -18,7 +19,7 @@
 	fields = get_fields("Employee", ["name", "employee_name"])
 
 	return frappe.db.sql("""select {fields} from `tabEmployee`
-		where status = 'Active'
+		where status in ('Active', 'Suspended')
 			and docstatus < 2
 			and ({key} like %(txt)s
 				or employee_name like %(txt)s)
@@ -87,7 +88,7 @@
 	fields = get_fields("Customer", fields)
 
 	searchfields = frappe.get_meta("Customer").get_search_fields()
-	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
+	searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
 
 	return frappe.db.sql("""select {fields} from `tabCustomer`
 		where docstatus < 2
@@ -198,13 +199,16 @@
 def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
 	conditions = []
 
+	if isinstance(filters, str):
+		filters = json.loads(filters)
+
 	#Get searchfields from meta and use in Item Link field query
 	meta = frappe.get_meta("Item", cached=True)
 	searchfields = meta.get_search_fields()
 
 	if "description" in searchfields:
 		searchfields.remove("description")
-	
+
 	columns = ''
 	extra_searchfields = [field for field in searchfields
 		if not field in ["name", "item_group", "description"]]
@@ -216,9 +220,10 @@
 		if not field in searchfields]
 	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
 
-	if filters.get('supplier'):
-		item_group_list = frappe.get_all('Supplier Item Group', filters = {'supplier': filters.get('supplier')}, fields = ['item_group'])
-		
+	if filters and isinstance(filters, dict) and filters.get('supplier'):
+		item_group_list = frappe.get_all('Supplier Item Group',
+			filters = {'supplier': filters.get('supplier')}, fields = ['item_group'])
+
 		item_groups = []
 		for i in item_group_list:
 			item_groups.append(i.item_group)
@@ -227,7 +232,7 @@
 
 		if item_groups:
 			filters['item_group'] = ['in', item_groups]
-		
+
 	description_cond = ''
 	if frappe.db.count('Item', cache=True) < 50000:
 		# scan description only if items are less than 50000
@@ -310,7 +315,7 @@
 	return frappe.db.sql("""select {fields} from `tabProject`
 		where
 			`tabProject`.status not in ("Completed", "Cancelled")
-			and {cond} {match_cond} {scond}
+			and {cond} {scond} {match_cond}
 		order by
 			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
 			idx desc,
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/selling_controller.py b/erpnext/controllers/selling_controller.py
index 54156f37..da2765d 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -330,9 +330,15 @@
 
 				# For internal transfers use incoming rate as the valuation rate
 				if self.is_internal_transfer():
-					rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
-					if d.rate != rate:
-						d.rate = rate
+					if d.doctype == "Packed Item":
+						incoming_rate = flt(d.incoming_rate * d.conversion_factor, d.precision('incoming_rate'))
+						if d.incoming_rate != incoming_rate:
+							d.incoming_rate = incoming_rate
+					else:
+						rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
+						if d.rate != rate:
+							d.rate = rate
+
 						d.discount_percentage = 0
 						d.discount_amount = 0
 						frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
@@ -428,7 +434,7 @@
 		self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
 
 	def get_po_nos(self, ref_doctype, ref_fieldname, po_nos):
-		doc_list = list(set([d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname)]))
+		doc_list = list(set(d.get(ref_fieldname) for d in self.items if d.get(ref_fieldname)))
 		if doc_list:
 			po_nos += [d.po_no for d in frappe.get_all(ref_doctype, 'po_no', filters = {'name': ('in', doc_list)}) if d.get('po_no')]
 
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 83d4c33..943f7aa 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -299,8 +299,8 @@
 			args['name'] = self.get(args['percent_join_field_parent'])
 			self._update_percent_field(args, update_modified)
 		else:
-			distinct_transactions = set([d.get(args['percent_join_field'])
-				for d in self.get_all_children(args['source_dt'])])
+			distinct_transactions = set(d.get(args['percent_join_field'])
+				for d in self.get_all_children(args['source_dt']))
 
 			for name in distinct_transactions:
 				if name:
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 41ca404..2526e6d 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -1,17 +1,21 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-from __future__ import unicode_literals
-import frappe, erpnext
-from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
-from frappe import _
-import frappe.defaults
+import json
 from collections import defaultdict
-from erpnext.accounts.utils import get_fiscal_year, check_if_stock_and_account_balance_synced
+
+import frappe
+import frappe.defaults
+from frappe import _
+from frappe.utils import cint, cstr, flt, get_link_to_form, getdate
+
+import erpnext
 from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
+from erpnext.accounts.utils import get_fiscal_year
 from erpnext.controllers.accounts_controller import AccountsController
-from erpnext.stock.stock_ledger import get_valuation_rate
 from erpnext.stock import get_warehouse_account_map
+from erpnext.stock.stock_ledger import get_valuation_rate
+
 
 class QualityInspectionRequiredError(frappe.ValidationError): pass
 class QualityInspectionRejectedError(frappe.ValidationError): pass
@@ -189,7 +193,6 @@
 		if hasattr(self, "items"):
 			item_doclist = self.get("items")
 		elif self.doctype == "Stock Reconciliation":
-			import json
 			item_doclist = []
 			data = json.loads(self.reconciliation_json)
 			for row in data[data.index(self.head_row)+1:]:
@@ -310,7 +313,7 @@
 
 	def get_serialized_items(self):
 		serialized_items = []
-		item_codes = list(set([d.item_code for d in self.get("items")]))
+		item_codes = list(set(d.item_code for d in self.get("items")))
 		if item_codes:
 			serialized_items = frappe.db.sql_list("""select name from `tabItem`
 				where has_serial_no=1 and name in ({})""".format(", ".join(["%s"]*len(item_codes))),
@@ -319,10 +322,10 @@
 		return serialized_items
 
 	def validate_warehouse(self):
-		from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
+		from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company
 
-		warehouses = list(set([d.warehouse for d in
-			self.get("items") if getattr(d, "warehouse", None)]))
+		warehouses = list(set(d.warehouse for d in
+			self.get("items") if getattr(d, "warehouse", None)))
 
 		target_warehouses = list(set([d.target_warehouse for d in
 			self.get("items") if getattr(d, "target_warehouse", None)]))
@@ -353,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]))
@@ -494,24 +523,107 @@
 		})
 		if future_sle_exists(args):
 			create_repost_item_valuation_entry(args)
-		elif not is_reposting_pending():
-			check_if_stock_and_account_balance_synced(self.posting_date,
-				self.company, self.doctype, self.name)
+
+@frappe.whitelist()
+def make_quality_inspections(doctype, docname, items):
+	if isinstance(items, str):
+		items = json.loads(items)
+
+	inspections = []
+	for item in items:
+		if flt(item.get("sample_size")) > flt(item.get("qty")):
+			frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format(
+				item_name=item.get("item_name"),
+				sample_size=item.get("sample_size"),
+				accepted_quantity=item.get("qty")
+			))
+
+		quality_inspection = frappe.get_doc({
+			"doctype": "Quality Inspection",
+			"inspection_type": "Incoming",
+			"inspected_by": frappe.session.user,
+			"reference_type": doctype,
+			"reference_name": docname,
+			"item_code": item.get("item_code"),
+			"description": item.get("description"),
+			"sample_size": flt(item.get("sample_size")),
+			"item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None,
+			"batch_no": item.get("batch_no")
+		}).insert()
+		quality_inspection.save()
+		inspections.append(quality_inspection.name)
+
+	return inspections
 
 def is_reposting_pending():
 	return frappe.db.exists("Repost Item Valuation",
 		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
 
+def future_sle_exists(args, sl_entries=None):
+	key = (args.voucher_type, args.voucher_no)
 
-def future_sle_exists(args):
-	sl_entries = frappe.get_all("Stock Ledger Entry",
+	if validate_future_sle_not_exists(args, key, sl_entries):
+		return False
+	elif get_cached_data(args, key):
+		return True
+
+	if not sl_entries:
+		sl_entries = get_sle_entries_against_voucher(args)
+		if not sl_entries:
+			return
+
+	or_conditions = get_conditions_to_validate_future_sle(sl_entries)
+
+	data = frappe.db.sql("""
+		select item_code, warehouse, count(name) as total_row
+		from `tabStock Ledger Entry`
+		where
+			({})
+			and timestamp(posting_date, posting_time)
+				>= timestamp(%(posting_date)s, %(posting_time)s)
+			and voucher_no != %(voucher_no)s
+			and is_cancelled = 0
+		GROUP BY
+			item_code, warehouse
+		""".format(" or ".join(or_conditions)), args, as_dict=1)
+
+	for d in data:
+		frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
+
+	return len(data)
+
+def validate_future_sle_not_exists(args, key, sl_entries=None):
+	item_key = ''
+	if args.get('item_code'):
+		item_key = (args.get('item_code'), args.get('warehouse'))
+
+	if not sl_entries and hasattr(frappe.local, 'future_sle'):
+		if (not frappe.local.future_sle.get(key) or
+			(item_key and item_key not in frappe.local.future_sle.get(key))):
+			return True
+
+def get_cached_data(args, key):
+	if not hasattr(frappe.local, 'future_sle'):
+		frappe.local.future_sle = {}
+
+	if key not in frappe.local.future_sle:
+		frappe.local.future_sle[key] = frappe._dict({})
+
+	if args.get('item_code'):
+		item_key = (args.get('item_code'), args.get('warehouse'))
+		count = frappe.local.future_sle[key].get(item_key)
+
+		return True if (count or count == 0) else False
+	else:
+		return frappe.local.future_sle[key]
+
+def get_sle_entries_against_voucher(args):
+	return frappe.get_all("Stock Ledger Entry",
 		filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no},
 		fields=["item_code", "warehouse"],
 		order_by="creation asc")
 
-	if not sl_entries:
-		return
-
+def get_conditions_to_validate_future_sle(sl_entries):
 	warehouse_items_map = {}
 	for entry in sl_entries:
 		if entry.warehouse not in warehouse_items_map:
@@ -522,23 +634,10 @@
 	or_conditions = []
 	for warehouse, items in warehouse_items_map.items():
 		or_conditions.append(
-			"warehouse = '{}' and item_code in ({})".format(
-				warehouse,
-				", ".join(frappe.db.escape(item) for item in items)
-			)
-		)
+			f"""warehouse = {frappe.db.escape(warehouse)}
+				and item_code in ({', '.join(frappe.db.escape(item) for item in items)})""")
 
-	return frappe.db.sql("""
-		select name
-		from `tabStock Ledger Entry`
-		where
-			({})
-			and timestamp(posting_date, posting_time)
-				>= timestamp(%(posting_date)s, %(posting_time)s)
-			and voucher_no != %(voucher_no)s
-			and is_cancelled = 0
-		limit 1
-		""".format(" or ".join(or_conditions)), args)
+	return or_conditions
 
 def create_repost_item_valuation_entry(args):
 	args = frappe._dict(args)
diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py
new file mode 100644
index 0000000..36ae110
--- /dev/null
+++ b/erpnext/controllers/subcontracting.py
@@ -0,0 +1,393 @@
+import frappe
+import copy
+from frappe import _
+from frappe.utils import flt, cint, get_link_to_form
+from collections import defaultdict
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+class Subcontracting():
+	def set_materials_for_subcontracted_items(self, raw_material_table):
+		if self.doctype == 'Purchase Invoice' and not self.update_stock:
+			return
+
+		self.raw_material_table = raw_material_table
+		self.__identify_change_in_item_table()
+		self.__prepare_supplied_items()
+		self.__validate_supplied_items()
+
+	def __prepare_supplied_items(self):
+		self.initialized_fields()
+		self.__get_purchase_orders()
+		self.__get_pending_qty_to_receive()
+		self.get_available_materials()
+		self.__remove_changed_rows()
+		self.__set_supplied_items()
+
+	def initialized_fields(self):
+		self.available_materials = frappe._dict()
+		self.__transferred_items = frappe._dict()
+		self.alternative_item_details = frappe._dict()
+		self.__get_backflush_based_on()
+
+	def __get_backflush_based_on(self):
+		self.backflush_based_on = frappe.db.get_single_value("Buying Settings",
+			"backflush_raw_materials_of_subcontract_based_on")
+
+	def __get_purchase_orders(self):
+		self.purchase_orders = []
+
+		if self.doctype == 'Purchase Order':
+			return
+
+		self.purchase_orders = [d.purchase_order for d in self.items if d.purchase_order]
+
+	def __identify_change_in_item_table(self):
+		self.__changed_name = []
+		self.__reference_name = []
+
+		if self.doctype == 'Purchase Order' or self.is_new():
+			self.set(self.raw_material_table, [])
+			return
+
+		item_dict = self.__get_data_before_save()
+		if not item_dict:
+			return True
+
+		for n_row in self.items:
+			self.__reference_name.append(n_row.name)
+			if (n_row.name not in item_dict) or (n_row.item_code, n_row.qty) != item_dict[n_row.name]:
+				self.__changed_name.append(n_row.name)
+
+			if item_dict.get(n_row.name):
+				del item_dict[n_row.name]
+
+		self.__changed_name.extend(item_dict.keys())
+
+	def __get_data_before_save(self):
+		item_dict = {}
+		if self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self._doc_before_save:
+			for row in self._doc_before_save.get('items'):
+				item_dict[row.name] = (row.item_code, row.qty)
+
+		return item_dict
+
+	def get_available_materials(self):
+		''' Get the available raw materials which has been transferred to the supplier.
+			available_materials = {
+				(item_code, subcontracted_item, purchase_order): {
+					'qty': 1, 'serial_no': [ABC], 'batch_no': {'batch1': 1}, 'data': item_details
+				}
+			}
+		'''
+		if not self.purchase_orders:
+			return
+
+		for row in self.__get_transferred_items():
+			key = (row.rm_item_code, row.main_item_code, row.purchase_order)
+
+			if key not in self.available_materials:
+				self.available_materials.setdefault(key, frappe._dict({'qty': 0, 'serial_no': [],
+					'batch_no': defaultdict(float), 'item_details': row, 'po_details': []})
+				)
+
+			details = self.available_materials[key]
+			details.qty += row.qty
+			details.po_details.append(row.po_detail)
+
+			if row.serial_no:
+				details.serial_no.extend(get_serial_nos(row.serial_no))
+
+			if row.batch_no:
+				details.batch_no[row.batch_no] += row.qty
+
+			self.__set_alternative_item_details(row)
+
+		self.__transferred_items = copy.deepcopy(self.available_materials)
+		for doctype in ['Purchase Receipt', 'Purchase Invoice']:
+			self.__update_consumed_materials(doctype)
+
+	def __update_consumed_materials(self, doctype, return_consumed_items=False):
+		'''Deduct the consumed materials from the available materials.'''
+
+		pr_items = self.__get_received_items(doctype)
+		if not pr_items:
+			return ([], {}) if return_consumed_items else None
+
+		pr_items = {d.name: d.get(self.get('po_field') or 'purchase_order') for d in pr_items}
+		consumed_materials = self.__get_consumed_items(doctype, pr_items.keys())
+
+		if return_consumed_items:
+			return (consumed_materials, pr_items)
+
+		for row in consumed_materials:
+			key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name))
+			if not self.available_materials.get(key):
+				continue
+
+			self.available_materials[key]['qty'] -= row.consumed_qty
+			if row.serial_no:
+				self.available_materials[key]['serial_no'] = list(
+					set(self.available_materials[key]['serial_no']) - set(get_serial_nos(row.serial_no))
+				)
+
+			if row.batch_no:
+				self.available_materials[key]['batch_no'][row.batch_no] -= row.consumed_qty
+
+	def __get_transferred_items(self):
+		fields = ['`tabStock Entry`.`purchase_order`']
+		alias_dict = {'item_code': 'rm_item_code', 'subcontracted_item': 'main_item_code', 'basic_rate': 'rate'}
+
+		child_table_fields = ['item_code', 'item_name', 'description', 'qty', 'basic_rate', 'amount',
+			'serial_no', 'uom', 'subcontracted_item', 'stock_uom', 'batch_no', 'conversion_factor',
+			's_warehouse', 't_warehouse', 'item_group', 'po_detail']
+
+		if self.backflush_based_on == 'BOM':
+			child_table_fields.append('original_item')
+
+		for field in child_table_fields:
+			fields.append(f'`tabStock Entry Detail`.`{field}` As {alias_dict.get(field, field)}')
+
+		filters = [['Stock Entry', 'docstatus', '=', 1], ['Stock Entry', 'purpose', '=', 'Send to Subcontractor'],
+			['Stock Entry', 'purchase_order', 'in', self.purchase_orders]]
+
+		return frappe.get_all('Stock Entry', fields = fields, filters=filters)
+
+	def __get_received_items(self, doctype):
+		fields = []
+		self.po_field = 'purchase_order'
+
+		for field in ['name', self.po_field, 'parent']:
+			fields.append(f'`tab{doctype} Item`.`{field}`')
+
+		filters = [[doctype, 'docstatus', '=', 1], [f'{doctype} Item', self.po_field, 'in', self.purchase_orders]]
+		if doctype == 'Purchase Invoice':
+			filters.append(['Purchase Invoice', 'update_stock', "=", 1])
+
+		return frappe.get_all(f'{doctype}', fields = fields, filters = filters)
+
+	def __get_consumed_items(self, doctype, pr_items):
+		return frappe.get_all('Purchase Receipt Item Supplied',
+			fields = ['serial_no', 'rm_item_code', 'reference_name', 'batch_no', 'consumed_qty', 'main_item_code'],
+			filters = {'docstatus': 1, 'reference_name': ('in', list(pr_items)), 'parenttype': doctype})
+
+	def __set_alternative_item_details(self, row):
+		if row.get('original_item'):
+			self.alternative_item_details[row.get('original_item')] = row
+
+	def __get_pending_qty_to_receive(self):
+		'''Get qty to be received against the purchase order.'''
+
+		self.qty_to_be_received = defaultdict(float)
+
+		if self.doctype != 'Purchase Order' and self.backflush_based_on != 'BOM' and self.purchase_orders:
+			for row in frappe.get_all('Purchase Order Item',
+				fields = ['item_code', '(qty - received_qty) as qty', 'parent', 'name'],
+				filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)}):
+
+				self.qty_to_be_received[(row.item_code, row.parent)] += row.qty
+
+	def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
+		doctype = 'BOM Item' if not exploded_item else 'BOM Explosion Item'
+		fields = [f'`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit']
+
+		alias_dict = {'item_code': 'rm_item_code', 'name': 'bom_detail_no', 'source_warehouse': 'reserve_warehouse'}
+		for field in ['item_code', 'name', 'rate', 'stock_uom',
+			'source_warehouse', 'description', 'item_name', 'stock_uom']:
+			fields.append(f'`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}')
+
+		filters = [[doctype, 'parent', '=', bom_no], [doctype, 'docstatus', '=', 1],
+			['BOM', 'item', '=', item_code], [doctype, 'sourced_by_supplier', '=', 0]]
+
+		return frappe.get_all('BOM', fields = fields, filters=filters, order_by = f'`tab{doctype}`.`idx`') or []
+
+	def __remove_changed_rows(self):
+		if not self.__changed_name:
+			return
+
+		i=1
+		self.set(self.raw_material_table, [])
+		for d in self._doc_before_save.supplied_items:
+			if d.reference_name in self.__changed_name:
+				continue
+
+			if (d.reference_name not in self.__reference_name):
+				continue
+
+			d.idx = i
+			self.append('supplied_items', d)
+
+			i += 1
+
+	def __set_supplied_items(self):
+		self.bom_items = {}
+
+		has_supplied_items = True if self.get(self.raw_material_table) else False
+		for row in self.items:
+			if (self.doctype != 'Purchase Order' and ((self.__changed_name and row.name not in self.__changed_name)
+				or (has_supplied_items and not self.__changed_name))):
+				continue
+
+			if self.doctype == 'Purchase Order' or self.backflush_based_on == 'BOM':
+				for bom_item in self.__get_materials_from_bom(row.item_code, row.bom, row.get('include_exploded_items')):
+					qty = (flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor)
+					bom_item.main_item_code = row.item_code
+					self.__update_reserve_warehouse(bom_item, row)
+					self.__set_alternative_item(bom_item)
+					self.__add_supplied_item(row, bom_item, qty)
+
+			elif self.backflush_based_on != 'BOM':
+				for key, transfer_item in self.available_materials.items():
+					if (key[1], key[2]) == (row.item_code, row.purchase_order) and transfer_item.qty > 0:
+						qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
+						transfer_item.qty -= qty
+						self.__add_supplied_item(row, transfer_item.get('item_details'), qty)
+
+				if self.qty_to_be_received:
+					self.qty_to_be_received[(row.item_code, row.purchase_order)] -= row.qty
+
+	def __update_reserve_warehouse(self, row, item):
+		if self.doctype == 'Purchase Order':
+			row.reserve_warehouse = (self.set_reserve_warehouse or item.warehouse)
+
+	def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
+		key = (item_row.item_code, item_row.purchase_order)
+
+		if self.qty_to_be_received == item_row.qty:
+			return transfer_item.qty
+
+		if self.qty_to_be_received:
+			qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
+			transfer_item.item_details.required_qty = transfer_item.qty
+
+			if (transfer_item.serial_no or frappe.get_cached_value('UOM',
+				transfer_item.item_details.stock_uom, 'must_be_whole_number')):
+				return frappe.utils.ceil(qty)
+
+			return qty
+
+	def __set_alternative_item(self, bom_item):
+		if self.alternative_item_details.get(bom_item.rm_item_code):
+			bom_item.update(self.alternative_item_details[bom_item.rm_item_code])
+
+	def __add_supplied_item(self, item_row, bom_item, qty):
+		bom_item.conversion_factor = item_row.conversion_factor
+		rm_obj = self.append(self.raw_material_table, bom_item)
+		rm_obj.reference_name = item_row.name
+
+		if self.doctype == 'Purchase Order':
+			rm_obj.required_qty = qty
+		else:
+			rm_obj.consumed_qty = 0
+			rm_obj.purchase_order = item_row.purchase_order
+			self.__set_batch_nos(bom_item, item_row, rm_obj, qty)
+
+	def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
+		key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order)
+
+		if (self.available_materials.get(key) and self.available_materials[key]['batch_no']):
+			new_rm_obj = None
+			for batch_no, batch_qty in self.available_materials[key]['batch_no'].items():
+				if batch_qty >= qty:
+					self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
+					self.available_materials[key]['batch_no'][batch_no] -= qty
+					return
+
+				elif qty > 0 and batch_qty > 0:
+					qty -= batch_qty
+					new_rm_obj = self.append(self.raw_material_table, bom_item)
+					new_rm_obj.reference_name = item_row.name
+					self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty)
+					self.available_materials[key]['batch_no'][batch_no] = 0
+
+			if abs(qty) > 0 and not new_rm_obj:
+				self.__set_consumed_qty(rm_obj, qty)
+		else:
+			self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty)
+			self.__set_serial_nos(item_row, rm_obj)
+
+	def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
+		rm_obj.required_qty = required_qty
+		rm_obj.consumed_qty = consumed_qty
+
+	def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty):
+		rm_obj.update({'consumed_qty': qty, 'batch_no': batch_no,
+			'required_qty': qty, 'purchase_order': item_row.purchase_order})
+
+		self.__set_serial_nos(item_row, rm_obj)
+
+	def __set_serial_nos(self, item_row, rm_obj):
+		key = (rm_obj.rm_item_code, item_row.item_code, item_row.purchase_order)
+		if (self.available_materials.get(key) and self.available_materials[key]['serial_no']):
+			used_serial_nos = self.available_materials[key]['serial_no'][0: cint(rm_obj.consumed_qty)]
+			rm_obj.serial_no = '\n'.join(used_serial_nos)
+
+			# Removed the used serial nos from the list
+			for sn in used_serial_nos:
+				self.available_materials[key]['serial_no'].remove(sn)
+
+	def set_consumed_qty_in_po(self):
+		# Update consumed qty back in the purchase order
+		if self.is_subcontracted != 'Yes':
+			return
+
+		self.__get_purchase_orders()
+		itemwise_consumed_qty = defaultdict(float)
+		for doctype in ['Purchase Receipt', 'Purchase Invoice']:
+			consumed_items, pr_items = self.__update_consumed_materials(doctype, return_consumed_items=True)
+
+			for row in consumed_items:
+				key = (row.rm_item_code, row.main_item_code, pr_items.get(row.reference_name))
+				itemwise_consumed_qty[key] += row.consumed_qty
+
+		self.__update_consumed_qty_in_po(itemwise_consumed_qty)
+
+	def __update_consumed_qty_in_po(self, itemwise_consumed_qty):
+		fields = ['main_item_code', 'rm_item_code', 'parent', 'supplied_qty', 'name']
+		filters = {'docstatus': 1, 'parent': ('in', self.purchase_orders)}
+
+		for row in frappe.get_all('Purchase Order Item Supplied', fields = fields, filters=filters, order_by='idx'):
+			key = (row.rm_item_code, row.main_item_code, row.parent)
+			consumed_qty = itemwise_consumed_qty.get(key, 0)
+
+			if row.supplied_qty < consumed_qty:
+				consumed_qty = row.supplied_qty
+
+			itemwise_consumed_qty[key] -= consumed_qty
+			frappe.db.set_value('Purchase Order Item Supplied', row.name, 'consumed_qty', consumed_qty)
+
+	def __validate_supplied_items(self):
+		if self.doctype not in ['Purchase Invoice', 'Purchase Receipt']:
+			return
+
+		for row in self.get(self.raw_material_table):
+			self.__validate_consumed_qty(row)
+
+			key = (row.rm_item_code, row.main_item_code, row.purchase_order)
+			if not self.__transferred_items or not self.__transferred_items.get(key):
+				return
+
+			self.__validate_batch_no(row, key)
+			self.__validate_serial_no(row, key)
+
+	def __validate_consumed_qty(self, row):
+		if self.backflush_based_on != 'BOM' and flt(row.consumed_qty) == 0.0:
+			msg = f'Row {row.idx}: the consumed qty cannot be zero for the item {frappe.bold(row.rm_item_code)}'
+
+			frappe.throw(_(msg),title=_('Consumed Items Qty Check'))
+
+	def __validate_batch_no(self, row, key):
+		if row.get('batch_no') and row.get('batch_no') not in self.__transferred_items.get(key).get('batch_no'):
+			link = get_link_to_form('Purchase Order', row.purchase_order)
+			msg = f'The Batch No {frappe.bold(row.get("batch_no"))} has not supplied against the Purchase Order {link}'
+			frappe.throw(_(msg), title=_("Incorrect Batch Consumed"))
+
+	def __validate_serial_no(self, row, key):
+		if row.get('serial_no'):
+			serial_nos = get_serial_nos(row.get('serial_no'))
+			incorrect_sn = set(serial_nos).difference(self.__transferred_items.get(key).get('serial_no'))
+
+			if incorrect_sn:
+				incorrect_sn = "\n".join(incorrect_sn)
+				link = get_link_to_form('Purchase Order', row.purchase_order)
+				msg = f'The Serial Nos {incorrect_sn} has not supplied against the Purchase Order {link}'
+				frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed"))
\ No newline at end of file
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 9fae494..56da5b7 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -54,6 +54,7 @@
 			if item.item_code and item.get('item_tax_template'):
 				item_doc = frappe.get_cached_doc("Item", item.item_code)
 				args = {
+					'net_rate': item.net_rate or item.rate,
 					'tax_category': self.doc.get('tax_category'),
 					'posting_date': self.doc.get('posting_date'),
 					'bill_date': self.doc.get('bill_date'),
@@ -77,10 +78,12 @@
 
 				taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
 
-				if item.item_tax_template not in taxes:
-					frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
-						item.idx, frappe.bold(item.item_code)
-					))
+				if taxes:
+					if item.item_tax_template not in taxes:
+						item.item_tax_template = taxes[0]
+						frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
+							item.idx, frappe.bold(item.item_code)
+						))
 
 	def validate_conversion_rate(self):
 		# validate conversion rate
@@ -375,10 +378,10 @@
 
 	def manipulate_grand_total_for_inclusive_tax(self):
 		# if fully inclusive taxes and diff
-		if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]):
+		if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")):
 			last_tax = self.doc.get("taxes")[-1]
-			non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
-				for d in self.doc.get("taxes") if not d.included_in_print_rate])
+			non_inclusive_tax_amount = sum(flt(d.tax_amount_after_discount_amount)
+				for d in self.doc.get("taxes") if not d.included_in_print_rate)
 
 			diff = self.doc.total + non_inclusive_tax_amount \
 				- flt(last_tax.total, last_tax.precision("total"))
@@ -518,8 +521,8 @@
 
 	def calculate_total_advance(self):
 		if self.doc.docstatus < 2:
-			total_allocated_amount = sum([flt(adv.allocated_amount, adv.precision("allocated_amount"))
-				for adv in self.doc.get("advances")])
+			total_allocated_amount = sum(flt(adv.allocated_amount, adv.precision("allocated_amount"))
+				for adv in self.doc.get("advances"))
 
 			self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
 
@@ -619,7 +622,7 @@
 
 		if self.doc.doctype == "Sales Invoice" \
 			and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
-			and any([d.type == "Cash" for d in self.doc.payments]):
+			and any(d.type == "Cash" for d in self.doc.payments):
 			grand_total = self.doc.rounded_total or self.doc.grand_total
 			base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
 
@@ -655,7 +658,13 @@
 					item.margin_type = None
 					item.margin_rate_or_amount = 0.0
 
-			if item.margin_type and item.margin_rate_or_amount:
+			if not item.pricing_rules and flt(item.rate) > flt(item.price_list_rate):
+				item.margin_type = "Amount"
+				item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
+						item.precision("margin_rate_or_amount"))
+				item.rate_with_margin = item.rate
+
+			elif item.margin_type and item.margin_rate_or_amount:
 				margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
 				rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
 				base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
@@ -683,7 +692,6 @@
 
 		self.calculate_paid_amount()
 
-
 def get_itemised_tax_breakup_html(doc):
 	if not doc.taxes:
 		return
diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py
index 66459fd..7a4b2d3 100644
--- a/erpnext/controllers/tests/test_mapper.py
+++ b/erpnext/controllers/tests/test_mapper.py
@@ -26,8 +26,8 @@
 
 		# Assert that all inserted items are present in updated sales order
 		src_items = item_list_1 + item_list_2 + item_list_3
-		self.assertEqual(set([d for d in src_items]),
-			set([d.item_code for d in updated_so.items]))
+		self.assertEqual(set(d for d in src_items),
+			set(d.item_code for d in updated_so.items))
 
 
 	def make_quotation(self, item_list, customer):
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index ecf041e..7c072e4 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -113,7 +113,7 @@
 			doc.set_indicator()
 
 		doc.status_display = ", ".join(doc.status_display)
-		doc.items_preview = ", ".join([d.item_name for d in doc.items if d.item_name])
+		doc.items_preview = ", ".join(d.item_name for d in doc.items if d.item_name)
 		result.append(doc)
 
 	return result
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/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index 2e09a76..4ba4140 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -280,7 +280,6 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:",
    "fieldname": "territory",
    "fieldtype": "Link",
    "label": "Territory",
@@ -431,7 +430,7 @@
  "icon": "fa fa-info-sign",
  "idx": 195,
  "links": [],
- "modified": "2021-01-06 19:42:46.190051",
+ "modified": "2021-06-04 10:11:22.831139",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Opportunity",
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
index 3f5c95a..fe5707a 100644
--- a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
@@ -22,10 +22,10 @@
 	get_chart_data: function (_columns, result) {
 		return {
 			data: {
-				labels: result.map(d => d[0]),
+				labels: result.map(d => d.creation_date),
 				datasets: [{
 					name: "First Response Time",
-					values: result.map(d => d[1])
+					values: result.map(d => d.first_response_time)
 				}]
 			},
 			type: "line",
@@ -35,8 +35,7 @@
 						hide_days: 0,
 						hide_seconds: 0
 					};
-					value = frappe.utils.get_formatted_duration(d, duration_options);
-					return value;
+					return frappe.utils.get_formatted_duration(d, duration_options);
 				}
 			}
 		}
diff --git a/erpnext/education/doctype/assessment_result/assessment_result.js b/erpnext/education/doctype/assessment_result/assessment_result.js
index 617a873..c35f607 100644
--- a/erpnext/education/doctype/assessment_result/assessment_result.js
+++ b/erpnext/education/doctype/assessment_result/assessment_result.js
@@ -6,7 +6,8 @@
 		if (!frm.doc.__islocal) {
 			frm.trigger('setup_chart');
 		}
-		frm.set_df_property('details', 'read_only', 1);
+
+		frm.get_field('details').grid.cannot_add_rows = true;
 
 		frm.set_query('course', function() {
 			return {
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
index 8f51fef..3070e6a 100644
--- a/erpnext/education/utils.py
+++ b/erpnext/education/utils.py
@@ -219,7 +219,6 @@
 	try:
 		quiz = frappe.get_doc("Quiz", quiz_name)
 		questions = quiz.get_questions()
-		duration = quiz.duration
 	except:
 		frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
 		return None
@@ -236,15 +235,17 @@
 		return {
 			'questions': questions,
 			'activity': None,
-			'duration':duration
+			'is_time_bound': quiz.is_time_bound,
+			'duration': quiz.duration
 		}
 
 	student = get_current_student()
 	course_enrollment = get_enrollment("course", course, student.name)
 	status, score, result, time_taken = check_quiz_completion(quiz, course_enrollment)
 	return {
-		'questions': questions, 
+		'questions': questions,
 		'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken},
+		'is_time_bound': quiz.is_time_bound,
 		'duration': quiz.duration
 	}
 
@@ -354,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)
 
@@ -372,9 +373,9 @@
 def check_quiz_completion(quiz, enrollment_name):
 	attempts = frappe.get_all("Quiz Activity",
 		filters={
-			'enrollment': enrollment_name, 
+			'enrollment': enrollment_name,
 			'quiz': quiz.name
-		}, 
+		},
 		fields=["name", "activity_date", "score", "status", "time_taken"]
 	)
 	status = False if quiz.max_attempts == 0 else bool(len(attempts) >= quiz.max_attempts)
@@ -389,4 +390,4 @@
 		time_taken = attempts[0]['time_taken']
 		if result == 'Pass':
 			status = True
-	return status, score, result, time_taken
\ No newline at end of file
+	return status, score, result, time_taken
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/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py
index 789d452..cebcb20 100644
--- a/erpnext/healthcare/doctype/patient/patient.py
+++ b/erpnext/healthcare/doctype/patient/patient.py
@@ -33,21 +33,21 @@
 		self.reload() # self.notify_update()
 
 	def on_update(self):
-		if self.customer:
-			customer = frappe.get_doc('Customer', self.customer)
-			if self.customer_group:
-				customer.customer_group = self.customer_group
-			if self.territory:
-				customer.territory = self.territory
+		if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'):
+			if self.customer:
+				customer = frappe.get_doc('Customer', self.customer)
+				if self.customer_group:
+					customer.customer_group = self.customer_group
+				if self.territory:
+					customer.territory = self.territory
 
-			customer.customer_name = self.patient_name
-			customer.default_price_list = self.default_price_list
-			customer.default_currency = self.default_currency
-			customer.language = self.language
-			customer.ignore_mandatory = True
-			customer.save(ignore_permissions=True)
-		else:
-			if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'):
+				customer.customer_name = self.patient_name
+				customer.default_price_list = self.default_price_list
+				customer.default_currency = self.default_currency
+				customer.language = self.language
+				customer.ignore_mandatory = True
+				customer.save(ignore_permissions=True)
+			else:
 				create_customer(self)
 
 	def set_full_name(self):
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 55169df..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 = [
@@ -332,7 +333,9 @@
 		"erpnext.projects.doctype.project.project.collect_project_status",
 		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
 		"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
-		"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
+		"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders"
+	],
+	"hourly_long": [
 		"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
 	],
 	"daily": [
diff --git a/erpnext/hr/doctype/attendance/attendance.js b/erpnext/hr/doctype/attendance/attendance.js
index c3c3cb8..7964078 100644
--- a/erpnext/hr/doctype/attendance/attendance.js
+++ b/erpnext/hr/doctype/attendance/attendance.js
@@ -11,5 +11,5 @@
 cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
 	return{
 		query: "erpnext.controllers.queries.employee_query"
-	}	
+	}
 }
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index f3b8a79..3412675 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -15,6 +15,7 @@
 		validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
 		self.validate_attendance_date()
 		self.validate_duplicate_record()
+		self.validate_employee_status()
 		self.check_leave_record()
 
 	def validate_attendance_date(self):
@@ -38,6 +39,10 @@
 			frappe.throw(_("Attendance for employee {0} is already marked for the date {1}").format(
 				frappe.bold(self.employee), frappe.bold(self.attendance_date)))
 
+	def validate_employee_status(self):
+		if frappe.db.get_value("Employee", self.employee, "status") == "Inactive":
+			frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee))
+
 	def check_leave_record(self):
 		leave_record = frappe.db.sql("""
 			select leave_type, half_day, half_day_date
diff --git a/erpnext/hr/doctype/attendance/attendance_list.js b/erpnext/hr/doctype/attendance/attendance_list.js
index 0c7eafe..9a3bac0 100644
--- a/erpnext/hr/doctype/attendance/attendance_list.js
+++ b/erpnext/hr/doctype/attendance/attendance_list.js
@@ -21,6 +21,9 @@
 						label: __('For Employee'),
 						fieldtype: 'Link',
 						options: 'Employee',
+						get_query: () => {
+							return {query: "erpnext.controllers.queries.employee_query"}
+						},
 						reqd: 1,
 						onchange: function() {
 							dialog.set_df_property("unmarked_days", "hidden", 1);
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index 5123d6a..d592a9c 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -207,7 +207,7 @@
    "label": "Status",
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "Active\nLeft",
+   "options": "Active\nInactive\nSuspended\nLeft",
    "reqd": 1,
    "search_index": 1
   },
@@ -813,7 +813,7 @@
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2021-01-02 16:54:33.477439",
+ "modified": "2021-06-17 11:31:37.730760",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee",
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index ed7d588..fa017d9 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 
-from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime, cstr
+from frappe.utils import getdate, validate_email_address, today, add_years, cstr
 from frappe.model.naming import set_name_by_naming_series
 from frappe import throw, _, scrub
 from frappe.permissions import add_user_permission, remove_user_permission, \
@@ -12,7 +12,6 @@
 from frappe.model.document import Document
 from erpnext.utilities.transaction_base import delete_events
 from frappe.utils.nestedset import NestedSet
-from erpnext.hr.doctype.job_offer.job_offer import get_staffing_plan_detail
 
 class EmployeeUserDisabledError(frappe.ValidationError): pass
 class EmployeeLeftValidationError(frappe.ValidationError): pass
@@ -37,7 +36,7 @@
 
 	def validate(self):
 		from erpnext.controllers.status_updater import validate_status
-		validate_status(self.status, ["Active", "Temporary Leave", "Left"])
+		validate_status(self.status, ["Active", "Inactive", "Suspended", "Left"])
 
 		self.employee = self.name
 		self.set_employee_name()
@@ -478,7 +477,7 @@
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False):
 
-	filters = [['status', '!=', 'Left']]
+	filters = [['status', '=', 'Active']]
 	if company and company != 'All Companies':
 		filters.append(['company', '=', company])
 
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index 285374d..e853bee 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -7,7 +7,8 @@
 		'heatmap_message': _('This is based on the attendance of this Employee'),
 		'fieldname': 'employee',
 		'non_standard_fieldnames': {
-			'Bank Account': 'party'
+			'Bank Account': 'party',
+			'Employee Grievance': 'raised_by'
 		},
 		'transactions': [
 			{
@@ -20,7 +21,7 @@
 			},
 			{
 				'label': _('Lifecycle'),
-				'items': ['Employee Transfer', 'Employee Promotion', 'Employee Separation']
+				'items': ['Employee Transfer', 'Employee Promotion', 'Employee Separation', 'Employee Grievance']
 			},
 			{
 				'label': _('Shift'),
diff --git a/erpnext/hr/doctype/employee/employee_list.js b/erpnext/hr/doctype/employee/employee_list.js
index 4483703..d37e149 100644
--- a/erpnext/hr/doctype/employee/employee_list.js
+++ b/erpnext/hr/doctype/employee/employee_list.js
@@ -3,7 +3,7 @@
 	filters: [["status","=", "Active"]],
 	get_indicator: function(doc) {
 		var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
-		indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "gray"}[doc.status];
+		indicator[1] = {"Active": "green", "Inactive": "red", "Left": "gray", "Suspended": "orange"}[doc.status];
 		return indicator;
 	}
 };
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/hr/doctype/employee_grievance/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/hr/doctype/employee_grievance/__init__.py
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.js b/erpnext/hr/doctype/employee_grievance/employee_grievance.js
new file mode 100644
index 0000000..25c5bad
--- /dev/null
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.js
@@ -0,0 +1,39 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Grievance', {
+	setup: function(frm) {
+		frm.set_query('grievance_against_party', function() {
+			return {
+				filters: {
+					name: ['in', [
+						'Company', 'Department', 'Employee Group', 'Employee Grade', 'Employee']
+					]
+				}
+			};
+		});
+		frm.set_query('associated_document_type', function() {
+			let ignore_modules = ["Setup", "Core", "Integrations", "Automation", "Website",
+				"Utilities", "Event Streaming", "Social", "Chat", "Data Migration", "Printing", "Desk", "Custom"];
+			return {
+				filters: {
+					istable: 0,
+					issingle: 0,
+					module: ["Not In", ignore_modules]
+				}
+			};
+		});
+	},
+
+	grievance_against_party: function(frm) {
+		let filters = {};
+		if (frm.doc.grievance_against_party == 'Employee' && frm.doc.raised_by) {
+			filters.name =  ["!=", frm.doc.raised_by];
+		}
+		frm.set_query('grievance_against', function() {
+			return {
+				filters: filters
+			};
+		});
+	},
+});
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.json b/erpnext/hr/doctype/employee_grievance/employee_grievance.json
new file mode 100644
index 0000000..5a91856
--- /dev/null
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.json
@@ -0,0 +1,261 @@
+{
+ "actions": [],
+ "autoname": "HR-GRIEV-.YYYY.-.#####",
+ "creation": "2021-05-11 13:41:51.485295",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "subject",
+  "raised_by",
+  "employee_name",
+  "designation",
+  "column_break_3",
+  "date",
+  "status",
+  "reports_to",
+  "grievance_details_section",
+  "grievance_against_party",
+  "grievance_against",
+  "grievance_type",
+  "column_break_11",
+  "associated_document_type",
+  "associated_document",
+  "section_break_14",
+  "description",
+  "investigation_details_section",
+  "cause_of_grievance",
+  "resolution_details_section",
+  "resolved_by",
+  "resolution_date",
+  "employee_responsible",
+  "column_break_16",
+  "resolution_detail",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "grievance_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Grievance Type",
+   "options": "Grievance Type",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Date ",
+   "reqd": 1
+  },
+  {
+   "default": "Open",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Status",
+   "options": "Open\nInvestigated\nResolved\nInvalid",
+   "reqd": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description",
+   "reqd": 1
+  },
+  {
+   "fieldname": "cause_of_grievance",
+   "fieldtype": "Text",
+   "label": "Cause of Grievance",
+   "mandatory_depends_on": "eval: doc.status == \"Investigated\" || doc.status ==  \"Resolved\""
+  },
+  {
+   "fieldname": "resolution_details_section",
+   "fieldtype": "Section Break",
+   "label": "Resolution Details"
+  },
+  {
+   "fieldname": "resolved_by",
+   "fieldtype": "Link",
+   "label": "Resolved By",
+   "mandatory_depends_on": "eval: doc.status == \"Resolved\"",
+   "options": "User"
+  },
+  {
+   "fieldname": "employee_responsible",
+   "fieldtype": "Link",
+   "label": "Employee Responsible ",
+   "options": "Employee"
+  },
+  {
+   "fieldname": "resolution_detail",
+   "fieldtype": "Small Text",
+   "label": "Resolution Details",
+   "mandatory_depends_on": "eval: doc.status == \"Resolved\""
+  },
+  {
+   "fieldname": "column_break_16",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "resolution_date",
+   "fieldtype": "Date",
+   "label": "Resolution Date",
+   "mandatory_depends_on": "eval: doc.status == \"Resolved\""
+  },
+  {
+   "fieldname": "grievance_against",
+   "fieldtype": "Dynamic Link",
+   "label": "Grievance Against",
+   "options": "grievance_against_party",
+   "reqd": 1
+  },
+  {
+   "fieldname": "raised_by",
+   "fieldtype": "Link",
+   "label": "Raised By",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Grievance",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fetch_from": "raised_by.designation",
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "label": "Designation",
+   "options": "Designation",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "raised_by.reports_to",
+   "fieldname": "reports_to",
+   "fieldtype": "Link",
+   "label": "Reports To",
+   "options": "Employee",
+   "read_only": 1
+  },
+  {
+   "fieldname": "grievance_details_section",
+   "fieldtype": "Section Break",
+   "label": "Grievance Details"
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_14",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "grievance_against_party",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Grievance Against Party",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "associated_document_type",
+   "fieldtype": "Link",
+   "label": "Associated Document Type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "associated_document",
+   "fieldtype": "Dynamic Link",
+   "label": "Associated Document",
+   "options": "associated_document_type"
+  },
+  {
+   "fieldname": "investigation_details_section",
+   "fieldtype": "Section Break",
+   "label": "Investigation Details"
+  },
+  {
+   "fetch_from": "raised_by.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "label": "Employee Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "subject",
+   "fieldtype": "Data",
+   "label": "Subject",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-06-21 12:51:01.499486",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Employee Grievance",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "select": 1,
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "select": 1,
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "search_fields": "subject,raised_by,grievance_against_party",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "subject",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.py b/erpnext/hr/doctype/employee_grievance/employee_grievance.py
new file mode 100644
index 0000000..503b5ea
--- /dev/null
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _, bold
+from frappe.model.document import Document
+
+class EmployeeGrievance(Document):
+	def on_submit(self):
+		if self.status not in ["Invalid", "Resolved"]:
+			frappe.throw(_("Only Employee Grievance with status {0} or {1} can be submitted").format(
+				bold("Invalid"),
+				bold("Resolved"))
+			)
+
diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js
new file mode 100644
index 0000000..fc08e21
--- /dev/null
+++ b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings["Employee Grievance"] = {
+	has_indicator_for_draft: 1,
+	get_indicator: function(doc) {
+		var colors = {
+			"Open": "red",
+			"Investigated": "orange",
+			"Resolved": "green",
+			"Invalid": "grey"
+		};
+		return [__(doc.status), colors[doc.status], "status,=," + doc.status];
+	}
+};
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
new file mode 100644
index 0000000..a615b20
--- /dev/null
+++ b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+import unittest
+from frappe.utils import today
+from erpnext.hr.doctype.employee.test_employee import make_employee
+class TestEmployeeGrievance(unittest.TestCase):
+	def test_create_employee_grievance(self):
+		create_employee_grievance()
+
+def create_employee_grievance():
+	grievance_type = create_grievance_type()
+	emp_1 = make_employee("test_emp_grievance_@example.com", company="_Test Company")
+	emp_2 = make_employee("testculprit@example.com", company="_Test Company")
+
+	grievance = frappe.new_doc("Employee Grievance")
+	grievance.subject = "Test Employee Grievance"
+	grievance.raised_by = emp_1
+	grievance.date = today()
+	grievance.grievance_type = grievance_type
+	grievance.grievance_against_party = "Employee"
+	grievance.grievance_against = emp_2
+	grievance.description = "test descrip"
+
+	#set cause
+	grievance.cause_of_grievance = "test cause"
+
+	#resolution details
+	grievance.resolution_date = today()
+	grievance.resolution_detail = "test resolution detail"
+	grievance.resolved_by = "test_emp_grievance_@example.com"
+	grievance.employee_responsible = emp_2
+	grievance.status = "Resolved"
+
+	grievance.save()
+	grievance.submit()
+
+	return grievance
+
+
+def create_grievance_type():
+	if frappe.db.exists("Grievance Type", "Employee Abuse"):
+		return frappe.get_doc("Grievance Type", "Employee Abuse")
+	grievance_type = frappe.new_doc("Grievance Type")
+	grievance_type.name = "Employee Abuse"
+	grievance_type.description = "Test"
+	grievance_type.save()
+
+	return grievance_type.name
+
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index 4994921..83fb235 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -11,12 +11,12 @@
 
 class EmployeePromotion(Document):
 	def validate(self):
-		if frappe.get_value("Employee", self.employee, "status") == "Left":
-			frappe.throw(_("Cannot promote Employee with status Left"))
+		if frappe.get_value("Employee", self.employee, "status") != "Active":
+			frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
 
 	def before_submit(self):
 		if getdate(self.promotion_date) > getdate():
-			frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date "),
+			frappe.throw(_("Employee Promotion cannot be submitted before Promotion Date"),
 				frappe.DocstatusTransitionError)
 
 	def on_submit(self):
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index 3539970..6eec9fa 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -11,12 +11,12 @@
 
 class EmployeeTransfer(Document):
 	def validate(self):
-		if frappe.get_value("Employee", self.employee, "status") == "Left":
-			frappe.throw(_("Cannot transfer Employee with status Left"))
+		if frappe.get_value("Employee", self.employee, "status") != "Active":
+			frappe.throw(_("Cannot transfer Employee with status Left or Inactive"))
 
 	def before_submit(self):
 		if getdate(self.transfer_date) > getdate():
-			frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date "),
+			frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),
 				frappe.DocstatusTransitionError)
 
 	def on_submit(self):
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/patches/repair_tools/__init__.py b/erpnext/hr/doctype/grievance_type/__init__.py
similarity index 100%
rename from erpnext/patches/repair_tools/__init__.py
rename to erpnext/hr/doctype/grievance_type/__init__.py
diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.js b/erpnext/hr/doctype/grievance_type/grievance_type.js
new file mode 100644
index 0000000..425f2fd
--- /dev/null
+++ b/erpnext/hr/doctype/grievance_type/grievance_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Grievance Type', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.json b/erpnext/hr/doctype/grievance_type/grievance_type.json
new file mode 100644
index 0000000..1dce00a
--- /dev/null
+++ b/erpnext/hr/doctype/grievance_type/grievance_type.json
@@ -0,0 +1,70 @@
+{
+ "actions": [],
+ "autoname": "Prompt",
+ "creation": "2021-05-11 12:41:50.256071",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "section_break_5",
+  "description"
+ ],
+ "fields": [
+  {
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-06-21 12:54:37.764712",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Grievance Type",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/grievance_type/grievance_type.py b/erpnext/hr/doctype/grievance_type/grievance_type.py
new file mode 100644
index 0000000..618cf0a
--- /dev/null
+++ b/erpnext/hr/doctype/grievance_type/grievance_type.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class GrievanceType(Document):
+	pass
diff --git a/erpnext/hr/doctype/grievance_type/test_grievance_type.py b/erpnext/hr/doctype/grievance_type/test_grievance_type.py
new file mode 100644
index 0000000..a02a34d
--- /dev/null
+++ b/erpnext/hr/doctype/grievance_type/test_grievance_type.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestGrievanceType(unittest.TestCase):
+	pass
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_list.js b/erpnext/hr/doctype/job_applicant/job_applicant_list.js
index 3b9141b..2ad0d59 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_list.js
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_list.js
@@ -2,7 +2,7 @@
 // MIT License. See license.txt
 
 frappe.listview_settings['Job Applicant'] = {
-	add_fields: ["company", "designation", "job_applicant", "status"],
+	add_fields: ["status"],
 	get_indicator: function (doc) {
 		if (doc.status == "Accepted") {
 			return [__(doc.status), "green", "status,=," + doc.status];
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
index ae02c51..3a6539e 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
@@ -110,6 +110,7 @@
    "label": "Allocation"
   },
   {
+   "allow_on_submit": 1,
    "bold": 1,
    "fieldname": "new_leaves_allocated",
    "fieldtype": "Float",
@@ -235,7 +236,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-14 15:28:26.335104",
+ "modified": "2021-06-03 15:28:26.335104",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Allocation",
@@ -277,4 +278,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "timeline_field": "employee"
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 11302ca..4757cd3 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -8,6 +8,7 @@
 from frappe.model.document import Document
 from erpnext.hr.utils import set_employee_name, get_leave_period
 from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation, create_leave_ledger_entry
+from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period
 
 class OverlapError(frappe.ValidationError): pass
 class BackDatedAllocationError(frappe.ValidationError): pass
@@ -55,6 +56,43 @@
 		if self.carry_forward:
 			self.set_carry_forwarded_leaves_in_previous_allocation(on_cancel=True)
 
+	def on_update_after_submit(self):
+		if self.has_value_changed("new_leaves_allocated"):
+			self.validate_against_leave_applications()
+			leaves_to_be_added = self.new_leaves_allocated - self.get_existing_leave_count()
+			args = {
+				"leaves": leaves_to_be_added,
+				"from_date": self.from_date,
+				"to_date": self.to_date,
+				"is_carry_forward": 0
+			}
+			create_leave_ledger_entry(self, args, True)
+
+	def get_existing_leave_count(self):
+		ledger_entries = frappe.get_all("Leave Ledger Entry",
+								filters={
+									"transaction_type": "Leave Allocation",
+									"transaction_name": self.name,
+									"employee": self.employee,
+									"company": self.company,
+									"leave_type": self.leave_type
+								},
+								pluck="leaves")
+		total_existing_leaves = 0
+		for entry in ledger_entries:
+			total_existing_leaves += entry
+
+		return total_existing_leaves
+
+	def validate_against_leave_applications(self):
+		leaves_taken = get_approved_leaves_for_period(self.employee, self.leave_type,
+			self.from_date, self.to_date)
+		if flt(leaves_taken) > flt(self.total_leaves_allocated):
+			if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
+				frappe.msgprint(_("Note: Total allocated leaves {0} shouldn't be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken))
+			else:
+				frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
+
 	def update_leave_policy_assignments_when_no_allocations_left(self):
 		allocations = frappe.db.get_list("Leave Allocation", filters = {
 			"docstatus": 1,
@@ -225,4 +263,4 @@
 
 def validate_carry_forward(leave_type):
 	if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
-		frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type))
\ No newline at end of file
+		frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type))
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index 6e7ae87..bff06e6 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
 import frappe
+import erpnext
 import unittest
 from frappe.utils import nowdate, add_months, getdate, add_days
 from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
@@ -164,6 +165,51 @@
 		leave_allocation.cancel()
 		self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_allocation.name}))
 
+	def test_leave_addition_after_submit(self):
+		frappe.db.sql("delete from `tabLeave Allocation`")
+		frappe.db.sql("delete from `tabLeave Ledger Entry`")
+
+		leave_allocation = create_leave_allocation()
+		leave_allocation.submit()
+		self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+		leave_allocation.new_leaves_allocated = 40
+		leave_allocation.submit()
+		self.assertTrue(leave_allocation.total_leaves_allocated, 40)
+
+	def test_leave_subtraction_after_submit(self):
+		frappe.db.sql("delete from `tabLeave Allocation`")
+		frappe.db.sql("delete from `tabLeave Ledger Entry`")
+		leave_allocation = create_leave_allocation()
+		leave_allocation.submit()
+		self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+		leave_allocation.new_leaves_allocated = 10
+		leave_allocation.submit()
+		self.assertTrue(leave_allocation.total_leaves_allocated, 10)
+
+	def test_against_leave_application_validation_after_submit(self):
+		frappe.db.sql("delete from `tabLeave Allocation`")
+		frappe.db.sql("delete from `tabLeave Ledger Entry`")
+
+		leave_allocation = create_leave_allocation()
+		leave_allocation.submit()
+		self.assertTrue(leave_allocation.total_leaves_allocated, 15)
+		employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
+		leave_application = frappe.get_doc({
+			"doctype": 'Leave Application',
+			"employee": employee.name,
+			"leave_type": "_Test Leave Type",
+			"from_date": add_months(nowdate(), 2),
+			"to_date": add_months(add_days(nowdate(), 10), 2),
+			"company": erpnext.get_default_company() or "_Test Company",
+			"docstatus": 1,
+			"status": "Approved",
+			"leave_approver": 'test@example.com'
+		})
+		leave_application.submit()
+		leave_allocation.new_leaves_allocated = 8
+		leave_allocation.total_leaves_allocated = 8
+		self.assertRaises(frappe.ValidationError, leave_allocation.submit)
+
 def create_leave_allocation(**args):
 	args = frappe._dict(args)
 
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 533149a..e6c783a 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -41,7 +41,7 @@
 
 			detail.total_estimated_cost = 0
 			if detail.number_of_positions > 0:
-				if detail.vacancies > 0 and detail.estimated_cost_per_position:
+				if detail.vacancies and detail.estimated_cost_per_position:
 					detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
 
 			self.total_estimated_budget += detail.total_estimated_cost
@@ -76,12 +76,12 @@
 		if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or \
 			flt(staffing_plan_detail.total_estimated_cost) > flt(parent_plan_details[0].total_estimated_cost):
 			frappe.throw(_("You can only plan for upto {0} vacancies and budget {1} \
-				for {2} as per staffing plan {3} for parent company {4}."
-				.format(cint(parent_plan_details[0].vacancies),
+				for {2} as per staffing plan {3} for parent company {4}.").format(
+					cint(parent_plan_details[0].vacancies),
 					parent_plan_details[0].total_estimated_cost,
 					frappe.bold(staffing_plan_detail.designation),
 					parent_plan_details[0].name,
-					parent_company)), ParentCompanyError)
+					parent_company), ParentCompanyError)
 
 		#Get vacanices already planned for all companies down the hierarchy of Parent Company
 		lft, rgt = frappe.get_cached_value('Company',  parent_company,  ["lft", "rgt"])
@@ -98,14 +98,14 @@
 			(flt(parent_plan_details[0].total_estimated_cost) < \
 			(flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost))):
 			frappe.throw(_("{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. \
-				You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
-				.format(cint(all_sibling_details.vacancies),
+				You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}.").format(
+					cint(all_sibling_details.vacancies),
 					all_sibling_details.total_estimated_cost,
 					frappe.bold(staffing_plan_detail.designation),
 					parent_company,
 					cint(parent_plan_details[0].vacancies),
 					parent_plan_details[0].total_estimated_cost,
-					parent_plan_details[0].name)))
+					parent_plan_details[0].name))
 
 	def validate_with_subsidiary_plans(self, staffing_plan_detail):
 		#Valdate this plan with all child company plan
@@ -121,11 +121,11 @@
 			cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) or \
 			flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost):
 			frappe.throw(_("Subsidiary companies have already planned for {1} vacancies at a budget of {2}. \
-				Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
-				.format(self.company,
+				Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies").format(
+					self.company,
 					cint(children_details.vacancies),
 					children_details.total_estimated_cost,
-					frappe.bold(staffing_plan_detail.designation))), SubsidiaryCompanyError)
+					frappe.bold(staffing_plan_detail.designation)), SubsidiaryCompanyError)
 
 @frappe.whitelist()
 def get_designation_counts(designation, company):
@@ -170,4 +170,4 @@
 				designation, from_date, to_date)
 
 	# Only a single staffing plan can be active for a designation on given date
-	return staffing_plan if staffing_plan else None
\ No newline at end of file
+	return staffing_plan if staffing_plan else None
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/hr/notification/training_scheduled/training_scheduled.json b/erpnext/hr/notification/training_scheduled/training_scheduled.json
index e49541e..f365003 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.json
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.json
@@ -11,8 +11,8 @@
  "event": "Submit",
  "idx": 0,
  "is_standard": 1,
- "message": "<table class=\"panel-header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n    <tr height=\"10\"></tr>\n    <tr>\n        <td width=\"15\"></td>\n        <td>\n            <div class=\"text-medium text-muted\">\n                <span>{{_(\"Training Event:\")}} {{ doc.event_name }}</span>\n            </div>\n        </td>\n        <td width=\"15\"></td>\n    </tr>\n    <tr height=\"10\"></tr>\n</table>\n\n<table class=\"panel-body\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n    <tr height=\"10\"></tr>\n    <tr>\n        <td width=\"15\"></td>\n        <td>\n            <div>\n                {{ doc.introduction }}\n                <ul class=\"list-unstyled\" style=\"line-height: 1.7\">\n                    <li>{{_(\"Event Location\")}}: <b>{{ doc.location }}</b></li>\n                    {% set start = frappe.utils.get_datetime(doc.start_time) %}\n                    {% set end = frappe.utils.get_datetime(doc.end_time) %}\n                    {% if start.date() == end.date() %}\n                    <li>{{_(\"Date\")}}: <b>{{ start.strftime(\"%A, %d %b %Y\") }}</b></li>\n                    <li>\n                        {{_(\"Timing\")}}: <b>{{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}</b>\n                    </li>\n                    {% else %}\n                    <li>{{_(\"Start Time\")}}: <b>{{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n                    </li>\n                    <li>{{_(\"End Time\")}}: <b>{{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n                    </li>\n                    {% endif %}\n                    <li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>\n                    {% if doc.is_mandatory %}\n                    <li>Note: This Training Event is mandatory</li>\n                    {% endif %}\n                </ul>\n            </div>\n        </td>\n        <td width=\"15\"></td>\n    </tr>\n    <tr height=\"10\"></tr>\n</table>",
- "modified": "2021-05-24 16:29:13.165930",
+ "message": "<table class=\"panel-header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n    <tr height=\"10\"></tr>\n    <tr>\n        <td width=\"15\"></td>\n        <td>\n            <div class=\"text-medium text-muted\">\n                <span>{{_(\"Training Event:\")}} {{ doc.event_name }}</span>\n            </div>\n        </td>\n        <td width=\"15\"></td>\n    </tr>\n    <tr height=\"10\"></tr>\n</table>\n\n<table class=\"panel-body\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n    <tr height=\"10\"></tr>\n    <tr>\n        <td width=\"15\"></td>\n        <td>\n            <div>\n                {{ doc.introduction }}\n                <ul class=\"list-unstyled\" style=\"line-height: 1.7\">\n                    <li>{{_(\"Event Location\")}}: <b>{{ doc.location }}</b></li>\n                    {% set start = frappe.utils.get_datetime(doc.start_time) %}\n                    {% set end = frappe.utils.get_datetime(doc.end_time) %}\n                    {% if start.date() == end.date() %}\n                        <li>{{_(\"Date\")}}: <b>{{ start.strftime(\"%A, %d %b %Y\") }}</b></li>\n                        <li>\n                            {{_(\"Timing\")}}: <b>{{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}</b>\n                        </li>\n                    {% else %}\n                        <li>\n                            {{_(\"Start Time\")}}: <b>{{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n                        </li>\n                        <li>{{_(\"End Time\")}}: <b>{{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b></li>\n                    {% endif %}\n                    <li>{{ _(\"Event Link\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>\n                    {% if doc.is_mandatory %}\n                        <li>{{ _(\"Note: This Training Event is mandatory\") }}</li>\n                    {% endif %}\n                </ul>\n            </div>\n        </td>\n        <td width=\"15\"></td>\n    </tr>\n    <tr height=\"10\"></tr>\n</table>",
+ "modified": "2021-06-16 14:08:12.933367",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Training Scheduled",
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.md b/erpnext/hr/notification/training_scheduled/training_scheduled.md
index 418fd49..b9ba846 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.md
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.md
@@ -24,19 +24,19 @@
                     {% set start = frappe.utils.get_datetime(doc.start_time) %}
                     {% set end = frappe.utils.get_datetime(doc.end_time) %}
                     {% if start.date() == end.date() %}
-                    <li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
-                    <li>
-                        {{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
-                    </li>
+                        <li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
+                        <li>
+                            {{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
+                        </li>
                     {% else %}
-                    <li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
-                    </li>
-                    <li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
-                    </li>
+                        <li>
+                            {{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+                        </li>
+                        <li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b></li>
                     {% endif %}
-                    <li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
+                    <li>{{ _("Event Link") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
                     {% if doc.is_mandatory %}
-                    <li>Note: This Training Event is mandatory</li>
+                        <li>{{ _("Note: This Training Event is mandatory") }}</li>
                     {% endif %}
                 </ul>
             </div>
@@ -44,4 +44,4 @@
         <td width="15"></td>
     </tr>
     <tr height="10"></tr>
-</table>
\ No newline at end of file
+</table>
diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
index 4dd4570..b8953b3 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -178,7 +178,7 @@
 			is_carry_forward, is_expired
 		FROM `tabLeave Ledger Entry`
 		WHERE employee=%(employee)s AND leave_type=%(leave_type)s
-			AND docstatus=1 AND leaves>0
+			AND docstatus=1
 			AND (from_date between %(from_date)s AND %(to_date)s
 				OR to_date between %(from_date)s AND %(to_date)s
 				OR (from_date < %(from_date)s AND to_date > %(to_date)s))
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index c5201c2..4500ba4 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -154,6 +154,24 @@
    "type": "Link"
   },
   {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Grievance Type",
+   "link_to": "Grievance Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Grievance",
+   "link_to": "Employee Grievance",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
    "dependencies": "Employee",
    "hidden": 0,
    "is_query_report": 0,
@@ -823,7 +841,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-04-26 13:36:15.413819",
+ "modified": "2021-05-13 17:19:40.524444",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR",
diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js
index 28af3a9..f9c201a 100644
--- a/erpnext/loan_management/doctype/loan/loan.js
+++ b/erpnext/loan_management/doctype/loan/loan.js
@@ -28,7 +28,8 @@
 		frm.set_query("loan_type", function () {
 			return {
 				"filters": {
-					"docstatus": 1
+					"docstatus": 1,
+					"company": frm.doc.company
 				}
 			};
 		});
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index 69d11a8..ff7fbbd 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -60,8 +60,9 @@
 			self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
 
 	def check_sanctioned_amount_limit(self):
-		total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
 		sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
+		if sanctioned_amount_limit:
+			total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
 
 		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
 			frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))
@@ -155,9 +156,29 @@
 	frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
 
 def get_total_loan_amount(applicant_type, applicant, company):
-	return frappe.db.get_value('Loan',
-		{'applicant_type': applicant_type, 'company': company, 'applicant': applicant, 'docstatus': 1},
-		'sum(loan_amount)')
+	pending_amount = 0
+	loan_details = frappe.db.get_all("Loan",
+		filters={"applicant_type": applicant_type, "company": company, "applicant": applicant, "docstatus": 1,
+			"status": ("!=", "Closed")},
+		fields=["status", "total_payment", "disbursed_amount", "total_interest_payable", "total_principal_paid",
+			"written_off_amount"])
+
+	interest_amount = flt(frappe.db.get_value("Loan Interest Accrual", {"applicant_type": applicant_type,
+		"company": company, "applicant": applicant, "docstatus": 1}, "sum(interest_amount - paid_interest_amount)"))
+
+	for loan in loan_details:
+		if loan.status in ("Disbursed", "Loan Closure Requested"):
+			pending_amount += flt(loan.total_payment) - flt(loan.total_interest_payable) \
+				- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
+		elif loan.status == "Partially Disbursed":
+			pending_amount += flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
+				- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
+		elif loan.status == "Sanctioned":
+			pending_amount += flt(loan.total_payment)
+
+	pending_amount += interest_amount
+
+	return pending_amount
 
 def get_sanctioned_amount_limit(applicant_type, applicant, company):
 	return frappe.db.get_value('Sanctioned Loan Amount',
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index fa4707c..314f58d 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -49,7 +49,11 @@
 		if not frappe.db.exists("Customer", "_Test Loan Customer"):
 			frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
 
-		self.applicant2 = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name')
+		if not frappe.db.exists("Customer", "_Test Loan Customer 1"):
+			frappe.get_doc(get_customer_dict("_Test Loan Customer 1")).insert(ignore_permissions=True)
+
+		self.applicant2 = frappe.db.get_value("Customer", {"name": "_Test Loan Customer"}, "name")
+		self.applicant3 = frappe.db.get_value("Customer", {"name": "_Test Loan Customer 1"}, "name")
 
 		create_loan(self.applicant1, "Personal Loan", 280000, "Repay Over Number of Periods", 20)
 
@@ -125,6 +129,38 @@
 		self.assertTrue(gl_entries1)
 		self.assertTrue(gl_entries2)
 
+	def test_sanctioned_amount_limit(self):
+		# Clear loan docs before checking
+		frappe.db.sql("DELETE FROM `tabLoan` where applicant = '_Test Loan Customer 1'")
+		frappe.db.sql("DELETE FROM `tabLoan Application` where applicant = '_Test Loan Customer 1'")
+		frappe.db.sql("DELETE FROM `tabLoan Security Pledge` where applicant = '_Test Loan Customer 1'")
+
+		if not frappe.db.get_value("Sanctioned Loan Amount", filters={"applicant_type": "Customer",
+			"applicant": "_Test Loan Customer 1", "company": "_Test Company"}):
+			frappe.get_doc({
+				"doctype": "Sanctioned Loan Amount",
+				"applicant_type": "Customer",
+				"applicant": "_Test Loan Customer 1",
+				"sanctioned_amount_limit": 1500000,
+				"company": "_Test Company"
+			}).insert(ignore_permissions=True)
+
+		# Make First Loan
+		pledge = [{
+			"loan_security": "Test Security 1",
+			"qty": 4000.00
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge)
+		create_pledge(loan_application)
+		loan = create_demand_loan(self.applicant3, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan.submit()
+
+		# Make second loan greater than the sanctioned amount
+		loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge,
+			do_not_save=True)
+		self.assertRaises(frappe.ValidationError, loan_application.save)
+
 	def test_regular_loan_repayment(self):
 		pledge = [{
 			"loan_security": "Test Security 1",
@@ -367,7 +403,7 @@
 		unpledge_request.load_from_db()
 		self.assertEqual(unpledge_request.docstatus, 1)
 
-	def test_santined_loan_security_unpledge(self):
+	def test_sanctioned_loan_security_unpledge(self):
 		pledge = [{
 			"loan_security": "Test Security 1",
 			"qty": 4000.00
@@ -858,7 +894,7 @@
 	return lr
 
 def create_loan_application(company, applicant, loan_type, proposed_pledges, repayment_method=None,
-	repayment_periods=None, posting_date=None):
+	repayment_periods=None, posting_date=None, do_not_save=False):
 	loan_application = frappe.new_doc('Loan Application')
 	loan_application.applicant_type = 'Customer'
 	loan_application.company = company
@@ -874,6 +910,9 @@
 	for pledge in proposed_pledges:
 		loan_application.append('proposed_pledges', pledge)
 
+	if do_not_save:
+		return loan_application
+
 	loan_application.save()
 	loan_application.submit()
 
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js
index 1365274..eccbdc3 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.js
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.js
@@ -14,6 +14,13 @@
 	refresh: function(frm) {
 		frm.trigger("toggle_fields");
 		frm.trigger("add_toolbar_buttons");
+		frm.set_query('loan_type', () => {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			};
+		});
 	},
 	repayment_method: function(frm) {
 		frm.doc.repayment_amount = frm.doc.repayment_periods = ""
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index 9c0147e..d8f3577 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -46,9 +46,11 @@
 			frappe.throw(_("Loan Amount exceeds maximum loan amount of {0} as per proposed securities").format(self.maximum_loan_amount))
 
 	def check_sanctioned_amount_limit(self):
-		total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
 		sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
 
+		if sanctioned_amount_limit:
+			total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
+
 		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
 			frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))
 
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 3d99b1f..b8b1a40 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -235,70 +235,71 @@
 		else:
 			remarks = _("Repayment against Loan: ") + self.against_loan
 
-		if self.total_penalty_paid:
+		if not loan_details.repay_from_salary:
+			if self.total_penalty_paid:
+				gle_map.append(
+					self.get_gl_dict({
+						"account": loan_details.loan_account,
+						"against": loan_details.payment_account,
+						"debit": self.total_penalty_paid,
+						"debit_in_account_currency": self.total_penalty_paid,
+						"against_voucher_type": "Loan",
+						"against_voucher": self.against_loan,
+						"remarks": _("Penalty against loan:") + self.against_loan,
+						"cost_center": self.cost_center,
+						"party_type": self.applicant_type,
+						"party": self.applicant,
+						"posting_date": getdate(self.posting_date)
+					})
+				)
+
+				gle_map.append(
+					self.get_gl_dict({
+						"account": loan_details.penalty_income_account,
+						"against": loan_details.payment_account,
+						"credit": self.total_penalty_paid,
+						"credit_in_account_currency": self.total_penalty_paid,
+						"against_voucher_type": "Loan",
+						"against_voucher": self.against_loan,
+						"remarks": _("Penalty against loan:") + self.against_loan,
+						"cost_center": self.cost_center,
+						"posting_date": getdate(self.posting_date)
+					})
+				)
+
 			gle_map.append(
 				self.get_gl_dict({
-					"account": loan_details.loan_account,
-					"against": loan_details.payment_account,
-					"debit": self.total_penalty_paid,
-					"debit_in_account_currency": self.total_penalty_paid,
+					"account": loan_details.payment_account,
+					"against": loan_details.loan_account + ", " + loan_details.interest_income_account
+							+ ", " + loan_details.penalty_income_account,
+					"debit": self.amount_paid,
+					"debit_in_account_currency": self.amount_paid,
 					"against_voucher_type": "Loan",
 					"against_voucher": self.against_loan,
-					"remarks": _("Penalty against loan:") + self.against_loan,
+					"remarks": remarks,
 					"cost_center": self.cost_center,
-					"party_type": self.applicant_type,
-					"party": self.applicant,
 					"posting_date": getdate(self.posting_date)
 				})
 			)
 
 			gle_map.append(
 				self.get_gl_dict({
-					"account": loan_details.penalty_income_account,
+					"account": loan_details.loan_account,
+					"party_type": loan_details.applicant_type,
+					"party": loan_details.applicant,
 					"against": loan_details.payment_account,
-					"credit": self.total_penalty_paid,
-					"credit_in_account_currency": self.total_penalty_paid,
+					"credit": self.amount_paid,
+					"credit_in_account_currency": self.amount_paid,
 					"against_voucher_type": "Loan",
 					"against_voucher": self.against_loan,
-					"remarks": _("Penalty against loan:") + self.against_loan,
+					"remarks": remarks,
 					"cost_center": self.cost_center,
 					"posting_date": getdate(self.posting_date)
 				})
 			)
 
-		gle_map.append(
-			self.get_gl_dict({
-				"account": loan_details.payment_account,
-				"against": loan_details.loan_account + ", " + loan_details.interest_income_account
-						+ ", " + loan_details.penalty_income_account,
-				"debit": self.amount_paid,
-				"debit_in_account_currency": self.amount_paid,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.against_loan,
-				"remarks": remarks,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(self.posting_date)
-			})
-		)
-
-		gle_map.append(
-			self.get_gl_dict({
-				"account": loan_details.loan_account,
-				"party_type": loan_details.applicant_type,
-				"party": loan_details.applicant,
-				"against": loan_details.payment_account,
-				"credit": self.amount_paid,
-				"credit_in_account_currency": self.amount_paid,
-				"against_voucher_type": "Loan",
-				"against_voucher": self.against_loan,
-				"remarks": remarks,
-				"cost_center": self.cost_center,
-				"posting_date": getdate(self.posting_date)
-			})
-		)
-
-		if gle_map:
-			make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False)
+			if gle_map:
+				make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False)
 
 def create_repayment_entry(loan, applicant, company, posting_date, loan_type,
 	payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None):
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.js b/erpnext/manufacturing/doctype/bom/bom.js
index a09a5e3..15a7c31 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -71,7 +71,6 @@
 
 	refresh: function(frm) {
 		frm.toggle_enable("item", frm.doc.__islocal);
-		toggle_operations(frm);
 
 		frm.set_indicator_formatter('item_code',
 			function(doc) {
@@ -326,8 +325,7 @@
 			freeze: true,
 			args: {
 				update_parent: true,
-				from_child_bom:false,
-				save: frm.doc.docstatus === 1 ? true : false
+				from_child_bom:false
 			},
 			callback: function(r) {
 				refresh_field("items");
@@ -651,15 +649,8 @@
 	erpnext.bom.calculate_total(frm.doc);
 });
 
-var toggle_operations = function(frm) {
-	frm.toggle_display("operations_section", cint(frm.doc.with_operations) == 1);
-	frm.toggle_display("transfer_material_against", cint(frm.doc.with_operations) == 1);
-	frm.toggle_reqd("transfer_material_against", cint(frm.doc.with_operations) == 1);
-};
-
 frappe.ui.form.on("BOM", "with_operations", function(frm) {
 	if(!cint(frm.doc.with_operations)) {
 		frm.set_value("operations", []);
 	}
-	toggle_operations(frm);
 });
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json
index f551b91..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",
@@ -193,6 +196,7 @@
   },
   {
    "default": "Work Order",
+   "depends_on": "with_operations",
    "fieldname": "transfer_material_against",
    "fieldtype": "Select",
    "label": "Transfer Material Against",
@@ -235,6 +239,7 @@
   {
    "fieldname": "operations_section",
    "fieldtype": "Section Break",
+   "hide_border": 1,
    "oldfieldtype": "Section Break"
   },
   {
@@ -245,6 +250,7 @@
    "options": "Routing"
   },
   {
+   "depends_on": "with_operations",
    "fieldname": "operations",
    "fieldtype": "Table",
    "label": "Operations",
@@ -510,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",
@@ -517,7 +539,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-21 12:29:32.634952",
+ "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 d1f6385..c32a8a9 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1,7 +1,8 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-from __future__ import unicode_literals
+from typing import List
+from collections import deque
 import frappe, erpnext
 from frappe.utils import cint, cstr, flt, today
 from frappe import _
@@ -16,14 +17,85 @@
 
 import functools
 
-from six import string_types
-
 from operator import itemgetter
 
 form_grid_templates = {
 	"items": "templates/form_grid/item_grid.html"
 }
 
+
+class BOMTree:
+	"""Full tree representation of a BOM"""
+
+	# specifying the attributes to save resources
+	# ref: https://docs.python.org/3/reference/datamodel.html#slots
+	__slots__ = ["name", "child_items", "is_bom", "item_code", "exploded_qty", "qty"]
+
+	def __init__(self, name: str, is_bom: bool = True, exploded_qty: float = 1.0, qty: float = 1) -> None:
+		self.name = name  # name of node, BOM number if is_bom else item_code
+		self.child_items: List["BOMTree"] = []  # list of child items
+		self.is_bom = is_bom   # true if the node is a BOM and not a leaf item
+		self.item_code: str = None  # item_code associated with node
+		self.qty = qty  # required unit quantity to make one unit of parent item.
+		self.exploded_qty = exploded_qty  # total exploded qty required for making root of tree.
+		if not self.is_bom:
+			self.item_code = self.name
+		else:
+			self.__create_tree()
+
+	def __create_tree(self):
+		bom = frappe.get_cached_doc("BOM", self.name)
+		self.item_code = bom.item
+
+		for item in bom.get("items", []):
+			qty = item.qty / bom.quantity  # quantity per unit
+			exploded_qty = self.exploded_qty * qty
+			if item.bom_no:
+				child = BOMTree(item.bom_no, exploded_qty=exploded_qty, qty=qty)
+				self.child_items.append(child)
+			else:
+				self.child_items.append(
+					BOMTree(item.item_code, is_bom=False, exploded_qty=exploded_qty, qty=qty)
+				)
+
+	def level_order_traversal(self) -> List["BOMTree"]:
+		"""Get level order traversal of tree.
+		E.g. for following tree the traversal will return list of nodes in order from top to bottom.
+		BOM:
+			- SubAssy1
+				- item1
+				- item2
+			- SubAssy2
+				- item3
+			- item4
+
+		returns = [SubAssy1, item1, item2, SubAssy2, item3, item4]
+		"""
+		traversal = []
+		q = deque()
+		q.append(self)
+
+		while q:
+			node = q.popleft()
+
+			for child in node.child_items:
+				traversal.append(child)
+				q.append(child)
+
+		return traversal
+
+	def __str__(self) -> str:
+		return (
+			f"{self.item_code}{' - ' + self.name if self.is_bom else ''} qty(per unit): {self.qty}"
+			f" exploded_qty: {self.exploded_qty}"
+		)
+
+	def __repr__(self, level: int = 0) -> str:
+		rep = "┃  " * (level - 1) + "┣━ " * (level > 0) + str(self) + "\n"
+		for child in self.child_items:
+			rep += child.__repr__(level=level + 1)
+		return rep
+
 class BOM(WebsiteGenerator):
 	website = frappe._dict(
 		# page_title_field = "item_name",
@@ -81,7 +153,8 @@
 		self.validate_operations()
 		self.calculate_cost()
 		self.update_stock_qty()
-		self.update_cost(update_parent=False, from_child_bom=True, save=False)
+		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') }]
@@ -152,7 +225,7 @@
 		if not args:
 			args = frappe.form_dict.get('args')
 
-		if isinstance(args, string_types):
+		if isinstance(args, str):
 			import json
 			args = json.loads(args)
 
@@ -213,7 +286,7 @@
 		return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1)
 
 	@frappe.whitelist()
-	def update_cost(self, update_parent=True, from_child_bom=False, save=True):
+	def update_cost(self, update_parent=True, from_child_bom=False, update_hour_rate = True, save=True):
 		if self.docstatus == 2:
 			return
 
@@ -242,7 +315,7 @@
 
 		if self.docstatus == 1:
 			self.flags.ignore_validate_update_after_submit = True
-			self.calculate_cost()
+			self.calculate_cost(update_hour_rate)
 		if save:
 			self.db_update()
 
@@ -403,32 +476,47 @@
 		bom_list.reverse()
 		return bom_list
 
-	def calculate_cost(self):
+	def calculate_cost(self, update_hour_rate = False):
 		"""Calculate bom totals"""
-		self.calculate_op_cost()
+		self.calculate_op_cost(update_hour_rate)
 		self.calculate_rm_cost()
 		self.calculate_sm_cost()
 		self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost
 		self.base_total_cost = self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost
 
-	def calculate_op_cost(self):
+	def calculate_op_cost(self, update_hour_rate = False):
 		"""Update workstation rate and calculates totals"""
 		self.operating_cost = 0
 		self.base_operating_cost = 0
 		for d in self.get('operations'):
 			if d.workstation:
-				if not d.hour_rate:
-					hour_rate = flt(frappe.db.get_value("Workstation", d.workstation, "hour_rate"))
-					d.hour_rate = hour_rate / flt(self.conversion_rate) if self.conversion_rate else hour_rate
-
-			if d.hour_rate and d.time_in_mins:
-				d.base_hour_rate = flt(d.hour_rate) * flt(self.conversion_rate)
-				d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
-				d.base_operating_cost = flt(d.operating_cost) * flt(self.conversion_rate)
+				self.update_rate_and_time(d, update_hour_rate)
 
 			self.operating_cost += flt(d.operating_cost)
 			self.base_operating_cost += flt(d.base_operating_cost)
 
+	def update_rate_and_time(self, row, update_hour_rate = False):
+		if not row.hour_rate or update_hour_rate:
+			hour_rate = flt(frappe.get_cached_value("Workstation", row.workstation, "hour_rate"))
+			row.hour_rate = (hour_rate / flt(self.conversion_rate)
+				if self.conversion_rate and hour_rate else hour_rate)
+
+			if self.routing:
+				row.time_in_mins = flt(frappe.db.get_value("BOM Operation", {
+						"workstation": row.workstation,
+						"operation": row.operation,
+						"sequence_id": row.sequence_id,
+						"parent": self.routing
+				}, ["time_in_mins"]))
+
+		if row.hour_rate and row.time_in_mins:
+			row.base_hour_rate = flt(row.hour_rate) * flt(self.conversion_rate)
+			row.operating_cost = flt(row.hour_rate) * flt(row.time_in_mins) / 60.0
+			row.base_operating_cost = flt(row.operating_cost) * flt(self.conversion_rate)
+
+		if update_hour_rate:
+			row.db_update()
+
 	def calculate_rm_cost(self):
 		"""Fetch RM rate as per today's valuation rate and calculate totals"""
 		total_rm_cost = 0
@@ -575,7 +663,7 @@
 			self.get_routing()
 
 	def validate_operations(self):
-		if self.with_operations and not self.get('operations'):
+		if self.with_operations and not self.get('operations') and self.docstatus == 1:
 			frappe.throw(_("Operations cannot be left blank"))
 
 		if self.with_operations:
@@ -585,6 +673,24 @@
 				if not d.batch_size or d.batch_size <= 0:
 					d.batch_size = 1
 
+	def get_tree_representation(self) -> BOMTree:
+		"""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':
 		rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1)
@@ -768,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',
@@ -779,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:
@@ -792,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
 
@@ -975,7 +1082,7 @@
 
 	if filters and filters.get("is_stock_item"):
 		query_filters["is_stock_item"] = 1
-		
+
 	return frappe.get_all("Item",
 		fields = fields, filters=query_filters,
 		or_filters = or_cond_filters, order_by=order_by,
@@ -1008,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/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index e1cca9e..57a5458 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -2,16 +2,16 @@
 # License: GNU General Public License v3. See license.txt
 
 
-from __future__ import unicode_literals
+from collections import deque
 import unittest
 import frappe
 from frappe.utils import cstr, flt
 from frappe.test_runner import make_test_records
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
 from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
-from six import string_types
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.tests.test_subcontracting import set_backflush_based_on
 
 test_records = frappe.get_test_records('BOM')
 
@@ -122,7 +122,7 @@
 		bom.items[0].conversion_factor = 5
 		bom.insert()
 
-		bom.update_cost()
+		bom.update_cost(update_hour_rate = False)
 
 		# test amounts in selected currency
 		self.assertEqual(bom.items[0].rate, 300)
@@ -160,6 +160,7 @@
 
 	def test_subcontractor_sourced_item(self):
 		item_code = "_Test Subcontracted FG Item 1"
+		set_backflush_based_on('Material Transferred for Subcontract')
 
 		if not frappe.db.exists('Item', item_code):
 			make_item(item_code, {
@@ -225,11 +226,88 @@
 		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
 		self.assertEqual(bom_items, supplied_items)
 
+	def test_bom_tree_representation(self):
+		bom_tree = {
+			"Assembly": {
+				"SubAssembly1": {"ChildPart1": {}, "ChildPart2": {},},
+				"SubAssembly2": {"ChildPart3": {}},
+				"SubAssembly3": {"SubSubAssy1": {"ChildPart4": {}}},
+				"ChildPart5": {},
+				"ChildPart6": {},
+				"SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}},
+			}
+		}
+		parent_bom = create_nested_bom(bom_tree, prefix="")
+		created_tree = parent_bom.get_tree_representation()
+
+		reqd_order = level_order_traversal(bom_tree)[1:] # skip first item
+		created_order = created_tree.level_order_traversal()
+
+		self.assertEqual(len(reqd_order), len(created_order))
+
+		for reqd_item, created_item in zip(reqd_order, created_order):
+			self.assertEqual(reqd_item, created_item.item_code)
+
+
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
 
+
+
+
+def level_order_traversal(node):
+	traversal = []
+	q = deque()
+	q.append(node)
+
+	while q:
+		node = q.popleft()
+
+		for node_name, subtree in node.items():
+			traversal.append(node_name)
+			q.append(subtree)
+
+	return traversal
+
+def create_nested_bom(tree, prefix="_Test bom "):
+	""" Helper function to create a simple nested bom from tree describing item names. (along with required items)
+	"""
+
+	def create_items(bom_tree):
+		for item_code, subtree in bom_tree.items():
+			bom_item_code = prefix + item_code
+			if not frappe.db.exists("Item", bom_item_code):
+				frappe.get_doc(doctype="Item", item_code=bom_item_code, item_group="_Test Item Group").insert()
+			create_items(subtree)
+	create_items(tree)
+
+	def dfs(tree, node):
+		"""naive implementation for searching right subtree"""
+		for node_name, subtree in tree.items():
+			if node_name == node:
+				return subtree
+			else:
+				result = dfs(subtree, node)
+				if result is not None:
+					return result
+
+	order_of_creating_bom = reversed(level_order_traversal(tree))
+
+	for item in order_of_creating_bom:
+		child_items = dfs(tree, item)
+		if child_items:
+			bom_item_code = prefix + item
+			bom = frappe.get_doc(doctype="BOM", item=bom_item_code)
+			for child_item in child_items.keys():
+				bom.append("items", {"item_code": prefix + child_item})
+			bom.insert()
+			bom.submit()
+
+	return bom  # parent bom is last bom
+
+
 def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None):
-	if warehouse_list and isinstance(warehouse_list, string_types):
+	if warehouse_list and isinstance(warehouse_list, str):
 		warehouse_list = [warehouse_list]
 
 	if not warehouse_list:
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index 07464e3..4458e6d 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -13,10 +13,10 @@
   "col_break1",
   "hour_rate",
   "time_in_mins",
-  "batch_size",
   "operating_cost",
   "base_hour_rate",
   "base_operating_cost",
+  "batch_size",
   "image"
  ],
  "fields": [
@@ -61,6 +61,8 @@
   },
   {
    "description": "In minutes",
+   "fetch_from": "operation.total_operation_time",
+   "fetch_if_empty": 1,
    "fieldname": "time_in_mins",
    "fieldtype": "Float",
    "in_list_view": 1,
@@ -104,7 +106,8 @@
    "label": "Image"
   },
   {
-   "default": "1",
+   "fetch_from": "operation.batch_size",
+   "fetch_if_empty": 1,
    "fieldname": "batch_size",
    "fieldtype": "Int",
    "label": "Batch Size"
@@ -120,7 +123,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-10-13 18:14:10.018774",
+ "modified": "2021-01-12 14:48:09.596843",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Operation",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index 4e8dd41..81860c9 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -11,6 +11,16 @@
 				}
 			};
 		});
+
+		frm.set_indicator_formatter('sub_operation',
+			function(doc) {
+				if (doc.status == "Pending") {
+					return "red";
+				} else {
+					return doc.status === "Complete" ? "green" : "orange";
+				}
+			}
+		);
 	},
 
 	refresh: function(frm) {
@@ -31,6 +41,10 @@
 			}
 		}
 
+		if (frm.doc.docstatus == 1 && !frm.doc.is_corrective_job_card) {
+			frm.trigger('setup_corrective_job_card');
+		}
+
 		frm.set_query("quality_inspection", function() {
 			return {
 				query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query",
@@ -43,12 +57,62 @@
 
 		frm.trigger("toggle_operation_number");
 
-		if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
+		if (frm.doc.docstatus == 0 && !frm.is_new() &&
+			(frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
 			&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
 			frm.trigger("prepare_timer_buttons");
 		}
 	},
 
+	setup_corrective_job_card: function(frm) {
+		frm.add_custom_button(__('Corrective Job Card'), () => {
+			let operations = frm.doc.sub_operations.map(d => d.sub_operation).concat(frm.doc.operation);
+
+			let fields = [
+				{
+					fieldtype: 'Link', label: __('Corrective Operation'), options: 'Operation',
+					fieldname: 'operation', get_query() {
+						return {
+							filters: {
+								"is_corrective_operation": 1
+							}
+						};
+					}
+				}, {
+					fieldtype: 'Link', label: __('For Operation'), options: 'Operation',
+					fieldname: 'for_operation', get_query() {
+						return {
+							filters: {
+								"name": ["in", operations]
+							}
+						};
+					}
+				}
+			];
+
+			frappe.prompt(fields, d => {
+				frm.events.make_corrective_job_card(frm, d.operation, d.for_operation);
+			}, __("Select Corrective Operation"));
+		}, __('Make'));
+	},
+
+	make_corrective_job_card: function(frm, operation, for_operation) {
+		frappe.call({
+			method: 'erpnext.manufacturing.doctype.job_card.job_card.make_corrective_job_card',
+			args: {
+				source_name: frm.doc.name,
+				operation: operation,
+				for_operation: for_operation
+			},
+			callback: function(r) {
+				if (r.message) {
+					frappe.model.sync(r.message);
+					frappe.set_route("Form", r.message.doctype, r.message.name);
+				}
+			}
+		});
+	},
+
 	operation: function(frm) {
 		frm.trigger("toggle_operation_number");
 
@@ -97,101 +161,105 @@
 
 	prepare_timer_buttons: function(frm) {
 		frm.trigger("make_dashboard");
-		if (!frm.doc.job_started) {
-			frm.add_custom_button(__("Start"), () => {
-				if (!frm.doc.employee) {
-					frappe.prompt({fieldtype: 'Link', label: __('Employee'), options: "Employee",
-						fieldname: 'employee'}, d => {
-						if (d.employee) {
-							frm.set_value("employee", d.employee);
-						} else {
-							frm.events.start_job(frm);
-						}
-					}, __("Enter Value"), __("Start"));
+
+		if (!frm.doc.started_time && !frm.doc.current_time) {
+			frm.add_custom_button(__("Start Job"), () => {
+				if ((frm.doc.employee && !frm.doc.employee.length) || !frm.doc.employee) {
+					frappe.prompt({fieldtype: 'Table MultiSelect', label: __('Select Employees'),
+						options: "Job Card Time Log", fieldname: 'employees'}, d => {
+						frm.events.start_job(frm, "Work In Progress", d.employees);
+					}, __("Assign Job to Employee"));
 				} else {
-					frm.events.start_job(frm);
+					frm.events.start_job(frm, "Work In Progress", frm.doc.employee);
 				}
 			}).addClass("btn-primary");
 		} else if (frm.doc.status == "On Hold") {
-			frm.add_custom_button(__("Resume"), () => {
-				frappe.flags.resume_job = 1;
-				frm.events.start_job(frm);
+			frm.add_custom_button(__("Resume Job"), () => {
+				frm.events.start_job(frm, "Resume Job", frm.doc.employee);
 			}).addClass("btn-primary");
 		} else {
-			frm.add_custom_button(__("Pause"), () => {
-				frappe.flags.pause_job = 1;
-				frm.set_value("status", "On Hold");
-				frm.events.complete_job(frm);
+			frm.add_custom_button(__("Pause Job"), () => {
+				frm.events.complete_job(frm, "On Hold");
 			});
 
-			frm.add_custom_button(__("Complete"), () => {
-				let completed_time = frappe.datetime.now_datetime();
-				frm.trigger("hide_timer");
+			frm.add_custom_button(__("Complete Job"), () => {
+				var sub_operations = frm.doc.sub_operations;
 
-				if (frm.doc.for_quantity) {
+				let set_qty = true;
+				if (sub_operations && sub_operations.length > 1) {
+					set_qty = false;
+					let last_op_row = sub_operations[sub_operations.length - 2];
+
+					if (last_op_row.status == 'Complete') {
+						set_qty = true;
+					}
+				}
+
+				if (set_qty) {
 					frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'),
-						fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => {
-							frm.events.complete_job(frm, completed_time, data.qty);
-						}, __("Enter Value"), __("Complete"));
+						fieldname: 'qty', default: frm.doc.for_quantity}, data => {
+						frm.events.complete_job(frm, "Complete", data.qty);
+					}, __("Enter Value"));
 				} else {
-					frm.events.complete_job(frm, completed_time, 0);
+					frm.events.complete_job(frm, "Complete", 0.0);
 				}
 			}).addClass("btn-primary");
 		}
 	},
 
-	start_job: function(frm) {
-		let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs');
-		row.from_time = frappe.datetime.now_datetime();
-		frm.set_value('job_started', 1);
-		frm.set_value('started_time' , row.from_time);
-		frm.set_value("status", "Work In Progress");
-
-		if (!frappe.flags.resume_job) {
-			frm.set_value('current_time' , 0);
-		}
-
-		frm.save();
+	start_job: function(frm, status, employee) {
+		const args = {
+			job_card_id: frm.doc.name,
+			start_time: frappe.datetime.now_datetime(),
+			employees: employee,
+			status: status
+		};
+		frm.events.make_time_log(frm, args);
 	},
 
-	complete_job: function(frm, completed_time, completed_qty) {
-		frm.doc.time_logs.forEach(d => {
-			if (d.from_time && !d.to_time) {
-				d.to_time = completed_time || frappe.datetime.now_datetime();
-				d.completed_qty = completed_qty || 0;
+	complete_job: function(frm, status, completed_qty) {
+		const args = {
+			job_card_id: frm.doc.name,
+			complete_time: frappe.datetime.now_datetime(),
+			status: status,
+			completed_qty: completed_qty
+		};
+		frm.events.make_time_log(frm, args);
+	},
 
-				if(frappe.flags.pause_job) {
-					let currentIncrement = moment(d.to_time).diff(moment(d.from_time),"seconds") || 0;
-					frm.set_value('current_time' , currentIncrement + (frm.doc.current_time || 0));
-				} else {
-					frm.set_value('started_time' , '');
-					frm.set_value('job_started', 0);
-					frm.set_value('current_time' , 0);
-				}
+	make_time_log: function(frm, args) {
+		frm.events.update_sub_operation(frm, args);
 
-				frm.save();
+		frappe.call({
+			method: "erpnext.manufacturing.doctype.job_card.job_card.make_time_log",
+			args: {
+				args: args
+			},
+			freeze: true,
+			callback: function () {
+				frm.reload_doc();
+				frm.trigger("make_dashboard");
 			}
 		});
 	},
 
+	update_sub_operation: function(frm, args) {
+		if (frm.doc.sub_operations && frm.doc.sub_operations.length) {
+			let sub_operations = frm.doc.sub_operations.filter(d => d.status != 'Complete');
+			if (sub_operations && sub_operations.length) {
+				args["sub_operation"] = sub_operations[0].sub_operation;
+			}
+		}
+	},
+
 	validate: function(frm) {
 		if ((!frm.doc.time_logs || !frm.doc.time_logs.length) && frm.doc.started_time) {
 			frm.trigger("reset_timer");
 		}
 	},
 
-	employee: function(frm) {
-		if (frm.doc.job_started && !frm.doc.current_time) {
-			frm.trigger("reset_timer");
-		} else {
-			frm.events.start_job(frm);
-		}
-	},
-
 	reset_timer: function(frm) {
 		frm.set_value('started_time' , '');
-		frm.set_value('job_started', 0);
-		frm.set_value('current_time' , 0);
 	},
 
 	make_dashboard: function(frm) {
@@ -297,7 +365,6 @@
 	},
 
 	to_time: function(frm) {
-		frm.set_value('job_started', 0);
 		frm.set_value('started_time', '');
 	}
 })
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index 5713f69..046e2fd 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -9,38 +9,49 @@
   "naming_series",
   "work_order",
   "bom_no",
-  "workstation",
-  "operation",
-  "operation_row_number",
   "column_break_4",
   "posting_date",
   "company",
-  "remarks",
   "production_section",
   "production_item",
   "item_name",
   "for_quantity",
-  "quality_inspection",
-  "wip_warehouse",
+  "serial_no",
   "column_break_12",
-  "employee",
-  "employee_name",
-  "status",
+  "wip_warehouse",
+  "quality_inspection",
   "project",
+  "batch_no",
+  "operation_section_section",
+  "operation",
+  "operation_row_number",
+  "column_break_18",
+  "workstation",
+  "employee",
+  "section_break_21",
+  "sub_operations",
   "timing_detail",
   "time_logs",
   "section_break_13",
   "total_completed_qty",
-  "total_time_in_mins",
   "column_break_15",
+  "total_time_in_mins",
   "section_break_8",
   "items",
+  "corrective_operation_section",
+  "for_job_card",
+  "is_corrective_job_card",
+  "column_break_33",
+  "hour_rate",
+  "for_operation",
   "more_information",
   "operation_id",
   "sequence_id",
   "transferred_qty",
   "requested_qty",
+  "status",
   "column_break_20",
+  "remarks",
   "barcode",
   "job_started",
   "started_time",
@@ -118,13 +129,6 @@
    "label": "Timing Detail"
   },
   {
-   "fieldname": "employee",
-   "fieldtype": "Link",
-   "in_standard_filter": 1,
-   "label": "Employee",
-   "options": "Employee"
-  },
-  {
    "allow_bulk_edit": 1,
    "fieldname": "time_logs",
    "fieldtype": "Table",
@@ -133,9 +137,11 @@
   },
   {
    "fieldname": "section_break_13",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "hide_border": 1
   },
   {
+   "default": "0",
    "fieldname": "total_completed_qty",
    "fieldtype": "Float",
    "label": "Total Completed Qty",
@@ -160,8 +166,7 @@
    "fieldname": "items",
    "fieldtype": "Table",
    "label": "Items",
-   "options": "Job Card Item",
-   "read_only": 1
+   "options": "Job Card Item"
   },
   {
    "collapsible": 1,
@@ -251,12 +256,7 @@
    "reqd": 1
   },
   {
-   "fetch_from": "employee.employee_name",
-   "fieldname": "employee_name",
-   "fieldtype": "Read Only",
-   "label": "Employee Name"
-  },
-  {
+   "collapsible": 1,
    "fieldname": "production_section",
    "fieldtype": "Section Break",
    "label": "Production"
@@ -314,11 +314,89 @@
    "label": "Quality Inspection",
    "no_copy": 1,
    "options": "Quality Inspection"
+  },
+  {
+   "allow_bulk_edit": 1,
+   "fieldname": "sub_operations",
+   "fieldtype": "Table",
+   "label": "Sub Operations",
+   "options": "Job Card Operation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "operation_section_section",
+   "fieldtype": "Section Break",
+   "label": "Operation Section"
+  },
+  {
+   "fieldname": "column_break_18",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_21",
+   "fieldtype": "Section Break",
+   "hide_border": 1
+  },
+  {
+   "depends_on": "is_corrective_job_card",
+   "fieldname": "hour_rate",
+   "fieldtype": "Currency",
+   "label": "Hour Rate"
+  },
+  {
+   "collapsible": 1,
+   "depends_on": "is_corrective_job_card",
+   "fieldname": "corrective_operation_section",
+   "fieldtype": "Section Break",
+   "label": "Corrective Operation"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_corrective_job_card",
+   "fieldtype": "Check",
+   "label": "Is Corrective Job Card",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_33",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "for_job_card",
+   "fieldtype": "Link",
+   "label": "For Job Card",
+   "options": "Job Card",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "for_job_card.operation",
+   "fetch_if_empty": 1,
+   "fieldname": "for_operation",
+   "fieldtype": "Link",
+   "label": "For Operation",
+   "options": "Operation"
+  },
+  {
+   "fieldname": "employee",
+   "fieldtype": "Table MultiSelect",
+   "label": "Employee",
+   "options": "Job Card Time Log"
+  },
+  {
+   "fieldname": "serial_no",
+   "fieldtype": "Small Text",
+   "label": "Serial No"
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Link",
+   "label": "Batch No",
+   "options": "Batch"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-19 18:26:50.531664",
+ "modified": "2021-03-16 15:59:32.766484",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index fb26062..420bb00 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -5,11 +5,12 @@
 from __future__ import unicode_literals
 import frappe
 import datetime
+import json
 from frappe import _, bold
 from frappe.model.mapper import get_mapped_doc
 from frappe.model.document import Document
 from frappe.utils import (flt, cint, time_diff_in_hours, get_datetime, getdate,
-	get_time, add_to_date, time_diff, add_days, get_datetime_str, get_link_to_form)
+	get_time, add_to_date, time_diff, add_days, get_datetime_str, get_link_to_form, time_diff_in_seconds)
 
 from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
 
@@ -25,10 +26,21 @@
 		self.set_status()
 		self.validate_operation_id()
 		self.validate_sequence_id()
+		self.get_sub_operations()
+		self.update_sub_operation_status()
+
+	def get_sub_operations(self):
+		if self.operation:
+			self.sub_operations = []
+			for row in frappe.get_all("Sub Operation",
+				filters = {"parent": self.operation}, fields=["operation", "idx"]):
+				row.status = "Pending"
+				row.sub_operation = row.operation
+				self.append("sub_operations", row)
 
 	def validate_time_logs(self):
-		self.total_completed_qty = 0.0
 		self.total_time_in_mins = 0.0
+		self.total_completed_qty = 0.0
 
 		if self.get('time_logs'):
 			for d in self.get('time_logs'):
@@ -44,11 +56,14 @@
 					d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60
 					self.total_time_in_mins += d.time_in_mins
 
-				if d.completed_qty:
+				if d.completed_qty and not self.sub_operations:
 					self.total_completed_qty += d.completed_qty
 
 			self.total_completed_qty = flt(self.total_completed_qty, self.precision("total_completed_qty"))
 
+		for row in self.sub_operations:
+			self.total_completed_qty += row.completed_qty
+
 	def get_overlap_for(self, args, check_next_available_slot=False):
 		production_capacity = 1
 
@@ -57,7 +72,7 @@
 				self.workstation, 'production_capacity') or 1
 			validate_overlap_for = " and jc.workstation = %(workstation)s "
 
-		if self.employee:
+		if args.get("employee"):
 			# override capacity for employee
 			production_capacity = 1
 			validate_overlap_for = " and jc.employee = %(employee)s "
@@ -80,7 +95,7 @@
 				"to_time": args.to_time,
 				"name": args.name or "No Name",
 				"parent": args.parent or "No Name",
-				"employee": self.employee,
+				"employee": args.get("employee"),
 				"workstation": self.workstation
 			}, as_dict=True)
 
@@ -158,6 +173,108 @@
 			row.planned_start_time = datetime.datetime.combine(start_date,
 				get_time(workstation_doc.working_hours[0].start_time))
 
+	def add_time_log(self, args):
+		last_row = []
+		employees = args.employees
+		if isinstance(employees, str):
+			employees = json.loads(employees)
+
+		if self.time_logs and len(self.time_logs) > 0:
+			last_row = self.time_logs[-1]
+
+		self.reset_timer_value(args)
+		if last_row and args.get("complete_time"):
+			for row in self.time_logs:
+				if not row.to_time:
+					row.update({
+						"to_time": get_datetime(args.get("complete_time")),
+						"operation": args.get("sub_operation"),
+						"completed_qty": args.get("completed_qty") or 0.0
+					})
+		elif args.get("start_time"):
+			new_args = {
+				"from_time": get_datetime(args.get("start_time")),
+				"operation": args.get("sub_operation"),
+				"completed_qty": 0.0
+			}
+
+			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":
+			self.current_time = time_diff_in_seconds(last_row.to_time, last_row.from_time)
+
+		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', {
+				'employee': name.get('employee'),
+				'completed_qty': 0.0
+			})
+
+	def reset_timer_value(self, args):
+		self.started_time = None
+
+		if args.get("status") in ["Work In Progress", "Complete"]:
+			self.current_time = 0.0
+
+			if args.get("status") == "Work In Progress":
+				self.started_time = get_datetime(args.get("start_time"))
+
+		if args.get("status") == "Resume Job":
+			args["status"] = "Work In Progress"
+
+		if args.get("status"):
+			self.status = args.get("status")
+
+	def update_sub_operation_status(self):
+		if not (self.sub_operations and self.time_logs):
+			return
+
+		operation_wise_completed_time = {}
+		for time_log in self.time_logs:
+			if time_log.operation not in operation_wise_completed_time:
+				operation_wise_completed_time.setdefault(time_log.operation,
+					frappe._dict({"status": "Pending", "completed_qty":0.0, "completed_time": 0.0, "employee": []}))
+
+			op_row = operation_wise_completed_time[time_log.operation]
+			op_row.status = "Work In Progress" if not time_log.time_in_mins else "Complete"
+			if self.status == 'On Hold':
+				op_row.status = 'Pause'
+
+			op_row.employee.append(time_log.employee)
+			if time_log.time_in_mins:
+				op_row.completed_time += time_log.time_in_mins
+				op_row.completed_qty += time_log.completed_qty
+
+		for row in self.sub_operations:
+			operation_deatils = operation_wise_completed_time.get(row.sub_operation)
+			if operation_deatils:
+				if row.status != 'Complete':
+					row.status = operation_deatils.status
+
+				row.completed_time = operation_deatils.completed_time
+				if operation_deatils.employee:
+					row.completed_time = row.completed_time / len(set(operation_deatils.employee))
+
+					if operation_deatils.completed_qty:
+						row.completed_qty = operation_deatils.completed_qty / len(set(operation_deatils.employee))
+			else:
+				row.status = 'Pending'
+				row.completed_time = 0.0
+				row.completed_qty = 0.0
+
 	def update_time_logs(self, row):
 		self.append("time_logs", {
 			"from_time": row.planned_start_time,
@@ -182,15 +299,18 @@
 
 			if self.get('operation') == d.operation:
 				self.append('items', {
-					'item_code': d.item_code,
-					'source_warehouse': d.source_warehouse,
-					'uom': frappe.db.get_value("Item", d.item_code, 'stock_uom'),
-					'item_name': d.item_name,
-					'description': d.description,
-					'required_qty': (d.required_qty * flt(self.for_quantity)) / doc.qty
+					"item_code": d.item_code,
+					"source_warehouse": d.source_warehouse,
+					"uom": frappe.db.get_value("Item", d.item_code, 'stock_uom'),
+					"item_name": d.item_name,
+					"description": d.description,
+					"required_qty": (d.required_qty * flt(self.for_quantity)) / doc.qty,
+					"rate": d.rate,
+					"amount": d.amount
 				})
 
 	def on_submit(self):
+		self.validate_transfer_qty()
 		self.validate_job_card()
 		self.update_work_order()
 		self.set_transferred_qty()
@@ -199,7 +319,16 @@
 		self.update_work_order()
 		self.set_transferred_qty()
 
+	def validate_transfer_qty(self):
+		if self.items and self.transferred_qty < self.for_quantity:
+			frappe.throw(_('Materials needs to be transferred to the work in progress warehouse for the job card {0}')
+				.format(self.name))
+
 	def validate_job_card(self):
+		if self.work_order and frappe.get_cached_value('Work Order', self.work_order, 'status') == 'Stopped':
+			frappe.throw(_("Transaction not allowed against stopped Work Order {0}")
+				.format(get_link_to_form('Work Order', self.work_order)))
+
 		if not self.time_logs:
 			frappe.throw(_("Time logs are required for {0} {1}")
 				.format(bold("Job Card"), get_link_to_form("Job Card", self.name)))
@@ -215,6 +344,10 @@
 		if not self.work_order:
 			return
 
+		if self.is_corrective_job_card and not cint(frappe.db.get_single_value('Manufacturing Settings',
+			'add_corrective_operation_cost_in_finished_good_valuation')):
+			return
+
 		for_quantity, time_in_mins = 0, 0
 		from_time_list, to_time_list = [], []
 
@@ -225,10 +358,24 @@
 			time_in_mins = flt(data[0].time_in_mins)
 
 		wo = frappe.get_doc('Work Order', self.work_order)
-		if self.operation_id:
+
+		if self.is_corrective_job_card:
+			self.update_corrective_in_work_order(wo)
+
+		elif self.operation_id:
 			self.validate_produced_quantity(for_quantity, wo)
 			self.update_work_order_data(for_quantity, time_in_mins, wo)
 
+	def update_corrective_in_work_order(self, wo):
+		wo.corrective_operation_cost = 0.0
+		for row in frappe.get_all('Job Card', fields = ['total_time_in_mins', 'hour_rate'],
+			filters = {'is_corrective_job_card': 1, 'docstatus': 1, 'work_order': self.work_order}):
+			wo.corrective_operation_cost += flt(row.total_time_in_mins) * flt(row.hour_rate)
+
+		wo.calculate_operating_cost()
+		wo.flags.ignore_validate_update_after_submit = True
+		wo.save()
+
 	def validate_produced_quantity(self, for_quantity, wo):
 		if self.docstatus < 2: return
 
@@ -248,8 +395,8 @@
 					min(from_time) as start_time, max(to_time) as end_time
 				FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
 				WHERE
-					jctl.parent = jc.name and jc.work_order = %s
-					and jc.operation_id = %s and jc.docstatus = 1
+					jctl.parent = jc.name and jc.work_order = %s and jc.operation_id = %s
+					and jc.docstatus = 1 and IFNULL(jc.is_corrective_job_card, 0) = 0
 			""", (self.work_order, self.operation_id), as_dict=1)
 
 		for data in wo.operations:
@@ -271,7 +418,8 @@
 	def get_current_operation_data(self):
 		return frappe.get_all('Job Card',
 			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
-			filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id})
+			filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id,
+				"is_corrective_job_card": 0})
 
 	def set_transferred_qty_in_job_card(self, ste_doc):
 		for row in ste_doc.items:
@@ -317,7 +465,7 @@
 						'docstatus': ('!=', 2)}, fields = 'sum(transferred_qty) as qty', group_by='operation_id')
 
 					if job_cards:
-						qty = min([d.qty for d in job_cards])
+						qty = min(d.qty for d in job_cards)
 
 			doc.db_set('material_transferred_for_manufacturing', qty)
 
@@ -354,7 +502,11 @@
 				.format(bold(self.operation), work_order), OperationMismatchError)
 
 	def validate_sequence_id(self):
-		if not (self.work_order and self.sequence_id): return
+		if self.is_corrective_job_card:
+			return
+
+		if not (self.work_order and self.sequence_id):
+			return
 
 		current_operation_qty = 0.0
 		data = self.get_current_operation_data()
@@ -376,6 +528,17 @@
 				frappe.throw(_("{0}, complete the operation {1} before the operation {2}.")
 					.format(message, bold(row.operation), bold(self.operation)), OperationSequenceError)
 
+
+@frappe.whitelist()
+def make_time_log(args):
+	if isinstance(args, str):
+		args = json.loads(args)
+
+	args = frappe._dict(args)
+	doc = frappe.get_doc("Job Card", args.job_card_id)
+	doc.validate_sequence_id()
+	doc.add_time_log(args)
+
 @frappe.whitelist()
 def get_operation_details(work_order, operation):
 	if work_order and operation:
@@ -433,7 +596,8 @@
 def make_stock_entry(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
 		target.t_warehouse = source_parent.wip_warehouse
-		target.conversion_factor = 1
+		if not target.conversion_factor:
+			target.conversion_factor = 1
 
 	def set_missing_values(source, target):
 		target.purpose = "Material Transfer for Manufacture"
@@ -510,3 +674,28 @@
 			events.append(job_card_data)
 
 	return events
+
+@frappe.whitelist()
+def make_corrective_job_card(source_name, operation=None, for_operation=None, target_doc=None):
+	def set_missing_values(source, target):
+		target.is_corrective_job_card = 1
+		target.operation = operation
+		target.for_operation = for_operation
+
+		target.set('time_logs', [])
+		target.set('employee', [])
+		target.set('items', [])
+		target.get_sub_operations()
+		target.get_required_items()
+		target.validate_time_logs()
+
+	doclist = get_mapped_doc("Job Card", source_name, {
+		"Job Card": {
+			"doctype": "Job Card",
+			"field_map": {
+				"name": "for_job_card",
+			},
+		}
+	}, target_doc, set_missing_values)
+
+	return doclist
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
index 100ef4c..d91530d 100644
--- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
+++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
@@ -25,8 +25,7 @@
    "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Item Code",
-   "options": "Item",
-   "read_only": 1
+   "options": "Item"
   },
   {
    "fieldname": "source_warehouse",
@@ -67,8 +66,7 @@
    "fieldname": "required_qty",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Required Qty",
-   "read_only": 1
+   "label": "Required Qty"
   },
   {
    "fieldname": "column_break_9",
@@ -107,7 +105,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-11 13:50:13.804108",
+ "modified": "2021-04-22 18:50:00.003444",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card Item",
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/manufacturing/doctype/job_card_operation/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/manufacturing/doctype/job_card_operation/__init__.py
diff --git a/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.json b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.json
new file mode 100644
index 0000000..9a8692b
--- /dev/null
+++ b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.json
@@ -0,0 +1,59 @@
+{
+ "actions": [],
+ "creation": "2020-12-07 16:58:38.449041",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "sub_operation",
+  "completed_time",
+  "status",
+  "completed_qty"
+ ],
+ "fields": [
+  {
+   "default": "Pending",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Status",
+   "options": "Complete\nPause\nPending\nWork In Progress",
+   "read_only": 1
+  },
+  {
+   "description": "In mins",
+   "fieldname": "completed_time",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Completed Time",
+   "read_only": 1
+  },
+  {
+   "fieldname": "sub_operation",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Operation",
+   "options": "Operation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "completed_qty",
+   "fieldtype": "Float",
+   "label": "Completed Qty",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-03-16 18:24:35.399593",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Job Card Operation",
+ "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/job_card_operation/job_card_operation.py b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.py
new file mode 100644
index 0000000..85d7298
--- /dev/null
+++ b/erpnext/manufacturing/doctype/job_card_operation/job_card_operation.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 JobCardOperation(Document):
+	pass
diff --git a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json
index 9dd54dd..a7102d7 100644
--- a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json
+++ b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json
@@ -1,14 +1,17 @@
 {
+ "actions": [],
  "creation": "2019-03-08 23:56:43.187569",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
+  "employee",
   "from_time",
   "to_time",
   "column_break_2",
   "time_in_mins",
-  "completed_qty"
+  "completed_qty",
+  "operation"
  ],
  "fields": [
   {
@@ -41,10 +44,27 @@
    "in_list_view": 1,
    "label": "Completed Qty",
    "reqd": 1
+  },
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee"
+  },
+  {
+   "fieldname": "operation",
+   "fieldtype": "Link",
+   "label": "Operation",
+   "no_copy": 1,
+   "options": "Operation",
+   "read_only": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-12-03 12:56:02.285448",
+ "links": [],
+ "modified": "2020-12-23 14:30:00.970916",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card Time Log",
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index b7634da..024f784 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -26,7 +26,10 @@
   "column_break_16",
   "overproduction_percentage_for_work_order",
   "other_settings_section",
-  "update_bom_costs_automatically"
+  "update_bom_costs_automatically",
+  "add_corrective_operation_cost_in_finished_good_valuation",
+  "column_break_23",
+  "make_serial_no_batch_from_work_order"
  ],
  "fields": [
   {
@@ -155,13 +158,30 @@
   {
    "fieldname": "column_break_5",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_23",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "System will automatically create the serial numbers / batch for the Finished Good on submission of work order",
+   "fieldname": "make_serial_no_batch_from_work_order",
+   "fieldtype": "Check",
+   "label": "Make Serial No / Batch from Work Order"
+  },
+  {
+   "default": "0",
+   "fieldname": "add_corrective_operation_cost_in_finished_good_valuation",
+   "fieldtype": "Check",
+   "label": "Add Corrective Operation Cost in Finished Good Valuation"
   }
  ],
  "icon": "icon-wrench",
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-10-13 10:55:43.996581",
+ "modified": "2021-03-16 15:54:38.967341",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Manufacturing Settings",
@@ -178,4 +198,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/operation/operation.js b/erpnext/manufacturing/doctype/operation/operation.js
index 5c2aba6..102b678 100644
--- a/erpnext/manufacturing/doctype/operation/operation.js
+++ b/erpnext/manufacturing/doctype/operation/operation.js
@@ -2,7 +2,13 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Operation', {
-	refresh: function(frm) {
-
+	setup: function(frm) {
+		frm.set_query('operation', 'sub_operations', function() {
+			return {
+				filters: {
+					'name': ['not in', [frm.doc.name]]
+				}
+			};
+		});
 	}
-});
+});
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/operation/operation.json b/erpnext/manufacturing/doctype/operation/operation.json
index c231fba..10a97ed 100644
--- a/erpnext/manufacturing/doctype/operation/operation.json
+++ b/erpnext/manufacturing/doctype/operation/operation.json
@@ -1,167 +1,132 @@
 {
- "allow_copy": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "Prompt", 
- "beta": 0, 
- "creation": "2014-11-07 16:20:30.683186", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2014-11-07 16:20:30.683186",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+  "workstation",
+  "data_2",
+  "is_corrective_operation",
+  "job_card_section",
+  "create_job_card_based_on_batch_size",
+  "column_break_6",
+  "batch_size",
+  "sub_operations_section",
+  "sub_operations",
+  "total_operation_time",
+  "section_break_4",
+  "description"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "workstation", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Default Workstation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Workstation", 
-   "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, 
-   "unique": 0
-  }, 
+   "fieldname": "workstation",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Default Workstation",
+   "options": "Workstation"
+  },
   {
-   "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_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, 
-   "unique": 0
-  }, 
+   "collapsible": 1,
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "label": "Operation Description"
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Description", 
-   "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, 
-   "unique": 0
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "sub_operations_section",
+   "fieldtype": "Section Break",
+   "label": "Sub Operations"
+  },
+  {
+   "fieldname": "sub_operations",
+   "fieldtype": "Table",
+   "options": "Sub Operation"
+  },
+  {
+   "description": "Time in mins.",
+   "fieldname": "total_operation_time",
+   "fieldtype": "Float",
+   "label": "Total Operation Time",
+   "read_only": 1
+  },
+  {
+   "fieldname": "data_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "1",
+   "depends_on": "create_job_card_based_on_batch_size",
+   "fieldname": "batch_size",
+   "fieldtype": "Int",
+   "label": "Batch Size",
+   "mandatory_depends_on": "create_job_card_based_on_batch_size"
+  },
+  {
+   "default": "0",
+   "fieldname": "create_job_card_based_on_batch_size",
+   "fieldtype": "Check",
+   "label": "Create Job Card based on Batch Size"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "job_card_section",
+   "fieldtype": "Section Break",
+   "label": "Job Card"
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_corrective_operation",
+   "fieldtype": "Check",
+   "label": "Is Corrective Operation"
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-wrench", 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2016-11-07 05:28:27.462413", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Operation", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "icon": "fa fa-wrench",
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-01-12 15:09:23.593338",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Operation",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 0, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 1, 
-   "is_custom": 0, 
-   "permlevel": 0, 
-   "print": 0, 
-   "read": 1, 
-   "report": 0, 
-   "role": "Manufacturing User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "export": 1,
+   "import": 1,
+   "read": 1,
+   "role": "Manufacturing User",
+   "share": 1,
    "write": 1
-  }, 
+  },
   {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 0, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 1, 
-   "is_custom": 0, 
-   "permlevel": 0, 
-   "print": 0, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Manufacturing Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "export": 1,
+   "import": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Manufacturing Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/operation/operation.py b/erpnext/manufacturing/doctype/operation/operation.py
index 69e8329..374f320 100644
--- a/erpnext/manufacturing/doctype/operation/operation.py
+++ b/erpnext/manufacturing/doctype/operation/operation.py
@@ -2,9 +2,34 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
+
+import frappe
+from frappe import _
 from frappe.model.document import Document
 
 class Operation(Document):
 	def validate(self):
 		if not self.description:
 			self.description = self.name
+
+		self.duplicate_sub_operation()
+		self.set_total_time()
+
+	def duplicate_sub_operation(self):
+		operation_list = []
+		for row in self.sub_operations:
+			if row.operation in operation_list:
+				frappe.throw(_("The operation {0} can not add multiple times")
+					.format(frappe.bold(row.operation)))
+
+			if self.name == row.operation:
+				frappe.throw(_("The operation {0} can not be the sub operation")
+					.format(frappe.bold(row.operation)))
+
+			operation_list.append(row.operation)
+
+	def set_total_time(self):
+		self.total_operation_time = 0.0
+
+		for row in self.sub_operations:
+			self.total_operation_time += row.time_in_mins
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 288c1d0..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'));
+				}
 			}
 		}
 
@@ -211,16 +219,38 @@
 		});
 	},
 
-	get_items: function(frm) {
+	get_items: function (frm) {
+		frm.clear_table('prod_plan_references');
+
 		frappe.call({
 			method: "get_items",
 			freeze: true,
 			doc: frm.doc,
-			callback: function() {
+			callback: function () {
 				refresh_field('po_items');
 			}
 		});
 	},
+	combine_items: function (frm) {
+		frm.clear_table('prod_plan_references');
+
+		frappe.call({
+			method: "get_items",
+			freeze: true,
+			doc: frm.doc,
+		});
+	},
+
+	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) {
@@ -295,8 +325,25 @@
 	},
 
 	download_materials_required: function(frm) {
-		let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
-		open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc });
+		const fields = [{
+			fieldname: 'warehouses',
+			fieldtype: 'Table MultiSelect',
+			label: __('Warehouses'),
+			default: frm.doc.from_warehouse,
+			options: "Production Plan Material Request Warehouse",
+			get_query: function () {
+				return {
+					filters: {
+						company: frm.doc.company
+					}
+				};
+			},
+		}];
+
+		frappe.prompt(fields, (row) => {
+			let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
+			open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses: row.warehouses });
+		}, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock'));
 	},
 
 	show_progress: function(frm) {
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index f114700..8437895 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -28,7 +28,13 @@
   "material_requests",
   "select_items_to_manufacture_section",
   "get_items",
+  "combine_items",
   "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",
@@ -184,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",
@@ -196,7 +202,7 @@
   {
    "fieldname": "material_request_planning",
    "fieldtype": "Section Break",
-   "label": "Material Request Planning"
+   "label": "Material Requirement Planning"
   },
   {
    "default": "1",
@@ -234,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"
   },
@@ -316,13 +323,48 @@
    "fieldname": "include_safety_stock",
    "fieldtype": "Check",
    "label": "Include Safety Stock in Required Qty Calculation"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.get_items_from == 'Sales Order'",
+   "fieldname": "combine_items",
+   "fieldtype": "Check",
+   "label": "Consolidate Items"
+  },
+  {
+   "fieldname": "section_break_25",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "prod_plan_references",
+   "fieldtype": "Table",
+   "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-03-08 11:17:25.470147",
+ "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 a3e23a6..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
@@ -96,8 +97,10 @@
 
 	@frappe.whitelist()
 	def get_items(self):
+		self.set('po_items', [])
 		if self.get_items_from == "Sales Order":
 			self.get_so_items()
+
 		elif self.get_items_from == "Material Request":
 			self.get_mr_items()
 
@@ -165,9 +168,31 @@
 		self.calculate_total_planned_qty()
 
 	def add_items(self, items):
-		self.set('po_items', [])
+		refs = {}
 		for data in items:
 			item_details = get_item_details(data.item_code)
+			if self.combine_items:
+				if item_details.bom_no in refs:
+					refs[item_details.bom_no]['so_details'].append({
+						'sales_order': data.parent,
+						'sales_order_item': data.name,
+						'qty': data.pending_qty
+					})
+					refs[item_details.bom_no]['qty'] += data.pending_qty
+					continue
+
+				else:
+					refs[item_details.bom_no] = {
+						'qty': data.pending_qty,
+						'po_item_ref': data.name,
+						'so_details': []
+					}
+					refs[item_details.bom_no]['so_details'].append({
+						'sales_order': data.parent,
+						'sales_order_item': data.name,
+						'qty': data.pending_qty
+					})
+
 			pi = self.append('po_items', {
 				'include_exploded_items': 1,
 				'warehouse': data.warehouse,
@@ -191,6 +216,23 @@
 				pi.material_request_item = data.name
 				pi.description = data.description
 
+		if refs:
+			for po_item in self.po_items:
+				po_item.planned_qty = refs[po_item.bom_no]['qty']
+				po_item.pending_qty = refs[po_item.bom_no]['qty']
+				po_item.sales_order = ''
+			self.add_pp_ref(refs)
+
+	def add_pp_ref(self, refs):
+		for bom_no in refs:
+			for so_detail in refs[bom_no]['so_details']:
+				self.append('prod_plan_references', {
+						'item_reference': refs[bom_no]['po_item_ref'],
+						'sales_order': so_detail['sales_order'],
+						'sales_order_item': so_detail['sales_order_item'],
+						'qty': so_detail['qty']
+				})
+
 	def calculate_total_planned_qty(self):
 		self.total_planned_qty = 0
 		for d in self.po_items:
@@ -308,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
@@ -435,19 +516,43 @@
 		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):
-	if isinstance(doc, string_types):
+def download_raw_materials(doc, warehouses=None):
+	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
 	item_list = [['Item Code', 'Description', 'Stock UOM', 'Warehouse', 'Required Qty as per BOM',
-		'Projected Qty', 'Actual Qty', 'Ordered Qty', 'Reserved Qty for Production',
-		'Safety Stock', 'Required Qty']]
+		'Projected Qty', 'Available Qty In Hand', 'Ordered Qty', 'Planned Qty',
+		'Reserved Qty for Production', 'Safety Stock', 'Required Qty']]
 
-	for d in get_items_for_material_requests(doc):
+	doc.warehouse = None
+	for d in get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True):
 		item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'),
 			d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
-			d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
+			d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
 
 		if not doc.get('for_warehouse'):
 			row = {'item_code': d.get('item_code')}
@@ -466,7 +571,7 @@
 			ifnull(sum(bei.stock_qty/ifnull(bom.quantity, 1)), 0)*%s as qty, item.item_name,
 			bei.description, bei.stock_uom, item.min_order_qty, bei.source_warehouse,
 			item.default_material_request_type, item.min_order_qty, item_default.default_warehouse,
-			item.purchase_uom, item_uom.conversion_factor
+			item.purchase_uom, item_uom.conversion_factor, item.safety_stock
 		from
 			`tabBOM Explosion Item` bei
 			JOIN `tabBOM` bom ON bom.name = bei.parent
@@ -618,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)
@@ -636,32 +741,39 @@
 
 	return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
 		ifnull(sum(actual_qty),0) as actual_qty, ifnull(sum(ordered_qty),0) as ordered_qty,
-		ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse from `tabBin`
-		where item_code = %(item_code)s {conditions}
+		ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse,
+		ifnull(sum(planned_qty),0) as planned_qty
+		from `tabBin` where item_code = %(item_code)s {conditions}
 		group by item_code, warehouse
 	""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
 
+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:
+		child_warehouses = frappe.db.get_descendants('Warehouse', row.get("warehouse"))
+		if child_warehouses:
+			warehouse_list.extend(child_warehouses)
+		else:
+			warehouse_list.append(row.get("warehouse"))
+
 @frappe.whitelist()
-def get_items_for_material_requests(doc, warehouses=None):
-	if isinstance(doc, string_types):
+def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
+	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
 	warehouse_list = []
 	if warehouses:
-		if isinstance(warehouses, string_types):
-			warehouses = json.loads(warehouses)
-
-		for row in warehouses:
-			child_warehouses = frappe.db.get_descendants('Warehouse', row.get("warehouse"))
-			if child_warehouses:
-				warehouse_list.extend(child_warehouses)
-			else:
-				warehouse_list.append(row.get("warehouse"))
+		get_warehouse_list(warehouses, warehouse_list)
 
 	if warehouse_list:
 		warehouses = list(set(warehouse_list))
 
-		if doc.get("for_warehouse") and doc.get("for_warehouse") in warehouses:
+		if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses:
 			warehouses.remove(doc.get("for_warehouse"))
 
 		warehouse_list = None
@@ -680,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')
@@ -754,7 +869,7 @@
 				if items:
 					mr_items.append(items)
 
-	if not ignore_existing_ordered_qty and warehouses:
+	if (not ignore_existing_ordered_qty or get_parent_warehouse_data) and warehouses:
 		new_mr_items = []
 		for item in mr_items:
 			get_materials_from_other_locations(item, warehouses, new_mr_items, company)
@@ -811,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 27335aa..cce1bb6 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -100,7 +100,7 @@
 
 	def test_production_plan_sales_orders(self):
 		item = 'Test Production Item 1'
-		so = make_sales_order(item_code=item, qty=5)
+		so = make_sales_order(item_code=item, qty=1)
 		sales_order = so.name
 		sales_order_item = so.items[0].name
 
@@ -124,8 +124,8 @@
 
 		wo_doc = frappe.get_doc('Work Order', work_order)
 		wo_doc.update({
-			'wip_warehouse': '_Test Warehouse 1 - _TC',
-			'fg_warehouse': '_Test Warehouse - _TC'
+			'wip_warehouse': 'Work In Progress - _TC',
+			'fg_warehouse': 'Finished Goods - _TC'
 		})
 		wo_doc.submit()
 
@@ -145,6 +145,58 @@
 
 		self.assertEqual(sales_orders, [])
 
+	def test_production_plan_combine_items(self):
+		item = 'Test Production Item 1'
+		so = make_sales_order(item_code=item, qty=1)
+
+		pln = frappe.new_doc('Production Plan')
+		pln.company = so.company
+		pln.get_items_from = 'Sales Order'
+		pln.append('sales_orders', {
+			'sales_order': so.name,
+			'sales_order_date': so.transaction_date,
+			'customer': so.customer,
+			'grand_total': so.grand_total
+		})
+		so = make_sales_order(item_code=item, qty=2)
+		pln.append('sales_orders', {
+			'sales_order': so.name,
+			'sales_order_date': so.transaction_date,
+			'customer': so.customer,
+			'grand_total': so.grand_total
+		})
+		pln.combine_items = 1
+		pln.get_items()
+		pln.submit()
+
+		self.assertTrue(pln.po_items[0].planned_qty, 3)
+
+		pln.make_work_order()
+		work_order = frappe.db.get_value('Work Order', {
+			'production_plan_item': pln.po_items[0].name,
+			'production_plan': pln.name
+		}, 'name')
+
+		wo_doc = frappe.get_doc('Work Order', work_order)
+		wo_doc.update({
+			'wip_warehouse': 'Work In Progress - _TC',
+		})
+
+		wo_doc.submit()
+		so_items = []
+		for plan_reference in pln.prod_plan_references:
+			so_items.append(plan_reference.sales_order_item)
+			so_wo_qty = frappe.db.get_value('Sales Order Item', plan_reference.sales_order_item, 'work_order_qty')
+			self.assertEqual(so_wo_qty, plan_reference.qty)
+
+		wo_doc.cancel()
+		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)
@@ -184,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 d0dce53..f829d57 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
@@ -1,792 +1,220 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "hash", 
- "beta": 0, 
- "creation": "2013-02-22 01:27:49", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "editable_grid": 1, 
+ "actions": [],
+ "autoname": "hash",
+ "creation": "2013-02-22 01:27:49",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "include_exploded_items",
+  "item_code",
+  "bom_no",
+  "column_break_6",
+  "planned_qty",
+  "warehouse",
+  "planned_start_date",
+  "section_break_9",
+  "pending_qty",
+  "ordered_qty",
+  "column_break_17",
+  "description",
+  "stock_uom",
+  "produced_qty",
+  "reference_section",
+  "sales_order",
+  "sales_order_item",
+  "column_break_19",
+  "material_request",
+  "material_request_item",
+  "product_bundle_item",
+  "item_reference"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fetch_if_empty": 0, 
-   "fieldname": "include_exploded_items", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Include Exploded Items", 
-   "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
-  }, 
+   "columns": 1,
+   "default": "1",
+   "fieldname": "include_exploded_items",
+   "fieldtype": "Check",
+   "label": "Include Exploded Items"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fetch_if_empty": 0, 
-   "fieldname": "item_code", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "item_code", 
-   "oldfieldtype": "Link", 
-   "options": "Item", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "150px", 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0, 
+   "columns": 2,
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "oldfieldname": "item_code",
+   "oldfieldtype": "Link",
+   "options": "Item",
+   "print_width": "150px",
+   "reqd": 1,
    "width": "150px"
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fetch_if_empty": 0, 
-   "fieldname": "bom_no", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "BOM No", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "bom_no", 
-   "oldfieldtype": "Link", 
-   "options": "BOM", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "100px", 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0, 
+   "columns": 2,
+   "fieldname": "bom_no",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "BOM No",
+   "oldfieldname": "bom_no",
+   "oldfieldtype": "Link",
+   "options": "BOM",
+   "print_width": "100px",
+   "reqd": 1,
    "width": "100px"
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "planned_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Planned Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "planned_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "100px", 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0, 
+   "fieldname": "planned_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Planned Qty",
+   "oldfieldname": "planned_qty",
+   "oldfieldtype": "Currency",
+   "print_width": "100px",
+   "reqd": 1,
    "width": "100px"
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "column_break_6", 
-   "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_6",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "description": "If enabled, system will create the work order for the exploded items against which BOM is available.", 
-   "fetch_if_empty": 0, 
-   "fieldname": "make_work_order_for_sub_assembly_items", 
-   "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": "Make Work Order for Sub Assembly Items", 
-   "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": "warehouse",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "For Warehouse",
+   "options": "Warehouse"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "", 
-   "fetch_if_empty": 0, 
-   "fieldname": "warehouse", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "For Warehouse", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Warehouse", 
-   "permlevel": 0, 
-   "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": "Today",
+   "fieldname": "planned_start_date",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "Planned Start Date",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "Today", 
-   "fetch_if_empty": 0, 
-   "fieldname": "planned_start_date", 
-   "fieldtype": "Datetime", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Planned Start 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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Quantity and Description"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "section_break_9", 
-   "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": "Quantity and Description", 
-   "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
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fetch_if_empty": 0, 
-   "fieldname": "pending_qty", 
-   "fieldtype": "Float", 
-   "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": "Pending Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "prevdoc_reqd_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "100px", 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0, 
+   "default": "0",
+   "fieldname": "pending_qty",
+   "fieldtype": "Float",
+   "label": "Pending Qty",
+   "oldfieldname": "prevdoc_reqd_qty",
+   "oldfieldtype": "Currency",
+   "print_width": "100px",
+   "read_only": 1,
    "width": "100px"
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fetch_if_empty": 0, 
-   "fieldname": "ordered_qty", 
-   "fieldtype": "Float", 
-   "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": "Ordered Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "ordered_qty",
+   "fieldtype": "Float",
+   "label": "Ordered Qty",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fetch_if_empty": 0, 
-   "fieldname": "produced_qty", 
-   "fieldtype": "Float", 
-   "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": "Produced Qty", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "produced_qty",
+   "fieldtype": "Float",
+   "label": "Produced Qty",
+   "no_copy": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "column_break_17", 
-   "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_17",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text Editor", 
-   "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": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "description", 
-   "oldfieldtype": "Text", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "200px", 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0, 
+   "fieldname": "description",
+   "fieldtype": "Text Editor",
+   "label": "Description",
+   "oldfieldname": "description",
+   "oldfieldtype": "Text",
+   "print_width": "200px",
+   "read_only": 1,
    "width": "200px"
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "stock_uom", 
-   "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": "UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "stock_uom", 
-   "oldfieldtype": "Data", 
-   "options": "UOM", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "80px", 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0, 
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "oldfieldname": "stock_uom",
+   "oldfieldtype": "Data",
+   "options": "UOM",
+   "print_width": "80px",
+   "read_only": 1,
+   "reqd": 1,
    "width": "80px"
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "reference_section", 
-   "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": "Reference", 
-   "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": "reference_section",
+   "fieldtype": "Section Break",
+   "label": "Reference"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "sales_order", 
-   "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 Order", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "source_docname", 
-   "oldfieldtype": "Data", 
-   "options": "Sales Order", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "sales_order",
+   "fieldtype": "Link",
+   "label": "Sales Order",
+   "oldfieldname": "source_docname",
+   "oldfieldtype": "Data",
+   "options": "Sales Order",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "sales_order_item", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "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 Order Item", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "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": "sales_order_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Sales Order Item",
+   "no_copy": 1,
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "column_break_19", 
-   "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_19",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "material_request", 
-   "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": "Material Request", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Material Request", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "material_request",
+   "fieldtype": "Link",
+   "label": "Material Request",
+   "options": "Material Request",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "material_request_item", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "material_request_item", 
-   "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": "material_request_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "material_request_item"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "product_bundle_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": "Product Bundle Item", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "product_bundle_item",
+   "fieldtype": "Link",
+   "label": "Product Bundle Item",
+   "no_copy": 1,
+   "options": "Item",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "item_reference",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Item Reference"
   }
- ], 
- "has_web_view": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-04-08 23:09:57.199423", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Production Plan Item", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "show_name_in_global_search": 0, 
- "sort_order": "ASC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-28 18:31:06.822168",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/manufacturing/doctype/production_plan_item_reference/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/manufacturing/doctype/production_plan_item_reference/__init__.py
diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json
new file mode 100644
index 0000000..84dee4a
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json
@@ -0,0 +1,52 @@
+{
+ "actions": [],
+ "creation": "2021-04-22 10:32:58.896330",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_reference",
+  "sales_order",
+  "sales_order_item",
+  "qty"
+ ],
+ "fields": [
+  {
+   "fieldname": "sales_order",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Sales Order Reference",
+   "options": "Sales Order"
+  },
+  {
+   "fieldname": "sales_order_item",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Sales Order Item"
+  },
+  {
+   "fieldname": "qty",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "qty"
+  },
+  {
+   "fieldname": "item_reference",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Item Reference"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-07 17:03:49.707487",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Item Reference",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py
new file mode 100644
index 0000000..51fbc36
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, 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 ProductionPlanItemReference(Document):
+	pass
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
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/routing/routing.py b/erpnext/manufacturing/doctype/routing/routing.py
index 8312d74..ece0db7 100644
--- a/erpnext/manufacturing/doctype/routing/routing.py
+++ b/erpnext/manufacturing/doctype/routing/routing.py
@@ -4,14 +4,24 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import cint
+from frappe.utils import cint, flt
 from frappe import _
 from frappe.model.document import Document
 
 class Routing(Document):
 	def validate(self):
+		self.calculate_operating_cost()
 		self.set_routing_id()
 
+	def on_update(self):
+		self.calculate_operating_cost()
+
+	def calculate_operating_cost(self):
+		for operation in self.operations:
+			if not operation.hour_rate:
+				operation.hour_rate = frappe.db.get_value("Workstation", operation.workstation, 'hour_rate')
+			operation.operating_cost = flt(flt(operation.hour_rate) * flt(operation.time_in_mins) / 60, 2)
+
 	def set_routing_id(self):
 		sequence_id = 0
 		for row in self.operations:
@@ -21,4 +31,4 @@
 				frappe.throw(_("At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2}")
 					.format(row.idx, row.sequence_id, sequence_id))
 
-			sequence_id = row.sequence_id
\ No newline at end of file
+			sequence_id = row.sequence_id
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py
index 6a38dcf..92f2694 100644
--- a/erpnext/manufacturing/doctype/routing/test_routing.py
+++ b/erpnext/manufacturing/doctype/routing/test_routing.py
@@ -7,9 +7,7 @@
 import frappe
 from frappe.test_runner import make_test_records
 from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.manufacturing.doctype.operation.test_operation import make_operation
 from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
-from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 
 class TestRouting(unittest.TestCase):
@@ -48,7 +46,53 @@
 		wo_doc.cancel()
 		wo_doc.delete()
 
+	def test_update_bom_operation_time(self):
+		operations = [
+			{
+				"operation": "Test Operation A",
+				"workstation": "_Test Workstation A",
+				"hour_rate_rent": 300,
+				"hour_rate_labour": 750 ,
+				"time_in_mins": 30
+			},
+			{
+				"operation": "Test Operation B",
+				"workstation": "_Test Workstation B",
+				"hour_rate_labour": 200,
+				"hour_rate_rent": 1000,
+				"time_in_mins": 20
+			}
+		]
+
+		test_routing_operations = [
+			{
+				"operation": "Test Operation A",
+				"workstation": "_Test Workstation A",
+				"time_in_mins": 30
+			},
+			{
+				"operation": "Test Operation B",
+				"workstation": "_Test Workstation A",
+				"time_in_mins": 20
+			}
+		]
+		setup_operations(operations)
+		routing_doc = create_routing(routing_name="Routing Test", operations=test_routing_operations)
+		bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency = 'INR')
+		self.assertEqual(routing_doc.operations[0].time_in_mins, 30)
+		self.assertEqual(routing_doc.operations[1].time_in_mins, 20)
+		routing_doc.operations[0].time_in_mins = 90
+		routing_doc.operations[1].time_in_mins = 42.2
+		routing_doc.save()
+		bom_doc.update_cost()
+		bom_doc.reload()
+		self.assertEqual(bom_doc.operations[0].time_in_mins, 90)
+		self.assertEqual(bom_doc.operations[1].time_in_mins, 42.2)
+
+
 def setup_operations(rows):
+	from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
+	from erpnext.manufacturing.doctype.operation.test_operation import make_operation
 	for row in rows:
 		make_workstation(row)
 		make_operation(row)
@@ -61,12 +105,14 @@
 
 	if not args.do_not_save:
 		try:
-			for operation in args.operations:
-				doc.append("operations", operation)
-
 			doc.insert()
 		except frappe.DuplicateEntryError:
 			doc = frappe.get_doc("Routing", args.routing_name)
+			doc.delete_key('operations')
+			for operation in args.operations:
+				doc.append("operations", operation)
+
+			doc.save()
 
 	return doc
 
@@ -91,7 +137,7 @@
 	name = frappe.db.get_value('BOM', {'item': args.item_code}, 'name')
 	if not name:
 		bom_doc = make_bom(item = args.item_code, raw_materials = args.get("raw_materials"),
-			routing = args.routing, with_operations=1)
+			routing = args.routing, with_operations=1, currency = args.currency)
 	else:
 		bom_doc = frappe.get_doc("BOM", name)
 
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/manufacturing/doctype/sub_operation/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/manufacturing/doctype/sub_operation/__init__.py
diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.js b/erpnext/manufacturing/doctype/sub_operation/sub_operation.js
new file mode 100644
index 0000000..be9db6a
--- /dev/null
+++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Sub Operation', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json
new file mode 100644
index 0000000..f63d2b9
--- /dev/null
+++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json
@@ -0,0 +1,51 @@
+{
+ "actions": [],
+ "creation": "2020-12-07 15:39:47.488519",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "operation",
+  "time_in_mins",
+  "column_break_5",
+  "description"
+ ],
+ "fields": [
+  {
+   "fieldname": "operation",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Operation",
+   "options": "Operation"
+  },
+  {
+   "description": "Time in mins",
+   "fieldname": "time_in_mins",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Operation Time"
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-12-07 18:09:18.005578",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Sub Operation",
+ "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/sub_operation/sub_operation.py b/erpnext/manufacturing/doctype/sub_operation/sub_operation.py
new file mode 100644
index 0000000..f4b2775
--- /dev/null
+++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.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 SubOperation(Document):
+	pass
diff --git a/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py b/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
new file mode 100644
index 0000000..d3410ca
--- /dev/null
+++ b/erpnext/manufacturing/doctype/sub_operation/test_sub_operation.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestSubOperation(unittest.TestCase):
+	pass
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index cb1ee92..68de0b2 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -389,17 +389,12 @@
 		ste.submit()
 		stock_entries.append(ste)
 
-		job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
+		job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name}, order_by='creation asc')
 		self.assertEqual(len(job_cards), len(bom.operations))
 
 		for i, job_card in enumerate(job_cards):
 			doc = frappe.get_doc("Job Card", job_card)
-			doc.append("time_logs", {
-				"from_time": add_to_date(None, i),
-				"hours": 1,
-				"to_time": add_to_date(None, i + 1),
-				"completed_qty": doc.for_quantity
-			})
+			doc.time_logs[0].completed_qty = 1
 			doc.submit()
 
 		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 3e5a72d..5120485 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -141,8 +141,7 @@
 		}
 
 		if (frm.doc.docstatus === 1
-			&& frm.doc.operations && frm.doc.operations.length
-			&& frm.doc.qty != frm.doc.material_transferred_for_manufacturing) {
+			&& frm.doc.operations && frm.doc.operations.length) {
 
 			const not_completed = frm.doc.operations.filter(d => {
 				if(d.status != 'Completed') {
@@ -190,35 +189,41 @@
 		const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'),
 			fields: [
 				{
-					fieldtype:'Link',
-					fieldname:'operation',
+					fieldtype: 'Link',
+					fieldname: 'operation',
 					label: __('Operation'),
-					read_only:1,
-					in_list_view:1
+					read_only: 1,
+					in_list_view: 1
 				},
 				{
-					fieldtype:'Link',
-					fieldname:'workstation',
+					fieldtype: 'Link',
+					fieldname: 'workstation',
 					label: __('Workstation'),
-					read_only:1,
-					in_list_view:1
+					read_only: 1,
+					in_list_view: 1
 				},
 				{
-					fieldtype:'Data',
-					fieldname:'name',
+					fieldtype: 'Data',
+					fieldname: 'name',
 					label: __('Operation Id')
 				},
 				{
-					fieldtype:'Float',
-					fieldname:'pending_qty',
+					fieldtype: 'Float',
+					fieldname: 'pending_qty',
 					label: __('Pending Qty'),
 				},
 				{
-					fieldtype:'Float',
-					fieldname:'qty',
+					fieldtype: 'Float',
+					fieldname: 'qty',
 					label: __('Quantity to Manufacture'),
-					read_only:0,
-					in_list_view:1,
+					read_only: 0,
+					in_list_view: 1,
+				},
+				{
+					fieldtype: 'Float',
+					fieldname: 'batch_size',
+					label: __('Batch Size'),
+					read_only: 1
 				},
 			],
 			data: operations_data,
@@ -229,9 +234,13 @@
 		}, function(data) {
 			frappe.call({
 				method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card",
+				freeze: true,
 				args: {
 					work_order: frm.doc.name,
 					operations: data.operations,
+				},
+				callback: function() {
+					frm.reload_doc();
 				}
 			});
 		}, __("Job Card"), __("Create"));
@@ -243,13 +252,16 @@
 			if(data.completed_qty != frm.doc.qty) {
 				pending_qty = frm.doc.qty - flt(data.completed_qty);
 
-				dialog.fields_dict.operations.df.data.push({
-					'name': data.name,
-					'operation': data.operation,
-					'workstation': data.workstation,
-					'qty': pending_qty,
-					'pending_qty': pending_qty,
-				});
+				if (pending_qty) {
+					dialog.fields_dict.operations.df.data.push({
+						'name': data.name,
+						'operation': data.operation,
+						'workstation': data.workstation,
+						'batch_size': data.batch_size,
+						'qty': pending_qty,
+						'pending_qty': pending_qty
+					});
+				}
 			}
 		});
 		dialog.fields_dict.operations.grid.refresh();
@@ -704,6 +716,8 @@
 	stop_work_order: function(frm, status) {
 		frappe.call({
 			method: "erpnext.manufacturing.doctype.work_order.work_order.stop_unstop",
+			freeze: true,
+			freeze_message: __("Updating Work Order status"),
 			args: {
 				work_order: frm.doc.name,
 				status: status
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index cd9edee..3b56854 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -21,6 +21,12 @@
   "produced_qty",
   "sales_order",
   "project",
+  "serial_no_and_batch_for_finished_good_section",
+  "has_serial_no",
+  "has_batch_no",
+  "column_break_17",
+  "serial_no",
+  "batch_size",
   "settings_section",
   "allow_alternative_item",
   "use_multi_level_bom",
@@ -52,16 +58,22 @@
   "actual_operating_cost",
   "additional_operating_cost",
   "column_break_24",
+  "corrective_operation_cost",
   "total_operating_cost",
   "more_info",
   "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"
  ],
@@ -488,17 +500,77 @@
    "fieldtype": "Float",
    "label": "Lead Time",
    "read_only": 1
-  }
+  },
+  {
+   "collapsible": 1,
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "serial_no_and_batch_for_finished_good_section",
+   "fieldtype": "Section Break",
+   "label": "Serial No and Batch for Finished Good"
+  },
+  {
+   "fieldname": "column_break_17",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fetch_from": "production_item.has_serial_no",
+   "fieldname": "has_serial_no",
+   "fieldtype": "Check",
+   "label": "Has Serial No",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "production_item.has_batch_no",
+   "fieldname": "has_batch_no",
+   "fieldtype": "Check",
+   "label": "Has Batch No",
+   "read_only": 1
+  },
+  {
+   "depends_on": "has_serial_no",
+   "fieldname": "serial_no",
+   "fieldtype": "Small Text",
+   "label": "Serial Nos",
+   "no_copy": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "has_batch_no",
+   "fieldname": "batch_size",
+   "fieldtype": "Float",
+   "label": "Batch Size"
+  },
+  {
+   "allow_on_submit": 1,
+   "description": "From Corrective Job Card",
+   "fieldname": "corrective_operation_cost",
+   "fieldtype": "Currency",
+   "label": "Corrective Operation Cost",
+   "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-03-16 13:27:51.116484",
+ "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 8507f5e..779ae42 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1,7 +1,6 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-from __future__ import unicode_literals
 import frappe
 import json
 import math
@@ -19,18 +18,17 @@
 from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
 from erpnext.utilities.transaction_base import validate_uom_is_integer
 from frappe.model.mapper import get_mapped_doc
+from erpnext.stock.doctype.batch.batch import make_batch
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_auto_serial_nos, auto_make_serial_nos
 
 class OverProductionError(frappe.ValidationError): pass
 class CapacityError(frappe.ValidationError): pass
 class StockOverProductionError(frappe.ValidationError): pass
 class OperationTooLongError(frappe.ValidationError): pass
 class ItemHasVariantError(frappe.ValidationError): pass
+class SerialNoQtyError(frappe.ValidationError):
+	pass
 
-from six import string_types
-
-form_grid_templates = {
-	"operations": "templates/form_grid/work_order_grid.html"
-}
 
 class WorkOrder(Document):
 	def onload(self):
@@ -127,7 +125,9 @@
 
 		variable_cost = self.actual_operating_cost if self.actual_operating_cost \
 			else self.planned_operating_cost
-		self.total_operating_cost = flt(self.additional_operating_cost) + flt(variable_cost)
+
+		self.total_operating_cost = (flt(self.additional_operating_cost)
+			+ flt(variable_cost) + flt(self.corrective_operation_cost))
 
 	def validate_work_order_against_so(self):
 		# already ordered qty
@@ -235,13 +235,20 @@
 
 		production_plan.run_method("update_produced_qty", produced_qty, self.production_plan_item)
 
+	def before_submit(self):
+		self.create_serial_no_batch_no()
+
 	def on_submit(self):
 		if not self.wip_warehouse:
 			frappe.throw(_("Work-in-Progress Warehouse is required before Submit"))
 		if not self.fg_warehouse:
 			frappe.throw(_("For Warehouse is required before Submit"))
 
-		self.update_work_order_qty_in_so()
+		if self.production_plan and frappe.db.exists('Production Plan Item Reference',{'parent':self.production_plan}):
+			self.update_work_order_qty_in_combined_so()
+		else:
+			self.update_work_order_qty_in_so()
+
 		self.update_reserved_qty_for_production()
 		self.update_completed_qty_in_material_request()
 		self.update_planned_qty()
@@ -250,14 +257,82 @@
 
 	def on_cancel(self):
 		self.validate_cancel()
-
 		frappe.db.set(self,'status', 'Cancelled')
-		self.update_work_order_qty_in_so()
+
+		if self.production_plan and frappe.db.exists('Production Plan Item Reference',{'parent':self.production_plan}):
+			self.update_work_order_qty_in_combined_so()
+		else:
+			self.update_work_order_qty_in_so()
+
 		self.delete_job_card()
 		self.update_completed_qty_in_material_request()
 		self.update_planned_qty()
 		self.update_ordered_qty()
 		self.update_reserved_qty_for_production()
+		self.delete_auto_created_batch_and_serial_no()
+
+	def create_serial_no_batch_no(self):
+		if not (self.has_serial_no or self.has_batch_no):
+			return
+
+		if not cint(frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")):
+			return
+
+		if self.has_batch_no:
+			self.create_batch_for_finished_good()
+
+		args = {
+			"item_code": self.production_item,
+			"work_order": self.name
+		}
+
+		if self.has_serial_no:
+			self.make_serial_nos(args)
+
+	def create_batch_for_finished_good(self):
+		total_qty = self.qty
+		if not self.batch_size:
+			self.batch_size = total_qty
+
+		while total_qty > 0:
+			qty = self.batch_size
+			if self.batch_size >= total_qty:
+				qty = total_qty
+
+			if total_qty > self.batch_size:
+				total_qty -= self.batch_size
+			else:
+				qty = total_qty
+				total_qty = 0
+
+			make_batch(frappe._dict({
+				"item": self.production_item,
+				"qty_to_produce": qty,
+				"reference_doctype": self.doctype,
+				"reference_name": self.name
+			}))
+
+	def delete_auto_created_batch_and_serial_no(self):
+		for row in frappe.get_all("Serial No", filters = {"work_order": self.name}):
+			frappe.delete_doc("Serial No", row.name)
+			self.db_set("serial_no", "")
+
+		for row in frappe.get_all("Batch", filters = {"reference_name": self.name}):
+			frappe.delete_doc("Batch", row.name)
+
+	def make_serial_nos(self, args):
+		serial_no_series = frappe.get_cached_value("Item", self.production_item, "serial_no_series")
+		if serial_no_series:
+			self.serial_no = get_auto_serial_nos(serial_no_series, self.qty)
+
+		if self.serial_no:
+			args.update({"serial_no": self.serial_no, "actual_qty": self.qty})
+			auto_make_serial_nos(args)
+
+		serial_nos_length = len(get_serial_nos(self.serial_no))
+		if serial_nos_length != self.qty:
+			frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.")
+				.format(self.qty, self.production_item, serial_nos_length), SerialNoQtyError)
 
 	def create_job_card(self):
 		manufacturing_settings_doc = frappe.get_doc("Manufacturing Settings")
@@ -265,32 +340,40 @@
 		enable_capacity_planning = not cint(manufacturing_settings_doc.disable_capacity_planning)
 		plan_days = cint(manufacturing_settings_doc.capacity_planning_for_days) or 30
 
-		for i, row in enumerate(self.operations):
-			self.set_operation_start_end_time(i, row)
-
-			if not row.workstation:
-				frappe.throw(_("Row {0}: select the workstation against the operation {1}")
-					.format(row.idx, row.operation))
-
-			original_start_time = row.planned_start_time
-			job_card_doc = create_job_card(self, row,
-				enable_capacity_planning=enable_capacity_planning, auto_create=True)
-
-			if enable_capacity_planning and job_card_doc:
-				row.planned_start_time = job_card_doc.time_logs[-1].from_time
-				row.planned_end_time = job_card_doc.time_logs[-1].to_time
-
-				if date_diff(row.planned_start_time, original_start_time) > plan_days:
-					frappe.message_log.pop()
-					frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
-						.format(plan_days, row.operation), CapacityError)
-
-				row.db_update()
+		for index, row in enumerate(self.operations):
+			qty = self.qty
+			while qty > 0:
+				qty = split_qty_based_on_batch_size(self, row, qty)
+				if row.job_card_qty > 0:
+					self.prepare_data_for_job_card(row, index,
+						plan_days, enable_capacity_planning)
 
 		planned_end_date = self.operations and self.operations[-1].planned_end_time
 		if planned_end_date:
 			self.db_set("planned_end_date", planned_end_date)
 
+	def prepare_data_for_job_card(self, row, index, plan_days, enable_capacity_planning):
+		self.set_operation_start_end_time(index, row)
+
+		if not row.workstation:
+			frappe.throw(_("Row {0}: select the workstation against the operation {1}")
+				.format(row.idx, row.operation))
+
+		original_start_time = row.planned_start_time
+		job_card_doc = create_job_card(self, row, auto_create=True,
+			enable_capacity_planning=enable_capacity_planning)
+
+		if enable_capacity_planning and job_card_doc:
+			row.planned_start_time = job_card_doc.time_logs[-1].from_time
+			row.planned_end_time = job_card_doc.time_logs[-1].to_time
+
+			if date_diff(row.planned_start_time, original_start_time) > plan_days:
+				frappe.message_log.pop()
+				frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
+					.format(plan_days, row.operation), CapacityError)
+
+			row.db_update()
+
 	def set_operation_start_end_time(self, idx, row):
 		"""Set start and end time for given operation. If first operation, set start as
 		`planned_start_date`, else add time diff to end time of earlier operation."""
@@ -358,52 +441,74 @@
 		frappe.db.set_value('Sales Order Item',
 			self.sales_order_item, 'work_order_qty', flt(work_order_qty/total_bundle_qty, 2))
 
+	def update_work_order_qty_in_combined_so(self):
+		total_bundle_qty = 1
+		if self.product_bundle_item:
+			total_bundle_qty = frappe.db.sql(""" select sum(qty) from
+				`tabProduct Bundle Item` where parent = %s""", (frappe.db.escape(self.product_bundle_item)))[0][0]
+
+			if not total_bundle_qty:
+				# product bundle is 0 (product bundle allows 0 qty for items)
+				total_bundle_qty = 1
+
+		prod_plan = frappe.get_doc('Production Plan', self.production_plan)
+		item_reference = frappe.get_value('Production Plan Item', self.production_plan_item, 'sales_order_item')
+
+		for plan_reference in prod_plan.prod_plan_references:
+			work_order_qty = 0.0
+			if plan_reference.item_reference == item_reference:
+				if self.docstatus == 1:
+					work_order_qty = flt(plan_reference.qty) / total_bundle_qty
+				frappe.db.set_value('Sales Order Item',
+					plan_reference.sales_order_item, 'work_order_qty', work_order_qty)
+
 	def update_completed_qty_in_material_request(self):
 		if self.material_request:
 			frappe.get_doc("Material Request", self.material_request).update_completed_qty([self.material_request_item])
 
 	def set_work_order_operations(self):
 		"""Fetch operations from BOM and set in 'Work Order'"""
-		self.set('operations', [])
 
-		if not self.bom_no:
+		def _get_operations(bom_no, qty=1):
+			return frappe.db.sql(
+					f"""select
+						operation, description, workstation, idx,
+						base_hour_rate as hour_rate, time_in_mins * {qty} as time_in_mins,
+						"Pending" as status, parent as bom, batch_size, sequence_id
+					from
+						`tabBOM Operation`
+					where
+						parent = %s order by idx
+					""", bom_no, as_dict=1)
+
+
+		self.set('operations', [])
+		if not self.bom_no or not frappe.get_cached_value('BOM', self.bom_no, 'with_operations'):
 			return
 
-		if self.use_multi_level_bom:
-			bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
+		operations = []
+		if not self.use_multi_level_bom:
+			bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
+			operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
 		else:
-			bom_list = [self.bom_no]
+			bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation()
+			bom_traversal = list(reversed(bom_tree.level_order_traversal()))
+			bom_traversal.append(bom_tree) # add operation on top level item last
 
-		operations = frappe.db.sql("""
-			select
-				operation, description, workstation, idx,
-				base_hour_rate as hour_rate, time_in_mins,
-				"Pending" as status, parent as bom, batch_size, sequence_id
-			from
-				`tabBOM Operation`
-			where
-				 parent in (%s) order by idx
-		"""	% ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1)
+			for d in bom_traversal:
+				if d.is_bom:
+					operations.extend(_get_operations(d.name, qty=d.exploded_qty))
+
+			for correct_index, operation in enumerate(operations, start=1):
+				operation.idx = correct_index
+
 
 		self.set('operations', operations)
-
-		if self.use_multi_level_bom and self.get('operations') and self.get('items'):
-			raw_material_operations = [d.operation for d in self.get('items')]
-			operations = [d.operation for d in self.get('operations')]
-
-			for operation in raw_material_operations:
-				if operation not in operations:
-					self.append('operations', {
-						'operation': operation
-					})
-
 		self.calculate_time()
 
 	def calculate_time(self):
-		bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
-
 		for d in self.get("operations"):
-			d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * (flt(self.qty) / flt(d.batch_size))
+			d.time_in_mins = flt(d.time_in_mins) * (flt(self.qty) / flt(d.batch_size))
 
 		self.calculate_operating_cost()
 
@@ -485,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):
@@ -640,6 +746,17 @@
 		bom.set_bom_material_details()
 		return bom
 
+	def update_batch_produced_qty(self, stock_entry_doc):
+		if not cint(frappe.db.get_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order")):
+			return
+
+		for row in stock_entry_doc.items:
+			if row.batch_no and (row.is_finished_item or row.is_scrap_item):
+				qty = frappe.get_all("Stock Entry Detail", filters = {"batch_no": row.batch_no, "docstatus": 1},
+					or_filters= {"is_finished_item": 1, "is_scrap_item": 1}, fields = ["sum(qty)"], as_list=1)[0][0]
+
+				frappe.db.set_value("Batch", row.batch_no, "produced_qty", flt(qty))
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_bom_operations(doctype, txt, searchfield, start, page_len, filters):
@@ -717,7 +834,7 @@
 	return wo_doc
 
 def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"):
-	if isinstance(variant_items, string_types):
+	if isinstance(variant_items, str):
 		variant_items = json.loads(variant_items)
 
 	for item in variant_items:
@@ -797,6 +914,7 @@
 
 	stock_entry.set_stock_entry_type()
 	stock_entry.get_items()
+	stock_entry.set_serial_no_batch_for_finished_good()
 	return stock_entry.as_dict()
 
 @frappe.whitelist()
@@ -838,13 +956,47 @@
 
 @frappe.whitelist()
 def make_job_card(work_order, operations):
-	if isinstance(operations, string_types):
+	if isinstance(operations, str):
 		operations = json.loads(operations)
 
 	work_order = frappe.get_doc('Work Order', work_order)
 	for row in operations:
+		row = frappe._dict(row)
 		validate_operation_data(row)
-		create_job_card(work_order, row, row.get("qty"), auto_create=True)
+		qty = row.get("qty")
+		while qty > 0:
+			qty = split_qty_based_on_batch_size(work_order, row, qty)
+			if row.job_card_qty > 0:
+				create_job_card(work_order, row, auto_create=True)
+
+def split_qty_based_on_batch_size(wo_doc, row, qty):
+	if not cint(frappe.db.get_value("Operation",
+		row.operation, "create_job_card_based_on_batch_size")):
+		row.batch_size = row.get("qty") or wo_doc.qty
+
+	row.job_card_qty = row.batch_size
+	if row.batch_size and qty >= row.batch_size:
+		qty -= row.batch_size
+	elif qty > 0:
+		row.job_card_qty = qty
+		qty = 0
+
+	get_serial_nos_for_job_card(row, wo_doc)
+
+	return qty
+
+def get_serial_nos_for_job_card(row, wo_doc):
+	if not wo_doc.serial_no:
+		return
+
+	serial_nos = get_serial_nos(wo_doc.serial_no)
+	used_serial_nos = []
+	for d in frappe.get_all('Job Card', fields=['serial_no'],
+		filters={'docstatus': ('<', 2), 'work_order': wo_doc.name, 'operation_id': row.name}):
+		used_serial_nos.extend(get_serial_nos(d.serial_no))
+
+	serial_nos = sorted(list(set(serial_nos) - set(used_serial_nos)))
+	row.serial_no = '\n'.join(serial_nos[0:row.job_card_qty])
 
 def validate_operation_data(row):
 	if row.get("qty") <= 0:
@@ -863,20 +1015,22 @@
 			)
 		)
 
-def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto_create=False):
+def create_job_card(work_order, row, enable_capacity_planning=False, auto_create=False):
 	doc = frappe.new_doc("Job Card")
 	doc.update({
 		'work_order': work_order.name,
 		'operation': row.get("operation"),
 		'workstation': row.get("workstation"),
 		'posting_date': nowdate(),
-		'for_quantity': qty or work_order.get('qty', 0),
+		'for_quantity': row.job_card_qty or work_order.get('qty', 0),
 		'operation_id': row.get("name"),
 		'bom_no': work_order.bom_no,
 		'project': work_order.project,
 		'company': work_order.company,
 		'sequence_id': row.get("sequence_id"),
-		'wip_warehouse': work_order.wip_warehouse
+		'wip_warehouse': work_order.wip_warehouse,
+		'hour_rate': row.get("hour_rate"),
+		'serial_no': row.get("serial_no")
 	})
 
 	if work_order.transfer_material_against == 'Job Card' and not work_order.skip_transfer:
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
index 87c090f..9aa0715 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
@@ -4,10 +4,17 @@
 def get_data():
 	return {
 		'fieldname': 'work_order',
+		'non_standard_fieldnames': {
+			'Batch': 'reference_name'
+		},
 		'transactions': [
 			{
 				'label': _('Transactions'),
 				'items': ['Stock Entry', 'Job Card', 'Pick List']
+			},
+			{
+				'label': _('Reference'),
+				'items': ['Serial No', 'Batch']
 			}
 		]
 	}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
index 8c5cde9..f7b8787 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
@@ -2,14 +2,14 @@
  "actions": [],
  "creation": "2014-10-16 14:35:41.950175",
  "doctype": "DocType",
- "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "details",
   "operation",
   "bom",
-  "sequence_id",
+  "column_break_4",
   "description",
+  "sequence_id",
   "col_break1",
   "completed_qty",
   "status",
@@ -48,6 +48,7 @@
   {
    "fieldname": "bom",
    "fieldtype": "Link",
+   "in_list_view": 1,
    "label": "BOM",
    "no_copy": 1,
    "options": "BOM",
@@ -67,6 +68,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "columns": 1,
    "description": "Operation completed for how many finished goods?",
    "fieldname": "completed_qty",
    "fieldtype": "Float",
@@ -76,6 +78,7 @@
    "read_only": 1
   },
   {
+   "columns": 1,
    "default": "Pending",
    "fieldname": "status",
    "fieldtype": "Select",
@@ -118,6 +121,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "columns": 1,
    "description": "in Minutes",
    "fieldname": "time_in_mins",
    "fieldtype": "Float",
@@ -195,12 +199,16 @@
    "label": "Sequence ID",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-10-14 12:58:49.241252",
+ "modified": "2021-06-24 14:36:12.835543",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order Operation",
@@ -209,4 +217,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index c6699be..9b73aca 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -1,16 +1,19 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
 # See license.txt
 from __future__ import unicode_literals
+from erpnext.manufacturing.doctype.operation.test_operation import make_operation
 
 import frappe
 import unittest
 from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours, NotInWorkingHoursError, WorkstationHolidayError
+from erpnext.manufacturing.doctype.routing.test_routing import setup_bom, create_routing
+from frappe.test_runner import make_test_records
 
 test_dependencies = ["Warehouse"]
 test_records = frappe.get_test_records('Workstation')
+make_test_records('Workstation')
 
 class TestWorkstation(unittest.TestCase):
-
 	def test_validate_timings(self):
 		check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
 		check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
@@ -21,6 +24,58 @@
 		self.assertRaises(WorkstationHolidayError, check_if_within_operating_hours,
 			"_Test Workstation 1", "Operation 1", "2013-02-01 10:00:00", "2013-02-02 20:00:00")
 
+	def test_update_bom_operation_rate(self):
+		operations = [
+			{
+				"operation": "Test Operation A",
+				"workstation": "_Test Workstation A",
+				"hour_rate_rent": 300,
+				"time_in_mins": 60
+			},
+			{
+				"operation": "Test Operation B",
+				"workstation": "_Test Workstation B",
+				"hour_rate_rent": 1000,
+				"time_in_mins": 60
+			}
+		]
+
+		for row in operations:
+			make_workstation(row)
+			make_operation(row)
+
+		test_routing_operations = [
+			{
+				"operation": "Test Operation A",
+				"workstation": "_Test Workstation A",
+				"time_in_mins": 60
+			},
+			{
+				"operation": "Test Operation B",
+				"workstation": "_Test Workstation A",
+				"time_in_mins": 60
+			}
+		]
+		routing_doc = create_routing(routing_name = "Routing Test", operations=test_routing_operations)
+		bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency="INR")
+		w1 = frappe.get_doc("Workstation", "_Test Workstation A")
+		#resets values
+		w1.hour_rate_rent = 300
+		w1.hour_rate_labour = 0
+		w1.save()
+		bom_doc.update_cost()
+		bom_doc.reload()
+		self.assertEqual(w1.hour_rate, 300)
+		self.assertEqual(bom_doc.operations[0].hour_rate, 300)
+		w1.hour_rate_rent = 250
+		w1.save()
+		#updating after setting new rates in workstations
+		bom_doc.update_cost()
+		bom_doc.reload()
+		self.assertEqual(w1.hour_rate, 250)
+		self.assertEqual(bom_doc.operations[0].hour_rate, 250)
+		self.assertEqual(bom_doc.operations[1].hour_rate, 250)
+
 def make_workstation(*args, **kwargs):
 	args = args if args else kwargs
 	if isinstance(args, tuple):
@@ -34,9 +89,10 @@
 			"doctype": "Workstation",
 			"workstation_name": workstation_name
 		})
-
+		doc.hour_rate_rent = args.get("hour_rate_rent")
+		doc.hour_rate_labour = args.get("hour_rate_labour")
 		doc.insert()
 
 		return doc
 	except frappe.DuplicateEntryError:
-		return frappe.get_doc("Workstation", workstation_name)
\ No newline at end of file
+		return frappe.get_doc("Workstation", workstation_name)
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index 3512e59..f4483f7 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -39,7 +39,8 @@
 
 	def update_bom_operation(self):
 		bom_list = frappe.db.sql("""select DISTINCT parent from `tabBOM Operation`
-			where workstation = %s""", self.name)
+			where workstation = %s and parenttype = 'routing' """, self.name)
+
 		for bom_no in bom_list:
 			frappe.db.sql("""update `tabBOM Operation` set hour_rate = %s
 				where parent = %s and workstation = %s""",
@@ -71,7 +72,7 @@
 def is_within_operating_hours(workstation, operation, from_datetime, to_datetime):
 	operation_length = time_diff_in_seconds(to_datetime, from_datetime)
 	workstation = frappe.get_doc("Workstation", workstation)
-	
+
 	if not workstation.working_hours:
 		return
 
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/patches/repair_tools/__init__.py b/erpnext/manufacturing/report/cost_of_poor_quality_report/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/manufacturing/report/cost_of_poor_quality_report/__init__.py
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
new file mode 100644
index 0000000..97e7e0a
--- /dev/null
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
@@ -0,0 +1,105 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Cost of Poor Quality Report"] = {
+	"filters": [
+		{
+			label: __("Company"),
+			fieldname: "company",
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			label: __("From Date"),
+			fieldname:"from_date",
+			fieldtype: "Datetime",
+			default: frappe.datetime.convert_to_system_tz(frappe.datetime.add_months(frappe.datetime.now_datetime(), -1)),
+			reqd: 1
+		},
+		{
+			label: __("To Date"),
+			fieldname:"to_date",
+			fieldtype: "Datetime",
+			default: frappe.datetime.now_datetime(),
+			reqd: 1,
+		},
+		{
+			label: __("Job Card"),
+			fieldname: "name",
+			fieldtype: "Link",
+			options: "Job Card",
+			get_query: function() {
+				return {
+					filters: {
+						is_corrective_job_card: 1,
+						docstatus: 1
+					}
+				}
+			}
+		},
+		{
+			label: __("Work Order"),
+			fieldname: "work_order",
+			fieldtype: "Link",
+			options: "Work Order"
+		},
+		{
+			label: __("Operation"),
+			fieldname: "operation",
+			fieldtype: "Link",
+			options: "Operation",
+			get_query: function() {
+				return {
+					filters: {
+						is_corrective_operation: 1
+					}
+				}
+			}
+		},
+		{
+			label: __("Workstation"),
+			fieldname: "workstation",
+			fieldtype: "Link",
+			options: "Workstation"
+		},
+		{
+			label: __("Item"),
+			fieldname: "production_item",
+			fieldtype: "Link",
+			options: "Item"
+		},
+		{
+			label: __("Serial No"),
+			fieldname: "serial_no",
+			fieldtype: "Link",
+			options: "Serial No",
+			depends_on: "eval: doc.production_item",
+			get_query: function() {
+				var item_code = frappe.query_report.get_filter_value('production_item');
+				return {
+					filters: {
+						item_code: item_code
+					}
+				}
+			}
+		},
+		{
+			label: __("Batch No"),
+			fieldname: "batch_no",
+			fieldtype: "Link",
+			options: "Batch No",
+			depends_on: "eval: doc.production_item",
+			get_query: function() {
+				var item_code = frappe.query_report.get_filter_value('production_item');
+				return {
+					filters: {
+						item: item_code
+					}
+				}
+			}
+		},
+	]
+};
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.json b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.json
new file mode 100644
index 0000000..ee63bc1
--- /dev/null
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.json
@@ -0,0 +1,33 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-01-11 11:10:58.292896",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2021-01-11 11:11:03.594242",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Cost of Poor Quality Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Job Card",
+ "report_name": "Cost of Poor Quality Report",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Manufacturing User"
+  },
+  {
+   "role": "Manufacturing Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
new file mode 100644
index 0000000..9f81e7d
--- /dev/null
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
@@ -0,0 +1,127 @@
+# 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 import _
+from frappe.utils import flt
+
+def execute(filters=None):
+	columns, data = [], []
+
+	columns = get_columns(filters)
+	data = get_data(filters)
+
+	return columns, data
+
+def get_data(report_filters):
+	data = []
+	operations = frappe.get_all("Operation", filters = {"is_corrective_operation": 1})
+	if operations:
+		operations = [d.name for d in operations]
+		fields = ["production_item as item_code", "item_name", "work_order", "operation",
+			"workstation", "total_time_in_mins", "name", "hour_rate", "serial_no", "batch_no"]
+
+		filters = get_filters(report_filters, operations)
+
+		job_cards = frappe.get_all("Job Card", fields = fields,
+			filters = filters)
+
+		for row in job_cards:
+			row.operating_cost = flt(row.hour_rate) * (flt(row.total_time_in_mins) / 60.0)
+			update_raw_material_cost(row, report_filters)
+			data.append(row)
+
+	return data
+
+def get_filters(report_filters, operations):
+	filters = {"docstatus": 1, "operation": ("in", operations), "is_corrective_job_card": 1}
+	for field in ["name", "work_order", "operation", "workstation", "company", "serial_no", "batch_no", "production_item"]:
+		if report_filters.get(field):
+			if field != 'serial_no':
+				filters[field] = report_filters.get(field)
+			else:
+				filters[field] = ('like', '% {} %'.format(report_filters.get(field)))
+
+	return filters
+
+def update_raw_material_cost(row, filters):
+	row.rm_cost = 0.0
+	for data in frappe.get_all("Job Card Item", fields = ["amount"],
+		filters={"parent": row.name, "docstatus": 1}):
+		row.rm_cost += data.amount
+
+def get_columns(filters):
+	return [
+		{
+			"label": _("Job Card"),
+			"fieldtype": "Link",
+			"fieldname": "name",
+			"options": "Job Card",
+			"width": "100"
+		},
+		{
+			"label": _("Work Order"),
+			"fieldtype": "Link",
+			"fieldname": "work_order",
+			"options": "Work Order",
+			"width": "100"
+		},
+		{
+			"label": _("Item Code"),
+			"fieldtype": "Link",
+			"fieldname": "item_code",
+			"options": "Item",
+			"width": "100"
+		},
+		{
+			"label": _("Item Name"),
+			"fieldtype": "Data",
+			"fieldname": "item_name",
+			"width": "100"
+		},
+		{
+			"label": _("Operation"),
+			"fieldtype": "Link",
+			"fieldname": "operation",
+			"options": "Operation",
+			"width": "100"
+		},
+		{
+			"label": _("Serial No"),
+			"fieldtype": "Data",
+			"fieldname": "serial_no",
+			"width": "100"
+		},
+		{
+			"label": _("Batch No"),
+			"fieldtype": "Data",
+			"fieldname": "batch_no",
+			"width": "100"
+		},
+		{
+			"label": _("Workstation"),
+			"fieldtype": "Link",
+			"fieldname": "workstation",
+			"options": "Workstation",
+			"width": "100"
+		},
+		{
+			"label": _("Operating Cost"),
+			"fieldtype": "Currency",
+			"fieldname": "operating_cost",
+			"width": "100"
+		},
+		{
+			"label": _("Raw Material Cost"),
+			"fieldtype": "Currency",
+			"fieldname": "rm_cost",
+			"width": "100"
+		},
+		{
+			"label": _("Total Time (in Mins)"),
+			"fieldtype": "Float",
+			"fieldname": "total_time_in_mins",
+			"width": "100"
+		}
+	]
\ No newline at end of file
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/patches/repair_tools/__init__.py b/erpnext/manufacturing/report/production_plan_summary/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy 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 93689a0..29376f0 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -1,494 +1,19 @@
-execute:import unidecode # new requirement
-erpnext.patches.v8_0.move_perpetual_inventory_setting
-erpnext.patches.v8_9.set_print_zero_amount_taxes
 erpnext.patches.v12_0.update_is_cancelled_field
 erpnext.patches.v11_0.rename_production_order_to_work_order
 erpnext.patches.v11_0.refactor_naming_series
 erpnext.patches.v11_0.refactor_autoname_naming
-erpnext.patches.v10_0.rename_schools_to_education
-erpnext.patches.v4_0.validate_v3_patch
-erpnext.patches.v4_0.fix_employee_user_id
-erpnext.patches.v4_0.remove_employee_role_if_no_employee
-erpnext.patches.v4_0.update_user_properties
-erpnext.patches.v4_0.apply_user_permissions
-erpnext.patches.v4_0.move_warehouse_user_to_restrictions
-erpnext.patches.v4_0.global_defaults_to_system_settings
-erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule
 execute:frappe.reload_doc("accounts", "doctype", "POS Payment Method") #2020-05-28
 execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16 #2020-07-24
-execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24
-execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31
-execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29
-execute:frappe.reload_doc('selling', 'doctype', 'quotation') # 2014-01-29
-execute:frappe.reload_doc('stock', 'doctype', 'delivery_note') # 2014-01-29
-erpnext.patches.v4_0.reload_sales_print_format
-execute:frappe.reload_doc('accounts', 'doctype', 'purchase_invoice') # 2014-01-29
-execute:frappe.reload_doc('buying', 'doctype', 'purchase_order') # 2014-01-29
-execute:frappe.reload_doc('buying', 'doctype', 'supplier_quotation') # 2014-01-29
-execute:frappe.reload_doc('stock', 'doctype', 'purchase_receipt') # 2014-01-29
-execute:frappe.reload_doc('accounts', 'doctype', 'pos_setting') # 2014-01-29
-execute:frappe.reload_doc('selling', 'doctype', 'customer') # 2014-01-29
-execute:frappe.reload_doc('buying', 'doctype', 'supplier') # 2014-01-29
-execute:frappe.reload_doc('accounts', 'doctype', 'asset_category')
-execute:frappe.reload_doc('accounts', 'doctype', 'pricing_rule')
-erpnext.patches.v4_0.map_charge_to_taxes_and_charges
-execute:frappe.reload_doc('support', 'doctype', 'newsletter') # 2014-01-31
-execute:frappe.reload_doc('hr', 'doctype', 'employee') # 2014-02-03
-execute:frappe.db.sql("update tabPage set module='Core' where name='Setup'")
-erpnext.patches.v5_2.change_item_selects_to_checks
-execute:frappe.reload_doctype('Item')
-erpnext.patches.v4_0.fields_to_be_renamed
-erpnext.patches.v4_0.rename_sitemap_to_route
-erpnext.patches.v7_0.re_route #2016-06-27
-erpnext.patches.v4_0.fix_contact_address
-erpnext.patches.v4_0.customer_discount_to_pricing_rule
-execute:frappe.db.sql("""delete from `tabWebsite Item Group` where ifnull(item_group, '')=''""")
-erpnext.patches.v4_0.remove_module_home_pages
-erpnext.patches.v4_0.split_email_settings
-erpnext.patches.v4_0.import_country_codes
-erpnext.patches.v4_0.countrywise_coa
-execute:frappe.delete_doc("DocType", "MIS Control")
-execute:frappe.delete_doc("Page", "Financial Statements")
-execute:frappe.delete_doc("DocType", "Stock Ledger")
-execute:frappe.delete_doc("DocType", "Grade")
-execute:frappe.db.sql("delete from `tabWebsite Item Group` where ifnull(item_group, '')=''")
-execute:frappe.delete_doc("Print Format", "SalesInvoice")
-execute:import frappe.defaults;frappe.defaults.clear_default("price_list_currency")
-erpnext.patches.v4_0.update_account_root_type
-execute:frappe.delete_doc("Report", "Purchase In Transit")
-erpnext.patches.v4_0.new_address_template
-execute:frappe.delete_doc("DocType", "SMS Control")
-execute:frappe.delete_doc_if_exists("DocType", "Bulk SMS") #2015-08-18
-erpnext.patches.v4_0.fix_case_of_hr_module_def
-erpnext.patches.v4_0.fix_address_template
-
-# WATCHOUT: This patch reload's documents
-erpnext.patches.v4_0.reset_permissions_for_masters
-erpnext.patches.v6_20x.rename_project_name_to_project #2016-03-14
-
-erpnext.patches.v4_0.update_tax_amount_after_discount
-execute:frappe.permissions.reset_perms("GL Entry") #2014-06-09
-execute:frappe.permissions.reset_perms("Stock Ledger Entry") #2014-06-09
-erpnext.patches.v4_0.create_custom_fields_for_india_specific_fields
-erpnext.patches.v4_0.save_default_letterhead
-erpnext.patches.v4_0.update_custom_print_formats_for_renamed_fields
-erpnext.patches.v4_0.update_other_charges_in_custom_purchase_print_formats
-erpnext.patches.v4_0.create_price_list_if_missing
-execute:frappe.db.sql("update `tabItem` set end_of_life=null where end_of_life='0000-00-00'") #2014-06-16
-erpnext.patches.v4_0.update_users_report_view_settings
-erpnext.patches.v4_0.set_pricing_rule_for_buying_or_selling
-erpnext.patches.v4_1.set_outgoing_email_footer
-erpnext.patches.v4_1.fix_sales_order_delivered_status
-erpnext.patches.v4_1.fix_delivery_and_billing_status
-execute:frappe.db.sql("update `tabAccount` set root_type='Liability' where root_type='Income' and report_type='Balance Sheet'")
-execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool") # 29-07-2014
-execute:frappe.delete_doc("DocType", "Payment to Invoice Matching Tool Detail") # 29-07-2014
-execute:frappe.delete_doc("Page", "trial-balance") #2014-07-22
-erpnext.patches.v4_2.delete_old_print_formats #2014-07-29
-erpnext.patches.v4_2.toggle_rounded_total #2014-07-30
-erpnext.patches.v4_2.fix_account_master_type
-erpnext.patches.v4_2.update_project_milestones
-erpnext.patches.v4_2.add_currency_turkish_lira #2014-08-08
-execute:frappe.delete_doc("DocType", "Landed Cost Wizard")
-erpnext.patches.v4_2.default_website_style
-erpnext.patches.v4_2.set_company_country
-erpnext.patches.v4_2.update_sales_order_invoice_field_name
-erpnext.patches.v4_2.seprate_manufacture_and_repack
-execute:frappe.delete_doc("Report", "Warehouse-Wise Stock Balance")
-execute:frappe.delete_doc("DocType", "Purchase Request")
-execute:frappe.delete_doc("DocType", "Purchase Request Item")
-erpnext.patches.v4_2.recalculate_bom_cost
-erpnext.patches.v4_2.fix_gl_entries_for_stock_transactions
 erpnext.patches.v4_2.update_requested_and_ordered_qty #2021-03-31
-execute:frappe.rename_doc("DocType", "Support Ticket", "Issue", force=True)
-erpnext.patches.v4_4.make_email_accounts
-execute:frappe.delete_doc("DocType", "Contact Control")
-erpnext.patches.v4_2.discount_amount
-erpnext.patches.v4_2.reset_bom_costs
-erpnext.patches.v5_0.update_frozen_accounts_permission_role
-erpnext.patches.v5_0.update_dn_against_doc_fields
-execute:frappe.db.sql("update `tabMaterial Request` set material_request_type = 'Material Transfer' where material_request_type = 'Transfer'")
-execute:frappe.reload_doc('stock', 'doctype', 'item')
-erpnext.patches.v5_0.set_default_company_in_bom
-execute:frappe.reload_doc('crm', 'doctype', 'lead')
-execute:frappe.reload_doc('crm', 'doctype', 'opportunity')
-erpnext.patches.v5_0.rename_taxes_and_charges_master
-erpnext.patches.v5_1.sales_bom_rename
-erpnext.patches.v5_0.rename_table_fieldnames
-execute:frappe.db.sql("update `tabJournal Entry` set voucher_type='Journal Entry' where ifnull(voucher_type, '')=''")
-erpnext.patches.v5_0.is_group
-erpnext.patches.v4_2.party_model
-erpnext.patches.v5_0.party_model_patch_fix
-erpnext.patches.v4_1.fix_jv_remarks
-erpnext.patches.v4_2.update_landed_cost_voucher
-erpnext.patches.v4_2.set_item_has_batch
-erpnext.patches.v4_2.update_stock_uom_for_dn_in_sle
-erpnext.patches.v5_0.recalculate_total_amount_in_jv
-erpnext.patches.v5_0.update_companywise_payment_account
-erpnext.patches.v5_0.remove_birthday_events
-erpnext.patches.v5_0.update_item_name_in_bom
-erpnext.patches.v5_0.rename_customer_issue
-erpnext.patches.v5_0.rename_total_fields
-erpnext.patches.v5_0.new_crm_module
-erpnext.patches.v5_0.rename_customer_issue
-erpnext.patches.v5_0.update_material_transfer_for_manufacture
-execute:frappe.reload_doc('crm', 'doctype', 'opportunity_item')
-erpnext.patches.v5_0.update_item_description_and_image
-erpnext.patches.v5_0.update_material_transferred_for_manufacturing
-erpnext.patches.v5_0.stock_entry_update_value
-erpnext.patches.v5_0.convert_stock_reconciliation
-erpnext.patches.v5_0.update_projects
-erpnext.patches.v5_0.item_patches
-erpnext.patches.v5_0.update_journal_entry_title
-erpnext.patches.v5_0.taxes_and_totals_in_party_currency
-erpnext.patches.v5_0.replace_renamed_fields_in_custom_scripts_and_print_formats
-erpnext.patches.v5_0.update_from_bom
-erpnext.patches.v5_0.update_account_types
-erpnext.patches.v5_0.update_sms_sender
-erpnext.patches.v5_0.set_appraisal_remarks
-erpnext.patches.v5_0.update_time_log_title
-erpnext.patches.v7_0.create_warehouse_nestedset
-erpnext.patches.v7_0.merge_account_type_stock_and_warehouse_to_stock
-erpnext.patches.v7_0.set_is_group_for_warehouse
-erpnext.patches.v7_2.stock_uom_in_selling
-erpnext.patches.v4_2.repost_sle_for_si_with_no_warehouse
-erpnext.patches.v5_0.newsletter
-execute:frappe.delete_doc("DocType", "Chart of Accounts")
-execute:frappe.delete_doc("DocType", "Style Settings")
-erpnext.patches.v5_0.update_opportunity
-erpnext.patches.v5_0.opportunity_not_submittable
-execute:frappe.permissions.reset_perms("Purchase Taxes and Charges Template") #2014-06-09
-execute:frappe.permissions.reset_perms("Expense Claim Type") #2014-06-19
-erpnext.patches.v5_0.execute_on_doctype_update
-erpnext.patches.v4_2.fix_recurring_orders
-erpnext.patches.v4_2.delete_gl_entries_for_cancelled_invoices
-erpnext.patches.v5_0.project_costing
-erpnext.patches.v5_0.update_temporary_account
-erpnext.patches.v5_0.update_advance_paid
-erpnext.patches.v5_0.link_warehouse_with_account
-execute:frappe.delete_doc("Page", "stock-ledger")
-execute:frappe.delete_doc("Page","stock-level")
-erpnext.patches.v5_0.reclculate_planned_operating_cost_in_production_order
-erpnext.patches.v5_0.repost_requested_qty
-erpnext.patches.v5_0.fix_taxes_and_totals_in_party_currency
-erpnext.patches.v5_0.update_tax_amount_after_discount_in_purchase_cycle
-erpnext.patches.v5_0.rename_pos_setting
-erpnext.patches.v5_0.update_operation_description
-erpnext.patches.v5_0.set_footer_address
-execute:frappe.db.set_value("Backup Manager", None, "send_backups_to_dropbox", 1 if frappe.db.get_value("Backup Manager", None, "upload_backups_to_dropbox") in ("Daily", "Weekly") else 0)
-execute:frappe.db.sql_list("delete from `tabDocPerm` where parent='Issue' and modified_by='Administrator' and role='Guest'")
-erpnext.patches.v5_0.update_item_and_description_again
-erpnext.patches.v6_0.multi_currency
-erpnext.patches.v7_0.create_budget_record
-erpnext.patches.v5_0.repost_gle_for_jv_with_multiple_party
-erpnext.patches.v5_0.portal_fixes
-erpnext.patches.v5_0.reset_values_in_tools # 02-05-2016
-execute:frappe.delete_doc("Page", "users")
-erpnext.patches.v5_0.update_material_transferred_for_manufacturing_again
-erpnext.patches.v5_0.index_on_account_and_gl_entry
-execute:frappe.db.sql("""delete from `tabProject Task`""")
-erpnext.patches.v5_0.update_item_desc_in_invoice
-erpnext.patches.v5_1.fix_against_account
-execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True)
-erpnext.patches.v5_1.rename_roles
-erpnext.patches.v5_1.default_bom
-execute:frappe.delete_doc("DocType", "Party Type")
-execute:frappe.delete_doc("Module Def", "Contacts")
-erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items # 30-07-2015
-execute:frappe.reload_doctype("Leave Type")
-execute:frappe.db.sql("update `tabLeave Type` set include_holiday=0")
-erpnext.patches.v5_4.set_root_and_report_type
-erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation
-erpnext.patches.v5_4.fix_invoice_outstanding
-execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0")
-erpnext.patches.v5_4.fix_missing_item_images
-erpnext.patches.v5_4.stock_entry_additional_costs
-erpnext.patches.v5_4.cleanup_journal_entry #2015-08-14
 erpnext.patches.v5_7.update_item_description_based_on_item_master
-erpnext.patches.v5_7.item_template_attributes
-execute:frappe.delete_doc_if_exists("DocType", "Manage Variants")
-execute:frappe.delete_doc_if_exists("DocType", "Manage Variants Item")
 erpnext.patches.v4_2.repost_reserved_qty #2021-03-31
-erpnext.patches.v5_4.update_purchase_cost_against_project
-erpnext.patches.v5_8.update_order_reference_in_return_entries
-erpnext.patches.v5_8.add_credit_note_print_heading
-execute:frappe.delete_doc_if_exists("Print Format", "Credit Note - Negative Invoice")
-
-# V6.0
-erpnext.patches.v6_0.set_default_title # 2015-09-03
-erpnext.patches.v6_0.default_activity_rate
-execute:frappe.db.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
-execute:frappe.db.sql("""update `tabProject` set percent_complete=round(percent_complete, 2) where percent_complete is not null""")
-erpnext.patches.v6_0.fix_outstanding_amount
-erpnext.patches.v6_0.fix_planned_qty
-erpnext.patches.v6_2.remove_newsletter_duplicates
-erpnext.patches.v6_2.fix_missing_default_taxes_and_lead
-erpnext.patches.v6_3.convert_applicable_territory
-erpnext.patches.v6_4.round_status_updater_percentages
-erpnext.patches.v6_4.repost_gle_for_journal_entries_where_reference_name_missing
-erpnext.patches.v6_4.fix_journal_entries_due_to_reconciliation
-erpnext.patches.v6_4.fix_status_in_sales_and_purchase_order
-erpnext.patches.v6_4.fix_modified_in_sales_order_and_purchase_order
-erpnext.patches.v6_4.fix_duplicate_bins
-erpnext.patches.v6_4.fix_sales_order_maintenance_status
-erpnext.patches.v6_4.email_digest_update
-
-# delete shopping cart doctypes
-execute:frappe.delete_doc_if_exists("DocType", "Applicable Territory")
-execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
-execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master")
-
-erpnext.patches.v6_4.set_user_in_contact
-erpnext.patches.v6_4.make_image_thumbnail #2015-10-20
-erpnext.patches.v6_5.show_in_website_for_template_item
-erpnext.patches.v6_4.fix_expense_included_in_valuation
-execute:frappe.delete_doc_if_exists("Report", "Item-wise Last Purchase Rate")
-erpnext.patches.v6_6.fix_website_image
-erpnext.patches.v6_6.remove_fiscal_year_from_leave_allocation
-execute:frappe.delete_doc_if_exists("DocType", "Stock UOM Replace Utility")
-erpnext.patches.v6_8.make_webform_standard #2015-11-23
-erpnext.patches.v6_8.move_drop_ship_to_po_items
-erpnext.patches.v6_10.fix_ordered_received_billed
-erpnext.patches.v6_10.fix_jv_total_amount #2015-11-30
-erpnext.patches.v6_10.email_digest_default_quote
-erpnext.patches.v6_10.fix_billed_amount_in_drop_ship_po
-erpnext.patches.v6_10.fix_delivery_status_of_drop_ship_item #2015-12-08
-erpnext.patches.v5_8.tax_rule #2015-12-08
-erpnext.patches.v6_12.set_overdue_tasks
-erpnext.patches.v6_16.update_billing_status_in_dn_and_pr
-erpnext.patches.v6_16.create_manufacturer_records
-execute:frappe.db.sql("update `tabPricing Rule` set title=name where title='' or title is null") #2016-01-27
-erpnext.patches.v6_20.set_party_account_currency_in_orders
-erpnext.patches.v6_19.comment_feed_communication
-erpnext.patches.v6_21.fix_reorder_level
-erpnext.patches.v6_21.rename_material_request_fields
-erpnext.patches.v6_23.update_stopped_status_to_closed
-erpnext.patches.v6_24.set_recurring_id
-erpnext.patches.v6_20x.set_compact_print
-execute:frappe.delete_doc_if_exists("Web Form", "contact") #2016-03-10
-erpnext.patches.v6_20x.remove_fiscal_year_from_holiday_list
-erpnext.patches.v6_24.map_customer_address_to_shipping_address_on_po
-erpnext.patches.v6_27.fix_recurring_order_status
-erpnext.patches.v6_20x.update_product_bundle_description
-erpnext.patches.v7_0.update_party_status #2016-09-22
-erpnext.patches.v7_0.remove_features_setup
-erpnext.patches.v7_0.update_home_page
-execute:frappe.delete_doc_if_exists("Page", "financial-analytics")
-erpnext.patches.v7_0.update_project_in_gl_entry
-execute:frappe.db.sql('update tabQuotation set status="Cancelled" where docstatus=2')
-execute:frappe.rename_doc("DocType", "Payments", "Sales Invoice Payment", force=True)
-erpnext.patches.v7_0.update_mins_to_first_response
-erpnext.patches.v6_20x.repost_valuation_rate_for_negative_inventory
-erpnext.patches.v7_0.migrate_mode_of_payments_v6_to_v7
-erpnext.patches.v7_0.system_settings_setup_complete
-erpnext.patches.v7_0.set_naming_series_for_timesheet #2016-07-27
-execute:frappe.reload_doc('projects', 'doctype', 'project')
-execute:frappe.reload_doc('projects', 'doctype', 'project_user')
-erpnext.patches.v7_0.convert_timelogbatch_to_timesheet
-erpnext.patches.v7_0.convert_timelog_to_timesheet
-erpnext.patches.v7_0.move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet
-erpnext.patches.v7_0.remove_doctypes_and_reports #2016-10-29
-erpnext.patches.v7_0.update_maintenance_module_in_doctype
-erpnext.patches.v7_0.update_prevdoc_values_for_supplier_quotation_item
-erpnext.patches.v7_0.rename_advance_table_fields
-erpnext.patches.v7_0.rename_salary_components
-erpnext.patches.v7_0.rename_prevdoc_fields
-erpnext.patches.v7_0.rename_time_sheet_doctype
-execute:frappe.delete_doc_if_exists("Report", "Customers Not Buying Since Long Time")
-erpnext.patches.v7_0.make_is_group_fieldtype_as_check
-execute:frappe.reload_doc('projects', 'doctype', 'timesheet') #2016-09-12
-erpnext.patches.v7_1.rename_field_timesheet
-execute:frappe.delete_doc_if_exists("Report", "Employee Holiday Attendance")
-execute:frappe.delete_doc_if_exists("DocType", "Payment Tool")
-execute:frappe.delete_doc_if_exists("DocType", "Payment Tool Detail")
-erpnext.patches.v7_0.setup_account_table_for_expense_claim_type_if_exists
-erpnext.patches.v7_0.migrate_schools_to_erpnext
-erpnext.patches.v7_1.update_lead_source
-erpnext.patches.v6_20x.remove_customer_supplier_roles
-erpnext.patches.v7_0.remove_administrator_role_in_doctypes
-erpnext.patches.v7_0.rename_fee_amount_to_fee_component
-erpnext.patches.v7_0.calculate_total_costing_amount
-erpnext.patches.v7_0.fix_nonwarehouse_ledger_gl_entries_for_transactions
-erpnext.patches.v7_0.remove_old_earning_deduction_doctypes
-erpnext.patches.v7_0.make_guardian
-erpnext.patches.v7_0.update_refdoc_in_landed_cost_voucher
-erpnext.patches.v7_0.set_material_request_type_in_item
-erpnext.patches.v7_0.rename_examination_to_assessment
-erpnext.patches.v7_0.set_portal_settings
-erpnext.patches.v7_0.update_change_amount_account
-erpnext.patches.v7_0.fix_duplicate_icons
-erpnext.patches.v7_0.repost_gle_for_pos_sales_return
-erpnext.patches.v7_1.update_total_billing_hours
-erpnext.patches.v7_1.update_component_type
-erpnext.patches.v7_0.repost_gle_for_pos_sales_return
-erpnext.patches.v7_0.update_missing_employee_in_timesheet
-erpnext.patches.v7_0.update_status_for_timesheet
-erpnext.patches.v7_0.set_party_name_in_payment_entry
-erpnext.patches.v7_1.set_student_guardian
-erpnext.patches.v7_0.update_conversion_factor_in_supplier_quotation_item
-erpnext.patches.v7_1.move_sales_invoice_from_parent_to_child_timesheet
-execute:frappe.db.sql("update `tabTimesheet` ts, `tabEmployee` emp set ts.employee_name = emp.employee_name where emp.name = ts.employee and ts.employee_name is null and ts.employee is not null")
-erpnext.patches.v7_1.fix_link_for_customer_from_lead
-execute:frappe.db.sql("delete from `tabTimesheet Detail` where NOT EXISTS (select name from `tabTimesheet` where name = `tabTimesheet Detail`.parent)")
-erpnext.patches.v7_0.update_mode_of_payment_type
-
-execute:frappe.reload_doctype('Employee') #2016-10-18
-execute:frappe.db.sql("update `tabEmployee` set prefered_contact_email = IFNULL(prefered_contact_email,'') ")
 execute:frappe.reload_doc("Payroll", "doctype", "salary_slip")
-execute:frappe.db.sql("update `tabSalary Slip` set posting_date=creation")
-execute:frappe.reload_doc("stock", "doctype", "stock_settings")
-erpnext.patches.v8_0.create_domain_docs	#16-05-2017
-erpnext.patches.v7_1.update_portal_roles
-erpnext.patches.v7_1.set_total_amount_currency_in_je
-finally:erpnext.patches.v7_0.update_timesheet_communications
-erpnext.patches.v7_0.update_status_of_zero_amount_sales_order
-erpnext.patches.v7_1.add_field_for_task_dependent
-erpnext.patches.v7_0.repost_bin_qty_and_item_projected_qty
-erpnext.patches.v7_1.set_prefered_contact_email
-execute:frappe.reload_doc('accounts', 'doctype', 'accounts_settings')
-execute:frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancellation_of_invoice", 0)
-execute:frappe.db.sql("update `tabStock Entry` set total_amount = 0 where purpose in('Repack', 'Manufacture')")
-erpnext.patches.v7_1.save_stock_settings
-erpnext.patches.v7_0.repost_gle_for_pi_with_update_stock #2016-11-01
-erpnext.patches.v7_1.add_account_user_role_for_timesheet
-erpnext.patches.v7_0.set_base_amount_in_invoice_payment_table
-erpnext.patches.v7_1.update_invoice_status
-erpnext.patches.v7_0.po_status_issue_for_pr_return
-erpnext.patches.v7_1.update_missing_salary_component_type
-erpnext.patches.v7_1.rename_quality_inspection_field
-erpnext.patches.v7_0.update_autoname_field
-erpnext.patches.v7_1.update_bom_base_currency
-erpnext.patches.v7_0.update_status_of_po_so
-erpnext.patches.v7_1.set_budget_against_as_cost_center
-erpnext.patches.v7_1.set_currency_exchange_date
-erpnext.patches.v7_1.set_sales_person_status
-erpnext.patches.v7_1.repost_stock_for_deleted_bins_for_merging_items
-erpnext.patches.v7_2.update_website_for_variant
-erpnext.patches.v7_2.update_assessment_modules
-erpnext.patches.v7_2.update_doctype_status
-erpnext.patches.v7_2.update_salary_slips
-erpnext.patches.v7_2.delete_fleet_management_module_def
-erpnext.patches.v7_2.contact_address_links
-erpnext.patches.v7_2.mark_students_active
-erpnext.patches.v7_2.set_null_value_to_fields
-erpnext.patches.v7_2.update_guardian_name_in_student_master
-erpnext.patches.v7_2.update_abbr_in_salary_slips
-erpnext.patches.v7_2.rename_evaluation_criteria
-erpnext.patches.v7_2.update_party_type
-erpnext.patches.v7_2.setup_auto_close_settings
-erpnext.patches.v7_2.empty_supplied_items_for_non_subcontracted
-erpnext.patches.v7_2.arrear_leave_encashment_as_salary_component
-erpnext.patches.v7_2.rename_att_date_attendance
-erpnext.patches.v7_2.update_attendance_docstatus
-erpnext.patches.v7_2.make_all_assessment_group
-erpnext.patches.v8_0.repost_reserved_qty_for_multiple_sales_uom
-erpnext.patches.v8_0.addresses_linked_to_lead
-execute:frappe.delete_doc('DocType', 'Purchase Common')
-erpnext.patches.v8_0.update_stock_qty_value_in_purchase_invoice
-erpnext.patches.v8_0.update_supplier_address_in_stock_entry
-erpnext.patches.v8_0.rename_is_sample_item_to_allow_zero_valuation_rate
-erpnext.patches.v8_0.set_null_to_serial_nos_for_disabled_sales_invoices
-erpnext.patches.v8_0.enable_booking_asset_depreciation_automatically
-erpnext.patches.v8_0.set_project_copied_from
-erpnext.patches.v8_0.update_status_as_paid_for_completed_expense_claim
-erpnext.patches.v7_2.stock_uom_in_selling
-erpnext.patches.v8_0.revert_manufacturers_table_from_item
-erpnext.patches.v8_0.disable_instructor_role
-erpnext.patches.v8_0.merge_student_batch_and_student_group
-erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017
-erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding
-erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice
-erpnext.patches.v8_0.set_sales_invoice_serial_number_from_delivery_note
-erpnext.patches.v8_0.delete_schools_depricated_doctypes
-erpnext.patches.v8_0.update_customer_pos_id
-erpnext.patches.v8_0.rename_items_in_status_field_of_material_request
-erpnext.patches.v8_0.delete_bin_indexes
-erpnext.patches.v8_0.move_account_head_from_account_to_warehouse_for_inventory
-erpnext.patches.v8_0.change_in_words_varchar_length
-erpnext.patches.v8_0.update_stock_qty_value_in_bom_item
-erpnext.patches.v8_0.update_sales_cost_in_project
-erpnext.patches.v8_0.save_system_settings
-erpnext.patches.v8_1.delete_deprecated_reports
-erpnext.patches.v9_0.remove_subscription_module
-erpnext.patches.v8_7.make_subscription_from_recurring_data
 erpnext.patches.v8_1.setup_gst_india #2017-06-27
-execute:frappe.reload_doc('regional', 'doctype', 'gst_hsn_code')
 erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account #16-08-2018
-erpnext.patches.v8_1.gst_fixes #2017-07-06
-erpnext.patches.v8_0.update_production_orders
-erpnext.patches.v8_1.remove_sales_invoice_from_returned_serial_no
-erpnext.patches.v8_1.allow_invoice_copy_to_edit_after_submit
-erpnext.patches.v8_1.add_hsn_sac_codes
-erpnext.patches.v8_1.update_gst_state #17-07-2017
-erpnext.patches.v8_1.removed_report_support_hours
-erpnext.patches.v8_1.add_indexes_in_transaction_doctypes
-erpnext.patches.v8_3.set_restrict_to_domain_for_module_def
-erpnext.patches.v8_1.update_expense_claim_status
-erpnext.patches.v8_3.update_company_total_sales #2017-08-16
-erpnext.patches.v8_4.make_scorecard_records
-erpnext.patches.v8_1.set_delivery_date_in_so_item #2017-07-28
-erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
-erpnext.patches.v8_5.remove_quotations_route_in_sidebar
-erpnext.patches.v8_5.update_existing_data_in_project_type
-erpnext.patches.v8_5.set_default_mode_of_payment
-erpnext.patches.v8_5.update_customer_group_in_POS_profile
-erpnext.patches.v8_6.update_timesheet_company_from_PO
-erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
-erpnext.patches.v8_5.remove_project_type_property_setter
 erpnext.patches.v8_7.sync_india_custom_fields
-erpnext.patches.v8_7.fix_purchase_receipt_status
-erpnext.patches.v8_6.rename_bom_update_tool
-erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017 #22-11-2017 #15-12-2017
-erpnext.patches.v8_9.rename_company_sales_target_field
-erpnext.patches.v8_8.set_bom_rate_as_per_uom
-erpnext.patches.v8_8.add_new_fields_in_accounts_settings
-erpnext.patches.v8_9.set_default_customer_group
-erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
-erpnext.patches.v8_9.set_default_fields_in_variant_settings
-erpnext.patches.v8_9.update_billing_gstin_for_indian_account
-erpnext.patches.v8_9.set_member_party_type
-erpnext.patches.v9_0.add_user_to_child_table_in_pos_profile
-erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
-erpnext.patches.v9_0.student_admission_childtable_migrate
-erpnext.patches.v9_0.add_healthcare_domain
-erpnext.patches.v9_0.set_variant_item_description
-erpnext.patches.v9_0.set_uoms_in_variant_field
-erpnext.patches.v9_0.copy_old_fees_field_data
-execute:frappe.delete_doc_if_exists("DocType", "Program Fee")
-erpnext.patches.v9_0.set_pos_profile_name
-erpnext.patches.v9_0.remove_non_existing_warehouse_from_stock_settings
-execute:frappe.delete_doc_if_exists("DocType", "Program Fee")
-erpnext.patches.v8_10.change_default_customer_credit_days
-erpnext.patches.v9_0.update_employee_loan_details
-erpnext.patches.v9_2.delete_healthcare_domain_default_items
-erpnext.patches.v9_1.create_issue_opportunity_type
-erpnext.patches.v9_2.rename_translated_domains_in_en
-erpnext.patches.v9_0.set_shipping_type_for_existing_shipping_rules
-erpnext.patches.v9_0.update_multi_uom_fields_in_material_request
-erpnext.patches.v9_2.repost_reserved_qty_for_production
-erpnext.patches.v9_2.remove_company_from_patient
-erpnext.patches.v9_2.set_item_name_in_production_order
-erpnext.patches.v10_0.update_lft_rgt_for_employee
-erpnext.patches.v9_2.rename_net_weight_in_item_master
-erpnext.patches.v9_2.delete_process_payroll
-erpnext.patches.v10_0.add_agriculture_domain
-erpnext.patches.v10_0.add_non_profit_domain
-erpnext.patches.v10_0.setup_vat_for_uae_and_saudi_arabia #2017-12-28
-erpnext.patches.v10_0.set_primary_contact_for_customer
-erpnext.patches.v10_0.copy_projects_renamed_fields
-erpnext.patches.v10_0.enabled_regional_print_format_based_on_country
-erpnext.patches.v10_0.update_asset_calculate_depreciation
-erpnext.patches.v10_0.add_guardian_role_for_parent_portal
-erpnext.patches.v10_0.set_numeric_ranges_in_template_if_blank
-erpnext.patches.v10_0.update_reserved_qty_for_purchase_order
 erpnext.patches.v10_0.fichier_des_ecritures_comptables_for_france
-erpnext.patches.v10_0.update_assessment_plan
-erpnext.patches.v10_0.update_assessment_result
-erpnext.patches.v10_0.set_default_payment_terms_based_on_company
-erpnext.patches.v10_0.update_sales_order_link_to_purchase_order
 erpnext.patches.v10_0.rename_price_to_rate_in_pricing_rule
 erpnext.patches.v10_0.set_currency_in_pricing_rule
-erpnext.patches.v10_0.set_b2c_limit
 erpnext.patches.v10_0.update_translatable_fields
 erpnext.patches.v10_0.rename_offer_letter_to_job_offer
 execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True)
@@ -496,16 +21,6 @@
 erpnext.patches.v10_0.add_default_cash_flow_mappers
 erpnext.patches.v11_0.rename_duplicate_item_code_values
 erpnext.patches.v11_0.make_quality_inspection_template
-erpnext.patches.v10_0.update_status_for_multiple_source_in_po
-erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry
-erpnext.patches.v10_0.update_territory_and_customer_group
-erpnext.patches.v10_0.update_warehouse_address_details
-erpnext.patches.v10_0.update_reserved_qty_for_purchase_order
-erpnext.patches.v10_0.update_hub_connector_domain
-erpnext.patches.v10_0.set_student_party_type
-erpnext.patches.v10_0.update_project_in_sle
-erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
-erpnext.patches.v10_0.repost_requested_qty_for_non_stock_uom_items
 erpnext.patches.v11_0.merge_land_unit_with_location
 erpnext.patches.v11_0.add_index_on_nestedset_doctypes
 erpnext.patches.v11_0.remove_modules_setup_page
@@ -514,7 +29,6 @@
 erpnext.patches.v11_0.add_default_email_template_for_leave
 erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
 erpnext.patches.v11_0.uom_conversion_data #30-06-2018
-erpnext.patches.v10_0.taxes_issue_with_pos
 erpnext.patches.v11_0.update_account_type_in_party_type
 erpnext.patches.v11_0.rename_healthcare_doctype_and_fields
 erpnext.patches.v11_0.rename_supplier_type_to_supplier_group
@@ -522,8 +36,6 @@
 erpnext.patches.v11_0.update_brand_in_item_price
 erpnext.patches.v11_0.create_default_success_action
 erpnext.patches.v11_0.add_healthcare_service_unit_tree_root
-erpnext.patches.v10_0.set_qty_in_transactions_based_on_serial_no_input
-erpnext.patches.v10_0.show_leaves_of_all_department_members_in_calendar
 erpnext.patches.v11_0.rename_field_max_days_allowed
 erpnext.patches.v11_0.create_salary_structure_assignments
 erpnext.patches.v11_0.rename_health_insurance
@@ -536,7 +48,6 @@
 erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07
 erpnext.patches.v11_0.rename_overproduction_percent_field
 erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom
-erpnext.patches.v10_0.update_status_in_purchase_receipt
 erpnext.patches.v11_0.inter_state_field_for_gst
 erpnext.patches.v11_0.rename_members_with_naming_series #04-06-2018
 erpnext.patches.v11_0.set_update_field_and_value_in_workflow_state
@@ -550,13 +61,10 @@
 erpnext.patches.v11_0.set_department_for_doctypes
 erpnext.patches.v11_0.update_allow_transfer_for_manufacture
 erpnext.patches.v11_0.add_item_group_defaults
-erpnext.patches.v10_0.update_address_template_for_india
 erpnext.patches.v11_0.add_expense_claim_default_account
 execute:frappe.delete_doc("Page", "hub")
 erpnext.patches.v11_0.reset_publish_in_hub_for_all_items
 erpnext.patches.v11_0.update_hub_url # 2018-08-31  # 2018-09-03
-erpnext.patches.v10_0.set_discount_amount
-erpnext.patches.v10_0.recalculate_gross_margin_for_project
 erpnext.patches.v11_0.make_job_card
 erpnext.patches.v11_0.redesign_healthcare_billing_work_flow
 erpnext.patches.v10_0.delete_hub_documents # 12-08-2018
@@ -570,9 +78,6 @@
 execute:frappe.delete_doc_if_exists("Page", "production-analytics")
 erpnext.patches.v11_0.ewaybill_fields_gst_india #2018-11-13 #2019-01-09 #2019-04-01 #2019-04-26 #2019-05-03
 erpnext.patches.v11_0.drop_column_max_days_allowed
-erpnext.patches.v10_0.update_user_image_in_employee
-erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items
-erpnext.patches.v10_0.allow_operators_in_supplier_scorecard
 erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019
 erpnext.patches.v11_0.update_delivery_trip_status
 erpnext.patches.v11_0.set_missing_gst_hsn_code
@@ -783,3 +288,6 @@
 erpnext.patches.v13_0.update_timesheet_changes
 erpnext.patches.v13_0.set_training_event_attendance
 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/repair_tools/set_stock_balance_as_per_serial_no.py b/erpnext/patches/repair_tools/set_stock_balance_as_per_serial_no.py
deleted file mode 100644
index 5a421d1..0000000
--- a/erpnext/patches/repair_tools/set_stock_balance_as_per_serial_no.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	from erpnext.stock.stock_balance import set_stock_balance_as_per_serial_no
-	frappe.db.auto_commit_on_many_writes = 1
-
-	set_stock_balance_as_per_serial_no()
-
-	frappe.db.auto_commit_on_many_writes = 0
diff --git a/erpnext/patches/v10_0/__init__.py b/erpnext/patches/v10_0/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v10_0/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v10_0/add_agriculture_domain.py b/erpnext/patches/v10_0/add_agriculture_domain.py
deleted file mode 100644
index c18e69f..0000000
--- a/erpnext/patches/v10_0/add_agriculture_domain.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	domain = 'Agriculture'
-	if not frappe.db.exists('Domain', domain):
-		frappe.get_doc({
-			'doctype': 'Domain',
-			'domain': domain
-		}).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py b/erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py
deleted file mode 100644
index 0b891f2..0000000
--- a/erpnext/patches/v10_0/add_guardian_role_for_parent_portal.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# create guardian role
-	if not frappe.get_value('Role', dict(role_name='Guardian')):
-		frappe.get_doc({
-			'doctype': 'Role',
-			'role_name': 'Guardian',
-			'desk_access': 0,
-			'restrict_to_domain': 'Education'
-		}).insert(ignore_permissions=True)
-	
-	# set guardian roles in already created users
-	if frappe.db.exists("Doctype", "Guardian"):
-		for user in frappe.db.sql_list("""select u.name from `tabUser` u , `tabGuardian` g where g.email_address = u.name"""):
-			user = frappe.get_doc('User', user)
-			user.flags.ignore_validate = True
-			user.flags.ignore_mandatory = True
-			user.save()
diff --git a/erpnext/patches/v10_0/add_non_profit_domain.py b/erpnext/patches/v10_0/add_non_profit_domain.py
deleted file mode 100644
index b03d669..0000000
--- a/erpnext/patches/v10_0/add_non_profit_domain.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	domain = 'Non Profit'
-	if not frappe.db.exists('Domain', domain):
-		frappe.get_doc({
-			'doctype': 'Domain',
-			'domain': domain
-		}).insert(ignore_permissions=True)
-
-		frappe.get_doc({
-			'doctype': 'Role',
-			'role_name': 'Non Profit Portal User',
-			'desk_access': 0,
-			'restrict_to_domain': domain
-		}).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py b/erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py
deleted file mode 100644
index 827f9bc..0000000
--- a/erpnext/patches/v10_0/allow_operators_in_supplier_scorecard.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_criteria')
-	frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_scoring_criteria')
-	frappe.reload_doc('buying', 'doctype', 'supplier_scorecard')
-
-	for criteria in frappe.get_all('Supplier Scorecard Criteria', fields=['name', 'formula'], limit_page_length=None):
-		frappe.db.set_value('Supplier Scorecard Criteria', criteria.name,
-			'formula', criteria.formula.replace('&lt;','<').replace('&gt;','>'))
-
-	for criteria in frappe.get_all('Supplier Scorecard Scoring Criteria', fields=['name', 'formula'], limit_page_length=None):
-		if criteria.formula: # not mandatory
-			frappe.db.set_value('Supplier Scorecard Scoring Criteria', criteria.name,
-				'formula', criteria.formula.replace('&lt;','<').replace('&gt;','>'))
-
-	for sc in frappe.get_all('Supplier Scorecard', fields=['name', 'weighting_function'], limit_page_length=None):
-		frappe.db.set_value('Supplier Scorecard', sc.name, 'weighting_function',
-			sc.weighting_function.replace('&lt;','<').replace('&gt;','>'))
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/copy_projects_renamed_fields.py b/erpnext/patches/v10_0/copy_projects_renamed_fields.py
deleted file mode 100644
index 80db3bd..0000000
--- a/erpnext/patches/v10_0/copy_projects_renamed_fields.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-    """ copy data from old fields to new """
-    frappe.reload_doc("projects", "doctype", "project")
-
-    if frappe.db.has_column('Project', 'total_sales_cost'):
-        rename_field('Project', "total_sales_cost", "total_sales_amount")
-
-    if frappe.db.has_column('Project', 'total_billing_amount'):
-        rename_field('Project', "total_billing_amount", "total_billable_amount")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py b/erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py
deleted file mode 100644
index 38b04ce..0000000
--- a/erpnext/patches/v10_0/enabled_regional_print_format_based_on_country.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	print_format_mapper = {
-		'India': ['GST POS Invoice', 'GST Tax Invoice'],
-		'Saudi Arabia': ['Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice'],
-		'United Arab Emirates': ['Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice']
-	}
-
-	frappe.db.sql(""" update `tabPrint Format` set disabled = 1 where name
-		in ('GST POS Invoice', 'GST Tax Invoice', 'Simplified Tax Invoice', 'Detailed Tax Invoice')""")
-
-	for d in frappe.get_all('Company', fields = ["country"],
-		filters={'country': ('in', ['India', 'Saudi Arabia', 'United Arab Emirates'])}):
-		if print_format_mapper.get(d.country):
-			print_formats = print_format_mapper.get(d.country)
-			frappe.db.sql(""" update `tabPrint Format` set disabled = 0
-				where name in (%s)""" % ", ".join(["%s"]*len(print_formats)), tuple(print_formats))
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py b/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py
deleted file mode 100644
index c0a9e5e..0000000
--- a/erpnext/patches/v10_0/fix_reserved_qty_for_sub_contract.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.utils import get_bin
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "bin")
-	frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied")
-	for d in frappe.db.sql("""
-		select distinct rm_item_code, reserve_warehouse
-		from `tabPurchase Order Item Supplied`
-		where docstatus=1 and reserve_warehouse is not null and reserve_warehouse != ''"""):
-
-		try:
-			bin_doc = get_bin(d[0], d[1])
-			bin_doc.update_reserved_qty_for_sub_contracting()
-		except:
-			pass
-
-	for d in frappe.db.sql("""select distinct item_code, source_warehouse
-		from `tabWork Order Item`
-		where docstatus=1 and transferred_qty > required_qty
-			and source_warehouse is not null and source_warehouse != ''""", as_list=1):
-
-		try:
-			bin_doc = get_bin(d[0], d[1])
-			bin_doc.update_reserved_qty_for_production()
-		except:
-			pass
diff --git a/erpnext/patches/v10_0/recalculate_gross_margin_for_project.py b/erpnext/patches/v10_0/recalculate_gross_margin_for_project.py
deleted file mode 100644
index 6d461f3..0000000
--- a/erpnext/patches/v10_0/recalculate_gross_margin_for_project.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('projects', 'doctype', 'project')
-	for d in frappe.db.sql(""" select name from `tabProject` where
-		ifnull(total_consumed_material_cost, 0 ) > 0 and ifnull(total_billed_amount, 0) > 0""", as_dict=1):
-		doc = frappe.get_doc("Project", d.name)
-		doc.calculate_gross_margin()
-		doc.db_set('gross_margin', doc.gross_margin)
-		doc.db_set('per_gross_margin', doc.per_gross_margin)
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/rename_schools_to_education.py b/erpnext/patches/v10_0/rename_schools_to_education.py
deleted file mode 100644
index 85c25a8..0000000
--- a/erpnext/patches/v10_0/rename_schools_to_education.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# rename the School module as Education
-
-	# rename the school module
-	if frappe.db.exists('Module Def', 'Schools') and not frappe.db.exists('Module Def', 'Education'):
-		frappe.rename_doc("Module Def", "Schools", "Education")
-
-	# delete the school module
-	if frappe.db.exists('Module Def', 'Schools') and frappe.db.exists('Module Def', 'Education'):
-		frappe.db.sql("""delete from `tabModule Def` where module_name = 'Schools'""")
-
-
-	# rename "School Settings" to the "Education Settings
-	if frappe.db.exists('DocType', 'School Settings'):
-		frappe.rename_doc("DocType", "School Settings", "Education Settings", force=True)
-		frappe.reload_doc("education", "doctype", "education_settings")
-
-	# delete the discussion web form if exists
-	if frappe.db.exists('Web Form', 'Discussion'):
-		frappe.db.sql("""delete from `tabWeb Form` where name = 'discussion'""")
-
-	# rename the select option field from "School Bus" to "Institute's Bus"
-	frappe.reload_doc("education", "doctype", "Program Enrollment")
-	if "mode_of_transportation" in frappe.db.get_table_columns("Program Enrollment"):
-		frappe.db.sql("""update `tabProgram Enrollment` set mode_of_transportation = "Institute's Bus"
-			where mode_of_transportation = "School Bus" """)
diff --git a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py b/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
deleted file mode 100644
index e6546e3..0000000
--- a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe, erpnext
-
-def execute():
-	for company in frappe.get_all("Company"):
-		if not erpnext.is_perpetual_inventory_enabled(company.name):
-			continue
-
-		acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto") or "1900-01-01"
-		pr_with_rejected_warehouse = frappe.db.sql("""
-			select pr.name
-			from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
-			where pr.name = pr_item.parent
-				and pr.posting_date > %s
-				and pr.docstatus=1
-				and pr.company = %s
-				and pr_item.rejected_qty > 0
-		""", (acc_frozen_upto, company.name), as_dict=1)
-
-		for d in pr_with_rejected_warehouse:
-			doc = frappe.get_doc("Purchase Receipt", d.name)
-
-			doc.docstatus = 2
-			doc.make_gl_entries_on_cancel()
-
-
-			# update gl entries for submit state of PR
-			doc.docstatus = 1
-			doc.make_gl_entries()
diff --git a/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py b/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py
deleted file mode 100644
index 4fe4e97..0000000
--- a/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2019, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
-
-	count=0
-	for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse
-		from `tabMaterial Request Item` where docstatus = 1 and stock_uom<>uom"""):
-			try:
-				count += 1
-				update_bin_qty(item_code, warehouse, {
-					"indented_qty": get_indented_qty(item_code, warehouse),
-				})
-				if count % 200 == 0:
-					frappe.db.commit()
-			except:
-				frappe.db.rollback()
diff --git a/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py b/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py
deleted file mode 100644
index c6470f2..0000000
--- a/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	serialised_items = [d.name for d in frappe.get_all("Item", filters={"has_serial_no": 1})]
-
-	if not serialised_items:
-		return
-
-	for dt in ["Stock Entry Detail", "Purchase Receipt Item", "Purchase Invoice Item"]:
-		cond = ""
-		if dt=="Purchase Invoice Item":
-			cond = """ and parent in (select name from `tabPurchase Invoice`
-				where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.parent and update_stock=1)"""
-
-		item_rows = frappe.db.sql("""
-			select name
-			from `tab{0}`
-			where conversion_factor != 1
-				and docstatus = 1
-				and ifnull(serial_no, '') = ''
-				and item_code in ({1})
-				{2}
-		""".format(dt, ', '.join(['%s']*len(serialised_items)), cond), tuple(serialised_items))
-
-		if item_rows:
-			sle_serial_nos = dict(frappe.db.sql("""
-				select voucher_detail_no, serial_no
-				from `tabStock Ledger Entry`
-				where ifnull(serial_no, '') != ''
-					and voucher_detail_no in (%s)
-			""".format(', '.join(['%s']*len(item_rows))),
-				tuple([d[0] for d in item_rows])))
-
-			batch_size = 100
-			for i in range(0, len(item_rows), batch_size):
-				batch_item_rows = item_rows[i:i + batch_size]
-				when_then = []
-				for item_row in batch_item_rows:
-				
-					when_then.append('WHEN `name` = "{row_name}" THEN "{value}"'.format(
-						row_name=item_row[0],
-						value=sle_serial_nos.get(item_row[0])))
-
-				frappe.db.sql("""
-					update
-						`tab{doctype}`
-					set
-						serial_no = CASE {when_then_cond} ELSE `serial_no` END
-				""".format(
-					doctype = dt,
-					when_then_cond=" ".join(when_then)
-				))
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_b2c_limit.py b/erpnext/patches/v10_0/set_b2c_limit.py
deleted file mode 100644
index 5d964e6..0000000
--- a/erpnext/patches/v10_0/set_b2c_limit.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("regional", "doctype", "gst_settings")
-	frappe.reload_doc("accounts", "doctype", "gst_account")
-	gst_settings = frappe.get_doc("GST Settings")
-	gst_settings.b2c_limit = 250000
-	gst_settings.save()
diff --git a/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py b/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py
deleted file mode 100644
index a90e096..0000000
--- a/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.patches.v8_10.change_default_customer_credit_days import make_payment_term, make_template
-
-def execute():
-	for dt in ("Company", "Customer Group"):
-		frappe.reload_doc("setup", "doctype", frappe.scrub(dt))
-
-		credit_records = frappe.db.sql("""
-			SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
-			from `tab{0}`
-			where
-				((credit_days_based_on='Fixed Days' or credit_days_based_on is null) and credit_days is not null)
-				or credit_days_based_on='Last Day of the Next Month'
-		""".format(dt), as_dict=1)
-
-		for d in credit_records:
-			template = create_payment_terms_template(d)
-
-			frappe.db.sql("""
-				update `tab{0}`
-				set `payment_terms` = %s
-				where name = %s
-			""".format(dt), (template.name, d.name))
-
-def create_payment_terms_template(data):
-	if data.credit_days_based_on == "Fixed Days":
-		pyt_template_name = 'Default Payment Term - N{0}'.format(data.credit_days)
-	else:
-		pyt_template_name = 'Default Payment Term - EO2M'
-
-	if not frappe.db.exists("Payment Terms Template", pyt_template_name):
-		payment_term = make_payment_term(data.credit_days, data.credit_days_based_on)
-		template = make_template(payment_term)
-	else:
-		template = frappe.get_doc("Payment Terms Template", pyt_template_name)
-	return template
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_discount_amount.py b/erpnext/patches/v10_0/set_discount_amount.py
deleted file mode 100644
index d5e2c5a..0000000
--- a/erpnext/patches/v10_0/set_discount_amount.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "sales_invoice_item")
-	frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_item')
-	frappe.reload_doc('buying', 'doctype', 'purchase_order_item')
-	frappe.reload_doc('buying', 'doctype', 'supplier_quotation_item')
-	frappe.reload_doc('selling', 'doctype', 'sales_order_item')
-	frappe.reload_doc('selling', 'doctype', 'quotation_item')
-	frappe.reload_doc('stock', 'doctype', 'delivery_note_item')
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
-
-	selling_doctypes = ["Sales Order Item", "Sales Invoice Item", "Delivery Note Item", "Quotation Item"]
-	buying_doctypes = ["Purchase Order Item", "Purchase Invoice Item", "Purchase Receipt Item", "Supplier Quotation Item"]
-
-	for doctype in selling_doctypes:
-		frappe.db.sql('''
-			UPDATE
-				`tab%s`
-			SET
-				discount_amount = if(rate_with_margin > 0, rate_with_margin, price_list_rate) * discount_percentage / 100
-			WHERE
-				discount_percentage > 0
-		''' % (doctype))
-	for doctype in buying_doctypes:
-		frappe.db.sql('''
-			UPDATE
-				`tab%s`
-			SET
-				discount_amount = price_list_rate * discount_percentage / 100
-			WHERE
-				discount_percentage > 0
-		''' % (doctype))
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py b/erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py
deleted file mode 100644
index 6825f19..0000000
--- a/erpnext/patches/v10_0/set_numeric_ranges_in_template_if_blank.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	item_numeric_attributes = frappe.db.sql("""
-		select name, numeric_values, from_range, to_range, increment
-		from `tabItem Attribute`
-		where numeric_values = 1
-	""", as_dict=1)
-
-	for d in item_numeric_attributes:
-		frappe.db.sql("""
-			update `tabItem Variant Attribute`
-			set
-				from_range = CASE
-					WHEN from_range = 0 THEN %(from_range)s
-					ELSE from_range
-					END,
-				to_range = CASE
-					WHEN to_range = 0 THEN %(to_range)s
-					ELSE to_range
-					END,
-				increment = CASE
-					WHEN increment = 0 THEN %(increment)s
-					ELSE increment
-					END,
-				numeric_values = %(numeric_values)s
-			where
-				attribute = %(name)s
-				and exists(select name from tabItem 
-					where name=`tabItem Variant Attribute`.parent and has_variants=1)
-		""", d)
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_primary_contact_for_customer.py b/erpnext/patches/v10_0/set_primary_contact_for_customer.py
deleted file mode 100644
index ae0b31c2..0000000
--- a/erpnext/patches/v10_0/set_primary_contact_for_customer.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():	
-	frappe.reload_doctype('Customer')
-
-	frappe.db.sql("""
-		update 
-			`tabCustomer`, (           
-				select `tabContact`.name, `tabContact`.mobile_no, `tabContact`.email_id, 
-					`tabDynamic Link`.link_name from `tabContact`, `tabDynamic Link`
-				where `tabContact`.name = `tabDynamic Link`.parent and 
-				`tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.is_primary_contact = 1
-			) as contact
-		set 
-			`tabCustomer`.customer_primary_contact = contact.name,
-			`tabCustomer`.mobile_no = contact.mobile_no, `tabCustomer`.email_id = contact.email_id 
-		where `tabCustomer`.name = contact.link_name""")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py b/erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py
deleted file mode 100644
index 083b7f4..0000000
--- a/erpnext/patches/v10_0/set_qty_in_transactions_based_on_serial_no_input.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "stock_settings")
-
-	ss = frappe.get_doc("Stock Settings")
-	ss.set_qty_in_transactions_based_on_serial_no_input = 1
-
-	if ss.default_warehouse \
-		and not frappe.db.exists("Warehouse", ss.default_warehouse):
-			ss.default_warehouse = None
-
-	if ss.stock_uom and not frappe.db.exists("UOM", ss.stock_uom):
-		ss.stock_uom = None
-
-	ss.flags.ignore_mandatory = True
-	ss.save()
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_student_party_type.py b/erpnext/patches/v10_0/set_student_party_type.py
deleted file mode 100644
index 08376ae..0000000
--- a/erpnext/patches/v10_0/set_student_party_type.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if not frappe.db.exists("Party Type", "Student"):
-		party = frappe.new_doc("Party Type")
-		party.party_type = "Student"
-		party.save()
diff --git a/erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py b/erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py
deleted file mode 100644
index a8d9049..0000000
--- a/erpnext/patches/v10_0/setup_vat_for_uae_and_saudi_arabia.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.setup.doctype.company.company import install_country_fixtures
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "account")
-	frappe.reload_doc("accounts", "doctype", "payment_schedule")
-	for d in frappe.get_all('Company',
-		filters={'country': ('in', ['Saudi Arabia', 'United Arab Emirates'])}):
-		install_country_fixtures(d.name)
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py b/erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py
deleted file mode 100644
index 7e2ff7a..0000000
--- a/erpnext/patches/v10_0/show_leaves_of_all_department_members_in_calendar.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("hr", "doctype", "hr_settings")
-	frappe.db.set_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar", 1)
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/taxes_issue_with_pos.py b/erpnext/patches/v10_0/taxes_issue_with_pos.py
deleted file mode 100644
index 2a3275a..0000000
--- a/erpnext/patches/v10_0/taxes_issue_with_pos.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for d in frappe.get_all('Sales Invoice', fields=["name"],
-		filters = {'is_pos':1, 'docstatus': 1, 'creation': ('>', '2018-04-23')}):
-		doc = frappe.get_doc('Sales Invoice', d.name)
-		if (not doc.taxes and doc.taxes_and_charges and doc.pos_profile and doc.outstanding_amount != 0 and
-			frappe.db.get_value('POS Profile', doc.pos_profile, 'taxes_and_charges', cache=True) == doc.taxes_and_charges):
-
-			doc.append_taxes_from_master()
-			doc.calculate_taxes_and_totals()
-			for d in doc.taxes:
-				d.db_update()
-
-			doc.db_update()
-
-			delete_gle_for_voucher(doc.name)
-			doc.make_gl_entries()
-
-def delete_gle_for_voucher(voucher_no):
-	frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""",
-		{'voucher_no': voucher_no})
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_address_template_for_india.py b/erpnext/patches/v10_0/update_address_template_for_india.py
deleted file mode 100644
index 1ddca93..0000000
--- a/erpnext/patches/v10_0/update_address_template_for_india.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.regional.address_template.setup import set_up_address_templates
-
-def execute():
-	if frappe.db.get_value('Company',  {'country': 'India'},  'name'):
-		address_template = frappe.db.get_value('Address Template', 'India', 'template')
-		if not address_template or "gstin" not in address_template:
-			set_up_address_templates(default_country='India')
diff --git a/erpnext/patches/v10_0/update_assessment_plan.py b/erpnext/patches/v10_0/update_assessment_plan.py
deleted file mode 100644
index 174623c..0000000
--- a/erpnext/patches/v10_0/update_assessment_plan.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('education', 'doctype', 'assessment_plan')
-
-	frappe.db.sql("""
-		UPDATE `tabAssessment Plan` as ap
-		INNER JOIN `tabStudent Group` as sg ON sg.name = ap.student_group
-		SET ap.academic_term = sg.academic_term,
-			ap.academic_year = sg.academic_year,
-			ap.program = sg.program
-		WHERE ap.docstatus = 1
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_assessment_result.py b/erpnext/patches/v10_0/update_assessment_result.py
deleted file mode 100644
index 96218db..0000000
--- a/erpnext/patches/v10_0/update_assessment_result.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('education', 'doctype', 'assessment_result')
-
-	frappe.db.sql("""
-		UPDATE `tabAssessment Result` AS ar
-		INNER JOIN `tabAssessment Plan` AS ap ON ap.name = ar.assessment_plan
-		SET ar.academic_term = ap.academic_term,
-			ar.academic_year = ap.academic_year,
-			ar.program = ap.program,
-			ar.course = ap.course,
-			ar.assessment_group = ap.assessment_group,
-			ar.student_group = ap.student_group
-		WHERE ap.docstatus = 1
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_asset_calculate_depreciation.py b/erpnext/patches/v10_0/update_asset_calculate_depreciation.py
deleted file mode 100644
index b947a40..0000000
--- a/erpnext/patches/v10_0/update_asset_calculate_depreciation.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('assets', 'doctype', 'asset')
-	frappe.reload_doc('assets', 'doctype', 'depreciation_schedule')
-
-	frappe.db.sql("""
-		update tabAsset a
-		set calculate_depreciation = 1
-		where exists(select ds.name from `tabDepreciation Schedule` ds where ds.parent=a.name)
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_hub_connector_domain.py b/erpnext/patches/v10_0/update_hub_connector_domain.py
deleted file mode 100644
index baf580a..0000000
--- a/erpnext/patches/v10_0/update_hub_connector_domain.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("Data Migration Connector"):
-		frappe.db.sql("""
-			UPDATE `tabData Migration Connector`
-			SET hostname = 'https://hubmarket.org'
-			WHERE connector_name = 'Hub Connector'
-		""")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_lft_rgt_for_employee.py b/erpnext/patches/v10_0/update_lft_rgt_for_employee.py
deleted file mode 100644
index 46ca786..0000000
--- a/erpnext/patches/v10_0/update_lft_rgt_for_employee.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils.nestedset import rebuild_tree
-
-def execute():
-    """ assign lft and rgt appropriately """
-    frappe.reload_doc("hr", "doctype", "employee")
-
-    rebuild_tree("Employee", "reports_to")
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_project_in_sle.py b/erpnext/patches/v10_0/update_project_in_sle.py
deleted file mode 100644
index 08c64f1..0000000
--- a/erpnext/patches/v10_0/update_project_in_sle.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ['Sales Invoice', 'Delivery Note', 'Stock Entry']:
-		frappe.db.sql(""" update
-				`tabStock Ledger Entry` sle, `tab{0}` parent_doc
-			set
-				sle.project = parent_doc.project
-			where
-				sle.voucher_no = parent_doc.name and sle.voucher_type = %s and sle.project is null
-				and parent_doc.project is not null and parent_doc.project != ''""".format(doctype), doctype)
diff --git a/erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py b/erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py
deleted file mode 100644
index 7b2c366..0000000
--- a/erpnext/patches/v10_0/update_reserved_qty_for_purchase_order.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.utils import get_bin
-
-def execute():
-	po_item = list(frappe.db.sql(("""
-		select distinct po.name as poname, poitem.rm_item_code as rm_item_code, po.company
-		from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitem
-		where po.name = poitem.parent
-			and po.is_subcontracted = "Yes"
-			and po.docstatus = 1"""), as_dict=1))
-	if not po_item:
-		return
-
-	frappe.reload_doc("stock", "doctype", "bin")
-	frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied")
-	company_warehouse = frappe._dict(frappe.db.sql("""select company, min(name) from `tabWarehouse`
-		where is_group = 0 group by company"""))
-
-	items = list(set([d.rm_item_code for d in po_item]))
-	item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse
-		from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
-
-	# Update reserved warehouse
-	for item in po_item:
-		reserve_warehouse = get_warehouse(item.rm_item_code, item.company, company_warehouse, item_wh)
-		frappe.db.sql("""update `tabPurchase Order Item Supplied`
-			set reserve_warehouse = %s
-			where parent = %s and rm_item_code = %s
-		""", (reserve_warehouse, item["poname"], item["rm_item_code"]))
-
-	# Update bin
-	item_wh_bin = frappe.db.sql(("""
-		select distinct poitemsup.rm_item_code as rm_item_code,
-			poitemsup.reserve_warehouse as reserve_warehouse
-		from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
-		where po.name = poitemsup.parent
-			and po.is_subcontracted = "Yes"
-			and po.docstatus = 1"""), as_dict=1)
-	for d in item_wh_bin:
-		try:
-			stock_bin = get_bin(d["rm_item_code"], d["reserve_warehouse"])
-			stock_bin.update_reserved_qty_for_sub_contracting()
-		except:
-			pass
-
-def get_warehouse(item_code, company, company_warehouse, item_wh):
-	reserve_warehouse = item_wh.get(item_code)
-	if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != company:
-		reserve_warehouse = None
-	if not reserve_warehouse:
-		reserve_warehouse = company_warehouse.get(company)
-	return reserve_warehouse
diff --git a/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py b/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py
deleted file mode 100644
index b4f5838..0000000
--- a/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("buying", "doctype", "supplier_quotation_item")
-
-	for doctype in ['Purchase Order','Supplier Quotation']:
-		frappe.db.sql("""
-			Update
-				`tab{doctype} Item`, `tabMaterial Request Item`
-			set
-				`tab{doctype} Item`.sales_order = `tabMaterial Request Item`.sales_order
-			where
-				`tab{doctype} Item`.material_request= `tabMaterial Request Item`.parent
-				and `tab{doctype} Item`.material_request_item = `tabMaterial Request Item`.name
-				and `tabMaterial Request Item`.sales_order is not null""".format(doctype=doctype))
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py b/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py
deleted file mode 100644
index fd3be08..0000000
--- a/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-
-
-	# update the sales order item in the material request
-	frappe.reload_doc('stock', 'doctype', 'material_request_item')
-	frappe.db.sql('''update `tabMaterial Request Item` mri, `tabSales Order Item` soi
-		set mri.sales_order_item = soi.name
-		where ifnull(mri.sales_order, "")!="" and soi.parent=mri.sales_order
-		and soi.item_code=mri.item_code and mri.docstatus=1
-	''')
-
-	# update the sales order item in the purchase order
-	frappe.db.sql('''update `tabPurchase Order Item` poi, `tabSales Order Item` soi
-		set poi.sales_order_item = soi.name
-		where ifnull(poi.sales_order, "")!="" and soi.parent=poi.sales_order
-		and soi.item_code=poi.item_code and poi.docstatus = 1
-	''')
-
-	# Update the status in material request and sales order
-	po_list = frappe.db.sql('''
-			select parent from `tabPurchase Order Item` where ifnull(material_request, "")!="" and
-			ifnull(sales_order, "")!="" and docstatus=1
-		''',as_dict=1)
-
-	for po in list(set([d.get("parent") for d in po_list if d.get("parent")])):
-		try:
-			po_doc = frappe.get_doc("Purchase Order", po)
-
-			# update the so in the status updater
-			po_doc.update_status_updater()
-			po_doc.update_qty(update_modified=False)
-
-		except Exception:
-			pass
diff --git a/erpnext/patches/v10_0/update_status_in_purchase_receipt.py b/erpnext/patches/v10_0/update_status_in_purchase_receipt.py
deleted file mode 100644
index a0bdd9e..0000000
--- a/erpnext/patches/v10_0/update_status_in_purchase_receipt.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "purchase_receipt")
-	frappe.db.sql('''
-		UPDATE `tabPurchase Receipt` SET status = "Completed" WHERE per_billed = 100 AND docstatus = 1
-	''')
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_territory_and_customer_group.py b/erpnext/patches/v10_0/update_territory_and_customer_group.py
deleted file mode 100644
index 7f3dae9..0000000
--- a/erpnext/patches/v10_0/update_territory_and_customer_group.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.rename_doc import get_fetch_fields
-
-def execute():
-	ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
-	customers = frappe.get_all('Customer', fields=["name", "customer_group"])
-	customer_group_fetch = get_fetch_fields('Customer', 'Customer Group', ignore_doctypes)
-
-	batch_size = 1000
-	for i in range(0, len(customers), batch_size):
-		batch_customers = customers[i:i + batch_size]
-		for d in customer_group_fetch:
-			when_then = []
-			for customer in batch_customers:
-				value = frappe.db.escape(frappe.as_unicode(customer.get("customer_group")))
-
-				when_then.append('''
-					WHEN `%s` = %s and %s != %s
-					THEN %s
-				'''%(d["master_fieldname"], frappe.db.escape(frappe.as_unicode(customer.name)),
-					d["linked_to_fieldname"], value, value))
-
-			frappe.db.sql("""
-				update
-					`tab%s`
-				set
-					%s = CASE %s  ELSE `%s` END
-			"""%(d['doctype'], d.linked_to_fieldname, " ".join(when_then), d.linked_to_fieldname))
diff --git a/erpnext/patches/v10_0/update_user_image_in_employee.py b/erpnext/patches/v10_0/update_user_image_in_employee.py
deleted file mode 100644
index 72d5d2a..0000000
--- a/erpnext/patches/v10_0/update_user_image_in_employee.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('hr', 'doctype', 'employee')
-
-	frappe.db.sql("""
-		UPDATE
-			`tabEmployee`, `tabUser`
-		SET
-			`tabEmployee`.image = `tabUser`.user_image
-		WHERE
-			`tabEmployee`.user_id = `tabUser`.name and
-			`tabEmployee`.user_id is not null and
-			`tabEmployee`.user_id != '' and `tabEmployee`.image is null
-	""")
diff --git a/erpnext/patches/v10_0/update_warehouse_address_details.py b/erpnext/patches/v10_0/update_warehouse_address_details.py
deleted file mode 100644
index b982b9a..0000000
--- a/erpnext/patches/v10_0/update_warehouse_address_details.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	warehouse = frappe.db.sql("""select name, email_id, phone_no, mobile_no, address_line_1,
-		address_line_2, city, state, pin from `tabWarehouse` where ifnull(address_line_1, '') != '' 
-		or ifnull(mobile_no, '') != '' 
-		or ifnull(email_id, '') != '' """, as_dict=1)
-
-	for d in warehouse:
-		try:
-			address = frappe.new_doc('Address')
-			address.name = d.name
-			address.address_title = d.name
-			address.address_line1 = d.address_line_1
-			address.city = d.city
-			address.state = d.state
-			address.pincode = d.pin
-			address.db_insert()
-			address.append('links',{'link_doctype':'Warehouse','link_name':d.name})
-			address.links[0].db_insert()
-			if d.name and (d.email_id or d.mobile_no or d.phone_no):
-				contact = frappe.new_doc('Contact')
-				contact.name = d.name
-				contact.first_name = d.name
-				contact.mobile_no = d.mobile_no
-				contact.email_id = d.email_id
-				contact.phone = d.phone_no
-				contact.db_insert()
-				contact.append('links',{'link_doctype':'Warehouse','link_name':d.name})
-				contact.links[0].db_insert()
-		except frappe.DuplicateEntryError:
-			pass
-	
\ No newline at end of file
diff --git a/erpnext/patches/v10_1/__init__.py b/erpnext/patches/v10_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v10_1/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v11_0/__init__.py b/erpnext/patches/v11_0/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v11_0/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v11_0/remove_subscriber_doctype.py b/erpnext/patches/v11_0/remove_subscriber_doctype.py
deleted file mode 100644
index 4839a20..0000000
--- a/erpnext/patches/v11_0/remove_subscriber_doctype.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	""" copy subscribe field to customer """
-	frappe.reload_doc("accounts","doctype","subscription")
-
-	if frappe.db.exists("DocType", "Subscriber"):
-		if frappe.db.has_column('Subscription','subscriber'):
-			frappe.db.sql("""
-				update `tabSubscription` s1
-				set customer=(select customer from tabSubscriber where name=s1.subscriber)
-			""")
-
-		frappe.delete_doc("DocType", "Subscriber")
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py
index b4a740f..882ec84 100644
--- a/erpnext/patches/v11_0/rename_bom_wo_fields.py
+++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py
@@ -6,6 +6,10 @@
 from frappe.model.utils.rename_field import rename_field
 
 def execute():
+    # updating column value to handle field change from Data to Currency
+    changed_field = "base_scrap_material_cost"
+    frappe.db.sql(f"update `tabBOM` set {changed_field} = '0' where trim(coalesce({changed_field}, ''))= ''")
+
     for doctype in ['BOM Explosion Item', 'BOM Item', 'Work Order Item', 'Item']:
         if frappe.db.has_column(doctype, 'allow_transfer_for_manufacture'):
             if doctype != 'Item':
diff --git a/erpnext/patches/v11_1/__init__.py b/erpnext/patches/v11_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v11_1/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v12_0/__init__.py b/erpnext/patches/v12_0/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v12_0/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v13_0/__init__.py b/erpnext/patches/v13_0/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v13_0/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py b/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py
new file mode 100644
index 0000000..be85cfd
--- /dev/null
+++ b/erpnext/patches/v13_0/bill_for_rejected_quantity_in_purchase_invoice.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doctype("Buying Settings")
+	buying_settings = frappe.get_single("Buying Settings")
+	buying_settings.bill_for_rejected_quantity_in_purchase_invoice = 0
+	buying_settings.save()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
index 9af0a8d..2549a1e 100644
--- a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
+++ b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
@@ -2,6 +2,7 @@
 import frappe
 from frappe.model.utils.rename_field import rename_field
 
+
 def execute():
 	if frappe.db.exists('DocType', 'Lab Test') and frappe.db.exists('DocType', 'Lab Test Template'):
 		# rename child doctypes
@@ -17,7 +18,12 @@
 		frappe.reload_doc('healthcare', 'doctype', 'lab_test_template')
 
 		for old_dt, new_dt in doctypes.items():
-			if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt):
+			frappe.flags.link_fields = {}
+			should_rename = (
+				frappe.db.table_exists(old_dt)
+				and not frappe.db.table_exists(new_dt)
+			)
+			if should_rename:
 				frappe.reload_doc('healthcare', 'doctype', frappe.scrub(old_dt))
 				frappe.rename_doc('DocType', old_dt, new_dt, force=True)
 				frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt))
diff --git a/erpnext/patches/v13_0/update_job_card_details.py b/erpnext/patches/v13_0/update_job_card_details.py
new file mode 100644
index 0000000..d4e65c6
--- /dev/null
+++ b/erpnext/patches/v13_0/update_job_card_details.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc("manufacturing", "doctype", "job_card")
+	frappe.reload_doc("manufacturing", "doctype", "job_card_item")
+	frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
+
+	frappe.db.sql(""" update `tabJob Card` jc, `tabWork Order Operation` wo
+		SET	jc.hour_rate =  wo.hour_rate
+		WHERE
+			jc.operation_id = wo.name and jc.docstatus < 2 and wo.hour_rate > 0
+	""")
\ No newline at end of file
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/patches/v4_0/__init__.py b/erpnext/patches/v4_0/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v4_0/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v4_0/apply_user_permissions.py b/erpnext/patches/v4_0/apply_user_permissions.py
deleted file mode 100644
index 3c5d612..0000000
--- a/erpnext/patches/v4_0/apply_user_permissions.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.hr.doctype.employee.employee import EmployeeUserDisabledError
-
-def execute():
-	update_hr_permissions()
-	update_permissions()
-	remove_duplicate_user_permissions()
-	frappe.clear_cache()
-
-def update_hr_permissions():
-	# add set user permissions rights to HR Manager
-	frappe.db.sql("""update `tabDocPerm` set `set_user_permissions`=1 where parent in ('Employee', 'Leave Application')
-		and role='HR Manager' and permlevel=0 and `read`=1""")
-	docperm_meta = frappe.get_meta('DocPerm')
-	if docperm_meta.get_field('apply_user_permissions'):
-		# apply user permissions on Employee and Leave Application
-		frappe.db.sql("""update `tabDocPerm` set `apply_user_permissions`=1 where parent in ('Employee', 'Leave Application')
-			and role in ('Employee', 'Leave Approver') and permlevel=0 and `read`=1""")
-
-	frappe.clear_cache()
-
-	# save employees to run on_update events
-	for employee in frappe.db.sql_list("""select name from `tabEmployee` where docstatus < 2"""):
-		try:
-			emp = frappe.get_doc("Employee", employee)
-			emp.flags.ignore_mandatory = True
-			emp.save()
-		except EmployeeUserDisabledError:
-			pass
-
-def update_permissions():
-	# clear match conditions other than owner
-	frappe.db.sql("""update tabDocPerm set `match`=''
-		where ifnull(`match`,'') not in ('', 'owner')""")
-
-def remove_duplicate_user_permissions():
-	# remove duplicate user_permissions (if they exist)
-	for d in frappe.db.sql("""select parent, defkey, defvalue,
-		count(*) as cnt from tabDefaultValue
-		where parent not in ('__global', '__default')
-		group by parent, defkey, defvalue""", as_dict=1):
-		if d.cnt > 1:
-			# order by parenttype so that user permission does not get removed!
-			frappe.db.sql("""delete from tabDefaultValue where `parent`=%s and `defkey`=%s and
-				`defvalue`=%s order by parenttype limit %s""", (d.parent, d.defkey, d.defvalue, d.cnt-1))
-
diff --git a/erpnext/patches/v4_0/countrywise_coa.py b/erpnext/patches/v4_0/countrywise_coa.py
deleted file mode 100644
index f45e602..0000000
--- a/erpnext/patches/v4_0/countrywise_coa.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("setup", 'doctype', "company")
-	frappe.reload_doc("accounts", 'doctype', "account")
-
-	frappe.db.sql("""update tabAccount set account_type='Cash'
-		where account_type='Bank or Cash' and account_name in ('Cash', 'Cash In Hand')""")
-
-	frappe.db.sql("""update tabAccount set account_type='Stock'
-		where account_name = 'Stock Assets'""")
-
-	ac_types = {"Fixed Asset Account": "Fixed Asset", "Bank or Cash": "Bank"}
-	for old, new in ac_types.items():
-		frappe.db.sql("""update tabAccount set account_type=%s
-			where account_type=%s""", (new, old))
-
-	try:
-		frappe.db.sql("""update `tabAccount` set report_type =
-			if(is_pl_account='Yes', 'Profit and Loss', 'Balance Sheet')""")
-
-		frappe.db.sql("""update `tabAccount` set balance_must_be=debit_or_credit
-			where ifnull(allow_negative_balance, 0) = 0""")
-	except:
-		pass
diff --git a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py b/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py
deleted file mode 100644
index fe50e44..0000000
--- a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field_if_values_exist
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "purchase_receipt")
-	frappe.reload_doc("hr", "doctype", "employee")
-	frappe.reload_doc("Payroll", "doctype", "salary_slip")
-
-	india_specific_fields = {
-		"Purchase Receipt": [{
-			"label": "Supplier Shipment No",
-			"fieldname": "challan_no",
-			"fieldtype": "Data",
-			"insert_after": "is_subcontracted"
-		}, {
-			"label": "Supplier Shipment Date",
-			"fieldname": "challan_date",
-			"fieldtype": "Date",
-			"insert_after": "is_subcontracted"
-		}],
-		"Employee": [{
-			"label": "PAN Number",
-			"fieldname": "pan_number",
-			"fieldtype": "Data",
-			"insert_after": "company_email"
-		}, {
-			"label": "Gratuity LIC Id",
-			"fieldname": "gratuity_lic_id",
-			"fieldtype": "Data",
-			"insert_after": "company_email"
-		}, {
-			"label": "Esic Card No",
-			"fieldname": "esic_card_no",
-			"fieldtype": "Data",
-			"insert_after": "bank_ac_no"
-		}, {
-			"label": "PF Number",
-			"fieldname": "pf_number",
-			"fieldtype": "Data",
-			"insert_after": "bank_ac_no"
-		}],
-		"Salary Slip": [{
-			"label": "Esic No",
-			"fieldname": "esic_no",
-			"fieldtype": "Data",
-			"insert_after": "letter_head",
-			"permlevel": 1
-		}, {
-			"label": "PF Number",
-			"fieldname": "pf_no",
-			"fieldtype": "Data",
-			"insert_after": "letter_head",
-			"permlevel": 1
-		}]
-	}
-
-	for dt, docfields in india_specific_fields.items():
-		for df in docfields:
-			create_custom_field_if_values_exist(dt, df)
diff --git a/erpnext/patches/v4_0/create_price_list_if_missing.py b/erpnext/patches/v4_0/create_price_list_if_missing.py
deleted file mode 100644
index 039e521..0000000
--- a/erpnext/patches/v4_0/create_price_list_if_missing.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.utils.nestedset import get_root_of
-
-def execute():
-	# setup not complete
-	if not frappe.db.sql("""select name from tabCompany limit 1"""):
-		return
-
-	if "shopping_cart" in frappe.get_installed_apps():
-		frappe.reload_doc("shopping_cart", "doctype", "shopping_cart_settings")
-
-	if not frappe.db.sql("select name from `tabPrice List` where buying=1"):
-		create_price_list(_("Standard Buying"), buying=1)
-
-	if not frappe.db.sql("select name from `tabPrice List` where selling=1"):
-		create_price_list(_("Standard Selling"), selling=1)
-
-def create_price_list(pl_name, buying=0, selling=0):
-	price_list = frappe.get_doc({
-		"doctype": "Price List",
-		"price_list_name": pl_name,
-		"enabled": 1,
-		"buying": buying,
-		"selling": selling,
-		"currency": frappe.db.get_default("currency"),
-		"territories": [{
-			"territory": get_root_of("Territory")
-		}]
-	})
-	price_list.insert()
diff --git a/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py b/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py
deleted file mode 100644
index 1b260c4..0000000
--- a/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils.nestedset import get_root_of
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "pricing_rule")
-
-	frappe.db.auto_commit_on_many_writes = True
-
-	default_item_group = get_root_of("Item Group")
-
-	for d in frappe.db.sql("""select * from `tabCustomer Discount`
-		where ifnull(parent, '') != ''""", as_dict=1):
-			if not d.discount:
-				continue
-
-			frappe.get_doc({
-				"doctype": "Pricing Rule",
-				"apply_on": "Item Group",
-				"item_group": d.item_group or default_item_group,
-				"applicable_for": "Customer",
-				"customer": d.parent,
-				"price_or_discount": "Discount Percentage",
-				"discount_percentage": d.discount,
-				"selling": 1
-			}).insert()
-
-	frappe.db.auto_commit_on_many_writes = False
-
-	frappe.delete_doc("DocType", "Customer Discount")
diff --git a/erpnext/patches/v4_0/fields_to_be_renamed.py b/erpnext/patches/v4_0/fields_to_be_renamed.py
deleted file mode 100644
index cc17697..0000000
--- a/erpnext/patches/v4_0/fields_to_be_renamed.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
-
-rename_map = {
-	"Quotation Item": [
-		["ref_rate", "price_list_rate"],
-		["base_ref_rate", "base_price_list_rate"],
-		["adj_rate", "discount_percentage"],
-		["export_rate", "rate"],
-		["basic_rate", "base_rate"],
-		["amount", "base_amount"],
-		["export_amount", "amount"]
-	],
-
-	"Sales Order Item": [
-		["ref_rate", "price_list_rate"],
-		["base_ref_rate", "base_price_list_rate"],
-		["adj_rate", "discount_percentage"],
-		["export_rate", "rate"],
-		["basic_rate", "base_rate"],
-		["amount", "base_amount"],
-		["export_amount", "amount"],
-		["reserved_warehouse", "warehouse"]
-	],
-
-	"Delivery Note Item": [
-		["ref_rate", "price_list_rate"],
-		["base_ref_rate", "base_price_list_rate"],
-		["adj_rate", "discount_percentage"],
-		["export_rate", "rate"],
-		["basic_rate", "base_rate"],
-		["amount", "base_amount"],
-		["export_amount", "amount"]
-	],
-
-	"Sales Invoice Item": [
-		["ref_rate", "price_list_rate"],
-		["base_ref_rate", "base_price_list_rate"],
-		["adj_rate", "discount_percentage"],
-		["export_rate", "rate"],
-		["basic_rate", "base_rate"],
-		["amount", "base_amount"],
-		["export_amount", "amount"]
-	],
-
-	"Supplier Quotation Item": [
-		["import_ref_rate", "price_list_rate"],
-		["purchase_ref_rate", "base_price_list_rate"],
-		["discount_rate", "discount_percentage"],
-		["import_rate", "rate"],
-		["purchase_rate", "base_rate"],
-		["amount", "base_amount"],
-		["import_amount", "amount"]
-	],
-
-	"Purchase Order Item": [
-		["import_ref_rate", "price_list_rate"],
-		["purchase_ref_rate", "base_price_list_rate"],
-		["discount_rate", "discount_percentage"],
-		["import_rate", "rate"],
-		["purchase_rate", "base_rate"],
-		["amount", "base_amount"],
-		["import_amount", "amount"]
-	],
-
-	"Purchase Receipt Item": [
-		["import_ref_rate", "price_list_rate"],
-		["purchase_ref_rate", "base_price_list_rate"],
-		["discount_rate", "discount_percentage"],
-		["import_rate", "rate"],
-		["purchase_rate", "base_rate"],
-		["amount", "base_amount"],
-		["import_amount", "amount"]
-	],
-
-	"Purchase Invoice Item": [
-		["import_ref_rate", "price_list_rate"],
-		["purchase_ref_rate", "base_price_list_rate"],
-		["discount_rate", "discount_percentage"],
-		["import_rate", "rate"],
-		["rate", "base_rate"],
-		["amount", "base_amount"],
-		["import_amount", "amount"],
-		["expense_head", "expense_account"]
-	],
-
-	"Item": [
-		["purchase_account", "expense_account"],
-		["default_sales_cost_center", "selling_cost_center"],
-		["cost_center", "buying_cost_center"],
-		["default_income_account", "income_account"],
-	],
-	"Item Price": [
-		["ref_rate", "price_list_rate"]
-	]
-}
-
-def execute():
-	for dn in rename_map:
-		frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn))
-
-	for dt, field_list in rename_map.items():
-		for field in field_list:
-			rename_field(dt, field[0], field[1])
diff --git a/erpnext/patches/v4_0/fix_address_template.py b/erpnext/patches/v4_0/fix_address_template.py
deleted file mode 100644
index 905e3db..0000000
--- a/erpnext/patches/v4_0/fix_address_template.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	missing_line = """{{ address_line1 }}<br>"""
-	for name, template in frappe.db.sql("select name, template from `tabAddress Template`"):
-		if missing_line not in template:
-			d = frappe.get_doc("Address Template", name)
-			d.template = missing_line + d.template
-			d.save()
diff --git a/erpnext/patches/v4_0/fix_case_of_hr_module_def.py b/erpnext/patches/v4_0/fix_case_of_hr_module_def.py
deleted file mode 100644
index e77b427..0000000
--- a/erpnext/patches/v4_0/fix_case_of_hr_module_def.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	hr = frappe.db.get_value("Module Def", "HR")
-	if hr == "Hr":
-		frappe.rename_doc("Module Def", "Hr", "HR")
-		frappe.db.set_value("Module Def", "HR", "module_name", "HR")
-
-	frappe.clear_cache()
-	frappe.setup_module_map()
diff --git a/erpnext/patches/v4_0/fix_contact_address.py b/erpnext/patches/v4_0/fix_contact_address.py
deleted file mode 100644
index 6a3e106..0000000
--- a/erpnext/patches/v4_0/fix_contact_address.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("website", "doctype", "contact_us_settings")
-	address = frappe.db.get_value("Contact Us Settings", None, "address")
-	if address:
-		address = frappe.get_doc("Address", address)
-		contact = frappe.get_doc("Contact Us Settings", "Contact Us Settings")
-		for f in ("address_title", "address_line1", "address_line2", "city", "state", "country", "pincode"):
-			contact.set(f, address.get(f))
-		
-		contact.save()
\ No newline at end of file
diff --git a/erpnext/patches/v4_0/fix_employee_user_id.py b/erpnext/patches/v4_0/fix_employee_user_id.py
deleted file mode 100644
index 6f449f9..0000000
--- a/erpnext/patches/v4_0/fix_employee_user_id.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-from frappe.utils import get_fullname
-
-def execute():
-	for user_id in frappe.db.sql_list("""select distinct user_id from `tabEmployee`
-		where ifnull(user_id, '')!=''
-		group by user_id having count(name) > 1"""):
-
-		fullname = get_fullname(user_id)
-		employee = frappe.db.get_value("Employee", {"employee_name": fullname, "user_id": user_id})
-
-		if employee:
-			frappe.db.sql("""update `tabEmployee` set user_id=null
-				where user_id=%s and name!=%s""", (user_id, employee))
-		else:
-			count = frappe.db.sql("""select count(*) from `tabEmployee` where user_id=%s""", user_id)[0][0]
-			frappe.db.sql("""update `tabEmployee` set user_id=null
-				where user_id=%s limit %s""", (user_id, count - 1))
diff --git a/erpnext/patches/v4_0/global_defaults_to_system_settings.py b/erpnext/patches/v4_0/global_defaults_to_system_settings.py
deleted file mode 100644
index 2905fe1..0000000
--- a/erpnext/patches/v4_0/global_defaults_to_system_settings.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-from collections import Counter
-from frappe.core.doctype.user.user import STANDARD_USERS
-
-def execute():
-	frappe.reload_doc("core", "doctype", "system_settings")
-	system_settings = frappe.get_doc("System Settings")
-
-	# set values from global_defauls
-	global_defaults = frappe.db.get_value("Global Defaults", None,
-		["time_zone", "date_format", "number_format", "float_precision", "session_expiry"], as_dict=True)
-
-	if global_defaults:
-		for key, val in global_defaults.items():
-			if not system_settings.get(key):
-				system_settings.set(key, val)
-
-	# language
-	if not system_settings.get("language"):
-		# find most common language
-		lang = frappe.db.sql_list("""select language from `tabUser`
-			where ifnull(language, '')!='' and language not like "Loading%%" and name not in ({standard_users})""".format(
-			standard_users=", ".join(["%s"]*len(STANDARD_USERS))), tuple(STANDARD_USERS))
-		lang = Counter(lang).most_common(1)
-		lang = (len(lang) > 0) and lang[0][0] or "english"
-
-		system_settings.language = lang
-
-	system_settings.flags.ignore_mandatory = True
-	system_settings.save()
-
-	global_defaults = frappe.get_doc("Global Defaults")
-	global_defaults.flags.ignore_mandatory = True
-	global_defaults.save()
diff --git a/erpnext/patches/v4_0/import_country_codes.py b/erpnext/patches/v4_0/import_country_codes.py
deleted file mode 100644
index 43e23d5..0000000
--- a/erpnext/patches/v4_0/import_country_codes.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.geo.country_info import get_all
-from frappe.utils.install import import_country_and_currency
-
-from six import iteritems
-
-def execute():
-	frappe.reload_doc("setup", "doctype", "country")
-	import_country_and_currency()
-	for name, country in iteritems(get_all()):
-		frappe.set_value("Country", name, "code", country.get("code"))
\ No newline at end of file
diff --git a/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py b/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py
deleted file mode 100644
index 97e217a..0000000
--- a/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# update sales cycle
-	for d in ['Sales Invoice', 'Sales Order', 'Quotation', 'Delivery Note']:
-		frappe.db.sql("""update `tab%s` set taxes_and_charges=charge""" % d)
-
-	# update purchase cycle
-	for d in ['Purchase Invoice', 'Purchase Order', 'Supplier Quotation', 'Purchase Receipt']:
-		frappe.db.sql("""update `tab%s` set taxes_and_charges=purchase_other_charges""" % d)
-	
-	frappe.db.sql("""update `tabPurchase Taxes and Charges` set parentfield='other_charges'""")
diff --git a/erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py b/erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py
deleted file mode 100644
index 8b81936..0000000
--- a/erpnext/patches/v4_0/move_warehouse_user_to_restrictions.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import frappe.permissions
-
-def execute():
-	for warehouse, user in frappe.db.sql("""select parent, user from `tabWarehouse User`"""):
-		frappe.permissions.add_user_permission("Warehouse", warehouse, user)
-
-	frappe.delete_doc_if_exists("DocType", "Warehouse User")
-	frappe.reload_doc("stock", "doctype", "warehouse")
diff --git a/erpnext/patches/v4_0/new_address_template.py b/erpnext/patches/v4_0/new_address_template.py
deleted file mode 100644
index fa66027..0000000
--- a/erpnext/patches/v4_0/new_address_template.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("utilities", "doctype", "address_template")
-	if not frappe.db.sql("select name from `tabAddress Template`"):
-		try:
-			d = frappe.new_doc("Address Template")
-			d.update({"country":frappe.db.get_default("country") or
-				frappe.db.get_value("Global Defaults", "Global Defaults", "country")})
-			d.insert()
-		except:
-			print(frappe.get_traceback())
-
diff --git a/erpnext/patches/v4_0/reload_sales_print_format.py b/erpnext/patches/v4_0/reload_sales_print_format.py
deleted file mode 100644
index b8f090f..0000000
--- a/erpnext/patches/v4_0/reload_sales_print_format.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'Print Format', 'POS Invoice')
diff --git a/erpnext/patches/v4_0/remove_employee_role_if_no_employee.py b/erpnext/patches/v4_0/remove_employee_role_if_no_employee.py
deleted file mode 100644
index 8766ace..0000000
--- a/erpnext/patches/v4_0/remove_employee_role_if_no_employee.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import frappe.permissions
-
-def execute():
-	for user in frappe.db.sql_list("select distinct parent from `tabHas Role` where role='Employee'"):
-		# if employee record does not exists, remove employee role!
-		if not frappe.db.get_value("Employee", {"user_id": user}):
-			try:
-				user = frappe.get_doc("User", user)
-				for role in user.get("roles", {"role": "Employee"}):
-					user.get("roles").remove(role)
-				user.save()
-			except frappe.DoesNotExistError:
-				pass
diff --git a/erpnext/patches/v4_0/remove_module_home_pages.py b/erpnext/patches/v4_0/remove_module_home_pages.py
deleted file mode 100644
index a2720e0..0000000
--- a/erpnext/patches/v4_0/remove_module_home_pages.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for page in ("accounts-home", "website-home", "support-home", "stock-home", "selling-home", "projects-home",
-		"manufacturing-home", "hr-home", "buying-home"):
-		frappe.delete_doc("Page", page)
\ No newline at end of file
diff --git a/erpnext/patches/v4_0/rename_sitemap_to_route.py b/erpnext/patches/v4_0/rename_sitemap_to_route.py
deleted file mode 100644
index ffb1fda..0000000
--- a/erpnext/patches/v4_0/rename_sitemap_to_route.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-import frappe.model
-
-def execute():
-	frappe.reload_doc("setup", "doctype", "item_group")
-	frappe.reload_doc("stock", "doctype", "item")
-	frappe.reload_doc("setup", "doctype", "sales_partner")
-	
-	try:
-		frappe.model.rename_field("Item Group", "parent_website_sitemap", "parent_website_route")
-		frappe.model.rename_field("Item", "parent_website_sitemap", "parent_website_route")
-		frappe.model.rename_field("Sales Partner", "parent_website_sitemap",
-			 "parent_website_route")
-	except Exception as e:
-		if e.args[0]!=1054:
-			raise
diff --git a/erpnext/patches/v4_0/reset_permissions_for_masters.py b/erpnext/patches/v4_0/reset_permissions_for_masters.py
deleted file mode 100644
index bc1b438..0000000
--- a/erpnext/patches/v4_0/reset_permissions_for_masters.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-from frappe.permissions import reset_perms
-
-def execute():
-	for doctype in ("About Us Settings", "Accounts Settings", "Activity Type",
-		"Blog Category", "Blog Settings", "Blogger", "Branch", "Brand", "Buying Settings",
-		"Communication", "Company", "Contact Us Settings",
-		"Country", "Currency", "Currency Exchange", "Deduction Type", "Department",
-		"Designation", "Earning Type", "Event", "Feed", "File", "Fiscal Year",
-		"HR Settings", "Industry Type", "Leave Type", "Letter Head",
-		"Mode of Payment", "Module Def", "Naming Series", "POS Setting", "Print Heading",
-		"Report", "Role", "Selling Settings", "Stock Settings", "Supplier Type", "UOM"):
-		try:
-			reset_perms(doctype)
-		except:
-			print("Error resetting perms for", doctype)
-			raise
diff --git a/erpnext/patches/v4_0/save_default_letterhead.py b/erpnext/patches/v4_0/save_default_letterhead.py
deleted file mode 100644
index 5d75f09..0000000
--- a/erpnext/patches/v4_0/save_default_letterhead.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	"""save default letterhead to set default_letter_head_content"""
-	try:
-		letter_head = frappe.get_doc("Letter Head", {"is_default": 1})
-		letter_head.save()
-	except frappe.DoesNotExistError:
-		pass
diff --git a/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py b/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py
deleted file mode 100644
index 7e472e2..0000000
--- a/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "pricing_rule")
-	frappe.db.sql("""update `tabPricing Rule` set selling=1 where ifnull(applicable_for, '') in
-		('', 'Customer', 'Customer Group', 'Territory', 'Sales Partner', 'Campaign')""")
-
-	frappe.db.sql("""update `tabPricing Rule` set buying=1 where ifnull(applicable_for, '') in
-		('', 'Supplier', 'Supplier Type')""")
diff --git a/erpnext/patches/v4_0/split_email_settings.py b/erpnext/patches/v4_0/split_email_settings.py
deleted file mode 100644
index 5d1dea6..0000000
--- a/erpnext/patches/v4_0/split_email_settings.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	print("WARNING!!!! Email Settings not migrated. Please setup your email again.")
-
-	# this will happen if you are migrating very old accounts
-	# comment out this line below and remember to create new Email Accounts
-	# for incoming and outgoing mails
-	raise Exception
-
-	return
-
-
-	frappe.reload_doc("core", "doctype", "outgoing_email_settings")
-	frappe.reload_doc("support", "doctype", "support_email_settings")
-
-	email_settings = get_email_settings()
-	map_outgoing_email_settings(email_settings)
-	map_support_email_settings(email_settings)
-
-
-def map_outgoing_email_settings(email_settings):
-	outgoing_email_settings = frappe.get_doc("Outgoing Email Settings")
-	for fieldname in (("outgoing_mail_server", "mail_server"),
-		"use_ssl", "mail_port", "mail_login", "mail_password",
-		"always_use_login_id_as_sender", "auto_email_id"):
-
-		if isinstance(fieldname, tuple):
-			from_fieldname, to_fieldname = fieldname
-		else:
-			from_fieldname = to_fieldname = fieldname
-
-		outgoing_email_settings.set(to_fieldname, email_settings.get(from_fieldname))
-
-	outgoing_email_settings._fix_numeric_types()
-	outgoing_email_settings.save()
-
-def map_support_email_settings(email_settings):
-	support_email_settings = frappe.get_doc("Support Email Settings")
-
-	for fieldname in ("sync_support_mails", "support_email",
-		("support_host", "mail_server"),
-		("support_use_ssl", "use_ssl"),
-		("support_username", "mail_login"),
-		("support_password", "mail_password"),
-		"support_signature", "send_autoreply", "support_autoreply"):
-
-		if isinstance(fieldname, tuple):
-			from_fieldname, to_fieldname = fieldname
-		else:
-			from_fieldname = to_fieldname = fieldname
-
-		support_email_settings.set(to_fieldname, email_settings.get(from_fieldname))
-
-	support_email_settings._fix_numeric_types()
-	support_email_settings.save()
-
-def get_email_settings():
-	ret = {}
-	for field, value in frappe.db.sql("select field, value from tabSingles where doctype='Email Settings'"):
-		ret[field] = value
-	return ret
-
diff --git a/erpnext/patches/v4_0/update_account_root_type.py b/erpnext/patches/v4_0/update_account_root_type.py
deleted file mode 100644
index 15ddf03..0000000
--- a/erpnext/patches/v4_0/update_account_root_type.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "account")
-
-	account_table_columns = frappe.db.get_table_columns("Account")
-	if "debit_or_credit" in account_table_columns and "is_pl_account" in account_table_columns:
-		frappe.db.sql("""UPDATE tabAccount
-			SET root_type = CASE
-				WHEN (debit_or_credit='Debit' and is_pl_account = 'No') THEN 'Asset'
-				WHEN (debit_or_credit='Credit' and is_pl_account = 'No') THEN 'Liability'
-				WHEN (debit_or_credit='Debit' and is_pl_account = 'Yes') THEN 'Expense'
-				WHEN (debit_or_credit='Credit' and is_pl_account = 'Yes') THEN 'Income'
-				END
-			WHERE ifnull(parent_account, '') = ''
-		""")
-
-	else:
-		for key, root_type in (("asset", "Asset"), ("liabilities", "Liability"), ("expense", "Expense"),
-			("income", "Income")):
-			frappe.db.sql("""update tabAccount set root_type=%s where name like %s
-				and ifnull(parent_account, '')=''""", (root_type, "%" + key + "%"))
-
-	for root in frappe.db.sql("""SELECT name, lft, rgt, root_type FROM `tabAccount`
-		WHERE ifnull(parent_account, '')=''""",	as_dict=True):
-			if root.root_type:
-				frappe.db.sql("""UPDATE tabAccount SET root_type=%s WHERE lft>%s and rgt<%s""",
-					(root.root_type, root.lft, root.rgt))
-			else:
-				print(b"Root type not found for {0}".format(root.name.encode("utf-8")))
diff --git a/erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py b/erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py
deleted file mode 100644
index d784a1b..0000000
--- a/erpnext/patches/v4_0/update_custom_print_formats_for_renamed_fields.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import re
-
-def execute():
-	# NOTE: sequence is important
-	fields_list = (
-		("amount", "base_amount"),
-		("ref_rate", "price_list_rate"),
-		("base_ref_rate", "base_price_list_rate"),
-		("adj_rate", "discount_percentage"),
-		("export_rate", "rate"),
-		("basic_rate", "base_rate"),
-		("export_amount", "amount"),
-		("reserved_warehouse", "warehouse"),
-		("import_ref_rate", "price_list_rate"),
-		("purchase_ref_rate", "base_price_list_rate"),
-		("discount_rate", "discount_percentage"),
-		("import_rate", "rate"),
-		("purchase_rate", "base_rate"),
-		("import_amount", "amount")
-	)
-
-	condition = " or ".join("""html like "%%{}%%" """.format(d[0].replace("_", "\\_")) for d in fields_list
-		if d[0] != "amount")
-
-	for name, html in frappe.db.sql("""select name, html from `tabPrint Format`
-		where standard = 'No' and ({}) and html not like '%%frappe.%%'""".format(condition)):
-			html = html.replace("wn.", "frappe.")
-			for from_field, to_field in fields_list:
-				html = re.sub(r"\b{}\b".format(from_field), to_field, html)
-
-			frappe.db.set_value("Print Format", name, "html", html)
diff --git a/erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py b/erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py
deleted file mode 100644
index fe66a5e..0000000
--- a/erpnext/patches/v4_0/update_incharge_name_to_sales_person_in_maintenance_schedule.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("support", "doctype", "schedules")
-	frappe.reload_doc("support", "doctype", "maintenance_schedule_item")
-	
-	frappe.db.sql("""update `tabMaintenance Schedule Detail` set sales_person=incharge_name""")
-	frappe.db.sql("""update `tabMaintenance Schedule Item` set sales_person=incharge_name""")
\ No newline at end of file
diff --git a/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py b/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py
deleted file mode 100644
index 2e2e77a..0000000
--- a/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import re
-
-def execute():
-	for name, html in frappe.db.sql("""select name, html from `tabPrint Format`
-		where standard = 'No' and html like '%%purchase\\_tax\\_details%%'"""):
-			html = re.sub(r"\bpurchase_tax_details\b", "taxes", html)
-			frappe.db.set_value("Print Format", name, "html", html)
diff --git a/erpnext/patches/v4_0/update_tax_amount_after_discount.py b/erpnext/patches/v4_0/update_tax_amount_after_discount.py
deleted file mode 100644
index d10329e..0000000
--- a/erpnext/patches/v4_0/update_tax_amount_after_discount.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges")
-	docs_with_discount_amount = frappe._dict()
-	for dt in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
-		records = frappe.db.sql_list("""select name from `tab%s`
-			where ifnull(discount_amount, 0) > 0 and docstatus=1""" % dt)
-		docs_with_discount_amount[dt] = records
-
-	for dt, discounted_records in docs_with_discount_amount.items():
-		frappe.db.sql("""update `tabSales Taxes and Charges`
-			set tax_amount_after_discount_amount = tax_amount
-			where parenttype = %s and parent not in (%s)""" %
-			('%s', ', '.join(['%s']*(len(discounted_records)+1))),
-			tuple([dt, ''] + discounted_records))
diff --git a/erpnext/patches/v4_0/update_user_properties.py b/erpnext/patches/v4_0/update_user_properties.py
deleted file mode 100644
index f2085ce..0000000
--- a/erpnext/patches/v4_0/update_user_properties.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import frappe.permissions
-import frappe.defaults
-
-def execute():
-	frappe.reload_doc("core", "doctype", "docfield")
-	frappe.reload_doc("hr", "doctype", "employee")
-
-	set_print_email_permissions()
-	migrate_user_properties_to_user_permissions()
-
-	frappe.clear_cache()
-
-def migrate_user_properties_to_user_permissions():
-	for d in frappe.db.sql("""select parent, defkey, defvalue from tabDefaultValue
-		where parent not in ('__global', '__default')""", as_dict=True):
-		df = frappe.db.sql("""select options from tabDocField
-			where fieldname=%s and fieldtype='Link'""", d.defkey, as_dict=True)
-
-		if df:
-			frappe.db.sql("""update tabDefaultValue
-				set defkey=%s, parenttype='User Permission'
-				where defkey=%s and
-				parent not in ('__global', '__default')""", (df[0].options, d.defkey))
-
-def set_print_email_permissions():
-	# reset Page perms
-	from frappe.core.page.permission_manager.permission_manager import reset
-	reset("Page")
-	reset("Report")
-
-	if "allow_print" not in frappe.db.get_table_columns("DocType"):
-		return
-
-	# patch to move print, email into DocPerm
-	# NOTE: allow_print and allow_email are misnamed. They were used to hide print / hide email
-	for doctype, hide_print, hide_email in frappe.db.sql("""select name, ifnull(allow_print, 0), ifnull(allow_email, 0)
-		from `tabDocType` where ifnull(issingle, 0)=0 and ifnull(istable, 0)=0 and
-		(ifnull(allow_print, 0)=0 or ifnull(allow_email, 0)=0)"""):
-
-		if not hide_print:
-			frappe.db.sql("""update `tabDocPerm` set `print`=1
-				where permlevel=0 and `read`=1 and parent=%s""", doctype)
-
-		if not hide_email:
-			frappe.db.sql("""update `tabDocPerm` set `email`=1
-				where permlevel=0 and `read`=1 and parent=%s""", doctype)
diff --git a/erpnext/patches/v4_0/update_users_report_view_settings.py b/erpnext/patches/v4_0/update_users_report_view_settings.py
deleted file mode 100644
index 6f216f5..0000000
--- a/erpnext/patches/v4_0/update_users_report_view_settings.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
-from frappe.model.utils.rename_field import update_users_report_view_settings
-from erpnext.patches.v4_0.fields_to_be_renamed import rename_map
-
-def execute():
-	for dt, field_list in rename_map.items():
-		for field in field_list:
-			update_users_report_view_settings(dt, field[0], field[1])
diff --git a/erpnext/patches/v4_0/validate_v3_patch.py b/erpnext/patches/v4_0/validate_v3_patch.py
deleted file mode 100644
index 3df39ed..0000000
--- a/erpnext/patches/v4_0/validate_v3_patch.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	from frappe.modules.patch_handler import executed
-	last_v3_patch = 'patches.1401.fix_pos_outstanding'
-	if not executed(last_v3_patch):
-		raise Exception("site not ready to migrate to version 4")
diff --git a/erpnext/patches/v4_1/__init__.py b/erpnext/patches/v4_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v4_1/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v4_1/fix_delivery_and_billing_status.py b/erpnext/patches/v4_1/fix_delivery_and_billing_status.py
deleted file mode 100644
index 8cc6a4b..0000000
--- a/erpnext/patches/v4_1/fix_delivery_and_billing_status.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""update `tabSales Order` set delivery_status = 'Not Delivered'
-		where delivery_status = 'Delivered' and ifnull(per_delivered, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""")
-
-	frappe.db.sql("""update `tabSales Order` set billing_status = 'Not Billed'
-		where billing_status = 'Billed' and ifnull(per_billed, 0) = 0 and ifnull(docstatus, 0) in (0, 1)""")
diff --git a/erpnext/patches/v4_1/fix_jv_remarks.py b/erpnext/patches/v4_1/fix_jv_remarks.py
deleted file mode 100644
index e07bf05..0000000
--- a/erpnext/patches/v4_1/fix_jv_remarks.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	reference_date = guess_reference_date()
-	for name in frappe.db.sql_list("""select name from `tabJournal Entry`
-			where date(creation)>=%s""", reference_date):
-		jv = frappe.get_doc("Journal Entry", name)
-		try:
-			jv.create_remarks()
-		except frappe.MandatoryError:
-			pass
-		else:
-			frappe.db.set_value("Journal Entry", jv.name, "remark", jv.remark)
-
-def guess_reference_date():
-	return (frappe.db.get_value("Patch Log", {"patch": "erpnext.patches.v4_0.validate_v3_patch"}, "creation")
-		or "2014-05-06")
diff --git a/erpnext/patches/v4_1/fix_sales_order_delivered_status.py b/erpnext/patches/v4_1/fix_sales_order_delivered_status.py
deleted file mode 100644
index 66037b8..0000000
--- a/erpnext/patches/v4_1/fix_sales_order_delivered_status.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for si in frappe.db.sql_list("""select name
-		from `tabSales Invoice`
-		where ifnull(update_stock,0) = 1 and docstatus = 1 and exists(
-			select name from `tabSales Invoice Item` where parent=`tabSales Invoice`.name and
-				ifnull(so_detail, "") != "")"""):
-
-		invoice = frappe.get_doc("Sales Invoice", si)
-		invoice.update_qty()
diff --git a/erpnext/patches/v4_1/set_outgoing_email_footer.py b/erpnext/patches/v4_1/set_outgoing_email_footer.py
deleted file mode 100644
index 54d016b..0000000
--- a/erpnext/patches/v4_1/set_outgoing_email_footer.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.setup.install import default_mail_footer
-
-def execute():
-	return
-	mail_footer = frappe.db.get_default('mail_footer') or ''
-	mail_footer += default_mail_footer
-	frappe.db.set_value("Outgoing Email Settings", "Outgoing Email Settings", "footer", mail_footer)
diff --git a/erpnext/patches/v4_2/__init__.py b/erpnext/patches/v4_2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v4_2/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v4_2/add_currency_turkish_lira.py b/erpnext/patches/v4_2/add_currency_turkish_lira.py
deleted file mode 100644
index 1a46089..0000000
--- a/erpnext/patches/v4_2/add_currency_turkish_lira.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	return
-	# country = get_country_info(country="Turkey")
-	# add_country_and_currency("Turkey", country)
diff --git a/erpnext/patches/v4_2/default_website_style.py b/erpnext/patches/v4_2/default_website_style.py
deleted file mode 100644
index e8f9502..0000000
--- a/erpnext/patches/v4_2/default_website_style.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	return
-	# frappe.reload_doc('website', 'doctype', 'style_settings')
-	# style_settings = frappe.get_doc("Style Settings", "Style Settings")
-	# if not style_settings.apply_style:
-	# 	style_settings.update(default_properties)
-	# 	style_settings.apply_style = 1
-	# 	style_settings.save()
diff --git a/erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py b/erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py
deleted file mode 100644
index 169b1e2..0000000
--- a/erpnext/patches/v4_2/delete_gl_entries_for_cancelled_invoices.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice` 
-		where docstatus = 2 and ifnull(update_stock, 0) = 1""")
-
-	if cancelled_invoices:
-		frappe.db.sql("""delete from `tabGL Entry` 
-			where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" 
-			% (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices))
\ No newline at end of file
diff --git a/erpnext/patches/v4_2/delete_old_print_formats.py b/erpnext/patches/v4_2/delete_old_print_formats.py
deleted file mode 100644
index cacdb85..0000000
--- a/erpnext/patches/v4_2/delete_old_print_formats.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	old_formats = ("Sales Invoice", "Sales Invoice Spartan", "Sales Invoice Modern",
-		"Sales Invoice Classic",
-		"Sales Order Spartan", "Sales Order Modern", "Sales Order Classic",
-		"Purchase Order Spartan", "Purchase Order Modern", "Purchase Order Classic",
-		"Quotation Spartan", "Quotation Modern", "Quotation Classic",
-		"Delivery Note Spartan", "Delivery Note Modern", "Delivery Note Classic")
-
-	for fmt in old_formats:
-		# update property setter
-		for ps in frappe.db.sql_list("""select name from `tabProperty Setter`
-			where property='default_print_format' and value=%s""", fmt):
-			ps = frappe.get_doc("Property Setter", ps)
-			ps.value = "Standard"
-			ps.save(ignore_permissions = True)
-
-		frappe.delete_doc_if_exists("Print Format", fmt)
diff --git a/erpnext/patches/v4_2/discount_amount.py b/erpnext/patches/v4_2/discount_amount.py
deleted file mode 100644
index 7ab61bd..0000000
--- a/erpnext/patches/v4_2/discount_amount.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.modules import scrub, get_doctype_module
-
-def execute():
-	for dt in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
-		frappe.reload_doc(get_doctype_module(dt), "doctype", scrub(dt))
-		frappe.db.sql("""update `tab{0}` set base_discount_amount=discount_amount,
-			discount_amount=discount_amount/conversion_rate""".format(dt))
diff --git a/erpnext/patches/v4_2/fix_account_master_type.py b/erpnext/patches/v4_2/fix_account_master_type.py
deleted file mode 100644
index 99444ce..0000000
--- a/erpnext/patches/v4_2/fix_account_master_type.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for d in frappe.db.sql("""select name from `tabAccount`
-		where ifnull(master_type, '') not in ('Customer', 'Supplier', 'Employee', '') and docstatus=0"""):
-			ac = frappe.get_doc("Account", d[0])
-			ac.master_type = None
-			ac.save()
diff --git a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py b/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py
deleted file mode 100644
index c6c94d4..0000000
--- a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-from frappe.utils import flt
-
-def execute():
-	from erpnext.stock.stock_balance import repost
-	repost(allow_zero_rate=True, only_actual=True)
-
-	frappe.reload_doctype("Account")
-
-	warehouse_account = frappe.db.sql("""select name, master_name from tabAccount
-		where ifnull(account_type, '') = 'Warehouse'""")
-	if warehouse_account:
-		warehouses = [d[1] for d in warehouse_account]
-		accounts = [d[0] for d in warehouse_account]
-
-		stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
-			from `tabStock Ledger Entry` sle
-			where sle.warehouse in (%s)
-			order by sle.posting_date""" %
-			', '.join(['%s']*len(warehouses)), tuple(warehouses))
-
-		rejected = []
-		for voucher_type, voucher_no in stock_vouchers:
-			stock_bal = frappe.db.sql("""select sum(stock_value_difference) from `tabStock Ledger Entry`
-				where voucher_type=%s and voucher_no =%s and warehouse in (%s)""" %
-				('%s', '%s', ', '.join(['%s']*len(warehouses))), tuple([voucher_type, voucher_no] + warehouses))
-
-			account_bal = frappe.db.sql("""select ifnull(sum(ifnull(debit, 0) - ifnull(credit, 0)), 0)
-				from `tabGL Entry`
-				where voucher_type=%s and voucher_no =%s and account in (%s)
-				group by voucher_type, voucher_no""" %
-				('%s', '%s', ', '.join(['%s']*len(accounts))), tuple([voucher_type, voucher_no] + accounts))
-
-			if stock_bal and account_bal and abs(flt(stock_bal[0][0]) - flt(account_bal[0][0])) > 0.1:
-				try:
-					print(voucher_type, voucher_no, stock_bal[0][0], account_bal[0][0])
-
-					frappe.db.sql("""delete from `tabGL Entry`
-						where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
-
-					voucher = frappe.get_doc(voucher_type, voucher_no)
-					voucher.make_gl_entries()
-					frappe.db.commit()
-				except Exception as e:
-					print(frappe.get_traceback())
-					rejected.append([voucher_type, voucher_no])
-					frappe.db.rollback()
-
-		print("Failed to repost: ")
-		print(rejected)
diff --git a/erpnext/patches/v4_2/fix_recurring_orders.py b/erpnext/patches/v4_2/fix_recurring_orders.py
deleted file mode 100644
index ea1724a..0000000
--- a/erpnext/patches/v4_2/fix_recurring_orders.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	sales_orders = frappe.db.sql("""select name from `tabSales Order` 
-		where docstatus = 1 and ifnull(is_recurring, 0) = 1 
-		and (per_delivered > 0 or per_billed > 0)""", as_dict=1)
-
-	for so in sales_orders:
-		if not frappe.db.exists("Delivery Note Item", {"against_sales_order": so.name, "docstatus": 1}):
-			frappe.db.sql("""update `tabSales Order` set per_delivered = 0, 
-				delivery_status = 'Not Delivered' where name = %s""", so.name)
-			frappe.db.sql("""update `tabSales Order Item` set delivered_qty = 0
-				where parent = %s""", so.name)
-
-		if not frappe.db.exists("Sales Invoice Item", {"sales_order": so.name, "docstatus": 1}):
-			frappe.db.sql("""update `tabSales Order` set per_billed = 0, 
-				billing_status = 'Not Billed' where name = %s""", so.name)
-			frappe.db.sql("""update `tabSales Order Item` set billed_amt = 0
-				where parent = %s""", so.name)
-
-	purchase_orders = frappe.db.sql("""select name from `tabPurchase Order` 
-		where docstatus = 1 and ifnull(is_recurring, 0) = 1 
-		and (per_received > 0 or per_billed > 0)""", as_dict=1)
-
-	for po in purchase_orders:
-		if not frappe.db.exists("Purchase Receipt Item", {"prevdoc_doctype": "Purchase Order", 
-			"prevdoc_docname": po.name, "docstatus": 1}):
-				frappe.db.sql("""update `tabPurchase Order` set per_received = 0
-					where name = %s""", po.name)
-				frappe.db.sql("""update `tabPurchase Order Item` set received_qty = 0
-					where parent = %s""", po.name)
-
-		if not frappe.db.exists("Purchase Invoice Item", {"purchase_order": po.name, "docstatus": 1}):
-			frappe.db.sql("""update `tabPurchase Order` set per_billed = 0
-				where name = %s""", po.name)
-			frappe.db.sql("""update `tabPurchase Order Item` set billed_amt = 0
-				where parent = %s""", po.name)
\ No newline at end of file
diff --git a/erpnext/patches/v4_2/party_model.py b/erpnext/patches/v4_2/party_model.py
deleted file mode 100644
index 46d7fff..0000000
--- a/erpnext/patches/v4_2/party_model.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "account")
-	frappe.reload_doc("setup", "doctype", "company")
-	frappe.reload_doc("accounts", "doctype", "gl_entry")
-	frappe.reload_doc("accounts", "doctype", "journal_entry_account")
-	receivable_payable_accounts = create_receivable_payable_account()
-	if receivable_payable_accounts:
-		set_party_in_jv_and_gl_entry(receivable_payable_accounts)
-		delete_individual_party_account()
-		remove_customer_supplier_account_report()
-
-def create_receivable_payable_account():
-	receivable_payable_accounts = frappe._dict()
-
-	def _create_account(args):
-		if args["parent_account"] and frappe.db.exists("Account", args["parent_account"]):
-			account_id = frappe.db.get_value("Account", 
-					{"account_name": args["account_name"], "company": args["company"]})
-			if not account_id:
-				account = frappe.new_doc("Account")
-				account.is_group = 0
-				account.update(args)
-				account.insert()
-			
-				account_id = account.name
-			
-			frappe.db.set_value("Company", args["company"], ("default_receivable_account"
-				if args["account_type"]=="Receivable" else "default_payable_account"), account_id)
-
-			receivable_payable_accounts.setdefault(args["company"], {}).setdefault(args["account_type"], account_id)
-
-	for company in frappe.db.sql_list("select name from tabCompany"):
-		_create_account({
-				"account_name": "Debtors",
-				"account_type": "Receivable",
-				"company": company,
-				"parent_account": get_parent_account(company, "Customer")
-			})
-
-		_create_account({
-			"account_name": "Creditors",
-			"account_type": "Payable",
-			"company": company,
-			"parent_account": get_parent_account(company, "Supplier")
-		})
-
-	return receivable_payable_accounts
-
-def get_parent_account(company, master_type):
-	parent_account = None
-	
-	if "receivables_group" in frappe.db.get_table_columns("Company"):
-		parent_account = frappe.get_cached_value('Company',  company, 
-			"receivables_group" if master_type=="Customer" else "payables_group")
-	if not parent_account:
-		parent_account = frappe.db.get_value("Account", {"company": company,
-			"account_name": "Accounts Receivable" if master_type=="Customer" else "Accounts Payable"})
-
-	if not parent_account:
-		parent_account = frappe.db.sql_list("""select parent_account from tabAccount
-			where company=%s and ifnull(master_type, '')=%s and ifnull(master_name, '')!='' limit 1""",
-			(company, master_type))
-		parent_account = parent_account[0][0] if parent_account else None
-
-	return parent_account
-
-def set_party_in_jv_and_gl_entry(receivable_payable_accounts):
-	accounts = frappe.db.sql("""select name, master_type, master_name, company from `tabAccount`
-		where ifnull(master_type, '') in ('Customer', 'Supplier') and ifnull(master_name, '') != ''""", as_dict=1)
-
-	account_map = frappe._dict()
-	for d in accounts:
-		account_map.setdefault(d.name, d)
-
-	if not account_map:
-		return
-
-	for dt in ["Journal Entry Account", "GL Entry"]:
-		records = frappe.db.sql("""select name, account from `tab%s` 
-			where account in (%s) and ifnull(party, '') = '' and docstatus < 2""" % 
-			(dt, ", ".join(['%s']*len(account_map))), tuple(account_map.keys()), as_dict=1)
-		for i, d in enumerate(records):
-			account_details = account_map.get(d.account, {})
-			account_type = "Receivable" if account_details.get("master_type")=="Customer" else "Payable"
-			new_account = receivable_payable_accounts[account_details.get("company")][account_type]
-
-			frappe.db.sql("update `tab{0}` set account=%s, party_type=%s, party=%s where name=%s".format(dt),
-				(new_account, account_details.get("master_type"), account_details.get("master_name"), d.name))
-
-			if i%500 == 0:
-				frappe.db.commit()
-
-def delete_individual_party_account():
-	frappe.db.sql("""delete from `tabAccount` 
-		where ifnull(master_type, '') in ('Customer', 'Supplier') 
-			and ifnull(master_name, '') != '' 
-			and not exists(select gle.name from `tabGL Entry` gle 
-				where gle.account = tabAccount.name)""")
-		
-	accounts_not_deleted = frappe.db.sql_list("""select tabAccount.name from `tabAccount` 
-		where ifnull(master_type, '') in ('Customer', 'Supplier')
-		and ifnull(master_name, '') != '' 
-		and exists(select gle.name from `tabGL Entry` gle where gle.account = tabAccount.name)""")
-		
-	if accounts_not_deleted:
-		print("Accounts not deleted: " + "\n".join(accounts_not_deleted))
-		
-
-def remove_customer_supplier_account_report():
-	for d in ["Customer Account Head", "Supplier Account Head"]:
-		frappe.delete_doc("Report", d)
diff --git a/erpnext/patches/v4_2/recalculate_bom_cost.py b/erpnext/patches/v4_2/recalculate_bom_cost.py
deleted file mode 100644
index eee89fc..0000000
--- a/erpnext/patches/v4_2/recalculate_bom_cost.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for d in frappe.db.sql("select name from `tabBOM` where docstatus < 2"):
-		try:
-			document = frappe.get_doc('BOM', d[0])
-			if document.docstatus == 1:
-				document.flags.ignore_validate_update_after_submit = True
-				document.calculate_cost()
-			document.save()
-		except:
-			pass
diff --git a/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py b/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py
deleted file mode 100644
index 1356129..0000000
--- a/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-from erpnext.stock.stock_ledger import NegativeStockError
-
-def execute():
-	si_list = frappe.db.sql("""select distinct si.name 
-		from `tabSales Invoice Item` si_item, `tabSales Invoice` si 
-		where si.name = si_item.parent and si.modified > '2015-02-16' and si.docstatus=1 
-		and ifnull(si_item.warehouse, '') = '' and ifnull(si.update_stock, 0) = 1 
-		order by posting_date, posting_time""", as_dict=1)
-		
-	failed_list = []
-	for si in si_list:
-		try:
-			si_doc = frappe.get_doc("Sales Invoice", si.name)		
-			si_doc.docstatus = 2
-			si_doc.on_cancel()
-
-			si_doc.docstatus = 1
-			si_doc.set_missing_item_details()
-			si_doc.on_submit()
-			frappe.db.commit()
-		except:
-			failed_list.append(si.name)
-			frappe.local.stockledger_exceptions = None
-			frappe.db.rollback()
-
-	print("Failed to repost: ", failed_list)
-					
-		
-	
\ No newline at end of file
diff --git a/erpnext/patches/v4_2/repost_stock_reconciliation.py b/erpnext/patches/v4_2/repost_stock_reconciliation.py
deleted file mode 100644
index ad20ebb..0000000
--- a/erpnext/patches/v4_2/repost_stock_reconciliation.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import json
-
-def execute():
-	existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
-	frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
-
-	head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
-	stock_reco_to_be_reposted = []
-	for d in frappe.db.sql("""select name, reconciliation_json from `tabStock Reconciliation`
-		where docstatus=1 and creation > '2014-03-01'""", as_dict=1):
-			data = json.loads(d.reconciliation_json)
-			for row in data[data.index(head_row)+1:]:
-				if row[3] in ["", None]:
-					stock_reco_to_be_reposted.append(d.name)
-					break
-
-	for dn in stock_reco_to_be_reposted:
-		reco = frappe.get_doc("Stock Reconciliation", dn)
-		reco.docstatus = 2
-		reco.on_cancel()
-
-		reco.docstatus = 1
-		reco.validate()
-		reco.on_submit()
-
-	frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
diff --git a/erpnext/patches/v4_2/reset_bom_costs.py b/erpnext/patches/v4_2/reset_bom_costs.py
deleted file mode 100644
index 42ca759..0000000
--- a/erpnext/patches/v4_2/reset_bom_costs.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('manufacturing', 'doctype', 'bom_operation')
-	for d in frappe.db.sql("""select name from `tabBOM` where docstatus < 2""", as_dict=1):
-		try:
-			bom = frappe.get_doc('BOM', d.name)
-			bom.flags.ignore_validate_update_after_submit = True
-			bom.calculate_cost()
-			bom.save()
-			frappe.db.commit()
-		except:
-			frappe.db.rollback()
diff --git a/erpnext/patches/v4_2/seprate_manufacture_and_repack.py b/erpnext/patches/v4_2/seprate_manufacture_and_repack.py
deleted file mode 100644
index 2c93543..0000000
--- a/erpnext/patches/v4_2/seprate_manufacture_and_repack.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(work_order,"")!="" """)
-	frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(work_order,"")="" """)
\ No newline at end of file
diff --git a/erpnext/patches/v4_2/set_company_country.py b/erpnext/patches/v4_2/set_company_country.py
deleted file mode 100644
index 89f07f2..0000000
--- a/erpnext/patches/v4_2/set_company_country.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	country = frappe.db.get_single_value("Global Defaults", "country")
-	if not country:
-		print("Country not specified in Global Defaults")
-		return
-
-	for company in frappe.db.sql_list("""select name from `tabCompany`
-		where ifnull(country, '')=''"""):
-		frappe.db.set_value("Company", company, "country", country)
diff --git a/erpnext/patches/v4_2/set_item_has_batch.py b/erpnext/patches/v4_2/set_item_has_batch.py
deleted file mode 100644
index 7e52d2d..0000000
--- a/erpnext/patches/v4_2/set_item_has_batch.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("update tabItem set has_batch_no = 0 where ifnull(has_batch_no, '') = ''")
-	frappe.db.sql("update tabItem set has_serial_no = 0 where ifnull(has_serial_no, '') = ''")
-
-	item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem
-		where is_stock_item = 1""", as_dict=1)
-
-	sle_count = get_sle_count()
-	sle_with_batch = get_sle_with_batch()
-	sle_with_serial = get_sle_with_serial()
-
-	batch_items = get_items_with_batch()
-	serialized_items = get_items_with_serial()
-
-	for d in item_list:
-		if d.has_batch_no == 1:
-			if d.name not in batch_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_batch.get(d.name):
-					frappe.db.set_value("Item", d.name, "has_batch_no", 0)
-		else:
-			if d.name in batch_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_batch.get(d.name)):
-				frappe.db.set_value("Item", d.name, "has_batch_no", 1)
-
-		if d.has_serial_no == 1:
-			if d.name not in serialized_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_serial.get(d.name):
-				frappe.db.set_value("Item", d.name, "has_serial_no", 0)
-		else:
-			if d.name in serialized_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_serial.get(d.name)):
-				frappe.db.set_value("Item", d.name, "has_serial_no", 1)
-
-
-def get_sle_count():
-	sle_count = {}
-	for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` group by item_code""", as_dict=1):
-		sle_count.setdefault(d.item_code, d.cnt)
-
-	return sle_count
-
-def get_sle_with_batch():
-	sle_with_batch = {}
-	for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry`
-		where ifnull(batch_no, '') != '' group by item_code""", as_dict=1):
-			sle_with_batch.setdefault(d.item_code, d.cnt)
-
-	return sle_with_batch
-
-
-def get_sle_with_serial():
-	sle_with_serial = {}
-	for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry`
-		where ifnull(serial_no, '') != '' group by item_code""", as_dict=1):
-			sle_with_serial.setdefault(d.item_code, d.cnt)
-
-	return sle_with_serial
-
-def get_items_with_batch():
-	return frappe.db.sql_list("select item from tabBatch")
-
-def get_items_with_serial():
-	return frappe.db.sql_list("select item_code from `tabSerial No`")
diff --git a/erpnext/patches/v4_2/toggle_rounded_total.py b/erpnext/patches/v4_2/toggle_rounded_total.py
deleted file mode 100644
index e571208..0000000
--- a/erpnext/patches/v4_2/toggle_rounded_total.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
-	global_defaults.toggle_rounded_total()
diff --git a/erpnext/patches/v4_2/update_landed_cost_voucher.py b/erpnext/patches/v4_2/update_landed_cost_voucher.py
deleted file mode 100644
index ec00296..0000000
--- a/erpnext/patches/v4_2/update_landed_cost_voucher.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "landed_cost_voucher")
-	frappe.db.sql("""update `tabLanded Cost Voucher` set distribute_charges_based_on = 'Amount'
-		where docstatus=1""")
diff --git a/erpnext/patches/v4_2/update_project_milestones.py b/erpnext/patches/v4_2/update_project_milestones.py
deleted file mode 100644
index e704116..0000000
--- a/erpnext/patches/v4_2/update_project_milestones.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for project in frappe.db.sql_list("select name from tabProject"):
-		frappe.reload_doc("projects", "doctype", "project")
-		p = frappe.get_doc("Project", project)
-		p.update_milestones_completed()
-		p.db_set("percent_milestones_completed", p.percent_milestones_completed)
diff --git a/erpnext/patches/v4_2/update_sales_order_invoice_field_name.py b/erpnext/patches/v4_2/update_sales_order_invoice_field_name.py
deleted file mode 100644
index 28dd5c0..0000000
--- a/erpnext/patches/v4_2/update_sales_order_invoice_field_name.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-	frappe.db.sql("""update `tabSales Invoice` set from_date = invoice_period_from_date,
-		to_date = invoice_period_to_date, is_recurring = convert_into_recurring_invoice""")
diff --git a/erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py b/erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py
deleted file mode 100644
index 89bf795..0000000
--- a/erpnext/patches/v4_2/update_stock_uom_for_dn_in_sle.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""update `tabStock Ledger Entry` sle, tabItem item
-		set sle.stock_uom = item.stock_uom
-		where sle.voucher_type="Delivery Note" and item.name = sle.item_code
-		and sle.stock_uom != item.stock_uom""")
diff --git a/erpnext/patches/v4_4/__init__.py b/erpnext/patches/v4_4/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v4_4/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v4_4/make_email_accounts.py b/erpnext/patches/v4_4/make_email_accounts.py
deleted file mode 100644
index 57df1ae..0000000
--- a/erpnext/patches/v4_4/make_email_accounts.py
+++ /dev/null
@@ -1,96 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model import default_fields
-
-from six import iteritems
-
-def execute():
-	frappe.reload_doc("email", "doctype", "email_account")
-
-	# outgoing
-	outgoing = dict(frappe.db.sql("select field, value from tabSingles where doctype='Outgoing Email Settings'"))
-	if outgoing and outgoing['mail_server'] and outgoing['mail_login']:
-		account = frappe.new_doc("Email Account")
-		mapping = {
-			"login_id_is_different": 1,
-			"email_id": "auto_email_id",
-			"login_id": "mail_login",
-			"password": "mail_password",
-			"footer": "footer",
-			"smtp_server": "mail_server",
-			"smtp_port": "mail_port",
-			"use_tls": "use_ssl"
-		}
-
-		for target_fieldname, source_fieldname in iteritems(mapping):
-			account.set(target_fieldname, outgoing.get(source_fieldname))
-
-		account.enable_outgoing = 1
-		account.enable_incoming = 0
-
-		account.insert()
-
-	# support
-	support = dict(frappe.db.sql("select field, value from tabSingles where doctype='Support Email Settings'"))
-	if support and support['mail_server'] and support['mail_login']:
-		account = frappe.new_doc("Email Account")
-		mapping = {
-			"enable_incoming": "sync_support_mails",
-			"email_id": "mail_login",
-			"password": "mail_password",
-			"email_server": "mail_server",
-			"use_ssl": "use_ssl",
-			"signature": "support_signature",
-			"enable_auto_reply": "send_autoreply",
-			"auto_reply_message": "support_autoreply"
-		}
-
-		for target_fieldname, source_fieldname in iteritems(mapping):
-			account.set(target_fieldname, support.get(source_fieldname))
-
-		account.enable_outgoing = 0
-		account.append_to = "Issue"
-
-		insert_or_update(account)
-
-	# sales, jobs
-	for doctype in ("Sales Email Settings", "Jobs Email Settings"):
-		source = dict(frappe.db.sql("select field, value from tabSingles where doctype=%s", doctype))
-		if source and  source.get('host') and source.get('username'):
-			account = frappe.new_doc("Email Account")
-			mapping = {
-				"enable_incoming": "extract_emails",
-				"email_id": "username",
-				"password": "password",
-				"email_server": "host",
-				"use_ssl": "use_ssl",
-			}
-
-			for target_fieldname, source_fieldname in iteritems(mapping):
-				account.set(target_fieldname, source.get(source_fieldname))
-
-			account.enable_outgoing = 0
-			account.append_to = "Lead" if doctype=="Sales Email Settings" else "Job Applicant"
-
-			insert_or_update(account)
-
-	for doctype in ("Outgoing Email Settings", "Support Email Settings",
-		"Sales Email Settings", "Jobs Email Settings"):
-		frappe.delete_doc("DocType", doctype)
-
-def insert_or_update(account):
-	try:
-		account.insert()
-	except frappe.NameError as e:
-		if e.args[0]=="Email Account":
-			existing_account = frappe.get_doc("Email Account", e.args[1])
-			for key, value in account.as_dict().items():
-				if not existing_account.get(key) and value \
-					and key not in default_fields \
-					and key != "__islocal":
-					existing_account.set(key, value)
-
-			existing_account.save()
-		else:
-			raise
-
diff --git a/erpnext/patches/v5_0/__init__.py b/erpnext/patches/v5_0/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v5_0/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v5_0/convert_stock_reconciliation.py b/erpnext/patches/v5_0/convert_stock_reconciliation.py
deleted file mode 100644
index 75d1da7..0000000
--- a/erpnext/patches/v5_0/convert_stock_reconciliation.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from __future__ import unicode_literals
-import frappe, json
-
-def execute():
-	# stock reco now amendable
-	frappe.db.sql("""update tabDocPerm set `amend` = 1 where parent='Stock Reconciliation' and submit = 1""")
-
-	frappe.reload_doc("stock", "doctype", "stock_reconciliation_item")
-	frappe.reload_doctype("Stock Reconciliation")
-	
-	if frappe.db.has_column("Stock Reconciliation", "reconciliation_json"):
-		for sr in frappe.db.get_all("Stock Reconciliation", ["name"],
-			{"reconciliation_json": ["!=", ""]}):
-			start = False
-			sr = frappe.get_doc("Stock Reconciliation", sr.name)
-			for row in json.loads(sr.reconciliation_json):
-				if start:
-					sr.append("items", {
-						"item_code": row[0],
-						"warehouse": row[1],
-						"qty": row[2] if len(row) > 2 else None,
-						"valuation_rate": row[3] if len(row) > 3 else None
-					})
-
-				elif row[0]=="Item Code":
-					start = True
-
-
-			for item in sr.items:
-				item.db_update()
-
diff --git a/erpnext/patches/v5_0/execute_on_doctype_update.py b/erpnext/patches/v5_0/execute_on_doctype_update.py
deleted file mode 100644
index 70b1d8d..0000000
--- a/erpnext/patches/v5_0/execute_on_doctype_update.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt in ("Stock Ledger Entry", "Communication", "DefaultValue", "DocShare", "File", "ToDo"):
-		frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")
diff --git a/erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py b/erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py
deleted file mode 100644
index 30dc0f8..0000000
--- a/erpnext/patches/v5_0/fix_taxes_and_totals_in_party_currency.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.meta import get_field_precision
-
-def execute():
-	if not frappe.db.sql("""select name from `tabPatch Log`
-		where patch = 'erpnext.patches.v5_0.taxes_and_totals_in_party_currency'"""):
-			return
-	selling_doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]
-	buying_doctypes = ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]
-
-	for dt in selling_doctypes:
-		update_values(dt, "Sales Taxes and Charges")
-
-	for dt in buying_doctypes:
-		update_values(dt, "Purchase Taxes and Charges")
-
-def update_values(dt, tax_table):
-	rate_field_precision = get_field_precision(frappe.get_meta(dt + " Item").get_field("rate"))
-	tax_amount_precision = get_field_precision(frappe.get_meta(tax_table).get_field("tax_amount"))
-
-	# update net_total, discount_on
-	frappe.db.sql("""
-		UPDATE
-			`tab{0}`
-		SET
-			total_taxes_and_charges = round(base_total_taxes_and_charges / conversion_rate, {1})
-		WHERE
-			docstatus < 2
-			and ifnull(base_total_taxes_and_charges, 0) != 0
-			and ifnull(total_taxes_and_charges, 0) = 0
-	""".format(dt, tax_amount_precision))
-
-	# update net_amount
-	frappe.db.sql("""
-		UPDATE
-			`tab{0}` par, `tab{1}` item
-		SET
-			item.net_amount = round(item.base_net_amount / par.conversion_rate, {2}),
-			item.net_rate = round(item.base_net_rate / par.conversion_rate, {2})
-		WHERE
-			par.name = item.parent
-			and par.docstatus < 2
-			and ((ifnull(item.base_net_amount, 0) != 0 and ifnull(item.net_amount, 0) = 0)
-				or (ifnull(item.base_net_rate, 0) != 0 and ifnull(item.net_rate, 0) = 0))
-	""".format(dt, dt + " Item", rate_field_precision))
-
-	# update tax in party currency
-	frappe.db.sql("""
-		UPDATE
-			`tab{0}` par, `tab{1}` tax
-		SET
-			tax.tax_amount = round(tax.base_tax_amount / par.conversion_rate, {2}),
-			tax.total = round(tax.base_total / conversion_rate, {2}),
-			tax.tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount / conversion_rate, {2})
-		WHERE
-			par.name = tax.parent
-			and par.docstatus < 2
-			and ((ifnull(tax.base_tax_amount, 0) != 0 and  ifnull(tax.tax_amount, 0) = 0)
-				or (ifnull(tax.base_total, 0) != 0 and ifnull(tax.total, 0) = 0)
-				or (ifnull(tax.base_tax_amount_after_discount_amount, 0) != 0 and
-					ifnull(tax.tax_amount_after_discount_amount, 0) = 0))
-	""".format(dt, tax_table, tax_amount_precision))
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/index_on_account_and_gl_entry.py b/erpnext/patches/v5_0/index_on_account_and_gl_entry.py
deleted file mode 100644
index 2920e92..0000000
--- a/erpnext/patches/v5_0/index_on_account_and_gl_entry.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	index_map = {
-		"Account": ["parent_account", "lft", "rgt"],
-		"GL Entry": ["posting_date", "account", 'party', "voucher_no"],
-		"Sales Invoice": ["posting_date", "debit_to", "customer"],
-		"Purchase Invoice": ["posting_date", "credit_to", "supplier"]
-	}
-	
-	for dt, indexes in index_map.items():
-		existing_indexes = [(d.Key_name, d.Column_name) for d in frappe.db.sql("""show index from `tab{0}` 
-			where Column_name != 'name'""".format(dt), as_dict=1)]
-
-		for old, column in existing_indexes:
-			if column in ("parent", "group_or_ledger", "is_group", "is_pl_account", "debit_or_credit", 
-					"account_name", "company", "project", "voucher_date", "due_date", "bill_no", 
-					"bill_date", "is_opening", "fiscal_year", "outstanding_amount"):
-				frappe.db.sql("alter table `tab{0}` drop index {1}".format(dt, old))
-		
-		existing_indexes = [(d.Key_name, d.Column_name) for d in frappe.db.sql("""show index from `tab{0}` 
-			where Column_name != 'name'""".format(dt), as_dict=1)]
-			
-		existing_indexed_columns = list(set([x[1] for x in existing_indexes]))
-							
-		for new in indexes:
-			if new not in existing_indexed_columns:
-				frappe.db.sql("alter table `tab{0}` add index ({1})".format(dt, new))
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/is_group.py b/erpnext/patches/v5_0/is_group.py
deleted file mode 100644
index 4e3f760..0000000
--- a/erpnext/patches/v5_0/is_group.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	frappe.reload_doctype("Account")
-	frappe.reload_doctype("Cost Center")
-	frappe.db.sql("update tabAccount set is_group = if(group_or_ledger='Group', 1, 0)")
-	frappe.db.sql("update `tabCost Center` set is_group = if(group_or_ledger='Group', 1, 0)")
diff --git a/erpnext/patches/v5_0/item_patches.py b/erpnext/patches/v5_0/item_patches.py
deleted file mode 100644
index e223e09..0000000
--- a/erpnext/patches/v5_0/item_patches.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("update `tabItem` set end_of_life='2099-12-31' where ifnull(end_of_life, '0000-00-00')='0000-00-00'")
-	frappe.db.sql("update `tabItem` set website_image = image where ifnull(website_image, '') = 'attach_files:'")
diff --git a/erpnext/patches/v5_0/link_warehouse_with_account.py b/erpnext/patches/v5_0/link_warehouse_with_account.py
deleted file mode 100644
index 338fd7a..0000000
--- a/erpnext/patches/v5_0/link_warehouse_with_account.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if "master_name" in frappe.db.get_table_columns("Account"):	
-		frappe.db.sql("""update tabAccount set warehouse=master_name
-			where ifnull(account_type, '') = 'Warehouse' and ifnull(master_name, '') != ''""")
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/new_crm_module.py b/erpnext/patches/v5_0/new_crm_module.py
deleted file mode 100644
index f5dda1f..0000000
--- a/erpnext/patches/v5_0/new_crm_module.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import json
-import frappe
-
-def execute():
-	frappe.reload_doc('crm', 'doctype', 'lead')
-	frappe.reload_doc('crm', 'doctype', 'opportunity')
-
-	add_crm_to_user_desktop_items()
-
-def add_crm_to_user_desktop_items():
-	key = "_user_desktop_items"
-	for user in frappe.get_all("User", filters={"enabled": 1, "user_type": "System User"}):
-		user = user.name
-		user_desktop_items = frappe.db.get_defaults(key, parent=user)
-		if user_desktop_items:
-			user_desktop_items = json.loads(user_desktop_items)
-			if "CRM" not in user_desktop_items:
-				user_desktop_items.append("CRM")
-				frappe.db.set_default(key, json.dumps(user_desktop_items), parent=user)
-
diff --git a/erpnext/patches/v5_0/newsletter.py b/erpnext/patches/v5_0/newsletter.py
deleted file mode 100644
index 63e3312..0000000
--- a/erpnext/patches/v5_0/newsletter.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import frappe.permissions
-
-def execute():
-	frappe.reload_doc("core", "doctype", "block_module")
-	frappe.reload_doctype("User")
-	frappe.reload_doctype("Lead")
-	frappe.reload_doctype("Contact")
-
-	frappe.reload_doc('email', 'doctype', 'email_group')
-	frappe.reload_doc('email', 'doctype', 'email_group_member')
-	frappe.reload_doc('email', 'doctype', 'newsletter')
-
-	frappe.permissions.reset_perms("Newsletter")
-
-	if not frappe.db.exists("Role", "Newsletter Manager"):
-		frappe.get_doc({"doctype": "Role", "role": "Newsletter Manager"}).insert()
-
-	for userrole in frappe.get_all("Has Role", "parent", {"role": "Sales Manager", "parenttype": "User"}):
-		if frappe.db.exists("User", userrole.parent):
-			user = frappe.get_doc("User", userrole.parent)
-			user.append("roles", {
-				"doctype": "Has Role",
-				"role": "Newsletter Manager"
-			})
-			user.flags.ignore_mandatory = True
-			user.save()
-
-	# create default lists
-	general = frappe.new_doc("Email Group")
-	general.title = "General"
-	general.insert()
-	general.import_from("Lead")
-	general.import_from("Contact")
diff --git a/erpnext/patches/v5_0/opportunity_not_submittable.py b/erpnext/patches/v5_0/opportunity_not_submittable.py
deleted file mode 100644
index e29d66f..0000000
--- a/erpnext/patches/v5_0/opportunity_not_submittable.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Opportunity")
-	frappe.db.sql("update tabDocPerm set submit=0, cancel=0, amend=0 where parent='Opportunity'")
-	frappe.db.sql("update tabOpportunity set docstatus=0 where docstatus=1")
diff --git a/erpnext/patches/v5_0/party_model_patch_fix.py b/erpnext/patches/v5_0/party_model_patch_fix.py
deleted file mode 100644
index d9b6709..0000000
--- a/erpnext/patches/v5_0/party_model_patch_fix.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for company in frappe.get_all("Company",
-			["name", "default_receivable_account", "default_payable_account"]):
-
-		if company.default_receivable_account:
-			frappe.db.sql("""update `tabSales Invoice` invoice set `debit_to`=%(account)s
-				where company=%(company)s
-				and not exists (select name from `tabAccount` account where account.name=invoice.debit_to)""",
-				{"company": company.name, "account": company.default_receivable_account})
-
-		if company.default_payable_account:
-			frappe.db.sql("""update `tabPurchase Invoice` invoice set `credit_to`=%(account)s
-				where company=%(company)s
-				and not exists (select name from `tabAccount` account where account.name=invoice.credit_to)""",
-				{"company": company.name, "account": company.default_payable_account})
diff --git a/erpnext/patches/v5_0/portal_fixes.py b/erpnext/patches/v5_0/portal_fixes.py
deleted file mode 100644
index 1fefd99..0000000
--- a/erpnext/patches/v5_0/portal_fixes.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-import erpnext.setup.install
-
-def execute():
-	frappe.reload_doc("website", "doctype", "web_form_field", force=True, reset_permissions=True)
-	#erpnext.setup.install.add_web_forms()
diff --git a/erpnext/patches/v5_0/project_costing.py b/erpnext/patches/v5_0/project_costing.py
deleted file mode 100644
index e2d65d0..0000000
--- a/erpnext/patches/v5_0/project_costing.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Project")
-	frappe.db.sql("update `tabProject` set expected_start_date = project_start_date, \
-		expected_end_date = completion_date, actual_end_date = act_completion_date, \
-		estimated_costing = project_value, gross_margin = gross_margin_value")
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/recalculate_total_amount_in_jv.py b/erpnext/patches/v5_0/recalculate_total_amount_in_jv.py
deleted file mode 100644
index d5af43c..0000000
--- a/erpnext/patches/v5_0/recalculate_total_amount_in_jv.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import money_in_words
-
-def execute():
-	company_currency = dict(frappe.db.sql("select name, default_currency from `tabCompany`"))
-	bank_or_cash_accounts = frappe.db.sql_list("""select name from `tabAccount`
-		where account_type in ('Bank', 'Cash') and docstatus < 2""")
-
-	for je in frappe.db.sql_list("""select name from `tabJournal Entry` where docstatus < 2"""):
-		total_amount = 0
-		total_amount_in_words = ""
-
-		je_doc = frappe.get_doc('Journal Entry', je)
-		for d in je_doc.get("accounts"):
-			if (d.party_type and d.party) or d.account in bank_or_cash_accounts:
-				total_amount = d.debit or d.credit
-				if total_amount:
-					total_amount_in_words = money_in_words(total_amount, company_currency.get(je_doc.company))
-
-		if total_amount:
-			frappe.db.sql("""update `tabJournal Entry` set total_amount=%s, total_amount_in_words=%s
-				where name = %s""", (total_amount, total_amount_in_words, je))
diff --git a/erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py b/erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py
deleted file mode 100644
index 6d39283..0000000
--- a/erpnext/patches/v5_0/reclculate_planned_operating_cost_in_production_order.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():					
-	for wo in frappe.db.sql("""select name from `tabWork Order` where docstatus < 2""", as_dict=1):
-		work_order = frappe.get_doc("Work Order", wo.name)
-		if work_order.operations:
-			work_order.flags.ignore_validate_update_after_submit = True
-			work_order.calculate_time()
-			work_order.save()
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/remove_birthday_events.py b/erpnext/patches/v5_0/remove_birthday_events.py
deleted file mode 100644
index 3ead866..0000000
--- a/erpnext/patches/v5_0/remove_birthday_events.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for e in frappe.db.sql_list("""select name from tabEvent where
-		repeat_on='Every Year' and ref_type='Employee'"""):
-		frappe.delete_doc("Event", e, force=True)
diff --git a/erpnext/patches/v5_0/rename_customer_issue.py b/erpnext/patches/v5_0/rename_customer_issue.py
deleted file mode 100644
index 1bd69ce..0000000
--- a/erpnext/patches/v5_0/rename_customer_issue.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("Customer Issue"):
-		frappe.rename_doc("DocType", "Customer Issue", "Warranty Claim")
diff --git a/erpnext/patches/v5_0/rename_pos_setting.py b/erpnext/patches/v5_0/rename_pos_setting.py
deleted file mode 100644
index bf10333..0000000
--- a/erpnext/patches/v5_0/rename_pos_setting.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("POS Setting"):
-		frappe.rename_doc("DocType", "POS Setting", "POS Profile")
diff --git a/erpnext/patches/v5_0/rename_table_fieldnames.py b/erpnext/patches/v5_0/rename_table_fieldnames.py
deleted file mode 100644
index aefb0a2..0000000
--- a/erpnext/patches/v5_0/rename_table_fieldnames.py
+++ /dev/null
@@ -1,243 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
-
-rename_map = {
-	"Opportunity": [
-		["enquiry_details", "items"]
-	],
-	"Quotation": [
-		["quotation_details", "items"],
-		["other_charges", "taxes"]
-	],
-	"Sales Order": [
-		["sales_order_details", "items"],
-		["other_charges", "taxes"],
-		["packing_details", "packed_items"]
-	],
-	"Delivery Note": [
-		["delivery_note_details", "items"],
-		["other_charges", "taxes"],
-		["packing_details", "packed_items"]
-	],
-	"Sales Invoice": [
-		["entries", "items"],
-		["other_charges", "taxes"],
-		["packing_details", "packed_items"],
-		["advance_adjustment_details", "advances"]
-	],
-	"Material Request": [
-		["indent_details", "items"]
-	],
-	"Supplier Quotation": [
-		["quotation_items", "items"],
-		["other_charges", "taxes"]
-	],
-	"Purchase Order": [
-		["po_details", "items"],
-		["other_charges", "taxes"],
-		["po_raw_material_details", "supplied_items"]
-	],
-	"Purchase Receipt": [
-		["purchase_receipt_details", "items"],
-		["other_charges", "taxes"],
-		["pr_raw_material_details", "supplied_items"]
-	],
-	"Purchase Invoice": [
-		["entries", "items"],
-		["other_charges", "taxes"],
-		["advance_allocation_details", "advances"]
-	],
-	"Work Order": [
-		["production_order_operations", "operations"]
-	],
-	"BOM": [
-		["bom_operations", "operations"],
-		["bom_materials", "items"],
-		["flat_bom_details", "exploded_items"]
-	],
-	"Payment Reconciliation": [
-		["payment_reconciliation_payments", "payments"],
-		["payment_reconciliation_invoices", "invoices"]
-	],
-	"Sales Taxes and Charges Template": [
-		["other_charges", "taxes"],
-		["valid_for_territories", "territories"]
-	],
-	"Purchase Taxes and Charges Template": [
-		["other_charges", "taxes"]
-	],
-	"Shipping Rule": [
-		["shipping_rule_conditions", "conditions"],
-		["valid_for_territories", "territories"]
-	],
-	"Price List": [
-		["valid_for_territories", "territories"]
-	],
-	"Appraisal": [
-		["appraisal_details", "goals"]
-	],
-	"Appraisal Template": [
-		["kra_sheet", "goals"]
-	],
-	"Bank Reconciliation": [
-		["entries", "journal_entries"]
-	],
-	"C-Form": [
-		["invoice_details", "invoices"]
-	],
-	"Employee": [
-		["employee_leave_approvers", "leave_approvers"],
-		["educational_qualification_details", "education"],
-		["previous_experience_details", "external_work_history"],
-		["experience_in_company_details", "internal_work_history"]
-	],
-	"Expense Claim": [
-		["expense_voucher_details", "expenses"]
-	],
-	"Fiscal Year": [
-		["fiscal_year_companies", "companies"]
-	],
-	"Holiday List": [
-		["holiday_list_details", "holidays"]
-	],
-	"Installation Note": [
-		["installed_item_details", "items"]
-	],
-	"Item": [
-		["item_reorder", "reorder_levels"],
-		["uom_conversion_details", "uoms"],
-		["item_supplier_details", "supplier_items"],
-		["item_customer_details", "customer_items"],
-		["item_tax", "taxes"],
-		["item_specification_details", "quality_parameters"],
-		["item_website_specifications", "website_specifications"]
-	],
-	"Item Group": [
-		["item_website_specifications", "website_specifications"]
-	],
-	"Landed Cost Voucher": [
-		["landed_cost_purchase_receipts", "purchase_receipts"],
-		["landed_cost_items", "items"],
-		["landed_cost_taxes_and_charges", "taxes"]
-	],
-	"Maintenance Schedule": [
-		["item_maintenance_detail", "items"],
-		["maintenance_schedule_detail", "schedules"]
-	],
-	"Maintenance Visit": [
-		["maintenance_visit_details", "purposes"]
-	],
-	"Packing Slip": [
-		["item_details", "items"]
-	],
-	"Customer": [
-		["party_accounts", "accounts"]
-	],
-	"Customer Group": [
-		["party_accounts", "accounts"]
-	],
-	"Supplier": [
-		["party_accounts", "accounts"]
-	],
-	"Supplier Type": [
-		["party_accounts", "accounts"]
-	],
-	"Payment Tool": [
-		["payment_tool_details", "vouchers"]
-	],
-	"Production Planning Tool": [
-		["pp_so_details", "sales_orders"],
-		["pp_details", "items"]
-	],
-	"Quality Inspection": [
-		["qa_specification_details", "readings"]
-	],
-	"Salary Slip": [
-		["earning_details", "earnings"],
-		["deduction_details", "deductions"]
-	],
-	"Salary Structure": [
-		["earning_details", "earnings"],
-		["deduction_details", "deductions"]
-	],
-	"Product Bundle": [
-		["sales_bom_items", "items"]
-	],
-	"SMS Settings": [
-		["static_parameter_details", "parameters"]
-	],
-	"Stock Entry": [
-		["mtn_details", "items"]
-	],
-	"Sales Partner": [
-		["partner_target_details", "targets"]
-	],
-	"Sales Person": [
-		["target_details", "targets"]
-	],
-	"Territory": [
-		["target_details", "targets"]
-	],
-	"Time Sheet": [
-		["time_sheet_details", "time_logs"]
-	],
-	"Workstation": [
-		["workstation_operation_hours", "working_hours"]
-	],
-	"Payment Reconciliation Payment": [
-		["journal_voucher", "journal_entry"],
-	],
-	"Purchase Invoice Advance": [
-		["journal_voucher", "journal_entry"],
-	],
-	"Sales Invoice Advance": [
-		["journal_voucher", "journal_entry"],
-	],
-	"Journal Entry": [
-		["entries", "accounts"]
-	],
-	"Monthly Distribution": [
-		["budget_distribution_details", "percentages"]
-	]
-}
-
-def execute():
-	# rename doctypes
-	tables = frappe.db.sql_list("show tables")
-	for old_dt, new_dt in [["Journal Voucher Detail", "Journal Entry Account"],
-		["Journal Voucher", "Journal Entry"],
-		["Budget Distribution Detail", "Monthly Distribution Percentage"],
-		["Budget Distribution", "Monthly Distribution"]]:
-			if "tab"+new_dt not in tables:
-				frappe.rename_doc("DocType", old_dt, new_dt, force=True)
-
-	# reload new child doctypes
-	frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
-	frappe.reload_doc("manufacturing", "doctype", "workstation_working_hour")
-	frappe.reload_doc("stock", "doctype", "item_variant")
-	frappe.reload_doc("Payroll", "doctype", "salary_detail")
-	frappe.reload_doc("accounts", "doctype", "party_account")
-	frappe.reload_doc("accounts", "doctype", "fiscal_year_company")
-
-	#rename table fieldnames
-	for dn in rename_map:
-		if not frappe.db.exists("DocType", dn):
-			continue
-		frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn))
-
-	for dt, field_list in rename_map.items():
-		if not frappe.db.exists("DocType", dt):
-			continue
-		for field in field_list:
-			rename_field(dt, field[0], field[1])
-
-	# update voucher type
-	for old, new in [["Bank Voucher", "Bank Entry"], ["Cash Voucher", "Cash Entry"],
-		["Credit Card Voucher", "Credit Card Entry"], ["Contra Voucher", "Contra Entry"],
-		["Write Off Voucher", "Write Off Entry"], ["Excise Voucher", "Excise Entry"]]:
-			frappe.db.sql("update `tabJournal Entry` set voucher_type=%s where voucher_type=%s", (new, old))
diff --git a/erpnext/patches/v5_0/rename_taxes_and_charges_master.py b/erpnext/patches/v5_0/rename_taxes_and_charges_master.py
deleted file mode 100644
index e26f48c..0000000
--- a/erpnext/patches/v5_0/rename_taxes_and_charges_master.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-
-def execute():
-	if frappe.db.table_exists("Sales Taxes and Charges Master"):
-		frappe.rename_doc("DocType", "Sales Taxes and Charges Master",
-			"Sales Taxes and Charges Template")
-		frappe.delete_doc("DocType", "Sales Taxes and Charges Master")
-
-	if frappe.db.table_exists("Purchase Taxes and Charges Master"):
-		frappe.rename_doc("DocType", "Purchase Taxes and Charges Master",
-			"Purchase Taxes and Charges Template")
-		frappe.delete_doc("DocType", "Purchase Taxes and Charges Master")
diff --git a/erpnext/patches/v5_0/rename_total_fields.py b/erpnext/patches/v5_0/rename_total_fields.py
deleted file mode 100644
index 6657dd8..0000000
--- a/erpnext/patches/v5_0/rename_total_fields.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
-
-selling_doctypes = ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice")
-
-buying_doctypes = ("Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice")
-
-selling_renamed_fields = (
-	("net_total", "base_net_total"),
-	("net_total_export", "net_total"),
-	("other_charges_total", "base_total_taxes_and_charges"),
-	("other_charges_total_export", "total_taxes_and_charges"),
-	("grand_total", "base_grand_total"),
-	("grand_total_export", "grand_total"),
-	("rounded_total", "base_rounded_total"),
-	("rounded_total_export", "rounded_total"),
-	("in_words", "base_in_words"),
-	("in_words_export", "in_words")
-)
-
-buying_renamed_fields = (
-	("net_total", "base_net_total"),
-	("net_total_import", "net_total"),
-	("grand_total", "base_grand_total"),
-	("grand_total_import", "grand_total"),
-	("rounded_total", "base_rounded_total"),
-	("in_words", "base_in_words"),
-	("in_words_import", "in_words"),
-	("other_charges_added", "base_taxes_and_charges_added"),
-	("other_charges_added_import", "taxes_and_charges_added"),
-	("other_charges_deducted", "base_taxes_and_charges_deducted"),
-	("other_charges_deducted_import", "taxes_and_charges_deducted"),
-	("total_tax", "base_total_taxes_and_charges")
-)
-
-def execute():
-	for doctypes, fields in [[selling_doctypes, selling_renamed_fields], [buying_doctypes, buying_renamed_fields]]:
-		for dt in doctypes:
-			frappe.reload_doc(get_doctype_module(dt), "doctype", scrub(dt))
-			table_columns = frappe.db.get_table_columns(dt)
-			base_net_total = frappe.db.sql("select sum(ifnull({0}, 0)) from `tab{1}`".format(fields[0][1], dt))[0][0]
-			if not base_net_total:
-				for f in fields:
-					if f[0] in table_columns:
-						rename_field(dt, f[0], f[1])
-
-				# Added new field "total_taxes_and_charges" in buying cycle, updating value
-				if dt in ("Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"):
-					frappe.db.sql("""update `tab{0}` set total_taxes_and_charges =
-						round(base_total_taxes_and_charges/conversion_rate, 2)""".format(dt))
diff --git a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
deleted file mode 100644
index c564f8b..0000000
--- a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import re
-
-def execute():
-	# NOTE: sequence is important
-	renamed_fields = get_all_renamed_fields()
-
-	for dt, script_field, ref_dt_field in (("Client Script", "script", "dt"), ("Print Format", "html", "doc_type")):
-
-		cond1 = " or ".join("""{0} like "%%{1}%%" """.format(script_field, d[0].replace("_", "\\_")) for d in renamed_fields)
-		cond2 = " and standard = 'No'" if dt == "Print Format" else ""
-
-		for name, script, ref_dt in frappe.db.sql("select name, {0} as script, {1} as ref_dt from `tab{2}` where ({3}) {4}".format(script_field, ref_dt_field, dt, cond1, cond2)):
-			update_script(dt, name, ref_dt, script_field, script, renamed_fields)
-
-def get_all_renamed_fields():
-	from erpnext.patches.v5_0.rename_table_fieldnames import rename_map
-
-	renamed_fields = (
-		("base_amount", "base_net_amount"),
-		("net_total", "base_net_total"),
-		("net_total_export", "total"),
-		("net_total_import", "total"),
-		("other_charges_total", "base_total_taxes_and_charges"),
-		("other_charges_total_export", "total_taxes_and_charges"),
-		("other_charges_added", "base_taxes_and_charges_added"),
-		("other_charges_added_import", "taxes_and_charges_added"),
-		("other_charges_deducted", "base_taxes_and_charges_deducted"),
-		("other_charges_deducted_import", "taxes_and_charges_deducted"),
-		("total_tax", "base_total_taxes_and_charges"),
-		("grand_total", "base_grand_total"),
-		("grand_total_export", "grand_total"),
-		("grand_total_import", "grand_total"),
-		("rounded_total", "base_rounded_total"),
-		("rounded_total_export", "rounded_total"),
-		("rounded_total_import", "rounded_total"),
-		("in_words", "base_in_words"),
-		("in_words_export", "in_words"),
-		("in_words_import", "in_words"),
-		("tax_amount", "base_tax_amount"),
-		("tax_amount_after_discount_amount", "base_tax_amount_after_discount_amount"),
-	)
-
-	for fields in rename_map.values():
-		renamed_fields += tuple(fields)
-	
-	return renamed_fields
-
-def update_script(dt, name, ref_dt, script_field, script, renamed_fields):
-	for from_field, to_field in renamed_fields:
-		if from_field != "entries":
-			script = re.sub(r"\b{}\b".format(from_field), to_field, script)
-			
-	if ref_dt == "Journal Entry":
-		script = re.sub(r"\bentries\b", "accounts", script)
-	elif ref_dt == "Bank Reconciliation":
-		script = re.sub(r"\bentries\b", "journal_entries", script)
-	elif ref_dt in ("Sales Invoice", "Purchase Invoice"):
-		script = re.sub(r"\bentries\b", "items", script)
-
-	frappe.db.set_value(dt, name, script_field, script)
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py b/erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py
deleted file mode 100644
index 76efdcc..0000000
--- a/erpnext/patches/v5_0/repost_gle_for_jv_with_multiple_party.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	je_list = frappe.db.sql_list("""
-		select par.name from `tabJournal Entry` par 
-		where par.docstatus=1 and par.creation > '2015-03-01'
-			and (select count(distinct child.party) from `tabJournal Entry Account` child
-				where par.name=child.parent and ifnull(child.party, '') != '') > 1	
-	""")
-	
-	for d in je_list:		
-		# delete existing gle
-		frappe.db.sql("delete from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s", d)
-		
-		# repost gl entries
-		je = frappe.get_doc("Journal Entry", d)
-		je.make_gl_entries()
-		
-	if je_list:
-		print(je_list)
-		
-		
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/repost_requested_qty.py b/erpnext/patches/v5_0/repost_requested_qty.py
deleted file mode 100644
index 6af71f3..0000000
--- a/erpnext/patches/v5_0/repost_requested_qty.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
-
-	count=0
-	for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse 
-		from `tabMaterial Request Item` where docstatus = 1"""):
-			try:
-				count += 1
-				update_bin_qty(item_code, warehouse, {
-					"indented_qty": get_indented_qty(item_code, warehouse),
-				})
-				if count % 200 == 0:
-					frappe.db.commit()
-			except:
-				frappe.db.rollback()
diff --git a/erpnext/patches/v5_0/reset_values_in_tools.py b/erpnext/patches/v5_0/reset_values_in_tools.py
deleted file mode 100644
index fd970ba..0000000
--- a/erpnext/patches/v5_0/reset_values_in_tools.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt in ["Payment Tool", "Bank Reconciliation", "Payment Reconciliation", "Leave Control Panel", 
-		"Salary Manager", "Upload Attenadance", "Production Planning Tool", "BOM Update Tool", "Customize Form",
-		 "Employee Attendance Tool", "Rename Tool", "BOM Update Tool", "Process Payroll", "Naming Series"]:
-			frappe.db.sql("delete from `tabSingles` where doctype=%s", dt)
-		
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/set_appraisal_remarks.py b/erpnext/patches/v5_0/set_appraisal_remarks.py
deleted file mode 100644
index 8652c32..0000000
--- a/erpnext/patches/v5_0/set_appraisal_remarks.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Appraisal")
-	frappe.db.sql("update `tabAppraisal` set remarks = comments")
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/set_default_company_in_bom.py b/erpnext/patches/v5_0/set_default_company_in_bom.py
deleted file mode 100644
index a5cd761..0000000
--- a/erpnext/patches/v5_0/set_default_company_in_bom.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("manufacturing", "doctype", "bom")
-	company = frappe.db.get_value("Global Defaults", None, "default_company")
-	frappe.db.sql("""update  `tabBOM` set company = %s""",company)
diff --git a/erpnext/patches/v5_0/set_footer_address.py b/erpnext/patches/v5_0/set_footer_address.py
deleted file mode 100644
index 8120d83..0000000
--- a/erpnext/patches/v5_0/set_footer_address.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("System Settings")
-	ss = frappe.get_doc("System Settings", "System Settings")
-	ss.email_footer_address = frappe.db.get_default("company")
-	ss.flags.ignore_mandatory = True
-	ss.save()
diff --git a/erpnext/patches/v5_0/stock_entry_update_value.py b/erpnext/patches/v5_0/stock_entry_update_value.py
deleted file mode 100644
index ba1af31..0000000
--- a/erpnext/patches/v5_0/stock_entry_update_value.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for d in frappe.db.get_all("Stock Entry"):
-		se = frappe.get_doc("Stock Entry", d.name)
-		se.set_total_incoming_outgoing_value()
-		se.db_update()
diff --git a/erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py b/erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py
deleted file mode 100644
index 76d1082..0000000
--- a/erpnext/patches/v5_0/taxes_and_totals_in_party_currency.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.meta import get_field_precision
-from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-
-def execute():
-	selling_doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]
-	buying_doctypes = ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]
-
-	for dt in selling_doctypes:
-		update_values(dt, "Sales Taxes and Charges")
-
-	for dt in buying_doctypes:
-		update_values(dt, "Purchase Taxes and Charges")
-
-def update_values(dt, tax_table):
-	frappe.reload_doctype(dt)
-	frappe.reload_doctype(dt + " Item")
-	frappe.reload_doctype(tax_table)
-
-	net_total_precision = get_field_precision(frappe.get_meta(dt).get_field("net_total"))
-	for field in ("total", "base_total", "base_net_total"):
-		make_property_setter(dt, field, "precision", net_total_precision, "Select")
-
-	rate_field_precision = get_field_precision(frappe.get_meta(dt + " Item").get_field("rate"))
-	for field in ("net_rate", "base_net_rate", "net_amount", "base_net_amount", "base_rate", "base_amount"):
-		make_property_setter(dt + " Item", field, "precision", rate_field_precision, "Select")
-
-	tax_amount_precision = get_field_precision(frappe.get_meta(tax_table).get_field("tax_amount"))
-	for field in ("base_tax_amount", "total", "base_total", "tax_amount_after_discount_amount",
-		"base_tax_amount_after_discount_amount"):
-			make_property_setter(tax_table, field, "precision", tax_amount_precision, "Select")
-
-	# update net_total, discount_on
-	frappe.db.sql("""
-		UPDATE
-			`tab{0}`
-		SET
-			total = round(net_total, {1}),
-			base_total = round(net_total*conversion_rate, {1}),
-			net_total = round(base_net_total / conversion_rate, {1}),
-			apply_discount_on = "Grand Total"
-		WHERE
-			docstatus < 2
-	""".format(dt, net_total_precision))
-
-	# update net_amount
-	frappe.db.sql("""
-		UPDATE
-			`tab{0}` par, `tab{1}` item
-		SET
-			item.base_net_amount = round(item.base_amount, {2}),
-			item.base_net_rate = round(item.base_rate, {2}),
-			item.net_amount = round(item.base_amount / par.conversion_rate, {2}),
-			item.net_rate = round(item.base_rate / par.conversion_rate, {2}),
-			item.base_amount = round(item.amount * par.conversion_rate, {2}),
-			item.base_rate = round(item.rate * par.conversion_rate, {2})
-		WHERE
-			par.name = item.parent
-			and par.docstatus < 2
-	""".format(dt, dt + " Item", rate_field_precision))
-
-	# update tax in party currency
-	frappe.db.sql("""
-		UPDATE
-			`tab{0}` par, `tab{1}` tax
-		SET
-			tax.base_tax_amount = round(tax.tax_amount, {2}),
-			tax.tax_amount = round(tax.tax_amount / par.conversion_rate, {2}),
-			tax.base_total = round(tax.total, {2}),
-			tax.total = round(tax.total / conversion_rate, {2}),
-			tax.base_tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, {2}),
-			tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount / conversion_rate, {2})
-		WHERE
-			par.name = tax.parent
-			and par.docstatus < 2
-	""".format(dt, tax_table, tax_amount_precision))
diff --git a/erpnext/patches/v5_0/update_account_types.py b/erpnext/patches/v5_0/update_account_types.py
deleted file mode 100644
index 424743e..0000000
--- a/erpnext/patches/v5_0/update_account_types.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for company in frappe.db.get_all("Company"):
-		company = frappe.get_doc("Company", company.name)
-
-		match_types = ("Stock Received But Not Billed", "Stock Adjustment", "Expenses Included In Valuation",
-			"Cost of Goods Sold")
-
-		for account_type in match_types:
-			account_name = "{0} - {1}".format(account_type, company.abbr)
-			current_account_type = frappe.db.get_value("Account", account_name, "account_type")
-			if current_account_type != account_type:
-				frappe.db.set_value("Account", account_name, "account_type", account_type)
-
-		company.set_default_accounts()
diff --git a/erpnext/patches/v5_0/update_advance_paid.py b/erpnext/patches/v5_0/update_advance_paid.py
deleted file mode 100644
index 74e71e8..0000000
--- a/erpnext/patches/v5_0/update_advance_paid.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt in ("Sales Order", "Purchase Order"):
-		orders_with_advance = frappe.db.sql("""select name from `tab{0}` 
-			where docstatus < 2 and ifnull(advance_paid, 0) != 0""".format(dt), as_dict=1)
-			
-		for order in orders_with_advance:
-			frappe.get_doc(dt, order.name).set_total_advance_paid()
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_companywise_payment_account.py b/erpnext/patches/v5_0/update_companywise_payment_account.py
deleted file mode 100644
index fb4b919..0000000
--- a/erpnext/patches/v5_0/update_companywise_payment_account.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'mode_of_payment')
-	frappe.reload_doc('accounts', 'doctype', 'mode_of_payment_account')
-
-	mode_of_payment_list = frappe.db.sql("""select name, default_account
-		from `tabMode of Payment`""", as_dict=1)
-
-	for d in mode_of_payment_list:
-		if d.get("default_account"):
-			parent_doc = frappe.get_doc("Mode of Payment", d.get("name"))
-
-			parent_doc.set("accounts",
-				[{"company": frappe.db.get_value("Account", d.get("default_account"), "company"),
-				"default_account": d.get("default_account")}])
-			parent_doc.save()
diff --git a/erpnext/patches/v5_0/update_dn_against_doc_fields.py b/erpnext/patches/v5_0/update_dn_against_doc_fields.py
deleted file mode 100644
index 56f4f48..0000000
--- a/erpnext/patches/v5_0/update_dn_against_doc_fields.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('stock', 'doctype', 'delivery_note_item')
-
-	frappe.db.sql("""update `tabDelivery Note Item` set so_detail = prevdoc_detail_docname
-		where ifnull(against_sales_order, '') != ''""")
-
-	frappe.db.sql("""update `tabDelivery Note Item` set si_detail = prevdoc_detail_docname
-		where ifnull(against_sales_invoice, '') != ''""")
diff --git a/erpnext/patches/v5_0/update_from_bom.py b/erpnext/patches/v5_0/update_from_bom.py
deleted file mode 100644
index 4b3e62d..0000000
--- a/erpnext/patches/v5_0/update_from_bom.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Stock Entry")
-	frappe.db.sql("update `tabStock Entry` set from_bom = if(ifnull(bom_no, '')='', 0, 1)")
diff --git a/erpnext/patches/v5_0/update_frozen_accounts_permission_role.py b/erpnext/patches/v5_0/update_frozen_accounts_permission_role.py
deleted file mode 100644
index b52785a..0000000
--- a/erpnext/patches/v5_0/update_frozen_accounts_permission_role.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	account_settings = frappe.get_doc("Accounts Settings")
-
-	if not account_settings.frozen_accounts_modifier and account_settings.bde_auth_role:
-		frappe.db.set_value("Accounts Settings", None,
-			"frozen_accounts_modifier", account_settings.bde_auth_role)
-
diff --git a/erpnext/patches/v5_0/update_item_and_description_again.py b/erpnext/patches/v5_0/update_item_and_description_again.py
deleted file mode 100644
index 35dedcc..0000000
--- a/erpnext/patches/v5_0/update_item_and_description_again.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cstr
-import re
-
-def execute():
-	item_details = frappe._dict()
-	for d in frappe.db.sql("select name, description from `tabItem`", as_dict=1):
-		description = cstr(d.description).strip()
-		new_desc = extract_description(description)
-
-		item_details.setdefault(d.name, frappe._dict({
-			"old_description": description,
-			"new_description": new_desc
-		}))
-
-
-	dt_list= ["Purchase Order Item","Supplier Quotation Item", "BOM", "BOM Explosion Item" , \
-	"BOM Item", "Opportunity Item" , "Quotation Item" , "Sales Order Item" , "Delivery Note Item" , \
-	"Material Request Item" , "Purchase Receipt Item" , "Stock Entry Detail"]
-	for dt in dt_list:
-		frappe.reload_doctype(dt)
-		records = frappe.db.sql("""select name, `{0}` as item_code, description from `tab{1}`
-			where description is not null and description like '%%<table%%'"""
-			.format("item" if dt=="BOM" else "item_code", dt), as_dict=1)
-
-		count = 1
-		for d in records:
-			if d.item_code and item_details.get(d.item_code) \
-					and cstr(d.description) == item_details.get(d.item_code).old_description:
-				desc = item_details.get(d.item_code).new_description
-			else:
-				desc = extract_description(cstr(d.description))
-
-			frappe.db.sql("""update `tab{0}` set description = %s
-				where name = %s """.format(dt), (desc, d.name))
-
-			count += 1
-			if count % 500 == 0:
-				frappe.db.commit()
-
-
-def extract_description(desc):
-	for tag in ("img", "table", "tr", "td"):
-		desc =  re.sub("\</*{0}[^>]*\>".format(tag), "", desc)
-
-	return desc
diff --git a/erpnext/patches/v5_0/update_item_desc_in_invoice.py b/erpnext/patches/v5_0/update_item_desc_in_invoice.py
deleted file mode 100644
index dba35d5..0000000
--- a/erpnext/patches/v5_0/update_item_desc_in_invoice.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.website.utils import find_first_image
-from frappe.utils import cstr
-import re
-
-def execute():
-	item_details = frappe._dict()
-	for d in frappe.db.sql("select name, description, image from `tabItem`", as_dict=1):
-		description = cstr(d.description).strip()
-		item_details.setdefault(d.name, frappe._dict({
-			"description": description,
-			"image": d.image
-		}))
-
-
-	dt_list= ["Sales Invoice Item","Purchase Invoice Item"]
-	for dt in dt_list:
-		frappe.reload_doctype(dt)
-		records = frappe.db.sql("""select name, item_code, description from `tab{0}`
-			where ifnull(item_code, '') != '' and description is not null """.format(dt), as_dict=1)
-
-		count = 1
-		for d in records:
-			if item_details.get(d.item_code) and cstr(d.description) == item_details.get(d.item_code).description:
-				desc = item_details.get(d.item_code).description
-				image = item_details.get(d.item_code).image
-			else:
-				desc, image = extract_image_and_description(cstr(d.description))
-
-				if not image:
-					item_detail = item_details.get(d.item_code)
-					if item_detail:
-						image = item_detail.image
-
-			frappe.db.sql("""update `tab{0}` set description = %s, image = %s
-				where name = %s """.format(dt), (desc, image, d.name))
-
-			count += 1
-			if count % 500 == 0:
-				frappe.db.commit()
-
-
-def extract_image_and_description(data):
-	image_url = find_first_image(data)
-	desc = data
-	for tag in ("img", "table", "tr", "td"):
-		desc =  re.sub("\</*{0}[^>]*\>".format(tag), "", desc)
-	return desc, image_url
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_item_description_and_image.py b/erpnext/patches/v5_0/update_item_description_and_image.py
deleted file mode 100644
index 75df39e..0000000
--- a/erpnext/patches/v5_0/update_item_description_and_image.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.website.utils import find_first_image
-from frappe.utils import cstr
-import re
-
-def execute():
-	item_details = frappe._dict()
-	for d in frappe.db.sql("select name, description_html, description from `tabItem`", as_dict=1):
-		description = cstr(d.description_html).strip() or cstr(d.description).strip()
-		image_url, new_desc = extract_image_and_description(description)
-
-		item_details.setdefault(d.name, frappe._dict({
-			"old_description": description,
-			"new_description": new_desc,
-			"image_url": image_url
-		}))
-
-
-	dt_list= ["Purchase Order Item","Supplier Quotation Item", "BOM", "BOM Explosion Item" , \
-	"BOM Item", "Opportunity Item" , "Quotation Item" , "Sales Order Item" , "Delivery Note Item" , \
-	"Material Request Item" , "Purchase Receipt Item" , "Stock Entry Detail"]
-	for dt in dt_list:
-		frappe.reload_doctype(dt)
-		records = frappe.db.sql("""select name, `{0}` as item_code, description from `tab{1}`
-			where description is not null and image is null and description like '%%<img%%'"""
-			.format("item" if dt=="BOM" else "item_code", dt), as_dict=1)
-
-		count = 1
-		for d in records:
-			if d.item_code and item_details.get(d.item_code) \
-					and cstr(d.description) == item_details.get(d.item_code).old_description:
-				image_url = item_details.get(d.item_code).image_url
-				desc = item_details.get(d.item_code).new_description
-			else:
-				image_url, desc = extract_image_and_description(cstr(d.description))
-
-			if image_url:
-				frappe.db.sql("""update `tab{0}` set description = %s, image = %s
-					where name = %s """.format(dt), (desc, image_url, d.name))
-
-				count += 1
-				if count % 500 == 0:
-					frappe.db.commit()
-
-
-def extract_image_and_description(data):
-	image_url = find_first_image(data)
-	desc =  re.sub("\<img[^>]+\>", "", data)
-
-	return image_url, desc
diff --git a/erpnext/patches/v5_0/update_item_name_in_bom.py b/erpnext/patches/v5_0/update_item_name_in_bom.py
deleted file mode 100644
index 5781542..0000000
--- a/erpnext/patches/v5_0/update_item_name_in_bom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("manufacturing", "doctype", "bom")
-	frappe.reload_doc("manufacturing", "doctype", "bom_item")
-	frappe.reload_doc("manufacturing", "doctype", "bom_explosion_item")
-	frappe.reload_doc("manufacturing", "doctype", "bom_operation")
-
-	frappe.db.sql("""update `tabBOM` as bom  set bom.item_name = \
-		( select item.item_name from `tabItem` as item  where item.name = bom.item)""")
-	frappe.db.sql("""update `tabBOM Item` as bomItem set bomItem.item_name = ( select item.item_name  \
-		from `tabItem` as item where item.name = bomItem.item_code)""")
-	frappe.db.sql("""update `tabBOM Explosion Item` as explosionItem set explosionItem.item_name = \
-		( select item.item_name from `tabItem` as item where item.name = explosionItem.item_code)""")
diff --git a/erpnext/patches/v5_0/update_journal_entry_title.py b/erpnext/patches/v5_0/update_journal_entry_title.py
deleted file mode 100644
index eaa2be0..0000000
--- a/erpnext/patches/v5_0/update_journal_entry_title.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Journal Entry")
-	frappe.db.sql("""update `tabJournal Entry` set title =
-		if(ifnull(pay_to_recd_from, "")!="", pay_to_recd_from,
-			(select account from `tabJournal Entry Account`
-				where parent=`tabJournal Entry`.name and idx=1 limit 1))""")
diff --git a/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py b/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py
deleted file mode 100644
index f31c9fe..0000000
--- a/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""update `tabStock Entry` set purpose='Material Transfer for Manufacture'
-		where ifnull(work_order, '')!='' and purpose='Material Transfer'""")
diff --git a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py b/erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py
deleted file mode 100644
index 2a09aa2..0000000
--- a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Work Order")
-	frappe.db.sql("""update `tabWork Order` set material_transferred_for_manufacturing=
-		(select sum(fg_completed_qty) from `tabStock Entry`
-			where docstatus=1
-			and work_order=`tabWork Order`.name
-			and purpose = "Material Transfer for Manufacture")""")
diff --git a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py b/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py
deleted file mode 100644
index 5847c83..0000000
--- a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	wo_order_qty_transferred = frappe._dict()
-	for se in frappe.db.sql("""select work_order, sum(fg_completed_qty) as transferred_qty 
-		from `tabStock Entry`
-		where docstatus=1 and ifnull(work_order, '') != ''
-		and purpose = 'Material Transfer for Manufacture'
-		group by work_order""", as_dict=1):
-			wo_order_qty_transferred.setdefault(se.work_order, se.transferred_qty)
-	
-	for d in frappe.get_all("Work Order", filters={"docstatus": 1}, fields=["name", "qty"]):
-		if d.name in wo_order_qty_transferred:
-			material_transferred_for_manufacturing = wo_order_qty_transferred.get(d.name) \
-				if wo_order_qty_transferred.get(d.name) <= d.qty else d.qty		
-		
-			frappe.db.sql("""update `tabWork Order` set material_transferred_for_manufacturing=%s
-				where name=%s""", (material_transferred_for_manufacturing, d.name))
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_operation_description.py b/erpnext/patches/v5_0/update_operation_description.py
deleted file mode 100644
index 4ce32f3..0000000
--- a/erpnext/patches/v5_0/update_operation_description.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import frappe.permissions
-
-def execute():
-	if "opn_description" in frappe.db.get_table_columns("BOM Operation"):
-		frappe.db.sql("""update `tabBOM Operation` set description = opn_description
-			where ifnull(description, '') = ''""")
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_opportunity.py b/erpnext/patches/v5_0/update_opportunity.py
deleted file mode 100644
index 8eb45c4..0000000
--- a/erpnext/patches/v5_0/update_opportunity.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('crm', 'doctype', 'opportunity')
-	frappe.reload_doc('crm', 'doctype', 'opportunity_item')
-
-	# all existing opportunities were with items
-	frappe.db.sql("update `tabDocType` set module = 'CRM' where name='Opportunity Item'")
-	frappe.db.sql("update tabOpportunity set with_items=1, title=customer_name")
-	frappe.db.sql("update `tabEmail Account` set append_to='Opportunity' where append_to='Lead'")
diff --git a/erpnext/patches/v5_0/update_projects.py b/erpnext/patches/v5_0/update_projects.py
deleted file mode 100644
index 68e03c9..0000000
--- a/erpnext/patches/v5_0/update_projects.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# convert milestones to tasks
-	frappe.reload_doctype("Project")
-	frappe.reload_doc("projects", "doctype", "project_task")
-	frappe.reload_doctype("Task")
-	frappe.reload_doc("projects", "doctype", "task_depends_on")
-	frappe.reload_doc("projects", "doctype", "time_log")
-
-	for m in frappe.get_all("Project Milestone", "*"):
-		if (m.milestone and m.milestone_date
-			and frappe.db.exists("Project", m.parent)):
-			subject = (m.milestone[:139] + "…") if (len(m.milestone) > 140) else m.milestone
-			description = m.milestone
-			task = frappe.get_doc({
-				"doctype": "Task",
-				"subject": subject,
-				"description": description if description!=subject else None,
-				"expected_start_date": m.milestone_date,
-				"status": "Open" if m.status=="Pending" else "Closed",
-				"project": m.parent,
-			})
-			task.flags.ignore_mandatory = True
-			task.insert(ignore_permissions=True)
-
-	# remove project milestone
-	frappe.delete_doc("DocType", "Project Milestone")
-
-	# remove calendar events for milestone
-	for e in frappe.get_all("Event", ["name"], {"ref_type": "Project"}):
-		frappe.delete_doc("Event", e.name)
diff --git a/erpnext/patches/v5_0/update_sms_sender.py b/erpnext/patches/v5_0/update_sms_sender.py
deleted file mode 100644
index 7ffc703..0000000
--- a/erpnext/patches/v5_0/update_sms_sender.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.set_value("SMS Settings", "SMS Settings", "sms_sender_name",
-		frappe.db.get_single_value("Global Defaults", "sms_sender_name"))
diff --git a/erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py b/erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py
deleted file mode 100644
index 53df942..0000000
--- a/erpnext/patches/v5_0/update_tax_amount_after_discount_in_purchase_cycle.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""
-		update
-			`tabPurchase Taxes and Charges`
-		set
-			tax_amount_after_discount_amount = tax_amount,
-			base_tax_amount_after_discount_amount = base_tax_amount
-		where
-			ifnull(tax_amount_after_discount_amount, 0) = 0
-			and ifnull(base_tax_amount_after_discount_amount, 0) = 0
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_temporary_account.py b/erpnext/patches/v5_0/update_temporary_account.py
deleted file mode 100644
index 078c871..0000000
--- a/erpnext/patches/v5_0/update_temporary_account.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""Update `tabAccount` set account_type = 'Temporary' 
-		where account_name in ('Temporary Assets', 'Temporary Liabilities', 'Temporary Opening')""")
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/update_time_log_title.py b/erpnext/patches/v5_0/update_time_log_title.py
deleted file mode 100644
index 8263be0..0000000
--- a/erpnext/patches/v5_0/update_time_log_title.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Time Log")
-	for d in frappe.get_all("Time Log"):
-		time_log = frappe.get_doc("Time Log", d.name)
-		time_log.set_title()
-		frappe.db.set_value("Time Log", time_log.name, "title", time_log.title)
diff --git a/erpnext/patches/v5_1/__init__.py b/erpnext/patches/v5_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v5_1/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v5_1/default_bom.py b/erpnext/patches/v5_1/default_bom.py
deleted file mode 100644
index 6484edd..0000000
--- a/erpnext/patches/v5_1/default_bom.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	frappe.db.sql("""Update `tabItem` as item set default_bom = NULL where 
-		not exists(select name from `tabBOM` as bom where item.default_bom = bom.name and bom.docstatus =1 )""")
\ No newline at end of file
diff --git a/erpnext/patches/v5_1/fix_against_account.py b/erpnext/patches/v5_1/fix_against_account.py
deleted file mode 100644
index a62c15b..0000000
--- a/erpnext/patches/v5_1/fix_against_account.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-from erpnext.accounts.doctype.gl_entry.gl_entry import update_against_account
-
-def execute():
-    from_date = "2015-05-01"
-
-    for doc in frappe.get_all("Journal Entry",
-        filters={"creation": (">", from_date), "docstatus": "1"}):
-
-        # update in gl_entry
-        update_against_account("Journal Entry", doc.name)
-
-        # update in jv
-        doc = frappe.get_doc("Journal Entry", doc.name)
-        doc.set_against_account()
-        doc.db_update()
-
-    for doc in frappe.get_all("Sales Invoice",
-        filters={"creation": (">", from_date), "docstatus": "1"},
-        fields=["name", "customer"]):
-
-        frappe.db.sql("""update `tabGL Entry` set against=%s
-            where voucher_type='Sales Invoice' and voucher_no=%s
-            and credit > 0 and ifnull(party, '')=''""",
-            (doc.customer, doc.name))
-
-    for doc in frappe.get_all("Purchase Invoice",
-        filters={"creation": (">", from_date), "docstatus": "1"},
-        fields=["name", "supplier"]):
-
-        frappe.db.sql("""update `tabGL Entry` set against=%s
-            where voucher_type='Purchase Invoice' and voucher_no=%s
-            and debit > 0 and ifnull(party, '')=''""",
-            (doc.supplier, doc.name))
diff --git a/erpnext/patches/v5_1/rename_roles.py b/erpnext/patches/v5_1/rename_roles.py
deleted file mode 100644
index e19c22a..0000000
--- a/erpnext/patches/v5_1/rename_roles.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if not frappe.db.exists("Role", "Stock User"):
-		frappe.rename_doc("Role", "Material User", "Stock User")
-	if not frappe.db.exists("Role", "Stock Manager"):
-		frappe.rename_doc("Role", "Material Manager", "Stock Manager")
-	if not frappe.db.exists("Role", "Item Manager"):
-		frappe.rename_doc("Role", "Material Master Manager", "Item Manager")
diff --git a/erpnext/patches/v5_1/sales_bom_rename.py b/erpnext/patches/v5_1/sales_bom_rename.py
deleted file mode 100644
index e06012f..0000000
--- a/erpnext/patches/v5_1/sales_bom_rename.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	tables = frappe.db.sql_list("show tables")
-	for old_dt, new_dt in [["Sales BOM Item", "Product Bundle Item"],
-		["Sales BOM", "Product Bundle"]]:
-			if "tab"+new_dt not in tables:
-				frappe.rename_doc("DocType", old_dt, new_dt, force=True)
diff --git a/erpnext/patches/v5_2/__init__.py b/erpnext/patches/v5_2/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v5_2/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v5_2/change_item_selects_to_checks.py b/erpnext/patches/v5_2/change_item_selects_to_checks.py
deleted file mode 100644
index 1ee8f6c..0000000
--- a/erpnext/patches/v5_2/change_item_selects_to_checks.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	fields = ("is_stock_item", "is_asset_item", "has_batch_no", "has_serial_no",
-		"is_sales_item", "is_purchase_item", "inspection_required", "is_sub_contracted_item")
-
-	# convert to 1 or 0
-	update_str = ", ".join(["`{0}`=if(`{0}`='Yes',1,0)".format(f) for f in fields])
-	frappe.db.sql("update tabItem set {0}".format(update_str))
-
-	frappe.db.commit()
-
-	# alter fields to int
-	for f in fields:
-		frappe.db.sql("alter table tabItem change {0} {0} int(1) default '0'".format(f, f))
-
-	frappe.reload_doctype("Item")
diff --git a/erpnext/patches/v5_4/__init__.py b/erpnext/patches/v5_4/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v5_4/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py
deleted file mode 100644
index 6860e6a..0000000
--- a/erpnext/patches/v5_4/cleanup_journal_entry.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from pymysql import InternalError
-
-def execute():
-	frappe.reload_doctype("Journal Entry Account")
-	for doctype, fieldname in (
-		("Sales Order", "against_sales_order"),
-		("Purchase Order", "against_purchase_order"),
-		("Sales Invoice", "against_invoice"),
-		("Purchase Invoice", "against_voucher"),
-		("Journal Entry", "against_jv"),
-		("Expense Claim", "against_expense_claim"),
-	):
-		try:
-			frappe.db.sql("""update `tabJournal Entry Account`
-				set reference_type=%s, reference_name={0} where ifnull({0}, '') != ''
-			""".format(fieldname), doctype)
-		except InternalError:
-			# column not found
-			pass
diff --git a/erpnext/patches/v5_4/fix_invoice_outstanding.py b/erpnext/patches/v5_4/fix_invoice_outstanding.py
deleted file mode 100644
index 54a1da6..0000000
--- a/erpnext/patches/v5_4/fix_invoice_outstanding.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
-
-def execute():
-	frappe.reload_doctype("Sales Invoice")
-	return_entries = frappe.get_list("Sales Invoice", filters={"is_return": 1, "docstatus": 1}, 
-		fields=["debit_to", "customer", "return_against"])
-	for d in return_entries:
-		update_outstanding_amt(d.debit_to, "Customer", d.customer, "Sales Invoice", d.return_against)
diff --git a/erpnext/patches/v5_4/fix_missing_item_images.py b/erpnext/patches/v5_4/fix_missing_item_images.py
deleted file mode 100644
index c6fe578..0000000
--- a/erpnext/patches/v5_4/fix_missing_item_images.py
+++ /dev/null
@@ -1,126 +0,0 @@
-from __future__ import print_function, unicode_literals
-import frappe
-import os
-from frappe.utils import get_files_path
-from frappe.core.doctype.file.file import get_content_hash
-
-def execute():
-	files_path = get_files_path()
-
-	# get files that don't have attached_to_name but exist
-	unlinked_files = get_unlinked_files(files_path)
-	if not unlinked_files:
-		return
-
-	fixed_files = fix_files_for_item(files_path, unlinked_files)
-
-	# fix remaining files
-	for key, file_data in unlinked_files.items():
-		if key not in fixed_files:
-			rename_and_set_content_hash(files_path, unlinked_files, key)
-			frappe.db.commit()
-
-def fix_files_for_item(files_path, unlinked_files):
-	fixed_files = []
-
-	# make a list of files/something and /files/something to check in child table's image column
-	file_urls = [key for key in unlinked_files.keys()] + ["/" + key for key in unlinked_files.keys()]
-	file_item_code = get_file_item_code(file_urls)
-
-	for (file_url, item_code), children in file_item_code.items():
-		new_file_url = "/files/{0}".format(unlinked_files[file_url]["file_name"])
-
-		for row in children:
-			# print file_url, new_file_url, item_code, row.doctype, row.name
-
-			# replace image in these rows with the new file url
-			frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False)
-
-		# set it as attachment of this item code
-		file_data = frappe.get_doc("File", unlinked_files[file_url]["file"])
-		file_data.attached_to_doctype = "Item"
-		file_data.attached_to_name = item_code
-		file_data.flags.ignore_folder_validate = True
-
-		try:
-			file_data.save()
-		except IOError:
-			print("File {0} does not exist".format(new_file_url))
-
-			# marking fix to prevent further errors
-			fixed_files.append(file_url)
-
-			continue
-
-		# set it as image in Item
-		if not frappe.db.get_value("Item", item_code, "image"):
-			frappe.db.set_value("Item", item_code, "image", new_file_url, update_modified=False)
-
-		rename_and_set_content_hash(files_path, unlinked_files, file_url)
-
-		fixed_files.append(file_url)
-
-		# commit
-		frappe.db.commit()
-
-	return fixed_files
-
-def rename_and_set_content_hash(files_path, unlinked_files, file_url):
-	# rename this file
-	old_filename = os.path.join(files_path, unlinked_files[file_url]["file"])
-	new_filename = os.path.join(files_path, unlinked_files[file_url]["file_name"])
-
-	if not os.path.exists(new_filename):
-		os.rename(old_filename, new_filename)
-
-	# set content hash if missing
-	file_data_name = unlinked_files[file_url]["file"]
-	if not frappe.db.get_value("File", file_data_name, "content_hash"):
-		with open(new_filename, "r") as f:
-			content_hash = get_content_hash(f.read())
-			frappe.db.set_value("File", file_data_name, "content_hash", content_hash)
-
-def get_unlinked_files(files_path):
-	# find files that have the same name as a File doc
-	# and the file_name mentioned in that File doc doesn't exist
-	# and it isn't already attached to a doc
-	unlinked_files = {}
-	files = os.listdir(files_path)
-	for file in files:
-		if not frappe.db.exists("File", {"file_name": file}):
-			file_data = frappe.db.get_value("File", {"name": file},
-				["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True)
-
-			if (file_data
-				and file_data.file_name
-				and file_data.file_name not in files
-				and not file_data.attached_to_doctype
-				and not file_data.attached_to_name):
-
-				file_data["file"] = file
-				unlinked_files["files/{0}".format(file)] = file_data
-
-	return unlinked_files
-
-def get_file_item_code(file_urls):
-	# get a map of file_url, item_code and list of documents where file_url will need to be changed in image field
-	file_item_code = {}
-
-	doctypes = frappe.db.sql_list("""select name from `tabDocType` dt
-		where istable=1
-			and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='item_code')
-			and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='image')""")
-
-	for doctype in doctypes:
-		result = frappe.db.sql("""select name, image, item_code, '{0}' as doctype from `tab{0}`
-				where image in ({1})""".format(doctype, ", ".join(["%s"]*len(file_urls))),
-				file_urls, as_dict=True)
-
-		for r in result:
-			key = (r.image, r.item_code)
-			if key not in file_item_code:
-				file_item_code[key] = []
-
-			file_item_code[key].append(r)
-
-	return file_item_code
diff --git a/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py b/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py
deleted file mode 100644
index 6eb3994..0000000
--- a/erpnext/patches/v5_4/fix_reserved_qty_and_sle_for_packed_items.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import repost_actual_qty
-
-def execute():
-	cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice` 
-		where docstatus = 2 and ifnull(update_stock, 0) = 1""")
-
-	if cancelled_invoices:
-		repost_for = frappe.db.sql("""select distinct item_code, warehouse from `tabStock Ledger Entry`
-			where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" 
-			% (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices))
-			
-		frappe.db.sql("""delete from `tabStock Ledger Entry` 
-			where voucher_type = 'Sales Invoice' and voucher_no in (%s)""" 
-			% (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices))
-			
-		for item_code, warehouse in repost_for:
-			repost_actual_qty(item_code, warehouse)
\ No newline at end of file
diff --git a/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py b/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py
deleted file mode 100644
index ba31122..0000000
--- a/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-from frappe.email import sendmail_to_system_managers
-from frappe.utils import get_link_to_form
-
-def execute():
-	wrong_records = []
-	for dt in ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice", 
-		"Purchase Order", "Purchase Receipt", "Purchase Invoice"):
-			records = frappe.db.sql_list("""select name from `tab{0}` 
-				where apply_discount_on = 'Net Total' and ifnull(discount_amount, 0) != 0
-				and modified >= '2015-02-17' and docstatus=1""".format(dt))
-		
-			if records:
-				records = [get_link_to_form(dt, d) for d in records]
-				wrong_records.append([dt, records])
-				
-	if wrong_records:
-		content = """Dear System Manager,
-
-Due to an error related to Discount Amount on Net Total, tax calculation might be wrong in the following records. We did not fix the tax amount automatically because it can corrupt the entries, so we request you to check these records and amend if you found the calculation wrong.
-
-Please check following Entries:
-
-%s
-
-
-Regards,
-
-Administrator""" % "\n".join([(d[0] + ": " + ", ".join(d[1])) for d in wrong_records])
-		try:
-			sendmail_to_system_managers("[Important] [ERPNext] Tax calculation might be wrong, please check.", content)
-		except:
-			pass
-		
-		print("="*50)
-		print(content)
-		print("="*50)
\ No newline at end of file
diff --git a/erpnext/patches/v5_4/set_root_and_report_type.py b/erpnext/patches/v5_4/set_root_and_report_type.py
deleted file mode 100644
index 9147644..0000000
--- a/erpnext/patches/v5_4/set_root_and_report_type.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	roots = frappe.db.sql("""select lft, rgt, report_type, root_type 
-		from `tabAccount` where ifnull(parent_account, '')=''""", as_dict=1)
-	for d in roots:
-		frappe.db.sql("update `tabAccount` set report_type=%s, root_type=%s where lft > %s and rgt < %s", 
-			(d.report_type, d.root_type, d.lft, d.rgt))
\ No newline at end of file
diff --git a/erpnext/patches/v5_4/stock_entry_additional_costs.py b/erpnext/patches/v5_4/stock_entry_additional_costs.py
deleted file mode 100644
index 3a98deb..0000000
--- a/erpnext/patches/v5_4/stock_entry_additional_costs.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt
-
-def execute():
-	frappe.reload_doctype("Stock Entry")
-	frappe.reload_doctype("Stock Entry Detail")
-	frappe.reload_doctype("Landed Cost Taxes and Charges")
-
-	stock_entry_db_columns = frappe.db.get_table_columns("Stock Entry")
-	if "additional_operating_cost" in stock_entry_db_columns:
-		operating_cost_fieldname = "additional_operating_cost"
-	elif "total_fixed_cost" in stock_entry_db_columns:
-		operating_cost_fieldname = "total_fixed_cost"
-	else:
-		return
-		
-
-	frappe.db.sql("""update `tabStock Entry Detail` sed, `tabStock Entry` se
-		set sed.valuation_rate=sed.incoming_rate, sed.basic_rate=sed.incoming_rate, sed.basic_amount=sed.amount
-		where sed.parent = se.name
-		and (se.purpose not in ('Manufacture', 'Repack') or ifnull({0}, 0)=0)
-	""".format(operating_cost_fieldname))
-		
-
-	stock_entries = frappe.db.sql_list("""select name from `tabStock Entry`
-		where purpose in ('Manufacture', 'Repack') and ifnull({0}, 0)!=0
-		and docstatus < 2""".format(operating_cost_fieldname))
-
-	for d in stock_entries:
-		stock_entry = frappe.get_doc("Stock Entry", d)
-		stock_entry.append("additional_costs", {
-			"description": "Additional Operating Cost",
-			"amount": stock_entry.get(operating_cost_fieldname)
-		})
-
-		number_of_fg_items = len([t.t_warehouse for t in stock_entry.get("items") if t.t_warehouse])
-
-		for d in stock_entry.get("items"):
-			d.valuation_rate = d.incoming_rate
-
-			if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
-				d.additional_cost = stock_entry.get(operating_cost_fieldname)
-
-			d.basic_rate = flt(d.valuation_rate) - flt(d.additional_cost)
-			d.basic_amount = flt(flt(d.basic_rate) *flt(d.transfer_qty), d.precision("basic_amount"))
-
-		stock_entry.flags.ignore_validate = True
-		stock_entry.flags.ignore_validate_update_after_submit = True
-		stock_entry.save()
diff --git a/erpnext/patches/v5_4/update_purchase_cost_against_project.py b/erpnext/patches/v5_4/update_purchase_cost_against_project.py
deleted file mode 100644
index 1b917c8..0000000
--- a/erpnext/patches/v5_4/update_purchase_cost_against_project.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for p in frappe.get_all("Project"):
-		purchase_cost = frappe.db.sql("""select sum(ifnull(base_net_amount, 0))
-			from `tabPurchase Invoice Item` where project = %s and docstatus=1""", p.name)
-		purchase_cost = purchase_cost and purchase_cost[0][0] or 0
-		
-		frappe.db.set_value("Project", p.name, "total_purchase_cost", purchase_cost)
\ No newline at end of file
diff --git a/erpnext/patches/v5_7/__init__.py b/erpnext/patches/v5_7/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v5_7/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v5_7/item_template_attributes.py b/erpnext/patches/v5_7/item_template_attributes.py
deleted file mode 100644
index 6aa81f7..0000000
--- a/erpnext/patches/v5_7/item_template_attributes.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-from frappe.exceptions import SQLError
-
-def execute():
-	"""
-		Structure History:
-			1. Item and Item Variant
-			2. Item, Variant Attribute, Manage Variants and Manage Variant Items
-			3. Item, Item Variant Attribute, Item Attribute and Item Attribute Type (latest)
-	"""
-	rename_and_reload_doctypes()
-
-	variant_templates = frappe.get_all("Item", filters={"has_variants": 1}, limit_page_length=1)
-	if not variant_templates:
-		# database does not have items that have variants
-		# so no point in running the patch
-		return
-
-	variant_attributes = frappe.get_all("Item Variant Attribute", fields=["*"], limit_page_length=1)
-
-	if variant_attributes:
-		# manage variant patch is already applied
-		migrate_manage_variants()
-
-	else:
-		# old structure based on "Item Variant" table
-		try:
-			migrate_item_variants()
-
-		except SQLError:
-			print("`tabItem Variant` not found")
-
-def rename_and_reload_doctypes():
-	if "tabVariant Attribute" in frappe.db.get_tables():
-		frappe.rename_doc("DocType", "Variant Attribute", "Item Variant Attribute")
-
-	frappe.reload_doctype("Item")
-	frappe.reload_doc("Stock", "DocType", "Item Variant Attribute")
-	frappe.reload_doc("Stock", "DocType", "Item Attribute Value")
-	frappe.reload_doc("Stock", "DocType", "Item Attribute")
-
-def migrate_manage_variants():
-	item_attribute = {}
-	for d in  frappe.db.sql("""select DISTINCT va.attribute, i.variant_of
-		from `tabItem Variant Attribute` va, `tabItem` i
-		where va.parent = i.name and ifnull(i.variant_of, '')!=''""", as_dict=1):
-		item_attribute.setdefault(d.variant_of, []).append({"attribute": d.attribute})
-
-	for item, attributes in item_attribute.items():
-		template = frappe.get_doc("Item", item)
-		template.set('attributes', attributes)
-		template.save()
-
-# patch old style
-def migrate_item_variants():
-	for item in frappe.get_all("Item", filters={"has_variants": 1}):
-		all_variants = frappe.get_all("Item", filters={"variant_of": item.name}, fields=["name", "description"])
-		item_attributes = frappe.db.sql("""select distinct item_attribute, item_attribute_value
-			from `tabItem Variant` where parent=%s""", item.name)
-
-		if not item_attributes and not all_variants:
-			item = frappe.get_doc("Item", item.name)
-			item.has_variants = 0
-			item.save()
-			continue
-
-		attribute_value_options = {}
-		for attribute, value in item_attributes:
-			attribute_value_options.setdefault(attribute, []).append(value)
-
-		possible_combinations = get_possible_combinations(attribute_value_options)
-
-		for variant in all_variants:
-			for combination in possible_combinations:
-				match = True
-				for attribute, value in combination.items():
-					if "{0}: {1}".format(attribute, value) not in variant.description:
-						match = False
-						break
-
-				if match:
-					# found the right variant
-					save_attributes_in_variant(variant, combination)
-					break
-
-		save_attributes_in_template(item, attribute_value_options)
-
-	frappe.delete_doc("DocType", "Item Variant")
-
-def save_attributes_in_template(item, attribute_value_options):
-	# store attribute in Item Variant Attribute table for template
-	template = frappe.get_doc("Item", item)
-	template.set("attributes", [{"attribute": attribute} for attribute in attribute_value_options.keys()])
-	template.save()
-
-def get_possible_combinations(attribute_value_options):
-	possible_combinations = []
-
-	for attribute, values in attribute_value_options.items():
-		if not possible_combinations:
-			for v in values:
-				possible_combinations.append({attribute: v})
-
-		else:
-			for v in values:
-				for combination in possible_combinations:
-					combination[attribute] = v
-
-	return possible_combinations
-
-def save_attributes_in_variant(variant, combination):
-	# add data into attributes table
-	variant_item = frappe.get_doc("Item", variant.name)
-	variant_item.set("attributes", [])
-	for attribute, value in combination.items():
-		variant_item.append("attributes", {
-			"attribute": attribute,
-			"attribute_value": value
-		})
-	variant_item.save()
diff --git a/erpnext/patches/v5_8/__init__.py b/erpnext/patches/v5_8/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v5_8/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v5_8/add_credit_note_print_heading.py b/erpnext/patches/v5_8/add_credit_note_print_heading.py
deleted file mode 100644
index 476cbc8..0000000
--- a/erpnext/patches/v5_8/add_credit_note_print_heading.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def execute():
-	for print_heading in (_("Credit Note"), _("Debit Note")):
-		if not frappe.db.exists("Print Heading", print_heading):
-			frappe.get_doc({
-				"doctype": "Print Heading",
-				"print_heading": print_heading
-			}).insert()
diff --git a/erpnext/patches/v5_8/tax_rule.py b/erpnext/patches/v5_8/tax_rule.py
deleted file mode 100644
index 8da28ba..0000000
--- a/erpnext/patches/v5_8/tax_rule.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "tax_rule")
-
-	customers = frappe.db.sql("""select name, default_taxes_and_charges from tabCustomer where
-		ifnull(default_taxes_and_charges, '') != '' """, as_dict=1)
-
-	for d in customers:
-		if not frappe.db.sql("select name from `tabTax Rule` where customer=%s", d.name):
-			tr = frappe.new_doc("Tax Rule")
-			tr.tax_type = "Sales"
-			tr.customer = d.name
-			tr.sales_tax_template = d.default_taxes_and_charges
-			tr.save()
-
-
-	suppliers = frappe.db.sql("""select name, default_taxes_and_charges from tabSupplier where
-		ifnull(default_taxes_and_charges, '') != '' """, as_dict=1)
-
-	for d in suppliers:
-		if not frappe.db.sql("select name from `tabTax Rule` where supplier=%s", d.name):
-			tr = frappe.new_doc("Tax Rule")
-			tr.tax_type = "Purchase"
-			tr.supplier = d.name
-			tr.purchase_tax_template = d.default_taxes_and_charges
-			tr.save()
\ No newline at end of file
diff --git a/erpnext/patches/v5_8/update_order_reference_in_return_entries.py b/erpnext/patches/v5_8/update_order_reference_in_return_entries.py
deleted file mode 100644
index 5032638..0000000
--- a/erpnext/patches/v5_8/update_order_reference_in_return_entries.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Delivery Note")
-	frappe.reload_doctype("Sales Invoice")
-	frappe.reload_doctype("Purchase Receipt")
-	frappe.reload_doctype("Sales Order Item")
-	frappe.reload_doctype("Purchase Order Item")
-	frappe.reload_doctype("Purchase Order Item Supplied")
-
-	# sales return
-	return_entries = list(frappe.db.sql("""
-		select dn.name as name, dn_item.name as row_id, dn.return_against,
-			dn_item.item_code, "Delivery Note" as doctype
-		from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn
-		where dn_item.parent=dn.name and dn.is_return=1 and dn.docstatus < 2
-	""", as_dict=1))
-
-	return_entries += list(frappe.db.sql("""
-		select si.name as name, si_item.name as row_id, si.return_against,
-			si_item.item_code, "Sales Invoice" as doctype, update_stock
-		from `tabSales Invoice Item` si_item, `tabSales Invoice` si
-		where si_item.parent=si.name and si.is_return=1 and si.docstatus < 2
-	""", as_dict=1))
-
-	for d in return_entries:
-		ref_field = "against_sales_order" if d.doctype == "Delivery Note" else "sales_order"
-		order_details = frappe.db.sql("""
-			select {ref_field} as sales_order, so_detail,
-				(select transaction_date from `tabSales Order` where name=item.{ref_field}) as sales_order_date
-			from `tab{doctype} Item` item
-			where
-				parent=%s
-				and item_code=%s
-				and ifnull(so_detail, '') !=''
-			order by sales_order_date DESC limit 1
-		""".format(ref_field=ref_field, doctype=d.doctype), (d.return_against, d.item_code), as_dict=1)
-
-		if order_details:
-			frappe.db.sql("""
-				update `tab{doctype} Item`
-				set {ref_field}=%s, so_detail=%s
-				where name=%s
-			""".format(doctype=d.doctype, ref_field=ref_field),
-			(order_details[0].sales_order, order_details[0].so_detail, d.row_id))
-
-			if (d.doctype=="Sales Invoice" and d.update_stock) or d.doctype=="Delivery Note":
-				doc = frappe.get_doc(d.doctype, d.name)
-				doc.update_reserved_qty()
-
-				if d.doctype=="Sales Invoice":
-					doc.status_updater = []
-					doc.update_status_updater_args()
-
-				doc.update_prevdoc_status()
-
-	#--------------------------
-	# purchase return
-	return_entries = frappe.db.sql("""
-		select pr.name as name, pr_item.name as row_id, pr.return_against, pr_item.item_code
-		from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
-		where pr_item.parent=pr.name and pr.is_return=1 and pr.docstatus < 2
-	""", as_dict=1)
-
-	for d in return_entries:
-		order_details = frappe.db.sql("""
-			select prevdoc_docname as purchase_order, prevdoc_detail_docname as po_detail,
-				(select transaction_date from `tabPurchase Order` where name=item.prevdoc_detail_docname) as purchase_order_date
-			from `tabPurchase Receipt Item` item
-			where
-				parent=%s
-				and item_code=%s
-				and ifnull(prevdoc_detail_docname, '') !=''
-				and ifnull(prevdoc_doctype, '') = 'Purchase Order' and ifnull(prevdoc_detail_docname, '') != ''
-			order by purchase_order_date DESC limit 1
-		""", (d.return_against, d.item_code), as_dict=1)
-
-		if order_details:
-			frappe.db.sql("""
-				update `tabPurchase Receipt Item`
-				set prevdoc_doctype='Purchase Order', prevdoc_docname=%s, prevdoc_detail_docname=%s
-				where name=%s
-			""", (order_details[0].purchase_order, order_details[0].po_detail, d.row_id))
-
-			pr = frappe.get_doc("Purchase Receipt", d.name)
-			pr.update_ordered_and_reserved_qty()
-			pr.update_prevdoc_status()
-
diff --git a/erpnext/patches/v6_0/__init__.py b/erpnext/patches/v6_0/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_0/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_0/default_activity_rate.py b/erpnext/patches/v6_0/default_activity_rate.py
deleted file mode 100644
index cfbfb72..0000000
--- a/erpnext/patches/v6_0/default_activity_rate.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("projects", "doctype", "activity_cost")
-
-	for cost in frappe.db.get_list("Activity Cost", filters = {"employee": ""},
-		fields = ("name", "activity_type", "costing_rate", "billing_rate")):
-		activity_type = frappe.get_doc("Activity Type", cost.activity_type)
-		activity_type.costing_rate = cost.costing_rate
-		activity_type.billing_rate = cost.billing_rate
-		activity_type.save()
-
-		frappe.delete_doc("Activity Cost", cost.name)
diff --git a/erpnext/patches/v6_0/fix_outstanding_amount.py b/erpnext/patches/v6_0/fix_outstanding_amount.py
deleted file mode 100644
index 0de6890..0000000
--- a/erpnext/patches/v6_0/fix_outstanding_amount.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
-
-def execute():
-	for dt, party_field, account_field in (("Sales Invoice", "customer", "debit_to"), 
-			("Purchase Invoice", "supplier", "credit_to")):
-			
-		wrong_invoices = frappe.db.sql("""select name, {0} as account from `tab{1}` 
-			where docstatus=1 and ifnull({2}, '')=''""".format(account_field, dt, party_field))
-			
-		for invoice, account in wrong_invoices:
-			update_outstanding_amt(account, party_field.title(), None, dt, invoice)
\ No newline at end of file
diff --git a/erpnext/patches/v6_0/fix_planned_qty.py b/erpnext/patches/v6_0/fix_planned_qty.py
deleted file mode 100644
index cf7b429..0000000
--- a/erpnext/patches/v6_0/fix_planned_qty.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
-
-def execute():
-	for item_code, warehouse in frappe.db.sql("""select distinct production_item, fg_warehouse
-		from `tabWork Order`"""):
-			if frappe.db.exists("Item", item_code) and frappe.db.exists("Warehouse", warehouse):
-				update_bin_qty(item_code, warehouse, {
-					"planned_qty": get_planned_qty(item_code, warehouse)
-				})
diff --git a/erpnext/patches/v6_0/multi_currency.py b/erpnext/patches/v6_0/multi_currency.py
deleted file mode 100644
index b4c37fc..0000000
--- a/erpnext/patches/v6_0/multi_currency.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# Reload doctype
-	for dt in ("Account", "GL Entry", "Journal Entry",
-		"Journal Entry Account", "Sales Invoice", "Purchase Invoice", "Customer", "Supplier"):
-			frappe.reload_doctype(dt)
-
-	company_list = frappe.get_all("Company", fields=["name", "default_currency", "default_receivable_account"])
-	for company in company_list:
-
-		# update currency in account and gl entry as per company currency
-		frappe.db.sql("""update `tabAccount` set account_currency = %s
-			where ifnull(account_currency, '') = '' and company=%s""", (company.default_currency, company.name))
-
-		# update newly introduced field's value in sales / purchase invoice
-		frappe.db.sql("""
-			update
-				`tabSales Invoice`
-			set
-				base_paid_amount=paid_amount,
-				base_write_off_amount=write_off_amount,
-				party_account_currency=%s
-			where company=%s
-		""", (company.default_currency, company.name))
-
-		frappe.db.sql("""
-			update
-				`tabPurchase Invoice`
-			set
-				base_write_off_amount=write_off_amount,
-				party_account_currency=%s
-			where company=%s
-		""", (company.default_currency, company.name))
-
-		# update exchange rate, debit/credit in account currency in Journal Entry
-		frappe.db.sql("""
-			update `tabJournal Entry Account` jea
-			set exchange_rate=1,
-				debit_in_account_currency=debit,
-				credit_in_account_currency=credit,
-				account_type=(select account_type from `tabAccount` where name=jea.account)
-		""")
-
-		frappe.db.sql("""
-			update `tabJournal Entry Account` jea, `tabJournal Entry` je
-			set account_currency=%s
-			where jea.parent = je.name and je.company=%s
-		""", (company.default_currency, company.name))
-
-		# update debit/credit in account currency in GL Entry
-		frappe.db.sql("""
-			update
-				`tabGL Entry`
-			set
-				debit_in_account_currency=debit,
-				credit_in_account_currency=credit,
-				account_currency=%s
-			where
-				company=%s
-		""", (company.default_currency, company.name))
diff --git a/erpnext/patches/v6_0/set_default_title.py b/erpnext/patches/v6_0/set_default_title.py
deleted file mode 100644
index cceff3f..0000000
--- a/erpnext/patches/v6_0/set_default_title.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Quotation")
-	frappe.db.sql("""update tabQuotation set title = customer_name""")
-
-	frappe.reload_doctype("Sales Order")
-	frappe.db.sql("""update `tabSales Order` set title = customer_name""")
-
-	frappe.reload_doctype("Delivery Note")
-	frappe.db.sql("""update `tabDelivery Note` set title = customer_name""")
-
-	frappe.reload_doctype("Material Request")
-	frappe.db.sql("""update `tabMaterial Request` set title = material_request_type""")
-
-	frappe.reload_doctype("Supplier Quotation")
-	frappe.db.sql("""update `tabSupplier Quotation` set title = supplier_name""")
-
-	frappe.reload_doctype("Purchase Order")
-	frappe.db.sql("""update `tabPurchase Order` set title = supplier_name""")
-
-	frappe.reload_doctype("Purchase Receipt")
-	frappe.db.sql("""update `tabPurchase Receipt` set title = supplier_name""")
-
-	frappe.reload_doctype("Purchase Invoice")
-	frappe.db.sql("""update `tabPurchase Invoice` set title = supplier_name""")
-
-	frappe.reload_doctype("Stock Entry")
-	frappe.db.sql("""update `tabStock Entry` set title = purpose""")
-
-	frappe.reload_doctype("Sales Invoice")
-	frappe.db.sql("""update `tabSales Invoice` set title = customer_name""")
-
-	frappe.reload_doctype("Expense Claim")
-	frappe.db.sql("""update `tabExpense Claim` set title = employee_name""")
diff --git a/erpnext/patches/v6_10/__init__.py b/erpnext/patches/v6_10/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_10/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_10/email_digest_default_quote.py b/erpnext/patches/v6_10/email_digest_default_quote.py
deleted file mode 100644
index 6139f1a..0000000
--- a/erpnext/patches/v6_10/email_digest_default_quote.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Email Digest")
-	frappe.db.sql("update `tabEmail Digest` set add_quote = 1")
diff --git a/erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py b/erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py
deleted file mode 100644
index d7f72b5..0000000
--- a/erpnext/patches/v6_10/fix_billed_amount_in_drop_ship_po.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""update `tabPurchase Order Item` set billed_amt = 0
-			where delivered_by_supplier=1 and docstatus=1""")
-			
-	drop_ship_pos = frappe.db.sql("""select distinct parent from `tabPurchase Order Item` 
-		where delivered_by_supplier=1 and docstatus=1""")
-		
-	for po in drop_ship_pos:
-		invoices = frappe.db.sql("""select distinct parent from `tabPurchase Invoice Item`
-			where purchase_order=%s and docstatus=1""", po[0])
-		if invoices:
-			for inv in invoices:
-				frappe.get_doc("Purchase Invoice", inv[0]).update_qty(update_modified=False)
-		else:
-			frappe.db.sql("""update `tabPurchase Order` set per_billed=0 where name=%s""", po[0])
\ No newline at end of file
diff --git a/erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py b/erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py
deleted file mode 100644
index 9a53b7f..0000000
--- a/erpnext/patches/v6_10/fix_delivery_status_of_drop_ship_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Sales Order Item")
-	for so_name in frappe.db.sql("""select distinct parent from `tabSales Order Item`
-			where delivered_by_supplier=1 and docstatus=1"""):
-		so = frappe.get_doc("Sales Order", so_name[0])
-		so.update_delivery_status()
-		so.set_status(update=True, update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v6_10/fix_jv_total_amount.py b/erpnext/patches/v6_10/fix_jv_total_amount.py
deleted file mode 100644
index 42cb9e9..0000000
--- a/erpnext/patches/v6_10/fix_jv_total_amount.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-# patch all for-print field (total amount) in Journal Entry in 2015
-def execute():
-	for je in frappe.get_all("Journal Entry", filters={"creation": (">", "2015-01-01")}):
-		je = frappe.get_doc("Journal Entry", je.name)
-		original = je.total_amount
-
-		je.set_print_format_fields()
-
-		if je.total_amount != original:
-			je.db_set("total_amount", je.total_amount, update_modified=False)
-			je.db_set("total_amount_in_words", je.total_amount_in_words, update_modified=False)
diff --git a/erpnext/patches/v6_10/fix_ordered_received_billed.py b/erpnext/patches/v6_10/fix_ordered_received_billed.py
deleted file mode 100644
index c81a20e..0000000
--- a/erpnext/patches/v6_10/fix_ordered_received_billed.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	not_null_patch_date = frappe.db.sql("""select date(creation) from `tabPatch Log` where patch='frappe.patches.v6_9.int_float_not_null'""")
-	if not not_null_patch_date:
-		return
-
-	not_null_patch_date = not_null_patch_date[0][0]
-
-	for doctype in ("Purchase Invoice", "Sales Invoice", "Purchase Order", "Delivery Note", "Installation Note", "Delivery Note", "Purchase Receipt"):
-		for name in frappe.db.sql_list("""select name from `tab{doctype}`
-			where docstatus > 0 and (date(creation) >= %(patch_date)s or date(modified) >= %(patch_date)s)""".format(doctype=doctype),
-			{"patch_date": not_null_patch_date}):
-
-			doc = frappe.get_doc(doctype, name)
-			doc.update_qty(update_modified=False)
diff --git a/erpnext/patches/v6_12/__init__.py b/erpnext/patches/v6_12/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_12/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_12/repost_entries_with_target_warehouse.py b/erpnext/patches/v6_12/repost_entries_with_target_warehouse.py
deleted file mode 100644
index fb5eab4..0000000
--- a/erpnext/patches/v6_12/repost_entries_with_target_warehouse.py
+++ /dev/null
@@ -1,175 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-"""
-This patch is written to fix Stock Ledger Entries and GL Entries 
-against Delivery Notes and Sales Invoice where Target Warehouse has been set wrongly
-due to User Permissions on Warehouse.
-
-This cannot be run automatically because we can't take a call that 
-Target Warehouse has been set purposefully or by mistake. 
-Thats why we left it to the users to take the call, and manually run the patch.
-
-This patch has 2 main functions, `check()` and `repost()`. 
-- Run `check` function, to list out all the Sales Orders, Delivery Notes 
-	and Sales Invoice with Target Warehouse.
-- Run `repost` function to remove the Target Warehouse value and repost SLE and GLE again.
-
-To execute this patch run following commands from frappe-bench directory:
-```
-	bench --site [your-site-name] execute erpnext.patches.v6_12.repost_entries_with_target_warehouse.check
-	bench --site [your-site-name] backup
-	bench --site [your-site-name] execute erpnext.patches.v6_12.repost_entries_with_target_warehouse.repost
-```
-
-Exception Handling:
-While reposting, if you get any exception, it will printed on screen. 
-Mostly it can be due to negative stock issue. If that is the case, follow these steps
-	- Ensure that stock is available for those items in the mentioned warehouse on the date mentioned in the error
-	- Execute `repost` funciton again
-"""
-
-def check():
-	so_list = get_affected_sales_order()
-	dn_list = get_affected_delivery_notes()
-	si_list = get_affected_sales_invoice()
-	
-	if so_list or dn_list or si_list:
-		print("Entries with Target Warehouse:")
-		
-		if so_list:
-			print("Sales Order")
-			print(so_list)
-
-		if dn_list:
-			print("Delivery Notes")
-			print([d.name for d in dn_list])
-
-		if si_list:
-			print("Sales Invoice")
-			print([d.name for d in si_list])
-		
-		
-def repost():
-	dn_failed_list, si_failed_list = [], []
-	repost_dn(dn_failed_list)
-	repost_si(si_failed_list)
-	repost_so()
-	frappe.db.commit()
-	
-	if dn_failed_list:
-		print("-"*40)
-		print("Delivery Note Failed to Repost")
-		print(dn_failed_list)
-
-	if si_failed_list:
-		print("-"*40)
-		print("Sales Invoice Failed to Repost")
-		print(si_failed_list)
-		print()
-		
-		print("""
-If above Delivery Notes / Sales Invoice failed due to negative stock, follow these steps:
-	- Ensure that stock is available for those items in the mentioned warehouse on the date mentioned in the error
-	- Run this patch again
-""")
-	
-def repost_dn(dn_failed_list):
-	dn_list = get_affected_delivery_notes()
-		
-	if dn_list:
-		print("-"*40)
-		print("Reposting Delivery Notes")
-
-	for dn in dn_list:
-		if dn.docstatus == 0:
-			continue
-			
-		print(dn.name)
-	
-		try:
-			dn_doc = frappe.get_doc("Delivery Note", dn.name)
-			dn_doc.docstatus = 2
-			dn_doc.update_prevdoc_status()
-			dn_doc.update_stock_ledger()
-			dn_doc.cancel_packing_slips()
-			frappe.db.sql("""delete from `tabGL Entry` 
-				where voucher_type='Delivery Note' and voucher_no=%s""", dn.name)
-
-			frappe.db.sql("update `tabDelivery Note Item` set target_warehouse='' where parent=%s", dn.name)
-			dn_doc = frappe.get_doc("Delivery Note", dn.name)
-			dn_doc.docstatus = 1
-			dn_doc.on_submit()
-			frappe.db.commit()
-		except Exception:
-			dn_failed_list.append(dn.name)
-			frappe.local.stockledger_exceptions = None
-			print(frappe.get_traceback())
-			frappe.db.rollback()
-		
-	frappe.db.sql("update `tabDelivery Note Item` set target_warehouse='' where docstatus=0")
-
-def repost_si(si_failed_list):
-	si_list = get_affected_sales_invoice()
-
-	if si_list:
-		print("-"*40)
-		print("Reposting Sales Invoice")
-	
-	for si in si_list:
-		if si.docstatus == 0:
-			continue
-		
-		print(si.name)
-	
-		try:
-			si_doc = frappe.get_doc("Sales Invoice", si.name)
-			si_doc.docstatus = 2
-			si_doc.update_stock_ledger()
-			frappe.db.sql("""delete from `tabGL Entry` 
-				where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
-			
-			frappe.db.sql("update `tabSales Invoice Item` set target_warehouse='' where parent=%s", si.name)
-			si_doc = frappe.get_doc("Sales Invoice", si.name)
-			si_doc.docstatus = 1
-			si_doc.update_stock_ledger()
-			si_doc.make_gl_entries()
-			frappe.db.commit()
-		except Exception:
-			si_failed_list.append(si.name)
-			frappe.local.stockledger_exceptions = None
-			print(frappe.get_traceback())
-			frappe.db.rollback()
-		
-	frappe.db.sql("update `tabSales Invoice Item` set target_warehouse='' where docstatus=0")
-	
-def repost_so():
-	so_list = get_affected_sales_order()
-	
-	frappe.db.sql("update `tabSales Order Item` set target_warehouse=''")
-	
-	if so_list:
-		print("-"*40)
-		print("Sales Order reposted")
-	
-	
-def get_affected_delivery_notes():
-	return frappe.db.sql("""select distinct dn.name, dn.docstatus
-		from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
-		where dn.name=dn_item.parent and dn.docstatus < 2
-			and dn_item.target_warehouse is not null and dn_item.target_warehouse != '' 
-		order by dn.posting_date asc""", as_dict=1)
-			
-def get_affected_sales_invoice():
-	return frappe.db.sql("""select distinct si.name, si.docstatus
-		from `tabSales Invoice` si, `tabSales Invoice Item` si_item
-		where si.name=si_item.parent and si.docstatus < 2 and si.update_stock=1
-			and si_item.target_warehouse is not null and si_item.target_warehouse != '' 
-		order by si.posting_date asc""", as_dict=1)
-		
-def get_affected_sales_order():
-	return frappe.db.sql_list("""select distinct parent from `tabSales Order Item` 
-		where target_warehouse is not null and target_warehouse != '' and docstatus <2""")
\ No newline at end of file
diff --git a/erpnext/patches/v6_12/set_overdue_tasks.py b/erpnext/patches/v6_12/set_overdue_tasks.py
deleted file mode 100644
index 7dbb8ba..0000000
--- a/erpnext/patches/v6_12/set_overdue_tasks.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Task")
-
-	from erpnext.projects.doctype.task.task import set_tasks_as_overdue
-	set_tasks_as_overdue()
diff --git a/erpnext/patches/v6_16/__init__.py b/erpnext/patches/v6_16/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_16/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_16/create_manufacturer_records.py b/erpnext/patches/v6_16/create_manufacturer_records.py
deleted file mode 100644
index 5ae65f0..0000000
--- a/erpnext/patches/v6_16/create_manufacturer_records.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cstr
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "manufacturer")
-	frappe.reload_doctype("Item")
-	
-	for d in frappe.db.sql("""select distinct manufacturer from tabItem 
-		where ifnull(manufacturer, '') != '' and disabled=0"""):
-			manufacturer_name = cstr(d[0]).strip()
-			if manufacturer_name and not frappe.db.exists("Manufacturer", manufacturer_name):
-				man = frappe.new_doc("Manufacturer")
-				man.short_name = manufacturer_name
-				man.full_name = manufacturer_name
-				man.save()
diff --git a/erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py b/erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py
deleted file mode 100644
index 481f130..0000000
--- a/erpnext/patches/v6_16/update_billing_status_in_dn_and_pr.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt in ("Delivery Note", "Purchase Receipt"):
-		frappe.reload_doctype(dt)
-		frappe.reload_doctype(dt + " Item")
-
-	# Update billed_amt in DN and PR which are not against any order
-	for d in frappe.db.sql("""select name from `tabDelivery Note Item` item
-		where (so_detail is null or so_detail = '') and docstatus=1""", as_dict=1):
-
-		billed_amt = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item`
-			where dn_detail=%s and docstatus=1""", d.name)
-		billed_amt = billed_amt and billed_amt[0][0] or 0
-		frappe.db.set_value("Delivery Note Item", d.name, "billed_amt", billed_amt, update_modified=False)
-
-		frappe.db.commit()
-
-	# Update billed_amt in DN and PR which are not against any order
-	for d in frappe.db.sql("""select name from `tabPurchase Receipt Item` item
-		where (purchase_order_item is null or purchase_order_item = '') and docstatus=1""", as_dict=1):
-
-		billed_amt = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
-			where pr_detail=%s and docstatus=1""", d.name)
-		billed_amt = billed_amt and billed_amt[0][0] or 0
-		frappe.db.set_value("Purchase Receipt Item", d.name, "billed_amt", billed_amt, update_modified=False)
-
-		frappe.db.commit()
-
-	for dt in ("Delivery Note", "Purchase Receipt"):
-		# Update billed amt which are against order or invoice
-		# Update billing status for all
-		for d in frappe.db.sql("select name from `tab{0}` where docstatus=1".format(dt), as_dict=1):
-			doc = frappe.get_doc(dt, d.name)
-			doc.update_billing_status(update_modified=False)
-			doc.set_status(update=True, update_modified=False)
-
-			frappe.db.commit()
diff --git a/erpnext/patches/v6_19/__init__.py b/erpnext/patches/v6_19/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_19/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_19/comment_feed_communication.py b/erpnext/patches/v6_19/comment_feed_communication.py
deleted file mode 100644
index bc41c2d..0000000
--- a/erpnext/patches/v6_19/comment_feed_communication.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.patches.v6_19.comment_feed_communication import update_timeline_doc_for
-
-def execute():
-	for doctype in ("Customer", "Supplier", "Employee", "Project"):
-		update_timeline_doc_for(doctype)
diff --git a/erpnext/patches/v6_2/__init__.py b/erpnext/patches/v6_2/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_2/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py b/erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py
deleted file mode 100644
index b0cfc3d..0000000
--- a/erpnext/patches/v6_2/fix_missing_default_taxes_and_lead.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# remove missing lead
-	for customer in frappe.db.sql_list("""select name from `tabCustomer`
-		where ifnull(lead_name, '')!='' and not exists (select name from `tabLead` where name=`tabCustomer`.lead_name)"""):
-		frappe.db.set_value("Customer", customer, "lead_name", None)
-
-	# remove missing default taxes
-	for customer in frappe.db.sql_list("""select name from `tabCustomer`
-		where ifnull(default_taxes_and_charges, '')!='' and not exists (
-			select name from `tabSales Taxes and Charges Template` where name=`tabCustomer`.default_taxes_and_charges
-		)"""):
-		c = frappe.get_doc("Customer", customer)
-		c.default_taxes_and_charges = None
-		c.save()
-
-	for supplier in frappe.db.sql_list("""select name from `tabSupplier`
-		where ifnull(default_taxes_and_charges, '')!='' and not exists (
-			select name from `tabPurchase Taxes and Charges Template` where name=`tabSupplier`.default_taxes_and_charges
-		)"""):
-		c = frappe.get_doc("Supplier", supplier)
-		c.default_taxes_and_charges = None
-		c.save()
diff --git a/erpnext/patches/v6_2/remove_newsletter_duplicates.py b/erpnext/patches/v6_2/remove_newsletter_duplicates.py
deleted file mode 100644
index f9d1547..0000000
--- a/erpnext/patches/v6_2/remove_newsletter_duplicates.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	duplicates = frappe.db.sql("""select email_group, email, count(name)
-		from `tabEmail Group Member`
-		group by email_group, email
-		having count(name) > 1""")
-
-	# delete all duplicates except 1
-	for email_group, email, count in duplicates:
-		frappe.db.sql("""delete from `tabEmail Group Member`
-			where email_group=%s and email=%s limit %s""", (email_group, email, count-1))
diff --git a/erpnext/patches/v6_20/__init__.py b/erpnext/patches/v6_20/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_20/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_20/set_party_account_currency_in_orders.py b/erpnext/patches/v6_20/set_party_account_currency_in_orders.py
deleted file mode 100644
index ae7ad95..0000000
--- a/erpnext/patches/v6_20/set_party_account_currency_in_orders.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ("Sales Order", "Purchase Order"):
-		frappe.reload_doctype(doctype)
-		
-		for order in frappe.db.sql("""select name, {0} as party from `tab{1}` 
-			where advance_paid > 0 and docstatus=1"""
-			.format(("customer" if doctype=="Sales Order" else "supplier"), doctype), as_dict=1):
-			
-			party_account_currency = frappe.db.get_value("Journal Entry Account", {
-				"reference_type": doctype,
-				"reference_name": order.name,
-				"party": order.party,
-				"docstatus": 1,
-				"is_advance": "Yes"
-			}, "account_currency")
-			
-			frappe.db.set_value(doctype, order.name, "party_account_currency", party_account_currency)
-		
\ No newline at end of file
diff --git a/erpnext/patches/v6_20x/__init__.py b/erpnext/patches/v6_20x/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_20x/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_20x/remove_customer_supplier_roles.py b/erpnext/patches/v6_20x/remove_customer_supplier_roles.py
deleted file mode 100644
index a651576..0000000
--- a/erpnext/patches/v6_20x/remove_customer_supplier_roles.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("buying", "doctype", "request_for_quotation_supplier")
-	frappe.reload_doc("buying", "doctype", "request_for_quotation_item")
-	frappe.reload_doc("buying", "doctype", "request_for_quotation")
-	frappe.reload_doc("projects", "doctype", "timesheet")
-	
-	for role in ('Customer', 'Supplier'):
-		frappe.db.sql('''delete from `tabHas Role`
-			where role=%s and parent in ("Administrator", "Guest")''', role)
-
-		if not frappe.db.sql('select name from `tabHas Role` where role=%s', role):
-
-			# delete DocPerm
-			for doctype in frappe.db.sql('select parent from tabDocPerm where role=%s', role):
-				d = frappe.get_doc("DocType", doctype[0])
-				d.permissions = [p for p in d.permissions if p.role != role]
-				d.save()
-
-			# delete Role
-			frappe.delete_doc_if_exists('Role', role)
diff --git a/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py b/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py
deleted file mode 100644
index d440c68..0000000
--- a/erpnext/patches/v6_20x/remove_fiscal_year_from_holiday_list.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Holiday List")
-
-	default_holiday_list = frappe.db.get_value("Holiday List", {"is_default": 1})
-	if default_holiday_list:
-		for company in frappe.get_all("Company", fields=["name", "default_holiday_list"]):
-			if not company.default_holiday_list:
-				frappe.db.set_value("Company", company.name, "default_holiday_list", default_holiday_list)
-
-
-	fiscal_years = frappe._dict((fy.name, fy) for fy in frappe.get_all("Fiscal Year", fields=["name", "year_start_date", "year_end_date"]))
-
-	for holiday_list in frappe.get_all("Holiday List", fields=["name", "fiscal_year"]):
-		fy = fiscal_years[holiday_list.fiscal_year]
-		frappe.db.set_value("Holiday List", holiday_list.name, "from_date", fy.year_start_date)
-		frappe.db.set_value("Holiday List", holiday_list.name, "to_date", fy.year_end_date)
diff --git a/erpnext/patches/v6_20x/rename_project_name_to_project.py b/erpnext/patches/v6_20x/rename_project_name_to_project.py
deleted file mode 100644
index 49ec9d2..0000000
--- a/erpnext/patches/v6_20x/rename_project_name_to_project.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	
-	doc_list = ["Work Order", "BOM", "Purchase Invoice Item", "Sales Invoice", 
-		"Purchase Order Item", "Stock Entry", "Delivery Note", "Sales Order", 
-		"Purchase Receipt Item", "Supplier Quotation Item"]
-	
-	for doctype in doc_list:
-		frappe.reload_doctype(doctype)
-		rename_field(doctype, "project_name", "project")
-	
\ No newline at end of file
diff --git a/erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py b/erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py
deleted file mode 100644
index 8369fea..0000000
--- a/erpnext/patches/v6_20x/repost_valuation_rate_for_negative_inventory.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint
-from erpnext.stock.stock_balance import repost
-
-def execute():
-	if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")):
-		repost(only_actual=True)
\ No newline at end of file
diff --git a/erpnext/patches/v6_20x/set_compact_print.py b/erpnext/patches/v6_20x/set_compact_print.py
deleted file mode 100644
index 495407f..0000000
--- a/erpnext/patches/v6_20x/set_compact_print.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from erpnext.setup.install import create_compact_item_print_custom_field
-
-def execute():
-	create_compact_item_print_custom_field()
-	frappe.db.set_value("Print Settings", None, "compact_item_print", 1)
diff --git a/erpnext/patches/v6_20x/update_product_bundle_description.py b/erpnext/patches/v6_20x/update_product_bundle_description.py
deleted file mode 100644
index 1fac44b..0000000
--- a/erpnext/patches/v6_20x/update_product_bundle_description.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import sanitize_html
-
-def execute():
-	for product_bundle in frappe.get_all('Product Bundle'):
-		doc = frappe.get_doc('Product Bundle', product_bundle.name)
-		for item in doc.items:
-			if item.description:
-				description = sanitize_html(item.description)
-				item.db_set('description', description, update_modified=False)
diff --git a/erpnext/patches/v6_21/__init__.py b/erpnext/patches/v6_21/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_21/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_21/fix_reorder_level.py b/erpnext/patches/v6_21/fix_reorder_level.py
deleted file mode 100644
index 82a35eb..0000000
--- a/erpnext/patches/v6_21/fix_reorder_level.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-from erpnext.stock.doctype.item.item import DuplicateReorderRows
-
-def execute():
-	if frappe.db.has_column("Item", "re_order_level"):
-		for item in frappe.db.sql("""select name, default_warehouse, re_order_level, re_order_qty
-			from tabItem
-			where ifnull(re_order_level, 0) != 0
-				and ifnull(re_order_qty, 0) != 0""", as_dict=1):
-
-			item_doc = frappe.get_doc("Item", item.name)
-			item_doc.append("reorder_levels", {
-				"warehouse": item.default_warehouse,
-				"warehouse_reorder_level": item.re_order_level,
-				"warehouse_reorder_qty": item.re_order_qty,
-				"material_request_type": "Purchase"
-			})
-
-			try:
-				item_doc.save()
-			except DuplicateReorderRows:
-				pass
diff --git a/erpnext/patches/v6_21/rename_material_request_fields.py b/erpnext/patches/v6_21/rename_material_request_fields.py
deleted file mode 100644
index 07be27a..0000000
--- a/erpnext/patches/v6_21/rename_material_request_fields.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	frappe.reload_doc('stock', 'doctype', 'material_request_item')
-	rename_field("Material Request Item", "sales_order_no", "sales_order")
-	
-	frappe.reload_doc('support', 'doctype', 'maintenance_schedule_item')
-	rename_field("Maintenance Schedule Item", "prevdoc_docname", "sales_order")
-	
\ No newline at end of file
diff --git a/erpnext/patches/v6_23/__init__.py b/erpnext/patches/v6_23/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_23/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_23/update_stopped_status_to_closed.py b/erpnext/patches/v6_23/update_stopped_status_to_closed.py
deleted file mode 100644
index 79d1e0a..0000000
--- a/erpnext/patches/v6_23/update_stopped_status_to_closed.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt in ("Sales Order", "Purchase Order"):
-		frappe.db.sql("update `tab{0}` set status='Closed' where status='Stopped'".format(dt))
\ No newline at end of file
diff --git a/erpnext/patches/v6_24/__init__.py b/erpnext/patches/v6_24/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_24/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py b/erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py
deleted file mode 100644
index 1dd8083..0000000
--- a/erpnext/patches/v6_24/map_customer_address_to_shipping_address_on_po.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Purchase Order")
-	
-	if not frappe.db.has_column("Purchase Order", "shipping_address"):
-		return
-		
-	if not frappe.db.has_column("Purchase Order", "customer_address"):
-		return
-	
-	frappe.db.sql("""update `tabPurchase Order` set shipping_address=customer_address, 
-		shipping_address_display=customer_address_display""")
-	
-	frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/patches/v6_24/set_recurring_id.py b/erpnext/patches/v6_24/set_recurring_id.py
deleted file mode 100644
index 527a2fd..0000000
--- a/erpnext/patches/v6_24/set_recurring_id.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ('Sales Order', 'Purchase Order', 'Sales Invoice',
-		'Purchase Invoice'):
-		frappe.reload_doctype(doctype)
-		frappe.db.sql('''update `tab{0}` set submit_on_creation=1, notify_by_email=1
-			where is_recurring=1'''.format(doctype))
-		frappe.db.sql('''update `tab{0}` set notify_by_email=1
-			where is_recurring=1'''.format(doctype))
-		frappe.db.sql('''update `tab{0}` set recurring_id = name
-			where is_recurring=1 and ifnull(recurring_id, '') = "" '''.format(doctype))
diff --git a/erpnext/patches/v6_27/__init__.py b/erpnext/patches/v6_27/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_27/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_27/fix_recurring_order_status.py b/erpnext/patches/v6_27/fix_recurring_order_status.py
deleted file mode 100644
index 5843c9f..0000000
--- a/erpnext/patches/v6_27/fix_recurring_order_status.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doc in (
-		{
-			"doctype": "Sales Order",
-			"stock_doctype": "Delivery Note",
-			"invoice_doctype": "Sales Invoice",
-			"stock_doctype_ref_field": "against_sales_order",
-			"invoice_ref_field": "sales_order",
-			"qty_field": "delivered_qty"
-		},
-		{
-			"doctype": "Purchase Order",
-			"stock_doctype": "Purchase Receipt",
-			"invoice_doctype": "Purchase Invoice",
-			"stock_doctype_ref_field": "prevdoc_docname",
-			"invoice_ref_field": "purchase_order",
-			"qty_field": "received_qty"
-		}):
-
-		order_list = frappe.db.sql("""select name from `tab{0}`
-			where docstatus=1 and is_recurring=1
-			and ifnull(recurring_id, '') != name and creation >= '2016-01-25'"""
-			.format(doc["doctype"]), as_dict=1)
-
-		for order in order_list:
-			frappe.db.sql("""update `tab{0} Item`
-				set {1}=0, billed_amt=0 where parent=%s""".format(doc["doctype"],
-					doc["qty_field"]), order.name)
-
-			# Check against Delivery Note and Purchase Receipt
-			stock_doc_list = frappe.db.sql("""select distinct parent from `tab{0} Item`
-				where docstatus=1 and ifnull({1}, '')=%s"""
-				.format(doc["stock_doctype"], doc["stock_doctype_ref_field"]), order.name)
-
-			if stock_doc_list:
-				for dn in stock_doc_list:
-					frappe.get_doc(doc["stock_doctype"], dn[0]).update_qty(update_modified=False)
-
-			# Check against Invoice
-			invoice_list = frappe.db.sql("""select distinct parent from `tab{0} Item`
-				where docstatus=1 and ifnull({1}, '')=%s"""
-				.format(doc["invoice_doctype"], doc["invoice_ref_field"]), order.name)
-
-			if invoice_list:
-				for dn in invoice_list:
-					frappe.get_doc(doc["invoice_doctype"], dn[0]).update_qty(update_modified=False)
-
-			frappe.get_doc(doc["doctype"], order.name).set_status(update=True, update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v6_3/__init__.py b/erpnext/patches/v6_3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_3/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_3/convert_applicable_territory.py b/erpnext/patches/v6_3/convert_applicable_territory.py
deleted file mode 100644
index 231a483..0000000
--- a/erpnext/patches/v6_3/convert_applicable_territory.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "price_list_country")
-	frappe.reload_doc("accounts", "doctype", "shipping_rule_country")
-	frappe.reload_doctype("Price List")
-	frappe.reload_doctype("Shipping Rule")
-	frappe.reload_doctype("shopping_cart", "doctype", "shopping_cart_settings")
-
-	# for price list
-	countries = frappe.db.sql_list("select name from tabCountry")
-
-	for doctype in ("Price List", "Shipping Rule"):
-		for at in frappe.db.sql("""select name, parent, territory from `tabApplicable Territory` where
-			parenttype = %s """, doctype, as_dict=True):
-			if at.territory in countries:
-				parent = frappe.get_doc(doctype, at.parent)
-				if not parent.countries:
-					parent.append("countries", {"country": at.territory})
-				parent.save()
-
-
-	frappe.delete_doc("DocType", "Applicable Territory")
diff --git a/erpnext/patches/v6_4/__init__.py b/erpnext/patches/v6_4/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_4/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_4/email_digest_update.py b/erpnext/patches/v6_4/email_digest_update.py
deleted file mode 100644
index 8342b7f..0000000
--- a/erpnext/patches/v6_4/email_digest_update.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Email Digest")
-	frappe.db.sql("""update `tabEmail Digest` set expense_year_to_date =
-		income_year_to_date""")
-
-	if frappe.db.exists("Email Digest", "Scheduler Errors"):
-		frappe.delete_doc("Email Digest", "Scheduler Errors")
diff --git a/erpnext/patches/v6_4/fix_duplicate_bins.py b/erpnext/patches/v6_4/fix_duplicate_bins.py
deleted file mode 100644
index 77d0527..0000000
--- a/erpnext/patches/v6_4/fix_duplicate_bins.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import repost_stock
-
-def execute():
-	bins = frappe.db.sql("""select item_code, warehouse, count(*) from `tabBin` 
-		group by item_code, warehouse having count(*) > 1""", as_dict=True)
-		
-	for d in bins:
-		try:
-			frappe.db.sql("delete from tabBin where item_code=%s and warehouse=%s", (d.item_code, d.warehouse))
-		
-			repost_stock(d.item_code, d.warehouse, allow_zero_rate=True, only_actual=False, only_bin=True)
-			
-			frappe.db.commit()
-		except:
-			frappe.db.rollback()
\ No newline at end of file
diff --git a/erpnext/patches/v6_4/fix_expense_included_in_valuation.py b/erpnext/patches/v6_4/fix_expense_included_in_valuation.py
deleted file mode 100644
index 7ed15ab..0000000
--- a/erpnext/patches/v6_4/fix_expense_included_in_valuation.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-from frappe.utils import cstr
-
-def execute():
-	for company in frappe.db.sql("select name, expenses_included_in_valuation from tabCompany", as_dict=1):
-		frozen_date = get_frozen_date(company.name, company.expenses_included_in_valuation)
-
-		# Purchase Invoices after frozen date
-		# which are not against Receipt, but valuation related tax is there
-		pi_list = frappe.db.sql("""
-			select distinct pi.name
-			from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
-			where
-				pi.name = pi_item.parent
-				and pi.company = %s
-				and pi.posting_date > %s
-				and pi.docstatus = 1
-				and pi.is_opening = 'No'
-				and (pi_item.item_tax_amount is not null and pi_item.item_tax_amount > 0)
-				and (pi_item.purchase_receipt is null or pi_item.purchase_receipt = '')
-				and (pi_item.item_code is not null and pi_item.item_code != '')
-				and exists(select name from `tabItem` where name=pi_item.item_code and is_stock_item=1)
-		""", (company.name, frozen_date), as_dict=1)
-
-		for pi in pi_list:
-			# Check whether gle exists for Expenses Included in Valuation account against the PI
-			gle_for_expenses_included_in_valuation = frappe.db.sql("""select name from `tabGL Entry`
-				where voucher_type='Purchase Invoice' and voucher_no=%s and account=%s""",
-				(pi.name, company.expenses_included_in_valuation))
-
-			if gle_for_expenses_included_in_valuation:
-				print(pi.name)
-
-				frappe.db.sql("""delete from `tabGL Entry`
-					where voucher_type='Purchase Invoice' and voucher_no=%s""", pi.name)
-
-				purchase_invoice = frappe.get_doc("Purchase Invoice", pi.name)
-
-				# some old entries have missing expense accounts
-				if purchase_invoice.against_expense_account:
-					expense_account = purchase_invoice.against_expense_account.split(",")
-					if len(expense_account) == 1:
-						expense_account = expense_account[0]
-						for item in purchase_invoice.items:
-							if not item.expense_account:
-								item.db_set("expense_account", expense_account, update_modified=False)
-
-				purchase_invoice.make_gl_entries()
-
-def get_frozen_date(company, account):
-	# Accounting frozen upto
-	accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
-
-	# Last adjustment entry to correct Expenses Included in Valuation account balance
-	last_adjustment_entry = frappe.db.sql("""select posting_date from `tabGL Entry`
-		where account=%s and company=%s and voucher_type = 'Journal Entry'
-		order by posting_date desc limit 1""", (account, company))
-
-	last_adjustment_date = cstr(last_adjustment_entry[0][0]) if last_adjustment_entry else None
-
-	# Last period closing voucher
-	last_closing_entry = frappe.db.sql("""select posting_date from `tabGL Entry`
-		where company=%s and voucher_type = 'Period Closing Voucher'
-		order by posting_date desc limit 1""", company)
-
-	last_closing_date = cstr(last_closing_entry[0][0]) if last_closing_entry else None
-
-	frozen_date = max([accounts_frozen_upto, last_adjustment_date, last_closing_date])
-
-	return frozen_date or '1900-01-01'
diff --git a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py b/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
deleted file mode 100644
index b53412d..0000000
--- a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Sales Invoice Advance")
-	frappe.reload_doctype("Purchase Invoice Advance")
-	
-	je_rows = frappe.db.sql("""
-		select name, parent, reference_type, reference_name, debit, credit
-		from `tabJournal Entry Account`
-		where docstatus=1 and date(modified) >= '2015-09-17'
-			and ((ifnull(debit_in_account_currency, 0)*exchange_rate != ifnull(debit, 0))
-			or (ifnull(credit_in_account_currency, 0)*exchange_rate != ifnull(credit, 0)))
-		order by parent
-	""", as_dict=True)
-
-	journal_entries = []
-	
-	for d in je_rows:
-		if d.parent not in journal_entries:
-			journal_entries.append(d.parent)
-
-		is_advance_entry=None
-		if d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name:
-			is_advance_entry = frappe.db.sql("""select name from `tab{0}` 
-				where reference_name=%s and reference_row=%s 
-					and ifnull(allocated_amount, 0) > 0 and docstatus=1"""
-				.format(d.reference_type + " Advance"), (d.parent, d.name))
-				
-		if is_advance_entry or not (d.debit or d.credit):
-			frappe.db.sql("""
-				update `tabJournal Entry Account`
-				set	debit=debit_in_account_currency*exchange_rate,
-					credit=credit_in_account_currency*exchange_rate
-				where name=%s""", d.name)
-		else:
-			frappe.db.sql("""
-				update `tabJournal Entry Account`
-				set debit_in_account_currency=debit/exchange_rate,
-					credit_in_account_currency=credit/exchange_rate
-				where name=%s""", d.name)
-
-	for d in journal_entries:
-		print(d)
-		# delete existing gle
-		frappe.db.sql("delete from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s", d)
-
-		# repost gl entries
-		je = frappe.get_doc("Journal Entry", d)
-		je.make_gl_entries()
\ No newline at end of file
diff --git a/erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py b/erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py
deleted file mode 100644
index f27489e..0000000
--- a/erpnext/patches/v6_4/fix_modified_in_sales_order_and_purchase_order.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ("Sales Order", "Purchase Order"):
-		data = frappe.db.sql("""select parent, modified_by, modified
-			from `tab{doctype} Item` where docstatus=1 group by parent""".format(doctype=doctype), as_dict=True)
-		for item in data:
-			frappe.db.sql("""update `tab{doctype}` set modified_by=%(modified_by)s, modified=%(modified)s
-				where name=%(parent)s""".format(doctype=doctype), item)
diff --git a/erpnext/patches/v6_4/fix_sales_order_maintenance_status.py b/erpnext/patches/v6_4/fix_sales_order_maintenance_status.py
deleted file mode 100644
index 50aa9e5..0000000
--- a/erpnext/patches/v6_4/fix_sales_order_maintenance_status.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doc in frappe.get_all("Sales Order", filters={"docstatus": 1,
-		"order_type": "Maintenance"}):
-		doc = frappe.get_doc("Sales Order", doc.name)
-		doc.set_status(update=True)
diff --git a/erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py b/erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py
deleted file mode 100644
index 746a990..0000000
--- a/erpnext/patches/v6_4/fix_status_in_sales_and_purchase_order.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ("Sales Order", "Purchase Order"):
-		for doc in frappe.get_all(doctype, filters={"docstatus": 1}):
-			doc = frappe.get_doc(doctype, doc.name)
-			doc.set_status(update=True)
diff --git a/erpnext/patches/v6_4/make_image_thumbnail.py b/erpnext/patches/v6_4/make_image_thumbnail.py
deleted file mode 100644
index 2c86e8a..0000000
--- a/erpnext/patches/v6_4/make_image_thumbnail.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("File")
-	frappe.reload_doctype("Item")
-	for item in frappe.get_all("Item", fields=("name", "website_image", "thumbnail")):
-		if item.website_image and not item.thumbnail:
-			item_doc = frappe.get_doc("Item", item.name)
-			try:
-				item_doc.make_thumbnail()
-				if item_doc.thumbnail:
-					item_doc.db_set("thumbnail", item_doc.thumbnail, update_modified=False)
-			except Exception:
-				print("Unable to make thumbnail for {0}".format(item.website_image.encode("utf-8")))
diff --git a/erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py b/erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py
deleted file mode 100644
index 1319b53..0000000
--- a/erpnext/patches/v6_4/repost_gle_for_journal_entries_where_reference_name_missing.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe
-
-def execute():
-	je_list = frappe.db.sql_list("""select distinct parent from `tabJournal Entry Account` je
-		where docstatus=1 and ifnull(reference_name, '') !='' and creation > '2015-03-01'
-			and not exists(select name from `tabGL Entry` 
-				where voucher_type='Journal Entry' and voucher_no=je.parent 
-				and against_voucher_type=je.reference_type 
-				and against_voucher=je.reference_name)""")
-
-	for d in je_list:
-		print(d)
-		
-		# delete existing gle
-		frappe.db.sql("delete from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s", d)
-
-		# repost gl entries
-		je = frappe.get_doc("Journal Entry", d)
-		je.make_gl_entries()
\ No newline at end of file
diff --git a/erpnext/patches/v6_4/round_status_updater_percentages.py b/erpnext/patches/v6_4/round_status_updater_percentages.py
deleted file mode 100644
index 900e906..0000000
--- a/erpnext/patches/v6_4/round_status_updater_percentages.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype, fieldname in (
-		("Sales Order", "per_billed"),
-		("Sales Order", "per_delivered"),
-		("Delivery Note", "per_installed"),
-		("Purchase Order", "per_billed"),
-		("Purchase Order", "per_received"),
-		("Material Request", "per_ordered"),
-	):
-		frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=round(`{fieldname}`, 2)""".format(
-			doctype=doctype, fieldname=fieldname))
diff --git a/erpnext/patches/v6_4/set_user_in_contact.py b/erpnext/patches/v6_4/set_user_in_contact.py
deleted file mode 100644
index 7e8a6ee..0000000
--- a/erpnext/patches/v6_4/set_user_in_contact.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Contact")
-	frappe.db.sql("""update tabContact, tabUser set tabContact.user = tabUser.name
-		where tabContact.email_id = tabUser.email""")
diff --git a/erpnext/patches/v6_5/__init__.py b/erpnext/patches/v6_5/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_5/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_5/show_in_website_for_template_item.py b/erpnext/patches/v6_5/show_in_website_for_template_item.py
deleted file mode 100644
index af6e830..0000000
--- a/erpnext/patches/v6_5/show_in_website_for_template_item.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-import frappe.website.render
-
-def execute():
-	for item_code in frappe.db.sql_list("""select distinct variant_of from `tabItem`
-		where variant_of is not null and variant_of !='' and show_in_website=1"""):
-
-		item = frappe.get_doc("Item", item_code)
-		item.db_set("show_in_website", 1, update_modified=False)
-
-		item.make_route()
-		item.db_set("route", item.route, update_modified=False)
-
-	frappe.website.render.clear_cache()
diff --git a/erpnext/patches/v6_6/__init__.py b/erpnext/patches/v6_6/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v6_6/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v6_6/fix_website_image.py b/erpnext/patches/v6_6/fix_website_image.py
deleted file mode 100644
index cc3e2d8..0000000
--- a/erpnext/patches/v6_6/fix_website_image.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from __future__ import print_function, unicode_literals
-import frappe
-from frappe.utils import encode
-
-def execute():
-	"""Fix the File records created via item.py even if the website_image file didn't exist"""
-	for item in frappe.db.sql_list("""select name from `tabItem`
-		where website_image is not null and website_image != ''
-			and website_image like '/files/%'
-			and exists (
-				select name from `tabFile`
-					where attached_to_doctype='Item'
-					and attached_to_name=`tabItem`.name
-					and file_url=`tabItem`.website_image
-					and (file_name is null or file_name = '')
-				)"""):
-
-		item = frappe.get_doc("Item", item)
-		file = frappe.get_doc("File", {
-			"attached_to_doctype": "Item",
-			"attached_to_name": item.name,
-			"file_url": item.website_image
-		})
-
-		try:
-			file.validate_file()
-		except IOError:
-			print(encode(item.website_image), "does not exist")
-			file.delete()
-			item.db_set("website_image", None, update_modified=False)
-
-
diff --git a/erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py b/erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py
deleted file mode 100644
index 11c582f..0000000
--- a/erpnext/patches/v6_6/remove_fiscal_year_from_leave_allocation.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Leave Allocation")
-	if frappe.db.has_column("Leave Allocation", "fiscal_year"):
-		for leave_allocation in frappe.db.sql("select name, fiscal_year from `tabLeave Allocation`", as_dict=True):
-			dates = frappe.db.get_value("Fiscal Year", leave_allocation["fiscal_year"],
-				["year_start_date", "year_end_date"])
-
-			if dates:
-				year_start_date, year_end_date = dates
-
-				frappe.db.sql("""update `tabLeave Allocation`
-					set from_date=%s, to_date=%s where name=%s""",
-					(year_start_date, year_end_date, leave_allocation["name"]))
-
diff --git a/erpnext/patches/v6_8/__init__.py b/erpnext/patches/v6_8/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v6_8/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v6_8/make_webform_standard.py b/erpnext/patches/v6_8/make_webform_standard.py
deleted file mode 100644
index 2cc16a2..0000000
--- a/erpnext/patches/v6_8/make_webform_standard.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	pass
-
-	# done via fixtures
-
-	# frappe.reload_doctype("Web Form")
-	# frappe.delete_doc("Web Form", "Issues")
-	# frappe.delete_doc("Web Form", "Addresses")
-
-	# from erpnext.setup.install import add_web_forms
-	# add_web_forms()
diff --git a/erpnext/patches/v6_8/move_drop_ship_to_po_items.py b/erpnext/patches/v6_8/move_drop_ship_to_po_items.py
deleted file mode 100644
index 7184dee..0000000
--- a/erpnext/patches/v6_8/move_drop_ship_to_po_items.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Purchase Order")
-	frappe.reload_doctype("Purchase Order Item")
-
-	if not frappe.db.has_column("Purchase Order", "delivered_by_supplier"):
-		return
-
-	for po in frappe.get_all("Purchase Order", filters={"delivered_by_supplier": 1}, fields=["name"]):
-		purchase_order = frappe.get_doc("Purchase Order", po)
-
-		for item in purchase_order.items:
-			if item.prevdoc_doctype == "Sales Order":
-				delivered_by_supplier = frappe.get_value("Sales Order Item", item.prevdoc_detail_docname,
-					"delivered_by_supplier")
-
-				if delivered_by_supplier:
-					frappe.db.sql("""update `tabPurchase Order Item`
-						set delivered_by_supplier=1, billed_amt=amount, received_qty=qty
-						where name=%s """, item.name)
-
-		update_per_received(purchase_order)
-		update_per_billed(purchase_order)
-
-def update_per_received(po):
-	frappe.db.sql(""" update `tabPurchase Order`
-				set per_received = round((select sum(if(qty > ifnull(received_qty, 0),
-					ifnull(received_qty, 0), qty)) / sum(qty) *100
-				from `tabPurchase Order Item`
-				where parent = %(name)s), 2)
-			where name = %(name)s """, {"name": po.name})
-
-def update_per_billed(po):
-	frappe.db.sql(""" update `tabPurchase Order`
-				set per_billed = round((select sum( if(amount > ifnull(billed_amt, 0),
-					ifnull(billed_amt, 0), amount)) / sum(amount) *100
-				from `tabPurchase Order Item`
-				where parent = %(name)s), 2)
-			where name = %(name)s """, {"name": po.name})
-
-
diff --git a/erpnext/patches/v7_0/__init__.py b/erpnext/patches/v7_0/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v7_0/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v7_0/calculate_total_costing_amount.py b/erpnext/patches/v7_0/calculate_total_costing_amount.py
deleted file mode 100644
index 8ed60a2..0000000
--- a/erpnext/patches/v7_0/calculate_total_costing_amount.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt
-
-def execute():
-	frappe.reload_doc('projects', 'doctype', 'timesheet')
-
-	for data in frappe.get_all('Timesheet', fields=["name, total_costing_amount"],
-		filters = [["docstatus", "<", "2"]]):
-		if flt(data.total_costing_amount) == 0.0:
-			ts = frappe.get_doc('Timesheet', data.name)
-			ts.update_cost()
-			ts.calculate_total_amounts()
-			ts.flags.ignore_validate = True
-			ts.flags.ignore_mandatory = True
-			ts.flags.ignore_validate_update_after_submit = True
-			ts.flags.ignore_links = True
-			ts.save()
diff --git a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
deleted file mode 100644
index 8c60b5b..0000000
--- a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('projects', 'doctype', 'task')
-	frappe.reload_doc('projects', 'doctype', 'timesheet')
-	if not frappe.db.table_exists("Time Log"):
-		return
-
-	from erpnext.manufacturing.doctype.work_order.work_order \
-		import make_timesheet, add_timesheet_detail
-
-	for data in frappe.db.sql("select * from `tabTime Log`", as_dict=1):
-		if data.task:
-			company = frappe.db.get_value("Task", data.task, "company")
-		elif data.work_order:
-			company = frappe.db.get_value("Work Order", data.work_order, "company")
-		else:
-			company = frappe.db.get_single_value('Global Defaults', 'default_company')
-		
-		time_sheet = make_timesheet(data.work_order, company)
-		args = get_timelog_data(data)
-		add_timesheet_detail(time_sheet, args)
-		if data.docstatus == 2:
-			time_sheet.docstatus = 0
-		else:
-			time_sheet.docstatus = data.docstatus
-		time_sheet.employee = data.employee
-		time_sheet.note = data.note
-		time_sheet.company = company
-
-		time_sheet.set_status()
-		time_sheet.set_dates()
-		time_sheet.update_cost()
-		time_sheet.calculate_total_amounts()
-		time_sheet.flags.ignore_validate = True
-		time_sheet.flags.ignore_links = True
-		time_sheet.save(ignore_permissions=True)
-
-		# To ignore validate_mandatory_fields function
-		if data.docstatus == 1:
-			time_sheet.db_set("docstatus", 1)
-			for d in time_sheet.get("time_logs"):
-				d.db_set("docstatus", 1)
-			time_sheet.update_work_order(time_sheet.name)
-			time_sheet.update_task_and_project()
-		if data.docstatus == 2:
-			time_sheet.db_set("docstatus", 2)
-			for d in time_sheet.get("time_logs"):
-				d.db_set("docstatus", 2)
-
-def get_timelog_data(data):
-	return {
-		'is_billable': data.billable,
-		'from_time': data.from_time,
-		'hours': data.hours,
-		'to_time': data.to_time,
-		'project': data.project,
-		'task': data.task,
-		'activity_type': data.activity_type,
-		'operation': data.operation,
-		'operation_id': data.operation_id,
-		'workstation': data.workstation,
-		'completed_qty': data.completed_qty,
-		'billing_rate': data.billing_rate,
-		'billing_amount': data.billing_amount,
-		'costing_rate': data.costing_rate,
-		'costing_amount': data.costing_amount
-	}
diff --git a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py b/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
deleted file mode 100644
index e78f163..0000000
--- a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint
-
-def execute():
-	if not frappe.db.exists("DocType", "Time Log Batch"):
-		return
-
-	from erpnext.manufacturing.doctype.work_order.work_order import add_timesheet_detail
-
-	for tlb in frappe.get_all('Time Log Batch', fields=["*"], 
-		filters = [["docstatus", "<", "2"]]):
-		time_sheet = frappe.new_doc('Timesheet')
-		time_sheet.employee= ""
-		time_sheet.company = frappe.db.get_single_value('Global Defaults', 'default_company')
-		time_sheet.sales_invoice = tlb.sales_invoice
-
-		for data in frappe.get_all('Time Log Batch Detail', fields=["*"],
-			filters = {'parent': tlb.name}):
-			args = get_timesheet_data(data)
-			add_timesheet_detail(time_sheet, args)
-
-		time_sheet.docstatus = tlb.docstatus
-		time_sheet.flags.ignore_links = True
-		time_sheet.save(ignore_permissions=True)
-
-def get_timesheet_data(data):
-	from erpnext.patches.v7_0.convert_timelog_to_timesheet import get_timelog_data
-
-	time_log = frappe.get_all('Time Log', fields=["*"], filters = {'name': data.time_log})
-	if time_log:
-		return get_timelog_data(time_log[0])
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/create_budget_record.py b/erpnext/patches/v7_0/create_budget_record.py
deleted file mode 100644
index fd8bec9..0000000
--- a/erpnext/patches/v7_0/create_budget_record.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from erpnext.accounts.doctype.budget.budget import DuplicateBudgetError
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "budget")
-	frappe.reload_doc("accounts", "doctype", "budget_account")
-
-	existing_budgets = frappe.db.sql("""
-		select
-			cc.name, cc.company, cc.distribution_id,
-			budget.account, budget.budget_allocated, budget.fiscal_year
-		from
-			`tabCost Center` cc, `tabBudget Detail` budget
-		where
-			cc.name=budget.parent
-	""", as_dict=1)
-
-	actions = {}
-	for d in frappe.db.sql("select name, yearly_bgt_flag, monthly_bgt_flag from tabCompany", as_dict=1):
-		actions.setdefault(d.name, d)
-
-	budget_records = []
-	for d in existing_budgets:
-		budget = frappe.db.get_value("Budget",
-			{"cost_center": d.name, "fiscal_year": d.fiscal_year, "company": d.company})
-
-		if not budget:
-			budget = frappe.new_doc("Budget")
-			budget.cost_center = d.name
-			budget.fiscal_year = d.fiscal_year
-			budget.monthly_distribution = d.distribution_id
-			budget.company = d.company
-			if actions[d.company]["yearly_bgt_flag"]:
-				budget.action_if_annual_budget_exceeded = actions[d.company]["yearly_bgt_flag"]
-			if actions[d.company]["monthly_bgt_flag"]:
-				budget.action_if_accumulated_monthly_budget_exceeded = actions[d.company]["monthly_bgt_flag"]
-		else:
-			budget = frappe.get_doc("Budget", budget)
-
-		budget.append("accounts", {
-			"account": d.account,
-			"budget_amount": d.budget_allocated
-		})
-
-		try:
-			budget.insert()
-			budget_records.append(budget)
-		except DuplicateBudgetError:
-			pass
-
-	for budget in budget_records:
-		budget.submit()
-
-	if frappe.db.get_value("DocType", "Budget Detail"):
-		frappe.delete_doc("DocType", "Budget Detail")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/create_warehouse_nestedset.py b/erpnext/patches/v7_0/create_warehouse_nestedset.py
deleted file mode 100644
index 1c9fc32..0000000
--- a/erpnext/patches/v7_0/create_warehouse_nestedset.py
+++ /dev/null
@@ -1,128 +0,0 @@
-
-from __future__ import unicode_literals
-import frappe, erpnext
-from frappe import _
-from frappe.utils import cint
-from frappe.utils.nestedset import rebuild_tree
-
-def execute():
-	"""
-	Patch Reference:
-		1. check whether warehouse is associated to company or not
-		2. if warehouse is associated with company
-			a. create warehouse group for company
-			b. set warehouse group as parent to other warehouses and set is_group as 0
-		3. if warehouses is not associated with company
-			a. get distinct companies from stock ledger entries
-			b. if sle have only company,
-				i. set default company to all warehouse
-				ii. repeat 2.a and 2.b
-			c. if have multiple companies,
-				i. create group warehouse without company
-				ii. repeat 2.b
-	"""
-	
-	frappe.reload_doc("stock", "doctype", "warehouse")
-
-	if check_is_warehouse_associated_with_company():
-		for company in frappe.get_all("Company", fields=["name", "abbr"]):
-			make_warehouse_nestedset(company)
-	else:
-		sle_against_companies = frappe.db.sql_list("""select distinct company from `tabStock Ledger Entry`""")
-
-		if len(sle_against_companies) == 1:
-			company = frappe.get_cached_value('Company',  sle_against_companies[0],  
-				fieldname=["name", "abbr"], as_dict=1)
-			set_company_to_warehouse(company.name)
-			make_warehouse_nestedset(company)
-
-		elif len(sle_against_companies) > 1:
-			make_warehouse_nestedset()
-
-def check_is_warehouse_associated_with_company():
-	warehouse_associcated_with_company = False
-
-	for warehouse in frappe.get_all("Warehouse", fields=["name", "company"]):
-		if warehouse.company:
-			warehouse_associcated_with_company = True
-
-	return warehouse_associcated_with_company
-
-def make_warehouse_nestedset(company=None):
-	validate_parent_account_for_warehouse(company)
-	stock_account_group = get_stock_account_group(company.name)
-	enable_perpetual_inventory = cint(erpnext.is_perpetual_inventory_enabled(company.name)) or 0
-	if not stock_account_group and enable_perpetual_inventory:
-		return
-
-	if company:
-		warehouse_group = "{0} - {1}".format(_("All Warehouses"), company.abbr)
-		ignore_mandatory = False
-	else:
-		warehouse_group = _("All Warehouses")
-		ignore_mandatory = True
-
-	if not frappe.db.get_value("Warehouse", warehouse_group):
-		create_default_warehouse_group(company, stock_account_group, ignore_mandatory)
-
-	set_parent_to_warehouse(warehouse_group, company)
-	if enable_perpetual_inventory:
-		set_parent_to_warehouse_account(company)
-
-def validate_parent_account_for_warehouse(company=None):
-	if not company:
-		return
-
-	if cint(erpnext.is_perpetual_inventory_enabled(company.name)):
-		parent_account = frappe.db.sql("""select name from tabAccount
-			where account_type='Stock' and company=%s and is_group=1
-			and (warehouse is null or warehouse = '')""", company.name)
-
-		if not parent_account:
-			current_parent_accounts_for_warehouse = frappe.db.sql("""select parent_account from tabAccount
-				where account_type='Warehouse' and (warehouse is not null or warehouse != '') """)
-
-			if current_parent_accounts_for_warehouse:
-				frappe.db.set_value("Account", current_parent_accounts_for_warehouse[0][0], "account_type", "Stock")
-
-def create_default_warehouse_group(company=None, stock_account_group=None, ignore_mandatory=False):
-	wh = frappe.get_doc({
-		"doctype": "Warehouse",
-		"warehouse_name": _("All Warehouses"),
-		"is_group": 1,
-		"company": company.name if company else "",
-		"parent_warehouse": ""
-	})
-
-	if ignore_mandatory:
-		wh.flags.ignore_mandatory = ignore_mandatory
-
-	wh.insert(ignore_permissions=True)
-
-def set_parent_to_warehouse(warehouse_group, company=None):
-	frappe.db.sql(""" update tabWarehouse set parent_warehouse = %s, is_group = 0
-		where (is_group = 0 or is_group is null or is_group = '') and ifnull(company, '') = %s
-		""",(warehouse_group, company.name if company else ""))
-
-	rebuild_tree("Warehouse", "parent_warehouse")
-
-def set_parent_to_warehouse_account(company):
-	frappe.db.sql(""" update tabAccount set parent_account = %s
-		where is_group = 0 and account_type = "Warehouse"
-		and (warehouse is not null or warehouse != '') and company = %s
-		""",("{0} - {1}".format(_("All Warehouses"), company.abbr), company.name))
-
-	rebuild_tree("Account", "parent_account")
-
-def set_company_to_warehouse(company):
-	frappe.db.sql("update tabWahouse set company=%s", company)
-
-def get_stock_account_group(company):
-	stock_account_group = frappe.db.get_all('Account', filters = {'company': company, 'is_group': 1,
-		'account_type': 'Stock', 'root_type': 'Asset'}, limit=1)
-			
-	if not stock_account_group:
-		stock_account_group = frappe.db.get_all('Account', filters = {'company': company, 'is_group': 1,
-				'parent_account': '', 'root_type': 'Asset'}, limit=1)
-
-	return stock_account_group[0].name if stock_account_group else None
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/fix_duplicate_icons.py b/erpnext/patches/v7_0/fix_duplicate_icons.py
deleted file mode 100644
index 9f44202..0000000
--- a/erpnext/patches/v7_0/fix_duplicate_icons.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from frappe.desk.doctype.desktop_icon.desktop_icon import (sync_desktop_icons,
-	get_desktop_icons, set_hidden)
-from erpnext.patches.v7_0.migrate_schools_to_erpnext import reload_doctypes_for_schools_icons
-
-def execute():
-	'''hide new style icons if old ones are set'''
-	frappe.reload_doc('desk', 'doctype', 'desktop_icon')
-
-	reload_doctypes_for_schools_icons()
-
-	sync_desktop_icons()
-
-	for user in frappe.get_all('User', filters={'user_type': 'System User'}):
-		desktop_icons = get_desktop_icons(user.name)
-		icons_dict = {}
-		for d in desktop_icons:
-			if not d.hidden:
-				icons_dict[d.module_name] = d
-
-		for key in (('Selling', 'Customer'), ('Stock', 'Item'), ('Buying', 'Supplier'),
-			('HR', 'Employee'), ('CRM', 'Lead'), ('Support', 'Issue'), ('Projects', 'Project')):
-			if key[0] in icons_dict and key[1] in icons_dict:
-				set_hidden(key[1], user.name, 1)
-
diff --git a/erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py b/erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py
deleted file mode 100644
index 2bc0971..0000000
--- a/erpnext/patches/v7_0/fix_nonwarehouse_ledger_gl_entries_for_transactions.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import print_function, unicode_literals
-import frappe, erpnext
-
-def execute():
-	frappe.reload_doctype("Account")
-
-	warehouses = frappe.db.sql("""select name, company from tabAccount
-		where account_type = 'Stock' and is_group = 0
-		and (warehouse is null or warehouse = '')""", as_dict=1)
-	warehouses = [d.name for d in warehouses if erpnext.is_perpetual_inventory_enabled(d.company)]
-
-	if len(warehouses) > 0:
-		warehouses = set_warehouse_for_stock_account(warehouses)
-		if not warehouses:
-			return
-
-		stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
-			from `tabStock Ledger Entry` sle
-			where sle.warehouse in (%s) and creation > '2016-05-01'
-			and not exists(select name from `tabGL Entry` 
-				where account=sle.warehouse and voucher_type=sle.voucher_type and voucher_no=sle.voucher_no)
-			order by sle.posting_date""" %
-			', '.join(['%s']*len(warehouses)), tuple(warehouses))
-
-		rejected = []
-		for voucher_type, voucher_no in stock_vouchers:
-			try:
-				frappe.db.sql("""delete from `tabGL Entry`
-					where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
-
-				voucher = frappe.get_doc(voucher_type, voucher_no)
-				voucher.make_gl_entries()
-				frappe.db.commit()
-			except Exception as e:
-				print(frappe.get_traceback())
-				rejected.append([voucher_type, voucher_no])
-				frappe.db.rollback()
-
-		print(rejected)
-
-def set_warehouse_for_stock_account(warehouse_account):
-	for account in warehouse_account:
-		if frappe.db.exists('Warehouse', account):
-			frappe.db.set_value("Account", account, "warehouse", account)
-		else:
-			warehouse_account.remove(account)
-
-	return warehouse_account
diff --git a/erpnext/patches/v7_0/make_guardian.py b/erpnext/patches/v7_0/make_guardian.py
deleted file mode 100644
index 519969b..0000000
--- a/erpnext/patches/v7_0/make_guardian.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists("DocType", "Student"):
-		student_table_cols = frappe.db.get_table_columns("Student")
-		if "father_name" in student_table_cols:
-
-			# 'Schools' module changed to the 'Education'
-			# frappe.reload_doc("schools", "doctype", "student")
-			# frappe.reload_doc("schools", "doctype", "guardian")
-			# frappe.reload_doc("schools", "doctype", "guardian_interest")
-
-			frappe.reload_doc("education", "doctype", "student")
-			frappe.reload_doc("education", "doctype", "guardian")
-			frappe.reload_doc("education", "doctype", "guardian_interest")
-			frappe.reload_doc("hr", "doctype", "interest")
-		
-			fields = ["name", "father_name", "mother_name"]
-			
-			if "father_email_id" in student_table_cols:
-				fields += ["father_email_id", "mother_email_id"]
-	
-			students = frappe.get_all("Student", fields)
-			for stud in students:
-				if stud.father_name:
-					make_guardian(stud.father_name, stud.name, stud.father_email_id)
-				if stud.mother_name:
-					make_guardian(stud.mother_name, stud.name, stud.mother_email_id)
-		
-def make_guardian(name, student, email=None):
-	frappe.get_doc({
-		'doctype': 'Guardian',
-		'guardian_name': name,
-		'email': email,
-		'student': student
-	}).insert()
diff --git a/erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py b/erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py
deleted file mode 100644
index ba82e86..0000000
--- a/erpnext/patches/v7_0/make_is_group_fieldtype_as_check.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ["Sales Person", "Customer Group", "Item Group", "Territory"]:
-		
-		# convert to 1 or 0
-		frappe.db.sql("update `tab{doctype}` set is_group = if(is_group='Yes',1,0) "
-			.format(doctype=doctype))
-
-		frappe.db.commit()
-
-		# alter fields to int
-				
-		frappe.db.sql("alter table `tab{doctype}` change is_group is_group int(1) default '0'"
-			.format(doctype=doctype))
-
-		frappe.reload_doctype(doctype)
diff --git a/erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py b/erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py
deleted file mode 100644
index 02808a7..0000000
--- a/erpnext/patches/v7_0/merge_account_type_stock_and_warehouse_to_stock.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "account")
-	
-	frappe.db.sql(""" update tabAccount set account_type = "Stock"
-		where account_type = "Warehouse" """)
-	
-	frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py b/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py
deleted file mode 100644
index e0e3f70..0000000
--- a/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_payment')
-	frappe.reload_doc('accounts', 'doctype', 'mode_of_payment')
-
-	count = 0
-	for data in frappe.db.sql("""select name, mode_of_payment, cash_bank_account, paid_amount, company 
-		from `tabSales Invoice` si
-		where si.is_pos = 1 and si.docstatus < 2 
-		and si.cash_bank_account is not null and si.cash_bank_account != ''
-		and not exists(select name from `tabSales Invoice Payment` where parent=si.name)""", as_dict=1):
-		
-		if not data.mode_of_payment and not frappe.db.exists("Mode of Payment", "Cash"):
-			mop = frappe.new_doc("Mode of Payment")
-			mop.mode_of_payment = "Cash"
-			mop.type = "Cash"
-			mop.save()
-		
-		si_doc = frappe.get_doc('Sales Invoice', data.name)
-		row = si_doc.append('payments', {
-			'mode_of_payment': data.mode_of_payment or 'Cash',
-			'account': data.cash_bank_account,
-			'type': frappe.db.get_value('Mode of Payment', data.mode_of_payment, 'type') or 'Cash',
-			'amount': data.paid_amount
-		})
-		row.db_update()
-		
-		si_doc.set_paid_amount()
-		si_doc.db_set("paid_amount", si_doc.paid_amount, update_modified = False)
-		si_doc.db_set("base_paid_amount", si_doc.base_paid_amount, update_modified = False)
-		
-		count +=1
-		
-		if count % 200 == 0:
-			frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/migrate_schools_to_erpnext.py b/erpnext/patches/v7_0/migrate_schools_to_erpnext.py
deleted file mode 100644
index b72bc13..0000000
--- a/erpnext/patches/v7_0/migrate_schools_to_erpnext.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from __future__ import unicode_literals
-import frappe, os
-from frappe.installer import remove_from_installed_apps
-
-def execute():
-	reload_doctypes_for_schools_icons()
-
-	frappe.reload_doc('website', 'doctype', 'portal_settings')
-	frappe.reload_doc('website', 'doctype', 'portal_menu_item')
-	frappe.reload_doc('buying', 'doctype', 'request_for_quotation')
-
-	if 'schools' in frappe.get_installed_apps():
-		if not frappe.db.exists('Module Def', 'Schools') and frappe.db.exists('Module Def', 'Academics'):
-
-			# 'Schools' module changed to the 'Education'
-			# frappe.rename_doc("Module Def", "Academics", "Schools")
-
-			frappe.rename_doc("Module Def", "Academics", "Education")
-
-		remove_from_installed_apps("schools")
-
-def reload_doctypes_for_schools_icons():
-	# 'Schools' module changed to the 'Education'
-	# base_path = frappe.get_app_path('erpnext', 'schools', 'doctype')
-
-	base_path = frappe.get_app_path('erpnext', 'education', 'doctype')
-	for doctype in os.listdir(base_path):
-		if os.path.exists(os.path.join(base_path, doctype, doctype + '.json')) \
-			and doctype not in ("fee_component", "assessment", "assessment_result"):
-			frappe.reload_doc('education', 'doctype', doctype)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
deleted file mode 100644
index 998c4b6..0000000
--- a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_payment')
-	for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billable_amount from `tabTimesheet`
-		where sales_invoice is not null and docstatus < 2""", as_dict=True):
-		if not frappe.db.exists('Sales Invoice', time_sheet.sales_invoice):
-			continue
-		si_doc = frappe.get_doc('Sales Invoice', time_sheet.sales_invoice)
-		ts = si_doc.append('timesheets',{})
-		ts.time_sheet = time_sheet.name
-		ts.billing_amount = time_sheet.total_billable_amount
-		ts.db_update()
-		si_doc.calculate_billing_amount_from_timesheet()
-		si_doc.db_set("total_billing_amount", si_doc.total_billing_amount, update_modified = False)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/po_status_issue_for_pr_return.py b/erpnext/patches/v7_0/po_status_issue_for_pr_return.py
deleted file mode 100644
index 910814f..0000000
--- a/erpnext/patches/v7_0/po_status_issue_for_pr_return.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	parent_list = []
-	count = 0
-
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
-
-	for data in frappe.db.sql("""
-		select
-			`tabPurchase Receipt Item`.purchase_order, `tabPurchase Receipt Item`.name,
-			`tabPurchase Receipt Item`.item_code, `tabPurchase Receipt Item`.idx,
-			`tabPurchase Receipt Item`.parent
-		from
-			`tabPurchase Receipt Item`, `tabPurchase Receipt`
-		where
-			`tabPurchase Receipt Item`.parent = `tabPurchase Receipt`.name and
-			`tabPurchase Receipt Item`.purchase_order_item is null and
-			`tabPurchase Receipt Item`.purchase_order is not null and
-			`tabPurchase Receipt`.is_return = 1""", as_dict=1):
-			name = frappe.db.get_value('Purchase Order Item',
-				{'item_code': data.item_code, 'parent': data.purchase_order, 'idx': data.idx}, 'name')
-
-			if name:
-				frappe.db.set_value('Purchase Receipt Item', data.name, 'purchase_order_item', name, update_modified=False)
-				parent_list.append(data.parent)
-
-			count +=1
-			if count % 200 == 0:
-				frappe.db.commit()
-
-	if len(parent_list) > 0:
-		for parent in set(parent_list):
-			doc = frappe.get_doc('Purchase Receipt', parent)
-			doc.update_qty(update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/re_route.py b/erpnext/patches/v7_0/re_route.py
deleted file mode 100644
index 3cec6f3..0000000
--- a/erpnext/patches/v7_0/re_route.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import unicode_literals
-from frappe.patches.v7_0.re_route import update_routes
-
-def execute():
-	update_routes(['Item', 'Item Group', 'Sales Partner', 'Job Opening'])
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py b/erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py
deleted file mode 100644
index 8c87c4e..0000000
--- a/erpnext/patches/v7_0/remove_administrator_role_in_doctypes.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""delete from tabDocPerm where role="Administrator" and parent in 
-		("Payment Gateway", "Payment Gateway Account", "Payment Request", "Academic Term", "Academic Year", "Course",
-		"Course Schedule", "Examination", "Fee Category", "Fee Structure", "Fees", "Instructor", "Program", "Program Enrollment Tool",
-		"Room", "Scheduling Tool", "Student", "Student Applicant", "Student Attendance", "Student Group", "Student Group Creation Tool")
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
deleted file mode 100644
index 2356e2f..0000000
--- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("Time Log"):
-		frappe.db.sql("""delete from `tabDocType`
-			where name in('Time Log Batch', 'Time Log Batch Detail', 'Time Log')""")
-
-	frappe.db.sql("""delete from `tabDocField` where parent in ('Time Log', 'Time Log Batch')""")
-	frappe.db.sql("""update `tabClient Script` set dt = 'Timesheet' where dt = 'Time Log'""")
-
-	for data in frappe.db.sql(""" select label, fieldname from  `tabCustom Field` where dt = 'Time Log'""", as_dict=1):
-		custom_field = frappe.get_doc({
-			'doctype': 'Custom Field',
-			'label': data.label,
-			'dt': 'Timesheet Detail',
-			'fieldname': data.fieldname,
-			'fieldtype': data.fieldtype or "Data"
-		}).insert(ignore_permissions=True)
-
-	frappe.db.sql("""delete from `tabCustom Field` where dt = 'Time Log'""")
-	frappe.reload_doc('projects', 'doctype', 'timesheet')
-	frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
-
-	report = "Daily Time Log Summary"
-	if frappe.db.exists("Report", report):
-		frappe.delete_doc('Report', report)
diff --git a/erpnext/patches/v7_0/remove_features_setup.py b/erpnext/patches/v7_0/remove_features_setup.py
deleted file mode 100644
index 49393cc..0000000
--- a/erpnext/patches/v7_0/remove_features_setup.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from erpnext.setup.install import create_compact_item_print_custom_field
-from frappe.utils import cint
-
-def execute():
-	frappe.reload_doctype('Stock Settings')
-	stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings')
-	stock_settings.show_barcode_field = cint(frappe.db.get_value("Features Setup", None, "fs_item_barcode"))
-	if not frappe.db.exists("UOM", stock_settings.stock_uom):
-		stock_settings.stock_uom = None
-	stock_settings.save()
-
-	create_compact_item_print_custom_field()
-
-	compact_item_print = frappe.db.get_value("Features Setup", None, "compact_item_print")
-	frappe.db.set_value("Print Settings", None, "compact_item_print", compact_item_print)
-
-	# remove defaults
-	frappe.db.sql("""delete from tabDefaultValue where defkey in ('fs_item_serial_nos',
-		'fs_item_batch_nos', 'fs_brands', 'fs_item_barcode',
-		'fs_item_advanced', 'fs_packing_details', 'fs_item_group_in_details',
-		'fs_exports', 'fs_imports', 'fs_discounts', 'fs_purchase_discounts',
-		'fs_after_sales_installations', 'fs_projects', 'fs_sales_extras',
-		'fs_recurring_invoice', 'fs_pos', 'fs_manufacturing', 'fs_quality',
-		'fs_page_break', 'fs_more_info', 'fs_pos_view', 'compact_item_print')""")
-
-	frappe.delete_doc('DocType', 'Features Setup')
diff --git a/erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py b/erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py
deleted file mode 100644
index 05a2c49..0000000
--- a/erpnext/patches/v7_0/remove_old_earning_deduction_doctypes.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists("DocType", "Salary Component"):
-		for dt in ("Salary Structure Earning", "Salary Structure Deduction", "Salary Slip Earning", 
-			"Salary Slip Deduction", "Earning Type", "Deduction Type"):
-				frappe.delete_doc("DocType", dt)
-				
-					
-		for d in frappe.db.sql("""select name from `tabCustom Field` 
-			where dt in ('Salary Detail', 'Salary Component')"""):
-				frappe.get_doc("Custom Field", d[0]).save()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/rename_advance_table_fields.py b/erpnext/patches/v7_0/rename_advance_table_fields.py
deleted file mode 100644
index 34d8134..0000000
--- a/erpnext/patches/v7_0/rename_advance_table_fields.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	for dt in ("Sales Invoice Advance", "Purchase Invoice Advance"):
-		frappe.reload_doctype(dt)
-
-		frappe.db.sql("update `tab{0}` set reference_type = 'Journal Entry'".format(dt))
-
-		if frappe.get_meta(dt).has_field('journal_entry'):
-			rename_field(dt, "journal_entry", "reference_name")
-
-		if frappe.get_meta(dt).has_field('jv_detail_no'):
-			rename_field(dt, "jv_detail_no", "reference_row")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/rename_examination_to_assessment.py b/erpnext/patches/v7_0/rename_examination_to_assessment.py
deleted file mode 100644
index dc248de..0000000
--- a/erpnext/patches/v7_0/rename_examination_to_assessment.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	if frappe.db.exists("DocType", "Examination"):
-		frappe.rename_doc("DocType", "Examination", "Assessment")
-		frappe.rename_doc("DocType", "Examination Result", "Assessment Result")
-
-		# 'Schools' module changed to the 'Education'
-		# frappe.reload_doc("schools", "doctype", "assessment")
-		# frappe.reload_doc("schools", "doctype", "assessment_result")
-
-		frappe.reload_doc("education", "doctype", "assessment")
-		frappe.reload_doc("education", "doctype", "assessment_result")
-
-		rename_field("Assessment", "exam_name", "assessment_name")
-		rename_field("Assessment", "exam_code", "assessment_code")
-	
-		frappe.db.sql("delete from `tabPortal Menu Item` where route = '/examination'")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py b/erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py
deleted file mode 100644
index 5cb6a3b..0000000
--- a/erpnext/patches/v7_0/rename_fee_amount_to_fee_component.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	if frappe.db.exists("DocType", "Fee Amount"):
-		frappe.rename_doc("DocType", "Fee Amount", "Fee Component")
-		for dt in ("Fees", "Fee Structure"):
-			frappe.reload_doctype(dt)
-			rename_field(dt, "amount", "components")
-		
-	
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/rename_prevdoc_fields.py b/erpnext/patches/v7_0/rename_prevdoc_fields.py
deleted file mode 100644
index ded4ad4..0000000
--- a/erpnext/patches/v7_0/rename_prevdoc_fields.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe.model.utils.rename_field import update_reports, rename_field, update_property_setters
-from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-
-def execute():
-	frappe.reload_doctype('Purchase Order Item')
-	frappe.reload_doctype('Purchase Receipt Item')
-	update_po_fields()
-	update_prop_setters_reports_print_format_for_po()
-	set_sales_order_field()
-	rename_pr_fields()
-
-def update_po_fields():
-	for data in frappe.db.sql(""" select prevdoc_docname, prevdoc_detail_docname, name, prevdoc_doctype
-		from `tabPurchase Order Item` where prevdoc_doctype is not null""", as_dict=True):
-		if data.prevdoc_doctype == 'Material Request':
-			frappe.db.set_value("Purchase Order Item", data.name, "material_request", data.prevdoc_docname, update_modified=False)
-			frappe.db.set_value("Purchase Order Item", data.name, "material_request_item", data.prevdoc_detail_docname, update_modified=False)
-		elif data.prevdoc_doctype == 'Sales Order':
-			frappe.db.set_value("Purchase Order Item", data.name, "sales_order", data.prevdoc_docname, update_modified=False)
-			frappe.db.set_value("Purchase Order Item", data.name, "sales_order_item", data.prevdoc_detail_docname, update_modified=False)
-
-def get_columns():
-	return {
-		'prevdoc_docname': 'material_request',
-		'prevdoc_detail_docname': 'material_request_item'
-	}
-
-def update_prop_setters_reports_print_format_for_po():
-	for key, val in get_columns().items():
-		update_property_setters('Purchase Order Item', key, val)
-		update_reports('Purchase Order Item', key, val)
-		update_print_format_for_po(key, val, 'Purchase Order')
-
-def update_print_format_for_po(old_fieldname, new_fieldname, doc_type):
-	column_mapper = get_columns()
-
-	for data in frappe.db.sql(""" select name, format_data from `tabPrint Format` where
-		format_data like %(old_fieldname)s and doc_type = %(doc_type)s""", 
-		{'old_fieldname': '%%%s%%'%(old_fieldname), 'doc_type': doc_type}, as_dict=True):
-
-		update_print_format_fields(old_fieldname, new_fieldname, data)
-
-def update_print_format_fields(old_fieldname, new_fieldname, args):
-	report_dict = json.loads(args.format_data)
-	update = False
-
-	for col in report_dict:
-		if col.get('fieldname') and col.get('fieldname') == old_fieldname:
-			col['fieldname'] = new_fieldname
-			update = True
-
-		if col.get('visible_columns'):
-			for key in col.get('visible_columns'):
-				if key.get('fieldname') == old_fieldname:
-					key['fieldname'] = new_fieldname
-					update = True
-
-	if update:
-		val = json.dumps(report_dict)
-		frappe.db.sql("""update `tabPrint Format` set `format_data`=%s where name=%s""", (val, args.name))
-		
-def set_sales_order_field():
-	for data in frappe.db.sql("""select doc_type, field_name, property, value, property_type 
-		from `tabProperty Setter` where doc_type = 'Purchase Order Item' 
-		and field_name in('material_request', 'material_request_item')""", as_dict=True):
-		if data.field_name == 'material_request':
-			make_property_setter(data.doc_type, 'sales_order', data.property, data.value, data.property_type)
-		else:
-			make_property_setter(data.doc_type, 'sales_order_item', data.property, data.value, data.property_type)
-
-def rename_pr_fields():
-	rename_field("Purchase Receipt Item", "prevdoc_docname", "purchase_order")
-	rename_field("Purchase Receipt Item", "prevdoc_detail_docname", "purchase_order_item")
diff --git a/erpnext/patches/v7_0/rename_salary_components.py b/erpnext/patches/v7_0/rename_salary_components.py
deleted file mode 100644
index 1693f3b..0000000
--- a/erpnext/patches/v7_0/rename_salary_components.py
+++ /dev/null
@@ -1,149 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import update_property_setters
-
-def execute():
-	if not frappe.db.exists("DocType", "Salary Structure Earning"):
-		return
-
-	frappe.reload_doc("Payroll", "doctype", "salary_detail")
-	frappe.reload_doc("Payroll", "doctype", "salary_component")
-
-	standard_cols = ["name", "creation", "modified", "owner", "modified_by", "parent", "parenttype", "parentfield", "idx"]
-
-	dt_cols = {
-		"Salary Structure Deduction": ["d_type", "d_modified_amt", "depend_on_lwp"],
-		"Salary Structure Earning": ["e_type", "modified_value", "depend_on_lwp"],
-		"Salary Slip Earning": ["e_type", "e_modified_amount", "e_depends_on_lwp", "e_amount"],
-		"Salary Slip Deduction": ["d_type", "d_modified_amount", "d_depends_on_lwp", "d_amount"],
-	}
-
-	earning_type_exists = True if "earning_type" in frappe.db.get_table_columns("Salary Slip Earning") else False
-	e_type_exists = True if "e_type" in frappe.db.get_table_columns("Salary Slip Earning") else False
-
-
-	if e_type_exists and earning_type_exists:
-		frappe.db.sql("""update `tabSalary Slip Earning`
-			set e_type = earning_type, e_modified_amount = earning_amount
-			where e_type is null and earning_type is not null""")
-
-		frappe.db.sql("""update `tabSalary Structure Earning` set e_type = earning_type
-			where e_type is null and earning_type is not null""")
-
-		frappe.db.sql("""update `tabSalary Slip Deduction` set
-			d_type = deduction_type, d_modified_amount = deduction_amount
-			where d_type is null and deduction_type is not null""")
-
-		frappe.db.sql("""update `tabSalary Structure Deduction` set d_type = deduction_type
-			where d_type is null and deduction_type is not null""")
-
-	if earning_type_exists and not e_type_exists:
-		for val in dt_cols.values():
-			if val[0] == "e_type":
-				val[0] = "earning_type"
-
-			if val[0] == "d_type":
-				val[0] = "deduction_type"
-
-			if val[1] == "e_modified_amount":
-				val[1]  ="earning_amount"
-
-			if val[1] == "d_modified_amount":
-				val[1]  ="deduction_amount"
-
-
-
-	target_cols = standard_cols + ["salary_component", "amount", "depends_on_payment_days", "default_amount"]
-	target_cols = "`" + "`, `".join(target_cols) + "`"
-
-	for doctype, cols in dt_cols.items():
-		source_cols = "`" + "`, `".join(standard_cols + cols) + "`"
-		if len(cols) == 3:
-			source_cols += ", 0"
-
-
-		frappe.db.sql("""INSERT INTO `tabSalary Detail` ({0}) SELECT {1} FROM `tab{2}`"""
-			.format(target_cols, source_cols, doctype))
-
-
-	dt_cols_de = {
-		"Deduction Type": ["deduction_name", "description"],
-		"Earning Type": ["earning_name", "description"],
-	}
-
-	standard_cols_de = standard_cols
-
-
-	target_cols = standard_cols_de + ["salary_component", "description"]
-	target_cols = "`" + "`, `".join(target_cols) + "`"
-
-	for doctype, cols in dt_cols_de.items():
-		source_cols = "`" + "`, `".join(standard_cols_de + cols) + "`"
-		try:
-			frappe.db.sql("""INSERT INTO `tabSalary Component` ({0}) SELECT {1} FROM `tab{2}`"""
-				.format(target_cols, source_cols, doctype))
-		except Exception as e:
-			if e.args[0]==1062:
-				pass
-
-	update_customizations()
-
-	for doctype in ["Salary Structure Deduction", "Salary Structure Earning", "Salary Slip Earning",
-			"Salary Slip Deduction", "Deduction Type", "Earning Type"] :
-		frappe.delete_doc("DocType", doctype)
-
-
-def update_customizations():
-	dt_cols = {
-		"Salary Structure Deduction": {
-			"d_type": "salary_component",
-			"deduction_type": "salary_component",
-			"d_modified_amt": "amount",
-			"depend_on_lwp": "depends_on_payment_days"
-		},
-		"Salary Structure Earning": {
-			"e_type": "salary_component",
-			"earning_type": "salary_component",
-			"modified_value": "amount",
-			"depend_on_lwp": "depends_on_payment_days"
-		},
-		"Salary Slip Earning": {
-			"e_type": "salary_component",
-			"earning_type": "salary_component",
-			"e_modified_amount": "amount",
-			"e_amount" : "default_amount",
-			"e_depends_on_lwp": "depends_on_payment_days"
-		},
-		"Salary Slip Deduction": {
-			"d_type": "salary_component",
-			"deduction_type": "salary_component",
-			"d_modified_amount": "amount",
-			"d_amount" : "default_amount",
-			"d_depends_on_lwp": "depends_on_payment_days"
-		}
-	}
-
-	update_property_setters_and_custom_fields("Salary Detail", dt_cols)
-
-	dt_cols = {
-		"Earning Type": {
-			"earning_name": "salary_component"
-		},
-		"Deduction Type": {
-			"deduction_name": "salary_component"
-		}
-	}
-
-	update_property_setters_and_custom_fields("Salary Component", dt_cols)
-
-
-
-
-def update_property_setters_and_custom_fields(new_dt, dt_cols):
-	for doctype, cols in dt_cols.items():
-		frappe.db.sql("update `tabProperty Setter` set doc_type = %s where doc_type=%s", (new_dt, doctype))
-		frappe.db.sql("update `tabCustom Field` set dt = %s where dt=%s", (new_dt, doctype))
-
-
-		for old_fieldname, new_fieldname in cols.items():
-			update_property_setters(new_dt, old_fieldname, new_fieldname)
diff --git a/erpnext/patches/v7_0/rename_time_sheet_doctype.py b/erpnext/patches/v7_0/rename_time_sheet_doctype.py
deleted file mode 100644
index f80a830..0000000
--- a/erpnext/patches/v7_0/rename_time_sheet_doctype.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("Time Sheet") and not frappe.db.table_exists("Timesheet"):
-		frappe.rename_doc("DocType", "Time Sheet", "Timesheet")
-		frappe.rename_doc("DocType", "Time Sheet Detail", "Timesheet Detail")
-		
-		for doctype in ['Time Sheet', 'Time Sheet Detail']:
-			frappe.delete_doc('DocType', doctype)
-		
-		report = "Daily Time Sheet Summary"
-		if frappe.db.exists("Report", report):
-			frappe.delete_doc('Report', report)
diff --git a/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py b/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py
deleted file mode 100644
index a5cf22c..0000000
--- a/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	repost_bin_qty()
-
-def repost_bin_qty():
-	for bin in frappe.db.sql(""" select name from `tabBin`
-		where (actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty - reserved_qty_for_production - reserved_qty_for_sub_contract) != projected_qty """, as_dict=1):
-		bin_doc = frappe.get_doc('Bin', bin.name)
-		bin_doc.set_projected_qty()
-		bin_doc.db_set("projected_qty", bin_doc.projected_qty, update_modified = False)
diff --git a/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py b/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py
deleted file mode 100644
index b864e59..0000000
--- a/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint
-
-def execute():
-	frappe.reload_doctype("Purchase Invoice")
-
-	for pi in frappe.db.sql("""select name from `tabPurchase Invoice`
-		where company in(select name from tabCompany where enable_perpetual_inventory = 1) and
-		update_stock=1 and docstatus=1 order by posting_date asc""", as_dict=1):
-
-			frappe.db.sql("""delete from `tabGL Entry`
-				where voucher_type = 'Purchase Invoice' and voucher_no = %s""", pi.name)
-
-			pi_doc = frappe.get_doc("Purchase Invoice", pi.name)
-			pi_doc.make_gl_entries()
-			frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py b/erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py
deleted file mode 100644
index 77ecafd..0000000
--- a/erpnext/patches/v7_0/repost_gle_for_pos_sales_return.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint, flt
-
-def execute():
-	frappe.reload_doctype("Sales Invoice")
-	frappe.reload_doctype("Sales Invoice Item")
-	
-	for si in frappe.get_all("Sales Invoice", fields = ["name"], 
-		filters={"docstatus": 1, "is_pos": 1, "is_return": 1}):
-		si_doc = frappe.get_doc("Sales Invoice", si.name)
-		if len(si_doc.payments) > 0:
-			si_doc.set_paid_amount()
-			si_doc.flags.ignore_validate_update_after_submit = True
-			si_doc.save()
-			if si_doc.grand_total <= si_doc.paid_amount and si_doc.paid_amount < 0:
-				delete_gle_for_voucher(si_doc.name)
-				si_doc.run_method("make_gl_entries")
-
-def delete_gle_for_voucher(voucher_no):
-	frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""",
-		{'voucher_no': voucher_no})
diff --git a/erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py b/erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py
deleted file mode 100644
index 5dd61a0..0000000
--- a/erpnext/patches/v7_0/set_base_amount_in_invoice_payment_table.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt
-
-def execute():
-	si_list = frappe.db.sql("""
-		select distinct parent
-		from `tabSales Invoice Payment`
-		where docstatus!=2 and parenttype = 'Sales Invoice'
-		and amount != 0 and base_amount = 0
-	""")
-
-	count = 0
-	for d in si_list:
-		si = frappe.get_doc("Sales Invoice", d[0])
-		for p in si.get("payments"):
-			if p.amount and not p.base_amount:
-				base_amount = flt(p.amount*si.conversion_rate, si.precision("base_paid_amount"))
-				frappe.db.set_value("Sales Invoice Payment", p.name, "base_amount", base_amount, update_modified=False)
-
-		count +=1
-			
-		if count % 200 == 0:
-			frappe.db.commit()
diff --git a/erpnext/patches/v7_0/set_is_group_for_warehouse.py b/erpnext/patches/v7_0/set_is_group_for_warehouse.py
deleted file mode 100644
index 3e69616..0000000
--- a/erpnext/patches/v7_0/set_is_group_for_warehouse.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "warehouse")
-	frappe.db.sql("""update tabWarehouse
-		set is_group = if ((ifnull(is_group, "No") = "Yes" or ifnull(is_group, 0) = 1), 1, 0)""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/set_material_request_type_in_item.py b/erpnext/patches/v7_0/set_material_request_type_in_item.py
deleted file mode 100644
index 5fb14ad..0000000
--- a/erpnext/patches/v7_0/set_material_request_type_in_item.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Item")
-	if "default_bom" in frappe.db.get_table_columns("Item"):
-		frappe.db.sql("""update `tabItem` 
-			set default_material_request_type = (
-				case 
-					when (default_bom is not null and default_bom != '')
-					then 'Manufacture' 
-					else 'Purchase' 
-				end )""")
-				
-	else:
-		frappe.db.sql("update tabItem set default_material_request_type='Purchase'")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py b/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
deleted file mode 100644
index d4d1a69..0000000
--- a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-from frappe.custom.doctype.property_setter.property_setter import make_property_setter
-
-def execute():
-	frappe.reload_doc('projects', 'doctype', 'timesheet')
-	frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
-	
-	make_property_setter('Timesheet', "naming_series", "options", 'TS-', "Text")
-	make_property_setter('Timesheet', "naming_series", "default", 'TS-', "Text")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/set_party_name_in_payment_entry.py b/erpnext/patches/v7_0/set_party_name_in_payment_entry.py
deleted file mode 100644
index bbdcf5c..0000000
--- a/erpnext/patches/v7_0/set_party_name_in_payment_entry.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	customers = frappe._dict(frappe.db.sql("select name, customer_name from tabCustomer"))
-	suppliers = frappe._dict(frappe.db.sql("select name, supplier_name from tabSupplier"))
-	
-	frappe.reload_doc('accounts', 'doctype', 'payment_entry')
-	
-	pe_list = frappe.db.sql("""select name, party_type, party from `tabPayment Entry` 
-		where party is not null and party != ''""", as_dict=1)
-	for pe in pe_list:
-		party_name = customers.get(pe.party) if pe.party_type=="Customer" else suppliers.get(pe.party)
-		
-		frappe.db.set_value("Payment Entry", pe.name, "party_name", party_name, update_modified=False)
-	
diff --git a/erpnext/patches/v7_0/set_portal_settings.py b/erpnext/patches/v7_0/set_portal_settings.py
deleted file mode 100644
index 5259d4f..0000000
--- a/erpnext/patches/v7_0/set_portal_settings.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
-	frappe.reload_doctype('Role')
-	for dt in ("assessment", "course", "fees"):
-		# 'Schools' module changed to the 'Education'
-		# frappe.reload_doc("schools", "doctype", dt)
-		frappe.reload_doc("education", "doctype", dt)
-
-	for dt in ("domain", "has_domain", "domain_settings"):
-		frappe.reload_doc("core", "doctype", dt)
-
-	frappe.reload_doc('website', 'doctype', 'portal_menu_item')
-
-	frappe.get_doc('Portal Settings').sync_menu()
-
-	if 'schools' in frappe.get_installed_apps():
-		domain = frappe.get_doc('Domain', 'Education')
-		domain.setup_domain()
-	else:
-		domain = frappe.get_doc('Domain', 'Manufacturing')
-		domain.setup_data()
-		domain.setup_sidebar_items()
diff --git a/erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py b/erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py
deleted file mode 100644
index c565707..0000000
--- a/erpnext/patches/v7_0/setup_account_table_for_expense_claim_type_if_exists.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("hr", "doctype", "expense_claim_type")
-	frappe.reload_doc("hr", "doctype", "expense_claim_account")
-
-	if not frappe.db.has_column('Expense Claim Type', 'default_account'):
-		return
-
-	for expense_claim_type in frappe.get_all("Expense Claim Type", fields=["name", "default_account"]):
-		if expense_claim_type.default_account \
-				and frappe.db.exists("Account", expense_claim_type.default_account):
-			doc = frappe.get_doc("Expense Claim Type", expense_claim_type.name)
-			doc.append("accounts", {
-				"company": frappe.db.get_value("Account", expense_claim_type.default_account, "company"),
-				"default_account": expense_claim_type.default_account,
-			})
-			doc.flags.ignore_mandatory = True
-			doc.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/system_settings_setup_complete.py b/erpnext/patches/v7_0/system_settings_setup_complete.py
deleted file mode 100644
index 0feeee9..0000000
--- a/erpnext/patches/v7_0/system_settings_setup_complete.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('System Settings')
-	companies = frappe.db.sql("""select name, country
-		from tabCompany order by creation asc""", as_dict=True)
-	if companies:
-		frappe.db.set_value('System Settings', 'System Settings', 'setup_complete', 1)
-
-	for company in companies:
-		if company.country:
-			frappe.db.set_value('System Settings', 'System Settings', 'country', company.country)
-			break
-
-
diff --git a/erpnext/patches/v7_0/update_autoname_field.py b/erpnext/patches/v7_0/update_autoname_field.py
deleted file mode 100644
index bfa9b28..0000000
--- a/erpnext/patches/v7_0/update_autoname_field.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	doctypes = frappe.db.sql(""" select name, autoname from `tabDocType`
-		where autoname like 'field:%' and allow_rename = 1""", as_dict=1)
-
-	for doctype in doctypes:
-		fieldname = doctype.autoname.split(":")[1]
-		if fieldname:
-			frappe.db.sql(""" update `tab%s` set %s = name """%(doctype.name, fieldname))
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_change_amount_account.py b/erpnext/patches/v7_0/update_change_amount_account.py
deleted file mode 100644
index 1741095..0000000
--- a/erpnext/patches/v7_0/update_change_amount_account.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-
-	for company in frappe.db.sql("""select company from `tabSales Invoice` 
-		where change_amount <> 0 and account_for_change_amount is null group by company""", as_list = 1):
-		cash_account = get_default_bank_cash_account(company[0], 'Cash').get('account')
-		if not cash_account:
-			bank_account = get_default_bank_cash_account(company[0], 'Bank').get('account')
-			cash_account = bank_account
-
-		if cash_account:
-			frappe.db.sql("""update `tabSales Invoice` 
-				set account_for_change_amount = %(cash_account)s where change_amount <> 0 
-				and company = %(company)s and account_for_change_amount is null""",
-				{'cash_account': cash_account, 'company': company[0]})
diff --git a/erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py b/erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py
deleted file mode 100644
index 24da4b1..0000000
--- a/erpnext/patches/v7_0/update_conversion_factor_in_supplier_quotation_item.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('buying', 'doctype', 'supplier_quotation_item')
-
-	frappe.db.sql("""update 
-			`tabSupplier Quotation Item` as sqi_t,
-			(select sqi.item_code as item_code, sqi.uom as uom, ucd.conversion_factor as conversion_factor  
-				from `tabSupplier Quotation Item` sqi left join `tabUOM Conversion Detail` ucd  
-				on ucd.uom = sqi.uom and sqi.item_code = ucd.parent) as conversion_data,
-			`tabItem` as item
-		set 
-			sqi_t.conversion_factor= ifnull(conversion_data.conversion_factor, 1), 
-			sqi_t.stock_qty = (ifnull(conversion_data.conversion_factor, 1) * sqi_t.qty), 
-			sqi_t.stock_uom = item.stock_uom  
-		where 
-			sqi_t.item_code = conversion_data.item_code and
-			sqi_t.uom = conversion_data.uom and sqi_t.item_code = item.name""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_home_page.py b/erpnext/patches/v7_0/update_home_page.py
deleted file mode 100644
index 909825c..0000000
--- a/erpnext/patches/v7_0/update_home_page.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-import erpnext
-
-def execute():
-	frappe.reload_doc('portal', 'doctype', 'homepage_featured_product')
-	frappe.reload_doc('portal', 'doctype', 'homepage')
-	frappe.reload_doc('portal', 'doctype', 'products_settings')
-	frappe.reload_doctype('Item')
-	frappe.reload_doctype('Item Group')
-
-	website_settings = frappe.get_doc('Website Settings', 'Website Settings')
-	if frappe.db.exists('Web Page', website_settings.home_page):
-		header = frappe.db.get_value('Web Page', website_settings.home_page, 'header')
-		if header and header.startswith("<div class='hero text-center'>"):
-			homepage = frappe.get_doc('Homepage', 'Homepage')
-			homepage.company = erpnext.get_default_company() or frappe.get_all("Company")[0].name
-			if '<h1>' in header:
-				homepage.tag_line = header.split('<h1>')[1].split('</h1>')[0] or 'Default Website'
-			else:
-				homepage.tag_line = 'Default Website'
-			homepage.setup_items()
-			homepage.save()
-
-			website_settings.home_page = 'home'
-			website_settings.save()
-
diff --git a/erpnext/patches/v7_0/update_maintenance_module_in_doctype.py b/erpnext/patches/v7_0/update_maintenance_module_in_doctype.py
deleted file mode 100644
index 4c0c6a9..0000000
--- a/erpnext/patches/v7_0/update_maintenance_module_in_doctype.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.set_value("DocType", "Maintenance Schedule", "module", "Maintenance")
-	frappe.db.set_value("DocType", "Maintenance Schedule Detail", "module", "Maintenance")
-	frappe.db.set_value("DocType", "Maintenance Schedule Item", "module", "Maintenance")
-	frappe.db.set_value("DocType", "Maintenance Visit", "module", "Maintenance")
-	frappe.db.set_value("DocType", "Maintenance Visit Purpose", "module", "Maintenance")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_mins_to_first_response.py b/erpnext/patches/v7_0/update_mins_to_first_response.py
deleted file mode 100644
index 1668135..0000000
--- a/erpnext/patches/v7_0/update_mins_to_first_response.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from frappe.core.doctype.communication.communication import update_mins_to_first_communication
-
-def execute():
-	frappe.reload_doctype('Issue')
-	frappe.reload_doctype('Opportunity')
-
-	for doctype in ('Issue', 'Opportunity'):
-		frappe.db.sql('update tab{0} set mins_to_first_response=0'.format(doctype))
-		for parent in frappe.get_all(doctype, order_by='creation desc', limit=500):
-			parent_doc = frappe.get_doc(doctype, parent.name)
-			for communication in frappe.get_all('Communication',
-				filters={'reference_doctype': doctype, 'reference_name': parent.name,
-				'communication_medium': 'Email'},
-				order_by = 'creation asc', limit=2):
-
-				communication_doc = frappe.get_doc('Communication', communication.name)
-
-				update_mins_to_first_communication(parent_doc, communication_doc)
-
-				if parent_doc.mins_to_first_response:
-					continue
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_missing_employee_in_timesheet.py b/erpnext/patches/v7_0/update_missing_employee_in_timesheet.py
deleted file mode 100644
index 54d492b..0000000
--- a/erpnext/patches/v7_0/update_missing_employee_in_timesheet.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("Time Log") and "employee" in frappe.db.get_table_columns("Time Log"):
-		timesheet = frappe.db.sql("""select tl.employee as employee, ts.name as name,
-				tl.modified as modified, tl.modified_by as modified_by, tl.creation as creation, tl.owner as owner
-			from 
-				`tabTimesheet` ts, `tabTimesheet Detail` tsd, `tabTime Log` tl
-			where 
-				tsd.parent = ts.name and tl.from_time = tsd.from_time and tl.to_time = tsd.to_time 
-				and tl.hours = tsd.hours and tl.billing_rate = tsd.billing_rate and tsd.idx=1 
-				and tl.docstatus < 2 and (ts.employee = '' or ts.employee is null)""", as_dict=1)
-		
-		for data in timesheet:
-			ts_doc = frappe.get_doc('Timesheet', data.name)
-			if len(ts_doc.time_logs) == 1:
-				frappe.db.sql(""" update `tabTimesheet` set creation = %(creation)s,
-					owner = %(owner)s, modified = %(modified)s, modified_by = %(modified_by)s,
-					employee = %(employee)s where name = %(name)s""", data)
diff --git a/erpnext/patches/v7_0/update_mode_of_payment_type.py b/erpnext/patches/v7_0/update_mode_of_payment_type.py
deleted file mode 100644
index 9292a1b..0000000
--- a/erpnext/patches/v7_0/update_mode_of_payment_type.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'mode_of_payment')
-
-	frappe.db.sql(""" update `tabMode of Payment` set type = 'Cash' where (type is null or type = '') and name = 'Cash'""")
-
-	for data in frappe.db.sql("""select name from `tabSales Invoice` where is_pos=1 and docstatus<2 and
-		(ifnull(paid_amount, 0) - ifnull(change_amount, 0)) > ifnull(grand_total, 0) and modified > '2016-05-01'""", as_dict=1):
-		if data.name:
-			si_doc = frappe.get_doc("Sales Invoice", data.name)
-			remove_payment = []
-			mode_of_payment = [d.mode_of_payment for d in si_doc.payments if flt(d.amount) > 0]
-			if mode_of_payment != set(mode_of_payment):
-				for payment_data in si_doc.payments:
-					if payment_data.idx != 1 and payment_data.amount == si_doc.grand_total:
-						remove_payment.append(payment_data)
-						frappe.db.sql(""" delete from `tabSales Invoice Payment` 
-							where name = %(name)s""", {'name': payment_data.name})
-
-			if len(remove_payment) > 0:
-				for d in remove_payment:
-					si_doc.remove(d)
-
-				si_doc.set_paid_amount()
-				si_doc.db_set("paid_amount", si_doc.paid_amount, update_modified = False)
-				si_doc.db_set("base_paid_amount", si_doc.base_paid_amount, update_modified = False)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_party_status.py b/erpnext/patches/v7_0/update_party_status.py
deleted file mode 100644
index 0c6b4ea..0000000
--- a/erpnext/patches/v7_0/update_party_status.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	return
-	# for party_type in ('Customer', 'Supplier'):
-	# 	frappe.reload_doctype(party_type)
-	#
-	# 	# set all as default status
-	# 	frappe.db.sql('update `tab{0}` set status=%s'.format(party_type), default_status[party_type])
-	#
-	# 	for doctype in status_depends_on[party_type]:
-	# 		filters = get_filters_for(doctype)
-	# 		parties = frappe.get_all(doctype, fields="{0} as party".format(party_type.lower()),
-	# 			filters=filters, limit_page_length=1)
-	#
-	# 		parties = filter(None, [p.party for p in parties])
-	#
-	# 		if parties:
-	# 			frappe.db.sql('update `tab{0}` set status="Open" where name in ({1})'.format(party_type,
-	# 				', '.join(len(parties) * ['%s'])), parties)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py b/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py
deleted file mode 100644
index e90de50..0000000
--- a/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Supplier Quotation Item')
-	for data in frappe.db.sql(""" select prevdoc_docname, prevdoc_detail_docname, name
-		from `tabSupplier Quotation Item` where prevdoc_docname is not null""", as_dict=True):
-		frappe.db.set_value("Supplier Quotation Item", data.name, "material_request", data.prevdoc_docname)
-		frappe.db.set_value("Supplier Quotation Item", data.name, "material_request_item", data.prevdoc_detail_docname)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_project_in_gl_entry.py b/erpnext/patches/v7_0/update_project_in_gl_entry.py
deleted file mode 100644
index d99e9a4..0000000
--- a/erpnext/patches/v7_0/update_project_in_gl_entry.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("GL Entry")
-	
-	for doctype in ("Delivery Note", "Sales Invoice", "Stock Entry"):
-		frappe.db.sql("""
-			update `tabGL Entry` gle, `tab{0}` dt
-			set gle.project = dt.project
-			where gle.voucher_type=%s and gle.voucher_no = dt.name
-				and ifnull(gle.cost_center, '') != '' and ifnull(dt.project, '') != ''
-		""".format(doctype), doctype)
-		
-	for doctype in ("Purchase Receipt", "Purchase Invoice"):
-		frappe.db.sql("""
-			update `tabGL Entry` gle, `tab{0} Item` dt
-			set gle.project = dt.project
-			where gle.voucher_type=%s and gle.voucher_no = dt.parent and gle.cost_center=dt.cost_center 
-				and ifnull(gle.cost_center, '') != '' and ifnull(dt.project, '') != ''
-		""".format(doctype), doctype)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py b/erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py
deleted file mode 100644
index 2d562bb..0000000
--- a/erpnext/patches/v7_0/update_refdoc_in_landed_cost_voucher.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if "purchase_receipt" not in frappe.db.get_table_columns("Landed Cost Purchase Receipt"):
-		return
-		
-	frappe.reload_doctype("Landed Cost Purchase Receipt")
-	
-	frappe.db.sql("""
-		update `tabLanded Cost Purchase Receipt`
-		set receipt_document_type = 'Purchase Receipt', receipt_document = purchase_receipt
-		where (receipt_document is null or receipt_document = '')
-			and (purchase_receipt is not null and purchase_receipt != '')
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_status_for_timesheet.py b/erpnext/patches/v7_0/update_status_for_timesheet.py
deleted file mode 100644
index 117c40c..0000000
--- a/erpnext/patches/v7_0/update_status_for_timesheet.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""update 
-		`tabTimesheet` as ts,
-		(
-			select min(from_time)as from_time, max(to_time) as to_time, parent from `tabTimesheet Detail` group by parent
-		) as tsd
-		set ts.status = 'Submitted', ts.start_date = tsd.from_time, ts.end_date = tsd.to_time 
-		where tsd.parent = ts.name and ts.status = 'Draft' and ts.docstatus =1""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_status_of_po_so.py b/erpnext/patches/v7_0/update_status_of_po_so.py
deleted file mode 100644
index d630e8f..0000000
--- a/erpnext/patches/v7_0/update_status_of_po_so.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint, flt
-
-def execute():
-	update_po_per_received_per_billed()
-	update_so_per_delivered_per_billed()
-	update_status()
-
-def update_po_per_received_per_billed():
-	frappe.db.sql(""" 
-		update
-			`tabPurchase Order`
-		set
-			`tabPurchase Order`.per_received = round((select sum(if(qty > ifnull(received_qty, 0),
-					ifnull(received_qty, 0), qty)) / sum(qty) *100 from `tabPurchase Order Item`
-					where parent = `tabPurchase Order`.name), 2),
-			`tabPurchase Order`.per_billed = ifnull(round((select sum( if(amount > ifnull(billed_amt, 0),
-					ifnull(billed_amt, 0), amount)) / sum(amount) *100 from `tabPurchase Order Item`
-					where parent = `tabPurchase Order`.name), 2), 0)
-		where
-			net_total > 0
-	""")
-
-def update_so_per_delivered_per_billed():
-	frappe.db.sql(""" 
-		update
-			`tabSales Order`
-		set 
-			`tabSales Order`.per_delivered = round((select sum( if(qty > ifnull(delivered_qty, 0),
-					ifnull(delivered_qty, 0), qty)) / sum(qty) *100 from `tabSales Order Item` 
-					where parent = `tabSales Order`.name), 2), 
-			`tabSales Order`.per_billed = ifnull(round((select sum( if(amount > ifnull(billed_amt, 0),
-					ifnull(billed_amt, 0), amount)) / sum(amount) *100 from `tabSales Order Item`
-					where parent = `tabSales Order`.name), 2), 0)
-		where
-			net_total > 0
-	""")
-
-def update_status():
-	frappe.db.sql("""
-		update
-			`tabSales Order`
-		set status = (Case when status = 'Closed' then 'Closed'
-			When per_delivered < 100 and per_billed < 100 and docstatus = 1 then 'To Deliver and Bill'
-			when per_delivered = 100 and per_billed < 100 and docstatus = 1 then 'To Bill'
-			when per_delivered < 100 and per_billed = 100 and docstatus = 1 then 'To Deliver'
-			when per_delivered = 100 and per_billed = 100 and docstatus = 1 then 'Completed'
-			when order_type = 'Maintenance' and per_billed = 100 and docstatus = 1 then 'Completed'
-			when docstatus = 2 then 'Cancelled'
-			else 'Draft'
-		End)""")
-
-	frappe.db.sql("""
-		update 
-			`tabPurchase Order` 
-		set status = (Case when status = 'Closed' then 'Closed'
-			when status = 'Delivered' then 'Delivered'
-			When per_received < 100 and per_billed < 100 and docstatus = 1 then 'To Receive and Bill'
-			when per_received = 100 and per_billed < 100 and docstatus = 1 then 'To Bill'
-			when per_received < 100 and per_billed = 100 and docstatus = 1 then 'To Receive'
-			when per_received = 100 and per_billed = 100 and docstatus = 1 then 'Completed'
-			when docstatus = 2 then 'Cancelled'
-			else 'Draft'
-		End)""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py b/erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py
deleted file mode 100644
index 9b2b247..0000000
--- a/erpnext/patches/v7_0/update_status_of_zero_amount_sales_order.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for data in frappe.get_all('Sales Order', fields = ["name"], filters = [["docstatus", "=", "1"], ["grand_total", "=", "0"]]):
-		sales_order = frappe.get_doc('Sales Order', data.name)
-		sales_order.set_status(update=True, update_modified = False)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_timesheet_communications.py b/erpnext/patches/v7_0/update_timesheet_communications.py
deleted file mode 100644
index 203471e..0000000
--- a/erpnext/patches/v7_0/update_timesheet_communications.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("Time Log"):
-		timesheet = frappe.db.sql("""SELECT ts.name AS name, tl.name AS timelogname,
-				tl.modified AS modified, tl.modified_by AS modified_by, tl.creation AS creation, tl.owner AS owner
-			FROM 
-				`tabTimesheet` ts, `tabTimesheet Detail` tsd, `tabTime Log` tl
-			WHERE 
-				tsd.parent = ts.name AND tl.from_time = tsd.from_time AND tl.to_time = tsd.to_time 
-				AND tl.hours = tsd.hours AND tl.billing_rate = tsd.billing_rate AND tsd.idx=1 
-				AND tl.docstatus < 2""", as_dict=1)
-				
-		for data in timesheet:
-			frappe.db.sql(""" update `tabTimesheet` set creation = %(creation)s,
-				owner = %(owner)s, modified = %(modified)s, modified_by = %(modified_by)s
-				where name = %(name)s""", data)
-								
-			frappe.db.sql("""
-				update 
-					tabCommunication 
-				set 
-					reference_doctype = "Timesheet", reference_name = %(timesheet)s
-				where 
-					reference_doctype = "Time Log" and reference_name = %(timelog)s
-			""", {'timesheet': data.name, 'timelog': data.timelogname}, auto_commit=1)
diff --git a/erpnext/patches/v7_1/__init__.py b/erpnext/patches/v7_1/__init__.py
deleted file mode 100644
index 519ff49..0000000
--- a/erpnext/patches/v7_1/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/add_account_user_role_for_timesheet.py b/erpnext/patches/v7_1/add_account_user_role_for_timesheet.py
deleted file mode 100644
index 7372b0c..0000000
--- a/erpnext/patches/v7_1/add_account_user_role_for_timesheet.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if not frappe.db.get_value('DocPerm', {'parent': 'Timesheet', 'role': 'Accounts User', 'permlevel': 1}):
-		doc = frappe.get_doc('DocType', 'Timesheet')
-		doc.append('permissions', {
-			'role': "Accounts User",
-			'permlevel': 0,
-			'read': 1,
-			'write': 1,
-			'create': 1,
-			'delete': 1,
-			'submit': 1,
-			'cancel': 1,
-			'amend': 1,
-			'report': 1,
-			'email': 1
-		})
-
-		doc.append('permissions', {
-			'role': "Accounts User",
-			'permlevel': 1,
-			'read': 1,
-			'write': 1
-		})
-
-		doc.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/add_field_for_task_dependent.py b/erpnext/patches/v7_1/add_field_for_task_dependent.py
deleted file mode 100644
index 65b1c74..0000000
--- a/erpnext/patches/v7_1/add_field_for_task_dependent.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Task')
-	for t in frappe.get_all('Task', fields=['name']):
-		task = frappe.get_doc('Task', t.name)
-		task.update_depends_on()
-		if task.depends_on_tasks:
-			task.db_set('depends_on_tasks', task.depends_on_tasks, update_modified=False)
diff --git a/erpnext/patches/v7_1/fix_link_for_customer_from_lead.py b/erpnext/patches/v7_1/fix_link_for_customer_from_lead.py
deleted file mode 100644
index 33f809f..0000000
--- a/erpnext/patches/v7_1/fix_link_for_customer_from_lead.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for c in frappe.db.sql('select name from tabCustomer where ifnull(lead_name,"")!=""'):
-		customer = frappe.get_doc('Customer', c[0])
-		customer.update_lead_status()
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py b/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py
deleted file mode 100644
index d1ec7c6..0000000
--- a/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
-
-	frappe.db.sql(""" update 
-			`tabTimesheet` as ts, 
-		(select 
-			sum(billing_amount) as billing_amount, sum(billing_hours) as billing_hours, time_sheet 
-			from `tabSales Invoice Timesheet` where docstatus = 1 group by time_sheet
-		) as sit
-		set 
-			ts.total_billed_amount = sit.billing_amount, ts.total_billed_hours = sit.billing_hours, 
-			ts.per_billed = ((sit.billing_amount * 100)/ts.total_billable_amount)
-		where ts.name = sit.time_sheet and ts.docstatus = 1""")
-
-	frappe.db.sql(""" update `tabTimesheet Detail` tsd, `tabTimesheet` ts set tsd.sales_invoice = ts.sales_invoice
-		where tsd.parent = ts.name and ts.sales_invoice is not null""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/rename_field_timesheet.py b/erpnext/patches/v7_1/rename_field_timesheet.py
deleted file mode 100644
index 3690a2e..0000000
--- a/erpnext/patches/v7_1/rename_field_timesheet.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	doctype = 'Timesheet'
-	fields_dict = {'total_billing_amount': 'total_billable_amount', 'total_billing_hours': 'total_billable_hours'}
-
-	for old_fieldname, new_fieldname in fields_dict.items():
-		if old_fieldname in frappe.db.get_table_columns(doctype):
-			rename_field(doctype, old_fieldname, new_fieldname)
diff --git a/erpnext/patches/v7_1/rename_quality_inspection_field.py b/erpnext/patches/v7_1/rename_quality_inspection_field.py
deleted file mode 100644
index 3b5a7d9..0000000
--- a/erpnext/patches/v7_1/rename_quality_inspection_field.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import *
-
-def execute():
-	for doctype in ("Purchase Receipt Item", "Delivery Note Item"):
-		frappe.reload_doctype(doctype)
-
-		table_columns = frappe.db.get_table_columns(doctype)
-		if "qa_no" in table_columns:
-			rename_field(doctype, "qa_no", "quality_inspection")
-
-	frappe.reload_doctype("Item")
-	rename_field("Item", "inspection_required", "inspection_required_before_purchase")
-
-	frappe.reload_doc('stock', 'doctype', 'quality_inspection')
-	frappe.db.sql("""
-		update
-			`tabQuality Inspection`
-		set
-			reference_type = 'Purchase Receipt', reference_name = purchase_receipt_no
-		where
-			ifnull(purchase_receipt_no, '') != '' and inspection_type = 'Incoming'
-	""")
-
-	frappe.db.sql("""
-		update
-			`tabQuality Inspection`
-		set
-			reference_type = 'Delivery Note', reference_name = delivery_note_no
-		where
-			ifnull(delivery_note_no, '') != '' and inspection_type = 'Outgoing'
-	""")
-
-	for old_fieldname in ["purchase_receipt_no", "delivery_note_no"]:
-		update_reports("Quality Inspection", old_fieldname, "reference_name")
-		update_users_report_view_settings("Quality Inspection", old_fieldname, "reference_name")
-		update_property_setters("Quality Inspection", old_fieldname, "reference_name")
diff --git a/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py b/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py
deleted file mode 100644
index aca2108..0000000
--- a/erpnext/patches/v7_1/repost_stock_for_deleted_bins_for_merging_items.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import repost_stock
-
-def execute():
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order_item')
-	frappe.reload_doc('manufacturing', 'doctype', 'work_order')
-	
-	modified_items = frappe.db.sql_list("""
-		select name from `tabItem` 
-		where is_stock_item=1 and modified >= '2016-10-31'
-	""")
-	
-	if not modified_items:
-		return
-	
-	item_warehouses_with_transactions = []
-	transactions = ("Sales Order Item", "Material Request Item", "Purchase Order Item", 
-		"Stock Ledger Entry", "Packed Item")
-	
-	for doctype in transactions:
-		item_warehouses_with_transactions += list(frappe.db.sql("""
-			select distinct item_code, warehouse
-			from `tab{0}` where docstatus=1 and item_code in ({1})"""
-			.format(doctype, ', '.join(['%s']*len(modified_items))), tuple(modified_items)))
-			
-	item_warehouses_with_transactions += list(frappe.db.sql("""
-		select distinct production_item, fg_warehouse 
-		from `tabWork Order` where docstatus=1 and production_item in ({0})"""
-		.format(', '.join(['%s']*len(modified_items))), tuple(modified_items)))
-	
-	item_warehouses_with_transactions += list(frappe.db.sql("""
-		select distinct pr_item.item_code, pr_item.source_warehouse 
-		from `tabWork Order` pr, `tabWork Order Item` pr_item 
-		where pr_item.parent and pr.name and pr.docstatus=1 and pr_item.item_code in ({0})"""
-		.format(', '.join(['%s']*len(modified_items))), tuple(modified_items)))
-	
-	item_warehouses_with_bin = list(frappe.db.sql("select distinct item_code, warehouse from `tabBin`"))
-	
-	item_warehouses_with_missing_bin = list(
-		set(item_warehouses_with_transactions) - set(item_warehouses_with_bin))	
-	
-	for item_code, warehouse in item_warehouses_with_missing_bin:
-		repost_stock(item_code, warehouse)
diff --git a/erpnext/patches/v7_1/save_stock_settings.py b/erpnext/patches/v7_1/save_stock_settings.py
deleted file mode 100644
index d3f0263..0000000
--- a/erpnext/patches/v7_1/save_stock_settings.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	stock_settings = frappe.get_doc('Stock Settings')
-	
-	if stock_settings.default_warehouse \
-		and not frappe.db.exists("Warehouse", stock_settings.default_warehouse):
-			stock_settings.default_warehouse = None
-			
-	if stock_settings.stock_uom and not frappe.db.exists("UOM", stock_settings.stock_uom):
-		stock_settings.stock_uom = None
-		
-	stock_settings.flags.ignore_mandatory = True
-	stock_settings.save()
diff --git a/erpnext/patches/v7_1/set_budget_against_as_cost_center.py b/erpnext/patches/v7_1/set_budget_against_as_cost_center.py
deleted file mode 100644
index dd9a432..0000000
--- a/erpnext/patches/v7_1/set_budget_against_as_cost_center.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "budget")
-	frappe.db.sql("""
-		update
-			`tabBudget`
-		set
-			budget_against = 'Cost Center'
-		""")
diff --git a/erpnext/patches/v7_1/set_currency_exchange_date.py b/erpnext/patches/v7_1/set_currency_exchange_date.py
deleted file mode 100644
index 2a2d420..0000000
--- a/erpnext/patches/v7_1/set_currency_exchange_date.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Currency Exchange")
-	frappe.db.sql("""
-		update `tabCurrency Exchange` 
-		set `date` = '2010-01-01' 
-		where date is null or date = '0000-00-00'
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/set_prefered_contact_email.py b/erpnext/patches/v7_1/set_prefered_contact_email.py
deleted file mode 100644
index 3b68e22..0000000
--- a/erpnext/patches/v7_1/set_prefered_contact_email.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('User')
-	for d in frappe.get_all("Employee"):
-		employee = frappe.get_doc("Employee", d.name)
-		if employee.company_email:
-			employee.prefered_contact_email = "Company Email"
-			employee.prefered_email = employee.company_email
-		elif employee.personal_email:
-			employee.prefered_contact_email = "Personal Email"
-			employee.prefered_email = employee.personal_email
-		elif employee.user_id:
-			employee.prefered_contact_email = "User ID"
-			employee.prefered_email = employee.user_id
-		employee.db_update()
diff --git a/erpnext/patches/v7_1/set_sales_person_status.py b/erpnext/patches/v7_1/set_sales_person_status.py
deleted file mode 100644
index 929beac..0000000
--- a/erpnext/patches/v7_1/set_sales_person_status.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('setup','doctype','sales_person')
-	frappe.db.sql("""update `tabSales Person` set enabled=1 
-		where (employee is null or employee = '' 
-			or employee IN (select employee from tabEmployee where status != "Left"))""")
diff --git a/erpnext/patches/v7_1/set_student_guardian.py b/erpnext/patches/v7_1/set_student_guardian.py
deleted file mode 100644
index 093c0bf..0000000
--- a/erpnext/patches/v7_1/set_student_guardian.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists("DocType", "Guardian"):
-
-		# 'Schools' module changed to the 'Education'
-		# frappe.reload_doc("schools", "doctype", "student")
-		# frappe.reload_doc("schools", "doctype", "student_guardian")
-		# frappe.reload_doc("schools", "doctype", "student_sibling")
-
-		frappe.reload_doc("education", "doctype", "student")
-		frappe.reload_doc("education", "doctype", "student_guardian")
-		frappe.reload_doc("education", "doctype", "student_sibling")
-		if "student" not in frappe.db.get_table_columns("Guardian"):
-			return
-		guardian = frappe.get_all("Guardian", fields=["name", "student"])
-		for d in guardian:
-			if d.student:
-				student = frappe.get_doc("Student", d.student)
-				if student:
-					student.append("guardians", {"guardian": d.name})
-					student.save()
diff --git a/erpnext/patches/v7_1/set_total_amount_currency_in_je.py b/erpnext/patches/v7_1/set_total_amount_currency_in_je.py
deleted file mode 100644
index 8426ddcd..0000000
--- a/erpnext/patches/v7_1/set_total_amount_currency_in_je.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext import get_default_currency
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "journal_entry")
-
-	frappe.db.sql(""" update `tabJournal Entry` set total_amount_currency = %s
-		where ifnull(multi_currency, 0) = 0
-		and (pay_to_recd_from is not null or pay_to_recd_from != "") """, get_default_currency())
-
-	for je in frappe.db.sql(""" select name from `tabJournal Entry` where multi_currency = 1
-		and (pay_to_recd_from is not null or pay_to_recd_from != "")""", as_dict=1):
-
-		doc = frappe.get_doc("Journal Entry", je.name)
-		for d in doc.get('accounts'):
-			if d.party_type and d.party:
-				total_amount_currency = d.account_currency
-
-			elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
-				total_amount_currency = d.account_currency
-
-		frappe.db.set_value("Journal Entry", je.name, "total_amount_currency",
-			total_amount_currency, update_modified=False)
diff --git a/erpnext/patches/v7_1/update_bom_base_currency.py b/erpnext/patches/v7_1/update_bom_base_currency.py
deleted file mode 100644
index 9a59209..0000000
--- a/erpnext/patches/v7_1/update_bom_base_currency.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext import get_default_currency
-
-def execute():
-	frappe.reload_doc("manufacturing", "doctype", "bom")
-	frappe.reload_doc("manufacturing", "doctype", "bom_item")
-	frappe.reload_doc("manufacturing", "doctype", "bom_explosion_item")
-	frappe.reload_doc("manufacturing", "doctype", "bom_operation")
-	frappe.reload_doc("manufacturing", "doctype", "bom_scrap_item")
-
-	frappe.db.sql(""" update `tabBOM Operation` set base_hour_rate = hour_rate,
-		base_operating_cost = operating_cost """)
-
-	frappe.db.sql(""" update `tabBOM Item` set base_rate = rate, base_amount = amount """)
-	frappe.db.sql(""" update `tabBOM Scrap Item` set base_rate = rate, base_amount = amount """)
-
-	frappe.db.sql(""" update `tabBOM` set `tabBOM`.base_operating_cost = `tabBOM`.operating_cost, 
-		`tabBOM`.base_raw_material_cost = `tabBOM`.raw_material_cost,
-		`tabBOM`.currency = (select default_currency from `tabCompany` where name = `tabBOM`.company)""")
diff --git a/erpnext/patches/v7_1/update_component_type.py b/erpnext/patches/v7_1/update_component_type.py
deleted file mode 100644
index 24ca057..0000000
--- a/erpnext/patches/v7_1/update_component_type.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import flt
-
-def execute():
-	frappe.reload_doc('Payroll', 'doctype', 'salary_component')
-	sal_components = frappe.db.sql("""
-		select DISTINCT salary_component, parentfield from `tabSalary Detail`""", as_dict=True)
-
-	if sal_components:
-		for sal_component in sal_components:
-			if sal_component.parentfield == "earnings":
-				frappe.db.sql("""update `tabSalary Component` set type='Earning' where salary_component=%(sal_comp)s""",{"sal_comp": sal_component.salary_component})
-			else:
-				frappe.db.sql("""update `tabSalary Component` set type='Deduction' where salary_component=%(sal_comp)s""",{"sal_comp": sal_component.salary_component})
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/update_invoice_status.py b/erpnext/patches/v7_1/update_invoice_status.py
deleted file mode 100644
index 851af80..0000000
--- a/erpnext/patches/v7_1/update_invoice_status.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-	frappe.reload_doc('accounts', 'doctype', 'purchase_invoice')
-
-	frappe.db.sql(""" 
-		update 
-			`tabPurchase Invoice` 
-		set 
-			status = (Case When outstanding_amount = 0 and docstatus = 1 and is_return = 0 then 'Paid'
-			when due_date < CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Overdue'
-			when due_date >= CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Unpaid'
-			when outstanding_amount < 0 and docstatus =1 then 'Debit Note Issued'
-			when is_return = 1 and docstatus =1 then 'Return'
-			when docstatus = 2 then 'Cancelled'
-			else 'Draft'
-		End)""")
-
-	frappe.db.sql(""" 
-		update 
-			`tabSales Invoice` 
-		set status = (Case When outstanding_amount = 0 and docstatus = 1 and is_return = 0 then 'Paid'
-			when due_date < CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Overdue'
-			when due_date >= CURDATE() and outstanding_amount > 0 and docstatus =1 then 'Unpaid'
-			when outstanding_amount < 0 and docstatus =1 then 'Credit Note Issued'
-			when is_return = 1 and docstatus =1 then 'Return'
-			when docstatus = 2 then 'Cancelled'
-			else 'Draft'
-		End)""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/update_lead_source.py b/erpnext/patches/v7_1/update_lead_source.py
deleted file mode 100644
index a2a48a6..0000000
--- a/erpnext/patches/v7_1/update_lead_source.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def execute():
-	from erpnext.setup.setup_wizard.operations.install_fixtures import default_lead_sources
-
-	frappe.reload_doc('crm', 'doctype', 'lead_source')
-
-	frappe.local.lang = frappe.db.get_default("lang") or 'en'
-
-	for s in default_lead_sources:
-		insert_lead_source(_(s))
-
-	# get lead sources in existing forms (customized)
-	# and create a document if not created
-	for d in ['Lead', 'Opportunity', 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
-		sources = frappe.db.sql_list('select distinct source from `tab{0}`'.format(d))
-		for s in sources:
-			if s and s not in default_lead_sources:
-				insert_lead_source(s)
-
-		# remove customization for source
-		for p in frappe.get_all('Property Setter', {'doc_type':d, 'field_name':'source', 'property':'options'}):
-			frappe.delete_doc('Property Setter', p.name)
-
-def insert_lead_source(s):
-	if not frappe.db.exists('Lead Source', s):
-		frappe.get_doc(dict(doctype='Lead Source', source_name=s)).insert()
diff --git a/erpnext/patches/v7_1/update_missing_salary_component_type.py b/erpnext/patches/v7_1/update_missing_salary_component_type.py
deleted file mode 100644
index 824f2b8..0000000
--- a/erpnext/patches/v7_1/update_missing_salary_component_type.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-from frappe.utils import cstr
-
-'''
-Some components do not have type set, try and guess whether they turn up in
-earnings or deductions in existing salary slips
-'''
-
-def execute():
-	frappe.reload_doc("accounts", "doctype", "salary_component_account")
-	frappe.reload_doc("Payroll", "doctype", "salary_component")
-	frappe.reload_doc("Payroll", "doctype", "taxable_salary_slab")
-
-	for s in frappe.db.sql('''select name, type, salary_component_abbr from `tabSalary Component`
-			where ifnull(type, "")="" or ifnull(salary_component_abbr, "") = ""''', as_dict=1):
-
-		component = frappe.get_doc('Salary Component', s.name)
-
-		# guess
-		if not s.type:
-			guess = frappe.db.sql('''select
-				parentfield from `tabSalary Detail`
-				where salary_component=%s limit 1''', s.name)
-
-			if guess:
-				component.type = 'Earning' if guess[0][0]=='earnings' else 'Deduction'
-
-			else:
-				component.type = 'Deduction'
-
-		if not s.salary_component_abbr:
-			abbr = ''.join([c[0] for c in component.salary_component.split()]).upper()
-
-			abbr_count = frappe.db.sql("""
-				select
-					count(name)
-				from
-					`tabSalary Component`
-				where
-					salary_component_abbr = %s or salary_component_abbr like %s
-				""", (abbr, abbr + "-%%"))
-
-			if abbr_count and abbr_count[0][0] > 0:
-				abbr = abbr + "-" + cstr(abbr_count[0][0])
-
-			component.salary_component_abbr = abbr
-
-		component.save()
diff --git a/erpnext/patches/v7_1/update_portal_roles.py b/erpnext/patches/v7_1/update_portal_roles.py
deleted file mode 100644
index 482586b..0000000
--- a/erpnext/patches/v7_1/update_portal_roles.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Role')
-	frappe.reload_doctype('User')
-	for role_name in ('Customer', 'Supplier', 'Student'):
-		if frappe.db.exists('Role', role_name):
-			frappe.db.set_value('Role', role_name, 'desk_access', 0)
-		else:
-			frappe.get_doc(dict(doctype='Role', role_name=role_name, desk_access=0)).insert()
-
-
-	# set customer, supplier roles
-	for c in frappe.get_all('Contact', fields=['user'], filters={'ifnull(user, "")': ('!=', '')}):
-		user = frappe.get_doc('User', c.user)
-		user.flags.ignore_validate = True
-		user.flags.ignore_mandatory = True
-		user.save()
-
-
diff --git a/erpnext/patches/v7_1/update_total_billing_hours.py b/erpnext/patches/v7_1/update_total_billing_hours.py
deleted file mode 100644
index b9c9602..0000000
--- a/erpnext/patches/v7_1/update_total_billing_hours.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
-
-	frappe.db.sql("""update tabTimesheet set total_billable_hours=total_hours 
-		where total_billable_amount>0 and docstatus = 1""")
-
-	frappe.db.sql("""update `tabTimesheet Detail` set billing_hours=hours where docstatus < 2""")
-
-	frappe.db.sql(""" update `tabSales Invoice Timesheet` set billing_hours = (select total_billable_hours from `tabTimesheet`
-		where name = time_sheet) where time_sheet is not null""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/__init__.py b/erpnext/patches/v7_2/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v7_2/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py b/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py
deleted file mode 100644
index d2583b9..0000000
--- a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# frappe.reload_doctype('Salary Slip', 'Salary Component')
-	frappe.reload_doc("Payroll", "doctype", "Salary Slip")
-	frappe.reload_doc("Payroll", "doctype", "Salary Component")
-	salary_components = [['Arrear', "ARR"], ['Leave Encashment', 'LENC']]
-	for salary_component, salary_abbr in salary_components:
-		if not frappe.db.exists('Salary Component', salary_component):
-			sal_comp = frappe.get_doc({
-				"doctype": "Salary Component",
-				"salary_component": salary_component,
-				"type": "Earning",
-				"salary_component_abbr": salary_abbr
-			}).insert()
-
-	salary_slips = frappe.db.sql("""select name, arrear_amount, leave_encashment_amount from `tabSalary Slip`
-					where docstatus !=2 and (arrear_amount > 0 or leave_encashment_amount > 0)""", as_dict=True)
-
-	for salary_slip in salary_slips:
-		doc = frappe.get_doc('Salary Slip', salary_slip.name)
-
-		if salary_slip.get("arrear_amount") > 0:
-			r = doc.append('earnings', {
-				'salary_component': 'Arrear',
-				'amount': salary_slip.arrear_amount
-			})
-			r.db_update()
-
-		if salary_slip.get("leave_encashment_amount") > 0:
-			r = doc.append('earnings', {
-				'salary_component': 'Leave Encashment',
-				'amount': salary_slip.leave_encashment_amount
-			})
-			r.db_update()
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/contact_address_links.py b/erpnext/patches/v7_2/contact_address_links.py
deleted file mode 100644
index 200434c..0000000
--- a/erpnext/patches/v7_2/contact_address_links.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links
-from frappe.utils import update_progress_bar
-
-def execute():
-	frappe.reload_doc('core', 'doctype', 'dynamic_link')
-	frappe.reload_doc('contacts', 'doctype', 'contact')
-	frappe.reload_doc('contacts', 'doctype', 'address')
-	map_fields = (
-		('Customer', 'customer'),
-		('Supplier', 'supplier'),
-		('Lead', 'lead'),
-		('Sales Partner', 'sales_partner')
-	)
-	for doctype in ('Contact', 'Address'):
-		if frappe.db.has_column(doctype, 'customer'):
-			items = frappe.get_all(doctype)
-			for i, doc in enumerate(items):
-				doc = frappe.get_doc(doctype, doc.name)
-				dirty = False
-				for field in map_fields:
-					if doc.get(field[1]):
-						doc.append('links', dict(link_doctype=field[0], link_name=doc.get(field[1])))
-						dirty = True
-
-					if dirty:
-						deduplicate_dynamic_links(doc)
-						doc.update_children()
-
-					update_progress_bar('Updating {0}'.format(doctype), i, len(items))
-			print
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/delete_fleet_management_module_def.py b/erpnext/patches/v7_2/delete_fleet_management_module_def.py
deleted file mode 100644
index 542ac11..0000000
--- a/erpnext/patches/v7_2/delete_fleet_management_module_def.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists('Module Def', 'Fleet Management'):
-		frappe.db.sql("""delete from `tabModule Def`
-			where module_name = 'Fleet Management'""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py b/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py
deleted file mode 100644
index ec6f8af..0000000
--- a/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]:
-		child_table = 'Purchase Receipt Item Supplied' if doctype != 'Purchase Order' else 'Purchase Order Item Supplied'
-		for data in frappe.db.sql(""" select distinct `tab{doctype}`.name from `tab{doctype}` , `tab{child_table}`
-			where `tab{doctype}`.name = `tab{child_table}`.parent and `tab{doctype}`.docstatus != 2
-			and `tab{doctype}`.is_subcontracted = 'No' """.format(doctype = doctype, child_table = child_table), as_dict=1):
-			frappe.db.sql(""" delete from `tab{child_table}` 
-				where parent = %s and parenttype = %s""".format(child_table= child_table), (data.name, doctype))
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/make_all_assessment_group.py b/erpnext/patches/v7_2/make_all_assessment_group.py
deleted file mode 100644
index f3ec628..0000000
--- a/erpnext/patches/v7_2/make_all_assessment_group.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if not frappe.db.exists({"doctype": "Assessment Group","assessment_group_name": "All Assessment Groups"}):
-		frappe.reload_doc("education", "doctype", "assessment_group")
-		doc = frappe.new_doc("Assessment Group")
-		doc.assessment_group_name = "All Assessment Groups"
-		doc.is_group = 1
-		doc.flags.ignore_mandatory = True
-		doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/mark_students_active.py b/erpnext/patches/v7_2/mark_students_active.py
deleted file mode 100644
index 7289e4a..0000000
--- a/erpnext/patches/v7_2/mark_students_active.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# 'Schools' module changed to the 'Education'
-	# frappe.reload_doc('schools', 'doctype', 'student_group_student')
-
-	frappe.reload_doc('education', 'doctype', 'student_group_student')
-	frappe.db.sql("update `tabStudent Group Student` set active=1")
diff --git a/erpnext/patches/v7_2/rename_att_date_attendance.py b/erpnext/patches/v7_2/rename_att_date_attendance.py
deleted file mode 100644
index 7f06d8f..0000000
--- a/erpnext/patches/v7_2/rename_att_date_attendance.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import update_reports, update_users_report_view_settings, update_property_setters
-
-def execute():
-	if "att_date" not in frappe.db.get_table_columns("Attendance"):
-		return
-	frappe.reload_doc("hr", "doctype", "attendance")
-	frappe.db.sql("""update `tabAttendance` 
-	 		set attendance_date = att_date
-			where attendance_date is null or attendance_date = '0000-00-00'""")
-	
-	update_reports("Attendance", "att_date", "attendance_date")
-	update_users_report_view_settings("Attendance", "att_date", "attendance_date")
-	update_property_setters("Attendance", "att_date", "attendance_date")
diff --git a/erpnext/patches/v7_2/rename_evaluation_criteria.py b/erpnext/patches/v7_2/rename_evaluation_criteria.py
deleted file mode 100644
index c6520b1..0000000
--- a/erpnext/patches/v7_2/rename_evaluation_criteria.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	# 'Schools' module changed to the 'Education'
-
-
-	frappe.rename_doc("DocType", "Evaluation Criteria", "Assessment Criteria", force=True)
-	# frappe.reload_doc("schools", "doctype", "assessment_criteria")
-	frappe.reload_doc("education", "doctype", "assessment_criteria")
-	if 'evaluation_criteria' in frappe.db.get_table_columns('Assessment Criteria'):
-		rename_field("Assessment Criteria", "evaluation_criteria", "assessment_criteria")
-
-	frappe.rename_doc("DocType", "Assessment Evaluation Criteria", "Assessment Plan Criteria", force=True)
-	# frappe.reload_doc("schools", "doctype", "assessment_plan_criteria")
-	frappe.reload_doc("education", "doctype", "assessment_plan_criteria")
-	if 'evaluation_criteria' in frappe.db.get_table_columns('Assessment Plan'):
-		rename_field("Assessment Plan Criteria", "evaluation_criteria", "assessment_criteria")
-
-	# frappe.reload_doc("schools", "doctype", "assessment_plan")
-	frappe.reload_doc("education", "doctype", "assessment_plan")
-	rename_field("Assessment Plan", "evaluation_criterias", "assessment_criteria")
-
-	# frappe.reload_doc("schools", "doctype", "assessment_result_detail")
-	frappe.reload_doc("education", "doctype", "assessment_result_detail")
-	if 'evaluation_criteria' in frappe.db.get_table_columns('Assessment Result Detail'):
-		rename_field("Assessment Result Detail", "evaluation_criteria", "assessment_criteria")
-
-	frappe.rename_doc("DocType", "Course Evaluation Criteria", "Course Assessment Criteria", force=True)
-	# frappe.reload_doc("schools", "doctype", "course_assessment_criteria")
-	frappe.reload_doc("education", "doctype", "course_assessment_criteria")
-	if 'evaluation_criteria' in frappe.db.get_table_columns('Course Assessment Criteria'):
-		rename_field("Course Assessment Criteria", "evaluation_criteria", "assessment_criteria")
-
-	# frappe.reload_doc("schools", "doctype", "course")
-	frappe.reload_doc("education", "doctype", "course")
-	if 'evaluation_criteria' in frappe.db.get_table_columns('Course'):
-		rename_field("Course", "evaluation_criterias", "assessment_criteria")
diff --git a/erpnext/patches/v7_2/set_null_value_to_fields.py b/erpnext/patches/v7_2/set_null_value_to_fields.py
deleted file mode 100644
index 6388be4..0000000
--- a/erpnext/patches/v7_2/set_null_value_to_fields.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	fields = {"Cost Center": "project", "Project": "cost_center"}
-	for budget_against, field in fields.items():
-		frappe.db.sql(""" update `tabBudget` set {field} = null
-			where budget_against = %s """.format(field = field), budget_against)
diff --git a/erpnext/patches/v7_2/setup_auto_close_settings.py b/erpnext/patches/v7_2/setup_auto_close_settings.py
deleted file mode 100644
index 4eef2b9..0000000
--- a/erpnext/patches/v7_2/setup_auto_close_settings.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# update the selling settings and set the close_opportunity_after_days
-	frappe.reload_doc("selling", "doctype", "selling_settings")
-	frappe.db.set_value("Selling Settings", "Selling Settings", "close_opportunity_after_days", 15)
-
-	# Auto close Replied opportunity
-	frappe.db.sql("""update `tabOpportunity` set status='Closed' where status='Replied'
-		 and date_sub(curdate(), interval 15 Day)>modified""")
-
-	# create Support Settings doctype and update close_issue_after_days
-	frappe.reload_doc("support", "doctype", "support_settings")
-	frappe.db.set_value("Support Settings", "Support Settings", "close_issue_after_days", 7)
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/stock_uom_in_selling.py b/erpnext/patches/v7_2/stock_uom_in_selling.py
deleted file mode 100644
index d029555..0000000
--- a/erpnext/patches/v7_2/stock_uom_in_selling.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Sales Order')
-	frappe.reload_doctype('Sales Invoice')
-	frappe.reload_doctype('Quotation')
-	frappe.reload_doctype('Delivery Note')
-
-	doctype_list = ['Sales Order Item', 'Delivery Note Item', 'Quotation Item', 'Sales Invoice Item']
-
-	for doctype in doctype_list:
-		frappe.reload_doctype(doctype)
-		frappe.db.sql("""update `tab{doctype}` 
-		 		set uom = stock_uom, conversion_factor = 1, stock_qty = qty""".format(doctype=doctype))
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py b/erpnext/patches/v7_2/update_abbr_in_salary_slips.py
deleted file mode 100644
index 57432fe..0000000
--- a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('Payroll', 'doctype', 'Salary Slip')
-	if not frappe.db.has_column('Salary Detail', 'abbr'):
-		return
-
-	salary_details = frappe.db.sql("""select abbr, salary_component, name from `tabSalary Detail`
-				where abbr is null or abbr = ''""", as_dict=True)
-
-	for salary_detail in salary_details:
-		salary_component_abbr = frappe.get_value("Salary Component", salary_detail.salary_component, "salary_component_abbr")
-		frappe.db.sql("""update `tabSalary Detail` set abbr = %s where name = %s""",(salary_component_abbr, salary_detail.name))
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_assessment_modules.py b/erpnext/patches/v7_2/update_assessment_modules.py
deleted file mode 100644
index 2b5e774..0000000
--- a/erpnext/patches/v7_2/update_assessment_modules.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	#Rename Grading Structure to Grading Scale
-	if not frappe.db.exists("DocType", "Grading Scale"):
-		frappe.rename_doc("DocType", "Grading Structure", "Grading Scale", force=True)
-	if not frappe.db.exists("DocType", "Grading Scale Interval"):
-		frappe.rename_doc("DocType", "Grade Interval", "Grading Scale Interval", force=True)
-
-	# frappe.reload_doc("schools", "doctype", "grading_scale_interval")
-	frappe.reload_doc("education", "doctype", "grading_scale_interval")
-	if "to_score" in frappe.db.get_table_columns("Grading Scale Interval"):
-		rename_field("Grading Scale Interval", "to_score", "threshold")
-
-	if not frappe.db.exists("DocType", "Assessment Plan"):
-		frappe.rename_doc("DocType", "Assessment", "Assessment Plan", force=True)
-
-	# 'Schools' module changed to the 'Education'
-	# frappe.reload_doc("schools", "doctype", "assessment_plan")
-
-	#Rename Assessment Results
-	frappe.reload_doc("education", "doctype", "assessment_plan")
-	if "grading_structure" in frappe.db.get_table_columns("Assessment Plan"):
-		rename_field("Assessment Plan", "grading_structure", "grading_scale")
-
-	# frappe.reload_doc("schools", "doctype", "assessment_result")
-	# frappe.reload_doc("schools", "doctype", "assessment_result_detail")
-	# frappe.reload_doc("schools", "doctype", "assessment_criteria")
-	frappe.reload_doc("education", "doctype", "assessment_result")
-	frappe.reload_doc("education", "doctype", "assessment_result_detail")
-	frappe.reload_doc("education", "doctype", "assessment_criteria")
-
-
-	for assessment in frappe.get_all("Assessment Plan", 
-			fields=["name", "grading_scale"], filters = [["docstatus", "!=", 2 ]]):
-		for stud_result in frappe.db.sql("select * from `tabAssessment Result` where parent= %s", 
-				assessment.name, as_dict=True):
-			if stud_result.result:
-				assessment_result = frappe.new_doc("Assessment Result")
-				assessment_result.student = stud_result.student
-				assessment_result.student_name = stud_result.student_name
-				assessment_result.assessment_plan = assessment.name
-				assessment_result.grading_scale = assessment.grading_scale
-				assessment_result.total_score = stud_result.result
-				assessment_result.flags.ignore_validate = True
-				assessment_result.flags.ignore_mandatory = True
-				assessment_result.save()
-	
-	frappe.db.sql("""delete from `tabAssessment Result` where parent != '' or parent is not null""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_attendance_docstatus.py b/erpnext/patches/v7_2/update_attendance_docstatus.py
deleted file mode 100644
index a690526..0000000
--- a/erpnext/patches/v7_2/update_attendance_docstatus.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("education", "doctype", "student_attendance")
-	frappe.db.sql('''
-		update `tabStudent Attendance` set
-			docstatus=0
-		where
-			docstatus=1''')
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_doctype_status.py b/erpnext/patches/v7_2/update_doctype_status.py
deleted file mode 100644
index c66f3f2..0000000
--- a/erpnext/patches/v7_2/update_doctype_status.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	doctypes = ["Opportunity", "Quotation", "Sales Order", "Sales Invoice", "Purchase Invoice", "Purchase Order", "Delivery Note", "Purchase Receipt"]
-	for doctype in doctypes:
-		frappe.db.sql(""" update `tab{doctype}` set status = 'Draft'
-			where status = 'Cancelled' and docstatus = 0 """.format(doctype = doctype))
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_guardian_name_in_student_master.py b/erpnext/patches/v7_2/update_guardian_name_in_student_master.py
deleted file mode 100644
index 9f589ef..0000000
--- a/erpnext/patches/v7_2/update_guardian_name_in_student_master.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	# 'Schools' module changed to the 'Education'
-	# frappe.reload_doc("schools", "doctype", "student_guardian")
-	frappe.reload_doc("education", "doctype", "student_guardian")
-
-	student_guardians = frappe.get_all("Student Guardian", fields=["guardian"])
-	for student_guardian in student_guardians:
-		guardian_name = frappe.db.get_value("Guardian", student_guardian.guardian, "guardian_name")
-		frappe.db.sql("update `tabStudent Guardian` set guardian_name = %s where guardian= %s",
-			(guardian_name, student_guardian.guardian))
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_party_type.py b/erpnext/patches/v7_2/update_party_type.py
deleted file mode 100644
index 147f5a3..0000000
--- a/erpnext/patches/v7_2/update_party_type.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('setup', 'doctype', 'party_type')
-	make_party_type()
-
-def make_party_type():
-	for party_type in ["Customer", "Supplier", "Employee"]:
-		if not frappe.db.get_value("Party Type", party_type):
-			doc = frappe.new_doc("Party Type")
-			doc.party_type = party_type
-			doc.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_salary_slips.py b/erpnext/patches/v7_2/update_salary_slips.py
deleted file mode 100644
index 9fcce62..0000000
--- a/erpnext/patches/v7_2/update_salary_slips.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details
-from frappe.utils import cint
-
-def execute():
-	frappe.reload_doc("Payroll", "doctype", "Salary Slip")
-	if not frappe.db.has_column('Salary Slip', 'fiscal_year'):
-		return
-
-	salary_slips = frappe.db.sql("""select month, name, fiscal_year from `tabSalary Slip`
-				where (month is not null and month != '') and
-				start_date is null and end_date is null and docstatus != 2""", as_dict=True)
-
-	for salary_slip in salary_slips:
-		if not cint(salary_slip.month):
-			continue
-		get_start_end_date = get_month_details(salary_slip.fiscal_year, cint(salary_slip.month))
-		start_date = get_start_end_date['month_start_date']
-		end_date = get_start_end_date['month_end_date']
-		frappe.db.sql("""update `tabSalary Slip` set start_date = %s, end_date = %s where name = %s""",
-		(start_date, end_date, salary_slip.name))
\ No newline at end of file
diff --git a/erpnext/patches/v7_2/update_website_for_variant.py b/erpnext/patches/v7_2/update_website_for_variant.py
deleted file mode 100644
index e8eef6e..0000000
--- a/erpnext/patches/v7_2/update_website_for_variant.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# variant must have show_in_website = 0
-	frappe.reload_doctype('Item')
-	frappe.db.sql('''
-		update tabItem set
-			show_variant_in_website = 1,
-			show_in_website = 0
-		where
-			show_in_website=1
-			and ifnull(variant_of, "")!=""''')
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/__init__.py b/erpnext/patches/v8_0/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v8_0/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v8_0/addresses_linked_to_lead.py b/erpnext/patches/v8_0/addresses_linked_to_lead.py
deleted file mode 100644
index b5f2234..0000000
--- a/erpnext/patches/v8_0/addresses_linked_to_lead.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""UPDATE `tabDynamic Link` SET link_doctype = 'Lead' WHERE link_doctype = 'Load'""")
diff --git a/erpnext/patches/v8_0/change_in_words_varchar_length.py b/erpnext/patches/v8_0/change_in_words_varchar_length.py
deleted file mode 100644
index 68ff95b..0000000
--- a/erpnext/patches/v8_0/change_in_words_varchar_length.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	doctypes = frappe.db.sql_list("""select parent from tabDocField where fieldname = 'in_words'""")
-		
-	for dt in doctypes:
-		for fieldname in ("in_words", "base_in_words"):
-			frappe.db.sql("alter table `tab{0}` change column `{1}` `{2}` varchar(255)"
-				.format(dt, fieldname, fieldname))
-				
-	frappe.db.sql("""alter table `tabJournal Entry` 
-		change column `total_amount_in_words` `total_amount_in_words` varchar(255)""")
diff --git a/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py b/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py
deleted file mode 100644
index cf1bc5a..0000000
--- a/erpnext/patches/v8_0/create_address_doc_from_address_field_in_company.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# new field address_html is created in place of address field for the company's address in PR #8754 (without patch)
-	# so here is the patch for moving the address details in the address doc
-	company_list = []
-	if 'address' in frappe.db.get_table_columns('Company'):
-		company_list = frappe.db.sql('''select name, address from `tabCompany` 
-			where address is not null and address != ""''', as_dict=1)
-
-	for company in company_list:
-		add_list = company.address.split(" ")
-		if ',' in company.address:
-			add_list = company.address.rpartition(',')
-		elif ' ' in company.address:
-			add_list = company.address.rpartition(' ')
-		else:
-			add_list = [company.address, None, company.address]
-
-		doc = frappe.get_doc({
-			"doctype":"Address",
-			"address_line1": add_list[0],
-			"city": add_list[2],
-			"links": [{
-				"link_doctype": "Company",
-				"link_name": company.name
-				}]
-			})
-		doc.save()
diff --git a/erpnext/patches/v8_0/create_domain_docs.py b/erpnext/patches/v8_0/create_domain_docs.py
deleted file mode 100644
index 3ef4f3c..0000000
--- a/erpnext/patches/v8_0/create_domain_docs.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-import erpnext
-
-def execute():
-	"""Create domain documents"""
-	frappe.reload_doc("core", "doctype", "domain")
-	frappe.reload_doc("core", "doctype", "domain_settings")
-	frappe.reload_doc("core", "doctype", "has_domain")
-	frappe.reload_doc("core", "doctype", "role")
-
-	for domain in ("Distribution", "Manufacturing", "Retail", "Services", "Education"):
-		if not frappe.db.exists({"doctype": "Domain", "domain": domain}):
-			create_domain(domain)
-
-	# set domain in domain settings based on company domain
-
-	domains = []
-	condition = ""
-	company = erpnext.get_default_company()
-	if company:
-		condition = " and name={0}".format(frappe.db.escape(company))
-
-	domains = frappe.db.sql_list("select distinct domain from `tabCompany` where domain != 'Other' {0}".format(condition))
-
-	if not domains:
-		return
-
-	domain_settings = frappe.get_doc("Domain Settings", "Domain Settings")
-	checked_domains = [row.domain for row in domain_settings.active_domains]
-
-	for domain in domains:
-		# check and ignore if the domains is already checked in domain settings
-		if domain in checked_domains:
-			continue
-
-		if not frappe.db.get_value("Domain", domain):
-			# user added custom domain in companies domain field
-			create_domain(domain)
-
-		row = domain_settings.append("active_domains", dict(domain=domain))
-
-	domain_settings.save(ignore_permissions=True)
-
-def create_domain(domain):
-	# create new domain
-
-	doc = frappe.new_doc("Domain")
-	doc.domain = domain
-	doc.db_update()
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/delete_bin_indexes.py b/erpnext/patches/v8_0/delete_bin_indexes.py
deleted file mode 100644
index 12cacdb..0000000
--- a/erpnext/patches/v8_0/delete_bin_indexes.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-# -*- coding: utf-8 -*-
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# delete bin indexes
-	unwanted_indexes = ["item_code", "warehouse"]
-
-	for k in unwanted_indexes:
-		try:
-			frappe.db.sql("drop index {0} on `tabBin`".format(k))
-		except:
-			pass
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/delete_schools_depricated_doctypes.py b/erpnext/patches/v8_0/delete_schools_depricated_doctypes.py
deleted file mode 100644
index 09a78ed..0000000
--- a/erpnext/patches/v8_0/delete_schools_depricated_doctypes.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2017, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	""" delete doctypes """
-
-	if frappe.db.exists("DocType", "Grading Structure"):
-		frappe.delete_doc("DocType", "Grading Structure", force=1)
-
-	if frappe.db.exists("DocType", "Grade Interval"):
-		frappe.delete_doc("DocType", "Grade Interval", force=1)
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/disable_instructor_role.py b/erpnext/patches/v8_0/disable_instructor_role.py
deleted file mode 100644
index 4ba78d1..0000000
--- a/erpnext/patches/v8_0/disable_instructor_role.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	""" 
-		disable the instructor role for companies with domain other than
-		Education.
-	"""
-
-	domains = frappe.db.sql_list("select domain from tabCompany")
-	if "Education" not in domains:
-		if frappe.db.exists("Role", "Instructor"):
-			role = frappe.get_doc("Role", "Instructor")
-			role.disabled = 1
-			role.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py b/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py
deleted file mode 100644
index 1088d70..0000000
--- a/erpnext/patches/v8_0/enable_booking_asset_depreciation_automatically.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.set_value("Accounts Settings", None, 
-		"book_asset_depreciation_entry_automatically", 1)
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py b/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py
deleted file mode 100644
index 2e7f360..0000000
--- a/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt, status in [["Sales Invoice", "Credit Note Issued"], ["Purchase Invoice", "Debit Note Issued"]]:
-		invoices = frappe.db.sql("""
-			select name 
-			from `tab{0}`
-			where 
-				status = %s
-				and outstanding_amount < 0
-				and docstatus=1
-				and is_return=0
-		""".format(dt), status)
-		
-		for inv in invoices:
-			return_inv = frappe.db.sql("""select name from `tab{0}` 
-				where is_return=1 and return_against=%s and docstatus=1""".format(dt), inv[0])
-			if not return_inv:
-				frappe.db.sql("update `tab{0}` set status='Paid' where name = %s".format(dt), inv[0])
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py b/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py
deleted file mode 100644
index 9750fb7..0000000
--- a/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Sales Invoice')
-
-	frappe.db.sql("""
-		delete from 
-			`tabSales Invoice Payment` 
-		where 
-			parent in (select name from `tabSales Invoice` where is_pos = 0)
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/merge_student_batch_and_student_group.py b/erpnext/patches/v8_0/merge_student_batch_and_student_group.py
deleted file mode 100644
index fb9021f..0000000
--- a/erpnext/patches/v8_0/merge_student_batch_and_student_group.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import *
-from frappe.model.mapper import get_mapped_doc
-
-
-def execute():
-	# for converting student batch into student group
-	for doctype in ["Student Group", "Student Group Student", 'Program Enrollment',
-		"Student Group Instructor", "Student Attendance", "Student", "Student Batch Name"]:
-		# 'Schools' module changed to the 'Education'
-		# frappe.reload_doc("schools", "doctype", frappe.scrub(doctype))
-
-		frappe.reload_doc("education", "doctype", frappe.scrub(doctype))
-
-	if frappe.db.table_exists("Student Batch"):
-		student_batches = frappe.db.sql('''select name as student_group_name, student_batch_name as batch,
-			program, academic_year, academic_term from `tabStudent Batch`''', as_dict=1)
-
-		for student_batch in student_batches:
-			# create student batch name if does not exists !!
-			if student_batch.get("batch") and not frappe.db.exists("Student Batch Name", student_batch.get("batch")):
-				frappe.get_doc({
-					"doctype": "Student Batch Name",
-					"batch_name": student_batch.get("batch")
-				}).insert(ignore_permissions=True)
-
-			student_batch.update({"doctype":"Student Group", "group_based_on": "Batch"})
-			doc = frappe.get_doc(student_batch)
-
-			if frappe.db.sql("SHOW COLUMNS FROM `tabStudent Batch Student` LIKE 'active'"):
-				cond = ", active"
-			else:
-				cond = " "
-			student_list = frappe.db.sql('''select student, student_name {cond} from `tabStudent Batch Student`
-				where parent=%s'''.format(cond=cond), (doc.student_group_name), as_dict=1)
-
-			if student_list:
-				for i, student in enumerate(student_list):
-					student.update({"group_roll_number": i+1})
-				doc.extend("students", student_list)
-
-			instructor_list = None
-			if frappe.db.table_exists("Student Batch Instructor"):
-				instructor_list = frappe.db.sql('''select instructor, instructor_name from `tabStudent Batch Instructor`
-					where parent=%s''', (doc.student_group_name), as_dict=1)
-			if instructor_list:
-				doc.extend("instructors", instructor_list)
-			doc.save()
-
-	# delete the student batch and child-table
-	if frappe.db.table_exists("Student Batch"):
-		frappe.delete_doc("DocType", "Student Batch", force=1)
-	if frappe.db.table_exists("Student Batch Student"):
-		frappe.delete_doc("DocType", "Student Batch Student", force=1)
-	if frappe.db.table_exists("Student Batch Instructor"):
-		frappe.delete_doc("DocType", "Student Batch Instructor", force=1)
-
-	# delete the student batch creation tool
-	if frappe.db.table_exists("Student Batch Creation Tool"):
-		frappe.delete_doc("DocType", "Student Batch Creation Tool", force=1)
-
-	# delete the student batch creation tool
-	if frappe.db.table_exists("Attendance Tool Student"):
-		frappe.delete_doc("DocType", "Attendance Tool Student", force=1)
-
-	# change the student batch to student group in the student attendance
-	table_columns = frappe.db.get_table_columns("Student Attendance")
-	if "student_batch" in table_columns:
-		rename_field("Student Attendance", "student_batch", "student_group")
diff --git a/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py b/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py
deleted file mode 100644
index b59d818..0000000
--- a/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Warehouse")
-	frappe.db.sql("""
-		update 
-			`tabWarehouse` 
-		set 
-			account = (select name from `tabAccount` 
-				where account_type = 'Stock' and 
-				warehouse = `tabWarehouse`.name and is_group = 0 limit 1)""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/move_perpetual_inventory_setting.py b/erpnext/patches/v8_0/move_perpetual_inventory_setting.py
deleted file mode 100644
index 78322d4..0000000
--- a/erpnext/patches/v8_0/move_perpetual_inventory_setting.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Company')
-	enabled = frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock") or 0
-	for data in frappe.get_all('Company', fields = ["name"]):
-		doc = frappe.get_doc('Company', data.name)
-		doc.enable_perpetual_inventory = enabled
-		doc.db_update()
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py b/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py
deleted file mode 100644
index e517df5..0000000
--- a/erpnext/patches/v8_0/rename_is_sample_item_to_allow_zero_valuation_rate.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	
-	doc_list = ["Purchase Invoice Item", "Stock Entry Detail", "Delivery Note Item", 
-		"Purchase Receipt Item", "Sales Invoice Item"]
-	
-	for doctype in doc_list:
-		frappe.reload_doctype(doctype)
-		if "is_sample_item" in frappe.db.get_table_columns(doctype):
-			rename_field(doctype, "is_sample_item", "allow_zero_valuation_rate")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py b/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py
deleted file mode 100644
index 5ad862a..0000000
--- a/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql(
-		"""
-		UPDATE `tabMaterial Request`
-			SET status = CASE
-							WHEN docstatus = 2 THEN 'Cancelled'
-							WHEN docstatus = 0 THEN 'Draft'
-							ELSE CASE
-								WHEN status = 'Stopped' THEN 'Stopped'
-								WHEN status != 'Stopped' AND per_ordered = 0 THEN 'Pending'
-								WHEN per_ordered < 100 AND per_ordered > 0 AND status != 'Stopped'
-									THEN 'Partially Ordered'
-								WHEN per_ordered = 100 AND material_request_type = 'Purchase'
-									AND status != 'Stopped' THEN 'Ordered'
-								WHEN per_ordered = 100 AND material_request_type = 'Material Transfer'
-									AND status != 'Stopped' THEN 'Transferred'
-								WHEN per_ordered = 100 AND material_request_type = 'Material Issue'
-									AND status != 'Stopped' THEN 'Issued'
-							END
-			END
-		"""
-	)
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py b/erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py
deleted file mode 100644
index 4065438..0000000
--- a/erpnext/patches/v8_0/rename_total_margin_to_rate_with_margin.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	""" 
-		Rename Total Margin field to Rate With Margin in
-		"Sales Order Item", "Sales Invoice Item", "Delivery Note Item",
-		"Quotation Item"
-	"""
-
-	for d in ("Sales Order Item", "Sales Invoice Item",
-		"Delivery Note Item", "Quotation Item"):
-		frappe.reload_doctype(d)
-		rename_field_if_exists(d, "total_margin", "rate_with_margin")
-
-
-def rename_field_if_exists(doctype, old_fieldname, new_fieldname):
-	try:
-		rename_field(doctype, old_fieldname, new_fieldname)
-	except Exception as e:
-		if e.args[0] != 1054:
-			raise
diff --git a/erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py b/erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py
deleted file mode 100644
index 3030b8e..0000000
--- a/erpnext/patches/v8_0/repost_reserved_qty_for_multiple_sales_uom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
-
-def execute():
-	for doctype in ("Sales Order Item", "Bin"):
-		frappe.reload_doctype(doctype)
-
-	repost_for = frappe.db.sql("""select distinct item_code, warehouse
-		from `tabSales Order Item` where docstatus=1 and uom != stock_uom and
-		exists(select name from tabItem where name=`tabSales Order Item`.item_code and ifnull(is_stock_item, 0)=1)""")
-
-	for item_code, warehouse in repost_for:
-		update_bin_qty(item_code, warehouse, {
-			"reserved_qty": get_reserved_qty(item_code, warehouse)
-		})
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/revert_manufacturers_table_from_item.py b/erpnext/patches/v8_0/revert_manufacturers_table_from_item.py
deleted file mode 100644
index 60cbb33..0000000
--- a/erpnext/patches/v8_0/revert_manufacturers_table_from_item.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists("DocType", "Item Manufacturer"):
-		frappe.reload_doctype("Item")
-		item_manufacturers = frappe.db.sql("""
-			select parent, manufacturer, manufacturer_part_no 
-			from `tabItem Manufacturer`
-		""", as_dict=1)
-		
-		for im in item_manufacturers:
-			frappe.db.sql("""
-				update tabItem 
-				set manufacturer=%s, manufacturer_part_no=%s
-				where name=%s
-			""", (im.manufacturer, im.manufacturer_part_no, im.parent))
-		
-		frappe.delete_doc("DocType", "Item Manufacturer")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/save_system_settings.py b/erpnext/patches/v8_0/save_system_settings.py
deleted file mode 100644
index d479ece..0000000
--- a/erpnext/patches/v8_0/save_system_settings.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint
-
-def execute():
-	"""
-		save system settings document
-	"""
-
-	frappe.reload_doc("core", "doctype", "system_settings")
-	doc = frappe.get_doc("System Settings")
-	doc.flags.ignore_mandatory = True
-
-	if cint(doc.currency_precision) == 0:
-		doc.currency_precision = ''
-
-	doc.save(ignore_permissions=True)
diff --git a/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py b/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py
deleted file mode 100644
index 197d6de..0000000
--- a/erpnext/patches/v8_0/set_null_to_serial_nos_for_disabled_sales_invoices.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
-
-def execute():
-	frappe.db.sql("""
-		update 
-			`tabSales Invoice Item` 
-		set serial_no = NULL
-		where 
-			parent in (select name from `tabSales Invoice` where update_stock = 0 and docstatus = 1)""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/set_project_copied_from.py b/erpnext/patches/v8_0/set_project_copied_from.py
deleted file mode 100644
index d428797..0000000
--- a/erpnext/patches/v8_0/set_project_copied_from.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Project")
-	
-	frappe.db.sql('''
-		UPDATE `tabProject`
-		SET copied_from=name
-		WHERE copied_from is NULL
-	''')
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py b/erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py
deleted file mode 100644
index 8a4ef40..0000000
--- a/erpnext/patches/v8_0/set_sales_invoice_serial_number_from_delivery_note.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
-
-def execute():
-	""" Set the Serial Numbers in Sales Invoice Item from Delivery Note Item """
-
-	frappe.reload_doc("stock", "doctype", "serial_no")
-
-	frappe.db.sql(""" update `tabSales Invoice Item` sii inner join
-		`tabDelivery Note Item` dni on sii.dn_detail=dni.name and  sii.qty=dni.qty
-		set sii.serial_no=dni.serial_no where sii.parent IN (select si.name
-			from `tabSales Invoice` si where si.update_stock=0 and si.docstatus=1)""")
-
-	items = frappe.db.sql(""" select  sii.parent, sii.serial_no from  `tabSales Invoice Item` sii
-		left join `tabSales Invoice` si on sii.parent=si.name
-		where si.docstatus=1 and si.update_stock=0""", as_dict=True)
-
-	for item in items:
-		sales_invoice = item.get("parent", None)
-		serial_nos = item.get("serial_no", "")
-
-		if not sales_invoice or not serial_nos:
-			continue
-
-		serial_nos = ["{}".format(frappe.db.escape(no)) for no in serial_nos.split("\n")]
-
-		frappe.db.sql("""
-			UPDATE
-				`tabSerial No`
-			SET
-				sales_invoice={sales_invoice}
-			WHERE
-				name in ({serial_nos})
-			""".format(
-				sales_invoice=frappe.db.escape(sales_invoice),
-				serial_nos=",".join(serial_nos)
-			)
-		)
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/update_customer_pos_id.py b/erpnext/patches/v8_0/update_customer_pos_id.py
deleted file mode 100644
index a772ae9..0000000
--- a/erpnext/patches/v8_0/update_customer_pos_id.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Customer")
-	frappe.db.sql(""" update `tabCustomer` set customer_pos_id = name """)
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/update_production_orders.py b/erpnext/patches/v8_0/update_production_orders.py
deleted file mode 100644
index 8e993cc..0000000
--- a/erpnext/patches/v8_0/update_production_orders.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# reload schema
-	for doctype in ("Work Order", "Work Order Item", "Work Order Operation", 
-		"BOM Item", "BOM Explosion Item", "BOM"):
-			frappe.reload_doctype(doctype)
-
-	frappe.reload_doc("stock", "doctype", "item")
-	frappe.reload_doc("stock", "doctype", "item_default")
-
-	# fetch all draft and submitted work orders
-	fields = ["name"]
-	if "source_warehouse" in frappe.db.get_table_columns("Work Order"):
-		fields.append("source_warehouse")
-		
-	wo_orders = frappe.get_all("Work Order", filters={"docstatus": ["!=", 2]}, fields=fields)
-	
-	count = 0
-	for p in wo_orders:
-		wo_order = frappe.get_doc("Work Order", p.name)
-		count += 1
-
-		# set required items table
-		wo_order.set_required_items()
-		
-		for item in wo_order.get("required_items"):
-			# set source warehouse based on parent
-			if not item.source_warehouse and "source_warehouse" in fields:
-				item.source_warehouse = wo_order.get("source_warehouse")
-			item.db_update()
-		
-		if wo_order.docstatus == 1:
-			# update transferred qty based on Stock Entry, it also updates db
-			wo_order.update_transaferred_qty_for_required_items()
-			
-			# Set status where it was 'Unstopped', as it is deprecated
-			if wo_order.status == "Unstopped":
-				status = wo_order.get_status()
-				wo_order.db_set("status", status)
-			elif wo_order.status == "Stopped":
-				wo_order.update_reserved_qty_for_production()
-		
-		if count % 200 == 0:
-			frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/update_sales_cost_in_project.py b/erpnext/patches/v8_0/update_sales_cost_in_project.py
deleted file mode 100644
index 1a29fc4..0000000
--- a/erpnext/patches/v8_0/update_sales_cost_in_project.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("projects", "doctype", "project")
-
-	frappe.db.sql("""
-		update `tabProject` p
-		set total_sales_amount = ifnull((select sum(base_grand_total)
-			from `tabSales Order` where project=p.name and docstatus=1), 0)
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py b/erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py
deleted file mode 100644
index 19d27b2..0000000
--- a/erpnext/patches/v8_0/update_status_as_paid_for_completed_expense_claim.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	""" set status as Paid in Expense Claim if total_sactioned_amount 
-		and total_amount_reimbursed is equal """
-	
-	frappe.reload_doctype('Expense Claim')
-
-	frappe.db.sql("""
-		update 
-			`tabExpense Claim`
-		set status = 'Paid'
-		where 
-			total_sanctioned_amount = total_amount_reimbursed
-	""")
diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py
deleted file mode 100644
index 1f937bb..0000000
--- a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('manufacturing', 'doctype', 'bom_item')
-	frappe.reload_doc('manufacturing', 'doctype', 'bom_explosion_item')
-	frappe.reload_doc('manufacturing', 'doctype', 'bom_scrap_item')
-	frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom, conversion_factor = 1")
-	frappe.db.sql("update `tabBOM Explosion Item` set stock_qty = qty")
-	if "qty" in frappe.db.get_table_columns("BOM Scrap Item"):
-		frappe.db.sql("update `tabBOM Scrap Item` set stock_qty = qty")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py b/erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py
deleted file mode 100644
index be5cf3a..0000000
--- a/erpnext/patches/v8_0/update_stock_qty_value_in_purchase_invoice.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_item')
-	frappe.db.sql("update `tabPurchase Invoice Item` set stock_qty = qty, stock_uom = uom")
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/update_student_groups_from_student_batches.py b/erpnext/patches/v8_0/update_student_groups_from_student_batches.py
deleted file mode 100644
index ae24fe4..0000000
--- a/erpnext/patches/v8_0/update_student_groups_from_student_batches.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import *
-from frappe.model.mapper import get_mapped_doc
-
-
-def execute():
-	if frappe.db.table_exists("Student Batch"):
-		student_batches = frappe.db.sql('''select name from `tabStudent Batch`''', as_dict=1)
-
-		for student_batch in student_batches:
-			if frappe.db.exists("Student Group", student_batch.get("name")):
-				student_group = frappe.get_doc("Student Group", student_batch.get("name"))
-
-				if frappe.db.table_exists("Student Batch Student"):
-					current_student_list = frappe.db.sql_list('''select student from `tabStudent Group Student`
-						where parent=%s''', (student_group.name))
-					batch_student_list = frappe.db.sql_list('''select student from `tabStudent Batch Student`
-						where parent=%s''', (student_group.name))
-
-					student_list = list(set(batch_student_list)-set(current_student_list))
-					if student_list:
-						student_group.extend("students", [{"student":d} for d in student_list])
-
-				if frappe.db.table_exists("Student Batch Instructor"):
-					current_instructor_list = frappe.db.sql_list('''select instructor from `tabStudent Group Instructor`
-						where parent=%s''', (student_group.name))
-					batch_instructor_list = frappe.db.sql_list('''select instructor from `tabStudent Batch Instructor`
-						where parent=%s''', (student_group.name))
-
-					instructor_list = list(set(batch_instructor_list)-set(current_instructor_list))
-					if instructor_list:
-						student_group.extend("instructors", [{"instructor":d} for d in instructor_list])
-
-				student_group.save()
diff --git a/erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py b/erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py
deleted file mode 100644
index a217304..0000000
--- a/erpnext/patches/v8_0/update_supplier_address_in_stock_entry.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# copy supplier_address to address_display, and set supplier_address to blank
-
-	stock_entries = frappe.db.sql(""" select name, purchase_order, supplier_address from `tabStock Entry`
-		where ifnull(supplier_address, '') <> ''""", as_dict=True)
-
-	frappe.reload_doc('stock', 'doctype', 'stock_entry')
-
-	for stock_entry in stock_entries:
-		# move supplier address to address_display, and fetch the supplier address from purchase order
-
-		se = frappe.get_doc("Stock Entry", stock_entry.get("name"))
-		se.address_display = stock_entry.get("supplier_address")
-		se.supplier_address = frappe.db.get_value("Purchase Order", stock_entry.get("purchase_order"),"supplier_address") or None
-
-		se.db_update()
diff --git a/erpnext/patches/v8_1/__init__.py b/erpnext/patches/v8_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_1/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_1/add_hsn_sac_codes.py b/erpnext/patches/v8_1/add_hsn_sac_codes.py
deleted file mode 100644
index 0fce96a..0000000
--- a/erpnext/patches/v8_1/add_hsn_sac_codes.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.regional.india.setup import setup
-
-def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
-	if not company:
-		return
-
-	# call setup for india
-	setup(patch=True)
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py b/erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py
deleted file mode 100644
index 4631602..0000000
--- a/erpnext/patches/v8_1/add_indexes_in_transaction_doctypes.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for dt in ("Sales Order Item", "Purchase Order Item",
-		"Material Request Item", "Work Order Item", "Packed Item"):
-			frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py b/erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py
deleted file mode 100644
index 4c606af..0000000
--- a/erpnext/patches/v8_1/allow_invoice_copy_to_edit_after_submit.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	inv_copy_options = "ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nDUPLICATE FOR SUPPLIER\nTRIPLICATE FOR SUPPLIER"
-	
-	frappe.db.sql("""update `tabCustom Field` set allow_on_submit=1, options=%s
-		where fieldname='invoice_copy' and dt = 'Sales Invoice'
-	""", inv_copy_options)
-	
-	frappe.db.sql("""update `tabCustom Field` set read_only=1
-		where fieldname='gst_state_number' and dt = 'Address'
-	""")
diff --git a/erpnext/patches/v8_1/delete_deprecated_reports.py b/erpnext/patches/v8_1/delete_deprecated_reports.py
deleted file mode 100644
index 3e0fdee..0000000
--- a/erpnext/patches/v8_1/delete_deprecated_reports.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	""" delete deprecated reports """
-
-	reports = [
-		"Monthly Salary Register", "Customer Addresses And Contacts",
-		"Supplier Addresses And Contacts"
-	]
-
-	for report in reports:
-		if frappe.db.exists("Report", report):
-			check_and_update_auto_email_report(report)
-			frappe.db.commit()
-
-			frappe.delete_doc("Report", report, ignore_permissions=True)
-
-def check_and_update_auto_email_report(report):
-	""" delete or update auto email report for deprecated report """
-
-	auto_email_report = frappe.db.get_value("Auto Email Report", {"report": report})
-	if not auto_email_report:
-		return
-
-	if report == "Monthly Salary Register":
-		frappe.delete_doc("Auto Email Report", auto_email_report)
-
-	elif report in ["Customer Addresses And Contacts", "Supplier Addresses And Contacts"]:
-		frappe.db.set_value("Auto Email Report", auto_email_report, "report", report)
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/gst_fixes.py b/erpnext/patches/v8_1/gst_fixes.py
deleted file mode 100644
index 34255eb..0000000
--- a/erpnext/patches/v8_1/gst_fixes.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-from erpnext.regional.address_template.setup import set_up_address_templates
-
-
-def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
-	if not company:
-		return
-
-	update_existing_custom_fields()
-	add_custom_fields()
-	set_up_address_templates(default_country='India')
-	frappe.reload_doc("regional", "print_format", "gst_tax_invoice")
-
-
-def update_existing_custom_fields():
-	frappe.db.sql("""update `tabCustom Field` set label = 'HSN/SAC'
-		where fieldname='gst_hsn_code' and label='GST HSN Code'
-	""")
-
-	frappe.db.sql("""update `tabCustom Field` set print_hide = 1
-		where fieldname in ('customer_gstin', 'supplier_gstin', 'company_gstin')
-	""")
-
-	frappe.db.sql("""update `tabCustom Field` set insert_after = 'address_display'
-		where fieldname in ('customer_gstin', 'supplier_gstin')
-	""")
-
-	frappe.db.sql("""update `tabCustom Field` set insert_after = 'company_address_display'
-		where fieldname = 'company_gstin'
-	""")
-
-	frappe.db.sql("""update `tabCustom Field` set insert_after = 'description'
-		where fieldname='gst_hsn_code' and dt in ('Sales Invoice Item', 'Purchase Invoice Item')
-	""")
-
-
-def add_custom_fields():
-	hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
-		fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description')
-
-	custom_fields = {
-		'Address': [
-			dict(fieldname='gst_state_number', label='GST State Number',
-				fieldtype='Int', insert_after='gst_state'),
-		],
-		'Sales Invoice': [
-			dict(fieldname='invoice_copy', label='Invoice Copy',
-				fieldtype='Select', insert_after='project', print_hide=1, allow_on_submit=1,
-				options='ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nTRIPLICATE FOR SUPPLIER'),
-		],
-		'Sales Order Item': [hsn_sac_field],
-		'Delivery Note Item': [hsn_sac_field],
-		'Purchase Order Item': [hsn_sac_field],
-		'Purchase Receipt Item': [hsn_sac_field]
-	}
-
-	for doctype, fields in custom_fields.items():
-		for df in fields:
-			create_custom_field(doctype, df)
diff --git a/erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py b/erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py
deleted file mode 100644
index 3962f8f..0000000
--- a/erpnext/patches/v8_1/remove_sales_invoice_from_returned_serial_no.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Serial No")
-
-	frappe.db.sql("""
-		update
-			`tabSerial No`
-		set
-			sales_invoice = NULL
-		where
-			sales_invoice in (select return_against from
-				`tabSales Invoice` where docstatus =1 and is_return=1)
-			and sales_invoice is not null and sales_invoice !='' """)
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/removed_report_support_hours.py b/erpnext/patches/v8_1/removed_report_support_hours.py
deleted file mode 100644
index 0936b22..0000000
--- a/erpnext/patches/v8_1/removed_report_support_hours.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql(""" update `tabAuto Email Report` set report = %s
-		where name = %s""", ('Support Hour Distribution', 'Support Hours'))
-
-	frappe.db.sql(""" update `tabCustom Role` set report = %s
-		where report = %s""", ('Support Hour Distribution', 'Support Hours'))
-
-	frappe.delete_doc('Report', 'Support Hours')
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/set_delivery_date_in_so_item.py b/erpnext/patches/v8_1/set_delivery_date_in_so_item.py
deleted file mode 100644
index af2d28b..0000000
--- a/erpnext/patches/v8_1/set_delivery_date_in_so_item.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Sales Order")
-	frappe.reload_doctype("Sales Order Item")
-
-	if "final_delivery_date" in frappe.db.get_table_columns("Sales Order"):
-		frappe.db.sql("""
-			update `tabSales Order`
-			set delivery_date = final_delivery_date
-			where (delivery_date is null or delivery_date = '0000-00-00')
-				and order_type = 'Sales'""")
-
-	frappe.db.sql("""
-		update `tabSales Order` so, `tabSales Order Item` so_item
-		set so_item.delivery_date = so.delivery_date
-		where so.name = so_item.parent
-			and so.order_type = 'Sales'
-			and (so_item.delivery_date is null or so_item.delivery_date = '0000-00-00')
-			and (so.delivery_date is not null and so.delivery_date != '0000-00-00')
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/update_expense_claim_status.py b/erpnext/patches/v8_1/update_expense_claim_status.py
deleted file mode 100644
index 4c1b85a..0000000
--- a/erpnext/patches/v8_1/update_expense_claim_status.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Expense Claim')
-
-	for data in frappe.db.sql(""" select name from `tabExpense Claim`
-		where (docstatus=1 and total_sanctioned_amount=0 and status = 'Paid') or 
-		(docstatus = 1 and approval_status = 'Rejected' and total_sanctioned_amount > 0)""", as_dict=1):
-		doc = frappe.get_doc('Expense Claim', data.name)
-		if doc.approval_status == 'Rejected':
-			for d in doc.expenses:
-				d.db_set("sanctioned_amount", 0, update_modified = False)
-			doc.db_set("total_sanctioned_amount", 0, update_modified = False)
-
-			frappe.db.sql(""" delete from `tabGL Entry` where voucher_type = 'Expense Claim'
-				and voucher_no = %s""", (doc.name))
-
-		doc.set_status()
-		doc.db_set("status", doc.status, update_modified = False)
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/update_gst_state.py b/erpnext/patches/v8_1/update_gst_state.py
deleted file mode 100644
index 7aaf2d5..0000000
--- a/erpnext/patches/v8_1/update_gst_state.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.regional.india import states
-
-def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
-	if not company:
-		return
-
-	if not frappe.db.get_value("Custom Field", filters={'fieldname':'gst_state'}):
-		return
-
-	frappe.db.sql("update `tabCustom Field` set options=%s where fieldname='gst_state'", '\n'.join(states))
-	frappe.db.sql("update `tabAddress` set gst_state='Chhattisgarh' where gst_state='Chattisgarh'")
-	frappe.db.sql("update `tabAddress` set gst_state_number='05' where gst_state='Uttarakhand'")
diff --git a/erpnext/patches/v8_10/__init__.py b/erpnext/patches/v8_10/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_10/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_10/change_default_customer_credit_days.py b/erpnext/patches/v8_10/change_default_customer_credit_days.py
deleted file mode 100644
index 992be17..0000000
--- a/erpnext/patches/v8_10/change_default_customer_credit_days.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-
-def execute():
-	frappe.reload_doc("selling", "doctype", "customer")
-	frappe.reload_doc("buying", "doctype", "supplier")
-	frappe.reload_doc("setup", "doctype", "supplier_type")
-	frappe.reload_doc("accounts", "doctype", "payment_term")
-	frappe.reload_doc("accounts", "doctype", "payment_terms_template_detail")
-	frappe.reload_doc("accounts", "doctype", "payment_terms_template")
-
-	payment_terms = []
-	records = []
-	for doctype in ("Customer", "Supplier", "Supplier Type"):
-		credit_days = frappe.db.sql("""
-			SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
-			from `tab{0}`
-			where
-				((credit_days_based_on='Fixed Days' or credit_days_based_on is null)
-					and credit_days is not null)
-				or credit_days_based_on='Last Day of the Next Month'
-		""".format(doctype))
-
-		credit_records = ((record[0], record[1], record[2]) for record in credit_days)
-		for days, based_on, party_name in credit_records:
-			if based_on == "Fixed Days":
-				pyt_template_name = 'Default Payment Term - N{0}'.format(days)
-			else:
-				pyt_template_name = 'Default Payment Term - EO2M'
-
-			if not frappe.db.exists("Payment Terms Template", pyt_template_name):
-				payment_term = make_payment_term(days, based_on)
-				template = make_template(payment_term)
-			else:
-				template = frappe.get_doc("Payment Terms Template", pyt_template_name)
-
-			payment_terms.append('WHEN `name`={0} THEN {1}'.format(frappe.db.escape(party_name), template.template_name))
-			records.append(frappe.db.escape(party_name))
-
-		begin_query_str = "UPDATE `tab{0}` SET `payment_terms` = CASE ".format(doctype)
-		value_query_str = " ".join(payment_terms)
-		cond_query_str = " ELSE `payment_terms` END WHERE "
-
-		if records:
-			frappe.db.sql(
-				begin_query_str + value_query_str + cond_query_str + '`name` IN %s',
-				(records,)
-			)
-
-
-def make_template(payment_term):
-	doc = frappe.new_doc('Payment Terms Template Detail')
-	doc.payment_term = payment_term.payment_term_name
-	doc.due_date_based_on = payment_term.due_date_based_on
-	doc.invoice_portion = payment_term.invoice_portion
-	doc.description = payment_term.description
-	doc.credit_days = payment_term.credit_days
-	doc.credit_months = payment_term.credit_months
-
-	template = frappe.new_doc('Payment Terms Template')
-	template.template_name = 'Default Payment Term - {0}'.format(payment_term.payment_term_name)
-	template.append('terms', doc)
-	template.save()
-
-	return template
-
-
-def make_payment_term(days, based_on):
-	based_on_map = {
-		'Fixed Days': 'Day(s) after invoice date',
-		'Last Day of the Next Month': 'Month(s) after the end of the invoice month'
-	}
-
-	doc = frappe.new_doc('Payment Term')
-	doc.due_date_based_on = based_on_map.get(based_on)
-	doc.invoice_portion = 100
-
-	if based_on == 'Fixed Days':
-		doc.credit_days = days
-		doc.description = 'Net payable within {0} days'.format(days)
-		doc.payment_term_name = 'N{0}'.format(days)
-	else:
-		doc.credit_months = 1
-		doc.description = 'Net payable by the end of next month'
-		doc.payment_term_name = 'EO2M'
-
-	doc.save()
-	return doc
diff --git a/erpnext/patches/v8_3/__init__.py b/erpnext/patches/v8_3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_3/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py b/erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py
deleted file mode 100644
index 6c4c6d5..0000000
--- a/erpnext/patches/v8_3/set_restrict_to_domain_for_module_def.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	""" set the restrict to domain in module def """
-	pass
\ No newline at end of file
diff --git a/erpnext/patches/v8_3/update_company_total_sales.py b/erpnext/patches/v8_3/update_company_total_sales.py
deleted file mode 100644
index 78efecb..0000000
--- a/erpnext/patches/v8_3/update_company_total_sales.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from erpnext.setup.doctype.company.company import update_company_current_month_sales, update_company_monthly_sales
-
-def execute():
-	'''Update company monthly sales history based on sales invoices'''
-	frappe.reload_doctype("Company")
-	companies = [d['name'] for d in frappe.get_list("Company")]
-
-	for company in companies:
-		update_company_current_month_sales(company)
-		update_company_monthly_sales(company)
diff --git a/erpnext/patches/v8_4/__init__.py b/erpnext/patches/v8_4/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v8_4/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v8_4/make_scorecard_records.py b/erpnext/patches/v8_4/make_scorecard_records.py
deleted file mode 100644
index 73afa27..0000000
--- a/erpnext/patches/v8_4/make_scorecard_records.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
-def execute():
-	frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_variable')
-	frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_standing')
-	make_default_records()
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/__init__.py b/erpnext/patches/v8_5/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_5/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py b/erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py
deleted file mode 100644
index 82beba3..0000000
--- a/erpnext/patches/v8_5/fix_tax_breakup_for_non_invoice_docs.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from erpnext.regional.india.setup import make_custom_fields
-from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_html
-
-def execute():
-	companies = [d.name for d in frappe.get_all('Company', filters = {'country': 'India'})]
-	if not companies:
-		return
-
-	make_custom_fields()
-
-	# update invoice copy value
-	values = ["Original for Recipient", "Duplicate for Transporter",
-		"Duplicate for Supplier", "Triplicate for Supplier"]
-	for d in values:
-		frappe.db.sql("update `tabSales Invoice` set invoice_copy=%s where invoice_copy=%s", (d, d))
-
-	# update tax breakup in transactions made after 1st July 2017
-	doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", 
-		"Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]
-
-	for doctype in doctypes:
-		frappe.reload_doctype(doctype)
-
-		date_field = "posting_date"
-		if doctype in ["Quotation", "Sales Order", "Supplier Quotation", "Purchase Order"]:
-			date_field = "transaction_date"
-
-		records = [d.name for d in frappe.get_all(doctype, filters={
-			"docstatus": ["!=", 2],
-			date_field: [">=", "2017-07-01"],
-			"company": ["in", companies],
-			"total_taxes_and_charges": [">", 0],
-			"other_charges_calculation": ""
-		})]
-		if records:
-			frappe.db.sql("""
-				update `tab%s Item` dt_item
-				set gst_hsn_code = (select gst_hsn_code from tabItem where name=dt_item.item_code)
-				where parent in (%s)
-					and (gst_hsn_code is null or gst_hsn_code = '')
-			""" % (doctype, ', '.join(['%s']*len(records))), tuple(records))
-
-			for record in records:
-				doc = frappe.get_doc(doctype, record)
-				html = get_itemised_tax_breakup_html(doc)
-				doc.db_set("other_charges_calculation", html, update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/remove_project_type_property_setter.py b/erpnext/patches/v8_5/remove_project_type_property_setter.py
deleted file mode 100644
index 70a08f5..0000000
--- a/erpnext/patches/v8_5/remove_project_type_property_setter.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	ps = frappe.db.get_value('Property Setter', dict(doc_type='Project', field_name='project_type',
-		property='options'))
-	if ps:
-		frappe.delete_doc('Property Setter', ps)
-
-	project_types = frappe.db.sql_list('select distinct project_type from tabProject')
-
-	for project_type in project_types:
-		if project_type and not frappe.db.exists("Project Type", project_type):
-			p_type = frappe.get_doc({
-				"doctype": "Project Type",
-				"project_type": project_type
-			})
-			p_type.insert()
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py b/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py
deleted file mode 100644
index 2d7df4a..0000000
--- a/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Portal Settings")
-
-	frappe.db.sql("""
-		delete from
-			`tabPortal Menu Item`
-		where
-			(route = '/quotations' and title = 'Supplier Quotation')
-		or (route = '/quotation' and title = 'Quotations')
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/set_default_mode_of_payment.py b/erpnext/patches/v8_5/set_default_mode_of_payment.py
deleted file mode 100644
index 34ecbb0..0000000
--- a/erpnext/patches/v8_5/set_default_mode_of_payment.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("POS Profile")
-	frappe.reload_doctype("Sales Invoice Payment")
-
-	frappe.db.sql("""
-		update
-			`tabSales Invoice Payment`
-		set `tabSales Invoice Payment`.default = 1
-		where
-			`tabSales Invoice Payment`.parenttype = 'POS Profile'
-			and `tabSales Invoice Payment`.idx=1""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/update_customer_group_in_POS_profile.py b/erpnext/patches/v8_5/update_customer_group_in_POS_profile.py
deleted file mode 100644
index 2661914..0000000
--- a/erpnext/patches/v8_5/update_customer_group_in_POS_profile.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('POS Profile')
-	customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
-	if customer_group:
-		frappe.db.sql(""" update `tabPOS Profile`
-			set customer_group = %s where customer_group is null """, (customer_group))
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/update_existing_data_in_project_type.py b/erpnext/patches/v8_5/update_existing_data_in_project_type.py
deleted file mode 100644
index 497da06..0000000
--- a/erpnext/patches/v8_5/update_existing_data_in_project_type.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("projects", "doctype", "project_type")
-	frappe.reload_doc("projects", "doctype", "project")
-
-	project_types = ["Internal", "External", "Other"]
-
-	for project_type in project_types:
-		if not frappe.db.exists("Project Type", project_type):
-			p_type = frappe.get_doc({
-				"doctype": "Project Type",
-				"project_type": project_type
-			})
-			p_type.insert()
\ No newline at end of file
diff --git a/erpnext/patches/v8_6/__init__.py b/erpnext/patches/v8_6/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_6/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py b/erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py
deleted file mode 100644
index 014a74a..0000000
--- a/erpnext/patches/v8_6/point_sms_doctype_module_to_frappe_core.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql('''UPDATE `tabDocType` SET module="Core" 
-				WHERE name IN ("SMS Parameter", "SMS Settings");''')
\ No newline at end of file
diff --git a/erpnext/patches/v8_6/rename_bom_update_tool.py b/erpnext/patches/v8_6/rename_bom_update_tool.py
deleted file mode 100644
index ef5f335..0000000
--- a/erpnext/patches/v8_6/rename_bom_update_tool.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.delete_doc_if_exists("DocType", "BOM Replace Tool")
-
-	frappe.reload_doctype("BOM")
-	frappe.db.sql("update tabBOM set conversion_rate=1 where conversion_rate is null or conversion_rate=0")
-	frappe.db.sql("update tabBOM set set_rate_of_sub_assembly_item_based_on_bom=1")
\ No newline at end of file
diff --git a/erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py b/erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py
deleted file mode 100644
index db4f947..0000000
--- a/erpnext/patches/v8_6/set_write_permission_for_quotation_for_sales_manager.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# Set write permission to permlevel 1 for sales manager role in Quotation doctype
-	frappe.db.sql(""" update `tabCustom DocPerm` set `tabCustom DocPerm`.write = 1
-		where `tabCustom DocPerm`.parent = 'Quotation' and `tabCustom DocPerm`.role = 'Sales Manager'
-		and `tabCustom DocPerm`.permlevel = 1 """)
\ No newline at end of file
diff --git a/erpnext/patches/v8_6/update_timesheet_company_from_PO.py b/erpnext/patches/v8_6/update_timesheet_company_from_PO.py
deleted file mode 100644
index 2d46bee..0000000
--- a/erpnext/patches/v8_6/update_timesheet_company_from_PO.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Timesheet')
-	company = frappe.get_all('Company')
-
-	#Check more than one company exists
-	if len(company) > 1:
-		frappe.db.sql(""" update `tabTimesheet` set `tabTimesheet`.company =
-			(select company from `tabWork Order` where name = `tabTimesheet`.work_order)
-			where workn_order is not null and work_order !=''""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_7/__init__.py b/erpnext/patches/v8_7/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_7/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_7/fix_purchase_receipt_status.py b/erpnext/patches/v8_7/fix_purchase_receipt_status.py
deleted file mode 100644
index 99ecb44..0000000
--- a/erpnext/patches/v8_7/fix_purchase_receipt_status.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# there is no more status called "Submitted", there was an old issue that used
-	# to set it as Submitted, fixed in this commit
-	frappe.db.sql("""
-	update
-		`tabPurchase Receipt`
-	set
-		status = 'To Bill'
-	where
-		status = 'Submitted'""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py b/erpnext/patches/v8_7/make_subscription_from_recurring_data.py
deleted file mode 100644
index 2932749..0000000
--- a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import today
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'subscription')
-	frappe.reload_doc('selling', 'doctype', 'sales_order')
-	frappe.reload_doc('selling', 'doctype', 'quotation')
-	frappe.reload_doc('buying', 'doctype', 'purchase_order')
-	frappe.reload_doc('buying', 'doctype', 'supplier_quotation')
-	frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-	frappe.reload_doc('accounts', 'doctype', 'purchase_invoice')
-	frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
-	frappe.reload_doc('stock', 'doctype', 'delivery_note')
-	frappe.reload_doc('accounts', 'doctype', 'journal_entry')
-	frappe.reload_doc('accounts', 'doctype', 'payment_entry')
-
-	for doctype in ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']:
-		date_field = "transaction_date"
-		if doctype in ("Sales Invoice", "Purchase Invoice"):
-			date_field = "posting_date"
-
-		for data in get_data(doctype, date_field):
-			make_subscription(doctype, data, date_field)
-
-def get_data(doctype, date_field):
-	return frappe.db.sql(""" select name, from_date, end_date, recurring_type, recurring_id,
-		next_date, notify_by_email, notification_email_address, recurring_print_format,
-		repeat_on_day_of_month, submit_on_creation, docstatus, {0}
-		from `tab{1}` where is_recurring = 1 and next_date >= %s and docstatus < 2
-		order by next_date desc
-	""".format(date_field, doctype), today(), as_dict=1)
-
-def make_subscription(doctype, data, date_field):
-	if data.name == data.recurring_id:
-		start_date = data.get(date_field)
-	else:
-		start_date = frappe.db.get_value(doctype, data.recurring_id, date_field)
-
-	doc = frappe.get_doc({
-		'doctype': 'Subscription',
-		'reference_doctype': doctype,
-		'reference_document': data.recurring_id,
-		'start_date': start_date,
-		'end_date': data.end_date,
-		'frequency': data.recurring_type,
-		'repeat_on_day': data.repeat_on_day_of_month,
-		'notify_by_email': data.notify_by_email,
-		'recipients': data.notification_email_address,
-		'next_schedule_date': data.next_date,
-		'submit_on_creation': data.submit_on_creation
-	}).insert(ignore_permissions=True)
-
-	if data.docstatus == 1:
-		doc.submit()
\ No newline at end of file
diff --git a/erpnext/patches/v8_8/__init__.py b/erpnext/patches/v8_8/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v8_8/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py b/erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py
deleted file mode 100644
index bd25f15..0000000
--- a/erpnext/patches/v8_8/add_new_fields_in_accounts_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-
-def execute():
-	frappe.db.sql(
-		"INSERT INTO `tabSingles` (`doctype`, `field`, `value`) VALUES ('Accounts Settings', 'allow_stale', '1'), "
-		"('Accounts Settings', 'stale_days', '1')"
-	)
diff --git a/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py b/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py
deleted file mode 100644
index 5b169cd..0000000
--- a/erpnext/patches/v8_8/set_bom_rate_as_per_uom.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.db.sql("""
-		update `tabBOM Item`
-		set rate = rate * conversion_factor
-		where uom != stock_uom and docstatus < 2
-			and conversion_factor not in (0, 1)
-	""")
\ No newline at end of file
diff --git a/erpnext/patches/v8_9/__init__.py b/erpnext/patches/v8_9/__init__.py
deleted file mode 100644
index 8b13789..0000000
--- a/erpnext/patches/v8_9/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/erpnext/patches/v8_9/add_setup_progress_actions.py b/erpnext/patches/v8_9/add_setup_progress_actions.py
deleted file mode 100644
index 7750107..0000000
--- a/erpnext/patches/v8_9/add_setup_progress_actions.py
+++ /dev/null
@@ -1,47 +0,0 @@
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def execute():
-	"""Add setup progress actions"""
-	if not frappe.db.exists('DocType', 'Setup Progress') or not frappe.db.exists('DocType', 'Setup Progress Action'):
-		return
-
-	frappe.reload_doc("setup", "doctype", "setup_progress")
-	frappe.reload_doc("setup", "doctype", "setup_progress_action")
-
-	actions = [
-		{"action_name": "Add Company", "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1,
-			"domains": '[]' },
-		{"action_name": "Set Sales Target", "action_doctype": "Company", "min_doc_count": 99,
-			"action_document": frappe.defaults.get_defaults().get("company") or '',
-			"action_field": "monthly_sales_target", "is_completed": 0,
-			"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
-		{"action_name": "Add Customers", "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
-		{"action_name": "Add Suppliers", "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
-		{"action_name": "Add Products", "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
-		{"action_name": "Add Programs", "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Education"]' },
-		{"action_name": "Add Instructors", "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Education"]' },
-		{"action_name": "Add Courses", "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Education"]' },
-		{"action_name": "Add Rooms", "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0,
-			"domains": '["Education"]' },
-		{"action_name": "Add Users", "action_doctype": "User", "min_doc_count": 4, "is_completed": 0,
-			"domains": '[]' },
-		{"action_name": "Add Letterhead", "action_doctype": "Letter Head", "min_doc_count": 1, "is_completed": 0,
-			"domains": '[]' }
-	]
-
-	setup_progress = frappe.get_doc("Setup Progress", "Setup Progress")
-	setup_progress.actions = []
-	for action in actions:
-		setup_progress.append("actions", action)
-
-	setup_progress.save(ignore_permissions=True)
-
diff --git a/erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py b/erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py
deleted file mode 100644
index f67af90..0000000
--- a/erpnext/patches/v8_9/delete_gst_doctypes_for_outside_india_accounts.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
-	if not company:
-		if frappe.db.exists("DocType", "GST Settings"):
-			frappe.delete_doc("DocType", "GST Settings")
-			frappe.delete_doc("DocType", "GST HSN Code")
-		
-			for report_name in ('GST Sales Register', 'GST Purchase Register',
-				'GST Itemised Sales Register', 'GST Itemised Purchase Register'):
-
-				frappe.delete_doc('Report', report_name)
\ No newline at end of file
diff --git a/erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py b/erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py
deleted file mode 100644
index 808ae6d..0000000
--- a/erpnext/patches/v8_9/remove_employee_from_salary_structure_parent.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if 'employee' in frappe.db.get_table_columns("Salary Structure"):
-		frappe.db.sql("alter table `tabSalary Structure` drop column employee")
diff --git a/erpnext/patches/v8_9/rename_company_sales_target_field.py b/erpnext/patches/v8_9/rename_company_sales_target_field.py
deleted file mode 100644
index 5433eb6..0000000
--- a/erpnext/patches/v8_9/rename_company_sales_target_field.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	frappe.reload_doc("setup", "doctype", "company")
-	if frappe.db.has_column('Company', 'sales_target'):
-		rename_field("Company", "sales_target", "monthly_sales_target")
diff --git a/erpnext/patches/v8_9/set_default_customer_group.py b/erpnext/patches/v8_9/set_default_customer_group.py
deleted file mode 100644
index cbbe09d..0000000
--- a/erpnext/patches/v8_9/set_default_customer_group.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	selling_settings = frappe.get_single('Selling Settings')
-	selling_settings.set_default_customer_group_and_territory()
-	selling_settings.flags.ignore_mandatory = True
-	selling_settings.save()
diff --git a/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py b/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py
deleted file mode 100644
index a550d09..0000000
--- a/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('stock', 'doctype', 'item_variant_settings')
-	frappe.reload_doc('stock', 'doctype', 'variant_field')
-
-	doc = frappe.get_doc('Item Variant Settings')
-	doc.set_default_fields()
-	doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v8_9/set_member_party_type.py b/erpnext/patches/v8_9/set_member_party_type.py
deleted file mode 100644
index 33bbc11..0000000
--- a/erpnext/patches/v8_9/set_member_party_type.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if not frappe.db.exists("Party Type", "Member"):
-		frappe.reload_doc("non_profit", "doctype", "member")
-		party = frappe.new_doc("Party Type")
-		party.party_type = "Member"
-		party.save()
diff --git a/erpnext/patches/v8_9/set_print_zero_amount_taxes.py b/erpnext/patches/v8_9/set_print_zero_amount_taxes.py
deleted file mode 100644
index 3c508ea..0000000
--- a/erpnext/patches/v8_9/set_print_zero_amount_taxes.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-from erpnext.setup.install import create_print_zero_amount_taxes_custom_field
-
-def execute():
-	frappe.reload_doc('printing', 'doctype', 'print_style')
-	frappe.reload_doc('printing', 'doctype', 'print_settings')
-	create_print_zero_amount_taxes_custom_field()
\ No newline at end of file
diff --git a/erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py b/erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py
deleted file mode 100644
index 24e2040..0000000
--- a/erpnext/patches/v8_9/update_billing_gstin_for_indian_account.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	company = frappe.get_all('Company', filters = {'country': 'India'})
-
-	if company:
-		for doctype in ['Sales Invoice', 'Delivery Note']:
-			frappe.db.sql(""" update `tab{0}`
-				set billing_address_gstin = (select gstin from `tabAddress` 
-					where name = customer_address) 
-			where customer_address is not null and customer_address != ''""".format(doctype))
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/__init__.py b/erpnext/patches/v9_0/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/patches/v9_0/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/patches/v9_0/add_healthcare_domain.py b/erpnext/patches/v9_0/add_healthcare_domain.py
deleted file mode 100644
index 3c0433b..0000000
--- a/erpnext/patches/v9_0/add_healthcare_domain.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	domain = 'Healthcare'
-	if not frappe.db.exists('Domain', domain):
-		frappe.get_doc({
-			'doctype': 'Domain',
-			'domain': domain
-		}).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py b/erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py
deleted file mode 100644
index 8a8c806..0000000
--- a/erpnext/patches/v9_0/add_user_to_child_table_in_pos_profile.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.table_exists("POS Profile User"):
-		frappe.reload_doc('accounts', 'doctype', 'pos_profile_user')
-
-		frappe.db.sql(""" update `tabPOS Profile User`,
-			(select `tabPOS Profile User`.name from `tabPOS Profile User`, `tabPOS Profile`
-				where `tabPOS Profile`.name = `tabPOS Profile User`.parent
-				group by `tabPOS Profile User`.user, `tabPOS Profile`.company) as pfu
-			set
-				`tabPOS Profile User`.default = 1
-			where `tabPOS Profile User`.name = pfu.name""")
-	else:
-		doctype = 'POS Profile'
-		frappe.reload_doc('accounts', 'doctype', doctype)
-		frappe.reload_doc('accounts', 'doctype', 'pos_profile_user')
-		frappe.reload_doc('accounts', 'doctype', 'pos_item_group')
-		frappe.reload_doc('accounts', 'doctype', 'pos_customer_group')
-
-		for doc in frappe.get_all(doctype):
-			_doc = frappe.get_doc(doctype, doc.name)
-			user = frappe.db.get_value(doctype, doc.name, 'user')
-
-			if not user: continue
-
-			_doc.append('applicable_for_users', {
-				'user': user,
-				'default': 1
-			})
-
-			_doc.flags.ignore_validate  = True
-			_doc.flags.ignore_mandatory = True
-			_doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/copy_old_fees_field_data.py b/erpnext/patches/v9_0/copy_old_fees_field_data.py
deleted file mode 100644
index 1427820..0000000
--- a/erpnext/patches/v9_0/copy_old_fees_field_data.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# 'Schools' module changed to the 'Education'
-	# frappe.reload_doc("schools", "doctype", "fees")
-	frappe.reload_doc("education", "doctype", "fees")
-
-	if "total_amount" not in frappe.db.get_table_columns("Fees"):
-		return
-
-	frappe.db.sql("""update tabFees set grand_total=total_amount where grand_total = 0.0""")
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py b/erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py
deleted file mode 100644
index c685bbc..0000000
--- a/erpnext/patches/v9_0/remove_non_existing_warehouse_from_stock_settings.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	default_warehouse = frappe.db.get_value("Stock Settings", None, "default_warehouse")
-	if default_warehouse:
-		if not frappe.db.get_value("Warehouse", {"name": default_warehouse}):
-			frappe.db.set_value("Stock Settings", None, "default_warehouse", "")
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/remove_subscription_module.py b/erpnext/patches/v9_0/remove_subscription_module.py
deleted file mode 100644
index 493873f..0000000
--- a/erpnext/patches/v9_0/remove_subscription_module.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists('Module Def', 'Subscription'):
-		frappe.db.sql(""" delete from `tabModule Def` where name = 'Subscription'""")
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/revert_manufacturing_user_role.py b/erpnext/patches/v9_0/revert_manufacturing_user_role.py
deleted file mode 100644
index f38b7f2..0000000
--- a/erpnext/patches/v9_0/revert_manufacturing_user_role.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if 'Manufacturing' in frappe.get_active_domains(): return
-
-	role = 'Manufacturing User'
-	frappe.db.set_value('Role', role, 'restrict_to_domain', '')
-	frappe.db.set_value('Role', role, 'disabled', 0)
-
-	users = frappe.get_all('Has Role', filters = {
-		'parenttype': 'User',
-		'role': ('in', ['System Manager', 'Manufacturing Manager'])
-	}, fields=['parent'], as_list=1)
-
-	for user in users:
-		_user = frappe.get_doc('User', user[0])
-		_user.append('roles', {
-			'role': role
-		})
-		_user.flags.ignore_validate = True
-		_user.save()
diff --git a/erpnext/patches/v9_0/set_pos_profile_name.py b/erpnext/patches/v9_0/set_pos_profile_name.py
deleted file mode 100644
index a3a9735..0000000
--- a/erpnext/patches/v9_0/set_pos_profile_name.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	doctype = 'POS Profile'
-	frappe.reload_doctype(doctype)
-
-	for pos in frappe.get_all(doctype, filters={'disabled': 0}):
-		doc = frappe.get_doc(doctype, pos.name)
-
-		if not doc.user: continue
-
-		try:
-			pos_profile_name = doc.user + ' - ' + doc.company
-			doc.flags.ignore_validate  = True
-			doc.flags.ignore_mandatory = True
-			doc.save()
-
-			frappe.rename_doc(doctype, doc.name, pos_profile_name, force=True)
-		except frappe.LinkValidationError:
-			frappe.db.set_value("POS Profile", doc.name, 'disabled', 1)
diff --git a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py b/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py
deleted file mode 100644
index 3d01297..0000000
--- a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	for doctype in ("Material Request", "Purchase Order"):
-		frappe.reload_doctype(doctype)
-		frappe.reload_doctype(doctype + " Item")
-
-		if not frappe.db.has_column(doctype, "schedule_date"):
-			continue
-
-		#Update only submitted MR
-		for record in frappe.get_all(doctype, filters= [["docstatus", "=", 1]], fields=["name"]):
-			doc = frappe.get_doc(doctype, record)
-			if doc.items:
-				if not doc.schedule_date:
-					schedule_dates = [d.schedule_date for d in doc.items if d.schedule_date]
-					if len(schedule_dates) > 0:
-						min_schedule_date = min(schedule_dates)
-						frappe.db.set_value(doctype, record,
-							"schedule_date", min_schedule_date, update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py b/erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py
deleted file mode 100644
index 5092695..0000000
--- a/erpnext/patches/v9_0/set_shipping_type_for_existing_shipping_rules.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype("Shipping Rule")
-
-	# default "calculate_based_on"
-	frappe.db.sql('''update `tabShipping Rule`
-		set calculate_based_on = "Net Weight"
-		where ifnull(calculate_based_on, '') = '' ''')
-
-	# default "shipping_rule_type"
-	frappe.db.sql('''update `tabShipping Rule`
-		set shipping_rule_type = "Selling"
-		where ifnull(shipping_rule_type, '') = '' ''')
diff --git a/erpnext/patches/v9_0/set_uoms_in_variant_field.py b/erpnext/patches/v9_0/set_uoms_in_variant_field.py
deleted file mode 100644
index 9e783d9..0000000
--- a/erpnext/patches/v9_0/set_uoms_in_variant_field.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-
-def execute():
-	doc = frappe.get_doc('Item Variant Settings')
-	variant_field_names = [vf.field_name for vf in doc.fields]
-	if 'uoms' not in variant_field_names:
-		doc.append(
-			'fields', {
-					'field_name': 'uoms'
-				}
-		)
-	doc.save()
diff --git a/erpnext/patches/v9_0/set_variant_item_description.py b/erpnext/patches/v9_0/set_variant_item_description.py
deleted file mode 100644
index 82d6148..0000000
--- a/erpnext/patches/v9_0/set_variant_item_description.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cstr
-
-def execute():
-	'''
-	Issue:
-		While copying data from template item to variant item,
-		the system appending description multiple times to the respective variant.
-
-	Purpose:
-		Check variant description,
-		if variant have user defined description remove all system appended descriptions
-		else replace multiple system generated descriptions with single description
-
-	Steps:
-		1. Get all variant items
-		2. Create system generated variant description
-		3. If variant have user defined description, remove all system generated descriptions
-		4. If variant description only contains system generated description,
-			replace multiple descriptions by new description.
-	'''
-	for item in frappe.db.sql(""" select name from tabItem
-		where ifnull(variant_of, '') != '' """,as_dict=1):
-			variant = frappe.get_doc("Item", item.name)
-			temp_variant_description = '\n'
-
-			if variant.attributes:
-				for d in variant.attributes:
-					temp_variant_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
-
-				variant_description = variant.description.replace(temp_variant_description, '').rstrip()
-				if variant_description:
-					splitted_desc = variant.description.strip().split(temp_variant_description)
-
-					if len(splitted_desc) > 2:
-						if splitted_desc[0] == '':
-							variant_description = temp_variant_description + variant_description
-						elif splitted_desc[1] == '' or splitted_desc[1] == '\n':
-							variant_description += temp_variant_description
-						variant.db_set('description', variant_description, update_modified=False)
-					else:
-						variant.db_set('description', variant_description, update_modified=False)
-
-				else:
-					variant.db_set('description', temp_variant_description, update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/student_admission_childtable_migrate.py b/erpnext/patches/v9_0/student_admission_childtable_migrate.py
deleted file mode 100644
index a5712c7..0000000
--- a/erpnext/patches/v9_0/student_admission_childtable_migrate.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	# 'Schools' module changed to the 'Education'
-	# frappe.reload_doc('schools', 'doctype', 'Student Admission Program')
-	# frappe.reload_doc('schools', 'doctype', 'student_admission')
-	frappe.reload_doc('education', 'doctype', 'Student Admission Program')
-	frappe.reload_doc('education', 'doctype', 'student_admission')
-
-	if "program" not in frappe.db.get_table_columns("Student Admission"):
-		return
-
-	student_admissions = frappe.get_all("Student Admission", fields=["name", "application_fee", \
-		"naming_series_for_student_applicant", "program", "introduction", "eligibility"])
-	for student_admission in student_admissions:
-		doc = frappe.get_doc("Student Admission", student_admission.name)
-		doc.append("program_details", {
-			"program": student_admission.get("program"),
-			"application_fee": student_admission.get("application_fee"),
-			"applicant_naming_series": student_admission.get("naming_series_for_student_applicant"),
-		})
-		if student_admission.eligibility and student_admission.introduction:
-			doc.introduction = student_admission.introduction + "<br><div>" + \
-				student_admission.eligibility + "</div>"
-		doc.flags.ignore_validate = True
-		doc.flags.ignore_mandatory = True
-		doc.save()
diff --git a/erpnext/patches/v9_0/update_employee_loan_details.py b/erpnext/patches/v9_0/update_employee_loan_details.py
deleted file mode 100644
index ef8d328..0000000
--- a/erpnext/patches/v9_0/update_employee_loan_details.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc('Payroll', 'doctype', 'salary_slip_loan')
-	frappe.reload_doc('Payroll', 'doctype', 'salary_slip')
-
-	for data in frappe.db.sql(""" select name,
-			start_date, end_date, total_loan_repayment
-		from
-			`tabSalary Slip`
-		where
-			docstatus < 2 and ifnull(total_loan_repayment, 0) > 0""", as_dict=1):
-		salary_slip = frappe.get_doc('Salary Slip', data.name)
-		salary_slip.set_loan_repayment()
-
-		if salary_slip.total_loan_repayment == data.total_loan_repayment:
-			for row in salary_slip.loans:
-				row.db_update()
-
-			salary_slip.db_update()
diff --git a/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py b/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py
deleted file mode 100644
index 45610ed..0000000
--- a/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doctype('Material Request')
-	frappe.reload_doctype('Material Request Item')
-
-	frappe.db.sql(""" update `tabMaterial Request Item`
-		set stock_uom = uom, stock_qty = qty, conversion_factor = 1.0""")
\ No newline at end of file
diff --git a/erpnext/patches/v9_1/__init__.py b/erpnext/patches/v9_1/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v9_1/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v9_1/create_issue_opportunity_type.py b/erpnext/patches/v9_1/create_issue_opportunity_type.py
deleted file mode 100644
index aa8bbd1..0000000
--- a/erpnext/patches/v9_1/create_issue_opportunity_type.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-
-def execute():
-	# delete custom field if exists
-	for doctype, fieldname in (('Issue', 'issue_type'), ('Opportunity', 'opportunity_type')):
-		custom_field = frappe.db.get_value("Custom Field", {"fieldname": fieldname, 'dt': doctype})
-		if custom_field:
-			frappe.delete_doc("Custom Field", custom_field, ignore_permissions=True)
-
-	frappe.reload_doc('support', 'doctype', 'issue_type')
-	frappe.reload_doc('support', 'doctype', 'issue')
-	frappe.reload_doc('crm', 'doctype', 'opportunity_type')
-	frappe.reload_doc('crm', 'doctype', 'opportunity')
-
-	# rename enquiry_type -> opportunity_type
-	from frappe.model.utils.rename_field import rename_field
-	rename_field('Opportunity', 'enquiry_type', 'opportunity_type')
-
-	# create values if already set
-	for opts in (('Issue', 'issue_type', 'Issue Type'),
-		('Opportunity', 'opportunity_type', 'Opportunity Type')):
-		for d in frappe.db.sql('select distinct {0} from `tab{1}`'.format(opts[1], opts[0])):
-			if d[0] and not frappe.db.exists(opts[2], d[0]):
-				frappe.get_doc(dict(doctype = opts[2], name=d[0])).insert()
-
-	# fixtures
-	for name in ('Hub', _('Sales'), _('Support'), _('Maintenance')):
-		if not frappe.db.exists('Opportunity Type', name):
-			frappe.get_doc(dict(doctype = 'Opportunity Type', name=name)).insert()
diff --git a/erpnext/patches/v9_2/__init__.py b/erpnext/patches/v9_2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/patches/v9_2/__init__.py
+++ /dev/null
diff --git a/erpnext/patches/v9_2/delete_healthcare_domain_default_items.py b/erpnext/patches/v9_2/delete_healthcare_domain_default_items.py
deleted file mode 100644
index 54ae18b..0000000
--- a/erpnext/patches/v9_2/delete_healthcare_domain_default_items.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import getdate
-
-def execute():
-	domain_settings = frappe.get_doc('Domain Settings')
-	active_domains = [d.domain for d in domain_settings.active_domains]
-
-	if "Healthcare" not in active_domains:
-		items = ["TTT", "MCH", "LDL", "GTT", "HDL", "BILT", "BILD", "BP", "BS"]
-		for item_code in items:
-			try:
-				item = frappe.db.get_value("Item", {"item_code": item_code}, ["name", "creation"], as_dict=1)
-				if item and getdate(item.creation) >= getdate("2017-11-10"):
-					frappe.delete_doc("Item", item.name)
-			except:
-				pass
\ No newline at end of file
diff --git a/erpnext/patches/v9_2/delete_process_payroll.py b/erpnext/patches/v9_2/delete_process_payroll.py
deleted file mode 100644
index 91c49f5..0000000
--- a/erpnext/patches/v9_2/delete_process_payroll.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.delete_doc("DocType", "Process Payroll")
diff --git a/erpnext/patches/v9_2/remove_company_from_patient.py b/erpnext/patches/v9_2/remove_company_from_patient.py
deleted file mode 100644
index 1a50088..0000000
--- a/erpnext/patches/v9_2/remove_company_from_patient.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	if frappe.db.exists("DocType", "Patient"):
-		if 'company' in frappe.db.get_table_columns("Patient"):
-			frappe.db.sql("alter table `tabPatient` drop column company")
diff --git a/erpnext/patches/v9_2/rename_net_weight_in_item_master.py b/erpnext/patches/v9_2/rename_net_weight_in_item_master.py
deleted file mode 100644
index cad979d..0000000
--- a/erpnext/patches/v9_2/rename_net_weight_in_item_master.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "item")
-	if frappe.db.has_column('Item', 'net_weight'):
-		rename_field("Item", "net_weight", "weight_per_unit")
diff --git a/erpnext/patches/v9_2/rename_translated_domains_in_en.py b/erpnext/patches/v9_2/rename_translated_domains_in_en.py
deleted file mode 100644
index e5a9e24..0000000
--- a/erpnext/patches/v9_2/rename_translated_domains_in_en.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.rename_doc import rename_doc
-
-def execute():
-	frappe.reload_doc('stock', 'doctype', 'item')
-	language = frappe.get_single("System Settings").language
-
-	if language and language.startswith('en'): return
-
-	frappe.local.lang = language
-
-	all_domains = frappe.get_hooks("domains")
-
-	for domain in all_domains:
-		translated_domain = _(domain, lang=language)
-		if frappe.db.exists("Domain", translated_domain):
-			#if domain already exists merged translated_domain and domain
-			merge = False
-			if frappe.db.exists("Domain", domain):
-				merge=True
-
-			rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge)
-
-	domain_settings = frappe.get_single("Domain Settings")
-	active_domains = [d.domain for d in domain_settings.active_domains]
-
-	try:
-		for domain in active_domains:
-			domain = frappe.get_doc("Domain", domain)
-			domain.setup_domain()
-
-			if int(frappe.db.get_single_value('System Settings', 'setup_complete')):
-				domain.setup_sidebar_items()
-				domain.setup_desktop_icons()
-				domain.set_default_portal_role()
-	except frappe.LinkValidationError:
-		pass
\ No newline at end of file
diff --git a/erpnext/patches/v9_2/repost_reserved_qty_for_production.py b/erpnext/patches/v9_2/repost_reserved_qty_for_production.py
deleted file mode 100644
index 040e655..0000000
--- a/erpnext/patches/v9_2/repost_reserved_qty_for_production.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-	frappe.reload_doc("stock", "doctype", "bin")
-	bins = frappe.db.sql("select name from `tabBin` where reserved_qty_for_production > 0")
-	for d in bins:
-		bin_doc = frappe.get_doc("Bin", d[0])
-		bin_doc.update_reserved_qty_for_production()
diff --git a/erpnext/patches/v9_2/set_item_name_in_production_order.py b/erpnext/patches/v9_2/set_item_name_in_production_order.py
deleted file mode 100644
index 1f490e6..0000000
--- a/erpnext/patches/v9_2/set_item_name_in_production_order.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from __future__ import unicode_literals
-import frappe
-
-def execute():
-
-	frappe.db.sql("""
-		update `tabBOM Item` bom, `tabWork Order Item` po_item
-		set po_item.item_name = bom.item_name,
-			po_item.description = bom.description
-		where po_item.item_code = bom.item_code
-			and (po_item.item_name is null or po_item.description is null)
-	""")
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js
index d1ed91f..24ffce5 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.js
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js
@@ -12,8 +12,12 @@
 				}
 			};
 		});
+	},
 
-		frm.trigger('set_earning_component');
+	onload: function(frm) {
+		if (frm.doc.type) {
+			frm.trigger('set_component_query');
+		}
 	},
 
 	employee: function(frm) {
@@ -46,14 +50,19 @@
 	},
 
 	company: function(frm) {
-		frm.trigger('set_earning_component');
+		frm.set_value("type", "");
+		frm.trigger('set_component_query');
 	},
 
-	set_earning_component: function(frm) {
+	set_component_query: function(frm) {
 		if (!frm.doc.company) return;
+		let filters = {company: frm.doc.company};
+		if (frm.doc.type) {
+			filters.type = frm.doc.type;
+		}
 		frm.set_query("salary_component", function() {
 			return {
-				filters: {type: ["in", ["earning", "deduction"]], company: frm.doc.company}
+				filters: filters
 			};
 		});
 	},
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
index f289260..496c37b 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
@@ -135,10 +135,26 @@
 		});
 
 		frm.set_query('employee', 'employees', () => {
-			if (!frm.doc.company) {
-				frappe.msgprint(__("Please set a Company"));
-				return [];
+			let error_fields = [];
+			let mandatory_fields = ['company', 'payroll_frequency', 'start_date', 'end_date'];
+
+			let message = __('Mandatory fields required in {0}', [__(frm.doc.doctype)]);
+
+			mandatory_fields.forEach(field => {
+				if (!frm.doc[field]) {
+					error_fields.push(frappe.unscrub(field));
+				}
+			});
+
+			if (error_fields && error_fields.length) {
+				message = message + '<br><br><ul><li>' + error_fields.join('</li><li>') + "</ul>";
+				frappe.throw({
+					message: message,
+					indicator: 'red',
+					title: __('Missing Fields')
+				});
 			}
+
 			return {
 				query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.employee_query",
 				filters: frm.events.get_employee_filters(frm)
@@ -148,25 +164,22 @@
 
 	get_employee_filters: function (frm) {
 		let filters = {};
-		filters['company'] = frm.doc.company;
-		filters['start_date'] = frm.doc.start_date;
-		filters['end_date'] = frm.doc.end_date;
 		filters['salary_slip_based_on_timesheet'] = frm.doc.salary_slip_based_on_timesheet;
-		filters['payroll_frequency'] = frm.doc.payroll_frequency;
-		filters['payroll_payable_account'] = frm.doc.payroll_payable_account;
-		filters['currency'] = frm.doc.currency;
 
-		if (frm.doc.department) {
-			filters['department'] = frm.doc.department;
-		}
-		if (frm.doc.branch) {
-			filters['branch'] = frm.doc.branch;
-		}
-		if (frm.doc.designation) {
-			filters['designation'] = frm.doc.designation;
-		}
+		let fields = ['company', 'start_date', 'end_date', 'payroll_frequency', 'payroll_payable_account',
+			'currency', 'department', 'branch', 'designation'];
+
+		fields.forEach(field => {
+			if (frm.doc[field]) {
+				filters[field] = frm.doc[field];
+			}
+		});
+
 		if (frm.doc.employees) {
-			filters['employees'] = frm.doc.employees.filter(d => d.employee).map(d => d.employee);
+			let employees = frm.doc.employees.filter(d => d.employee).map(d => d.employee);
+			if (employees && employees.length) {
+				filters['employees'] = employees;
+			}
 		}
 		return filters;
 	},
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 3953b46..13cc423 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -11,6 +11,7 @@
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 from frappe.desk.reportview import get_match_cond, get_filters_cond
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
 
 class PayrollEntry(Document):
 	def onload(self):
@@ -41,7 +42,7 @@
 				emp_with_sal_slip.append(employee_details.employee)
 
 		if len(emp_with_sal_slip):
-			frappe.throw(_("Salary Slip already exists for {0} ").format(comma_and(emp_with_sal_slip)))
+			frappe.throw(_("Salary Slip already exists for {0}").format(comma_and(emp_with_sal_slip)))
 
 	def on_cancel(self):
 		frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
@@ -116,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({
@@ -211,7 +211,7 @@
 		return account_dict
 
 	def make_accrual_jv_entry(self):
-		self.check_permission('write')
+		self.check_permission("write")
 		earnings = self.get_salary_component_total(component_type = "earnings") or {}
 		deductions = self.get_salary_component_total(component_type = "deductions") or {}
 		payroll_payable_account = self.payroll_payable_account
@@ -219,12 +219,13 @@
 		precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
 
 		if earnings or deductions:
-			journal_entry = frappe.new_doc('Journal Entry')
-			journal_entry.voucher_type = 'Journal Entry'
-			journal_entry.user_remark = _('Accrual Journal Entry for salaries from {0} to {1}')\
+			journal_entry = frappe.new_doc("Journal Entry")
+			journal_entry.voucher_type = "Journal Entry"
+			journal_entry.user_remark = _("Accrual Journal Entry for salaries from {0} to {1}")\
 				.format(self.start_date, self.end_date)
 			journal_entry.company = self.company
 			journal_entry.posting_date = self.posting_date
+			accounting_dimensions = get_accounting_dimensions() or []
 
 			accounts = []
 			currencies = []
@@ -236,37 +237,34 @@
 			for acc_cc, amount in earnings.items():
 				exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
 				payable_amount += flt(amount, precision)
-				accounts.append({
+				accounts.append(self.update_accounting_dimensions({
 					"account": acc_cc[0],
 					"debit_in_account_currency": flt(amt, precision),
 					"exchange_rate": flt(exchange_rate),
-					"party_type": '',
 					"cost_center": acc_cc[1] or self.cost_center,
 					"project": self.project
-				})
+				}, accounting_dimensions))
 
 			# Deductions
 			for acc_cc, amount in deductions.items():
 				exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
 				payable_amount -= flt(amount, precision)
-				accounts.append({
+				accounts.append(self.update_accounting_dimensions({
 					"account": acc_cc[0],
 					"credit_in_account_currency": flt(amt, precision),
 					"exchange_rate": flt(exchange_rate),
 					"cost_center": acc_cc[1] or self.cost_center,
-					"party_type": '',
 					"project": self.project
-				})
+				}, accounting_dimensions))
 
 			# Payable amount
 			exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, payable_amount, company_currency, currencies)
-			accounts.append({
+			accounts.append(self.update_accounting_dimensions({
 				"account": payroll_payable_account,
 				"credit_in_account_currency": flt(payable_amt, precision),
 				"exchange_rate": flt(exchange_rate),
-				"party_type": '',
 				"cost_center": self.cost_center
-			})
+			}, accounting_dimensions))
 
 			journal_entry.set("accounts", accounts)
 			if len(currencies) > 1:
@@ -286,6 +284,12 @@
 
 		return jv_name
 
+	def update_accounting_dimensions(self, row, accounting_dimensions):
+		for dimension in accounting_dimensions:
+			row.update({dimension: self.get(dimension)})
+
+		return row
+
 	def get_amount_and_exchange_rate_for_journal_entry(self, account, amount, company_currency, currencies):
 		conversion_rate = 1
 		exchange_rate = self.exchange_rate
@@ -454,6 +458,7 @@
 			where
 				t1.name = t2.employee
 				and t2.docstatus = 1
+				and t1.status != 'Inactive'
 		%s order by t2.from_date desc
 		""" % cond, {"sal_struct": tuple(sal_struct), "from_date": end_date, "payroll_payable_account": payroll_payable_account}, as_dict=True)
 
@@ -665,6 +670,8 @@
 		emp_list = remove_payrolled_employees(emp_list, filters.start_date, filters.end_date)
 		return emp_list
 
+	return []
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def employee_query(doctype, txt, searchfield, start, page_len, filters):
@@ -672,9 +679,13 @@
 	conditions = []
 	include_employees = []
 	emp_cond = ''
+
+	if not filters.payroll_frequency:
+		frappe.throw(_('Select Payroll Frequency.'))
+
 	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/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
index b8e56ae..049ea26 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -10,8 +10,8 @@
 
 class RetentionBonus(Document):
 	def validate(self):
-		if frappe.get_value('Employee', self.employee, 'status') == 'Left':
-			frappe.throw(_('Cannot create Retention Bonus for left Employees'))
+		if frappe.get_value('Employee', self.employee, 'status') != 'Active':
+			frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees'))
 		if getdate(self.bonus_payment_date) < getdate():
 			frappe.throw(_('Bonus Payment Date cannot be a past date'))
 
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index afdf081..bead880 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -115,10 +115,23 @@
 			status = "Cancelled"
 		return status
 
-	def validate_dates(self):
+	def validate_dates(self, joining_date=None, relieving_date=None):
 		if date_diff(self.end_date, self.start_date) < 0:
 			frappe.throw(_("To date cannot be before From date"))
 
+		if not joining_date:
+			joining_date, relieving_date = frappe.get_cached_value(
+				"Employee",
+				self.employee,
+				("date_of_joining", "relieving_date")
+			)
+
+		if date_diff(self.end_date, joining_date) < 0:
+			frappe.throw(_("Cannot create Salary Slip for Employee joining after Payroll Period"))
+
+		if relieving_date and date_diff(relieving_date, self.start_date) < 0:
+			frappe.throw(_("Cannot create Salary Slip for Employee who has left before Payroll Period"))
+
 	def is_rounding_total_disabled(self):
 		return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total"))
 
@@ -154,9 +167,14 @@
 
 			if not self.salary_slip_based_on_timesheet:
 				self.get_date_details()
-			self.validate_dates()
-			joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
-				["date_of_joining", "relieving_date"])
+
+			joining_date, relieving_date = frappe.get_cached_value(
+				"Employee",
+				self.employee,
+				("date_of_joining", "relieving_date")
+			)
+
+			self.validate_dates(joining_date, relieving_date)
 
 			#getin leave details
 			self.get_working_days_details(joining_date, relieving_date)
@@ -492,11 +510,39 @@
 	def get_data_for_eval(self):
 		'''Returns data for evaluating formula'''
 		data = frappe._dict()
+		employee = frappe.get_doc("Employee", self.employee).as_dict()
 
-		data.update(frappe.get_doc("Salary Structure Assignment",
-			{"employee": self.employee, "salary_structure": self.salary_structure}).as_dict())
+		start_date = getdate(self.start_date)
+		date_to_validate = (
+			employee.date_of_joining
+			if employee.date_of_joining > start_date
+			else start_date
+		)
 
-		data.update(frappe.get_doc("Employee", self.employee).as_dict())
+		salary_structure_assignment = frappe.get_value(
+			"Salary Structure Assignment",
+			{
+				"employee": self.employee,
+				"salary_structure": self.salary_structure,
+				"from_date": ("<=", date_to_validate),
+				"docstatus": 1,
+			},
+			"*",
+			order_by="from_date desc",
+			as_dict=True,
+		)
+
+		if not salary_structure_assignment:
+			frappe.throw(
+				_("Please assign a Salary Structure for Employee {0} "
+				"applicable from or before {1} first").format(
+					frappe.bold(self.employee_name),
+					frappe.bold(formatdate(date_to_validate)),
+				)
+			)
+
+		data.update(salary_structure_assignment)
+		data.update(employee)
 		data.update(self.as_dict())
 
 		# set values for components
@@ -1045,6 +1091,7 @@
 				"applicant": self.employee,
 				"docstatus": 1,
 				"repay_from_salary": 1,
+				"company": self.company
 			})
 
 	def make_loan_repayment_entry(self):
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 01e4170..6e8d3b3 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -8,7 +8,6 @@
 import calendar
 import random
 from erpnext.accounts.utils import get_fiscal_year
-from frappe.utils.make_random import get_random
 from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day, cstr
 from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
 from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details
@@ -155,12 +154,14 @@
 		self.assertEqual(ss.gross_pay, 78000)
 
 	def test_payment_days(self):
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import create_salary_structure_assignment
+
 		no_of_days = self.get_no_of_days()
 		# Holidays not included in working days
 		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
 
 		# set joinng date in the same month
-		make_employee("test_payment_days@salary.com")
+		employee = make_employee("test_payment_days@salary.com")
 		if getdate(nowdate()).day >= 15:
 			relieving_date = getdate(add_days(nowdate(),-10))
 			date_of_joining = getdate(add_days(nowdate(),-10))
@@ -174,25 +175,30 @@
 			date_of_joining = getdate(nowdate())
 			relieving_date = getdate(nowdate())
 
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "date_of_joining", date_of_joining)
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", None)
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Active")
+		frappe.db.set_value("Employee", employee, {
+			"date_of_joining": date_of_joining,
+			"relieving_date": None,
+			"status": "Active"
+		})
 
-		ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", "Test Payment Days")
+		salary_structure = "Test Payment Days"
+		ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", salary_structure)
 
 		self.assertEqual(ss.total_working_days, no_of_days[0])
 		self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1))
 
 		# set relieving date in the same month
-		frappe.db.set_value("Employee",frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60)))
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", relieving_date)
-		frappe.db.set_value("Employee", frappe.get_value("Employee",
-			{"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Left")
+		frappe.db.set_value("Employee", employee, {
+			"date_of_joining": add_days(nowdate(),-60),
+			"relieving_date": relieving_date,
+			"status": "Left"
+		})
+
+		if date_of_joining.day > 1:
+			self.assertRaises(frappe.ValidationError, ss.save)
+
+		create_salary_structure_assignment(employee, salary_structure)
+		ss.reload()
 		ss.save()
 
 		self.assertEqual(ss.total_working_days, no_of_days[0])
@@ -285,6 +291,7 @@
 
 	def test_multi_currency_salary_slip(self):
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
 		applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company")
 		frappe.db.sql("""delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""")
 		salary_structure = make_salary_structure("Test Multi Currency Salary Slip", "Monthly", employee=applicant, company="_Test Company", currency='USD')
@@ -325,7 +332,8 @@
 	def test_component_wise_year_to_date_computation(self):
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
-		applicant = make_employee("test_ytd@salary.com", company="_Test Company")
+		employee_name = "test_component_wise_ytd@salary.com"
+		applicant = make_employee(employee_name, company="_Test Company")
 
 		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
 
@@ -336,13 +344,13 @@
 			"Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period)
 
 		# clear salary slip for this employee
-		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
+		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = '%s'" % employee_name)
 
 		create_salary_slips_for_payroll_period(applicant, salary_structure.name,
 			payroll_period, deduct_random=False, num=3)
 
 		salary_slips = frappe.get_all("Salary Slip", fields=["name"], filters={"employee_name":
-			"test_ytd@salary.com"}, order_by = "posting_date")
+			employee_name}, order_by="posting_date")
 
 		year_to_date = dict()
 		for slip in salary_slips:
@@ -380,10 +388,10 @@
 
 		from erpnext.payroll.doctype.salary_structure.test_salary_structure import \
 			make_salary_structure, create_salary_structure_assignment
+
 		salary_structure = make_salary_structure("Stucture to test tax", "Monthly",
-			other_details={"max_benefits": 100000}, test_tax=True)
-		create_salary_structure_assignment(employee, salary_structure.name,
-			payroll_period.start_date)
+			other_details={"max_benefits": 100000}, test_tax=True,
+			employee=employee, payroll_period=payroll_period)
 
 		# create salary slip for whole period deducting tax only on last period
 		# to find the total tax amount paid
@@ -469,17 +477,24 @@
 
 def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
 	from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
 	if not salary_structure:
 		salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
 
-	employee = frappe.db.get_value("Employee", {"user_id": user})
-	salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee)
+
+	employee = frappe.db.get_value("Employee",
+					{
+						"user_id": user
+					},
+					["name", "company", "employee_name"],
+					as_dict=True)
+
+	salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee.name, company=employee.company)
 	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
 
 	if not salary_slip_name:
-		salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee)
-		salary_slip.employee_name = frappe.get_value("Employee",
-			{"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name")
+		salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee.name)
+		salary_slip.employee_name = employee.employee_name
 		salary_slip.payroll_frequency = payroll_frequency
 		salary_slip.posting_date = nowdate()
 		salary_slip.insert()
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index 36387f2..3957d83 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -6,7 +6,7 @@
 import unittest
 import erpnext
 from frappe.utils.make_random import get_random
-from frappe.utils import nowdate, add_days, add_years, getdate, add_months
+from frappe.utils import nowdate, add_years, get_first_day, date_diff
 from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
 from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\
 	make_deduction_salary_component, make_employee_salary_slip, create_tax_slab
@@ -113,36 +113,49 @@
 		sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency='USD')
 		self.assertEqual(sal_struct.currency, 'USD')
 
-def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None,
-	test_tax=False, company=None, currency=erpnext.get_default_currency(), payroll_period=None):
+def make_salary_structure(salary_structure, payroll_frequency, employee=None,
+	from_date=None, dont_submit=False, other_details=None,test_tax=False,
+	company=None, currency=erpnext.get_default_currency(), payroll_period=None):
 	if test_tax:
 		frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
 
-	if not frappe.db.exists('Salary Structure', salary_structure):
-		details = {
-			"doctype": "Salary Structure",
-			"name": salary_structure,
-			"company": company or erpnext.get_default_company(),
-			"earnings": make_earning_salary_component(test_tax=test_tax, company_list=["_Test Company"]),
-			"deductions": make_deduction_salary_component(test_tax=test_tax, company_list=["_Test Company"]),
-			"payroll_frequency": payroll_frequency,
-			"payment_account": get_random("Account", filters={'account_currency': currency}),
-			"currency": currency
-		}
-		if other_details and isinstance(other_details, dict):
-			details.update(other_details)
-		salary_structure_doc = frappe.get_doc(details)
-		salary_structure_doc.insert()
-		if not dont_submit:
-			salary_structure_doc.submit()
+	if frappe.db.exists("Salary Structure", salary_structure):
+		frappe.db.delete("Salary Structure", salary_structure)
 
-	else:
-		salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure)
+	details = {
+		"doctype": "Salary Structure",
+		"name": salary_structure,
+		"company": company or erpnext.get_default_company(),
+		"earnings": make_earning_salary_component(setup=True,  test_tax=test_tax, company_list=["_Test Company"]),
+		"deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
+		"payroll_frequency": payroll_frequency,
+		"payment_account": get_random("Account", filters={'account_currency': currency}),
+		"currency": currency
+	}
+	if other_details and isinstance(other_details, dict):
+		details.update(other_details)
+	salary_structure_doc = frappe.get_doc(details)
+	salary_structure_doc.insert()
+	if not dont_submit:
+		salary_structure_doc.submit()
+
+	filters = {'employee':employee, 'docstatus': 1}
+	if not from_date and payroll_period:
+		from_date = payroll_period.start_date
+
+	if from_date:
+		filters['from_date'] = from_date
 
 	if employee and not frappe.db.get_value("Salary Structure Assignment",
-		{'employee':employee, 'docstatus': 1}) and salary_structure_doc.docstatus==1:
-			create_salary_structure_assignment(employee, salary_structure, company=company, currency=currency,
-			payroll_period=payroll_period)
+		filters) and salary_structure_doc.docstatus==1:
+		create_salary_structure_assignment(
+			employee,
+			salary_structure,
+			from_date=from_date,
+			company=company,
+			currency=currency,
+			payroll_period=payroll_period
+		)
 
 	return salary_structure_doc
 
@@ -165,12 +178,13 @@
 	salary_structure_assignment.base = 50000
 	salary_structure_assignment.variable = 5000
 
-	if getdate(nowdate()).day == 1:
-		date = from_date or nowdate()
-	else:
-		date = from_date or add_days(nowdate(), -1)
+	if not from_date:
+		from_date = get_first_day(nowdate())
+		joining_date = frappe.get_cached_value("Employee", employee, "date_of_joining")
+		if date_diff(joining_date, from_date) > 0:
+			from_date = joining_date
 
-	salary_structure_assignment.from_date = date
+	salary_structure_assignment.from_date = from_date
 	salary_structure_assignment.salary_structure = salary_structure
 	salary_structure_assignment.currency = currency
 	salary_structure_assignment.payroll_payable_account = get_payable_account(company)
@@ -183,4 +197,4 @@
 def get_payable_account(company=None):
 	if not company:
 		company = erpnext.get_default_company()
-	return frappe.db.get_value("Company", company, "default_payroll_payable_account")
\ No newline at end of file
+	return frappe.db.get_value("Company", company, "default_payroll_payable_account")
diff --git a/erpnext/portal/product_configurator/test_product_configurator.py b/erpnext/portal/product_configurator/test_product_configurator.py
index 3521e7e..daaba67 100644
--- a/erpnext/portal/product_configurator/test_product_configurator.py
+++ b/erpnext/portal/product_configurator/test_product_configurator.py
@@ -43,6 +43,30 @@
 				"show_variant_in_website": 1
 			}).insert()
 
+	def create_regular_web_item(self, name, item_group=None):
+		if not frappe.db.exists('Item', name):
+			doc = frappe.get_doc({
+				"description": name,
+				"item_code": name,
+				"item_name": name,
+				"doctype": "Item",
+				"is_stock_item": 1,
+				"item_group": item_group or "_Test Item Group",
+				"stock_uom": "_Test UOM",
+				"item_defaults": [{
+					"company": "_Test Company",
+					"default_warehouse": "_Test Warehouse - _TC",
+					"expense_account": "_Test Account Cost for Goods Sold - _TC",
+					"buying_cost_center": "_Test Cost Center - _TC",
+					"selling_cost_center": "_Test Cost Center - _TC",
+					"income_account": "Sales - _TC"
+				}],
+				"show_in_website": 1
+			}).insert()
+		else:
+			doc = frappe.get_doc("Item", name)
+		return doc
+
 	def test_product_list(self):
 		template_items = frappe.get_all('Item', {'show_in_website': 1})
 		variant_items = frappe.get_all('Item', {'show_variant_in_website': 1})
@@ -79,3 +103,42 @@
 			'Test Size': ['2XL']
 		})
 		self.assertEqual(len(items), 1)
+
+	def test_products_in_multiple_item_groups(self):
+		"""Check if product is visible on multiple item group pages barring its own."""
+		from erpnext.shopping_cart.product_query import ProductQuery
+
+		if not frappe.db.exists("Item Group", {"name": "Tech Items"}):
+			item_group_doc = frappe.get_doc({
+				"doctype": "Item Group",
+				"item_group_name": "Tech Items",
+				"parent_item_group": "All Item Groups",
+				"show_in_website": 1
+			}).insert()
+		else:
+			item_group_doc = frappe.get_doc("Item Group", "Tech Items")
+
+		doc = self.create_regular_web_item("Portal Item", item_group="Tech Items")
+		if not frappe.db.exists("Website Item Group", {"parent": "Portal Item"}):
+			doc.append("website_item_groups", {
+				"item_group": "_Test Item Group Desktops"
+			})
+			doc.save()
+
+		# check if item is visible in its own Item Group's page
+		engine = ProductQuery()
+		items = engine.query({}, {"item_group": "Tech Items"}, None, start=0, item_group="Tech Items")
+		self.assertEqual(len(items), 1)
+		self.assertEqual(items[0].item_code, "Portal Item")
+
+		# check if item is visible in configured foreign Item Group's page
+		engine = ProductQuery()
+		items = engine.query({}, {"item_group": "_Test Item Group Desktops"}, None, start=0, item_group="_Test Item Group Desktops")
+		item_codes = [row.item_code for row in items]
+
+		self.assertIn(len(items), [2, 3])
+		self.assertIn("Portal Item", item_codes)
+
+		# teardown
+		doc.delete()
+		item_group_doc.delete()
\ No newline at end of file
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index d1583f1..39a6024 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -232,7 +232,7 @@
 	meta = frappe.get_meta(doctype)
 	searchfields = meta.get_search_fields()
 	search_columns = ", " + ", ".join(searchfields) if searchfields else ''
-	search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields])
+	search_cond = " or " + " or ".join(field + " like %(txt)s" for field in searchfields)
 
 	return frappe.db.sql(""" select name {search_columns} from `tabProject`
 		where %(key)s like %(txt)s
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index a3e4577..c8bd80f 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -87,8 +87,8 @@
 
 	def set_dates(self):
 		if self.docstatus < 2 and self.time_logs:
-			start_date = min([getdate(d.from_time) for d in self.time_logs])
-			end_date = max([getdate(d.to_time) for d in self.time_logs])
+			start_date = min(getdate(d.from_time) for d in self.time_logs)
+			end_date = max(getdate(d.to_time) for d in self.time_logs)
 
 			if start_date and end_date:
 				self.start_date = getdate(start_date)
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index ea6bdb5..180926f 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 import unittest
 import frappe
-from frappe.utils import getdate, nowdate
+from frappe.utils import getdate, nowdate, add_days
 from erpnext.hr.doctype.employee.test_employee import make_employee
 from erpnext.projects.doctype.timesheet.test_timesheet import make_salary_structure_for_timesheet, make_timesheet
 from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
@@ -16,17 +16,22 @@
 		make_salary_structure_for_timesheet(emp, company='_Test Company')
 		self.timesheet = make_timesheet(emp, simulate = True, is_billable=1)
 		self.salary_slip = make_salary_slip(self.timesheet.name)
+		holidays = self.salary_slip.get_holidays_for_employee(nowdate(), nowdate())
+		if holidays:
+			frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 1)
+
 		self.salary_slip.submit()
 		self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
 		self.sales_invoice.due_date = nowdate()
 		self.sales_invoice.submit()
 
 		frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8)
+		frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 0)
 
 	def test_project_profitability(self):
 		filters = {
 			'company': '_Test Company',
-			'start_date': getdate(),
+			'start_date': add_days(getdate(), -3),
 			'end_date': getdate()
 		}
 
diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py
index 2c7bb49..98dd617 100644
--- a/erpnext/projects/report/project_summary/project_summary.py
+++ b/erpnext/projects/report/project_summary/project_summary.py
@@ -122,7 +122,7 @@
 	if not data:
 		return None
 
-	avg_completion = sum([project.percent_complete for project in data]) / len(data)
+	avg_completion = sum(project.percent_complete for project in data) / len(data)
 	total = sum([project.total_tasks for project in data])
 	total_overdue = sum([project.overdue_tasks for project in data])
 	completed = sum([project.completed_tasks for project in data])
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index ceeecb2..7b997a1 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -156,31 +156,31 @@
 	var d = locals[cdt][cdn];
 	var msg = "";
 
-	if(d.account_head && !d.description) {
+	if (d.account_head && !d.description) {
 		// set description from account head
 		d.description = d.account_head.split(' - ').slice(0, -1).join(' - ');
 	}
 
-	if(!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
+	if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
 		msg = __("Please select Charge Type first");
 		d.row_id = "";
 		d.rate = d.tax_amount = 0.0;
-	} else if((d.charge_type == 'Actual' || d.charge_type == 'On Net Total') && d.row_id) {
+	} else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) {
 		msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'");
 		d.row_id = "";
-	} else if((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
+	} else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
 		if (d.idx == 1) {
 			msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row");
 			d.charge_type = '';
 		} else if (!d.row_id) {
 			msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]);
 			d.row_id = "";
-		} else if(d.row_id && d.row_id >= d.idx) {
+		} else if (d.row_id && d.row_id >= d.idx) {
 			msg = __("Cannot refer row number greater than or equal to current row number for this Charge type");
 			d.row_id = "";
 		}
 	}
-	if(msg) {
+	if (msg) {
 		frappe.validated = false;
 		refresh_field("taxes");
 		frappe.throw(msg);
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index e7dcd41..5c9f5d7 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -122,9 +122,20 @@
 			this.set_from_product_bundle();
 		}
 
+		this.toggle_subcontracting_fields();
 		this._super();
 	},
 
+	toggle_subcontracting_fields: function() {
+		if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) {
+			this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty',
+				'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM');
+
+			this.frm.set_df_property('supplied_items', 'cannot_add_rows', 1);
+			this.frm.set_df_property('supplied_items', 'cannot_delete_rows', 1);
+		}
+	},
+
 	supplier: function() {
 		var me = this;
 		erpnext.utils.get_party_details(this.frm, null, null, function(){
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 2e133be..1de9ec1 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -12,7 +12,7 @@
 		if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
 			effective_item_rate = item.blanket_order_rate;
 		}
-		if(item.margin_type == "Percentage"){
+		if (item.margin_type == "Percentage") {
 			item.rate_with_margin = flt(effective_item_rate)
 				+ flt(effective_item_rate) * ( flt(item.margin_rate_or_amount) / 100);
 		} else {
@@ -22,7 +22,7 @@
 
 		item_rate = flt(item.rate_with_margin , precision("rate", item));
 
-		if(item.discount_percentage){
+		if (item.discount_percentage) {
 			item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
 		}
 
@@ -73,15 +73,18 @@
 	},
 
 	_calculate_taxes_and_totals: function() {
-		this.validate_conversion_rate();
-		this.calculate_item_values();
-		this.initialize_taxes();
-		this.determine_exclusive_rate();
-		this.calculate_net_total();
-		this.calculate_taxes();
-		this.manipulate_grand_total_for_inclusive_tax();
-		this.calculate_totals();
-		this._cleanup();
+		frappe.run_serially([
+			() => this.validate_conversion_rate(),
+			() => this.calculate_item_values(),
+			() => this.update_item_tax_map(),
+			() => this.initialize_taxes(),
+			() => this.determine_exclusive_rate(),
+			() => this.calculate_net_total(),
+			() => this.calculate_taxes(),
+			() => this.manipulate_grand_total_for_inclusive_tax(),
+			() => this.calculate_totals(),
+			() => this._cleanup()
+		]);
 	},
 
 	validate_conversion_rate: function() {
@@ -263,6 +266,66 @@
 		frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
 	},
 
+	update_item_tax_map: function() {
+		let me = this;
+		let item_codes = [];
+		let item_rates = {};
+		let item_tax_templates = {};
+
+		$.each(this.frm.doc.items || [], function(i, item) {
+			if (item.item_code) {
+				// Use combination of name and item code in case same item is added multiple times
+				item_codes.push([item.item_code, item.name]);
+				item_rates[item.name] = item.net_rate;
+				item_tax_templates[item.name] = item.item_tax_template;
+			}
+		});
+
+		if (item_codes.length) {
+			return this.frm.call({
+				method: "erpnext.stock.get_item_details.get_item_tax_info",
+				args: {
+					company: me.frm.doc.company,
+					tax_category: cstr(me.frm.doc.tax_category),
+					item_codes: item_codes,
+					item_rates: item_rates,
+					item_tax_templates: item_tax_templates
+				},
+				callback: function(r) {
+					if (!r.exc) {
+						$.each(me.frm.doc.items || [], function(i, item) {
+							if (item.name && r.message.hasOwnProperty(item.name) && r.message[item.name].item_tax_template) {
+								item.item_tax_template = r.message[item.name].item_tax_template;
+								item.item_tax_rate = r.message[item.name].item_tax_rate;
+								me.add_taxes_from_item_tax_template(item.item_tax_rate);
+							}
+						});
+					}
+				}
+			});
+		}
+	},
+
+	add_taxes_from_item_tax_template: function(item_tax_map) {
+		let me = this;
+
+		if (item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
+			if (typeof (item_tax_map) == "string") {
+				item_tax_map = JSON.parse(item_tax_map);
+			}
+
+			$.each(item_tax_map, function(tax, rate) {
+				let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
+				if (!found) {
+					let child = frappe.model.add_child(me.frm.doc, "taxes");
+					child.charge_type = "On Net Total";
+					child.account_head = tax;
+					child.rate = 0;
+				}
+			});
+		}
+	},
+
 	calculate_taxes: function() {
 		var me = this;
 		this.frm.doc.rounding_adjustment = 0;
@@ -406,6 +469,11 @@
 		let tax_detail = tax.item_wise_tax_detail;
 		let key = item.item_code || item.item_name;
 
+		if(typeof (tax_detail) == "string") {
+			tax.item_wise_tax_detail = JSON.parse(tax.item_wise_tax_detail);
+			tax_detail = tax.item_wise_tax_detail;
+		}
+
 		let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
 		if (tax_detail && tax_detail[key])
 			item_wise_tax_amount += tax_detail[key][1];
@@ -464,7 +532,7 @@
 	},
 
 	calculate_totals: function() {
-		// Changing sequence can cause rounding_adjustmentng issue and on-screen discrepency
+		// Changing sequence can because of rounding adjustment issue and on-screen discrepancy
 		var me = this;
 		var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0;
 		this.frm.doc.grand_total = flt(tax_count
@@ -562,6 +630,8 @@
 				tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
 			});
 		}
+
+		this.frm.refresh_fields();
 	},
 
 	set_discount_amount: function() {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ad1976d..b3af3d6 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -6,6 +6,7 @@
 erpnext.TransactionController = erpnext.taxes_and_totals.extend({
 	setup: function() {
 		this._super();
+		let me = this;
 		frappe.flags.hide_serial_batch_dialog = true;
 		frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
 			var item = frappe.get_doc(cdt, cdn);
@@ -43,8 +44,6 @@
 			cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn);
 		});
 
-
-
 		frappe.ui.form.on(this.frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
 			cur_frm.cscript.calculate_taxes_and_totals();
 		});
@@ -121,7 +120,6 @@
 			}
 		});
 
-		var me = this;
 		if(this.frm.fields_dict["items"].grid.get_field('batch_no')) {
 			this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) {
 				return me.set_query_for_batch(doc, cdt, cdn);
@@ -261,11 +259,19 @@
 		if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
 			return;
 		}
-		var me = this;
-		var inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)
+
+		const me = this;
+		if (!this.frm.is_new() && this.frm.doc.docstatus === 0) {
+			this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
+				me.make_quality_inspection();
+			}, __("Create"));
+			this.frm.page.set_inner_btn_group_as_primary(__('Create'));
+		}
+
+		const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)
 			? "Incoming" : "Outgoing";
 
-		var quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
+		let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
 		quality_inspection_field.get_route_options_for_new_doc = function(row) {
 			if(me.frm.is_new()) return;
 			return {
@@ -280,7 +286,7 @@
 		}
 
 		this.frm.set_query("quality_inspection", "items", function(doc, cdt, cdn) {
-			var d = locals[cdt][cdn];
+			let d = locals[cdt][cdn];
 			return {
 				filters: {
 					docstatus: 1,
@@ -381,7 +387,7 @@
 
 		if(this.frm.doc.scan_barcode) {
 			frappe.call({
-				method: "erpnext.selling.page.point_of_sale.point_of_sale.search_serial_or_batch_or_barcode_number",
+				method: "erpnext.selling.page.point_of_sale.point_of_sale.search_for_serial_or_batch_or_barcode_number",
 				args: { search_value: this.frm.doc.scan_barcode }
 			}).then(r => {
 				const data = r && r.message;
@@ -556,6 +562,7 @@
 							name: me.frm.doc.name,
 							project: item.project || me.frm.doc.project,
 							qty: item.qty || 1,
+							net_rate: item.rate,
 							stock_qty: item.stock_qty,
 							conversion_factor: item.conversion_factor,
 							weight_per_unit: item.weight_per_unit,
@@ -712,30 +719,14 @@
 		});
 	},
 
-	add_taxes_from_item_tax_template: function(item_tax_map) {
-		let me = this;
-
-		if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
-			if(typeof (item_tax_map) == "string") {
-				item_tax_map = JSON.parse(item_tax_map);
-			}
-
-			$.each(item_tax_map, function(tax, rate) {
-				let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
-				if(!found) {
-					let child = frappe.model.add_child(me.frm.doc, "taxes");
-					child.charge_type = "On Net Total";
-					child.account_head = tax;
-					child.rate = 0;
-				}
-			});
-		}
-	},
-
 	serial_no: function(doc, cdt, cdn) {
 		var me = this;
 		var item = frappe.get_doc(cdt, cdn);
 
+		if (item && item.doctype === 'Purchase Receipt Item Supplied') {
+			return;
+		}
+
 		if (item && item.serial_no) {
 			if (!item.item_code) {
 				this.frm.trigger("item_code", cdt, cdn);
@@ -835,9 +826,9 @@
 
 				frappe.run_serially([
 					() => me.frm.script_manager.trigger("currency"),
-					() => me.update_item_tax_map(),
 					() => me.apply_default_taxes(),
-					() => me.apply_pricing_rule()
+					() => me.apply_pricing_rule(),
+					() => me.calculate_taxes_and_totals()
 				]);
 			}
 		}
@@ -877,9 +868,6 @@
 
 		}
 
-		if (this.frm.doc.posting_date) var date = this.frm.doc.posting_date;
-		else var date = this.frm.doc.transaction_date;
-
 		if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
 			in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
 			erpnext.utils.get_shipping_address(this.frm, function(){
@@ -1816,7 +1804,6 @@
 				callback: function(r) {
 					if(!r.exc) {
 						item.item_tax_rate = r.message;
-						me.add_taxes_from_item_tax_template(item.item_tax_rate);
 						me.calculate_taxes_and_totals();
 					}
 				}
@@ -1827,43 +1814,7 @@
 		}
 	},
 
-	update_item_tax_map: function() {
-		var me = this;
-		var item_codes = [];
-		$.each(this.frm.doc.items || [], function(i, item) {
-			if(item.item_code) {
-				item_codes.push(item.item_code);
-			}
-		});
 
-		if(item_codes.length) {
-			return this.frm.call({
-				method: "erpnext.stock.get_item_details.get_item_tax_info",
-				args: {
-					company: me.frm.doc.company,
-					tax_category: cstr(me.frm.doc.tax_category),
-					item_codes: item_codes
-				},
-				callback: function(r) {
-					if(!r.exc) {
-						$.each(me.frm.doc.items || [], function(i, item) {
-							if(item.item_code && r.message.hasOwnProperty(item.item_code)) {
-								if (!item.item_tax_template) {
-									item.item_tax_template = r.message[item.item_code].item_tax_template;
-									item.item_tax_rate = r.message[item.item_code].item_tax_rate;
-								}
-								me.add_taxes_from_item_tax_template(item.item_tax_rate);
-							} else {
-								item.item_tax_template = "";
-								item.item_tax_rate = "{}";
-							}
-						});
-						me.calculate_taxes_and_totals();
-					}
-				}
-			});
-		}
-	},
 
 	is_recurring: function() {
 		// set default values for recurring documents
@@ -1949,6 +1900,130 @@
 		});
 	},
 
+	make_quality_inspection: function () {
+		let data = [];
+		const fields = [
+			{
+				label: "Items",
+				fieldtype: "Table",
+				fieldname: "items",
+				cannot_add_rows: true,
+				in_place_edit: true,
+				data: data,
+				get_data: () => {
+					return data;
+				},
+				fields: [
+					{
+						fieldtype: "Data",
+						fieldname: "docname",
+						hidden: true
+					},
+					{
+						fieldtype: "Read Only",
+						fieldname: "item_code",
+						label: __("Item Code"),
+						in_list_view: true
+					},
+					{
+						fieldtype: "Read Only",
+						fieldname: "item_name",
+						label: __("Item Name"),
+						in_list_view: true
+					},
+					{
+						fieldtype: "Float",
+						fieldname: "qty",
+						label: __("Accepted Quantity"),
+						in_list_view: true,
+						read_only: true
+					},
+					{
+						fieldtype: "Float",
+						fieldname: "sample_size",
+						label: __("Sample Size"),
+						reqd: true,
+						in_list_view: true
+					},
+					{
+						fieldtype: "Data",
+						fieldname: "description",
+						label: __("Description"),
+						hidden: true
+					},
+					{
+						fieldtype: "Data",
+						fieldname: "serial_no",
+						label: __("Serial No"),
+						hidden: true
+					},
+					{
+						fieldtype: "Data",
+						fieldname: "batch_no",
+						label: __("Batch No"),
+						hidden: true
+					}
+				]
+			}
+		];
+
+		const me = this;
+		const dialog = new frappe.ui.Dialog({
+			title: __("Select Items for Quality Inspection"),
+			fields: fields,
+			primary_action: function () {
+				const data = dialog.get_values();
+				frappe.call({
+					method: "erpnext.controllers.stock_controller.make_quality_inspections",
+					args: {
+						doctype: me.frm.doc.doctype,
+						docname: me.frm.doc.name,
+						items: data.items
+					},
+					freeze: true,
+					callback: function (r) {
+						if (r.message.length > 0) {
+							if (r.message.length === 1) {
+								frappe.set_route("Form", "Quality Inspection", r.message[0]);
+							} else {
+								frappe.route_options = {
+									"reference_type": me.frm.doc.doctype,
+									"reference_name": me.frm.doc.name
+								};
+								frappe.set_route("List", "Quality Inspection");
+							}
+						}
+						dialog.hide();
+					}
+				});
+			},
+			primary_action_label: __("Create")
+		});
+
+		this.frm.doc.items.forEach(item => {
+			if (!item.quality_inspection) {
+				let dialog_items = dialog.fields_dict.items;
+				dialog_items.df.data.push({
+					"docname": item.name,
+					"item_code": item.item_code,
+					"item_name": item.item_name,
+					"qty": item.qty,
+					"description": item.description,
+					"serial_no": item.serial_no,
+					"batch_no": item.batch_no
+				});
+				dialog_items.grid.refresh();
+			}
+		});
+
+		data = dialog.fields_dict.items.df.data;
+		if (!data.length) {
+			frappe.msgprint(__("All items in this document already have a linked Quality Inspection."));
+		} else {
+			dialog.show();
+		}
+	},
+
 	get_method_for_payment: function(){
 		var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
 		if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){
diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js
index 32fa4ab..66160a7 100644
--- a/erpnext/public/js/education/lms/quiz.js
+++ b/erpnext/public/js/education/lms/quiz.js
@@ -20,10 +20,8 @@
 	}
 
 	make(data) {
-		if (data.duration) {
-			const timer_display = document.createElement("div");
-			timer_display.classList.add("lms-timer", "float-right", "font-weight-bold");
-			document.getElementsByClassName("lms-title")[0].appendChild(timer_display);
+		if (data.is_time_bound) {
+			$(".lms-timer").removeClass("hide");
 			if (!data.activity || (data.activity && !data.activity.is_complete)) {
 				this.initialiseTimer(data.duration);
 				this.is_time_bound = true;
@@ -118,7 +116,7 @@
 			quiz_response: this.get_selected(),
 			course: this.course,
 			program: this.program,
-			time_taken: this.is_time_bound ? this.time_taken : ""
+			time_taken: this.is_time_bound ? this.time_taken : 0
 		}).then(res => {
 			this.submit_btn.remove()
 			if (!res.message) {
@@ -237,4 +235,4 @@
 		this.options = option_list
 		this.wrapper.appendChild(options_wrapper)
 	}
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js
index 0d656bc..ddf8706 100644
--- a/erpnext/public/js/payment/payments.js
+++ b/erpnext/public/js/payment/payments.js
@@ -8,24 +8,23 @@
 		this.dialog = new frappe.ui.Dialog({
 			title: 'Payment'
 		});
-	
+
 		this.dialog.show();
 		this.$body = this.dialog.body;
 		this.set_payment_primary_action();
 		this.make_keyboard();
-		this.select_text()
+		this.select_text();
 	},
 
-	select_text: function(){
-		var me = this;
-		$(this.$body).find('.form-control').click(function(){
+	select_text() {
+		$(this.$body).find('.form-control').click(function() {
 			$(this).select();
-		})
+		});
 	},
 
-	set_payment_primary_action: function(){
+	set_payment_primary_action: function() {
 		var me = this;
-	
+
 		this.dialog.set_primary_action(__("Submit"), function() {
 			// Allow no ZERO payment
 			$.each(me.frm.doc.payments, function (index, data) {
@@ -38,20 +37,20 @@
 		})
 	},
 
-	make_keyboard: function(){
+	make_keyboard: function() {
 		var me = this;
 		$(this.$body).empty();
 		$(this.$body).html(frappe.render_template('pos_payment', this.frm.doc))
 		this.show_payment_details();
-		this.bind_keyboard_event()
-		this.clear_amount()
+		this.bind_keyboard_event();
+		this.clear_amount();
 	},
 
-	make_multimode_payment: function(){
+	make_multimode_payment: function() {
 		var me = this;
 
-		if(this.frm.doc.change_amount > 0){
-			me.payment_val = me.doc.outstanding_amount
+		if (this.frm.doc.change_amount > 0) {
+			me.payment_val = me.doc.outstanding_amount;
 		}
 
 		this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments");
@@ -59,11 +58,11 @@
 		this.payments.amount = flt(this.payment_val);
 	},
 
-	show_payment_details: function(){
+	show_payment_details: function() {
 		var me = this;
 		var multimode_payments = $(this.$body).find('.multimode-payments').empty();
-		if(this.frm.doc.payments.length){
-			$.each(this.frm.doc.payments, function(index, data){
+		if (this.frm.doc.payments.length) {
+			$.each(this.frm.doc.payments, function(index, data) {
 				$(frappe.render_template('payment_details', {
 					mode_of_payment: data.mode_of_payment,
 					amount: data.amount,
@@ -84,92 +83,90 @@
 		}
 	},
 
-	set_outstanding_amount: function(){
+	set_outstanding_amount: function() {
 		this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx}));
-		this.highlight_selected_row()
-		this.payment_val = 0.0
-		if(this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0){
+		this.highlight_selected_row();
+		this.payment_val = 0.0;
+		if (this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0) {
 			//When user first time click on row
 			this.payment_val = flt(this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, precision("outstanding_amount"))
 			this.selected_mode.val(format_currency(this.payment_val, this.frm.doc.currency));
-			this.update_payment_amount()
-		}else if(flt(this.selected_mode.val()) > 0){
+			this.update_payment_amount();
+		} else if (flt(this.selected_mode.val()) > 0) {
 			//If user click on existing row which has value
 			this.payment_val = flt(this.selected_mode.val());
 		}
 		this.selected_mode.select()
 		this.bind_amount_change_event();
 	},
-	
-	bind_keyboard_event: function(){
-		var me = this;
+
+	bind_keyboard_event() {
 		this.payment_val = '';
 		this.bind_form_control_event();
 		this.bind_numeric_keys_event();
 	},
 
-	bind_form_control_event: function(){
+	bind_form_control_event: function() {
 		var me = this;
-		$(this.$body).find('.pos-payment-row').click(function(){
+		$(this.$body).find('.pos-payment-row').click(function() {
 			me.idx = $(this).attr("idx");
-			me.set_outstanding_amount()
-		})
-		
-		$(this.$body).find('.form-control').click(function(){
+			me.set_outstanding_amount();
+		});
+
+		$(this.$body).find('.form-control').click(function() {
 			me.idx = $(this).attr("idx");
 			me.set_outstanding_amount();
 			me.update_paid_amount(true);
-		})
-		
-		$(this.$body).find('.write_off_amount').change(function(){
+		});
+
+		$(this.$body).find('.write_off_amount').change(function() {
 			me.write_off_amount(flt($(this).val()), precision("write_off_amount"));
-		})
-		
-		$(this.$body).find('.change_amount').change(function(){
+		});
+
+		$(this.$body).find('.change_amount').change(function() {
 			me.change_amount(flt($(this).val()), precision("change_amount"));
-		})
+		});
 	},
 
-	highlight_selected_row: function(){
-		var me = this;
-		var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx}));
-		$(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode')
-		selected_row.addClass('selected-payment-mode')
+	highlight_selected_row() {
+		var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']", {'idx': this.idx}));
+		$(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode');
+		selected_row.addClass('selected-payment-mode');
 		$(this.$body).find('.amount').attr('disabled', true);
 		this.selected_mode.attr('disabled', false);
 	},
-	
-	bind_numeric_keys_event: function(){
+
+	bind_numeric_keys_event: function() {
 		var me = this;
 		$(this.$body).find('.pos-keyboard-key').click(function(){
 			me.payment_val += $(this).text();
-			me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency))
-			me.idx = me.selected_mode.attr("idx")
-			me.update_paid_amount()
-		})
-		
-		$(this.$body).find('.delete-btn').click(function(){
+			me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency));
+			me.idx = me.selected_mode.attr("idx");
+			me.update_paid_amount();
+		});
+
+		$(this.$body).find('.delete-btn').click(function() {
 			me.payment_val =  cstr(flt(me.selected_mode.val())).slice(0, -1);
 			me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency));
-			me.idx = me.selected_mode.attr("idx")
+			me.idx = me.selected_mode.attr("idx");
 			me.update_paid_amount();
 		})
 
 	},
-	
-	bind_amount_change_event: function(){
+
+	bind_amount_change_event() {
 		var me = this;
-		this.selected_mode.change(function(){
+		this.selected_mode.change(function() {
 			me.payment_val =  flt($(this).val()) || 0.0;
-			me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency))
-			me.idx = me.selected_mode.attr("idx")
-			me.update_payment_amount()
-		})
+			me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency));
+			me.idx = me.selected_mode.attr("idx");
+			me.update_payment_amount();
+		});
 	},
 
 	clear_amount: function() {
 		var me = this;
-		$(this.$body).find('.clr').click(function(e){
+		$(this.$body).find('.clr').click(function(e) {
 			e.stopPropagation();
 			me.idx = $(this).attr("idx");
 			me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx}));
@@ -177,50 +174,48 @@
 			me.selected_mode.val(0.0);
 			me.highlight_selected_row();
 			me.update_payment_amount();
-		})
+		});
 	},
 
-	write_off_amount: function(write_off_amount) {
-		var me = this;
-
+	write_off_amount(write_off_amount) {
 		this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount"));
 		this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate,
 			precision("base_write_off_amount"));
-		this.calculate_outstanding_amount(false)
-		this.show_amounts()
+		this.calculate_outstanding_amount(false);
+		this.show_amounts();
 	},
 
 	change_amount: function(change_amount) {
 		var me = this;
 
 		this.frm.doc.change_amount = flt(change_amount, precision("change_amount"));
-		this.calculate_write_off_amount()
-		this.show_amounts()
+		this.calculate_write_off_amount();
+		this.show_amounts();
 	},
 
 	update_paid_amount: function(update_write_off) {
 		var me = this;
-		if(in_list(['change_amount', 'write_off_amount'], this.idx)){
+		if (in_list(['change_amount', 'write_off_amount'], this.idx)) {
 			var value = me.selected_mode.val();
-			if(me.idx == 'change_amount'){
-				me.change_amount(value)
-			} else{
+			if (me.idx == 'change_amount') {
+				me.change_amount(value);
+			} else {
 				if(flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) {
 					value = flt(me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, precision(me.idx));
 				}
-				me.write_off_amount(value)
+				me.write_off_amount(value);
 			}
-		}else{
-			this.update_payment_amount()
+		} else {
+			this.update_payment_amount();
 		}
 	},
 
-	update_payment_amount: function(){
+	update_payment_amount: function() {
 		var me = this;
 
-		$.each(this.frm.doc.payments, function(index, data){
-			if(cint(me.idx) == cint(data.idx)){
-				data.amount = flt(me.selected_mode.val(), 2)
+		$.each(this.frm.doc.payments, function(index, data) {
+			if (cint(me.idx) == cint(data.idx)) {
+				data.amount = flt(me.selected_mode.val(), 2);
 			}
 		})
 
@@ -228,12 +223,12 @@
 		this.show_amounts();
 	},
 
-	show_amounts: function(){
+	show_amounts: function() {
 		var me = this;
 		$(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency));
 		$(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency));
-		$(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency))
-		$(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency))
+		$(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency));
+		$(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency));
 		this.update_invoice();
 	}
 })
\ No newline at end of file
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/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 808dd5a..a79eadc 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -274,9 +274,9 @@
 	return true;
 }
 
-erpnext.utils.get_shipping_address = function(frm, callback){
+erpnext.utils.get_shipping_address = function(frm, callback) {
 	if (frm.doc.company) {
-		if (!(frm.doc.inter_com_order_reference || frm.doc.internal_invoice_reference ||
+		if ((frm.doc.inter_company_order_reference || frm.doc.internal_invoice_reference ||
 			frm.doc.internal_order_reference)) {
 			if (callback) {
 				return callback();
diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss
index 9bdaa8d..c77b2ce 100644
--- a/erpnext/public/scss/point-of-sale.scss
+++ b/erpnext/public/scss/point-of-sale.scss
@@ -806,6 +806,9 @@
 						display: none;
 						float: right;
 						font-weight: 700;
+						white-space: nowrap;
+						overflow: hidden;
+						text-overflow: ellipsis;
 					}
 
 					> .cash-shortcuts {
@@ -829,6 +832,11 @@
 						}
 					}
 				}
+
+				> .loyalty-card {
+					display: flex;
+					flex-direction: column;
+				}
 			}
 		}
 
@@ -1134,4 +1142,4 @@
 			}
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
index 9402cf9..5962859 100644
--- a/erpnext/public/scss/shopping_cart.scss
+++ b/erpnext/public/scss/shopping_cart.scss
@@ -467,11 +467,15 @@
 
 	.btn-change-address {
 		color: var(--blue-500);
-		box-shadow: none;
-		border: 1px solid var(--blue-500);
 	}
 }
 
+.btn-new-address:hover, .btn-change-address:hover {
+	box-shadow: none;
+	color: var(--blue-500) !important;
+	border: 1px solid var(--blue-500);
+}
+
 .modal .address-card {
 	.card-body {
 		padding: var(--padding-sm);
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 843fb01..ea600d9 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -38,11 +38,14 @@
 	einvoicing_eligible_from = frappe.db.get_single_value('E Invoice Settings', 'applicable_from') or '2021-04-01'
 	if getdate(doc.get('posting_date')) < getdate(einvoicing_eligible_from):
 		return False
-	
+
 	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:
@@ -135,7 +138,7 @@
 
 def get_party_details(address_name, is_shipping_address=False):
 	addr = frappe.get_doc('Address', address_name)
-	
+
 	validate_address_fields(addr, is_shipping_address)
 
 	if addr.gst_state_number == 97:
@@ -188,18 +191,14 @@
 
 		item.qty = abs(item.qty)
 
-		if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
-			item.discount_amount = abs(item.base_amount - item.base_net_amount)
-		else:
-			item.discount_amount = 0
-
-		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
-		item.is_service_item = 'N' if frappe.db.get_value('Item', d.item_code, 'is_stock_item') else 'Y'
+		item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N'
 		item.serial_no = ""
 
 		item = update_item_taxes(invoice, item)
@@ -254,18 +253,8 @@
 
 def get_invoice_value_details(invoice):
 	invoice_value_details = frappe._dict(dict())
-
-	if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
-		# Discount already applied on net total which means on items
-		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-		invoice_value_details.invoice_discount_amt = 0
-	elif invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount:
-		invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
-		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-	else:
-		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
-		# since tax already considers discount amount
-		invoice_value_details.invoice_discount_amt = 0
+	invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
+	invoice_value_details.invoice_discount_amt = 0
 
 	invoice_value_details.round_off = invoice.base_rounding_adjustment
 	invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
@@ -287,8 +276,7 @@
 	considered_rows = []
 
 	for t in invoice.taxes:
-		tax_amount = t.base_tax_amount if (invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount) \
-						else t.base_tax_amount_after_discount_amount
+		tax_amount = t.base_tax_amount_after_discount_amount
 		if t.account_head in gst_accounts_list:
 			if t.account_head in gst_accounts.cess_account:
 				# using after discount amt since item also uses after discount amt for cess calc
@@ -995,7 +983,7 @@
 		self.invoice.failure_description = self.get_failure_message(errors) if errors else ""
 		self.update_invoice()
 		frappe.db.commit()
-	
+
 	def get_failure_message(self, errors):
 		if isinstance(errors, list):
 			errors = ', '.join(errors)
@@ -1052,7 +1040,7 @@
 			_('{} e-invoices generated successfully').format(success),
 			title=_('Bulk E-Invoice Generation Complete')
 		)
-			
+
 	else:
 		enqueue_bulk_action(schedule_bulk_generate_irn, docnames=docnames)
 
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 229e0c0..5f9d5ed 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -25,8 +25,12 @@
 	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:
+		return
+
 	# HSN codes
 	with open(os.path.join(os.path.dirname(__file__), 'hsn_code_data.json'), 'r') as f:
 		hsn_codes = json.loads(f.read())
@@ -38,6 +42,9 @@
 		sac_codes = json.loads(f.read())
 	create_hsn_codes(sac_codes, code_field="sac_code")
 
+	if frappe.flags.in_test:
+		frappe.flags.created_hsn_codes = True
+
 def create_hsn_codes(data, code_field):
 	for d in data:
 		hsn_code = frappe.new_doc('GST HSN Code')
@@ -674,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)
@@ -692,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',
@@ -725,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:
@@ -743,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([
@@ -906,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")
@@ -924,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/india/utils.py b/erpnext/regional/india/utils.py
index 075c698..a4466e7 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -817,12 +817,8 @@
 				considered_rows.append(prev_row_id)
 
 	for item in doc.get('items'):
-		if doc.apply_discount_on == 'Grand Total' and doc.discount_amount:
-			proportionate_value = item.base_amount if doc.base_total else item.qty
-			total_value = doc.base_total if doc.base_total else doc.total_qty
-		else:
-			proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
-			total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
+		proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
+		total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
 
 		applicable_charges = flt(flt(proportionate_value * (flt(additional_taxes) / flt(total_value)),
 			item.precision('taxable_value')))
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/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index b7c0962..1096159 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -147,6 +147,13 @@
 	def get_invoice_data(self):
 		self.invoices = frappe._dict()
 		conditions = self.get_conditions()
+
+		company_gstins = get_company_gstin_number(self.filters.get('company'), all_gstins=True)
+
+		self.filters.update({
+			'company_gstins': company_gstins
+		})
+
 		invoice_data = frappe.db.sql("""
 			select
 				{select_columns}
@@ -193,6 +200,9 @@
 
 		elif self.filters.get("type_of_business") ==  "EXPORT":
 			conditions += """ AND is_return !=1 and gst_category = 'Overseas' """
+
+		conditions += " AND IFNULL(billing_address_gstin, '') NOT IN %(company_gstins)s"
+
 		return conditions
 
 	def get_invoice_items(self):
@@ -810,7 +820,8 @@
 
 	return {"num": int(num), "itm_det": itm_det}
 
-def get_company_gstin_number(company, address=None):
+def get_company_gstin_number(company, address=None, all_gstins=False):
+	gstin = ''
 	if address:
 		gstin = frappe.db.get_value("Address", address, "gstin")
 
@@ -822,9 +833,9 @@
 			["Dynamic Link", "parenttype", "=", "Address"],
 		]
 		gstin = frappe.get_all("Address", filters=filters, pluck="gstin")
-		if gstin:
-			gstin[0]
-	
+		if gstin and not all_gstins:
+			gstin = gstin[0]
+
 	if not gstin:
 		address = frappe.bold(address) if address else ""
 		frappe.throw(_("Please set valid GSTIN No. in Company Address {} for company {}").format(
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 51d86ff..818888c 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -75,7 +75,7 @@
 				self.loyalty_program_tier = customer.loyalty_program_tier
 
 		if self.sales_team:
-			if sum([member.allocated_percentage or 0 for member in self.sales_team]) != 100:
+			if sum(member.allocated_percentage or 0 for member in self.sales_team) != 100:
 				frappe.throw(_("Total contribution percentage should be equal to 100"))
 
 	def check_customer_group_change(self):
diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py
index d3281f7..ae3482f 100644
--- a/erpnext/selling/doctype/product_bundle/product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/product_bundle.py
@@ -4,6 +4,8 @@
 from __future__ import unicode_literals
 import frappe
 
+from frappe.utils import get_link_to_form
+
 from frappe import _
 
 from frappe.model.document import Document
@@ -18,6 +20,27 @@
 		from erpnext.utilities.transaction_base import validate_uom_is_integer
 		validate_uom_is_integer(self, "uom", "qty")
 
+	def on_trash(self):
+		linked_doctypes = ["Delivery Note", "Sales Invoice", "POS Invoice", "Purchase Receipt", "Purchase Invoice",
+			"Stock Entry", "Stock Reconciliation", "Sales Order", "Purchase Order", "Material Request"]
+
+		invoice_links = []
+		for doctype in linked_doctypes:
+			item_doctype = doctype + " Item"
+
+			if doctype == "Stock Entry":
+				item_doctype = doctype + " Detail"
+
+			invoices = frappe.db.get_all(item_doctype, {"item_code": self.new_item_code, "docstatus": 1}, ["parent"])
+
+			for invoice in invoices:
+				invoice_links.append(get_link_to_form(doctype, invoice['parent']))
+
+		if len(invoice_links):
+			frappe.throw(
+				"This Product Bundle is linked with {0}. You will have to cancel these documents in order to delete this Product Bundle"
+				.format(", ".join(invoice_links)), title=_("Not Allowed"))
+
 	def validate_main_item(self):
 		"""Validates, main Item is not a stock item"""
 		if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"):
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 246f923..e4f8a47 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -50,7 +50,7 @@
 			self.customer_name = company_name or lead_name
 
 	def update_opportunity(self, status):
-		for opportunity in list(set([d.prevdoc_docname for d in self.get("items")])):
+		for opportunity in set(d.prevdoc_docname for d in self.get("items")):
 			if opportunity:
 				self.update_opportunity_status(status, opportunity)
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index d9e52e1..41f57a3 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -151,7 +151,7 @@
 			frappe.db.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
 
 	def update_prevdoc_status(self, flag=None):
-		for quotation in list(set([d.prevdoc_docname for d in self.get("items")])):
+		for quotation in set(d.prevdoc_docname for d in self.get("items")):
 			if quotation:
 				doc = frappe.get_doc("Quotation", quotation)
 				if doc.docstatus==2:
@@ -233,7 +233,7 @@
 		# Checks Sales Invoice
 		submit_rv = frappe.db.sql_list("""select t1.name
 			from `tabSales Invoice` t1,`tabSales Invoice Item` t2
-			where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""",
+			where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus < 2""",
 			self.name)
 
 		if submit_rv:
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 9873710..974648d 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1217,6 +1217,19 @@
 		# To test if the SO does NOT have a Blanket Order
 		self.assertEqual(so_doc.items[0].blanket_order, None)
 
+	def test_so_cancellation_when_si_drafted(self):
+		"""
+			Test to check if Sales Order gets cancelled if Sales Invoice is in Draft state
+			Expected result: sales order should not get cancelled 
+		"""
+		so = make_sales_order()
+		so.submit()
+		si = make_sales_invoice(so.name)
+		si.save()
+
+		self.assertRaises(frappe.ValidationError, so.cancel)
+
+
 
 def make_sales_order(**args):
 	so = frappe.new_doc("Sales Order")
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index cb811df..296c8c2 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -8,38 +8,52 @@
 from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability
 
-from six import string_types
+def search_by_term(search_term, warehouse, price_list):
+	result = search_for_serial_or_batch_or_barcode_number(search_term) or {}
+
+	item_code = result.get("item_code") or search_term
+	serial_no = result.get("serial_no") or ""
+	batch_no = result.get("batch_no") or ""
+	barcode = result.get("barcode") or ""
+
+	if result:
+		item_info = frappe.db.get_value("Item", item_code,
+			["name as item_code", "item_name", "description", "stock_uom", "image as item_image", "is_stock_item"],
+			as_dict=1)
+
+		item_stock_qty = get_stock_availability(item_code, warehouse)
+		price_list_rate, currency = frappe.db.get_value('Item Price', {
+				'price_list': price_list,
+				'item_code': item_code
+		}, ["price_list_rate", "currency"]) or [None, None]
+
+		item_info.update({
+			'serial_no': serial_no,
+			'batch_no': batch_no,
+			'barcode': barcode,
+			'price_list_rate': price_list_rate,
+			'currency': currency,
+			'actual_qty': item_stock_qty
+		})
+
+		return {'items': [item_info]}
 
 @frappe.whitelist()
-def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
-	data = dict()
+def get_items(start, page_length, price_list, item_group, pos_profile, search_term=""):
+	warehouse, hide_unavailable_items = frappe.db.get_value(
+		'POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
+
 	result = []
 
-	warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
+	if search_term:
+		result = search_by_term(search_term, warehouse, price_list) or []
+		if result:
+			return result
 
 	if not frappe.db.exists('Item Group', item_group):
 		item_group = get_root_of('Item Group')
 
-	if search_value:
-		data = search_serial_or_batch_or_barcode_number(search_value)
-
-	item_code = data.get("item_code") if data.get("item_code") else search_value
-	serial_no = data.get("serial_no") if data.get("serial_no") else ""
-	batch_no = data.get("batch_no") if data.get("batch_no") else ""
-	barcode = data.get("barcode") if data.get("barcode") else ""
-
-	if data:
-		item_info = frappe.db.get_value(
-			"Item", data.get("item_code"),
-			["name as item_code", "item_name", "description", "stock_uom", "image as item_image", "is_stock_item"]
-		, as_dict=1)
-		item_info.setdefault('serial_no', serial_no)
-		item_info.setdefault('batch_no', batch_no)
-		item_info.setdefault('barcode', barcode)
-
-		return { 'items': [item_info] }
-
-	condition = get_conditions(item_code, serial_no, batch_no, barcode)
+	condition = get_conditions(search_term)
 	condition += get_item_group_condition(pos_profile)
 
 	lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
@@ -106,14 +120,10 @@
 			})
 			result.append(row)
 
-	res = {
-		'items': result
-	}
-
-	return res
+	return {'items': result}
 
 @frappe.whitelist()
-def search_serial_or_batch_or_barcode_number(search_value):
+def search_for_serial_or_batch_or_barcode_number(search_value):
 	# search barcode no
 	barcode_data = frappe.db.get_value('Item Barcode', {'barcode': search_value}, ['barcode', 'parent as item_code'], as_dict=True)
 	if barcode_data:
@@ -139,27 +149,21 @@
 	
 	return items
 
-def get_conditions(item_code, serial_no, batch_no, barcode):
-	if serial_no or batch_no or barcode:
-		return "item.name = {0}".format(frappe.db.escape(item_code))
-
-	return make_condition(item_code)
-
-def make_condition(item_code):
+def get_conditions(search_term):
 	condition = "("
-	condition += """item.name like {item_code}
-		or item.item_name like {item_code}""".format(item_code = frappe.db.escape('%' + item_code + '%'))
-	condition += add_search_fields_condition(item_code)
+	condition += """item.name like {search_term}
+		or item.item_name like {search_term}""".format(search_term=frappe.db.escape('%' + search_term + '%'))
+	condition += add_search_fields_condition(search_term)
 	condition += ")"
 
 	return condition
 
-def add_search_fields_condition(item_code):
+def add_search_fields_condition(search_term):
 	condition = ''
 	search_fields = frappe.get_all('POS Search Fields', fields = ['fieldname'])
 	if search_fields:
 		for field in search_fields:
-			condition += " or item.{0} like {1}".format(field['fieldname'], frappe.db.escape('%' + item_code + '%'))
+			condition += " or item.`{0}` like {1}".format(field['fieldname'], frappe.db.escape('%' + search_term + '%'))
 	return condition
 
 def get_item_group_condition(pos_profile):
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index ae3f9e3..c827368 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -241,8 +241,8 @@
 			events: {
 				get_frm: () => this.frm,
 
-				cart_item_clicked: (item_code, batch_no, uom, rate) => {
-					const item_row = this.get_item_from_frm(item_code, batch_no, uom, rate);
+				cart_item_clicked: (item) => {
+					const item_row = this.get_item_from_frm(item);
 					this.item_details.toggle_item_details_section(item_row);
 				},
 
@@ -273,17 +273,15 @@
 					this.cart.toggle_numpad(minimize);
 				},
 
-				form_updated: (cdt, cdn, fieldname, value) => {
-					const item_row = frappe.model.get_doc(cdt, cdn);
-					if (item_row && item_row[fieldname] != value) {
-
-						const { item_code, batch_no, uom, rate } = this.item_details.current_item;
-						const event = {
-							field: fieldname,
+				form_updated: (item, field, value) => {
+					const item_row = frappe.model.get_doc(item.doctype, item.name);
+					if (item_row && item_row[field] != value) {
+						const args = {
+							field,
 							value,
-							item: { item_code, batch_no, uom, rate }
-						}
-						return this.on_cart_update(event)
+							item: this.item_details.current_item
+						};
+						return this.on_cart_update(args);
 					}
 
 					return Promise.resolve();
@@ -300,19 +298,18 @@
 				set_value_in_current_cart_item: (selector, value) => {
 					this.cart.update_selector_value_in_cart_item(selector, value, this.item_details.current_item);
 				},
-				clone_new_batch_item_in_frm: (batch_serial_map, current_item) => {
+				clone_new_batch_item_in_frm: (batch_serial_map, item) => {
 					// called if serial nos are 'auto_selected' and if those serial nos belongs to multiple batches
 					// for each unique batch new item row is added in the form & cart
 					Object.keys(batch_serial_map).forEach(batch => {
-						const { item_code, batch_no } = current_item;
-						const item_to_clone = this.frm.doc.items.find(i => i.item_code === item_code && i.batch_no === batch_no);
+						const item_to_clone = this.frm.doc.items.find(i => i.name == item.name);
 						const new_row = this.frm.add_child("items", { ...item_to_clone });
 						// update new serialno and batch
 						new_row.batch_no = batch;
 						new_row.serial_no = batch_serial_map[batch].join(`\n`);
 						new_row.qty = batch_serial_map[batch].length;
 						this.frm.doc.items.forEach(row => {
-							if (item_code === row.item_code) {
+							if (item.item_code === row.item_code) {
 								this.update_cart_html(row);
 							}
 						});
@@ -321,8 +318,8 @@
 				remove_item_from_cart: () => this.remove_item_from_cart(),
 				get_item_stock_map: () => this.item_stock_map,
 				close_item_details: () => {
-					this.item_details.toggle_item_details_section(undefined);
-					this.cart.prev_action = undefined;
+					this.item_details.toggle_item_details_section(null);
+					this.cart.prev_action = null;
 					this.cart.toggle_item_highlight();
 				},
 				get_available_stock: (item_code, warehouse) => this.get_available_stock(item_code, warehouse)
@@ -506,50 +503,47 @@
 		let item_row = undefined;
 		try {
 			let { field, value, item } = args;
-			const { item_code, batch_no, serial_no, uom, rate } = item;
-			item_row = this.get_item_from_frm(item_code, batch_no, uom, rate);
+			item_row = this.get_item_from_frm(item);
+			const item_row_exists = !$.isEmptyObject(item_row);
 
-			const item_selected_from_selector = field === 'qty' && value === "+1"
+			const from_selector = field === 'qty' && value === "+1";
+			if (from_selector)
+				value = flt(item_row.qty) + flt(value);
 
-			if (item_row) {
-				item_selected_from_selector && (value = item_row.qty + flt(value))
-
-				field === 'qty' && (value = flt(value));
+			if (item_row_exists) {
+				if (field === 'qty')
+					value = flt(value);
 
 				if (['qty', 'conversion_factor'].includes(field) && value > 0 && !this.allow_negative_stock) {
 					const qty_needed = field === 'qty' ? value * item_row.conversion_factor : item_row.qty * value;
 					await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse);
 				}
 
-				if (this.is_current_item_being_edited(item_row) || item_selected_from_selector) {
+				if (this.is_current_item_being_edited(item_row) || from_selector) {
 					await frappe.model.set_value(item_row.doctype, item_row.name, field, value);
 					this.update_cart_html(item_row);
 				}
 
 			} else {
-				if (!this.frm.doc.customer) {
-					frappe.dom.unfreeze();
-					frappe.show_alert({
-						message: __('You must select a customer before adding an item.'),
-						indicator: 'orange'
-					});
-					frappe.utils.play_sound("error");
+				if (!this.frm.doc.customer) 
+					return this.raise_customer_selection_alert();
+
+				const { item_code, batch_no, serial_no, rate } = item;
+
+				if (!item_code)
 					return;
-				}
-				if (!item_code) return;
 
-				item_selected_from_selector && (value = flt(value))
-
-				const args = { item_code, batch_no, rate, [field]: value };
+				const new_item = { item_code, batch_no, rate, [field]: value };
 
 				if (serial_no) {
 					await this.check_serial_no_availablilty(item_code, this.frm.doc.set_warehouse, serial_no);
-					args['serial_no'] = serial_no;
+					new_item['serial_no'] = serial_no;
 				}
 
-				if (field === 'serial_no') args['qty'] = value.split(`\n`).length || 0;
+				if (field === 'serial_no')
+					new_item['qty'] = value.split(`\n`).length || 0;
 
-				item_row = this.frm.add_child('items', args);
+				item_row = this.frm.add_child('items', new_item);
 
 				if (field === 'qty' && value !== 0 && !this.allow_negative_stock)
 					await this.check_stock_availability(item_row, value, this.frm.doc.set_warehouse);
@@ -558,8 +552,11 @@
 				
 				this.update_cart_html(item_row);
 
-				this.item_details.$component.is(':visible') && this.edit_item_details_of(item_row);
-				this.check_serial_batch_selection_needed(item_row) && this.edit_item_details_of(item_row);
+				if (this.item_details.$component.is(':visible'))
+					this.edit_item_details_of(item_row);
+
+				if (this.check_serial_batch_selection_needed(item_row))
+					this.edit_item_details_of(item_row);
 			}
 
 		} catch (error) {
@@ -570,14 +567,33 @@
 		}
 	}
 
-	get_item_from_frm(item_code, batch_no, uom, rate) {
-		const has_batch_no = batch_no;
-		return this.frm.doc.items.find(
-			i => i.item_code === item_code
-				&& (!has_batch_no || (has_batch_no && i.batch_no === batch_no))
-				&& (i.uom === uom)
-				&& (i.rate == rate)
-		);
+	raise_customer_selection_alert() {
+		frappe.dom.unfreeze();
+		frappe.show_alert({
+			message: __('You must select a customer before adding an item.'),
+			indicator: 'orange'
+		});
+		frappe.utils.play_sound("error");
+	}
+
+	get_item_from_frm({ name, item_code, batch_no, uom, rate }) {
+		let item_row = null;
+		if (name) {
+			item_row = this.frm.doc.items.find(i => i.name == name);
+		} else {
+			// if item is clicked twice from item selector
+			// then "item_code, batch_no, uom, rate" will help in getting the exact item
+			// to increase the qty by one
+			const has_batch_no = batch_no;
+			item_row = this.frm.doc.items.find(
+				i => i.item_code === item_code
+					&& (!has_batch_no || (has_batch_no && i.batch_no === batch_no))
+					&& (i.uom === uom)
+					&& (i.rate == rate)
+			);
+		}
+
+		return item_row || {};
 	}
 
 	edit_item_details_of(item_row) {
@@ -585,9 +601,7 @@
 	}
 
 	is_current_item_being_edited(item_row) {
-		const { item_code, batch_no } = this.item_details.current_item;
-
-		return item_code !== item_row.item_code || batch_no != item_row.batch_no ? false : true;
+		return item_row.name == this.item_details.current_item.name;
 	}
 
 	update_cart_html(item_row, remove_item) {
@@ -669,7 +683,7 @@
 
 	update_item_field(value, field_or_action) {
 		if (field_or_action === 'checkout') {
-			this.item_details.toggle_item_details_section(undefined);
+			this.item_details.toggle_item_details_section(null);
 		} else if (field_or_action === 'remove') {
 			this.remove_item_from_cart();
 		} else {
@@ -688,7 +702,7 @@
 			.then(() => {
 				frappe.model.clear_doc(doctype, name);
 				this.update_cart_html(current_item, true);
-				this.item_details.toggle_item_details_section(undefined);
+				this.item_details.toggle_item_details_section(null);
 				frappe.dom.unfreeze();
 			})
 			.catch(e => console.log(e));
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 f5019f5..38508c2 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -181,11 +181,8 @@
 				me.$totals_section.find(".edit-cart-btn").click();
 			}
 
-			const item_code = unescape($cart_item.attr('data-item-code'));
-			const batch_no = unescape($cart_item.attr('data-batch-no'));
-			const uom = unescape($cart_item.attr('data-uom'));
-			const rate = unescape($cart_item.attr('data-rate'));
-			me.events.cart_item_clicked(item_code, batch_no, uom, rate);
+			const item_row_name = unescape($cart_item.attr('data-row-name'));
+			me.events.cart_item_clicked({ name: item_row_name });
 			this.numpad_value = '';
 		});
 
@@ -475,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) {
@@ -505,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);
@@ -521,25 +513,14 @@
 		}
 	}
 
-	get_cart_item({ item_code, batch_no, uom, rate }) {
-		const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
-		const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
-		const uom_attr = `[data-uom="${escape(uom)}"]`;
-		const rate_attr = `[data-rate="${escape(rate)}"]`;
-
-		const item_selector = batch_no ?
-			`.cart-item-wrapper${batch_attr}${uom_attr}${rate_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}${rate_attr}`;
-
+	get_cart_item({ name }) {
+		const item_selector = `.cart-item-wrapper[data-row-name="${escape(name)}"]`;
 		return this.$cart_items_wrapper.find(item_selector);
 	}
 
 	get_item_from_frm(item) {
 		const doc = this.events.get_frm().doc;
-		const { item_code, batch_no, uom, rate } = item;
-		const search_field = batch_no ? 'batch_no' : 'item_code';
-		const search_value = batch_no || item_code;
-
-		return doc.items.find(i => i[search_field] === search_value && i.uom === uom && i.rate === rate);
+		return doc.items.find(i => i.name == item.name);
 	}
 
 	update_item_html(item, remove_item) {
@@ -564,10 +545,7 @@
 
 		if (!$item_to_update.length) {
 			this.$cart_items_wrapper.append(
-				`<div class="cart-item-wrapper"
-						data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
-						data-batch-no="${escape(item_data.batch_no || '')}" data-rate="${escape(item_data.rate)}">
-				</div>
+				`<div class="cart-item-wrapper" data-row-name="${escape(item_data.name)}"></div>
 				<div class="seperator"></div>`
 			)
 			$item_to_update = this.get_cart_item(item_data);
@@ -642,7 +620,7 @@
 
 		function get_item_image_html() {
 			const { image, item_name } = item_data;
-			if (image) {
+			if (!me.hide_images && image) {
 				return `
 					<div class="item-image">
 						<img
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index df62696..6a4d3d5 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -2,6 +2,7 @@
 	constructor({ wrapper, events, settings }) {
 		this.wrapper = wrapper;
 		this.events = events;
+		this.hide_images = settings.hide_images;
 		this.allow_rate_change = settings.allow_rate_change;
 		this.allow_discount_change = settings.allow_discount_change;
 		this.current_item = {};
@@ -54,36 +55,28 @@
 		this.$dicount_section = this.$component.find('.discount-section');
 	}
 
-	has_item_has_changed(item) {
-		const { item_code, batch_no, uom, rate } = this.current_item;
-		const item_code_is_same = item && item_code === item.item_code;
-		const batch_is_same = item && batch_no == item.batch_no;
-		const uom_is_same = item && uom === item.uom;
-		const rate_is_same = item && rate === item.rate;
-		
-		if (!item)
-			return false;
-
-		if (item_code_is_same && batch_is_same && uom_is_same && rate_is_same)
-			return false;
-
-		return true;
+	compare_with_current_item(item) {
+		// returns true if `item` is currently being edited
+		return item && item.name == this.current_item.name;
 	}
 
 	toggle_item_details_section(item) {
-		this.item_has_changed = this.has_item_has_changed(item);
+		const current_item_changed = !this.compare_with_current_item(item);
 
-		this.events.toggle_item_selector(this.item_has_changed);
-		this.toggle_component(this.item_has_changed);
+		// if item is null or highlighted cart item is clicked twice
+		const hide_item_details = !Boolean(item) || !current_item_changed;
+		
+		this.events.toggle_item_selector(!hide_item_details);
+		this.toggle_component(!hide_item_details);
 
-		if (this.item_has_changed) {
+		if (item && current_item_changed) {
 			this.doctype = item.doctype;
 			this.item_meta = frappe.get_meta(this.doctype);
 			this.name = item.name;
 			this.item_row = item;
 			this.currency = this.events.get_frm().doc.currency;
 
-			this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom, rate: item.rate };
+			this.current_item = item;
 
 			this.render_dom(item);
 			this.render_discount_dom(item);
@@ -132,14 +125,25 @@
 		this.$item_name.html(item_name);
 		this.$item_description.html(get_description_html());
 		this.$item_price.html(format_currency(price_list_rate, this.currency));
-		if (image) {
-			this.$item_image.html(`<img src="${image}" alt="${image}">`);
+		if (!this.hide_images && image) {
+			this.$item_image.html(
+				`<img 
+					onerror="cur_pos.item_details.handle_broken_image(this)"
+					class="h-full" src="${image}"
+					alt="${frappe.get_abbr(item_name)}"
+					style="object-fit: cover;">`
+			);
 		} else {
 			this.$item_image.html(`<div class="item-abbr">${frappe.get_abbr(item_name)}</div>`);
 		}
 
 	}
 
+	handle_broken_image($img) {
+		const item_abbr = $($img).attr('alt');
+		$($img).replaceWith(`<div class="item-abbr">${item_abbr}</div>`);
+	}
+
 	render_discount_dom(item) {
 		if (item.discount_percentage) {
 			this.$dicount_section.html(
@@ -169,7 +173,7 @@
 				df: {
 					...field_meta,
 					onchange: function() {
-						me.events.form_updated(me.doctype, me.name, fieldname, this.value);
+						me.events.form_updated(me.current_item, fieldname, this.value);
 					}
 				},
 				parent: this.$form_container.find(`.${fieldname}-control`),
@@ -207,22 +211,17 @@
 	bind_custom_control_change_event() {
 		const me = this;
 		if (this.rate_control) {
-			if (this.allow_rate_change) {
-				this.rate_control.df.onchange = function() {
-					if (this.value || flt(this.value) === 0) {
-						me.events.set_value_in_current_cart_item('rate', this.value);
-						me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
-							const item_row = frappe.get_doc(me.doctype, me.name);
-							const doc = me.events.get_frm().doc;
-							me.$item_price.html(format_currency(item_row.rate, doc.currency));
-							me.render_discount_dom(item_row);
-						});
-						me.current_item.rate = this.value;
-					}
-				};
-			} else {
-				this.rate_control.df.read_only = 1;
-			}
+			this.rate_control.df.onchange = function() {
+				if (this.value || flt(this.value) === 0) {
+					me.events.form_updated(me.current_item, 'rate', this.value).then(() => {
+						const item_row = frappe.get_doc(me.doctype, me.name);
+						const doc = me.events.get_frm().doc;
+						me.$item_price.html(format_currency(item_row.rate, doc.currency));
+						me.render_discount_dom(item_row);
+					});
+				}
+			};
+			this.rate_control.df.read_only = !this.allow_rate_change;
 			this.rate_control.refresh();
 		}
 
@@ -235,7 +234,7 @@
 			this.warehouse_control.df.reqd = 1;
 			this.warehouse_control.df.onchange = function() {
 				if (this.value) {
-					me.events.form_updated(me.doctype, me.name, 'warehouse', this.value).then(() => {
+					me.events.form_updated(me.current_item, 'warehouse', this.value).then(() => {
 						me.item_stock_map = me.events.get_item_stock_map();
 						const available_qty = me.item_stock_map[me.item_row.item_code][this.value];
 						if (available_qty === undefined) {
@@ -267,7 +266,7 @@
 			this.serial_no_control.df.reqd = 1;
 			this.serial_no_control.df.onchange = async function() {
 				!me.current_item.batch_no && await me.auto_update_batch_no();
-				me.events.form_updated(me.doctype, me.name, 'serial_no', this.value);
+				me.events.form_updated(me.current_item, 'serial_no', this.value);
 			}
 			this.serial_no_control.refresh();
 		}
@@ -284,19 +283,12 @@
 					}
 				}
 			};
-			this.batch_no_control.df.onchange = function() {
-				me.events.set_value_in_current_cart_item('batch-no', this.value);
-				me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
-				me.current_item.batch_no = this.value;
-			}
 			this.batch_no_control.refresh();
 		}
 
 		if (this.uom_control) {
 			this.uom_control.df.onchange = function() {
-				me.events.set_value_in_current_cart_item('uom', this.value);
-				me.events.form_updated(me.doctype, me.name, 'uom', this.value);
-				me.current_item.uom = this.value;
+				me.events.form_updated(me.current_item, 'uom', this.value);
 
 				const item_row = frappe.get_doc(me.doctype, me.name);
 				me.conversion_factor_control.df.read_only = (item_row.stock_uom == this.value);
@@ -306,9 +298,9 @@
 
 		frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
 			const field_control = this[`${fieldname}_control`];
-			const item_is_same = !this.has_item_has_changed(item_row);
+			const item_row_is_being_edited = this.compare_with_current_item(item_row);
 
-			if (item_is_same && field_control && field_control.get_value() !== value) {
+			if (item_row_is_being_edited && field_control && field_control.get_value() !== value) {
 				field_control.set_value(value);
 				cur_pos.update_cart_html(item_row);
 			}
@@ -326,7 +318,9 @@
 				fields: ["batch_no", "name"]
 			});
 			const batch_serial_map = serials_with_batch_no.reduce((acc, r) => {
-				acc[r.batch_no] || (acc[r.batch_no] = []);
+				if (!acc[r.batch_no]) {
+					acc[r.batch_no] = [];
+				}
 				acc[r.batch_no] = [...acc[r.batch_no], r.name];
 				return acc;
 			}, {});
@@ -342,12 +336,10 @@
 			if (serial_nos_belongs_to_other_batch) {
 				this.serial_no_control.set_value(batch_serial_nos);
 				this.qty_control.set_value(batch_serial_map[batch_no].length);
-			}
 
-			delete batch_serial_map[batch_no];
-
-			if (serial_nos_belongs_to_other_batch)
+				delete batch_serial_map[batch_no];
 				this.events.clone_new_batch_item_in_frm(batch_serial_map, this.current_item);
+			}
 		}
 	}
 
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 5b48725..dd7f143 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -51,7 +51,7 @@
 		});
 	}
 
-	get_items({start = 0, page_length = 40, search_value=''}) {
+	get_items({start = 0, page_length = 40, search_term=''}) {
 		const doc = this.events.get_frm().doc;
 		const price_list = (doc && doc.selling_price_list) || this.price_list;
 		let { item_group, pos_profile } = this;
@@ -61,7 +61,7 @@
 		return frappe.call({
 			method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
 			freeze: true,
-			args: { start, page_length, price_list, item_group, search_value, pos_profile },
+			args: { start, page_length, price_list, item_group, search_term, pos_profile },
 		});
 	}
 
@@ -80,6 +80,7 @@
 		// eslint-disable-next-line no-unused-vars
 		const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom, price_list_rate } = item;
 		const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
+		const precision = flt(price_list_rate, 2) % 1 != 0 ? 2 : 0;
 
 		let qty_to_display = actual_qty;
 
@@ -121,7 +122,7 @@
 					<div class="item-name">
 						${frappe.ellipsis(item.item_name, 18)}
 					</div>
-					<div class="item-rate">${format_currency(price_list_rate, item.currency, 0) || 0}</div>
+					<div class="item-rate">${format_currency(price_list_rate, item.currency, precision) || 0}</div>
 				</div>
 			</div>`
 		);
@@ -231,7 +232,11 @@
 			uom = uom === "undefined" ? undefined : uom;
 			rate = rate === "undefined" ? undefined : rate;
 
-			me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom, rate }});
+			me.events.item_selected({
+				field: 'qty',
+				value: "+1",
+				item: { item_code, batch_no, serial_no, uom, rate }
+			});
 			me.set_search_value('');
 		});
 
@@ -302,7 +307,7 @@
 			}
 		}
 
-		this.get_items({ search_value: search_term })
+		this.get_items({ search_term })
 			.then(({ message }) => {
 				// eslint-disable-next-line no-unused-vars
 				const { items, serial_no, batch_no, barcode } = message;
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 600f160..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") {
@@ -171,7 +171,7 @@
 
 		this.setup_listener_for_payments();
 
-		this.$payment_modes.on('click', '.shortcut', () => {
+		this.$payment_modes.on('click', '.shortcut', function() {
 			const value = $(this).attr('data-value');
 			me.selected_mode.set_value(value);
 		});
@@ -481,7 +481,7 @@
 		const amount = doc.loyalty_amount > 0 ? format_currency(doc.loyalty_amount, doc.currency) : '';
 		this.$payment_modes.append(
 			`<div class="payment-mode-wrapper">
-				<div class="mode-of-payment" data-mode="loyalty-amount" data-payment-type="loyalty-amount">
+				<div class="mode-of-payment loyalty-card" data-mode="loyalty-amount" data-payment-type="loyalty-amount">
 					Redeem Loyalty Points
 					<div class="loyalty-amount-amount pay-amount">${amount}</div>
 					<div class="loyalty-amount-name">${loyalty_program}</div>
@@ -563,4 +563,4 @@
 	toggle_component(show) {
 		show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
 	}
-};
\ No newline at end of file
+};
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index f5feb95..8cb2446 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -59,7 +59,7 @@
 			IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay,
 			soi.qty, soi.delivered_qty,
 			(soi.qty - soi.delivered_qty) AS pending_qty,
-			IFNULL(sii.qty, 0) as billed_qty,
+			IFNULL(SUM(sii.qty), 0) as billed_qty,
 			soi.base_amount as amount,
 			(soi.delivered_qty * soi.base_rate) as delivered_qty_amount,
 			(soi.billed_amt * IFNULL(so.conversion_rate, 1)) as billed_amount,
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index b24048d..d05541b 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -269,7 +269,7 @@
 		["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
 		["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
 		["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
-		["unrealized_profit_loss_account", {"root_type": "Liability"},]
+		["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}]
 	], function(i, v) {
 		erpnext.company.set_custom_query(frm, v);
 	});
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 077538d..36a7d20 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -54,7 +54,7 @@
 
 	def validate_abbr(self):
 		if not self.abbr:
-			self.abbr = ''.join([c[0] for c in self.company_name.split()]).upper()
+			self.abbr = ''.join(c[0] for c in self.company_name.split()).upper()
 
 		self.abbr = self.abbr.strip()
 
@@ -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}):
@@ -335,7 +335,7 @@
 		clear_defaults_cache()
 
 	def abbreviate(self):
-		self.abbr = ''.join([c[0].upper() for c in self.company_name.split()])
+		self.abbr = ''.join(c[0].upper() for c in self.company_name.split())
 
 	def on_trash(self):
 		"""
@@ -395,7 +395,7 @@
 
 @frappe.whitelist()
 def enqueue_replace_abbr(company, old, new):
-	kwargs = dict(company=company, old=old, new=new)
+	kwargs = dict(queue="long", company=company, old=old, new=new)
 	frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs)
 
 
@@ -407,8 +407,6 @@
 
 	frappe.only_for("System Manager")
 
-	frappe.db.set_value("Company", company, "abbr", new)
-
 	def _rename_record(doc):
 		parts = doc[0].rsplit(" - ", 1)
 		if len(parts) == 1 or parts[1].lower() == old.lower():
@@ -419,11 +417,18 @@
 		doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company))
 		for d in doc:
 			_rename_record(d)
+	try:
+		frappe.db.auto_commit_on_many_writes = 1
+		frappe.db.set_value("Company", company, "abbr", new)
+		for dt in ["Warehouse", "Account", "Cost Center", "Department",
+				"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
+			_rename_records(dt)
+			frappe.db.commit()
 
-	for dt in ["Warehouse", "Account", "Cost Center", "Department",
-			"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
-		_rename_records(dt)
-		frappe.db.commit()
+	except Exception:
+		frappe.log_error(title=_('Abbreviation Rename Error'))
+	finally:
+		frappe.db.auto_commit_on_many_writes = 0
 
 
 def get_name_with_abbr(name, company):
@@ -435,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/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index bff806d..1c72ceb 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -91,7 +91,7 @@
 		field_filters['item_group'] = self.name
 
 		engine = ProductQuery()
-		context.items = engine.query(attribute_filters, field_filters, search, start)
+		context.items = engine.query(attribute_filters, field_filters, search, start, item_group=self.name)
 
 		filter_engine = ProductFiltersBuilder(self.name)
 
@@ -139,7 +139,7 @@
 			# return child item groups if the type is of "Is Group"
 			return get_child_groups_for_list_in_html(item_group, start, limit, search)
 
-	child_groups = ", ".join([frappe.db.escape(i[0]) for i in get_child_groups(product_group)])
+	child_groups = ", ".join(frappe.db.escape(i[0]) for i in get_child_groups(product_group))
 
 	# base query
 	query = """select I.name, I.item_name, I.item_code, I.route, I.image, I.website_image, I.thumbnail, I.item_group,
@@ -239,7 +239,7 @@
 	return frappe.get_template(products_template).render(context)
 
 def get_group_item_count(item_group):
-	child_groups = ", ".join(['"' + i[0] + '"' for i in get_child_groups(item_group)])
+	child_groups = ", ".join('"' + i[0] + '"' for i in get_child_groups(item_group))
 	return frappe.db.sql("""select count(*) from `tabItem`
 		where docstatus = 0 and show_in_website = 1
 		and (item_group in (%s)
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index 38f8de7..ece9fb5 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -12,10 +12,6 @@
 class TransactionDeletionRecord(Document):
 	def validate(self):
 		frappe.only_for('System Manager')
-		company_obj = frappe.get_doc('Company', self.company)
-		if frappe.session.user != company_obj.owner and frappe.session.user != 'Administrator':
-			frappe.throw(_('Transactions can only be deleted by the creator of the Company or the Administrator.'), 
-				frappe.PermissionError)
 		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
 		for doctype in self.doctypes_to_be_ignored:
 			if doctype.doctype_name not in doctypes_to_be_ignored_list:
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 5876488..34af093 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -481,37 +481,42 @@
 	},
 
 	"Germany": {
+		"tax_categories": [
+			"Umsatzsteuer",
+			"Vorsteuer"
+		],
 		"chart_of_accounts": {
 			"SKR04 mit Kontonummern": {
 				"sales_tax_templates": [
 					{
-						"title": "Umsatzsteuer 19%",
+						"title": "Umsatzsteuer",
+						"tax_category": "Umsatzsteuer",
+						"is_default": 1,
 						"taxes": [
 							{
 								"account_head": {
 									"account_name": "Umsatzsteuer 19%",
 									"account_number": "3806",
 									"tax_rate": 19.00
-								}
-							}
-						]
-					},
-					{
-						"title": "Umsatzsteuer 7%",
-						"taxes": [
+								},
+								"rate": 0.00
+							},
 							{
 								"account_head": {
 									"account_name": "Umsatzsteuer 7%",
 									"account_number": "3801",
 									"tax_rate": 7.00
-								}
+								},
+								"rate": 0.00
 							}
 						]
 					}
 				],
 				"purchase_tax_templates": [
 					{
-						"title": "Abziehbare Vorsteuer 19%",
+						"title": "Vorsteuer",
+						"tax_category": "Vorsteuer",
+						"is_default": 1,
 						"taxes": [
 							{
 								"account_head": {
@@ -519,20 +524,17 @@
 									"account_number": "1406",
 									"root_type": "Asset",
 									"tax_rate": 19.00
-								}
-							}
-						]
-					},
-					{
-						"title": "Abziehbare Vorsteuer 7%",
-						"taxes": [
+								},
+								"rate": 0.00
+							},
 							{
 								"account_head": {
 									"account_name": "Abziehbare Vorsteuer 7%",
 									"account_number": "1401",
 									"root_type": "Asset",
 									"tax_rate": 7.00
-								}
+								},
+								"rate": 0.00
 							}
 						]
 					},
@@ -559,19 +561,26 @@
 							}
 						]
 					}
-				]
-			},
-			"SKR03 mit Kontonummern": {
-				"sales_tax_templates": [
+				],
+				"item_tax_templates": [
 					{
 						"title": "Umsatzsteuer 19%",
 						"taxes": [
 							{
-								"account_head": {
+								"tax_type": {
 									"account_name": "Umsatzsteuer 19%",
-									"account_number": "1776",
+									"account_number": "3806",
 									"tax_rate": 19.00
-								}
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"account_number": "3801",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
 							}
 						]
 					},
@@ -579,18 +588,102 @@
 						"title": "Umsatzsteuer 7%",
 						"taxes": [
 							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"account_number": "3806",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"account_number": "3801",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 19%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"account_number": "1406",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"account_number": "1401",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 7%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"account_number": "1406",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"account_number": "1401",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
+							}
+						]
+					}
+				]
+			},
+			"SKR03 mit Kontonummern": {
+				"sales_tax_templates": [
+					{
+						"title": "Umsatzsteuer",
+						"tax_category": "Umsatzsteuer",
+						"is_default": 1,
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Umsatzsteuer 19%",
+									"account_number": "1776",
+									"tax_rate": 19.00
+								},
+								"rate": 0.00
+							},
+							{
 								"account_head": {
 									"account_name": "Umsatzsteuer 7%",
 									"account_number": "1771",
 									"tax_rate": 7.00
-								}
+								},
+								"rate": 0.00
 							}
 						]
 					}
 				],
 				"purchase_tax_templates": [
 					{
-						"title": "Abziehbare Vorsteuer 19%",
+						"title": "Vorsteuer",
+						"tax_category": "Vorsteuer",
+						"is_default": 1,
 						"taxes": [
 							{
 								"account_head": {
@@ -598,20 +691,107 @@
 									"account_number": "1576",
 									"root_type": "Asset",
 									"tax_rate": 19.00
-								}
-							}
-						]
-					},
-					{
-						"title": "Abziehbare Vorsteuer 7%",
-						"taxes": [
+								},
+								"rate": 0.00
+							},
 							{
 								"account_head": {
 									"account_name": "Abziehbare Vorsteuer 7%",
 									"account_number": "1571",
 									"root_type": "Asset",
 									"tax_rate": 7.00
-								}
+								},
+								"rate": 0.00
+							}
+						]
+					}
+				],
+				"item_tax_templates": [
+					{
+						"title": "Umsatzsteuer 19%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"account_number": "1776",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"account_number": "1771",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
+							}
+						]
+					},
+					{
+						"title": "Umsatzsteuer 7%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"account_number": "1776",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"account_number": "1771",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 19%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"account_number": "1576",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"account_number": "1571",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 7%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"account_number": "1576",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"account_number": "1571",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
 							}
 						]
 					}
@@ -620,33 +800,34 @@
 			"Standard with Numbers": {
 				"sales_tax_templates": [
 					{
-						"title": "Umsatzsteuer 19%",
+						"title": "Umsatzsteuer",
+						"tax_category": "Umsatzsteuer",
+						"is_default": 1,
 						"taxes": [
 							{
 								"account_head": {
 									"account_name": "Umsatzsteuer 19%",
 									"account_number": "2301",
 									"tax_rate": 19.00
-								}
-							}
-						]
-					},
-					{
-						"title": "Umsatzsteuer 7%",
-						"taxes": [
+								},
+								"rate": 0.00
+							},
 							{
 								"account_head": {
 									"account_name": "Umsatzsteuer 7%",
 									"account_number": "2302",
 									"tax_rate": 7.00
-								}
+								},
+								"rate": 0.00
 							}
 						]
 					}
 				],
 				"purchase_tax_templates": [
 					{
-						"title": "Abziehbare Vorsteuer 19%",
+						"title": "Vorsteuer",
+						"tax_category": "Vorsteuer",
+						"is_default": 1,
 						"taxes": [
 							{
 								"account_head": {
@@ -654,20 +835,107 @@
 									"account_number": "1501",
 									"root_type": "Asset",
 									"tax_rate": 19.00
-								}
-							}
-						]
-					},
-					{
-						"title": "Abziehbare Vorsteuer 7%",
-						"taxes": [
+								},
+								"rate": 0.00
+							},
 							{
 								"account_head": {
 									"account_name": "Abziehbare Vorsteuer 7%",
 									"account_number": "1502",
 									"root_type": "Asset",
 									"tax_rate": 7.00
-								}
+								},
+								"rate": 0.00
+							}
+						]
+					}
+				],
+				"item_tax_templates": [
+					{
+						"title": "Umsatzsteuer 19%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"account_number": "2301",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"account_number": "2302",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
+							}
+						]
+					},
+					{
+						"title": "Umsatzsteuer 7%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"account_number": "2301",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"account_number": "2302",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 19%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"account_number": "1501",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"account_number": "1502",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 7%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"account_number": "1501",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"account_number": "1502",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
 							}
 						]
 					}
@@ -676,13 +944,69 @@
 			"*": {
 				"sales_tax_templates": [
 					{
-						"title": "Umsatzsteuer 19%",
+						"title": "Umsatzsteuer",
+						"tax_category": "Umsatzsteuer",
+						"is_default": 1,
 						"taxes": [
 							{
 								"account_head": {
 									"account_name": "Umsatzsteuer 19%",
 									"tax_rate": 19.00
-								}
+								},
+								"rate": 0.00
+							},
+							{
+								"account_head": {
+									"account_name": "Umsatzsteuer 7%",
+									"tax_rate": 7.00
+								},
+								"rate": 0.00
+							}
+						]
+					}
+				],
+				"purchase_tax_templates": [
+					{
+						"title": "Vorsteuer 19%",
+						"tax_category": "Vorsteuer",
+						"is_default": 1,
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"tax_rate": 19.00,
+									"root_type": "Asset"
+								},
+								"rate": 0.00
+							},
+							{
+								"account_head": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"rate": 0.00
+							}
+						]
+					}
+				],
+				"item_tax_templates": [
+					{
+						"title": "Umsatzsteuer 19%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Umsatzsteuer 7%",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 0.00
 							}
 						]
 					},
@@ -690,36 +1014,60 @@
 						"title": "Umsatzsteuer 7%",
 						"taxes": [
 							{
-								"account_head": {
+								"tax_type": {
+									"account_name": "Umsatzsteuer 19%",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
 									"account_name": "Umsatzsteuer 7%",
 									"tax_rate": 7.00
-								}
-							}
-						]
-					}
-				],
-				"purchase_tax_templates": [
-					{
-						"title": "Abziehbare Vorsteuer 19%",
-						"taxes": [
-							{
-								"account_head": {
-									"account_name": "Abziehbare Vorsteuer 19%",
-									"tax_rate": 19.00,
-									"root_type": "Asset"
-								}
+								},
+								"tax_rate": 7.00
 							}
 						]
 					},
 					{
-						"title": "Abziehbare Vorsteuer 7%",
+						"title": "Vorsteuer 19%",
 						"taxes": [
 							{
-								"account_head": {
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 19.00
+							},
+							{
+								"tax_type": {
 									"account_name": "Abziehbare Vorsteuer 7%",
 									"root_type": "Asset",
 									"tax_rate": 7.00
-								}
+								},
+								"tax_rate": 0.00
+							}
+						]
+					},
+					{
+						"title": "Vorsteuer 7%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 19%",
+									"root_type": "Asset",
+									"tax_rate": 19.00
+								},
+								"tax_rate": 0.00
+							},
+							{
+								"tax_type": {
+									"account_name": "Abziehbare Vorsteuer 7%",
+									"root_type": "Asset",
+									"tax_rate": 7.00
+								},
+								"tax_rate": 7.00
 							}
 						]
 					}
@@ -816,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"
 								}
 							}
 						]
@@ -881,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": [
 							{
@@ -1001,7 +1689,7 @@
 		"Italy VAT 4%":{
 			"account_name": "IVA 4%",
 			"tax_rate": 4.00
-		}		
+		}
 	},
 
 	"Ivory Coast": {
@@ -1519,7 +2207,7 @@
 	"South Africa": {
 		"South Africa Tax": {
 			"account_name": "VAT",
-			"tax_rate": 14.00
+			"tax_rate": 15.00
 		}
 	},
 
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 5c725d3..3dcb638 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -449,6 +449,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):
@@ -480,17 +482,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,
@@ -499,10 +501,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 5019837..c7cc000 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -11,6 +11,9 @@
 
 
 def setup_taxes_and_charges(company_name: str, country: str):
+	if not frappe.db.exists('Company', company_name):
+		frappe.throw(_('Company {} does not exist yet. Taxes setup aborted.').format(company_name))
+
 	file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'country_wise_tax.json')
 	with open(file_path, 'r') as json_file:
 		tax_data = json.load(json_file)
@@ -23,7 +26,8 @@
 	if 'chart_of_accounts' not in country_wise_tax:
 		country_wise_tax = simple_to_detailed(country_wise_tax)
 
-	from_detailed_data(company_name, country_wise_tax.get('chart_of_accounts'))
+	from_detailed_data(company_name, country_wise_tax)
+	update_regional_tax_settings(country, company_name)
 
 
 def simple_to_detailed(templates):
@@ -74,10 +78,16 @@
 def from_detailed_data(company_name, data):
 	"""Create Taxes and Charges Templates from detailed data."""
 	coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts')
-	tax_templates = data.get(coa_name) or data.get('*')
-	sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*')
-	purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*')
-	item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*')
+	coa_data = data.get('chart_of_accounts', {})
+	tax_templates = coa_data.get(coa_name) or coa_data.get('*', {})
+	tax_categories = data.get('tax_categories')
+	sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*', {})
+	purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*', {})
+	item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*', {})
+
+	if tax_categories:
+		for tax_category in tax_categories:
+			make_tax_catgory(tax_category)
 
 	if sales_tax_templates:
 		for template in sales_tax_templates:
@@ -92,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
@@ -121,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.
@@ -147,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):
 	"""
@@ -166,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)
@@ -182,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'
@@ -228,8 +273,23 @@
 		'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
 
 	return tax_group_name
+
+
+def make_tax_catgory(tax_category):
+	doctype = 'Tax Category'
+	if isinstance(tax_category, str):
+		tax_category = {'title': tax_category}
+
+	tax_category['doctype'] = doctype
+	if not frappe.db.exists(doctype, tax_category['title']):
+		doc = frappe.get_doc(tax_category)
+		doc.insert(ignore_permissions=True)
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/filters.py b/erpnext/shopping_cart/filters.py
index 6c63d87..7dfa09e 100644
--- a/erpnext/shopping_cart/filters.py
+++ b/erpnext/shopping_cart/filters.py
@@ -22,12 +22,15 @@
 
 		filter_data = []
 		for df in fields:
-			filters = {}
+			filters, or_filters = {}, []
 			if df.fieldtype == "Link":
 				if self.item_group:
-					filters['item_group'] = self.item_group
+					or_filters.extend([
+						["item_group", "=", self.item_group],
+						["Website Item Group", "item_group", "=", self.item_group]
+					])
 
-				values =  frappe.get_all("Item", fields=[df.fieldname], filters=filters, distinct="True", pluck=df.fieldname)
+				values = frappe.get_all("Item", fields=[df.fieldname], filters=filters, or_filters=or_filters, distinct="True", pluck=df.fieldname)
 			else:
 				doctype = df.get_link_doctype()
 
@@ -44,7 +47,9 @@
 				values = [d.name for d in frappe.get_all(doctype, filters)]
 
 			# Remove None
-			values = values.remove(None) if None in values else values
+			if None in values:
+				values.remove(None)
+
 			if values:
 				filter_data.append([df, values])
 
@@ -61,14 +66,18 @@
 		for attr_doc in attribute_docs:
 			selected_attributes = []
 			for attr in attr_doc.item_attribute_values:
+				or_filters = []
 				filters= [
 					["Item Variant Attribute", "attribute", "=", attr.parent],
 					["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
 				]
 				if self.item_group:
-					filters.append(["item_group", "=", self.item_group])
+					or_filters.extend([
+						["item_group", "=", self.item_group],
+						["Website Item Group", "item_group", "=", self.item_group]
+					])
 
-				if frappe.db.get_all("Item", filters, limit=1):
+				if frappe.db.get_all("Item", filters, or_filters=or_filters, limit=1):
 					selected_attributes.append(attr)
 
 			if selected_attributes:
diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py
index 36d446e..6c92d96 100644
--- a/erpnext/shopping_cart/product_query.py
+++ b/erpnext/shopping_cart/product_query.py
@@ -22,13 +22,14 @@
 		self.settings = frappe.get_doc("Products Settings")
 		self.cart_settings = frappe.get_doc("Shopping Cart Settings")
 		self.page_length = self.settings.products_per_page or 20
-		self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route']
+		self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants',
+			'item_group', 'image', 'web_long_description', 'description', 'route', 'weightage']
 		self.filters = []
 		self.or_filters = [['show_in_website', '=', 1]]
 		if not self.settings.get('hide_variants'):
 			self.or_filters.append(['show_variant_in_website', '=', 1])
 
-	def query(self, attributes=None, fields=None, search_term=None, start=0):
+	def query(self, attributes=None, fields=None, search_term=None, start=0, item_group=None):
 		"""Summary
 
 		Args:
@@ -44,6 +45,15 @@
 		if search_term: self.build_search_filters(search_term)
 
 		result = []
+		website_item_groups = []
+
+		# if from item group page consider website item group table
+		if item_group:
+			website_item_groups = frappe.db.get_all(
+				"Item",
+				fields=self.fields + ["`tabWebsite Item Group`.parent as wig_parent"],
+				filters=[["Website Item Group", "item_group", "=", item_group]]
+			)
 
 		if attributes:
 			all_items = []
@@ -61,22 +71,39 @@
 					],
 					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}
-				# TODO: Replace Variants by their parent templates
 
 				all_items.append(set(items_dict.keys()))
 
 			result = [items_dict.get(item) for item in list(set.intersection(*all_items))]
 		else:
-			result = frappe.get_all("Item", fields=self.fields, filters=self.filters, or_filters=self.or_filters, start=start, limit=self.page_length)
+			result = frappe.get_all(
+				"Item",
+				fields=self.fields,
+				filters=self.filters,
+				or_filters=self.or_filters,
+				start=start,
+				limit=self.page_length,
+				order_by="weightage desc"
+			)
+
+		# Combine results having context of website item groups into item results
+		if item_group and website_item_groups:
+			items_list = {row.name for row in result}
+			for row in website_item_groups:
+				if row.wig_parent not in items_list:
+					result.append(row)
+
+		result = sorted(result, key=lambda x: x.get("weightage"), reverse=True)
 
 		for item in result:
 			product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info')
 			if product_info:
-				item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None
+				item.formatted_price = (product_info.get('price') or {}).get('formatted_price')
 
 		return result
 
@@ -90,7 +117,16 @@
 			if not values:
 				continue
 
-			if isinstance(values, list):
+			# handle multiselect fields in filter addition
+			meta = frappe.get_meta('Item', cached=True)
+			df = meta.get_field(field)
+			if df.fieldtype == 'Table MultiSelect':
+				child_doctype = df.options
+				child_meta = frappe.get_meta(child_doctype, cached=True)
+				fields = child_meta.get("fields")
+				if fields:
+					self.filters.append([child_doctype, fields[0].fieldname, 'IN', values])
+			elif isinstance(values, list):
 				# If value is a list use `IN` query
 				self.filters.append([field, 'IN', values])
 			else:
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index 943cb34..fc4cf1d 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "allow_import": 1,
  "autoname": "field:batch_id",
  "creation": "2013-03-05 14:50:38",
@@ -25,7 +26,11 @@
   "reference_doctype",
   "reference_name",
   "section_break_7",
-  "description"
+  "description",
+  "manufacturing_section",
+  "qty_to_produce",
+  "column_break_23",
+  "produced_qty"
  ],
  "fields": [
   {
@@ -160,13 +165,35 @@
    "label": "Batch UOM",
    "options": "UOM",
    "read_only": 1
+  },
+  {
+   "fieldname": "manufacturing_section",
+   "fieldtype": "Section Break",
+   "label": "Manufacturing"
+  },
+  {
+   "fieldname": "qty_to_produce",
+   "fieldtype": "Float",
+   "label": "Qty To Produce",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_23",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "produced_qty",
+   "fieldtype": "Float",
+   "label": "Produced Qty",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-archive",
  "idx": 1,
  "image_field": "image",
+ "links": [],
  "max_attachments": 5,
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-07-08 16:22:01.343105",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Batch",
@@ -190,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 8fdda56..b6eef6c 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -226,13 +226,12 @@
 	return batch.name
 
 
-def set_batch_nos(doc, warehouse_field, throw=False):
+def set_batch_nos(doc, warehouse_field, throw=False, child_table="items"):
 	"""Automatically select `batch_no` for outgoing items in item table"""
-	for d in doc.items:
+	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:
@@ -304,8 +303,13 @@
 		frappe.throw(_("The serial no {0} does not belong to item {1}")
 			.format(get_link_to_form("Serial No", serial_nos[0]), get_link_to_form("Item", item_code)))
 
-	serial_no_link = ','.join([get_link_to_form("Serial No", sn) for sn in serial_nos])
+	serial_no_link = ','.join(get_link_to_form("Serial No", sn) for sn in serial_nos)
 
 	message = "Serial Nos" if len(serial_nos) > 1 else "Serial No"
 	frappe.throw(_("There is no batch found against the {0}: {1}")
-		.format(message, serial_no_link))
\ No newline at end of file
+		.format(message, serial_no_link))
+
+def make_batch(args):
+	if frappe.db.get_value("Item", args.item, "has_batch_no"):
+		args.doctype = "Batch"
+		frappe.get_doc(args).insert().name
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 0514bd2..4364201 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -54,7 +54,7 @@
 		self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
 		self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
 		self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-		
+
 		self.set_projected_qty()
 		self.db_update()
 
@@ -115,7 +115,7 @@
 		#Get Transferred Entries
 		materials_transferred = frappe.db.sql("""
 			select
-				ifnull(sum(transfer_qty),0)
+				ifnull(sum(CASE WHEN se.is_return = 1 THEN (transfer_qty * -1) ELSE transfer_qty END),0)
 			from
 				`tabStock Entry` se, `tabStock Entry Detail` sed, `tabPurchase Order` po
 			where
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 7875b9c..74cb3fc 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -78,6 +78,9 @@
 		});
 
 		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+
+		frm.set_df_property('packed_items', 'cannot_add_rows', true);
+		frm.set_df_property('packed_items', 'cannot_delete_rows', true);
 	},
 
 	print_without_amount: function(frm) {
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 280fde1..f20e76f 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -554,8 +554,7 @@
    "oldfieldname": "packing_details",
    "oldfieldtype": "Table",
    "options": "Packed Item",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "product_bundle_help",
@@ -1289,7 +1288,7 @@
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-15 23:55:49.620641",
+ "modified": "2021-06-11 19:27:30.901112",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index cce51cb..4808e94 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -129,12 +129,13 @@
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_with_previous_doc()
 
-		if self._action != 'submit' and not self.is_return:
-			set_batch_nos(self, 'warehouse', True)
-
 		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 		make_packing_list(self)
 
+		if self._action != 'submit' and not self.is_return:
+			set_batch_nos(self, 'warehouse', throw=True)
+			set_batch_nos(self, 'warehouse', throw=True, child_table="packed_items")
+
 		self.update_current_stock()
 
 		if not self.installation_status: self.installation_status = 'Not Installed'
@@ -181,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):
@@ -264,7 +264,7 @@
 		"""
 			Validate that if packed qty exists, it should be equal to qty
 		"""
-		if not any([flt(d.get('packed_qty')) for d in self.get("items")]):
+		if not any(flt(d.get('packed_qty')) for d in self.get("items")):
 			return
 		has_error = False
 		for d in self.get("items"):
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js
index f08125b..0402898 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js
@@ -6,8 +6,8 @@
 			return [__("Return"), "gray", "is_return,=,Yes"];
 		} else if (doc.status === "Closed") {
 			return [__("Closed"), "green", "status,=,Closed"];
-		} else if (flt(doc.per_returned, 2) === 100) {
-			return [__("Return Issued"), "grey", "per_returned,=,100"];
+		} else if (doc.status === "Return Issued") {
+			return [__("Return Issued"), "grey", "status,=,Return Issued"];
 		} else if (flt(doc.per_billed, 2) < 100) {
 			return [__("To Bill"), "orange", "per_billed,<,100"];
 		} else if (flt(doc.per_billed, 2) === 100) {
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 0c63df0..f981aeb 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -7,7 +7,7 @@
 import frappe
 import json
 import frappe.defaults
-from frappe.utils import cint, nowdate, nowtime, cstr, add_days, flt, today
+from frappe.utils import nowdate, nowtime, cstr, flt
 from erpnext.stock.stock_ledger import get_previous_sle
 from erpnext.accounts.utils import get_balance_on
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
@@ -18,9 +18,11 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
 	import create_stock_reconciliation, set_valuation_method
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so
-from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
+from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
-from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+
 
 class TestDeliveryNote(unittest.TestCase):
 	def test_over_billing_against_dn(self):
@@ -277,8 +279,6 @@
 		dn.cancel()
 
 	def test_sales_return_for_non_bundled_items_full(self):
-		from erpnext.stock.doctype.item.test_item import make_item
-
 		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
 
 		make_item("Box", {'is_stock_item': 1})
@@ -741,6 +741,25 @@
 		self.assertEqual(si2.items[0].qty, 2)
 		self.assertEqual(si2.items[1].qty, 1)
 
+
+	def test_delivery_note_bundle_with_batched_item(self):
+		batched_bundle = make_item("_Test Batched bundle", {"is_stock_item": 0})
+		batched_item = make_item("_Test Batched Item",
+				{"is_stock_item": 1, "has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "TESTBATCH.#####"}
+				)
+		make_product_bundle(parent=batched_bundle.name, items=[batched_item.name])
+		make_stock_entry(item_code=batched_item.name, target="_Test Warehouse - _TC", qty=10, basic_rate=42)
+
+		try:
+			dn = create_delivery_note(item_code=batched_bundle.name, qty=1)
+		except frappe.ValidationError as e:
+			if "batch" in str(e).lower():
+				self.fail("Batch numbers not getting added to bundled items in DN.")
+			raise e
+
+		self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item")
+
+
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
 	args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index 81e7301..9ec28d8 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -68,7 +68,7 @@
 			delete (bool, optional): Defaults to `False`. `True` if driver details need to be emptied, else `False`.
 		"""
 
-		delivery_notes = list(set([stop.delivery_note for stop in self.delivery_stops if stop.delivery_note]))
+		delivery_notes = list(set(stop.delivery_note for stop in self.delivery_stops if stop.delivery_note))
 
 		update_fields = {
 			"driver": self.driver,
@@ -136,8 +136,8 @@
 
 				# Include last leg in the final distance calculation
 				self.uom = self.default_distance_uom
-				total_distance = sum([leg.get("distance", {}).get("value", 0.0)
-					for leg in directions.get("legs")])  # in meters
+				total_distance = sum(leg.get("distance", {}).get("value", 0.0)
+					for leg in directions.get("legs"))  # in meters
 				self.total_distance = total_distance * self.uom_conversion_factor
 			else:
 				idx += len(route) - 1
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/item/item.py b/erpnext/stock/doctype/item/item.py
index dbac794..fbd30cf 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -1,8 +1,6 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-from __future__ import unicode_literals
-
 import itertools
 import json
 import erpnext
@@ -12,7 +10,7 @@
 		copy_attributes_to_variant, get_variant, make_variant_item_code, validate_item_variant_attributes)
 from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for)
 from frappe import _, msgprint
-from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate,
+from frappe.utils import (cint, cstr, flt, formatdate, getdate,
 		now_datetime, random_string, strip, get_link_to_form, nowtime)
 from frappe.utils.html_utils import clean_html
 from frappe.website.doctype.website_slideshow.website_slideshow import \
@@ -21,8 +19,6 @@
 from frappe.website.render import clear_cache
 from frappe.website.website_generator import WebsiteGenerator
 
-from six import iteritems
-
 
 class DuplicateReorderRows(frappe.ValidationError):
 	pass
@@ -76,8 +72,6 @@
 		if not self.description:
 			self.description = self.item_name
 
-		# if self.is_sales_item and not self.get('is_item_from_hub'):
-		# 	self.publish_in_hub = 1
 
 	def after_insert(self):
 		'''set opening stock and item price'''
@@ -128,8 +122,9 @@
 		self.validate_auto_reorder_enabled_in_stock_settings()
 		self.cant_change()
 		self.update_show_in_website()
+		self.validate_item_tax_net_rate_range()
 
-		if not self.get("__islocal"):
+		if not self.is_new():
 			self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
 			self.old_website_item_groups = frappe.db.sql_list("""select item_group
 					from `tabWebsite Item Group`
@@ -203,7 +198,7 @@
 	def make_route(self):
 		if not self.route:
 			return cstr(frappe.db.get_value('Item Group', self.item_group,
-					'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
+					'route')) + '/' + self.scrub((self.item_name or self.item_code) + '-' + random_string(5))
 
 	def validate_website_image(self):
 		if frappe.flags.in_import:
@@ -258,7 +253,6 @@
 					"attached_to_name": self.name
 				})
 			except frappe.DoesNotExistError:
-				pass
 				# cleanup
 				frappe.local.message_log.pop()
 
@@ -362,47 +356,49 @@
 				context.update(get_slideshow(self))
 
 	def set_attribute_context(self, context):
-		if self.has_variants:
-			attribute_values_available = {}
-			context.attribute_values = {}
-			context.selected_attributes = {}
+		if not self.has_variants:
+			return
 
-			# load attributes
-			for v in context.variants:
-				v.attributes = frappe.get_all("Item Variant Attribute",
-					  fields=["attribute", "attribute_value"],
-					  filters={"parent": v.name})
-				# make a map for easier access in templates
-				v.attribute_map = frappe._dict({})
-				for attr in v.attributes:
-					v.attribute_map[attr.attribute] = attr.attribute_value
+		attribute_values_available = {}
+		context.attribute_values = {}
+		context.selected_attributes = {}
 
-				for attr in v.attributes:
-					values = attribute_values_available.setdefault(attr.attribute, [])
-					if attr.attribute_value not in values:
-						values.append(attr.attribute_value)
+		# load attributes
+		for v in context.variants:
+			v.attributes = frappe.get_all("Item Variant Attribute",
+				fields=["attribute", "attribute_value"],
+				filters={"parent": v.name})
+			# make a map for easier access in templates
+			v.attribute_map = frappe._dict({})
+			for attr in v.attributes:
+				v.attribute_map[attr.attribute] = attr.attribute_value
 
-					if v.name == context.variant.name:
-						context.selected_attributes[attr.attribute] = attr.attribute_value
+			for attr in v.attributes:
+				values = attribute_values_available.setdefault(attr.attribute, [])
+				if attr.attribute_value not in values:
+					values.append(attr.attribute_value)
 
-			# filter attributes, order based on attribute table
-			for attr in self.attributes:
-				values = context.attribute_values.setdefault(attr.attribute, [])
+				if v.name == context.variant.name:
+					context.selected_attributes[attr.attribute] = attr.attribute_value
 
-				if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
-					for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
-						values.append(val)
+		# filter attributes, order based on attribute table
+		for attr in self.attributes:
+			values = context.attribute_values.setdefault(attr.attribute, [])
 
-				else:
-					# get list of values defined (for sequence)
-					for attr_value in frappe.db.get_all("Item Attribute Value",
-						fields=["attribute_value"],
-						filters={"parent": attr.attribute}, order_by="idx asc"):
+			if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
+				for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
+					values.append(val)
 
-						if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
-							values.append(attr_value.attribute_value)
+			else:
+				# get list of values defined (for sequence)
+				for attr_value in frappe.db.get_all("Item Attribute Value",
+					fields=["attribute_value"],
+					filters={"parent": attr.attribute}, order_by="idx asc"):
 
-			context.variant_info = json.dumps(context.variants)
+					if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
+						values.append(attr_value.attribute_value)
+
+		context.variant_info = json.dumps(context.variants)
 
 	def set_disabled_attributes(self, context):
 		"""Disable selection options of attribute combinations that do not result in a variant"""
@@ -490,6 +486,11 @@
 		if self.disabled:
 			self.show_in_website = False
 
+	def validate_item_tax_net_rate_range(self):
+		for tax in self.get('taxes'):
+			if flt(tax.maximum_net_rate) < flt(tax.minimum_net_rate):
+				frappe.throw(_("Row #{0}: Maximum Net Rate cannot be greater than Minimum Net Rate"))
+
 	def update_template_tables(self):
 		template = frappe.get_doc("Item", self.variant_of)
 
@@ -521,7 +522,7 @@
 
 	def validate_item_type(self):
 		if self.has_serial_no == 1 and self.is_stock_item == 0 and not self.is_fixed_asset:
-			msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1)
+			frappe.throw(_("'Has Serial No' can not be 'Yes' for non-stock item"))
 
 		if self.has_serial_no == 0 and self.serial_no_series:
 			self.serial_no_series = None
@@ -542,10 +543,7 @@
 
 	def fill_customer_code(self):
 		""" Append all the customer codes and insert into "customer_code" field of item table """
-		cust_code = []
-		for d in self.get('customer_items'):
-			cust_code.append(d.ref_code)
-		self.customer_code = ','.join(cust_code)
+		self.customer_code = ','.join(d.ref_code for d in self.get("customer_items", []))
 
 	def check_item_tax(self):
 		"""Check whether Tax Rate is not entered twice for same Tax Type"""
@@ -742,23 +740,25 @@
 
 	def update_template_item(self):
 		"""Set Show in Website for Template Item if True for its Variant"""
-		if self.variant_of:
-			if self.show_in_website:
-				self.show_variant_in_website = 1
-				self.show_in_website = 0
+		if not self.variant_of:
+			return
 
-			if self.show_variant_in_website:
-				# show template
-				template_item = frappe.get_doc("Item", self.variant_of)
+		if self.show_in_website:
+			self.show_variant_in_website = 1
+			self.show_in_website = 0
 
-				if not template_item.show_in_website:
-					template_item.show_in_website = 1
-					template_item.flags.dont_update_variants = True
-					template_item.flags.ignore_permissions = True
-					template_item.save()
+		if self.show_variant_in_website:
+			# show template
+			template_item = frappe.get_doc("Item", self.variant_of)
+
+			if not template_item.show_in_website:
+				template_item.show_in_website = 1
+				template_item.flags.dont_update_variants = True
+				template_item.flags.ignore_permissions = True
+				template_item.save()
 
 	def validate_item_defaults(self):
-		companies = list(set([row.company for row in self.item_defaults]))
+		companies = {row.company for row in self.item_defaults}
 
 		if len(companies) != len(self.item_defaults):
 			frappe.throw(_("Cannot set multiple Item Defaults for a company."))
@@ -813,7 +813,7 @@
 				frappe.throw(_("Item has variants."))
 
 	def validate_attributes_in_variants(self):
-		if not self.has_variants or self.get("__islocal"):
+		if not self.has_variants or self.is_new():
 			return
 
 		old_doc = self.get_doc_before_save()
@@ -901,7 +901,7 @@
 				frappe.throw(_("Variant Based On cannot be changed"))
 
 	def validate_uom(self):
-		if not self.get("__islocal"):
+		if not self.is_new():
 			check_stock_uom_with_bin(self.name, self.stock_uom)
 		if self.has_variants:
 			for d in frappe.db.get_all("Item", filters={"variant_of": self.name}):
@@ -959,20 +959,20 @@
 				d.variant_of = self.variant_of
 
 	def cant_change(self):
-		if not self.get("__islocal"):
-			fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no")
+		if self.is_new():
+			return
 
-			values = frappe.db.get_value("Item", self.name, fields, as_dict=True)
-			if not values.get('valuation_method') and self.get('valuation_method'):
-				values['valuation_method'] = frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO"
+		fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no")
 
-			if values:
-				for field in fields:
-					if cstr(self.get(field)) != cstr(values.get(field)):
-						if not self.check_if_linked_document_exists(field):
-							break # no linked document, allowed
-						else:
-							frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
+		values = frappe.db.get_value("Item", self.name, fields, as_dict=True)
+		if not values.get('valuation_method') and self.get('valuation_method'):
+			values['valuation_method'] = frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO"
+
+		if values:
+			for field in fields:
+				if cstr(self.get(field)) != cstr(values.get(field)):
+					if self.check_if_linked_document_exists(field):
+						frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
 
 	def check_if_linked_document_exists(self, field):
 		linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Receipt Item",
@@ -1054,56 +1054,42 @@
 	}).insert()
 
 def get_timeline_data(doctype, name):
-	'''returns timeline data based on stock ledger entry'''
-	out = {}
-	items = dict(frappe.db.sql('''select posting_date, count(*)
-		from `tabStock Ledger Entry` where item_code=%s
-			and posting_date > date_sub(curdate(), interval 1 year)
-			group by posting_date''', name))
+	"""get timeline data based on Stock Ledger Entry. This is displayed as heatmap on the item page."""
 
-	for date, count in iteritems(items):
-		timestamp = get_timestamp(date)
-		out.update({timestamp: count})
+	items = frappe.db.sql("""select unix_timestamp(posting_date), count(*)
+							from `tabStock Ledger Entry`
+							where item_code=%s and posting_date > date_sub(curdate(), interval 1 year)
+							group by posting_date""", name)
 
-	return out
+	return dict(items)
 
 
-def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
+
+def validate_end_of_life(item_code, end_of_life=None, disabled=None):
 	if (not end_of_life) or (disabled is None):
 		end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
 
 	if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date():
-		msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
-		_msgprint(msg, verbose)
+		frappe.throw(_("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life)))
 
 	if disabled:
-		_msgprint(_("Item {0} is disabled").format(item_code), verbose)
+		frappe.throw(_("Item {0} is disabled").format(item_code))
 
 
-def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
+def validate_is_stock_item(item_code, is_stock_item=None):
 	if not is_stock_item:
 		is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
 
 	if is_stock_item != 1:
-		msg = _("Item {0} is not a stock Item").format(item_code)
-
-		_msgprint(msg, verbose)
+		frappe.throw(_("Item {0} is not a stock Item").format(item_code))
 
 
-def validate_cancelled_item(item_code, docstatus=None, verbose=1):
+def validate_cancelled_item(item_code, docstatus=None):
 	if docstatus is None:
 		docstatus = frappe.db.get_value("Item", item_code, "docstatus")
 
 	if docstatus == 2:
-		msg = _("Item {0} is cancelled").format(item_code)
-		_msgprint(msg, verbose)
-
-def _msgprint(msg, verbose):
-	if verbose:
-		msgprint(msg, raise_exception=True)
-	else:
-		raise frappe.ValidationError(msg)
-
+		frappe.throw(_("Item {0} is cancelled").format(item_code))
 
 def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
 	"""returns last purchase details in stock uom"""
@@ -1203,27 +1189,25 @@
 	if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
 		return
 
-	matched = True
 	ref_uom = frappe.db.get_value("Stock Ledger Entry",
 							   {"item_code": item}, "stock_uom")
 
 	if ref_uom:
 		if cstr(ref_uom) != cstr(stock_uom):
-			matched = False
-	else:
-		bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
-		for bin in bin_list:
-			if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0
-								or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
-				matched = False
-				break
+			frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
 
-		if matched and bin_list:
-			frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
+	bin_list = frappe.db.sql("""
+			select * from tabBin where item_code = %s
+				and (reserved_qty > 0 or ordered_qty > 0 or indented_qty > 0 or planned_qty > 0)
+				and stock_uom != %s
+			""", (item, stock_uom), as_dict=1)
 
-	if not matched:
-		frappe.throw(
-			_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
+	if bin_list:
+		frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item.").format(item))
+
+	# No SLE or documents against item. Bin UOM can be changed safely.
+	frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
+
 
 def get_item_defaults(item_code, company):
 	item = frappe.get_cached_doc('Item', item_code)
@@ -1264,45 +1248,59 @@
 
 @frappe.whitelist()
 def get_uom_conv_factor(uom, stock_uom):
-	uoms = [uom, stock_uom]
-	value = ""
-	uom_details = frappe.db.sql("""select to_uom, from_uom, value from `tabUOM Conversion Factor`\
-		where to_uom in ({0})
-		""".format(', '.join([frappe.db.escape(i, percent=False) for i in uoms])), as_dict=True)
+	""" Get UOM conversion factor from uom to stock_uom
+		e.g. uom = "Kg", stock_uom = "Gram" then returns 1000.0
+	"""
+	if uom == stock_uom:
+		return 1.0
 
-	for d in uom_details:
-		if d.from_uom == stock_uom and d.to_uom == uom:
-			value = 1/flt(d.value)
-		elif d.from_uom == uom and d.to_uom == stock_uom:
-			value = d.value
+	from_uom, to_uom = uom, stock_uom   # renaming for readability
 
-	if not value:
-		uom_stock = frappe.db.get_value("UOM Conversion Factor", {"to_uom": stock_uom}, ["from_uom", "value"], as_dict=1)
-		uom_row = frappe.db.get_value("UOM Conversion Factor", {"to_uom": uom}, ["from_uom", "value"], as_dict=1)
+	exact_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": to_uom, "from_uom": from_uom}, ["value"], as_dict=1)
+	if exact_match:
+		return exact_match.value
 
-		if uom_stock and uom_row:
-			if uom_stock.from_uom == uom_row.from_uom:
-				value = flt(uom_stock.value) * 1/flt(uom_row.value)
+	inverse_match = frappe.db.get_value("UOM Conversion Factor", {"to_uom": from_uom, "from_uom": to_uom}, ["value"], as_dict=1)
+	if inverse_match:
+		return 1 / inverse_match.value
 
-	return value
+	# This attempts to try and get conversion from intermediate UOM.
+	# case:
+	#            g -> mg = 1000
+	#            g -> kg = 0.001
+	# therefore  kg -> mg = 1000  / 0.001 = 1,000,000
+	intermediate_match = frappe.db.sql("""
+			select (first.value / second.value) as value
+			from `tabUOM Conversion Factor` first
+			join `tabUOM Conversion Factor` second
+				on first.from_uom = second.from_uom
+			where
+				first.to_uom = %(to_uom)s
+				and second.to_uom = %(from_uom)s
+			limit 1
+			""", {"to_uom": to_uom, "from_uom": from_uom}, as_dict=1)
+
+	if intermediate_match:
+		return intermediate_match[0].value
+
 
 @frappe.whitelist()
-def get_item_attribute(parent, attribute_value=''):
+def get_item_attribute(parent, attribute_value=""):
+	"""Used for providing auto-completions in child table."""
 	if not frappe.has_permission("Item"):
-		frappe.msgprint(_("No Permission"), raise_exception=1)
+		frappe.throw(_("No Permission"))
 
 	return frappe.get_all("Item Attribute Value", fields = ["attribute_value"],
-		filters = {'parent': parent, 'attribute_value': ("like", "%%%s%%" % attribute_value)})
+		filters = {'parent': parent, 'attribute_value': ("like", f"%{attribute_value}%")})
 
 def update_variants(variants, template, publish_progress=True):
-	count=0
-	for d in variants:
+	total = len(variants)
+	for count, d in enumerate(variants, start=1):
 		variant = frappe.get_doc("Item", d)
 		copy_attributes_to_variant(template, variant)
 		variant.save()
-		count+=1
 		if publish_progress:
-				frappe.publish_progress(count*100/len(variants), title = _("Updating Variants..."))
+			frappe.publish_progress(count / total * 100, title=_("Updating Variants..."))
 
 def on_doctype_update():
 	# since route is a Text column, it needs a length for indexing
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index e0b89d8..c7467a5 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -10,14 +10,15 @@
 from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError,
 	InvalidItemAttributeValueError, get_variant)
 from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode
-from erpnext.stock.doctype.item.item import get_uom_conv_factor
+from erpnext.stock.doctype.item.item import (get_uom_conv_factor, get_item_attribute,
+	validate_is_stock_item, get_timeline_data)
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.get_item_details import get_item_details
+from erpnext.tests.utils import change_settings
 
-from six import iteritems
 
 test_ignore = ["BOM"]
-test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand"]
+test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
 
 def make_item(item_code, properties=None):
 	if frappe.db.exists("Item", item_code):
@@ -98,7 +99,7 @@
 			"ignore_pricing_rule": 1
 		})
 
-		for key, value in iteritems(to_check):
+		for key, value in to_check.items():
 			self.assertEqual(value, details.get(key))
 
 	def test_item_tax_template(self):
@@ -194,7 +195,7 @@
 			"plc_conversion_rate": 1,
 			"customer": "_Test Customer",
 		})
-		for key, value in iteritems(sales_item_check):
+		for key, value in sales_item_check.items():
 			self.assertEqual(value, sales_item_details.get(key))
 
 		purchase_item_check = {
@@ -215,7 +216,7 @@
 			"plc_conversion_rate": 1,
 			"supplier": "_Test Supplier",
 		})
-		for key, value in iteritems(purchase_item_check):
+		for key, value in purchase_item_check.items():
 			self.assertEqual(value, purchase_item_details.get(key))
 
 	def test_item_attribute_change_after_variant(self):
@@ -375,6 +376,14 @@
 		self.assertEqual(item_doc.uoms[1].uom, "Kg")
 		self.assertEqual(item_doc.uoms[1].conversion_factor, 1000)
 
+	def test_uom_conv_intermediate(self):
+		factor = get_uom_conv_factor("Pound", "Gram")
+		self.assertAlmostEqual(factor, 453.592, 3)
+
+	def test_uom_conv_base_case(self):
+		factor = get_uom_conv_factor("m", "m")
+		self.assertEqual(factor, 1.0)
+
 	def test_item_variant_by_manufacturer(self):
 		fields = [{'field_name': 'description'}, {'field_name': 'variant_based_on'}]
 		set_item_variant_settings(fields)
@@ -464,7 +473,7 @@
 		self.assertEqual(len(matching_barcodes), 1)
 		details = matching_barcodes[0]
 
-		for key, value in iteritems(barcode_properties):
+		for key, value in barcode_properties.items():
 			self.assertEqual(value, details.get(key))
 
 		# Add barcode again - should cause DuplicateEntryError
@@ -480,6 +489,89 @@
 		new_barcode.barcode_type = 'EAN'
 		self.assertRaises(InvalidBarcode, item_doc.save)
 
+	def test_heatmap_data(self):
+		import time
+		data = get_timeline_data("Item", "_Test Item")
+		self.assertTrue(isinstance(data, dict))
+
+		now = time.time()
+		one_year_ago = now - 366 * 24 * 60 * 60
+
+		for timestamp, count in data.items():
+			self.assertIsInstance(timestamp, int)
+			self.assertTrue(one_year_ago <= timestamp <= now)
+			self.assertIsInstance(count, int)
+			self.assertTrue(count >= 0)
+
+	def test_index_creation(self):
+		"check if index is getting created in db"
+		from erpnext.stock.doctype.item.item import on_doctype_update
+		on_doctype_update()
+
+		indices = frappe.db.sql("show index from tabItem", as_dict=1)
+		expected_columns = {"item_code", "item_name", "item_group", "route"}
+		for index in indices:
+			expected_columns.discard(index.get("Column_name"))
+
+		if expected_columns:
+			self.fail(f"Expected db index on these columns: {', '.join(expected_columns)}")
+
+	def test_attribute_completions(self):
+		expected_attrs = {"Small", "Extra Small", "Extra Large", "Large", "2XL", "Medium"}
+
+		attrs = get_item_attribute("Test Size")
+		received_attrs = {attr.attribute_value for attr in attrs}
+		self.assertEqual(received_attrs, expected_attrs)
+
+		attrs = get_item_attribute("Test Size", attribute_value="extra")
+		received_attrs = {attr.attribute_value for attr in attrs}
+		self.assertEqual(received_attrs, {"Extra Small", "Extra Large"})
+
+	def test_check_stock_uom_with_bin(self):
+		# this item has opening stock and stock_uom set in test_records.
+		item = frappe.get_doc("Item", "_Test Item")
+		item.stock_uom = "Gram"
+		self.assertRaises(frappe.ValidationError, item.save)
+
+	def test_check_stock_uom_with_bin_no_sle(self):
+		from erpnext.stock.stock_balance import update_bin_qty
+		item = create_item("_Item with bin qty")
+		item.stock_uom = "Gram"
+		item.save()
+
+		update_bin_qty(item.item_code, "_Test Warehouse - _TC", {
+			"reserved_qty": 10
+		})
+
+		item.stock_uom = "Kilometer"
+		self.assertRaises(frappe.ValidationError, item.save)
+
+		update_bin_qty(item.item_code, "_Test Warehouse - _TC", {
+			"reserved_qty": 0
+		})
+
+		item.load_from_db()
+		item.stock_uom = "Kilometer"
+		try:
+			item.save()
+		except frappe.ValidationError as e:
+			self.fail(f"UoM change not allowed even though no SLE / BIN with positive qty exists: {e}")
+
+	def test_validate_stock_item(self):
+		self.assertRaises(frappe.ValidationError, validate_is_stock_item, "_Test Non Stock Item")
+
+		try:
+			validate_is_stock_item("_Test Item")
+		except frappe.ValidationError as e:
+			self.fail(f"stock item considered non-stock item: {e}")
+
+	@change_settings("Stock Settings", {"item_naming_by": "Naming Series"})
+	def test_autoname_series(self):
+		item = frappe.new_doc("Item")
+		item.item_group = "All Item Groups"
+		item.save()  # if item code saved without item_code then series worked
+
+
 def set_item_variant_settings(fields):
 	doc = frappe.get_doc('Item Variant Settings')
 	doc.set('fields', fields)
@@ -494,23 +586,24 @@
 
 test_records = frappe.get_test_records('Item')
 
-def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None, is_customer_provided_item=None,
-	customer=None, is_purchase_item=None, opening_stock=None, company=None):
+def create_item(item_code, is_stock_item=1, valuation_rate=0, warehouse="_Test Warehouse - _TC",
+		is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=0,
+		company="_Test Company"):
 	if not frappe.db.exists("Item", item_code):
 		item = frappe.new_doc("Item")
 		item.item_code = item_code
 		item.item_name = item_code
 		item.description = item_code
 		item.item_group = "All Item Groups"
-		item.is_stock_item = is_stock_item or 1
-		item.opening_stock = opening_stock or 0
-		item.valuation_rate = valuation_rate or 0.0
+		item.is_stock_item = is_stock_item
+		item.opening_stock = opening_stock
+		item.valuation_rate = valuation_rate
 		item.is_purchase_item = is_purchase_item
 		item.is_customer_provided_item = is_customer_provided_item
 		item.customer = customer or ''
 		item.append("item_defaults", {
-			"default_warehouse": warehouse or '_Test Warehouse - _TC',
-			"company": company or "_Test Company"
+			"default_warehouse": warehouse,
+			"company": company
 		})
 		item.save()
 	else:
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index d5700fe..8f76844 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -18,6 +18,9 @@
 		make_items()
 
 	def test_alternative_item_for_subcontract_rm(self):
+		frappe.db.set_value('Buying Settings', None,
+			'backflush_raw_materials_of_subcontract_based_on', 'BOM')
+
 		create_stock_reconciliation(item_code='Alternate Item For A RW 1', warehouse='_Test Warehouse - _TC',
 			qty=5, rate=2000)
 		create_stock_reconciliation(item_code='Test FG A RW 2', warehouse='_Test Warehouse - _TC',
@@ -65,6 +68,8 @@
 				status = True
 
 		self.assertEqual(status, True)
+		frappe.db.set_value('Buying Settings', None,
+			'backflush_raw_materials_of_subcontract_based_on', 'Material Transferred for Subcontract')
 
 	def test_alternative_item_for_production_rm(self):
 		create_stock_reconciliation(item_code='Alternate Item For A RW 1',
diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json
index ae36efc..fb10096 100644
--- a/erpnext/stock/doctype/item_tax/item_tax.json
+++ b/erpnext/stock/doctype/item_tax/item_tax.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2013-02-22 01:28:01",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -6,7 +7,9 @@
  "field_order": [
   "item_tax_template",
   "tax_category",
-  "valid_from"
+  "valid_from",
+  "minimum_net_rate",
+  "maximum_net_rate"
  ],
  "fields": [
   {
@@ -33,11 +36,24 @@
    "fieldtype": "Date",
    "in_list_view": 1,
    "label": "Valid From"
+  },
+  {
+   "fieldname": "maximum_net_rate",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Maximum Net Rate"
+  },
+  {
+   "fieldname": "minimum_net_rate",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Minimum Net Rate"
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2020-06-25 01:40:28.859752",
+ "links": [],
+ "modified": "2021-06-03 13:20:06.982303",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Tax",
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 8310946..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:
@@ -78,7 +87,7 @@
 					.format(item.idx, item.item_code))
 
 	def set_total_taxes_and_charges(self):
-		self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")])
+		self.total_taxes_and_charges = sum(flt(d.base_amount) for d in self.get("taxes"))
 
 	def set_applicable_charges_on_item(self):
 		if self.get('taxes') and self.distribute_charges_based_on != 'Distribute Manually':
@@ -104,15 +113,15 @@
 		based_on = self.distribute_charges_based_on.lower()
 
 		if based_on != 'distribute manually':
-			total = sum([flt(d.get(based_on)) for d in self.get("items")])
+			total = sum(flt(d.get(based_on)) for d in self.get("items"))
 		else:
 			# consider for proportion while distributing manually
-			total = sum([flt(d.get('applicable_charges')) for d in self.get("items")])
+			total = sum(flt(d.get('applicable_charges')) for d in self.get("items"))
 
 		if not total:
 			frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
 
-		total_applicable_charges = sum([flt(d.applicable_charges) for d in self.get("items")])
+		total_applicable_charges = sum(flt(d.applicable_charges) for d in self.get("items"))
 
 		precision = get_field_precision(frappe.get_meta("Landed Cost Item").get_field("applicable_charges"),
 		currency=frappe.get_cached_value('Company',  self.company,  "default_currency"))
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 984ae46..32b08f6 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -311,7 +311,7 @@
 
 def distribute_landed_cost_on_items(lcv):
 	based_on = lcv.distribute_charges_based_on.lower()
-	total = sum([flt(d.get(based_on)) for d in lcv.get("items")])
+	total = sum(flt(d.get(based_on)) for d in lcv.get("items"))
 
 	for item in lcv.get("items"):
 		item.applicable_charges = flt(item.get(based_on)) * flt(lcv.total_taxes_and_charges) / flt(total)
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 92c8d21..6e66f98 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -101,7 +101,8 @@
 		}
 
 		if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') {
-			if (flt(frm.doc.per_ordered, 2) < 100) {
+			let precision = frappe.defaults.get_default("float_precision");
+			if (flt(frm.doc.per_ordered, precision) < 100) {
 				let add_create_pick_list_button = () => {
 					frm.add_custom_button(__('Pick List'),
 						() => frm.events.create_pick_list(frm), __('Create'));
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/packing_slip/packing_slip.py b/erpnext/stock/doctype/packing_slip/packing_slip.py
index 2008bff..4a843e0 100644
--- a/erpnext/stock/doctype/packing_slip/packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/packing_slip.py
@@ -88,9 +88,9 @@
 		rows = [d.item_code for d in self.get("items")]
 
 		# also pick custom fields from delivery note
-		custom_fields = ', '.join(['dni.`{0}`'.format(d.fieldname)
+		custom_fields = ', '.join('dni.`{0}`'.format(d.fieldname)
 			for d in frappe.get_meta("Delivery Note Item").get_custom_fields()
-			if d.fieldtype not in no_value_fields])
+			if d.fieldtype not in no_value_fields)
 
 		if custom_fields:
 			custom_fields = ', ' + custom_fields
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/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 32d349f..44fb736 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -514,8 +514,7 @@
    "oldfieldname": "pr_raw_material_details",
    "oldfieldtype": "Table",
    "options": "Purchase Receipt Item Supplied",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "section_break0",
@@ -762,6 +761,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "base_rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment (Company Currency)",
@@ -805,6 +805,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.disable_rounded_total",
    "fieldname": "rounding_adjustment",
    "fieldtype": "Currency",
    "label": "Rounding Adjustment",
@@ -1147,7 +1148,7 @@
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-26 20:49:39.106049",
+ "modified": "2021-05-25 00:15:12.239017",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index f1292d8..82c87a8 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -202,6 +202,7 @@
 
 		self.make_gl_entries()
 		self.repost_future_sle_and_gle()
+		self.set_consumed_qty_in_po()
 
 	def check_next_docstatus(self):
 		submit_rv = frappe.db.sql("""select t1.name
@@ -233,6 +234,7 @@
 		self.repost_future_sle_and_gle()
 		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 		self.delete_auto_created_batches()
+		self.set_consumed_qty_in_po()
 
 	@frappe.whitelist()
 	def get_current_stock(self):
@@ -384,6 +386,7 @@
 			against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
 			total_valuation_amount = sum(valuation_tax.values())
 			amount_including_divisional_loss = negative_expense_to_be_booked
+			stock_rbnb = self.get_company_default("stock_received_but_not_billed")
 			i = 1
 			for tax in self.get("taxes"):
 				if valuation_tax.get(tax.name):
@@ -497,7 +500,7 @@
 	def update_billing_status(self, update_modified=True):
 		updated_pr = [self.name]
 		for d in self.get("items"):
-			if d.purchase_invoice and d.purchase_invoice_item:
+			if d.get("purchase_invoice") and d.get("purchase_invoice_item"):
 				d.db_set('billed_amt', d.amount, update_modified=update_modified)
 			elif d.purchase_order_item:
 				updated_pr += update_billed_amount_based_on_po(d.purchase_order_item, update_modified)
@@ -579,7 +582,6 @@
 
 @frappe.whitelist()
 def make_purchase_invoice(source_name, target_doc=None):
-	from frappe.model.mapper import get_mapped_doc
 	from erpnext.accounts.party import get_payment_terms_template
 
 	doc = frappe.get_doc('Purchase Receipt', source_name)
@@ -599,11 +601,16 @@
 
 	def update_item(source_doc, target_doc, source_parent):
 		target_doc.qty, returned_qty = get_pending_qty(source_doc)
+		if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"):
+			target_doc.rejected_qty = 0
 		target_doc.stock_qty = flt(target_doc.qty) * flt(target_doc.conversion_factor, target_doc.precision("conversion_factor"))
 		returned_qty_map[source_doc.name] = returned_qty
 
 	def get_pending_qty(item_row):
-		pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0)
+		qty = item_row.qty
+		if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"):
+			qty = item_row.received_qty
+		pending_qty = qty - invoiced_qty_map.get(item_row.name, 0)
 		returned_qty = flt(returned_qty_map.get(item_row.name, 0))
 		if returned_qty:
 			if returned_qty >= pending_qty:
@@ -748,4 +755,3 @@
 						account.base_amount * item.get(based_on_field) / total_item_cost
 
 	return item_account_wise_cost
-
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 5095a80..2eb8bfd 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -246,7 +246,7 @@
 		pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
 		self.assertEqual(len(pr.get("supplied_items")), 2)
 
-		rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")])
+		rm_supp_cost = sum(d.amount for d in pr.get("supplied_items"))
 		self.assertEqual(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
 
 		pr.cancel()
@@ -335,6 +335,10 @@
 		se2.cancel()
 		se3.cancel()
 		po.reload()
+		pr2.load_from_db()
+		pr2.cancel()
+
+		po.load_from_db()
 		po.cancel()
 
 	def test_serial_no_supplier(self):
@@ -417,11 +421,18 @@
 		self.assertEqual(return_pr_2.items[0].qty, -3)
 
 		# Make PI against unreturned amount
+		buying_settings = frappe.get_single("Buying Settings")
+		buying_settings.bill_for_rejected_quantity_in_purchase_invoice = 0
+		buying_settings.save()
+
 		pi = make_purchase_invoice(pr.name)
 		pi.submit()
 
 		self.assertEqual(pi.items[0].qty, 3)
 
+		buying_settings.bill_for_rejected_quantity_in_purchase_invoice = 1
+		buying_settings.save()
+
 		pr.load_from_db()
 		# PR should be completed on billing all unreturned amount
 		self.assertEqual(pr.items[0].billed_amt, 150)
@@ -1000,6 +1011,47 @@
 		self.assertEqual(pr.status, "To Bill")
 		self.assertAlmostEqual(pr.per_billed, 50.0, places=2)
 
+	def test_service_item_purchase_with_perpetual_inventory(self):
+		company = '_Test Company with perpetual inventory'
+		service_item = '_Test Non Stock Item'
+
+		before_test_value = frappe.db.get_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items')
+		frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', 1)
+		srbnb_account = 'Stock Received But Not Billed - TCP1'
+		frappe.db.set_value('Company', company, 'service_received_but_not_billed', srbnb_account)
+
+		pr = make_purchase_receipt(
+			company=company, item=service_item,
+			warehouse='Finished Goods - TCP1', do_not_save=1
+		)
+		item_row_with_diff_rate = frappe.copy_doc(pr.items[0])
+		item_row_with_diff_rate.rate = 100
+		pr.append('items', item_row_with_diff_rate)
+
+		pr.save()
+		pr.submit()
+
+		item_one_gl_entry = frappe.db.get_all("GL Entry", {
+			'voucher_type': pr.doctype,
+			'voucher_no': pr.name,
+			'account': srbnb_account,
+			'voucher_detail_no': pr.items[0].name
+		}, pluck="name")
+
+		item_two_gl_entry = frappe.db.get_all("GL Entry", {
+			'voucher_type': pr.doctype,
+			'voucher_no': pr.name,
+			'account': srbnb_account,
+			'voucher_detail_no': pr.items[1].name
+		}, pluck="name")
+		
+		# check if the entries are not merged into one
+		# seperate entries should be made since voucher_detail_no is different
+		self.assertEqual(len(item_one_gl_entry), 1)
+		self.assertEqual(len(item_two_gl_entry), 1)
+
+		frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', before_test_value)
+
 def get_sl_entries(voucher_type, voucher_no):
 	return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
 		from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s
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 56b046a..f5d076a 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -1,29 +1,45 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
 # See license.txt
 
-from __future__ import unicode_literals
-import frappe
 import unittest
+
+import frappe
 from frappe.utils import nowdate
-from erpnext.stock.doctype.item.test_item import create_item
+
+from erpnext.controllers.stock_controller import (
+	QualityInspectionNotSubmittedError,
+	QualityInspectionRejectedError,
+	QualityInspectionRequiredError,
+	make_quality_inspections,
+)
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError
+from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
 # test_records = frappe.get_test_records('Quality Inspection')
 
+
 class TestQualityInspection(unittest.TestCase):
 	def setUp(self):
 		create_item("_Test Item with QA")
-		frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
+		frappe.db.set_value(
+			"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
+		)
 
 	def test_qa_for_delivery(self):
-		make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100)
+		make_stock_entry(
+			item_code="_Test Item with QA",
+			target="_Test Warehouse - _TC",
+			qty=1,
+			basic_rate=100
+		)
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
 
 		self.assertRaises(QualityInspectionRequiredError, dn.submit)
 
-		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
+		qa = create_quality_inspection(
+			reference_type="Delivery Note", reference_name=dn.name, status="Rejected"
+		)
 		dn.reload()
 		self.assertRaises(QualityInspectionRejectedError, dn.submit)
 
@@ -38,7 +54,9 @@
 
 	def test_qa_not_submit(self):
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
-		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True)
+		qa = create_quality_inspection(
+			reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True
+		)
 		dn.items[0].quality_inspection = qa.name
 		self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
 
@@ -48,21 +66,28 @@
 	def test_value_based_qi_readings(self):
 		# Test QI based on acceptance values (Non formula)
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
-		readings = [{
-			"specification": "Iron Content", # numeric reading
-			"min_value": 0.1,
-			"max_value": 0.9,
-			"reading_1": "0.4"
-		},
-		{
-			"specification": "Particle Inspection Needed", # non-numeric reading
-			"numeric": 0,
-			"value": "Yes",
-			"reading_value": "Yes"
-		}]
+		readings = [
+			{
+				"specification": "Iron Content",  # numeric reading
+				"min_value": 0.1,
+				"max_value": 0.9,
+				"reading_1": "0.4"
+			},
+			{
+				"specification": "Particle Inspection Needed",  # non-numeric reading
+				"numeric": 0,
+				"value": "Yes",
+				"reading_value": "Yes"
+			}
+		]
 
-		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
-			readings=readings, do_not_save=True)
+		qa = create_quality_inspection(
+			reference_type="Delivery Note",
+			reference_name=dn.name,
+			readings=readings,
+			do_not_save=True
+		)
+
 		qa.save()
 
 		# status must be auto set as per formula
@@ -74,36 +99,43 @@
 
 	def test_formula_based_qi_readings(self):
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
-		readings = [{
-			"specification": "Iron Content", # numeric reading
-			"formula_based_criteria": 1,
-			"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
-			"reading_1": "0.4"
-		},
-		{
-			"specification": "Calcium Content", # numeric reading
-			"formula_based_criteria": 1,
-			"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
-			"reading_1": "0.7"
-		},
-		{
-			"specification": "Mg Content", # numeric reading
-			"formula_based_criteria": 1,
-			"acceptance_formula": "mean < 0.9",
-			"reading_1": "0.5",
-			"reading_2": "0.7",
-			"reading_3": "random text" # check if random string input causes issues
-		},
-		{
-			"specification": "Calcium Content", # non-numeric reading
-			"formula_based_criteria": 1,
-			"numeric": 0,
-			"acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')",
-			"reading_value": "Grade B"
-		}]
+		readings = [
+			{
+				"specification": "Iron Content",  # numeric reading
+				"formula_based_criteria": 1,
+				"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
+				"reading_1": "0.4"
+			},
+			{
+				"specification": "Calcium Content",  # numeric reading
+				"formula_based_criteria": 1,
+				"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
+				"reading_1": "0.7"
+			},
+			{
+				"specification": "Mg Content",  # numeric reading
+				"formula_based_criteria": 1,
+				"acceptance_formula": "mean < 0.9",
+				"reading_1": "0.5",
+				"reading_2": "0.7",
+				"reading_3": "random text"  # check if random string input causes issues
+			},
+			{
+				"specification": "Calcium Content",  # non-numeric reading
+				"formula_based_criteria": 1,
+				"numeric": 0,
+				"acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')",
+				"reading_value": "Grade B"
+			}
+		]
 
-		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
-			readings=readings, do_not_save=True)
+		qa = create_quality_inspection(
+			reference_type="Delivery Note",
+			reference_name=dn.name,
+			readings=readings,
+			do_not_save=True
+		)
+
 		qa.save()
 
 		# status must be auto set as per formula
@@ -115,6 +147,60 @@
 		qa.delete()
 		dn.delete()
 
+	def test_make_quality_inspections_from_linked_document(self):
+		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+		for item in dn.items:
+			item.sample_size = item.qty
+		quality_inspections = make_quality_inspections(dn.doctype, dn.name, dn.items)
+		self.assertEqual(len(dn.items), len(quality_inspections))
+
+		# cleanup
+		for qi in quality_inspections:
+			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)
 	qa = frappe.new_doc("Quality Inspection")
@@ -130,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"])
@@ -150,10 +235,11 @@
 
 	return qa
 
+
 def create_quality_inspection_parameter(parameter):
 	if not frappe.db.exists("Quality Inspection Parameter", parameter):
 		frappe.get_doc({
 			"doctype": "Quality Inspection Parameter",
 			"parameter": parameter,
 			"description": parameter
-		}).insert()
\ No newline at end of file
+		}).insert()
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 5b626ea..55f2ebb 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -37,6 +37,9 @@
 		self.db_set('status', status)
 
 	def on_submit(self):
+		if not frappe.flags.in_test:
+			return
+
 		frappe.enqueue(repost, timeout=1800, queue='long',
 			job_name='repost_sle', now=frappe.flags.in_test, doc=self)
 
@@ -115,12 +118,6 @@
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
 def repost_entries():
-	job_log = frappe.get_all('Scheduled Job Log', fields = ['status', 'creation'],
-		filters = {'scheduled_job_type': 'repost_item_valuation.repost_entries'}, order_by='creation desc', limit=1)
-
-	if job_log and job_log[0]['status'] == 'Start' and time_diff_in_hours(now(), job_log[0]['creation']) < 2:
-		return
-
 	riv_entries = get_repost_item_valuation_entries()
 
 	for row in riv_entries:
@@ -135,9 +132,7 @@
 		check_if_stock_and_account_balance_synced(today(), d.name)
 
 def get_repost_item_valuation_entries():
-	date = add_to_date(now(), hours=-3)
-
 	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
 		WHERE status != 'Completed' and creation <= %s and docstatus = 1
 		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
-	""", date, as_dict=1)
+	""", now(), as_dict=1)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index 3acf3a9..a3d44af 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -57,7 +57,8 @@
   "more_info",
   "serial_no_details",
   "company",
-  "status"
+  "status",
+  "work_order"
  ],
  "fields": [
   {
@@ -422,12 +423,18 @@
    "label": "Status",
    "options": "\nActive\nInactive\nDelivered\nExpired",
    "read_only": 1
+  },
+  {
+   "fieldname": "work_order",
+   "fieldtype": "Link",
+   "label": "Work Order",
+   "options": "Work Order"
   }
  ],
  "icon": "fa fa-barcode",
  "idx": 1,
  "links": [],
- "modified": "2020-07-20 20:50:16.660433",
+ "modified": "2021-01-08 14:31:15.375996",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial No",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 5ecc9f8..bad7b60 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -473,16 +473,13 @@
 		if s.strip()]
 
 def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False):
-	serial_no_doc.update({
-		"item_code": args.get("item_code"),
-		"company": args.get("company"),
-		"batch_no": args.get("batch_no"),
-		"via_stock_ledger": args.get("via_stock_ledger") or True,
-		"supplier": args.get("supplier"),
-		"location": args.get("location"),
-		"warehouse": (args.get("warehouse")
-			if args.get("actual_qty", 0) > 0 else None)
-	})
+	for field in ["item_code", "work_order", "company", "batch_no", "supplier", "location"]:
+		if args.get(field):
+			serial_no_doc.set(field, args.get(field))
+
+	serial_no_doc.via_stock_ledger = args.get("via_stock_ledger") or True
+	serial_no_doc.warehouse = (args.get("warehouse")
+		if args.get("actual_qty", 0) > 0 else None)
 
 	if is_new:
 		serial_no_doc.serial_no = serial_no
@@ -613,7 +610,7 @@
 	batch_nos = filters.get("batch_no")
 	expiry_date = filters.get("expiry_date")
 	if batch_nos:
-		batch_no_condition = """and sr.batch_no in ({}) """.format(', '.join(["'%s'" % d for d in batch_nos]))
+		batch_no_condition = """and sr.batch_no in ({}) """.format(', '.join("'%s'" % d for d in batch_nos))
 
 	if expiry_date:
 		batch_join_selection = "LEFT JOIN `tabBatch` batch on sr.batch_no = batch.name "
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index de23e76..6708393 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -115,6 +115,14 @@
 			return;
 		}
 
+		if (!frm.is_new() && frm.doc.docstatus === 0) {
+			frm.add_custom_button(__("Quality Inspection(s)"), () => {
+				let transaction_controller = new erpnext.TransactionController({ frm: frm });
+				transaction_controller.make_quality_inspection();
+			}, __("Create"));
+			frm.page.set_inner_btn_group_as_primary(__('Create'));
+		}
+
 		let quality_inspection_field = frm.get_docfield("items", "quality_inspection");
 		quality_inspection_field.get_route_options_for_new_doc = function(row) {
 			if (frm.is_new()) return;
@@ -155,7 +163,7 @@
 	refresh: function(frm) {
 		if(!frm.doc.docstatus) {
 			frm.trigger('validate_purpose_consumption');
-			frm.add_custom_button(__('Create Material Request'), function() {
+			frm.add_custom_button(__('Material Request'), function() {
 				frappe.model.with_doctype('Material Request', function() {
 					var mr = frappe.model.get_new_doc('Material Request');
 					var items = frm.get_field('items').grid.get_selected_children();
@@ -178,7 +186,7 @@
 					});
 					frappe.set_route('Form', 'Material Request', mr.name);
 				});
-			});
+			}, __("Create"));
 		}
 
 		if(frm.doc.items) {
@@ -978,7 +986,10 @@
 
 	items_add: function(doc, cdt, cdn) {
 		var row = frappe.get_doc(cdt, cdn);
-		this.frm.script_manager.copy_from_first_row("items", row, ["expense_account", "cost_center"]);
+
+		if (!(row.expense_account && row.cost_center)) {
+			this.frm.script_manager.copy_from_first_row("items", row, ["expense_account", "cost_center"]);
+		}
 
 		if(!row.s_warehouse) row.s_warehouse = this.frm.doc.from_warehouse;
 		if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
@@ -1068,6 +1079,10 @@
 }
 
 function attach_bom_items(bom_no) {
+	if (!bom_no) {
+		return
+	}
+
 	if (check_should_not_attach_bom_items(bom_no)) return
 	frappe.db.get_doc("BOM",bom_no).then(bom => {
 		const {name, items} = bom
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index a0b5457..523d332 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -74,7 +74,8 @@
   "total_amount",
   "job_card",
   "amended_from",
-  "credit_note"
+  "credit_note",
+  "is_return"
  ],
  "fields": [
   {
@@ -611,6 +612,16 @@
    "fieldname": "apply_putaway_rule",
    "fieldtype": "Check",
    "label": "Apply Putaway Rule"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_return",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Is Return",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
@@ -618,7 +629,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-05-24 11:32:23.904307",
+ "modified": "2021-05-26 17:07:58.015737",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 2f76bc7..90b81dd 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -97,8 +97,7 @@
 		update_serial_nos_after_submit(self, "items")
 		self.update_work_order()
 		self.validate_purchase_order()
-		if self.purchase_order and self.purpose == "Send to Subcontractor":
-			self.update_purchase_order_supplied_items()
+		self.update_purchase_order_supplied_items()
 
 		self.make_gl_entries()
 
@@ -117,9 +116,7 @@
 			self.set_material_request_transfer_status('Completed')
 
 	def on_cancel(self):
-
-		if self.purchase_order and self.purpose == "Send to Subcontractor":
-			self.update_purchase_order_supplied_items()
+		self.update_purchase_order_supplied_items()
 
 		if self.work_order and self.purpose == "Material Consumption for Manufacture":
 			self.validate_work_order_status()
@@ -465,7 +462,7 @@
 		"""
 		# Set rate for outgoing items
 		outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate)
-		finished_item_qty = sum([d.transfer_qty for d in self.items if d.is_finished_item])
+		finished_item_qty = sum(d.transfer_qty for d in self.items if d.is_finished_item)
 
 		# Set basic rate for incoming items
 		for d in self.get('items'):
@@ -501,6 +498,7 @@
 				d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
 				if not d.t_warehouse:
 					outgoing_items_cost += flt(d.basic_amount)
+
 		return outgoing_items_cost
 
 	def get_args_for_incoming_rate(self, item):
@@ -531,7 +529,7 @@
 		scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])
 
 		# Get raw materials cost from BOM if multiple material consumption entries
-		if frappe.db.get_single_value("Manufacturing Settings", "material_consumption"):
+		if frappe.db.get_single_value("Manufacturing Settings", "material_consumption", cache=True):
 			bom_items = self.get_bom_raw_materials(finished_item_qty)
 			outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
 
@@ -857,6 +855,7 @@
 				pro_doc.run_method("update_work_order_qty")
 				if self.purpose == "Manufacture":
 					pro_doc.run_method("update_planned_qty")
+					pro_doc.update_batch_produced_qty(self)
 
 			if not pro_doc.operations:
 				pro_doc.set_actual_dates()
@@ -1008,10 +1007,12 @@
 					if self.purchase_order and self.purpose == "Send to Subcontractor":
 						#Get PO Supplied Items Details
 						item_wh = frappe._dict(frappe.db.sql("""
-							select rm_item_code, reserve_warehouse
-							from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
-							where po.name = poitemsup.parent
-								and po.name = %s""",self.purchase_order))
+							SELECT
+								rm_item_code, reserve_warehouse
+							FROM
+								`tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
+							WHERE
+								po.name = poitemsup.parent and po.name = %s """,self.purchase_order))
 
 					for item in itervalues(item_dict):
 						if self.pro_doc and cint(self.pro_doc.from_wip_warehouse):
@@ -1077,18 +1078,54 @@
 			# in case of BOM
 			to_warehouse = item.get("default_warehouse")
 
+		args = {
+			"to_warehouse": to_warehouse,
+			"from_warehouse": "",
+			"qty": self.fg_completed_qty,
+			"item_name": item.item_name,
+			"description": item.description,
+			"stock_uom": item.stock_uom,
+			"expense_account": item.get("expense_account"),
+			"cost_center": item.get("buying_cost_center"),
+			"is_finished_item": 1
+		}
+
+		if self.work_order and self.pro_doc.has_batch_no:
+			self.set_batchwise_finished_goods(args, item)
+		else:
+			self.add_finisged_goods(args, item)
+
+	def set_batchwise_finished_goods(self, args, item):
+		qty = flt(self.fg_completed_qty)
+		filters = {
+			"reference_name": self.pro_doc.name,
+			"reference_doctype": self.pro_doc.doctype,
+			"qty_to_produce": (">", 0)
+		}
+
+		fields = ["qty_to_produce as qty", "produced_qty", "name"]
+
+		for row in frappe.get_all("Batch", filters = filters, fields = fields, order_by="creation asc"):
+			batch_qty = flt(row.qty) - flt(row.produced_qty)
+			if not batch_qty:
+				continue
+
+			if qty <=0:
+				break
+
+			fg_qty = batch_qty
+			if batch_qty >= qty:
+				fg_qty = qty
+
+			qty -= batch_qty
+			args["qty"] = fg_qty
+			args["batch_no"] = row.name
+
+			self.add_finisged_goods(args, item)
+
+	def add_finisged_goods(self, args, item):
 		self.add_to_stock_entry_detail({
-			item.name: {
-				"to_warehouse": to_warehouse,
-				"from_warehouse": "",
-				"qty": self.fg_completed_qty,
-				"item_name": item.item_name,
-				"description": item.description,
-				"stock_uom": item.stock_uom,
-				"expense_account": item.get("expense_account"),
-				"cost_center": item.get("buying_cost_center"),
-				"is_finished_item": 1
-			}
+			item.name: args
 		}, bom_no = self.bom_no)
 
 	def get_bom_raw_materials(self, qty):
@@ -1294,7 +1331,8 @@
 				item_dict[item]["qty"] = 0
 
 		# delete items with 0 qty
-		for item in item_dict.keys():
+		list_of_items = item_dict.keys()
+		for item in list_of_items:
 			if not item_dict[item]["qty"]:
 				del item_dict[item]
 
@@ -1347,7 +1385,7 @@
 			se_child.is_scrap_item = item_dict[d].get("is_scrap_item", 0)
 
 			for field in ["idx", "po_detail", "original_item",
-				"expense_account", "description", "item_name"]:
+				"expense_account", "description", "item_name", "serial_no", "batch_no"]:
 				if item_dict[d].get(field):
 					se_child.set(field, item_dict[d].get(field))
 
@@ -1400,33 +1438,26 @@
 							.format(item.batch_no, item.item_code))
 
 	def update_purchase_order_supplied_items(self):
-		#Get PO Supplied Items Details
-		item_wh = frappe._dict(frappe.db.sql("""
-			select rm_item_code, reserve_warehouse
-			from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
-			where po.name = poitemsup.parent
-			and po.name = %s""", self.purchase_order))
+		if (self.purchase_order and
+			(self.purpose in ['Send to Subcontractor', 'Material Transfer'] or self.is_return)):
 
-		#Update Supplied Qty in PO Supplied Items
+			#Get PO Supplied Items Details
+			item_wh = frappe._dict(frappe.db.sql("""
+				select rm_item_code, reserve_warehouse
+				from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
+				where po.name = poitemsup.parent
+				and po.name = %s""", self.purchase_order))
 
-		frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos
-			SET
-				pos.supplied_qty = IFNULL((SELECT ifnull(sum(transfer_qty), 0)
-					FROM
-						`tabStock Entry Detail` sed, `tabStock Entry` se
-					WHERE
-						pos.name = sed.po_detail AND pos.rm_item_code = sed.item_code
-						AND pos.parent = se.purchase_order AND sed.docstatus = 1
-						AND se.name = sed.parent and se.purchase_order = %(po)s
-				), 0)
-			WHERE pos.docstatus = 1 and pos.parent = %(po)s""", {"po": self.purchase_order})
+			supplied_items = get_supplied_items(self.purchase_order)
+			for name, item in supplied_items.items():
+				frappe.db.set_value('Purchase Order Item Supplied', name, item)
 
-		#Update reserved sub contracted quantity in bin based on Supplied Item Details and
-		for d in self.get("items"):
-			item_code = d.get('original_item') or d.get('item_code')
-			reserve_warehouse = item_wh.get(item_code)
-			stock_bin = get_bin(item_code, reserve_warehouse)
-			stock_bin.update_reserved_qty_for_sub_contracting()
+			#Update reserved sub contracted quantity in bin based on Supplied Item Details and
+			for d in self.get("items"):
+				item_code = d.get('original_item') or d.get('item_code')
+				reserve_warehouse = item_wh.get(item_code)
+				stock_bin = get_bin(item_code, reserve_warehouse)
+				stock_bin.update_reserved_qty_for_sub_contracting()
 
 	def update_so_in_serial_number(self):
 		so_name, item_code = frappe.db.get_value("Work Order", self.work_order, ["sales_order", "production_item"])
@@ -1480,7 +1511,7 @@
 				cond += """ WHEN (parent = %s and name = %s) THEN %s
 					""" %(frappe.db.escape(data[0]), frappe.db.escape(data[1]), transferred_qty)
 
-			if cond and stock_entries_child_list:
+			if stock_entries_child_list:
 				frappe.db.sql(""" UPDATE `tabStock Entry Detail`
 					SET
 						transferred_qty = CASE {cond} END
@@ -1531,6 +1562,36 @@
 				material_requests.append(material_request)
 				frappe.db.set_value('Material Request', material_request, 'transfer_status', status)
 
+	def set_serial_no_batch_for_finished_good(self):
+		args = {}
+		if self.pro_doc.serial_no:
+			self.get_serial_nos_for_fg(args)
+
+		for row in self.items:
+			if row.is_finished_item and row.item_code == self.pro_doc.production_item:
+				if args.get("serial_no"):
+					row.serial_no = '\n'.join(args["serial_no"][0: cint(row.qty)])
+
+	def get_serial_nos_for_fg(self, args):
+		fields = ["`tabStock Entry`.`name`", "`tabStock Entry Detail`.`qty`",
+			"`tabStock Entry Detail`.`serial_no`", "`tabStock Entry Detail`.`batch_no`"]
+
+		filters = [["Stock Entry","work_order","=",self.work_order], ["Stock Entry","purpose","=","Manufacture"],
+			["Stock Entry","docstatus","=",1], ["Stock Entry Detail","item_code","=",self.pro_doc.production_item]]
+
+		stock_entries = frappe.get_all("Stock Entry", fields=fields, filters=filters)
+
+		if self.pro_doc.serial_no:
+			args["serial_no"] = self.get_available_serial_nos(stock_entries)
+
+	def get_available_serial_nos(self, stock_entries):
+		used_serial_nos = []
+		for row in stock_entries:
+			if row.serial_no:
+				used_serial_nos.extend(get_serial_nos(row.serial_no))
+
+		return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos)))
+
 @frappe.whitelist()
 def move_sample_to_retention_warehouse(company, items):
 	if isinstance(items, string_types):
@@ -1642,6 +1703,10 @@
 		if bom.quantity:
 			operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
 
+	if work_order and work_order.produced_qty and cint(frappe.db.get_single_value('Manufacturing Settings',
+		'add_corrective_operation_cost_in_finished_good_valuation')):
+		operating_cost_per_unit += flt(work_order.corrective_operation_cost) / flt(work_order.produced_qty)
+
 	return operating_cost_per_unit
 
 def get_used_alternative_items(purchase_order=None, work_order=None):
@@ -1751,3 +1816,30 @@
 			format(max_retain_qty, batch_no, item_code), alert=True)
 		sample_quantity = qty_diff
 	return sample_quantity
+
+def get_supplied_items(purchase_order):
+	fields = ['`tabStock Entry Detail`.`transfer_qty`', '`tabStock Entry`.`is_return`',
+			'`tabStock Entry Detail`.`po_detail`', '`tabStock Entry Detail`.`item_code`']
+
+	filters = [['Stock Entry', 'docstatus', '=', 1], ['Stock Entry', 'purchase_order', '=', purchase_order]]
+
+	supplied_item_details = {}
+	for row in frappe.get_all('Stock Entry', fields = fields, filters = filters):
+		if not row.po_detail:
+			continue
+
+		key = row.po_detail
+		if key not in supplied_item_details:
+			supplied_item_details.setdefault(key,
+				frappe._dict({'supplied_qty': 0, 'returned_qty':0, 'total_supplied_qty':0}))
+
+		supplied_item = supplied_item_details[key]
+
+		if row.is_return:
+			supplied_item.returned_qty += row.transfer_qty
+		else:
+			supplied_item.supplied_qty += row.transfer_qty
+
+		supplied_item.total_supplied_qty = flt(supplied_item.supplied_qty) - flt(supplied_item.returned_qty)
+
+	return supplied_item_details
\ No newline at end of file
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 864ff48..22f412a 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -18,6 +18,7 @@
   "col_break2",
   "is_finished_item",
   "is_scrap_item",
+  "quality_inspection",
   "subcontracted_item",
   "section_break_8",
   "description",
@@ -69,7 +70,6 @@
   "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
-  "quality_inspection",
   "job_card_item"
  ],
  "fields": [
@@ -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-02-11 13:47:50.158754",
+ "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/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index b0e7440..cb939e6 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, date_diff
+from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, cint
 from frappe.model.document import Document
 from datetime import date
 from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
@@ -108,17 +108,18 @@
 		self.stock_uom = item_det.stock_uom
 
 	def check_stock_frozen_date(self):
-		stock_frozen_upto = frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
-		if stock_frozen_upto:
-			stock_auth_role = frappe.db.get_value('Stock Settings', None,'stock_auth_role')
-			if getdate(self.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in frappe.get_roles():
-				frappe.throw(_("Stock transactions before {0} are frozen").format(formatdate(stock_frozen_upto)), StockFreezeError)
+		stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings')
 
-		stock_frozen_upto_days = int(frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0)
+		if stock_settings.stock_frozen_upto:
+			if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto)
+				and stock_settings.stock_auth_role not in frappe.get_roles()):
+				frappe.throw(_("Stock transactions before {0} are frozen")
+					.format(formatdate(stock_settings.stock_frozen_upto)), StockFreezeError)
+
+		stock_frozen_upto_days = cint(stock_settings.stock_frozen_upto_days)
 		if stock_frozen_upto_days:
-			stock_auth_role = frappe.db.get_value('Stock Settings', None,'stock_auth_role')
 			older_than_x_days_ago = (add_days(getdate(self.posting_date), stock_frozen_upto_days) <= date.today())
-			if older_than_x_days_ago and not stock_auth_role in frappe.get_roles():
+			if older_than_x_days_ago and stock_settings.stock_auth_role not in frappe.get_roles():
 				frappe.throw(_("Not allowed to update stock transactions older than {0}").format(stock_frozen_upto_days), StockFreezeError)
 
 	def scrub_posting_time(self):
@@ -177,3 +178,4 @@
 
 	frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
 	frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
+	frappe.db.add_index("Stock Ledger Entry", ["voucher_detail_no"])
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.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index ac4ed5e..a01db80 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -48,37 +48,54 @@
 	},
 
 	get_items: function(frm) {
-		frappe.prompt({label:"Warehouse", fieldname: "warehouse", fieldtype:"Link", options:"Warehouse", reqd: 1,
+		let fields = [{
+			label: 'Warehouse', fieldname: 'warehouse', fieldtype: 'Link', options: 'Warehouse', reqd: 1,
 			"get_query": function() {
 				return {
 					"filters": {
 						"company": frm.doc.company,
 					}
-				}
-			}},
-			function(data) {
-				frappe.call({
-					method:"erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_items",
-					args: {
-						warehouse: data.warehouse,
-						posting_date: frm.doc.posting_date,
-						posting_time: frm.doc.posting_time,
-						company:frm.doc.company
-					},
-					callback: function(r) {
-						var items = [];
-						frm.clear_table("items");
-						for(var i=0; i< r.message.length; i++) {
-							var d = frm.add_child("items");
-							$.extend(d, r.message[i]);
-							if(!d.qty) d.qty = null;
-							if(!d.valuation_rate) d.valuation_rate = null;
-						}
-						frm.refresh_field("items");
-					}
-				});
+				};
 			}
-		, __("Get Items"), __("Update"));
+		}, {
+			label: "Item Code", fieldname: "item_code", fieldtype: "Link", options: "Item",
+			"get_query": function() {
+				return {
+					"filters": {
+						"disabled": 0,
+					}
+				};
+			}
+		}];
+
+		frappe.prompt(fields, function(data) {
+			frappe.call({
+				method: "erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_items",
+				args: {
+					warehouse: data.warehouse,
+					posting_date: frm.doc.posting_date,
+					posting_time: frm.doc.posting_time,
+					company: frm.doc.company,
+					item_code: data.item_code
+				},
+				callback: function(r) {
+					frm.clear_table("items");
+					for (var i=0; i<r.message.length; i++) {
+						var d = frm.add_child("items");
+						$.extend(d, r.message[i]);
+
+						if (!d.qty) {
+							d.qty = 0;
+						}
+
+						if (!d.valuation_rate) {
+							d.valuation_rate = 0;
+						}
+					}
+					frm.refresh_field("items");
+				}
+			});
+		}, __("Get Items"), __("Update"));
 	},
 
 	posting_date: function(frm) {
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 32e0ccb..2e09286 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -96,7 +96,7 @@
 
 	def validate_data(self):
 		def _get_msg(row_num, msg):
-			return _("Row # {0}: ").format(row_num+1) + msg
+			return _("Row # {0}:").format(row_num+1) + " " + msg
 
 		self.validation_messages = []
 		item_warehouse_combinations = []
@@ -167,8 +167,8 @@
 			item = frappe.get_doc("Item", item_code)
 
 			# end of life and stock item
-			validate_end_of_life(item_code, item.end_of_life, item.disabled, verbose=0)
-			validate_is_stock_item(item_code, item.is_stock_item, verbose=0)
+			validate_end_of_life(item_code, item.end_of_life, item.disabled)
+			validate_is_stock_item(item_code, item.is_stock_item)
 
 			# item should not be serialized
 			if item.has_serial_no and not row.serial_no and not item.serial_no_series:
@@ -179,10 +179,10 @@
 				raise frappe.ValidationError(_("Batch no is required for batched item {0}").format(item_code))
 
 			# docstatus should be < 2
-			validate_cancelled_item(item_code, item.docstatus, verbose=0)
+			validate_cancelled_item(item_code, item.docstatus)
 
 		except Exception as e:
-			self.validation_messages.append(_("Row # ") + ("%d: " % (row.idx)) + cstr(e))
+			self.validation_messages.append(_("Row #") + " " + ("%d: " % (row.idx)) + cstr(e))
 
 	def update_stock_ledger(self):
 		"""	find difference between current and expected entries
@@ -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)
@@ -473,46 +475,107 @@
 		else:
 			self._submit()
 
+	def cancel(self):
+		if len(self.items) > 100:
+			msgprint(_("The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage"))
+			self.queue_action('cancel', timeout=2000)
+		else:
+			self._cancel()
+
 @frappe.whitelist()
-def get_items(warehouse, posting_date, posting_time, company):
+def get_items(warehouse, posting_date, posting_time, company, item_code=None):
+	items = [frappe._dict({
+		'item_code': item_code,
+		'warehouse': warehouse
+	})]
+
+	if not item_code:
+		items = get_items_for_stock_reco(warehouse, company)
+
+	res = []
+	itemwise_batch_data = get_itemwise_batch(warehouse, posting_date, company, item_code)
+
+	for d in items:
+		if d.item_code in itemwise_batch_data:
+			stock_bal = get_stock_balance(d.item_code, d.warehouse,
+				posting_date, posting_time, with_valuation_rate=True)
+
+			for row in itemwise_batch_data.get(d.item_code):
+				args = get_item_data(row, row.qty, stock_bal[1])
+				res.append(args)
+		else:
+			stock_bal = get_stock_balance(d.item_code, d.warehouse, posting_date, posting_time,
+				with_valuation_rate=True , with_serial_no=cint(d.has_serial_no))
+
+			args = get_item_data(d, stock_bal[0], stock_bal[1],
+				stock_bal[2] if cint(d.has_serial_no) else '')
+
+			res.append(args)
+
+	return res
+
+def get_items_for_stock_reco(warehouse, company):
 	lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
 	items = frappe.db.sql("""
-		select i.name, i.item_name, bin.warehouse, i.has_serial_no
+		select i.name as item_code, i.item_name, bin.warehouse as warehouse, i.has_serial_no, i.has_batch_no
 		from tabBin bin, tabItem i
-		where i.name=bin.item_code and i.disabled=0 and i.is_stock_item = 1
-		and i.has_variants = 0 and i.has_batch_no = 0
-		and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse)
-	""", (lft, rgt))
+		where i.name=bin.item_code and IFNULL(i.disabled, 0) = 0 and i.is_stock_item = 1
+		and i.has_variants = 0 and exists(
+			select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse
+		)
+	""", (lft, rgt), as_dict=1)
 
 	items += frappe.db.sql("""
-		select i.name, i.item_name, id.default_warehouse, i.has_serial_no
+		select i.name as item_code, i.item_name, id.default_warehouse as warehouse, i.has_serial_no, i.has_batch_no
 		from tabItem i, `tabItem Default` id
 		where i.name = id.parent
 			and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=id.default_warehouse)
-			and i.is_stock_item = 1 and i.has_batch_no = 0
-			and i.has_variants = 0 and i.disabled = 0 and id.company=%s
+			and i.is_stock_item = 1 and i.has_variants = 0 and IFNULL(i.disabled, 0) = 0 and id.company=%s
 		group by i.name
-	""", (lft, rgt, company))
+	""", (lft, rgt, company), as_dict=1)
 
-	res = []
-	for d in set(items):
-		stock_bal = get_stock_balance(d[0], d[2], posting_date, posting_time,
-			with_valuation_rate=True , with_serial_no=cint(d[3]))
+	return items
 
-		if frappe.db.get_value("Item", d[0], "disabled") == 0:
-			res.append({
-				"item_code": d[0],
-				"warehouse": d[2],
-				"qty": stock_bal[0],
-				"item_name": d[1],
-				"valuation_rate": stock_bal[1],
-				"current_qty": stock_bal[0],
-				"current_valuation_rate": stock_bal[1],
-				"current_serial_no": stock_bal[2] if cint(d[3]) else '',
-				"serial_no": stock_bal[2] if cint(d[3]) else ''
-			})
+def get_item_data(row, qty, valuation_rate, serial_no=None):
+	return {
+		'item_code': row.item_code,
+		'warehouse': row.warehouse,
+		'qty': qty,
+		'item_name': row.item_name,
+		'valuation_rate': valuation_rate,
+		'current_qty': qty,
+		'current_valuation_rate': valuation_rate,
+		'current_serial_no': serial_no,
+		'serial_no': serial_no,
+		'batch_no': row.get('batch_no')
+	}
 
-	return res
+def get_itemwise_batch(warehouse, posting_date, company, item_code=None):
+	from erpnext.stock.report.batch_wise_balance_history.batch_wise_balance_history import execute
+	itemwise_batch_data = {}
+
+	filters = frappe._dict({
+		'warehouse': warehouse,
+		'from_date': posting_date,
+		'to_date': posting_date,
+		'company': company
+	})
+
+	if item_code:
+		filters.item_code = item_code
+
+	columns, data = execute(filters)
+
+	for row in data:
+		itemwise_batch_data.setdefault(row[0], []).append(frappe._dict({
+			'item_code': row[0],
+			'warehouse': warehouse,
+			'qty': row[8],
+			'item_name': row[1],
+			'batch_no': row[4]
+		}))
+
+	return itemwise_batch_data
 
 @frappe.whitelist()
 def get_stock_balance_for(item_code, warehouse,
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/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 95478f6..e3981c9 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -11,6 +11,7 @@
 import erpnext
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
+from erpnext.stock.doctype.item.test_item import create_item
 
 test_records = frappe.get_test_records('Warehouse')
 
@@ -92,6 +93,39 @@
 		self.assertTrue(frappe.db.get_value("Warehouse",
 			filters={"account": "Test Warehouse for Merging 2 - TCP1"}))
 
+	def test_unlinking_warehouse_from_item_defaults(self):
+		company = "_Test Company"
+
+		warehouse_names = [f'_Test Warehouse {i} for Unlinking' for i in range(2)]
+		warehouse_ids = []
+		for warehouse in warehouse_names:
+			warehouse_id = create_warehouse(warehouse, company=company)
+			warehouse_ids.append(warehouse_id)
+
+		item_names = [f'_Test Item {i} for Unlinking' for i in range(2)]
+		for item, warehouse in zip(item_names, warehouse_ids):
+			create_item(item, warehouse=warehouse, company=company)
+
+		# Delete warehouses
+		for warehouse in warehouse_ids:
+			frappe.delete_doc("Warehouse", warehouse)
+
+		# Check Item existance
+		for item in item_names:
+			self.assertTrue(
+				bool(frappe.db.exists("Item", item)),
+				f"{item} doesn't exist"
+			)
+
+			item_doc = frappe.get_doc("Item", item)
+			for item_default in item_doc.item_defaults:
+				self.assertNotIn(
+					item_default.default_warehouse,
+					warehouse_ids,
+					f"{item} linked to {item_default.default_warehouse} in {warehouse_ids}."
+				)
+
+
 def create_warehouse(warehouse_name, properties=None, company=None):
 	if not company:
 		company = "_Test Company"
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index 2062bdd..3abc139 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -54,6 +54,7 @@
 			throw(_("Child warehouse exists for this warehouse. You can not delete this warehouse."))
 
 		self.update_nsm_model()
+		self.unlink_from_items()
 
 	def check_if_sle_exists(self):
 		return frappe.db.sql("""select name from `tabStock Ledger Entry`
@@ -138,6 +139,12 @@
 			self.save()
 			return 1
 
+	def unlink_from_items(self):
+		frappe.db.sql("""
+				update `tabItem Default`
+				set default_warehouse=NULL
+				where default_warehouse=%s""", self.name)
+
 @frappe.whitelist()
 def get_children(doctype, parent=None, company=None, is_root=False):
 	if is_root:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index d1dcdc2..ca174a3 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -436,18 +436,37 @@
 	return itemwise_barcode
 
 @frappe.whitelist()
-def get_item_tax_info(company, tax_category, item_codes):
+def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_tax_templates=None):
 	out = {}
-	if isinstance(item_codes, string_types):
+
+	if item_tax_templates is None:
+		item_tax_templates = {}
+	
+	if item_rates is None:
+		item_rates = {}
+
+	if isinstance(item_codes, (str,)):
 		item_codes = json.loads(item_codes)
 
+	if isinstance(item_rates, (str,)):
+		item_rates = json.loads(item_rates)
+
+	if isinstance(item_tax_templates, (str,)):
+		item_tax_templates = json.loads(item_tax_templates)
+
 	for item_code in item_codes:
-		if not item_code or item_code in out:
+		if not item_code or item_code[1] in out or not item_tax_templates.get(item_code[1]):
 			continue
-		out[item_code] = {}
-		item = frappe.get_cached_doc("Item", item_code)
-		get_item_tax_template({"company": company, "tax_category": tax_category}, item, out[item_code])
-		out[item_code]["item_tax_rate"] = get_item_tax_map(company, out[item_code].get("item_tax_template"), as_json=True)
+
+		out[item_code[1]] = {}
+		item = frappe.get_cached_doc("Item", item_code[0])
+		args = {"company": company, "tax_category": tax_category, "net_rate": item_rates.get(item_code[1])}
+
+		if item_tax_templates:
+			args.update({"item_tax_template": item_tax_templates.get(item_code[1])})
+
+		get_item_tax_template(args, item, out[item_code[1]])
+		out[item_code[1]]["item_tax_rate"] = get_item_tax_map(company, out[item_code[1]].get("item_tax_template"), as_json=True)
 
 	return out
 
@@ -459,9 +478,7 @@
 		}
 	"""
 	item_tax_template = args.get("item_tax_template")
-
-	if not item_tax_template:
-		item_tax_template = _get_item_tax_template(args, item.taxes, out)
+	item_tax_template = _get_item_tax_template(args, item.taxes, out)
 
 	if not item_tax_template:
 		item_group = item.item_group
@@ -478,12 +495,13 @@
 
 	for tax in taxes:
 		tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company')
-		if tax.valid_from and tax_company == args['company']:
+		if (tax.valid_from or tax.maximum_net_rate) and tax_company == args['company']:
 			# In purchase Invoice first preference will be given to supplier invoice date
 			# if supplier date is not present then posting date
 			validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
 
-			if getdate(tax.valid_from) <= getdate(validation_date):
+			if getdate(tax.valid_from) <= getdate(validation_date) \
+				and is_within_valid_range(args, tax):
 				taxes_with_validity.append(tax)
 		else:
 			if tax_company == args['company']:
@@ -502,12 +520,26 @@
 	if not taxes_with_validity and (not taxes_with_no_validity):
 		return None
 
+	# do not change if already a valid template
+	if args.get('item_tax_template') in {t.item_tax_template for t in taxes}:
+		out["item_tax_template"] = args.get('item_tax_template')
+		return args.get('item_tax_template')
+
 	for tax in taxes:
 		if cstr(tax.tax_category) == cstr(args.get("tax_category")):
 			out["item_tax_template"] = tax.item_tax_template
 			return tax.item_tax_template
 	return None
 
+def is_within_valid_range(args, tax):
+	if not flt(tax.maximum_net_rate):
+		# No range specified, just ignore
+		return True
+	elif flt(tax.minimum_net_rate) <= flt(args.get('net_rate')) <= flt(tax.maximum_net_rate):
+		return True
+
+	return False
+
 @frappe.whitelist()
 def get_item_tax_map(company, item_tax_template, as_json=True):
 	item_tax_map = {}
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/stock/report/incorrect_balance_qty_after_transaction/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/stock/report/incorrect_balance_qty_after_transaction/__init__.py
diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js
new file mode 100644
index 0000000..bf11277
--- /dev/null
+++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Incorrect Balance Qty After Transaction"] = {
+	"filters": [
+		{
+			label: __("Company"),
+			fieldtype: "Link",
+			fieldname: "company",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			label: __('Item Code'),
+			fieldtype: 'Link',
+			fieldname: 'item_code',
+			options: 'Item'
+		},
+		{
+			label: __('Warehouse'),
+			fieldtype: 'Link',
+			fieldname: 'warehouse'
+		}
+	]
+};
diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.json b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.json
new file mode 100644
index 0000000..a5815bc
--- /dev/null
+++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-05-12 16:47:58.717853",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-05-12 16:48:28.347575",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Incorrect Balance Qty After Transaction",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Incorrect Balance Qty After Transaction",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Stock Manager"
+  },
+  {
+   "role": "Purchase User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py
new file mode 100644
index 0000000..cf174c9
--- /dev/null
+++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py
@@ -0,0 +1,111 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from six import iteritems
+from frappe.utils import flt
+
+def execute(filters=None):
+	columns, data = [], []
+	columns = get_columns()
+	data = get_data(filters)
+	return columns, data
+
+def get_data(filters):
+	data = get_stock_ledger_entries(filters)
+	itewise_balance_qty = {}
+
+	for row in data:
+		key = (row.item_code, row.warehouse)
+		itewise_balance_qty.setdefault(key, []).append(row)
+
+	res = validate_data(itewise_balance_qty)
+	return res
+
+def validate_data(itewise_balance_qty):
+	res = []
+	for key, data in iteritems(itewise_balance_qty):
+		row = get_incorrect_data(data)
+		if row:
+			res.append(row)
+			res.append({})
+
+	return res
+
+def get_incorrect_data(data):
+	balance_qty = 0.0
+	for row in data:
+		balance_qty += row.actual_qty
+		if row.voucher_type == "Stock Reconciliation" and not row.batch_no:
+			balance_qty = flt(row.qty_after_transaction)
+
+		row.expected_balance_qty = balance_qty
+		if abs(flt(row.expected_balance_qty) - flt(row.qty_after_transaction)) > 0.5:
+			row.differnce = abs(flt(row.expected_balance_qty) - flt(row.qty_after_transaction))
+			return row
+
+def get_stock_ledger_entries(report_filters):
+	filters = {}
+	fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'actual_qty',
+		'posting_date', 'posting_time', 'company', 'warehouse', 'qty_after_transaction', 'batch_no']
+
+	for field in ['warehouse', 'item_code', 'company']:
+		if report_filters.get(field):
+			filters[field] = report_filters.get(field)
+
+	return frappe.get_all('Stock Ledger Entry', fields = fields, filters = filters,
+		order_by = 'timestamp(posting_date, posting_time) asc, creation asc')
+
+def get_columns():
+	return [{
+		'label': _('Id'),
+		'fieldtype': 'Link',
+		'fieldname': 'name',
+		'options': 'Stock Ledger Entry',
+		'width': 120
+	}, {
+		'label': _('Posting Date'),
+		'fieldtype': 'Date',
+		'fieldname': 'posting_date',
+		'width': 110
+	}, {
+		'label': _('Voucher Type'),
+		'fieldtype': 'Link',
+		'fieldname': 'voucher_type',
+		'options': 'DocType',
+		'width': 120
+	}, {
+		'label': _('Voucher No'),
+		'fieldtype': 'Dynamic Link',
+		'fieldname': 'voucher_no',
+		'options': 'voucher_type',
+		'width': 120
+	}, {
+		'label': _('Item Code'),
+		'fieldtype': 'Link',
+		'fieldname': 'item_code',
+		'options': 'Item',
+		'width': 120
+	}, {
+		'label': _('Warehouse'),
+		'fieldtype': 'Link',
+		'fieldname': 'warehouse',
+		'options': 'Warehouse',
+		'width': 120
+	}, {
+		'label': _('Expected Balance Qty'),
+		'fieldtype': 'Float',
+		'fieldname': 'expected_balance_qty',
+		'width': 170
+	}, {
+		'label': _('Actual Balance Qty'),
+		'fieldtype': 'Float',
+		'fieldname': 'qty_after_transaction',
+		'width': 150
+	}, {
+		'label': _('Difference'),
+		'fieldtype': 'Float',
+		'fieldname': 'differnce',
+		'width': 110
+	}]
\ No newline at end of file
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/stock/report/incorrect_serial_no_valuation/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/stock/report/incorrect_serial_no_valuation/__init__.py
diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js
new file mode 100644
index 0000000..c62d480
--- /dev/null
+++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Incorrect Serial No Valuation"] = {
+	"filters": [
+		{
+			label: __('Item Code'),
+			fieldtype: 'Link',
+			fieldname: 'item_code',
+			options: 'Item',
+			get_query: function() {
+				return {
+					filters: {
+						'has_serial_no': 1
+					}
+				}
+			}
+		},
+		{
+			label: __('From Date'),
+			fieldtype: 'Date',
+			fieldname: 'from_date',
+			reqd: 1,
+			default: frappe.defaults.get_user_default("year_start_date")
+		},
+		{
+			label: __('To Date'),
+			fieldtype: 'Date',
+			fieldname: 'to_date',
+			reqd: 1,
+			default: frappe.defaults.get_user_default("year_end_date")
+		}
+	]
+};
diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.json b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.json
new file mode 100644
index 0000000..cc384a5
--- /dev/null
+++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.json
@@ -0,0 +1,36 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-05-13 13:07:00.767845",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2021-05-13 13:07:00.767845",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Incorrect Serial No Valuation",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Incorrect Serial No Valuation",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Accounts Manager"
+  },
+  {
+   "role": "Accounts User"
+  },
+  {
+   "role": "Stock Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py
new file mode 100644
index 0000000..e54cf4c
--- /dev/null
+++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py
@@ -0,0 +1,148 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+import copy
+from frappe import _
+from six import iteritems
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+def execute(filters=None):
+	columns, data = [], []
+	columns = get_columns()
+	data = get_data(filters)
+	return columns, data
+
+def get_data(filters):
+	data = get_stock_ledger_entries(filters)
+	serial_nos_data = prepare_serial_nos(data)
+	data = get_incorrect_serial_nos(serial_nos_data)
+
+	return data
+
+def prepare_serial_nos(data):
+	serial_no_wise_data = {}
+	for row in data:
+		if not row.serial_nos:
+			continue
+
+		for serial_no in get_serial_nos(row.serial_nos):
+			sle = copy.deepcopy(row)
+			sle.serial_no = serial_no
+			sle.qty = 1 if sle.actual_qty > 0 else -1
+			sle.valuation_rate = sle.valuation_rate if sle.actual_qty > 0 else sle.valuation_rate * -1
+			serial_no_wise_data.setdefault(serial_no, []).append(sle)
+
+	return serial_no_wise_data
+
+def get_incorrect_serial_nos(serial_nos_data):
+	result = []
+
+	total_value = frappe._dict({'qty': 0, 'valuation_rate': 0, 'serial_no': frappe.bold(_('Balance'))})
+
+	for serial_no, data in iteritems(serial_nos_data):
+		total_dict = frappe._dict({'qty': 0, 'valuation_rate': 0, 'serial_no': frappe.bold(_('Total'))})
+
+		if check_incorrect_serial_data(data, total_dict):
+			result.extend(data)
+
+			total_value.qty += total_dict.qty
+			total_value.valuation_rate += total_dict.valuation_rate
+
+			result.append(total_dict)
+			result.append({})
+
+	result.append(total_value)
+
+	return result
+
+def check_incorrect_serial_data(data, total_dict):
+	incorrect_data = False
+	for row in data:
+		total_dict.qty += row.qty
+		total_dict.valuation_rate += row.valuation_rate
+
+		if ((total_dict.qty == 0 and abs(total_dict.valuation_rate) > 0) or total_dict.qty < 0):
+			incorrect_data = True
+
+	return incorrect_data
+
+def get_stock_ledger_entries(report_filters):
+	fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'serial_no as serial_nos', 'actual_qty',
+		'posting_date', 'posting_time', 'company', 'warehouse', '(stock_value_difference / actual_qty) as valuation_rate']
+
+	filters = {'serial_no': ("is", "set")}
+
+	if report_filters.get('item_code'):
+		filters['item_code'] = report_filters.get('item_code')
+
+	if report_filters.get('from_date') and report_filters.get('to_date'):
+		filters['posting_date'] = ('between', [report_filters.get('from_date'), report_filters.get('to_date')])
+
+	return frappe.get_all('Stock Ledger Entry', fields = fields, filters = filters,
+		order_by = 'timestamp(posting_date, posting_time) asc, creation asc')
+
+def get_columns():
+	return [{
+		'label': _('Company'),
+		'fieldtype': 'Link',
+		'fieldname': 'company',
+		'options': 'Company',
+		'width': 120
+	}, {
+		'label': _('Id'),
+		'fieldtype': 'Link',
+		'fieldname': 'name',
+		'options': 'Stock Ledger Entry',
+		'width': 120
+	}, {
+		'label': _('Posting Date'),
+		'fieldtype': 'Date',
+		'fieldname': 'posting_date',
+		'width': 90
+	}, {
+		'label': _('Posting Time'),
+		'fieldtype': 'Time',
+		'fieldname': 'posting_time',
+		'width': 90
+	}, {
+		'label': _('Voucher Type'),
+		'fieldtype': 'Link',
+		'fieldname': 'voucher_type',
+		'options': 'DocType',
+		'width': 100
+	}, {
+		'label': _('Voucher No'),
+		'fieldtype': 'Dynamic Link',
+		'fieldname': 'voucher_no',
+		'options': 'voucher_type',
+		'width': 110
+	}, {
+		'label': _('Item Code'),
+		'fieldtype': 'Link',
+		'fieldname': 'item_code',
+		'options': 'Item',
+		'width': 120
+	}, {
+		'label': _('Warehouse'),
+		'fieldtype': 'Link',
+		'fieldname': 'warehouse',
+		'options': 'Warehouse',
+		'width': 120
+	}, {
+		'label': _('Serial No'),
+		'fieldtype': 'Link',
+		'fieldname': 'serial_no',
+		'options': 'Serial No',
+		'width': 100
+	}, {
+		'label': _('Qty'),
+		'fieldtype': 'Float',
+		'fieldname': 'qty',
+		'width': 80
+	}, {
+		'label': _('Valuation Rate (In / Out)'),
+		'fieldtype': 'Currency',
+		'fieldname': 'valuation_rate',
+		'width': 110
+	}]
\ No newline at end of file
diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/stock/report/incorrect_stock_value_report/__init__.py
similarity index 100%
copy from erpnext/patches/repair_tools/__init__.py
copy to erpnext/stock/report/incorrect_stock_value_report/__init__.py
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js
new file mode 100644
index 0000000..ff42480
--- /dev/null
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Incorrect Stock Value Report"] = {
+	"filters": [
+		{
+			"label": __("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"reqd": 1,
+			"default": frappe.defaults.get_user_default("Company")
+		},
+		{
+			"label": __("Account"),
+			"fieldname": "account",
+			"fieldtype": "Link",
+			"options": "Account",
+			get_query: function() {
+				var company = frappe.query_report.get_filter_value('company');
+				return {
+					filters: {
+						"account_type": "Stock",
+						"company": company
+					}
+				}
+			}
+		},
+		{
+			"label": __("From Date"),
+			"fieldname": "from_date",
+			"fieldtype": "Date"
+		}
+	]
+};
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.json b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.json
new file mode 100644
index 0000000..a7e9f20
--- /dev/null
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-06-22 15:35:05.148177",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-06-22 15:35:05.148177",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Incorrect Stock Value Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Incorrect Stock Value Report",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Accounts Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
new file mode 100644
index 0000000..a724387
--- /dev/null
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
@@ -0,0 +1,141 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import erpnext
+from frappe import _
+from six import iteritems
+from frappe.utils import add_days, today, getdate
+from erpnext.stock.utils import get_stock_value_on
+from erpnext.accounts.utils import get_stock_and_account_balance
+
+def execute(filters=None):
+	if not erpnext.is_perpetual_inventory_enabled(filters.company):
+		frappe.throw(_("Perpetual inventory required for the company {0} to view this report.")
+			.format(filters.company))
+
+	data = get_data(filters)
+	columns = get_columns(filters)
+
+	return columns, data
+
+def get_unsync_date(filters):
+	date = filters.from_date
+	if not date:
+		date = frappe.db.sql(""" SELECT min(posting_date) from `tabStock Ledger Entry`""")
+		date = date[0][0]
+
+	if not date:
+		return
+
+	while getdate(date) < getdate(today()):
+		account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(posting_date=date,
+			company=filters.company, account = filters.account)
+
+		if abs(account_bal - stock_bal) > 0.1:
+			return date
+
+		date = add_days(date, 1)
+
+def get_data(report_filters):
+	from_date = get_unsync_date(report_filters)
+
+	if not from_date:
+		return []
+
+	result = []
+
+	voucher_wise_dict = {}
+	data = frappe.db.sql('''
+			SELECT
+				name, posting_date, posting_time, voucher_type, voucher_no,
+				stock_value_difference, stock_value, warehouse, item_code
+			FROM
+				`tabStock Ledger Entry`
+			WHERE
+				posting_date
+				= %s and company = %s
+				and is_cancelled = 0
+			ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+		''', (from_date, report_filters.company), as_dict=1)
+
+	for d in data:
+		voucher_wise_dict.setdefault((d.item_code, d.warehouse), []).append(d)
+
+	closing_date = add_days(from_date, -1)
+	for key, stock_data in iteritems(voucher_wise_dict):
+		prev_stock_value = get_stock_value_on(posting_date = closing_date, item_code=key[0], warehouse =key[1])
+		for data in stock_data:
+			expected_stock_value = prev_stock_value + data.stock_value_difference
+			if abs(data.stock_value - expected_stock_value) > 0.1:
+				data.difference_value = abs(data.stock_value - expected_stock_value)
+				data.expected_stock_value = expected_stock_value
+				result.append(data)
+
+	return result
+
+def get_columns(filters):
+	return [
+		{
+			"label": _("Stock Ledger ID"),
+			"fieldname": "name",
+			"fieldtype": "Link",
+			"options": "Stock Ledger Entry",
+			"width": "80"
+		},
+		{
+			"label": _("Posting Date"),
+			"fieldname": "posting_date",
+			"fieldtype": "Date"
+		},
+		{
+			"label": _("Posting Time"),
+			"fieldname": "posting_time",
+			"fieldtype": "Time"
+		},
+		{
+			"label": _("Voucher Type"),
+			"fieldname": "voucher_type",
+			"width": "110"
+		},
+		{
+			"label": _("Voucher No"),
+			"fieldname": "voucher_no",
+			"fieldtype": "Dynamic Link",
+			"options": "voucher_type",
+			"width": "110"
+		},
+		{
+			"label": _("Item Code"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": "110"
+		},
+		{
+			"label": _("Warehouse"),
+			"fieldname": "warehouse",
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"width": "110"
+		},
+		{
+			"label": _("Expected Stock Value"),
+			"fieldname": "expected_stock_value",
+			"fieldtype": "Currency",
+			"width": "150"
+		},
+		{
+			"label": _("Stock Value"),
+			"fieldname": "stock_value",
+			"fieldtype": "Currency",
+			"width": "120"
+		},
+		{
+			"label": _("Difference Value"),
+			"fieldname": "difference_value",
+			"fieldtype": "Currency",
+			"width": "150"
+		}
+	]
\ No newline at end of file
diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.py b/erpnext/stock/report/item_price_stock/item_price_stock.py
index 5296211..db7498b 100644
--- a/erpnext/stock/report/item_price_stock/item_price_stock.py
+++ b/erpnext/stock/report/item_price_stock/item_price_stock.py
@@ -89,7 +89,7 @@
 		{conditions}"""
 		.format(conditions=conditions), filters, as_dict=1)
 
-	price_list_names = list(set([item.price_list_name for item in item_results]))
+	price_list_names = list(set(item.price_list_name for item in item_results))
 
 	buying_price_map = get_price_map(price_list_names, buying=1)
 	selling_price_map = get_price_map(price_list_names, selling=1)
diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
index 2f70523..2e13aa0 100644
--- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
+++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py
@@ -66,7 +66,7 @@
 			purpose is NULL
 			or purpose not in ({})
 		)
-	""".format(', '.join([f"'{p}'" for p in purpose_to_exclude]))
+	""".format(', '.join(f"'{p}'" for p in purpose_to_exclude))
 	condition = condition.replace("posting_date", "sle.posting_date")
 
 	consumed_items = frappe.db.sql("""
diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
index 276e42e..8fffbcc 100644
--- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
+++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
@@ -141,7 +141,7 @@
 		return []
 
 	item_conditions_sql = ' and sle.item_code in ({})' \
-		.format(', '.join([frappe.db.escape(i) for i in items]))
+		.format(', '.join(frappe.db.escape(i) for i in items))
 
 	conditions = get_sle_conditions(filters)
 
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index bbd73e9..b6a8063 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -157,7 +157,7 @@
 	item_conditions_sql = ''
 	if items:
 		item_conditions_sql = ' and sle.item_code in ({})'\
-			.format(', '.join([frappe.db.escape(i, percent=False) for i in items]))
+			.format(', '.join(frappe.db.escape(i, percent=False) for i in items))
 
 	conditions = get_conditions(filters)
 
@@ -253,7 +253,7 @@
 def get_item_details(items, sle, filters):
 	item_details = {}
 	if not items:
-		items = list(set([d.item_code for d in sle]))
+		items = list(set(d.item_code for d in sle))
 
 	if not items:
 		return item_details
@@ -291,7 +291,7 @@
 			select parent, warehouse, warehouse_reorder_qty, warehouse_reorder_level
 			from `tabItem Reorder`
 			where parent in ({0})
-		""".format(', '.join([frappe.db.escape(i, percent=False) for i in items])), as_dict=1)
+		""".format(', '.join(frappe.db.escape(i, percent=False) for i in items)), as_dict=1)
 
 	return dict((d.parent + d.warehouse, d) for d in item_reorder_details)
 
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 36996e9..8909f21 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -115,7 +115,7 @@
 	item_conditions_sql = ''
 	if items:
 		item_conditions_sql = 'and sle.item_code in ({})'\
-			.format(', '.join([frappe.db.escape(i) for i in items]))
+			.format(', '.join(frappe.db.escape(i) for i in items))
 
 	sl_entries = frappe.db.sql("""
 		SELECT
@@ -169,7 +169,7 @@
 def get_item_details(items, sl_entries, include_uom):
 	item_details = {}
 	if not items:
-		items = list(set([d.item_code for d in sl_entries]))
+		items = list(set(d.item_code for d in sl_entries))
 
 	if not items:
 		return item_details
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b2825fc..c15d1ed 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -6,13 +6,14 @@
 import erpnext
 import copy
 from frappe import _
-from frappe.utils import cint, flt, cstr, now, get_link_to_form
+from frappe.utils import cint, flt, cstr, now, get_link_to_form, getdate
 from frappe.model.meta import get_field_precision
 from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
 from erpnext.stock.utils import get_bin
 import json
 from six import iteritems
 
+
 # future reposting
 class NegativeStockError(frappe.ValidationError): pass
 class SerialNoExistsInFutureTransaction(frappe.ValidationError):
@@ -22,6 +23,7 @@
 # _exceptions = []
 
 def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
+	from erpnext.controllers.stock_controller import future_sle_exists
 	if sl_entries:
 		from erpnext.stock.utils import update_bin
 
@@ -30,6 +32,9 @@
 			validate_cancellation(sl_entries)
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
+		args = get_args_for_future_sle(sl_entries[0])
+		future_sle_exists(args, sl_entries)
+
 		for sle in sl_entries:
 			if sle.serial_no:
 				validate_serial_no(sle)
@@ -51,8 +56,21 @@
 				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):
+	return frappe._dict({
+		'voucher_type': row.get('voucher_type'),
+		'voucher_no': row.get('voucher_no'),
+		'posting_date': row.get('posting_date'),
+		'posting_time': row.get('posting_time')
+	})
+
 def validate_serial_no(sle):
 	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 	for sn in get_serial_nos(sle.serial_no):
@@ -113,7 +131,13 @@
 	if not args and voucher_type and voucher_no:
 		args = get_args_for_voucher(voucher_type, voucher_no)
 
-	distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args]
+	distinct_item_warehouses = {}
+	for i, d in enumerate(args):
+		distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({
+			"reposting_status": False,
+			"sle": d,
+			"args_idx": i
+		}))
 
 	i = 0
 	while i < len(args):
@@ -122,13 +146,21 @@
 			"warehouse": args[i].warehouse,
 			"posting_date": args[i].posting_date,
 			"posting_time": args[i].posting_time,
-			"creation": args[i].get("creation")
+			"creation": args[i].get("creation"),
+			"distinct_item_warehouses": distinct_item_warehouses
 		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
-		for item_wh, new_sle in iteritems(obj.new_items):
-			if item_wh not in distinct_item_warehouses:
-				args.append(new_sle)
+		distinct_item_warehouses[(args[i].item_code, args[i].warehouse)].reposting_status = True
 
+		if obj.new_items_found:
+			for item_wh, data in iteritems(distinct_item_warehouses):
+				if ('args_idx' not in data and not data.reposting_status) or (data.sle_changed and data.reposting_status):
+					data.args_idx = len(args)
+					args.append(data.sle)
+				elif data.sle_changed and not data.reposting_status:
+					args[data.args_idx] = data.sle
+				
+				data.sle_changed = False
 		i += 1
 
 def get_args_for_voucher(voucher_type, voucher_no):
@@ -169,11 +201,12 @@
 		self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
 		self.get_precision()
 		self.valuation_method = get_valuation_method(self.item_code)
-		self.new_items = {}
+
+		self.new_items_found = False
+		self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
 
 		self.data = frappe._dict()
 		self.initialize_previous_data(self.args)
-
 		self.build()
 
 	def get_precision(self):
@@ -203,7 +236,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"):
@@ -215,29 +248,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
 
@@ -302,11 +312,29 @@
 		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
 			return entries_to_fix
 		elif dependant_sle.item_code != self.item_code:
-			if (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items:
-				self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle
+			self.update_distinct_item_warehouses(dependant_sle)
 			return entries_to_fix
 		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data:
 			return entries_to_fix
+		else:
+			return self.append_future_sle_for_dependant(dependant_sle, entries_to_fix)
+
+	def update_distinct_item_warehouses(self, dependant_sle):
+		key = (dependant_sle.item_code, dependant_sle.warehouse)
+		val = frappe._dict({
+			"sle": dependant_sle
+		})
+		if key not in self.distinct_item_warehouses:
+			self.distinct_item_warehouses[key] = val
+			self.new_items_found = True
+		else:
+			existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
+			if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
+				val.sle_changed = True
+				self.distinct_item_warehouses[key] = val
+				self.new_items_found = True
+
+	def append_future_sle_for_dependant(self, dependant_sle, entries_to_fix):
 		self.initialize_previous_data(dependant_sle)
 
 		args = self.data[dependant_sle.warehouse].previous_sle \
@@ -399,6 +427,7 @@
 		rate = 0
 		# Material Transfer, Repack, Manufacturing
 		if sle.voucher_type == "Stock Entry":
+			self.recalculate_amounts_in_stock_entry(sle.voucher_no)
 			rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate")
 		# Sales and Purchase Return
 		elif sle.voucher_type in ("Purchase Receipt", "Purchase Invoice", "Delivery Note", "Sales Invoice"):
@@ -448,7 +477,11 @@
 		frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate)
 
 		# Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount
-		stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no, for_update=True)
+		if not sle.dependant_sle_voucher_detail_no:
+			self.recalculate_amounts_in_stock_entry(sle.voucher_no)
+
+	def recalculate_amounts_in_stock_entry(self, voucher_no):
+		stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True)
 		stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
 		stock_entry.db_update()
 		for d in stock_entry.items:
@@ -472,7 +505,7 @@
 			frappe.db.set_value("Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate)
 
 		# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
-		if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
+		if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted") == 'Yes':
 			doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
 			doc.update_valuation_rate(reset_outgoing_rate=False)
 			for d in (doc.items + doc.supplied_items):
@@ -521,7 +554,7 @@
 			fields=["purchase_rate", "name", "company"],
 			filters = {'name': ('in', serial_nos)})
 
-		incoming_values = sum([flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company])
+		incoming_values = sum(flt(d.purchase_rate) for d in all_serial_nos if d.company==sle.company)
 
 		# Get rate for serial nos which has been transferred to other company
 		invalid_serial_nos = [d.name for d in all_serial_nos if d.company!=sle.company]
@@ -722,6 +755,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,
@@ -850,9 +912,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
@@ -864,15 +941,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/stock/utils.py b/erpnext/stock/utils.py
index 034d3eb..8a6a3a3 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -177,7 +177,7 @@
 	return bin_obj
 
 def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
-	is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item')
+	is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
 	if is_stock_item:
 		bin = get_bin(args.get("item_code"), args.get("warehouse"))
 		bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json
index 3221dc4..529ce8e 100644
--- a/erpnext/stock/workspace/stock/stock.json
+++ b/erpnext/stock/workspace/stock/stock.json
@@ -15,6 +15,7 @@
  "hide_custom": 0,
  "icon": "stock",
  "idx": 0,
+ "is_default": 0,
  "is_standard": 1,
  "label": "Stock",
  "links": [
@@ -653,9 +654,44 @@
    "link_type": "Report",
    "onboard": 0,
    "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Incorrect Data Report",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Incorrect Serial No Qty and Valuation",
+   "link_to": "Incorrect Serial No Valuation",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Incorrect Balance Qty After Transaction",
+   "link_to": "Incorrect Balance Qty After Transaction",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Stock and Account Value Comparison",
+   "link_to": "Stock and Account Value Comparison",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
   }
  ],
- "modified": "2020-12-01 13:38:36.282890",
+ "modified": "2021-05-13 13:10:24.914983",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json
index bc29821..14712f8 100644
--- a/erpnext/support/doctype/issue/issue.json
+++ b/erpnext/support/doctype/issue/issue.json
@@ -166,7 +166,7 @@
    "options": "Service Level Agreement"
   },
   {
-   "depends_on": "eval: doc.status != 'Replied';",
+   "depends_on": "eval: doc.status != 'Replied' && doc.service_level_agreement;",
    "fieldname": "response_by",
    "fieldtype": "Datetime",
    "label": "Response By",
@@ -180,7 +180,7 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval: doc.status != 'Replied';",
+   "depends_on": "eval: doc.status != 'Replied' && doc.service_level_agreement;",
    "fieldname": "resolution_by",
    "fieldtype": "Datetime",
    "label": "Resolution By",
@@ -410,7 +410,7 @@
  "icon": "fa fa-ticket",
  "idx": 7,
  "links": [],
- "modified": "2021-05-26 10:49:07.574769",
+ "modified": "2021-06-10 03:22:27.098898",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Issue",
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index b068363..9c69deb 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -29,6 +29,9 @@
 		self.update_status()
 		self.set_lead_contact(self.raised_by)
 
+		if not self.service_level_agreement:
+			self.reset_sla_fields()
+
 	def on_update(self):
 		# Add a communication in the issue timeline
 		if self.flags.create_communication and self.via_customer_portal:
@@ -54,6 +57,13 @@
 				self.company = frappe.db.get_value("Lead", self.lead, "company") or \
 					frappe.db.get_default("Company")
 
+	def reset_sla_fields(self):
+		self.agreement_status = ""
+		self.response_by = ""
+		self.resolution_by = ""
+		self.response_by_variance = 0
+		self.resolution_by_variance = 0
+
 	def update_status(self):
 		status = frappe.db.get_value("Issue", self.name, "status")
 		if self.status != "Open" and status == "Open" and not self.first_responded_on:
@@ -511,4 +521,4 @@
 		Converts datetime.time(10, 36, 55, 961454) to datetime.timedelta(seconds=38215)
 	"""
 	import datetime
-	return datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
\ No newline at end of file
+	return datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.py b/erpnext/support/doctype/warranty_claim/warranty_claim.py
index a3428a2..a20e7a8 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.py
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.py
@@ -29,7 +29,7 @@
 			where t2.parent = t1.name and t2.prevdoc_docname = %s and	t1.docstatus!=2""",
 			(self.name))
 		if lst:
-			lst1 = ','.join([x[0] for x in lst])
+			lst1 = ','.join(x[0] for x in lst)
 			frappe.throw(_("Cancel Material Visit {0} before cancelling this Warranty Claim").format(lst1))
 		else:
 			frappe.db.set(self, 'status', 'Cancelled')
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
index 576e0b7..18691fe 100644
--- a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
@@ -22,10 +22,10 @@
 	get_chart_data: function(_columns, result) {
 		return {
 			data: {
-				labels: result.map(d => d[0]),
+				labels: result.map(d => d.creation_date),
 				datasets: [{
 					name: 'First Response Time',
-					values: result.map(d => d[1])
+					values: result.map(d => d.first_response_time)
 				}]
 			},
 			type: "line",
@@ -35,8 +35,7 @@
 						hide_days: 0,
 						hide_seconds: 0
 					};
-					value = frappe.utils.get_formatted_duration(d, duration_options);
-					return value;
+					return frappe.utils.get_formatted_duration(d, duration_options);
 				}
 			}
 		}
diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html
index 84a9430..4482bc1 100644
--- a/erpnext/templates/includes/cart/cart_address.html
+++ b/erpnext/templates/includes/cart/cart_address.html
@@ -99,6 +99,7 @@
 					fieldname: 'country',
 					fieldtype: 'Link',
 					options: 'Country',
+					only_select: true,
 					reqd: 1
 				},
 				{
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/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html
index 3834131..3cfb8d8 100644
--- a/erpnext/templates/includes/transaction_row.html
+++ b/erpnext/templates/includes/transaction_row.html
@@ -13,9 +13,11 @@
 				{{ doc.items_preview }}
 			</div>
 		</div>
-		<div class="col-sm-3 text-right bold">
-			{{ doc.get_formatted("grand_total") }}
-		</div>
+		{% if doc.get('grand_total') %}
+			<div class="col-sm-3 text-right bold">
+				{{ doc.get_formatted("grand_total") }}
+			</div>
+		{% endif %}
 	</div>
 	<a class="transaction-item-link" href="/{{ pathname }}/{{ doc.name }}">Link</a>
 </div>
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/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py
new file mode 100644
index 0000000..8b0ce09
--- /dev/null
+++ b/erpnext/tests/test_subcontracting.py
@@ -0,0 +1,877 @@
+from __future__ import unicode_literals
+import frappe
+import unittest
+import copy
+from frappe.utils import cint
+from collections import defaultdict
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+from erpnext.buying.doctype.purchase_order.purchase_order import (make_rm_stock_entry,
+	make_purchase_receipt, make_purchase_invoice, get_materials_from_supplier)
+
+class TestSubcontracting(unittest.TestCase):
+	def setUp(self):
+		make_subcontract_items()
+		make_raw_materials()
+		make_bom_for_subcontracted_items()
+
+	def test_po_with_bom(self):
+		'''
+			- Set backflush based on BOM
+			- Create subcontracted PO for the item Subcontracted Item SA1 and add same item two times.
+			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+			- Create purchase receipt against the PO and check serial nos and batch no.
+		'''
+
+		set_backflush_based_on('BOM')
+		item_code = 'Subcontracted Item SA1'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 5, 'rate': 100},
+			{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 6, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 5},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 5},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 5},
+			{'item_code': 'Subcontracted SRM Item 1', 'qty': 6},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 6},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 6}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			transferred_detais = itemwise_details.get(key)
+
+			for field in ['qty', 'serial_no', 'batch_no']:
+				if value.get(field):
+					transfer, consumed = (transferred_detais.get(field), value.get(field))
+					if field == 'serial_no':
+						transfer, consumed = (sorted(transfer), sorted(consumed))
+
+					self.assertEqual(transfer, consumed)
+
+	def test_po_with_material_transfer(self):
+		'''
+			- Set backflush based on Material Transfer
+			- Create subcontracted PO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
+			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+			- Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
+			- Create partial purchase receipt against the PO and check serial nos and batch no.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA1', 'qty': 5, 'rate': 100},
+			{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA5', 'qty': 6, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 5, 'main_item_code': 'Subcontracted Item SA1'},
+			{'item_code': 'Subcontracted SRM Item 5', 'qty': 6, 'main_item_code': 'Subcontracted Item SA5'},
+			{'item_code': 'Subcontracted SRM Item 4', 'qty': 6, 'main_item_code': 'Subcontracted Item SA5'}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name
+
+		make_stock_transfer_entry(po_no = po.name,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.remove(pr1.items[1])
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			transferred_detais = itemwise_details.get(key)
+
+			for field in ['qty', 'serial_no', 'batch_no']:
+				if value.get(field):
+					self.assertEqual(value.get(field), transferred_detais.get(field))
+
+		pr2 = make_purchase_receipt(po.name)
+		pr2.submit()
+
+		for key, value in get_supplied_items(pr2).items():
+			transferred_detais = itemwise_details.get(key)
+
+			for field in ['qty', 'serial_no', 'batch_no']:
+				if value.get(field):
+					self.assertEqual(value.get(field), transferred_detais.get(field))
+
+	def test_subcontract_with_same_components_different_fg(self):
+		'''
+			- Set backflush based on Material Transfer
+			- Create subcontracted PO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
+			- Transfer the components from Stores to Supplier warehouse with serial nos.
+			- Transfer extra qty of components for the item Subcontracted Item SA2.
+			- Create partial purchase receipt against the PO and check serial nos and batch no.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA2', 'qty': 5, 'rate': 100},
+			{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA3', 'qty': 6, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA2'},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA3'}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name if d.get('qty') == 5 else po.items[1].name
+
+		make_stock_transfer_entry(po_no = po.name,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 3
+		pr1.remove(pr1.items[1])
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			transferred_detais = itemwise_details.get(key)
+			self.assertEqual(value.qty, 4)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[0:4]))
+
+		pr2 = make_purchase_receipt(po.name)
+		pr2.items[0].qty = 2
+		pr2.remove(pr2.items[1])
+		pr2.submit()
+
+		for key, value in get_supplied_items(pr2).items():
+			transferred_detais = itemwise_details.get(key)
+
+			self.assertEqual(value.qty, 2)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[4:6]))
+
+		pr3 = make_purchase_receipt(po.name)
+		pr3.submit()
+		for key, value in get_supplied_items(pr3).items():
+			transferred_detais = itemwise_details.get(key)
+
+			self.assertEqual(value.qty, 6)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[6:12]))
+
+	def test_return_non_consumed_materials(self):
+		'''
+			- Set backflush based on Material Transfer
+			- Create subcontracted PO for the item Subcontracted Item SA2.
+			- Transfer the components from Stores to Supplier warehouse with serial nos.
+			- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
+			- Create purchase receipt for full qty against the PO and change the qty of raw material.
+			- After that return the non consumed material back to the store from supplier's warehouse.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': 'Subcontracted Item SA2', 'qty': 5, 'rate': 100}]
+		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 6, 'main_item_code': 'Subcontracted Item SA2'}]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.save()
+		pr1.supplied_items[0].consumed_qty = 5
+		pr1.supplied_items[0].serial_no = '\n'.join(sorted(
+			itemwise_details.get('Subcontracted SRM Item 2').get('serial_no')[0:5]
+		))
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			transferred_detais = itemwise_details.get(key)
+			self.assertEqual(value.qty, 5)
+			self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get('serial_no')[0:5]))
+
+		po.load_from_db()
+		self.assertEqual(po.supplied_items[0].consumed_qty, 5)
+		doc = get_materials_from_supplier(po.name, [d.name for d in po.supplied_items])
+		self.assertEqual(doc.items[0].qty, 1)
+		self.assertEqual(doc.items[0].s_warehouse, '_Test Warehouse 1 - _TC')
+		self.assertEqual(doc.items[0].t_warehouse, '_Test Warehouse - _TC')
+		self.assertEqual(get_serial_nos(doc.items[0].serial_no),
+			itemwise_details.get(doc.items[0].item_code)['serial_no'][5:6])
+
+	def test_item_with_batch_based_on_bom(self):
+		'''
+			- Set backflush based on BOM
+			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+			- Transfer the components in multiple batches.
+			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+		'''
+
+		set_backflush_based_on('BOM')
+		item_code = 'Subcontracted Item SA4'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 1}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 2
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 4)
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 2
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 4)
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 2
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 2)
+
+	def test_item_with_batch_based_on_material_transfer(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+			- Transfer the components in multiple batches with extra 2 qty for the batched item.
+			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+			- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA4'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 2
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			qty = 4 if key != 'Subcontracted SRM Item 3' else 6
+			self.assertEqual(value.qty, qty)
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 2
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 4)
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 2
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 2)
+
+	def test_partial_transfer_serial_no_components_based_on_material_transfer(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA2.
+			- Transfer the partial components from Stores to Supplier warehouse with serial nos.
+			- Create partial purchase receipt against the PO and change the qty manually.
+			- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
+			- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA2'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 5}]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 5
+		pr1.save()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, 3)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
+
+		pr1.load_from_db()
+		pr1.supplied_items[0].consumed_qty = 5
+		pr1.supplied_items[0].serial_no = '\n'.join(itemwise_details[pr1.supplied_items[0].rm_item_code]['serial_no'])
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
+
+	def test_incorrect_serial_no_components_based_on_material_transfer(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA2.
+			- Transfer the serialized componenets to the supplier.
+			- Create purchase receipt and change the serial no which is not transferred.
+			- System should throw the error and not allowed to save the purchase receipt.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA2'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 10}]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.save()
+		pr1.supplied_items[0].serial_no = 'ABCD'
+		self.assertRaises(frappe.ValidationError, pr1.save)
+		pr1.delete()
+
+	def test_partial_transfer_batch_based_on_material_transfer(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA6.
+			- Transfer the partial components from Stores to Supplier warehouse with batch.
+			- Create partial purchase receipt against the PO and change the qty manually.
+			- Transfer the remaining components from Stores to Supplier warehouse with batch.
+			- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA6'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 3', 'qty': 5}]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.items[0].qty = 5
+		pr1.save()
+
+		transferred_batch_no = ''
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, 3)
+			transferred_batch_no = details.batch_no
+			self.assertEqual(value.batch_no, details.batch_no)
+
+		pr1.load_from_db()
+		pr1.supplied_items[0].consumed_qty = 5
+		pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(value.batch_no, details.batch_no)
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_receipt(po.name)
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(value.batch_no, details.batch_no)
+
+
+	def test_item_with_batch_based_on_material_transfer_for_purchase_invoice(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+			- Transfer the components in multiple batches with extra 2 qty for the batched item.
+			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+			- In the first purchase receipt the batched raw materials will be consumed 2 extra qty.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA4'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 2
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			qty = 4 if key != 'Subcontracted SRM Item 3' else 6
+			self.assertEqual(value.qty, qty)
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.items[0].qty = 2
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 4)
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 2
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 2)
+
+	def test_partial_transfer_serial_no_components_based_on_material_transfer_for_purchase_invoice(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA2.
+			- Transfer the partial components from Stores to Supplier warehouse with serial nos.
+			- Create partial purchase receipt against the PO and change the qty manually.
+			- Transfer the remaining components from Stores to Supplier warehouse with serial nos.
+			- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA2'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 2', 'qty': 5}]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 5
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.save()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, 3)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no[0:3]))
+
+		pr1.load_from_db()
+		pr1.supplied_items[0].consumed_qty = 5
+		pr1.supplied_items[0].serial_no = '\n'.join(itemwise_details[pr1.supplied_items[0].rm_item_code]['serial_no'])
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
+
+	def test_partial_transfer_batch_based_on_material_transfer_for_purchase_invoice(self):
+		'''
+			- Set backflush based on Material Transferred for Subcontract
+			- Create subcontracted PO for the item Subcontracted Item SA6.
+			- Transfer the partial components from Stores to Supplier warehouse with batch.
+			- Create partial purchase receipt against the PO and change the qty manually.
+			- Transfer the remaining components from Stores to Supplier warehouse with batch.
+			- Create purchase receipt for remaining qty against the PO and change the qty manually.
+		'''
+
+		set_backflush_based_on('Material Transferred for Subcontract')
+		item_code = 'Subcontracted Item SA6'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 3', 'qty': 5}]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 5
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.save()
+
+		transferred_batch_no = ''
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, 3)
+			transferred_batch_no = details.batch_no
+			self.assertEqual(value.batch_no, details.batch_no)
+
+		pr1.load_from_db()
+		pr1.supplied_items[0].consumed_qty = 5
+		pr1.supplied_items[0].batch_no = list(transferred_batch_no.keys())[0]
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(value.batch_no, details.batch_no)
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			details = itemwise_details.get(key)
+			self.assertEqual(value.qty, details.qty)
+			self.assertEqual(value.batch_no, details.batch_no)
+
+	def test_item_with_batch_based_on_bom_for_purchase_invoice(self):
+		'''
+			- Set backflush based on BOM
+			- Create subcontracted PO for the item Subcontracted Item SA4 (has batch no).
+			- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
+			- Transfer the components in multiple batches.
+			- Create the 3 purchase receipt against the PO and split Subcontracted Items into two batches.
+			- Keep the qty as 2 for Subcontracted Item in the purchase receipt.
+		'''
+
+		set_backflush_based_on('BOM')
+		item_code = 'Subcontracted Item SA4'
+		items = [{'warehouse': '_Test Warehouse - _TC', 'item_code': item_code, 'qty': 10, 'rate': 100}]
+
+		rm_items = [{'item_code': 'Subcontracted SRM Item 1', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 2', 'qty': 10},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 3},
+			{'item_code': 'Subcontracted SRM Item 3', 'qty': 1}
+		]
+
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+		po = create_purchase_order(rm_items = items, is_subcontracted="Yes",
+			supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		for d in rm_items:
+			d['po_detail'] = po.items[0].name
+
+		make_stock_transfer_entry(po_no = po.name, main_item_code=item_code,
+			rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details))
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 2
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 4)
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 2
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		add_second_row_in_pr(pr1)
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 4)
+
+		pr1 = make_purchase_invoice(po.name)
+		pr1.update_stock = 1
+		pr1.items[0].qty = 2
+		pr1.items[0].expense_account = 'Stock Adjustment - _TC'
+		pr1.save()
+		pr1.submit()
+
+		for key, value in get_supplied_items(pr1).items():
+			self.assertEqual(value.qty, 2)
+
+def add_second_row_in_pr(pr):
+	item_dict = {}
+	for column in ['item_code', 'item_name', 'qty', 'uom', 'warehouse', 'stock_uom',
+		'purchase_order', 'purchase_order_item', 'conversion_factor', 'rate', 'expense_account', 'po_detail']:
+		item_dict[column] = pr.items[0].get(column)
+
+	pr.append('items', item_dict)
+	pr.set_missing_values()
+
+def get_supplied_items(pr_doc):
+	supplied_items = {}
+	for row in pr_doc.get('supplied_items'):
+		if row.rm_item_code not in supplied_items:
+			supplied_items.setdefault(row.rm_item_code,
+				frappe._dict({'qty': 0, 'serial_no': [], 'batch_no': defaultdict(float)}))
+
+		details = supplied_items[row.rm_item_code]
+		update_item_details(row, details)
+
+	return supplied_items
+
+def make_stock_in_entry(**args):
+	args = frappe._dict(args)
+
+	items = {}
+	for row in args.rm_items:
+		row = frappe._dict(row)
+
+		doc = make_stock_entry(target=row.warehouse or '_Test Warehouse - _TC',
+			item_code=row.item_code, qty=row.qty or 1, basic_rate=row.rate or 100)
+
+		if row.item_code not in items:
+			items.setdefault(row.item_code, frappe._dict({'qty': 0, 'serial_no': [], 'batch_no': defaultdict(float)}))
+
+		child_row = doc.items[0]
+		details = items[child_row.item_code]
+		update_item_details(child_row, details)
+
+	return items
+
+def update_item_details(child_row, details):
+	details.qty += (child_row.get('qty') if child_row.doctype == 'Stock Entry Detail'
+		else child_row.get('consumed_qty'))
+
+	if child_row.serial_no:
+		details.serial_no.extend(get_serial_nos(child_row.serial_no))
+
+	if child_row.batch_no:
+		details.batch_no[child_row.batch_no] += (child_row.get('qty') or child_row.get('consumed_qty'))
+
+def make_stock_transfer_entry(**args):
+	args = frappe._dict(args)
+
+	items = []
+	for row in args.rm_items:
+		row = frappe._dict(row)
+
+		item = {'item_code': row.main_item_code or args.main_item_code, 'rm_item_code': row.item_code,
+			'qty': row.qty or 1, 'item_name': row.item_code, 'rate': row.rate or 100,
+			'stock_uom': row.stock_uom or 'Nos', 'warehouse': row.warehuose or '_Test Warehouse - _TC'}
+
+		item_details = args.itemwise_details.get(row.item_code)
+
+		if item_details and item_details.serial_no:
+			serial_nos = item_details.serial_no[0:cint(row.qty)]
+			item['serial_no'] = '\n'.join(serial_nos)
+			item_details.serial_no = list(set(item_details.serial_no) - set(serial_nos))
+
+		if item_details and item_details.batch_no:
+			for batch_no, batch_qty in item_details.batch_no.items():
+				if batch_qty >= row.qty:
+					item['batch_no'] = batch_no
+					item_details.batch_no[batch_no] -= row.qty
+					break
+
+		items.append(item)
+
+	ste_dict = make_rm_stock_entry(args.po_no, items)
+	doc = frappe.get_doc(ste_dict)
+	doc.insert()
+	doc.submit()
+
+	return doc
+
+def make_subcontract_items():
+	sub_contracted_items = {'Subcontracted Item SA1': {}, 'Subcontracted Item SA2': {}, 'Subcontracted Item SA3': {},
+		'Subcontracted Item SA4': {'has_batch_no': 1, 'create_new_batch': 1, 'batch_number_series': 'SBAT.####'},
+		'Subcontracted Item SA5': {}, 'Subcontracted Item SA6': {}}
+
+	for item, properties in sub_contracted_items.items():
+		if not frappe.db.exists('Item', item):
+			properties.update({'is_stock_item': 1, 'is_sub_contracted_item': 1})
+			make_item(item, properties)
+
+def make_raw_materials():
+	raw_materials = {'Subcontracted SRM Item 1': {},
+		'Subcontracted SRM Item 2': {'has_serial_no': 1, 'serial_no_series': 'SRI.####'},
+		'Subcontracted SRM Item 3': {'has_batch_no': 1, 'create_new_batch': 1, 'batch_number_series': 'BAT.####'},
+		'Subcontracted SRM Item 4': {'has_serial_no': 1, 'serial_no_series': 'SRII.####'},
+		'Subcontracted SRM Item 5': {'has_serial_no': 1, 'serial_no_series': 'SRII.####'}}
+
+	for item, properties in raw_materials.items():
+		if not frappe.db.exists('Item', item):
+			properties.update({'is_stock_item': 1})
+			make_item(item, properties)
+
+def make_bom_for_subcontracted_items():
+	boms = {
+		'Subcontracted Item SA1': ['Subcontracted SRM Item 1', 'Subcontracted SRM Item 2', 'Subcontracted SRM Item 3'],
+		'Subcontracted Item SA2': ['Subcontracted SRM Item 2'],
+		'Subcontracted Item SA3': ['Subcontracted SRM Item 2'],
+		'Subcontracted Item SA4': ['Subcontracted SRM Item 1', 'Subcontracted SRM Item 2', 'Subcontracted SRM Item 3'],
+		'Subcontracted Item SA5': ['Subcontracted SRM Item 5'],
+		'Subcontracted Item SA6': ['Subcontracted SRM Item 3']
+	}
+
+	for item_code, raw_materials in boms.items():
+		if not frappe.db.exists('BOM', {'item': item_code}):
+			make_bom(item=item_code, raw_materials=raw_materials, rate=100)
+
+def set_backflush_based_on(based_on):
+	frappe.db.set_value('Buying Settings', None,
+		'backflush_raw_materials_of_subcontract_based_on', based_on)
\ No newline at end of file
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index 16ecd51..11eb6af 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -1,7 +1,8 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
-from __future__ import unicode_literals
+import copy
+from contextlib import contextmanager
 
 import frappe
 
@@ -41,3 +42,38 @@
 	contact.add_email("test_contact_customer@example.com", is_primary=True)
 	contact.add_phone("+91 0000000000", is_primary_phone=True)
 	contact.insert()
+
+
+@contextmanager
+def change_settings(doctype, settings_dict):
+	""" A context manager to ensure that settings are changed before running
+	function and restored after running it regardless of exceptions occured.
+	This is useful in tests where you want to make changes in a function but
+	don't retain those changes.
+	import and use as decorator to cover full function or using `with` statement.
+
+	example:
+	@change_settings("Stock Settings", {"item_naming_by": "Naming Series"})
+	def test_case(self):
+		...
+	"""
+
+	try:
+		settings = frappe.get_doc(doctype)
+		# remember setting
+		previous_settings = copy.deepcopy(settings_dict)
+		for key in previous_settings:
+			previous_settings[key] = getattr(settings, key)
+
+		# change setting
+		for key, value in settings_dict.items():
+			setattr(settings, key, value)
+		settings.save()
+		yield # yield control to calling function
+
+	finally:
+		# restore settings
+		settings = frappe.get_doc(doctype)
+		for key, value in previous_settings.items():
+			setattr(settings, key, value)
+		settings.save()
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index 66d6cd3..70b4176 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -131,6 +131,6 @@
 	if frappe.db.exists("Product Bundle", item_code):
 		items = frappe.get_doc("Product Bundle", item_code).get_all_children()
 		bundle_warehouse = frappe.db.get_value('Item', item_code, item_warehouse_field)
-		return all([ get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ])
+		return all(get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items)
 	else:
 		return 1
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index f99da58..db99726 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -147,7 +147,7 @@
 		if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype):
 			fieldname = self.prev_link_mapper[for_doctype]["fieldname"]
 
-			values = filter(None, tuple([item.as_dict()[fieldname] for item in self.items]))
+			values = filter(None, tuple(item.as_dict()[fieldname] for item in self.items))
 
 			if values:
 				ret = {
@@ -180,7 +180,7 @@
 	if isinstance(qty_fields, string_types):
 		qty_fields = [qty_fields]
 
-	distinct_uoms = list(set([d.get(uom_field) for d in doc.get_all_children()]))
+	distinct_uoms = list(set(d.get(uom_field) for d in doc.get_all_children()))
 	integer_uoms = list(filter(lambda uom: frappe.db.get_value("UOM", uom,
 		"must_be_whole_number", cache=True) or None, distinct_uoms))
 
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)
diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html
index 15afb09..d22ef66 100644
--- a/erpnext/www/lms/content.html
+++ b/erpnext/www/lms/content.html
@@ -64,6 +64,7 @@
 	</div>
 	<div class="lms-title">
 		<h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
+		<div class="lms-timer float-right fond-weight-bold hide"></div>
 	</div>
 {% endmacro %}