Merge branch 'campaign-module' of https://github.com/anupamvs/erpnext into campaign-module
diff --git a/.github/helper/semgrep_rules/frappe_correctness.yml b/.github/helper/semgrep_rules/frappe_correctness.yml
index faab334..d9603e8 100644
--- a/.github/helper/semgrep_rules/frappe_correctness.yml
+++ b/.github/helper/semgrep_rules/frappe_correctness.yml
@@ -98,8 +98,6 @@
   languages: [python]
   severity: WARNING
   paths:
-      exclude:
-        - test_*.py
       include:
         - "*/**/doctype/*"
 
diff --git a/.github/helper/semgrep_rules/security.yml b/.github/helper/semgrep_rules/security.yml
index 5a5098b..8b21979 100644
--- a/.github/helper/semgrep_rules/security.yml
+++ b/.github/helper/semgrep_rules/security.yml
@@ -8,18 +8,3 @@
     dynamic content. Avoid it or use safe_eval().
   languages: [python]
   severity: ERROR
-
-- id: frappe-sqli-format-strings
-  patterns:
-    - pattern-inside: |
-        @frappe.whitelist()
-        def $FUNC(...):
-            ...
-    - pattern-either:
-        - pattern: frappe.db.sql("..." % ...)
-        - pattern: frappe.db.sql(f"...", ...)
-        - pattern: frappe.db.sql("...".format(...), ...)
-  message: |
-      Detected use of raw string formatting for SQL queries. This can lead to sql injection vulnerabilities. Refer security guidelines - https://github.com/frappe/erpnext/wiki/Code-Security-Guidelines
-  languages: [python]
-  severity: WARNING
diff --git a/.github/stale.yml b/.github/stale.yml
index dabc66eb..9322ae8 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,11 +1,11 @@
 # Configuration for probot-stale - https://github.com/probot/stale
 
 # Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 30
+daysUntilStale: 15
 
 # Number of days of inactivity before a stale Issue or Pull Request is closed.
 # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 7
+daysUntilClose: 3
 
 # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
 exemptLabels:
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 7c6b843..cc98f45 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -1,16 +1,25 @@
 name: Backport
 on:
-  pull_request:
+  pull_request_target:
     types:
       - closed
       - labeled
 
 jobs:
-  backport:
-    runs-on: ubuntu-18.04
-    name: Backport
+  main:
+    runs-on: ubuntu-latest
     steps:
-      - name: Backport
-        uses: tibdex/backport@v1
+      - name: Checkout Actions
+        uses: actions/checkout@v2
         with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+          repository: "ankush/backport"
+          path: ./actions
+          ref: develop
+      - name: Install Actions
+        run: npm install --production --prefix ./actions
+      - name: Run backport
+        uses: ./actions/backport
+        with:
+          token: ${{secrets.BACKPORT_BOT_TOKEN}}
+          labelsToAdd: "backport"
+          title: "{{originalTitle}}"
diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml
index 389524e..e27b406 100644
--- a/.github/workflows/semgrep.yml
+++ b/.github/workflows/semgrep.yml
@@ -1,34 +1,18 @@
 name: Semgrep
 
 on:
-  pull_request:
-    branches:
-      - develop
-      - version-13-hotfix
-      - version-13-pre-release
+  pull_request: { }
+
 jobs:
   semgrep:
     name: Frappe Linter
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
-    - name: Setup python3
-      uses: actions/setup-python@v2
-      with:
-        python-version: 3.8
-
-    - name: Setup semgrep
-      run: |
-        python -m pip install -q semgrep
-        git fetch origin $GITHUB_BASE_REF:$GITHUB_BASE_REF -q
-
-    - name: Semgrep errors
-      run: |
-        files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF)
-        [[ -d .github/helper/semgrep_rules ]] && semgrep --severity ERROR --config=.github/helper/semgrep_rules --quiet --error $files
-        semgrep --config="r/python.lang.correctness" --quiet --error $files
-
-    - name: Semgrep warnings
-      run: |
-        files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF)
-        [[ -d .github/helper/semgrep_rules ]] && semgrep --severity WARNING --severity INFO --config=.github/helper/semgrep_rules --quiet $files
+      - uses: actions/checkout@v2
+      - uses: returntocorp/semgrep-action@v1
+        env:
+            SEMGREP_TIMEOUT: 120
+        with:
+            config: >-
+              r/python.lang.correctness
+              .github/helper/semgrep_rules
diff --git a/CODEOWNERS b/CODEOWNERS
index 7cf65a7..a4a14de 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -3,16 +3,33 @@
 # These owners will be the default owners for everything in
 # the repo. Unless a later match takes precedence,
 
-manufacturing/          @rohitwaghchaure @marination
-accounts/               @deepeshgarg007 @nextchamp-saqib
-loan_management/        @deepeshgarg007 @rohitwaghchaure
-pos*                    @nextchamp-saqib @rohitwaghchaure
-assets/                 @nextchamp-saqib @deepeshgarg007
-stock/                  @marination @rohitwaghchaure
-buying/                 @marination @deepeshgarg007
-hr/                     @Anurag810 @rohitwaghchaure
-projects/               @hrwX @nextchamp-saqib
-support/                @hrwX @marination
-healthcare/             @ruchamahabal @marination
-erpnext_integrations/   @Mangesh-Khairnar @nextchamp-saqib
-requirements.txt        @gavindsouza
+erpnext/accounts/               @nextchamp-saqib @deepeshgarg007
+erpnext/assets/                 @nextchamp-saqib @deepeshgarg007
+erpnext/erpnext_integrations/   @nextchamp-saqib
+erpnext/loan_management/        @nextchamp-saqib @deepeshgarg007
+erpnext/regional                @nextchamp-saqib @deepeshgarg007
+erpnext/selling                 @nextchamp-saqib @deepeshgarg007
+erpnext/support/                @nextchamp-saqib @deepeshgarg007
+pos*                            @nextchamp-saqib
+
+erpnext/buying/                 @marination @rohitwaghchaure @ankush
+erpnext/e_commerce/             @marination
+erpnext/maintenance/            @marination @rohitwaghchaure
+erpnext/manufacturing/          @marination @rohitwaghchaure @ankush
+erpnext/portal/                 @marination
+erpnext/quality_management/     @marination @rohitwaghchaure
+erpnext/shopping_cart/          @marination
+erpnext/stock/                  @marination @rohitwaghchaure @ankush
+
+erpnext/crm/                    @ruchamahabal @pateljannat
+erpnext/education/              @ruchamahabal @pateljannat
+erpnext/healthcare/             @ruchamahabal @pateljannat @chillaranand
+erpnext/hr/                     @ruchamahabal @pateljannat
+erpnext/non_profit/             @ruchamahabal
+erpnext/payroll                 @ruchamahabal @pateljannat
+erpnext/projects/               @ruchamahabal @pateljannat
+
+erpnext/controllers             @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
+
+.github/                        @surajshetty3416 @ankush
+requirements.txt                @gavindsouza
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 0c96d32..a181c2d 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
 from erpnext.hooks import regional_overrides
 from frappe.utils import getdate
 
-__version__ = '13.6.0'
+__version__ = '13.7.1'
 
 def get_default_company(user=None):
 	'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index 3b764aa..4fd8413 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -13,7 +13,8 @@
 from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file, read_xls_file_from_attached_file
 
 class ChartofAccountsImporter(Document):
-	pass
+	def validate(self):
+		validate_accounts(self.import_file)
 
 @frappe.whitelist()
 def validate_company(company):
@@ -301,28 +302,27 @@
 		if account["parent_account"] and accounts_dict.get(account["parent_account"]):
 			accounts_dict[account["parent_account"]]["is_group"] = 1
 
-	message = validate_root(accounts_dict)
-	if message: return message
-	message = validate_account_types(accounts_dict)
-	if message: return message
+	validate_root(accounts_dict)
+
+	validate_account_types(accounts_dict)
 
 	return [True, len(accounts)]
 
 def validate_root(accounts):
 	roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
 	if len(roots) < 4:
-		return _("Number of root accounts cannot be less than 4")
+		frappe.throw(_("Number of root accounts cannot be less than 4"))
 
 	error_messages = []
 
 	for account in roots:
 		if not account.get("root_type") and account.get("account_name"):
-			error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
+			error_messages.append(_("Please enter Root Type for account- {0}").format(account.get("account_name")))
 		elif account.get("root_type") not in get_root_types() and account.get("account_name"):
-			error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
+			error_messages.append(_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(account.get("account_name")))
 
 	if error_messages:
-		return "<br>".join(error_messages)
+		frappe.throw("<br>".join(error_messages))
 
 def get_root_types():
 	return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
@@ -356,7 +356,7 @@
 
 	missing = list(set(account_types_for_ledger) - set(account_types))
 	if missing:
-		return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
+		frappe.throw(_("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)))
 
 	account_types_for_group = ["Bank", "Cash", "Stock"]
 	# fix logic bug
@@ -364,7 +364,7 @@
 
 	missing = list(set(account_types_for_group) - set(account_groups))
 	if missing:
-		return _("Please identify/create Account (Group) for type - {0}").format(' , '.join(missing))
+		frappe.throw(_("Please identify/create Account (Group) for type - {0}").format(' , '.join(missing)))
 
 def unset_existing_data(company):
 	linked = frappe.db.sql('''select fieldname from tabDocField
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index c6c6892..1ef512a 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -25,7 +25,7 @@
 
 	def validate_amount(self):
 		amounts = calculate_interest_and_amount(
-			self.posting_date, self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
+			self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
 		if self.interest_amount != amounts.get('interest_amount'):
 			self.interest_amount = flt(amounts.get('interest_amount'), self.precision('interest_amount'))
 		if self.dunning_amount != amounts.get('dunning_amount'):
@@ -91,13 +91,13 @@
 			for dunning in dunnings:
 				frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved')
 
-def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
+def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
 	interest_amount = 0
-	grand_total = 0
+	grand_total = flt(outstanding_amount) + flt(dunning_fee)
 	if rate_of_interest:
 		interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100
 		interest_amount = (interest_per_year * cint(overdue_days)) / 365
-		grand_total = flt(outstanding_amount) + flt(interest_amount) + flt(dunning_fee)
+		grand_total += flt(interest_amount)
 	dunning_amount = flt(interest_amount) + flt(dunning_fee)
 	return {
 		'interest_amount': interest_amount,
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index e2d4d82..7fc2e4b 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,19 @@
 	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 +92,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 +128,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() 
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 5619321..f2b0a8c 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -27,6 +27,9 @@
 		if not (self.company and self.posting_date):
 			frappe.throw(_("Please select Company and Posting Date to getting entries"))
 
+	def on_cancel(self):
+		self.ignore_linked_doctypes = ('GL Entry')
+
 	@frappe.whitelist()
 	def check_journal_entry_condition(self):
 		total_debit = frappe.db.get_value("Journal Entry Account", {
@@ -99,10 +102,12 @@
 					sum(debit) - sum(credit) as balance
 				from `tabGL Entry`
 				where account in (%s)
-				group by account, party_type, party
+				and posting_date <= %s
+				and is_cancelled = 0
+				group by account, NULLIF(party_type,''), NULLIF(party,'')
 				having sum(debit) != sum(credit)
 				order by account
-			""" % ', '.join(['%s']*len(accounts)), tuple(accounts), as_dict=1)
+			""" % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
 
 		return account_details
 
@@ -143,9 +148,9 @@
 				"party_type": d.get("party_type"),
 				"party": d.get("party"),
 				"account_currency": d.get("account_currency"),
-				"balance": d.get("balance_in_account_currency"),
-				dr_or_cr: abs(d.get("balance_in_account_currency")),
-				"exchange_rate":d.get("new_exchange_rate"),
+				"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
+				dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
+				"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
 				"reference_type": "Exchange Rate Revaluation",
 				"reference_name": self.name,
 				})
@@ -154,9 +159,9 @@
 				"party_type": d.get("party_type"),
 				"party": d.get("party"),
 				"account_currency": d.get("account_currency"),
-				"balance": d.get("balance_in_account_currency"),
-				reverse_dr_or_cr: abs(d.get("balance_in_account_currency")),
-				"exchange_rate": d.get("current_exchange_rate"),
+				"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
+				reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
+				"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
 				"reference_type": "Exchange Rate Revaluation",
 				"reference_name": self.name
 				})
@@ -185,9 +190,9 @@
 
 	account_details = {}
 	company_currency = erpnext.get_company_currency(company)
-	balance = get_balance_on(account, party_type=party_type, party=party, in_account_currency=False)
+	balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
 	if balance:
-		balance_in_account_currency = get_balance_on(account, party_type=party_type, party=party)
+		balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
 		current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
 		new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
 		new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 51f18a5..6f362c1 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -667,6 +667,7 @@
   {
    "fieldname": "base_paid_amount_after_tax",
    "fieldtype": "Currency",
+   "hidden": 1,
    "label": "Paid Amount After Tax (Company Currency)",
    "options": "Company:company:default_currency",
    "read_only": 1
@@ -693,21 +694,25 @@
    "depends_on": "eval:doc.received_amount && doc.payment_type != 'Internal Transfer'",
    "fieldname": "received_amount_after_tax",
    "fieldtype": "Currency",
+   "hidden": 1,
    "label": "Received Amount After Tax",
-   "options": "paid_to_account_currency"
+   "options": "paid_to_account_currency",
+   "read_only": 1
   },
   {
    "depends_on": "doc.received_amount",
    "fieldname": "base_received_amount_after_tax",
    "fieldtype": "Currency",
+   "hidden": 1,
    "label": "Received Amount After Tax (Company Currency)",
-   "options": "Company:company:default_currency"
+   "options": "Company:company:default_currency",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-22 20:37:06.154206",
+ "modified": "2021-07-09 08:58:15.008761",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 0c21aae..46904f7 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -183,6 +183,13 @@
 					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.db_set(field, value)
 
@@ -404,9 +411,15 @@
 		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
+
+		for reference in self.get("references"):
+			net_total_for_tds = 0
+			if reference.reference_doctype == 'Purchase Order':
+				net_total_for_tds += flt(frappe.db.get_value('Purchase Order', reference.reference_name, 'net_total'))
+		
+			if net_total_for_tds:
+				net_total = net_total_for_tds
 
 		# Adding args as purchase invoice to get TDS amount
 		args = frappe._dict({
@@ -423,7 +436,7 @@
 			return
 
 		tax_withholding_details.update({
-			'included_in_paid_amount': included_in_paid_amount,
+			'add_deduct_tax': 'Add',
 			'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company)
 		})
 
@@ -512,16 +525,19 @@
 		self.unallocated_amount = 0
 		if self.party:
 			total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
+			included_taxes = self.get_included_taxes()
 			if self.payment_type == "Receive" \
-				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 -
+				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.received_amount + total_deductions -
 					self.base_total_allocated_amount) / self.source_exchange_rate
+				self.unallocated_amount -= included_taxes
 			elif self.payment_type == "Pay" \
-				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 +
+				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
+				self.unallocated_amount -= included_taxes
 
 	def set_difference_amount(self):
 		base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
@@ -530,17 +546,29 @@
 		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_after_tax
+			self.difference_amount = base_party_amount - self.base_received_amount
 		elif self.payment_type == "Pay":
-			self.difference_amount = self.base_paid_amount_after_tax - base_party_amount
+			self.difference_amount = self.base_paid_amount - base_party_amount
 		else:
-			self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax)
+			self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
 
 		total_deductions = sum(flt(d.amount) for d in self.get("deductions"))
+		included_taxes = self.get_included_taxes()
 
-		self.difference_amount = flt(self.difference_amount - total_deductions,
+		self.difference_amount = flt(self.difference_amount - total_deductions - included_taxes,
 			self.precision("difference_amount"))
 
+	def get_included_taxes(self):
+		included_taxes = 0
+		for tax in self.get('taxes'):
+			if tax.included_in_paid_amount:
+				if tax.add_deduct_tax == 'Add':
+					included_taxes += tax.base_tax_amount
+				else:
+					included_taxes -= tax.base_tax_amount
+
+		return included_taxes
+
 	# Paid amount is auto allocated in the reference document by default.
 	# Clear the reference document which doesn't have allocated amount on validate so that form can be loaded fast
 	def clear_unallocated_reference_document_rows(self):
@@ -664,8 +692,8 @@
 				gl_entries.append(gle)
 
 			if self.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()
 
@@ -683,8 +711,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_after_tax,
-					"credit": self.base_paid_amount_after_tax,
+					"credit_in_account_currency": self.paid_amount,
+					"credit": self.base_paid_amount,
 					"cost_center": self.cost_center
 				}, item=self)
 			)
@@ -694,8 +722,8 @@
 					"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_after_tax,
-					"debit": self.base_received_amount_after_tax,
+					"debit_in_account_currency": self.received_amount,
+					"debit": self.base_received_amount,
 					"cost_center": self.cost_center
 				}, item=self)
 			)
@@ -708,35 +736,42 @@
 
 			if self.payment_type in ('Pay', 'Internal Transfer'):
 				dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
+				against = self.party or self.paid_from
 			elif self.payment_type == 'Receive':
 				dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
+				against = self.party or self.paid_to
 
 			payment_or_advance_account = self.get_party_account_for_taxes()
+			tax_amount = d.tax_amount
+			base_tax_amount = d.base_tax_amount
+
+			if self.advance_tax_account:
+				tax_amount = -1 * tax_amount
+				base_tax_amount = -1 * base_tax_amount
 
 			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
+					"against": against,
+					dr_or_cr: tax_amount,
+					dr_or_cr + "_in_account_currency": 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))
+			if not d.included_in_paid_amount or self.advance_tax_account:
+				gl_entries.append(
+					self.get_gl_dict({
+						"account": payment_or_advance_account,
+						"against": against,
+						dr_or_cr: -1 * tax_amount,
+						dr_or_cr + "_in_account_currency": -1 * base_tax_amount
+						if account_currency==self.company_currency
+						else d.tax_amount,
+						"cost_center": self.cost_center,
+					}, account_currency, item=d))
 
 	def add_deductions_gl_entries(self, gl_entries):
 		for d in self.get("deductions"):
@@ -760,9 +795,9 @@
 		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
+		elif self.payment_type in ('Pay', 'Internal Transfer'):
+			return self.paid_from
 
 	def update_advance_paid(self):
 		if self.payment_type in ("Receive", "Pay") and self.party:
@@ -806,10 +841,17 @@
 
 		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)
@@ -1634,12 +1676,6 @@
 			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_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/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index c1cc092..b99d75e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -451,6 +451,7 @@
 			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)
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 189260a..db6f143 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -974,6 +974,120 @@
 		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)
+
+		original_account = frappe.db.get_value("Company", "_Test Company", "exchange_gain_loss_account")
+		frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", "Exchange Gain/Loss - _TC")
+
+		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)
+		frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account)
+
 	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
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/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index e7dd6b8..0a9a105 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -48,6 +48,8 @@
   "shipping_address",
   "company_address",
   "company_address_display",
+  "dispatch_address_name",
+  "dispatch_address",
   "currency_and_price_list",
   "currency",
   "conversion_rate",
@@ -1966,6 +1968,21 @@
    "fieldname": "disable_rounded_total",
    "fieldtype": "Check",
    "label": "Disable Rounded Total"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "dispatch_address_name",
+   "fieldtype": "Link",
+   "label": "Dispatch Address Name",
+   "options": "Address",
+   "print_hide": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "dispatch_address",
+   "fieldtype": "Small Text",
+   "label": "Dispatch Address",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
@@ -1978,7 +1995,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2021-05-20 22:48:33.988881",
+ "modified": "2021-07-08 14:03:55.502522",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 55a5b99..8889913 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -13,7 +13,7 @@
 from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
 from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
 from erpnext.assets.doctype.asset.depreciation \
-	import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
+	import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain
 from erpnext.stock.doctype.batch.batch import set_batch_nos
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
 from erpnext.setup.doctype.company.company import update_company_current_month_sales
@@ -149,7 +149,7 @@
 					if self.update_stock:
 						frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
 
-					elif asset.status in ("Scrapped", "Cancelled", "Sold"):
+					elif asset.status in ("Scrapped", "Cancelled") or (asset.status == "Sold" and not self.is_return):
 						frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
 
 	def validate_item_cost_centers(self):
@@ -840,6 +840,7 @@
 		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)
@@ -917,22 +918,33 @@
 		for item in self.get("items"):
 			if flt(item.base_net_amount, item.precision("base_net_amount")):
 				if item.is_fixed_asset:
-					asset = frappe.get_doc("Asset", item.asset)
-
+					if item.get('asset'):
+						asset = frappe.get_doc("Asset", item.asset)
+					else:
+						frappe.throw(_(
+							"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name), 
+							title=_("Missing Asset")
+						)
 					if (len(asset.finance_books) > 1 and not item.finance_book
 						and asset.finance_books[0].finance_book):
 						frappe.throw(_("Select finance book for the item {0} at row {1}")
 							.format(item.item_code, item.idx))
 
-					fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
-						item.base_net_amount, item.finance_book)
+					if self.is_return:
+						fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
+							item.base_net_amount, item.finance_book)
+						asset.db_set("disposal_date", None)
+					else:
+						fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
+							item.base_net_amount, item.finance_book)
+						asset.db_set("disposal_date", self.posting_date)
 
 					for gle in fixed_asset_gl_entries:
 						gle["against"] = self.customer
 						gl_entries.append(self.get_gl_dict(gle, item=item))
 
-					asset.db_set("disposal_date", self.posting_date)
-					asset.set_status("Sold" if self.docstatus==1 else None)
+					self.set_asset_status(asset)
+				
 				else:
 					# Do not book income for transfer within same company
 					if not self.is_internal_transfer():
@@ -958,6 +970,12 @@
 			erpnext.is_perpetual_inventory_enabled(self.company):
 			gl_entries += super(SalesInvoice, self).get_gl_entries()
 
+	def set_asset_status(self, asset):
+		if self.is_return:
+			asset.set_status()
+		else: 	
+			asset.set_status("Sold" if self.docstatus==1 else None)
+
 	def make_loyalty_point_redemption_gle(self, gl_entries):
 		if cint(self.redeem_loyalty_points):
 			gl_entries.append(
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index fe531d3..c6e6e3d 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -10,6 +10,7 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
 from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
+from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
 from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
 from frappe.model.naming import make_autoname
@@ -1069,6 +1070,36 @@
 		self.assertFalse(si1.outstanding_amount)
 		self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
 
+	def test_gle_made_when_asset_is_returned(self):
+		create_asset_data()
+		asset = create_asset(item_code="Macbook Pro")
+	
+		si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000)
+		return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000)
+
+		disposal_account = frappe.get_cached_value("Company", "_Test Company", "disposal_account")
+
+		# Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000
+		loss_for_si = frappe.get_all(
+			"GL Entry", 
+			filters = {
+				"voucher_no": si.name,
+				"account": disposal_account
+			},
+			fields = ["credit", "debit"]
+		)[0]
+
+		loss_for_return_si = frappe.get_all(
+			"GL Entry", 
+			filters = {
+				"voucher_no": return_si.name,
+				"account": disposal_account
+			},
+			fields = ["credit", "debit"]
+		)[0]
+
+		self.assertEqual(loss_for_si['credit'], loss_for_return_si['debit'])
+		self.assertEqual(loss_for_si['debit'], loss_for_return_si['credit'])
 
 	def test_discount_on_net_total(self):
 		si = frappe.copy_doc(test_records[2])
@@ -1908,6 +1939,8 @@
 		self.assertEqual(data['billLists'][0]['sgstValue'], 5400)
 		self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
 		self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
+		self.assertEqual(data['billLists'][0]['actualFromStateCode'],7)
+		self.assertEqual(data['billLists'][0]['fromStateCode'],27)
 
 	def test_einvoice_submission_without_irn(self):
 		# init
@@ -2061,6 +2094,30 @@
 
 		address.save()
 
+	if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'):
+		address = frappe.get_doc({
+			"address_line1": "_Test Dispatch Address Line 1",
+			"address_title": "_Test Dispatch-Address for Eway bill",
+			"address_type": "Shipping",
+			"city": "_Test City",
+			"state": "Test State",
+			"country": "India",
+			"doctype": "Address",
+			"is_primary_address": 0,
+			"phone": "+910000000000",
+			"gstin": "07AAACC1206D1ZI",
+			"gst_state": "Delhi",
+			"gst_state_number": "07",
+			"pincode": "1100101"
+		}).insert()
+
+		address.append("links", {
+			"link_doctype": "Company",
+			"link_name": "_Test Company"
+		})
+
+		address.save()
+
 def make_test_transporter_for_ewaybill():
 	if not frappe.db.exists('Supplier', '_Test Transporter'):
 		frappe.get_doc({
@@ -2087,9 +2144,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()
@@ -2099,6 +2156,7 @@
 	si.distance = 2000
 	si.company_address = "_Test Address for Eway bill-Billing"
 	si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
+	si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping"
 	si.vehicle_no = "KA12KA1234"
 	si.gst_category = "Registered Regular"
 	si.mode_of_transport = 'Road'
@@ -2106,7 +2164,7 @@
 
 	si.append("taxes", {
 		"charge_type": "On Net Total",
-		"account_head": "CGST - _TC",
+		"account_head": "Output Tax CGST - _TC",
 		"cost_center": "Main - _TC",
 		"description": "CGST @ 9.0",
 		"rate": 9
@@ -2114,7 +2172,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
@@ -2164,6 +2222,7 @@
 		"rate": args.rate if args.get("rate") is not None else 100,
 		"income_account": args.income_account or "Sales - _TC",
 		"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
+		"asset": args.asset or None,
 		"cost_center": args.cost_center or "_Test Cost Center - _TC",
 		"serial_no": args.serial_no,
 		"conversion_factor": 1
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_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 8e6952a..6690bda 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -743,7 +743,6 @@
    "fieldname": "asset",
    "fieldtype": "Link",
    "label": "Asset",
-   "no_copy": 1,
    "options": "Asset"
   },
   {
@@ -826,7 +825,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-23 01:05:22.123527",
+ "modified": "2021-06-21 23:03:11.599901",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
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/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 744ada9..1759fa3 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -48,17 +48,18 @@
 
 	if not filters.get("from_date") and not filters.get("to_date"):
 		frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
-
-	for account in filters.account:
-		if not account_details.get(account):
-			frappe.throw(_("Account {0} does not exists").format(account))
 			
 	if filters.get('account'):
 		filters.account = frappe.parse_json(filters.get('account'))
+		for account in filters.account:
+			if not account_details.get(account):
+				frappe.throw(_("Account {0} does not exists").format(account))
 
-	if (filters.get("account") and filters.get("group_by") == _('Group by Account')
-		and account_details[filters.account].is_group == 0):
-		frappe.throw(_("Can not filter based on Account, if grouped by Account"))
+	if (filters.get("account") and filters.get("group_by") == _('Group by Account')):
+		filters.account = frappe.parse_json(filters.get('account'))
+		for account in filters.account:
+			if account_details[account].is_group == 0:
+				frappe.throw(_("Can not filter based on Child Account, if grouped by Account"))
 
 	if (filters.get("voucher_no")
 		and filters.get("group_by") in [_('Group by Voucher')]):
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 e15715d..6b9df41 100644
--- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
+++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py
@@ -75,7 +75,8 @@
 		select voucher_no, credit
 		from `tabGL Entry`
 		where party in (%s) and credit > 0
-			and company=%s and posting_date between %s and %s
+			and company=%s and is_cancelled = 0
+			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))
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 66a9b60..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)
@@ -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/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 8f0afb4..8fdbbf9 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -176,22 +176,34 @@
 
 	asset.set_status()
 
-@frappe.whitelist()
+def get_gl_entries_on_asset_regain(asset, selling_amount=0, finance_book=None):
+	fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation = \
+		get_asset_details(asset, finance_book)
+
+	gl_entries = [
+		{
+			"account": fixed_asset_account,
+			"debit_in_account_currency": asset.gross_purchase_amount,
+			"debit": asset.gross_purchase_amount,
+			"cost_center": depreciation_cost_center
+		},
+		{
+			"account": accumulated_depr_account,
+			"credit_in_account_currency": accumulated_depr_amount,
+			"credit": accumulated_depr_amount,
+			"cost_center": depreciation_cost_center
+		}
+	]
+
+	profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
+	if profit_amount:
+		get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center)
+
+	return gl_entries
+
 def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None):
-	fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
-	disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
-	depreciation_cost_center = asset.cost_center or depreciation_cost_center
-
-	idx = 1
-	if finance_book:
-		for d in asset.finance_books:
-			if d.finance_book == finance_book:
-				idx = d.idx
-				break
-
-	value_after_depreciation = (asset.finance_books[idx - 1].value_after_depreciation
-		if asset.calculate_depreciation else asset.value_after_depreciation)
-	accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation)
+	fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation = \
+		get_asset_details(asset, finance_book)
 
 	gl_entries = [
 		{
@@ -210,16 +222,37 @@
 
 	profit_amount = flt(selling_amount) - flt(value_after_depreciation)
 	if profit_amount:
-		debit_or_credit = "debit" if profit_amount < 0 else "credit"
-		gl_entries.append({
-			"account": disposal_account,
-			"cost_center": depreciation_cost_center,
-			debit_or_credit: abs(profit_amount),
-			debit_or_credit + "_in_account_currency": abs(profit_amount)
-		})
+		get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center)
 
 	return gl_entries
 
+def get_asset_details(asset, finance_book=None):
+	fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
+	disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
+	depreciation_cost_center = asset.cost_center or depreciation_cost_center
+
+	idx = 1
+	if finance_book:
+		for d in asset.finance_books:
+			if d.finance_book == finance_book:
+				idx = d.idx
+				break
+
+	value_after_depreciation = (asset.finance_books[idx - 1].value_after_depreciation
+		if asset.calculate_depreciation else asset.value_after_depreciation)
+	accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation)
+
+	return fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation
+
+def get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center):
+	debit_or_credit = "debit" if profit_amount < 0 else "credit"
+	gl_entries.append({
+		"account": disposal_account,
+		"cost_center": depreciation_cost_center,
+		debit_or_credit: abs(profit_amount),
+		debit_or_credit + "_in_account_currency": abs(profit_amount)
+	})
+
 @frappe.whitelist()
 def get_disposal_account_and_cost_center(company):
 	disposal_account, depreciation_cost_center = frappe.get_cached_value('Company',  company,
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 1dbd7c6..132dd17 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -97,6 +97,9 @@
   "is_fixed_asset",
   "item_tax_rate",
   "section_break_72",
+  "production_plan",
+  "production_plan_item",
+  "production_plan_sub_assembly_item",
   "page_break"
  ],
  "fields": [
@@ -803,13 +806,37 @@
    "options": "Company:company:default_currency",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "production_plan",
+   "fieldtype": "Link",
+   "label": "Production Plan",
+   "options": "Production Plan",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "production_plan_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Production Plan Item",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "production_plan_sub_assembly_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Production Plan Sub Assembly Item",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-03-22 11:46:12.357435",
+ "modified": "2021-06-28 19:22:22.715365",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 4ddc458..1766c2c 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -60,10 +60,23 @@
 				erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
 			}, __('Create'));
 
+			frm.add_custom_button(__('Get Supplier Group Details'), function () {
+				frm.trigger("get_supplier_group_details");
+			}, __('Actions'));
+
 			// indicators
 			erpnext.utils.set_party_dashboard_indicators(frm);
 		}
 	},
+	get_supplier_group_details: function(frm) {
+		frappe.call({
+			method: "get_supplier_group_details",
+			doc: frm.doc,
+			callback: function() {
+				frm.refresh();
+			}
+		});
+	},
 
 	is_internal_supplier: function(frm) {
 		if (frm.doc.is_internal_supplier == 1) {
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index edeb135..fd16b23 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -51,6 +51,23 @@
 		validate_party_accounts(self)
 		self.validate_internal_supplier()
 
+	@frappe.whitelist()
+	def get_supplier_group_details(self):
+		doc = frappe.get_doc('Supplier Group', self.supplier_group)
+		self.payment_terms = ""
+		self.accounts = []
+
+		if doc.accounts:
+			for account in doc.accounts:
+				child = self.append('accounts')
+				child.company = account.company
+				child.account = account.account
+
+		if doc.payment_terms:
+			self.payment_terms = doc.payment_terms
+
+		self.save()
+
 	def validate_internal_supplier(self):
 		internal_supplier = frappe.db.get_value("Supplier",
 			{"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
@@ -86,4 +103,4 @@
 						create_contact(supplier, 'Supplier',
 							doc.name, args.get('supplier_email_' + str(i)))
 				except frappe.NameError:
-					pass
\ No newline at end of file
+					pass
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index f9c8d35..8980466 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -13,6 +13,30 @@
 
 
 class TestSupplier(unittest.TestCase):
+    def test_get_supplier_group_details(self):
+        doc = frappe.new_doc("Supplier Group")
+        doc.supplier_group_name = "_Testing Supplier Group"
+        doc.payment_terms = "_Test Payment Term Template 3"
+        doc.accounts = []
+        test_account_details = {
+            "company": "_Test Company",
+            "account": "Creditors - _TC",
+        }
+        doc.append("accounts", test_account_details)
+        doc.save()
+        s_doc = frappe.new_doc("Supplier")
+        s_doc.supplier_name = "Testing Supplier"
+        s_doc.supplier_group = "_Testing Supplier Group"
+        s_doc.payment_terms = ""
+        s_doc.accounts = []
+        s_doc.insert()
+        s_doc.get_supplier_group_details()
+        self.assertEqual(s_doc.payment_terms, "_Test Payment Term Template 3")
+        self.assertEqual(s_doc.accounts[0].company, "_Test Company")
+        self.assertEqual(s_doc.accounts[0].account, "Creditors - _TC")
+        s_doc.delete()
+        doc.delete()
+
     def test_supplier_default_payment_terms(self):
         # Payment Term based on Days after invoice date
         frappe.db.set_value(
@@ -136,4 +160,4 @@
         return doc
 
     except frappe.DuplicateEntryError:
-        return frappe.get_doc("Supplier", args.supplier_name)
\ No newline at end of file
+        return frappe.get_doc("Supplier", args.supplier_name)
diff --git a/erpnext/change_log/v13/v13_7_0.md b/erpnext/change_log/v13/v13_7_0.md
new file mode 100644
index 0000000..589f610
--- /dev/null
+++ b/erpnext/change_log/v13/v13_7_0.md
@@ -0,0 +1,69 @@
+# Version 13.7.0 Release Notes
+
+### Features & Enhancements
+- Optionally allow rejected quality inspection on submission ([#26133](https://github.com/frappe/erpnext/pull/26133))
+- Bootstrapped GST Setup for India ([#25415](https://github.com/frappe/erpnext/pull/25415))
+- Fetching details from supplier/customer groups ([#26454](https://github.com/frappe/erpnext/pull/26454))
+- Provision to make subcontracted purchase order from the production plan ([#26240](https://github.com/frappe/erpnext/pull/26240))
+- Optimized code for reposting item valuation ([#26432](https://github.com/frappe/erpnext/pull/26432))
+
+### Fixes
+- Auto process deferred accounting for multi-company setup ([#26277](https://github.com/frappe/erpnext/pull/26277))
+- Error while fetching item taxes ([#26218](https://github.com/frappe/erpnext/pull/26218))
+- Validation check for batch for stock reconciliation type in stock entry(bp #26370 ) ([#26488](https://github.com/frappe/erpnext/pull/26488))
+- Error popup for COA errors ([#26358](https://github.com/frappe/erpnext/pull/26358))
+- Precision for expected values in payment entry test ([#26394](https://github.com/frappe/erpnext/pull/26394))
+- Bank statement import ([#26287](https://github.com/frappe/erpnext/pull/26287))
+- LMS progress issue ([#26253](https://github.com/frappe/erpnext/pull/26253))
+- Paging buttons not working on item group portal page ([#26497](https://github.com/frappe/erpnext/pull/26497))
+- Omit item discount amount for e-invoicing ([#26353](https://github.com/frappe/erpnext/pull/26353))
+- Validate LCV for Invoices without Update Stock ([#26333](https://github.com/frappe/erpnext/pull/26333))
+- Remove cancelled entries in consolidated financial statements ([#26331](https://github.com/frappe/erpnext/pull/26331))
+- Fetching employee in payroll entry ([#26271](https://github.com/frappe/erpnext/pull/26271))
+- To fetch the correct field in Tax Rule ([#25927](https://github.com/frappe/erpnext/pull/25927))
+- Order and time of operations in multilevel BOM work order ([#25886](https://github.com/frappe/erpnext/pull/25886))
+- Fixed Budget Variance Graph color from all black to default ([#26368](https://github.com/frappe/erpnext/pull/26368))
+- TDS computation summary shows cancelled invoices (#26456) ([#26486](https://github.com/frappe/erpnext/pull/26486))
+- Do not consider cancelled entries in party dashboard ([#26231](https://github.com/frappe/erpnext/pull/26231))
+- Add validation for 'for_qty' else throws errors ([#25829](https://github.com/frappe/erpnext/pull/25829))
+- Move the rename abbreviation job to long queue (#26434) ([#26462](https://github.com/frappe/erpnext/pull/26462))
+- Query for Training Event ([#26388](https://github.com/frappe/erpnext/pull/26388))
+- Item group portal issues (backport) ([#26493](https://github.com/frappe/erpnext/pull/26493))
+- When lead is created with mobile_no, mobile_no value gets lost ([#26298](https://github.com/frappe/erpnext/pull/26298))
+- WIP needs to be set before submit on skip_transfer (bp #26499) ([#26507](https://github.com/frappe/erpnext/pull/26507))
+- Incorrect valuation rate in stock reconciliation ([#26259](https://github.com/frappe/erpnext/pull/26259))
+- Precision rate for packed items in internal transfers ([#26046](https://github.com/frappe/erpnext/pull/26046))
+- Changed profitability analysis report width ([#26165](https://github.com/frappe/erpnext/pull/26165))
+- Unable to download GSTR-1 json ([#26468](https://github.com/frappe/erpnext/pull/26468))
+- Unallocated amount in Payment Entry after taxes ([#26472](https://github.com/frappe/erpnext/pull/26472))
+- Include Stock Reco logic in `update_qty_in_future_sle` ([#26158](https://github.com/frappe/erpnext/pull/26158))
+- Update cost not working in the draft BOM ([#26279](https://github.com/frappe/erpnext/pull/26279))
+- Cancellation of Loan Security Pledges ([#26252](https://github.com/frappe/erpnext/pull/26252))
+- fix(e-invoicing): allow export invoice even if no taxes applied (#26363) ([#26405](https://github.com/frappe/erpnext/pull/26405))
+- Delete accounts (an empty file) ([#25323](https://github.com/frappe/erpnext/pull/25323))
+- Errors on parallel requests creation of company for India  ([#26470](https://github.com/frappe/erpnext/pull/26470))
+- Incorrect bom no added for non-variant items on variant boms ([#26320](https://github.com/frappe/erpnext/pull/26320))
+- Incorrect discount amount on amended document ([#26466](https://github.com/frappe/erpnext/pull/26466))
+- Added a message to enable appointment booking if disabled ([#26334](https://github.com/frappe/erpnext/pull/26334))
+- fix(pos): taxes amount in pos item cart ([#26411](https://github.com/frappe/erpnext/pull/26411))
+- Track changes on batch ([#26382](https://github.com/frappe/erpnext/pull/26382))
+- Stock entry with putaway rule not working ([#26350](https://github.com/frappe/erpnext/pull/26350))
+- Only "Tax" type accounts should be shown for selection in GST Settings ([#26300](https://github.com/frappe/erpnext/pull/26300))
+- Added permission for employee to book appointment ([#26255](https://github.com/frappe/erpnext/pull/26255))
+- Allow to make job card without employee ([#26312](https://github.com/frappe/erpnext/pull/26312))
+- Project Portal Enhancements ([#26290](https://github.com/frappe/erpnext/pull/26290))
+- BOM stock report not working ([#26332](https://github.com/frappe/erpnext/pull/26332))
+- Order Items by weightage in the web items query ([#26284](https://github.com/frappe/erpnext/pull/26284))
+- Removed values out of sync validation from stock transactions ([#26226](https://github.com/frappe/erpnext/pull/26226))
+- Payroll-entry minor fix ([#26349](https://github.com/frappe/erpnext/pull/26349))
+- Allow user to change the To Date in the blanket order even after submit of order ([#26241](https://github.com/frappe/erpnext/pull/26241))
+- Value fetching for custom field in POS ([#26367](https://github.com/frappe/erpnext/pull/26367))
+- Iteration through accounts only when accounts exist ([#26391](https://github.com/frappe/erpnext/pull/26391))
+- Employee Inactive status implications ([#26244](https://github.com/frappe/erpnext/pull/26244))
+- Multi-currency issue ([#26458](https://github.com/frappe/erpnext/pull/26458))
+- FG item not fetched in manufacture entry ([#26509](https://github.com/frappe/erpnext/pull/26509))
+- Set query for training events ([#26303](https://github.com/frappe/erpnext/pull/26303))
+- Fetch batch items in stock reconciliation ([#26213](https://github.com/frappe/erpnext/pull/26213))
+- Employee selection not working in payroll entry ([#26278](https://github.com/frappe/erpnext/pull/26278))
+- POS item cart dom updates (#26459) ([#26461](https://github.com/frappe/erpnext/pull/26461))
+- dunning calculation of grand total when rate of interest is 0% ([#26285](https://github.com/frappe/erpnext/pull/26285))
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 1c086e9..cdd865a 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -124,6 +124,8 @@
 			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:
@@ -584,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":
@@ -650,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:
@@ -690,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)
 
@@ -751,11 +818,11 @@
 					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"
+					else:
+						dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
+						rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
 
 					party = self.supplier if self.doctype == "Purchase Invoice" else self.customer
 					unallocated_amount = tax.tax_amount - tax.allocated_amount
@@ -1045,8 +1112,11 @@
 			for d in self.get("payment_schedule"):
 				if d.invoice_portion:
 					d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
-					d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
+					d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
 					d.outstanding = d.payment_amount
+				elif not d.invoice_portion:
+					d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
+
 
 	def set_due_date(self):
 		due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
@@ -1289,6 +1359,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 ""
 
@@ -1305,27 +1377,28 @@
 				"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)
 
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 2526e6d..17bd735 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -53,12 +53,17 @@
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		for d in self.get("items"):
 			if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no:
-				serial_nos = get_serial_nos(d.serial_no)
-				for serial_no_data in frappe.get_all("Serial No",
-					filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]):
-					if serial_no_data.batch_no != d.batch_no:
+				serial_nos = frappe.get_all("Serial No",
+					fields=["batch_no", "name", "warehouse"],
+					filters={
+						"name": ("in", get_serial_nos(d.serial_no))
+					}
+				)
+
+				for row in serial_nos:
+					if row.warehouse and row.batch_no != d.batch_no:
 						frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
-							.format(d.idx, serial_no_data.name, d.batch_no))
+							.format(d.idx, row.name, d.batch_no))
 
 			if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
 				expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json
index 016b8ec..fe7b4e1 100644
--- a/erpnext/crm/doctype/appointment/appointment.json
+++ b/erpnext/crm/doctype/appointment/appointment.json
@@ -102,7 +102,7 @@
   }
  ],
  "links": [],
- "modified": "2021-06-30 12:09:14.228756",
+ "modified": "2021-06-30 13:09:14.228756",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Appointment",
diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js
index 0487654..11bfa74 100644
--- a/erpnext/crm/doctype/campaign/campaign.js
+++ b/erpnext/crm/doctype/campaign/campaign.js
@@ -5,12 +5,11 @@
 	refresh: function(frm) {
 		erpnext.toggle_naming_series();
 
-		if(frm.doc.__islocal) {
+		if (frm.doc.__islocal) {
 			frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series");
-		}
-		else {
+		} else {
 			cur_frm.add_custom_button(__("View Leads"), function() {
-				frappe.route_options = {"source": "Campaign","campaign_name": frm.doc.name}
+				frappe.route_options = {"source": "Campaign", "campaign_name": frm.doc.name};
 				frappe.set_route("List", "Lead");
 			}, "fa fa-list", true);
 		}
diff --git a/erpnext/crm/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py
index 3433195..e32799f 100644
--- a/erpnext/crm/doctype/campaign/campaign.py
+++ b/erpnext/crm/doctype/campaign/campaign.py
@@ -3,6 +3,7 @@
 
 import frappe
 from frappe.model.document import Document
+from frappe.model.naming import set_name_by_naming_series
 
 class Campaign(Document):
 	def autoname(self):
diff --git a/erpnext/crm/doctype/campaign/test_campaign.py b/erpnext/crm/doctype/campaign/test_campaign.py
index 939bb8f..7124b8c 100644
--- a/erpnext/crm/doctype/campaign/test_campaign.py
+++ b/erpnext/crm/doctype/campaign/test_campaign.py
@@ -1,7 +1,7 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import frappe
+# import frappe
 import unittest
 
 class TestCampaign(unittest.TestCase):
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
index 3c2e59a..b0e662d 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -7,16 +7,21 @@
 import unittest
 from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import process_balance_info, verify_transaction
 from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+from erpnext.erpnext_integrations.utils import create_mode_of_payment
 
 class TestMpesaSettings(unittest.TestCase):
+	def setUp(self):
+		# create payment gateway in setup
+		create_mpesa_settings(payment_gateway_name="_Test")
+		create_mpesa_settings(payment_gateway_name="_Account Balance")
+		create_mpesa_settings(payment_gateway_name="Payment")
+
 	def tearDown(self):
 		frappe.db.sql('delete from `tabMpesa Settings`')
 		frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"')
 
 	def test_creation_of_payment_gateway(self):
-		create_mpesa_settings(payment_gateway_name="_Test")
-
-		mode_of_payment = frappe.get_doc("Mode of Payment", "Mpesa-_Test")
+		mode_of_payment = create_mode_of_payment('Mpesa-_Test', payment_type="Phone")
 		self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"}))
 		self.assertTrue(mode_of_payment.name)
 		self.assertEqual(mode_of_payment.type, "Phone")
@@ -47,7 +52,6 @@
 		integration_request.delete()
 
 	def test_processing_of_callback_payload(self):
-		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
@@ -90,7 +94,6 @@
 		pos_invoice.delete()
 
 	def test_processing_of_multiple_callback_payload(self):
-		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
@@ -141,7 +144,6 @@
 		pos_invoice.delete()
 
 	def test_processing_of_only_one_succes_callback_payload(self):
-		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
 		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
@@ -202,6 +204,7 @@
 
 	doc = frappe.get_doc(dict( #nosec
 		doctype="Mpesa Settings",
+		sandbox=1,
 		payment_gateway_name=payment_gateway_name,
 		consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn",
 		consumer_secret="VI1oS3oBGPJfh3JyvLHw",
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 3840e78..a5e162f 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -52,7 +52,8 @@
 			"payment_gateway": gateway
 		}, ['payment_account'])
 
-	if not frappe.db.exists("Mode of Payment", gateway) and payment_gateway_account:
+	mode_of_payment = frappe.db.exists("Mode of Payment", gateway) 
+	if not mode_of_payment and payment_gateway_account:
 		mode_of_payment = frappe.get_doc({
 			"doctype": "Mode of Payment",
 			"mode_of_payment": gateway,
@@ -66,6 +67,10 @@
 		})
 		mode_of_payment.insert(ignore_permissions=True)
 
+		return mode_of_payment
+	elif mode_of_payment:
+		return frappe.get_doc("Mode of Payment", mode_of_payment)
+
 def get_tracking_url(carrier, tracking_number):
 	# Return the formatted Tracking URL.
 	tracking_url = ''
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index ba10b58..59b011d 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -25,7 +25,8 @@
 	"Address": "public/js/address.js",
 	"Communication": "public/js/communication.js",
 	"Event": "public/js/event.js",
-	"Newsletter": "public/js/newsletter.js"
+	"Newsletter": "public/js/newsletter.js",
+	"Contact": "public/js/contact.js"
 }
 
 override_doctype_class = {
@@ -245,7 +246,10 @@
 			"erpnext.portal.utils.set_default_role"]
 	},
 	"Communication": {
-		"on_update": "erpnext.support.doctype.service_level_agreement.service_level_agreement.update_hold_time"
+		"on_update": [
+			"erpnext.support.doctype.service_level_agreement.service_level_agreement.update_hold_time",
+			"erpnext.support.doctype.issue.issue.set_first_response_time"
+		]
 	},
 	("Sales Taxes and Charges Template", 'Price List'): {
 		"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"
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/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_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js
index 1365274..017026c 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.js
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.js
@@ -14,11 +14,18 @@
 	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 = ""
-		frm.trigger("toggle_fields")
-		frm.trigger("toggle_required")
+		frm.doc.repayment_amount = frm.doc.repayment_periods = "";
+		frm.trigger("toggle_fields");
+		frm.trigger("toggle_required");
 	},
 	toggle_fields: function(frm) {
 		frm.toggle_enable("repayment_amount", frm.doc.repayment_method=="Repay Fixed Amount per Period")
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 c566688..3f50b41 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -83,7 +83,7 @@
 
 		if (!frm.doc.__islocal && frm.doc.docstatus<2) {
 			frm.add_custom_button(__("Update Cost"), function() {
-				frm.events.update_cost(frm);
+				frm.events.update_cost(frm, true);
 			});
 			frm.add_custom_button(__("Browse BOM"), function() {
 				frappe.route_options = {
@@ -318,14 +318,15 @@
 		})
 	},
 
-	update_cost: function(frm) {
+	update_cost: function(frm, save_doc=false) {
 		return frappe.call({
 			doc: frm.doc,
 			method: "update_cost",
 			freeze: true,
 			args: {
 				update_parent: true,
-				from_child_bom:false
+				save: save_doc,
+				from_child_bom: false
 			},
 			callback: function(r) {
 				refresh_field("items");
diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json
index f38d1b9..7e53918 100644
--- a/erpnext/manufacturing/doctype/bom/bom.json
+++ b/erpnext/manufacturing/doctype/bom/bom.json
@@ -36,6 +36,9 @@
   "materials_section",
   "inspection_required",
   "quality_inspection_template",
+  "column_break_31",
+  "bom_level",
+  "section_break_33",
   "items",
   "scrap_section",
   "scrap_items",
@@ -513,6 +516,22 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_31",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "bom_level",
+   "fieldtype": "Int",
+   "label": "BOM Level",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_33",
+   "fieldtype": "Section Break",
+   "hide_border": 1
   }
  ],
  "icon": "fa fa-sitemap",
@@ -520,7 +539,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-16 12:25:09.081968",
+ "modified": "2021-05-16 12:25:09.081968",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 3bd1fe6..bc092ef 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -154,6 +154,7 @@
 		self.calculate_cost()
 		self.update_stock_qty()
 		self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate = False, save=False)
+		self.set_bom_level()
 
 	def get_context(self, context):
 		context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
@@ -329,7 +330,7 @@
 				frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
 
 		if not from_child_bom:
-			frappe.msgprint(_("Cost Updated"))
+			frappe.msgprint(_("Cost Updated"), alert=True)
 
 	def update_parent_cost(self):
 		if self.total_cost:
@@ -676,6 +677,19 @@
 		"""Get a complete tree representation preserving order of child items."""
 		return BOMTree(self.name)
 
+	def set_bom_level(self, update=False):
+		levels = []
+
+		self.bom_level = 0
+		for row in self.items:
+			if row.bom_no:
+				levels.append(frappe.get_cached_value("BOM", row.bom_no, "bom_level") or 0)
+
+		if levels:
+			self.bom_level = max(levels) + 1
+
+		if update:
+			self.db_set("bom_level", self.bom_level)
 
 def get_bom_item_rate(args, bom_doc):
 	if bom_doc.rm_cost_as_per == 'Valuation Rate':
@@ -699,7 +713,8 @@
 			"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
 			"conversion_factor": args.get("conversion_factor") or 1,
 			"plc_conversion_rate": 1,
-			"ignore_party": True
+			"ignore_party": True,
+			"ignore_conversion_rate": True
 		})
 		item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
 		out = frappe._dict()
@@ -759,7 +774,7 @@
 				item.image,
 				bom.project,
 				bom_item.rate,
-				bom_item.amount,
+				sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * bom_item.rate * %(qty)s as amount,
 				item.stock_uom,
 				item.item_group,
 				item.allow_alternative_item,
@@ -860,7 +875,7 @@
 		frappe.form_dict.parent = parent
 
 	if frappe.form_dict.parent:
-		bom_doc = frappe.get_doc("BOM", frappe.form_dict.parent)
+		bom_doc = frappe.get_cached_doc("BOM", frappe.form_dict.parent)
 		frappe.has_permission("BOM", doc=bom_doc, throw=True)
 
 		bom_items = frappe.get_all('BOM Item',
@@ -871,7 +886,7 @@
 		item_names = tuple(d.get('item_code') for d in bom_items)
 
 		items = frappe.get_list('Item',
-			fields=['image', 'description', 'name', 'stock_uom', 'item_name'],
+			fields=['image', 'description', 'name', 'stock_uom', 'item_name', 'is_sub_contracted_item'],
 			filters=[['name', 'in', item_names]]) # to get only required item dicts
 
 		for bom_item in bom_items:
@@ -884,6 +899,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
 
diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
index 6cd5f8c..6088e46 100644
--- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html
+++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
@@ -1,13 +1,31 @@
 <div style="padding: 15px;">
-	{% if data.image %}
-	<img class="responsive" src={{ data.image }}>
-	<hr style="margin: 15px -15px;">
-	{% endif %}
-	<h4>
-		{{ __("Description") }}
-	</h4>
-	<div style="padding-top: 10px;">
-		{{ data.description }}
+	<div class="row mb-5">
+		<div class="col-md-5" style="max-height: 500px">
+			{% if data.image %}
+				<div class="border image-field " style="overflow: hidden;border-color:#e6e6e6">
+					<img class="responsive" src={{ data.image }}>
+				</div>
+			{% endif %}
+		</div>
+		<div class="col-md-7 h-500">
+			<h4>
+				{{ __("Description") }}
+			</h4>
+			<div style="padding-top: 10px;">
+				{{ data.description }}
+			</div>
+			<hr style="margin: 15px -15px;">
+			<p>
+				{% if data.value %}
+				<a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="#Form/BOM/{{ data.value }}">
+					{{ __("Open BOM {0}", [data.value.bold()]) }}</a>
+				{% endif %}
+				{% if data.item_code %}
+				<a class="btn btn-default btn-xs" href="#Form/Item/{{ data.item_code }}">
+					{{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
+				{% endif %}
+			</p>
+		</div>
 	</div>
 	<hr style="margin: 15px -15px;">
 	<p>
diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js
index 185b9ed..60fb377 100644
--- a/erpnext/manufacturing/doctype/bom/bom_tree.js
+++ b/erpnext/manufacturing/doctype/bom/bom_tree.js
@@ -64,7 +64,7 @@
 		if(node.is_root && node.data.value!="BOM") {
 			frappe.model.with_doc("BOM", node.data.value, function() {
 				var bom = frappe.model.get_doc("BOM", node.data.value);
-				node.data.image = bom.image || "";
+				node.data.image = escape(bom.image) || "";
 				node.data.description = bom.description || "";
 			});
 		}
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 7f8f2ef..420bb00 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -192,15 +192,20 @@
 						"completed_qty": args.get("completed_qty") or 0.0
 					})
 		elif args.get("start_time"):
-			for name in employees:
-				self.append("time_logs", {
-					"from_time": get_datetime(args.get("start_time")),
-					"employee": name.get('employee'),
-					"operation": args.get("sub_operation"),
-					"completed_qty": 0.0
-				})
+			new_args = {
+				"from_time": get_datetime(args.get("start_time")),
+				"operation": args.get("sub_operation"),
+				"completed_qty": 0.0
+			}
 
-		if not self.employee:
+			if employees:
+				for name in employees:
+					new_args.employee = name.get('employee')
+					self.add_start_time_log(new_args)
+			else:
+				self.add_start_time_log(new_args)
+
+		if not self.employee and employees:
 			self.set_employees(employees)
 
 		if self.status == "On Hold":
@@ -208,6 +213,9 @@
 
 		self.save()
 
+	def add_start_time_log(self, args):
+		self.append("time_logs", args)
+
 	def set_employees(self, employees):
 		for name in employees:
 			self.append('employee', {
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 450aa04..d198a69 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -4,7 +4,7 @@
 frappe.ui.form.on('Production Plan', {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
-			'Work Order': 'Work Order',
+			'Work Order': 'Work Order / Subcontract PO',
 			'Material Request': 'Material Request',
 		};
 
@@ -68,17 +68,13 @@
 			frm.trigger("show_progress");
 
 			if (frm.doc.status !== "Completed") {
-				if (frm.doc.po_items && frm.doc.status !== "Closed") {
-					frm.add_custom_button(__("Work Order"), ()=> {
-						frm.trigger("make_work_order");
-					}, __('Create'));
-				}
+				frm.add_custom_button(__("Work Order Tree"), ()=> {
+					frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name});
+				}, __('View'));
 
-				if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
-					frm.add_custom_button(__("Material Request"), ()=> {
-						frm.trigger("make_material_request");
-					}, __('Create'));
-				}
+				frm.add_custom_button(__("Production Plan Summary"), ()=> {
+					frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
+				}, __('View'));
 
 				if  (frm.doc.status === "Closed") {
 					frm.add_custom_button(__("Re-open"), function() {
@@ -89,6 +85,18 @@
 						frm.events.close_open_production_plan(frm, true);
 					}, __("Status"));
 				}
+
+				if (frm.doc.po_items && frm.doc.status !== "Closed") {
+					frm.add_custom_button(__("Work Order / Subcontract PO"), ()=> {
+						frm.trigger("make_work_order");
+					}, __('Create'));
+				}
+
+				if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
+					frm.add_custom_button(__("Material Request"), ()=> {
+						frm.trigger("make_material_request");
+					}, __('Create'));
+				}
 			}
 		}
 
@@ -233,6 +241,17 @@
 		});
 	},
 
+	get_sub_assembly_items: function(frm) {
+		frappe.call({
+			method: "get_sub_assembly_items",
+			freeze: true,
+			doc: frm.doc,
+			callback: function() {
+				refresh_field("sub_assembly_items");
+			}
+		});
+	},
+
 	get_items_for_mr: function(frm) {
 		if (!frm.doc.for_warehouse) {
 			frappe.throw(__("Select warehouse for material requests"));
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 1c0dde2..8437895 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -32,6 +32,9 @@
   "po_items",
   "section_break_25",
   "prod_plan_references",
+  "section_break_24",
+  "get_sub_assembly_items",
+  "sub_assembly_items",
   "material_request_planning",
   "include_non_stock_items",
   "include_subcontracted_items",
@@ -187,7 +190,7 @@
    "depends_on": "get_items_from",
    "fieldname": "get_items",
    "fieldtype": "Button",
-   "label": "Get Items For Work Order"
+   "label": "Get Finished Goods for Manufacture"
   },
   {
    "fieldname": "po_items",
@@ -199,7 +202,7 @@
   {
    "fieldname": "material_request_planning",
    "fieldtype": "Section Break",
-   "label": "Material Request Planning"
+   "label": "Material Requirement Planning"
   },
   {
    "default": "1",
@@ -237,12 +240,13 @@
   },
   {
    "fieldname": "section_break_27",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "hide_border": 1
   },
   {
    "fieldname": "mr_items",
    "fieldtype": "Table",
-   "label": "Material Request Plan Item",
+   "label": "Raw Materials",
    "no_copy": 1,
    "options": "Material Request Plan Item"
   },
@@ -337,13 +341,30 @@
    "hidden": 1,
    "label": "Production Plan Item Reference",
    "options": "Production Plan Item Reference"
+  },
+  {
+   "fieldname": "section_break_24",
+   "fieldtype": "Section Break",
+   "hide_border": 1
+  },
+  {
+   "fieldname": "sub_assembly_items",
+   "fieldtype": "Table",
+   "label": "Sub Assembly Items",
+   "no_copy": 1,
+   "options": "Production Plan Sub Assembly Item"
+  },
+  {
+   "fieldname": "get_sub_assembly_items",
+   "fieldtype": "Button",
+   "label": "Get Sub Assembly Items"
   }
  ],
  "icon": "fa fa-calendar",
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-05-24 16:59:03.643211",
+ "modified": "2021-06-28 20:00:33.905114",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 0ede1bd..6a024f2 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -5,10 +5,11 @@
 from __future__ import unicode_literals
 import frappe, json, copy
 from frappe import msgprint, _
-from six import string_types, iteritems
+from six import iteritems
 
 from frappe.model.document import Document
-from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime, ceil
+from frappe.utils import (flt, cint, nowdate, add_days, comma_and, now_datetime,
+	ceil, get_link_to_form, getdate)
 from frappe.utils.csvutils import build_csv_response
 from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_children
 from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
@@ -349,49 +350,88 @@
 
 	@frappe.whitelist()
 	def make_work_order(self):
-		wo_list = []
+		wo_list, po_list = [], []
+		subcontracted_po = {}
+
 		self.validate_data()
+		self.make_work_order_for_finished_goods(wo_list)
+		self.make_work_order_for_subassembly_items(wo_list, subcontracted_po)
+		self.make_subcontracted_purchase_order(subcontracted_po, po_list)
+		self.show_list_created_message('Work Order', wo_list)
+		self.show_list_created_message('Purchase Order', po_list)
+
+	def make_work_order_for_finished_goods(self, wo_list):
 		items_data = self.get_production_items()
 
 		for key, item in items_data.items():
+			if self.sub_assembly_items:
+				item['use_multi_level_bom'] = 0
+
 			work_order = self.create_work_order(item)
 			if work_order:
 				wo_list.append(work_order)
 
-			if item.get("make_work_order_for_sub_assembly_items"):
-				work_orders = self.make_work_order_for_sub_assembly_items(item)
-				wo_list.extend(work_orders)
+	def make_work_order_for_subassembly_items(self, wo_list, subcontracted_po):
+		for row in self.sub_assembly_items:
+			if row.type_of_manufacturing == 'Subcontract':
+				subcontracted_po.setdefault(row.supplier, []).append(row)
+				continue
+
+			args = {}
+			self.prepare_args_for_sub_assembly_items(row, args)
+			work_order = self.create_work_order(args)
+			if work_order:
+				wo_list.append(work_order)
+
+	def make_subcontracted_purchase_order(self, subcontracted_po, purchase_orders):
+		if not subcontracted_po:
+			return
+
+		for supplier, po_list in subcontracted_po.items():
+			po = frappe.new_doc('Purchase Order')
+			po.supplier = supplier
+			po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
+			po.is_subcontracted_item = 'Yes'
+			for row in po_list:
+				args = {
+					'item_code': row.production_item,
+					'warehouse': row.fg_warehouse,
+					'production_plan_sub_assembly_item': row.name,
+					'bom': row.bom_no,
+					'production_plan': self.name
+				}
+
+				for field in ['schedule_date', 'qty', 'uom', 'stock_uom', 'item_name',
+					'description', 'production_plan_item']:
+					args[field] = row.get(field)
+
+				po.append('items', args)
+
+			po.set_missing_values()
+			po.flags.ignore_mandatory = True
+			po.flags.ignore_validate = True
+			po.insert()
+			purchase_orders.append(po.name)
+
+	def show_list_created_message(self, doctype, doc_list=None):
+		if not doc_list:
+			return
 
 		frappe.flags.mute_messages = False
+		if doc_list:
+			doc_list = [get_link_to_form(doctype, p) for p in doc_list]
+			msgprint(_("{0} created").format(comma_and(doc_list)))
 
-		if wo_list:
-			wo_list = ["""<a href="/app/Form/Work Order/%s" target="_blank">%s</a>""" % \
-				(p, p) for p in wo_list]
-			msgprint(_("{0} created").format(comma_and(wo_list)))
-		else :
-			msgprint(_("No Work Orders created"))
+	def prepare_args_for_sub_assembly_items(self, row, args):
+		for field in ["production_item", "item_name", "qty", "fg_warehouse",
+			"description", "bom_no", "stock_uom", "bom_level", "production_plan_item"]:
+			args[field] = row.get(field)
 
-	def make_work_order_for_sub_assembly_items(self, item):
-		work_orders = []
-		bom_data = {}
-
-		get_sub_assembly_items(item.get("bom_no"), bom_data, item.get("qty"))
-
-		for key, data in bom_data.items():
-			data.update({
-				'qty': data.get("stock_qty"),
-				'production_plan': self.name,
-				'use_multi_level_bom': item.get("use_multi_level_bom"),
-				'company': self.company,
-				'fg_warehouse': item.get("fg_warehouse"),
-				'update_consumed_material_cost_in_project': 0
-			})
-
-			work_order = self.create_work_order(data)
-			if work_order:
-				work_orders.append(work_order)
-
-		return work_orders
+		args.update({
+			"use_multi_level_bom": 0,
+			"production_plan": self.name,
+			"production_plan_sub_assembly_item": row.name
+		})
 
 	def create_work_order(self, item):
 		from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError, get_default_warehouse
@@ -476,9 +516,32 @@
 		else :
 			msgprint(_("No material request created"))
 
+	@frappe.whitelist()
+	def get_sub_assembly_items(self, manufacturing_type=None):
+		self.sub_assembly_items = []
+		for row in self.po_items:
+			bom_data = []
+			get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
+			self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
+
+		self.save()
+
+	def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
+		bom_data = sorted(bom_data, key = lambda i: i.bom_level)
+
+		for data in bom_data:
+			data.qty = data.stock_qty
+			data.production_plan_item = row.name
+			data.fg_warehouse = row.warehouse
+			data.schedule_date = row.planned_start_date
+			data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
+				else "In House")
+
+			self.append("sub_assembly_items", data)
+
 @frappe.whitelist()
 def download_raw_materials(doc, warehouses=None):
-	if isinstance(doc, string_types):
+	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
 	item_list = [['Item Code', 'Description', 'Stock UOM', 'Warehouse', 'Required Qty as per BOM',
@@ -660,7 +723,7 @@
 
 @frappe.whitelist()
 def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
-	if isinstance(row, string_types):
+	if isinstance(row, str):
 		row = frappe._dict(json.loads(row))
 
 	company = frappe.db.escape(company)
@@ -684,8 +747,10 @@
 		group by item_code, warehouse
 	""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
 
-def get_warehouse_list(warehouses, warehouse_list=[]):
-	if isinstance(warehouses, string_types):
+def get_warehouse_list(warehouses):
+	warehouse_list = []
+
+	if isinstance(warehouses, str):
 		warehouses = json.loads(warehouses)
 
 	for row in warehouses:
@@ -695,23 +760,19 @@
 		else:
 			warehouse_list.append(row.get("warehouse"))
 
+	return warehouse_list
+
 @frappe.whitelist()
 def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
-	if isinstance(doc, string_types):
+	if isinstance(doc, str):
 		doc = frappe._dict(json.loads(doc))
 
-	warehouse_list = []
 	if warehouses:
-		get_warehouse_list(warehouses, warehouse_list)
-
-	if warehouse_list:
-		warehouses = list(set(warehouse_list))
+		warehouses = list(set(get_warehouse_list(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
-
 	doc['mr_items'] = []
 
 	po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
@@ -726,6 +787,9 @@
 
 	so_item_details = frappe._dict()
 	for data in po_items:
+		if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
+			data["include_exploded_items"] = 1
+
 		planned_qty = data.get('required_qty') or data.get('planned_qty')
 		ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
 		warehouse = doc.get('for_warehouse')
@@ -857,23 +921,28 @@
 #		"description": item_details.get("description")
 	}
 
-def get_sub_assembly_items(bom_no, bom_data, to_produce_qty):
+def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
 	data = get_children('BOM', parent = bom_no)
 	for d in data:
 		if d.expandable:
-			key = (d.name, d.value)
-			if key not in bom_data:
-				bom_data.setdefault(key, {
-					'stock_qty': 0,
-					'description': d.description,
-					'production_item': d.item_code,
-					'item_name': d.item_name,
-					'stock_uom': d.stock_uom,
-					'uom': d.stock_uom,
-					'bom_no': d.value
-				})
+			parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
+			bom_level = (frappe.get_cached_value("BOM", d.value, "bom_level")
+				if d.value else 0)
 
-			bom_item = bom_data.get(key)
-			bom_item["stock_qty"] += (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
+			stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
+			bom_data.append(frappe._dict({
+				'parent_item_code': parent_item_code,
+				'description': d.description,
+				'production_item': d.item_code,
+				'item_name': d.item_name,
+				'stock_uom': d.stock_uom,
+				'uom': d.stock_uom,
+				'bom_no': d.value,
+				'is_sub_contracted_item': d.is_sub_contracted_item,
+				'bom_level': bom_level,
+				'indent': indent,
+				'stock_qty': stock_qty
+			}))
 
-			get_sub_assembly_items(bom_item.get("bom_no"), bom_data, bom_item["stock_qty"])
+			if d.value:
+				get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent+1)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
index 09ec24a..ca597f6 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py
@@ -9,5 +9,9 @@
 				'label': _('Transactions'),
 				'items': ['Work Order', 'Material Request']
 			},
+			{
+				'label': _('Subcontract'),
+				'items': ['Purchase Order']
+			},
 		]
 	}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 768f99e..93e6d7a 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -10,7 +10,7 @@
 from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
+from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list
 
 class TestProductionPlan(unittest.TestCase):
 	def setUp(self):
@@ -169,7 +169,7 @@
 		pln.get_items()
 		pln.submit()
 
-		self.assertTrue(pln.po_items[0].planned_qty, 3)	
+		self.assertTrue(pln.po_items[0].planned_qty, 3)
 
 		pln.make_work_order()
 		work_order = frappe.db.get_value('Work Order', {
@@ -193,10 +193,10 @@
 		for so_item in so_items:
 			so_wo_qty = frappe.db.get_value('Sales Order Item', so_item, 'work_order_qty')
 			self.assertEqual(so_wo_qty, 0.0)
-		
+
 		latest_plan = frappe.get_doc('Production Plan', pln.name)
 		latest_plan.cancel()
-	
+
 	def test_pp_to_mr_customer_provided(self):
 		#Material Request from Production Plan for Customer Provided
 		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
@@ -236,10 +236,10 @@
 		pln.append("po_items", {
 			"item_code": item_code,
 			"bom_no": frappe.db.get_value('BOM', {'item': "Test BOM 1"}),
-			"planned_qty": 3,
-			"make_work_order_for_sub_assembly_items": 1
+			"planned_qty": 3
 		})
 
+		pln.get_sub_assembly_items('In House')
 		pln.submit()
 		pln.make_work_order()
 
@@ -251,6 +251,27 @@
 		pln.cancel()
 		frappe.delete_doc("Production Plan", pln.name)
 
+	def test_get_warehouse_list_group(self):
+		"""Check if required warehouses are returned"""
+		warehouse_json = '[{\"warehouse\":\"_Test Warehouse Group - _TC\"}]'
+
+		warehouses = set(get_warehouse_list(warehouse_json))
+		expected_warehouses = {"_Test Warehouse Group-C1 - _TC", "_Test Warehouse Group-C2 - _TC"}
+
+		missing_warehouse = expected_warehouses - warehouses
+
+		self.assertTrue(len(missing_warehouse) == 0,
+				msg=f"Following warehouses were expected {', '.join(missing_warehouse)}")
+
+	def test_get_warehouse_list_single(self):
+		warehouse_json = '[{\"warehouse\":\"_Test Scrap Warehouse - _TC\"}]'
+
+		warehouses = set(get_warehouse_list(warehouse_json))
+		expected_warehouses = {"_Test Scrap Warehouse - _TC", }
+
+		self.assertEqual(warehouses, expected_warehouses)
+
+
 def create_production_plan(**args):
 	args = frappe._dict(args)
 
diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
index 89ab7aa..f829d57 100644
--- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json
@@ -9,18 +9,17 @@
   "include_exploded_items",
   "item_code",
   "bom_no",
-  "planned_qty",
   "column_break_6",
-  "make_work_order_for_sub_assembly_items",
+  "planned_qty",
   "warehouse",
   "planned_start_date",
   "section_break_9",
   "pending_qty",
   "ordered_qty",
-  "produced_qty",
   "column_break_17",
   "description",
   "stock_uom",
+  "produced_qty",
   "reference_section",
   "sales_order",
   "sales_order_item",
@@ -32,11 +31,10 @@
  ],
  "fields": [
   {
-   "columns": 2,
-   "default": "0",
+   "columns": 1,
+   "default": "1",
    "fieldname": "include_exploded_items",
    "fieldtype": "Check",
-   "in_list_view": 1,
    "label": "Include Exploded Items"
   },
   {
@@ -81,13 +79,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "0",
-   "description": "If enabled, system will create the work order for the exploded items against which BOM is available.",
-   "fieldname": "make_work_order_for_sub_assembly_items",
-   "fieldtype": "Check",
-   "label": "Make Work Order for Sub Assembly Items"
-  },
-  {
    "fieldname": "warehouse",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -218,7 +209,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-04-28 19:14:57.772123",
+ "modified": "2021-06-28 18:31:06.822168",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan Item",
diff --git a/erpnext/accounts/accounts b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/__init__.py
similarity index 100%
copy from erpnext/accounts/accounts
copy to erpnext/manufacturing/doctype/production_plan_sub_assembly_item/__init__.py
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
new file mode 100644
index 0000000..657ee35
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
@@ -0,0 +1,202 @@
+{
+ "actions": [],
+ "creation": "2020-12-27 16:08:36.127199",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "production_item",
+  "item_name",
+  "fg_warehouse",
+  "parent_item_code",
+  "schedule_date",
+  "column_break_3",
+  "qty",
+  "bom_no",
+  "bom_level",
+  "type_of_manufacturing",
+  "supplier",
+  "work_order_details_section",
+  "work_order",
+  "purchase_order",
+  "production_plan_item",
+  "column_break_7",
+  "produced_qty",
+  "received_qty",
+  "indent",
+  "section_break_19",
+  "uom",
+  "stock_uom",
+  "column_break_22",
+  "description"
+ ],
+ "fields": [
+  {
+   "fetch_from": "sub_assembly_item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.type_of_manufacturing == \"In House\"",
+   "fieldname": "work_order_details_section",
+   "fieldtype": "Section Break",
+   "label": "Reference"
+  },
+  {
+   "fieldname": "work_order",
+   "fieldtype": "Link",
+   "label": "Work Order",
+   "options": "Work Order",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
+  {
+   "columns": 1,
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Required Qty",
+   "read_only": 1
+  },
+  {
+   "fieldname": "purchase_order",
+   "fieldtype": "Link",
+   "label": "Purchase Order",
+   "options": "Purchase Order",
+   "read_only": 1
+  },
+  {
+   "fieldname": "received_qty",
+   "fieldtype": "Float",
+   "label": "Received Qty"
+  },
+  {
+   "fieldname": "bom_no",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Bom No",
+   "options": "BOM"
+  },
+  {
+   "fieldname": "production_plan_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Production Plan Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "parent_item_code",
+   "fieldtype": "Link",
+   "label": "Finished Good",
+   "options": "Item",
+   "read_only": 1
+  },
+  {
+   "columns": 1,
+   "fetch_from": "bom_no.bom_level",
+   "fieldname": "bom_level",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Level (BOM)",
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_19",
+   "fieldtype": "Section Break",
+   "label": "Item Details"
+  },
+  {
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_22",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "description",
+   "read_only": 1
+  },
+  {
+   "fieldname": "production_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Sub Assembly Item Code",
+   "options": "Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "indent",
+   "fieldtype": "Int",
+   "label": "Indent"
+  },
+  {
+   "fieldname": "fg_warehouse",
+   "fieldtype": "Link",
+   "label": "Target Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "fieldname": "produced_qty",
+   "fieldtype": "Data",
+   "label": "Produced Quantity",
+   "read_only": 1
+  },
+  {
+   "default": "In House",
+   "fieldname": "type_of_manufacturing",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Manufacturing Type",
+   "options": "In House\nSubcontract"
+  },
+  {
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "label": "Supplier",
+   "mandatory_depends_on": "eval:doc.type_of_manufacturing == 'Subcontract'",
+   "options": "Supplier"
+  },
+  {
+   "fieldname": "schedule_date",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "Schedule Date"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-06-28 20:10:56.296410",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Sub Assembly Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py
new file mode 100644
index 0000000..6850a2e
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class ProductionPlanSubAssemblyItem(Document):
+	pass
diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json
index f63d2b9..10cee32 100644
--- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json
+++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json
@@ -19,6 +19,7 @@
    "options": "Operation"
   },
   {
+   "default": "0",
    "description": "Time in mins",
    "fieldname": "time_in_mins",
    "fieldtype": "Float",
@@ -38,7 +39,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-07 18:09:18.005578",
+ "modified": "2021-07-15 16:39:41.635362",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Sub Operation",
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 68de0b2..bf1ccb7 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -513,6 +513,60 @@
 		work_order1.save()
 		self.assertEqual(work_order1.operations[0].time_in_mins, 40.0)
 
+	def test_batch_size_for_fg_item(self):
+		fg_item = "Test Batch Size Item For BOM 3"
+		rm1 = "Test Batch Size Item RM 1 For BOM 3"
+
+		frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 0)
+		for item in ["Test Batch Size Item For BOM 3", "Test Batch Size Item RM 1 For BOM 3"]:
+			item_args = {
+				"include_item_in_manufacturing": 1,
+				"is_stock_item": 1
+			}
+
+			if item == fg_item:
+				item_args['has_batch_no'] = 1
+				item_args['create_new_batch'] = 1
+				item_args['batch_number_series'] = 'TBSI3.#####'
+
+			make_item(item, item_args)
+
+		bom_name = frappe.db.get_value("BOM",
+			{"item": fg_item, "is_active": 1, "with_operations": 1}, "name")
+
+		if not bom_name:
+			bom = make_bom(item=fg_item, rate=1000, raw_materials = [rm1], do_not_save=True)
+			bom.save()
+			bom.submit()
+			bom_name = bom.name
+
+		work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1)
+		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
+		for row in ste1.get('items'):
+			if row.is_finished_item:
+				self.assertEqual(row.item_code, fg_item)
+
+		work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1)
+		frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 1)
+		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
+		for row in ste1.get('items'):
+			if row.is_finished_item:
+				self.assertEqual(row.item_code, fg_item)
+
+		work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(),
+			qty=30, do_not_save = True)
+		work_order.batch_size = 10
+		work_order.insert()
+		work_order.submit()
+		self.assertEqual(work_order.has_batch_no, 1)
+		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 30))
+		for row in ste1.get('items'):
+			if row.is_finished_item:
+				self.assertEqual(row.item_code, fg_item)
+				self.assertEqual(row.qty, 10)
+
+		frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 0)
+
 	def test_partial_material_consumption(self):
 		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1)
 		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 44d76d2..3b56854 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -64,11 +64,16 @@
   "description",
   "stock_uom",
   "column_break2",
+  "references_section",
   "material_request",
   "material_request_item",
   "sales_order_item",
+  "column_break_61",
   "production_plan",
   "production_plan_item",
+  "production_plan_sub_assembly_item",
+  "parent_work_order",
+  "bom_level",
   "product_bundle_item",
   "amended_from"
  ],
@@ -546,17 +551,26 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
-  }
+  },
+  {
+    "fieldname": "production_plan_sub_assembly_item",
+    "fieldtype": "Data",
+    "label": "Production Plan Sub-assembly Item",
+    "no_copy": 1,
+    "print_hide": 1,
+    "read_only": 1
+   }
  ],
  "icon": "fa fa-cogs",
  "idx": 1,
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-20 15:19:14.902699",
+ "modified": "2021-06-28 16:19:14.902699",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
+ "nsm_parent_field": "parent_work_order",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 180815d..282b5d0 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -239,7 +239,7 @@
 		self.create_serial_no_batch_no()
 
 	def on_submit(self):
-		if not self.wip_warehouse:
+		if not self.wip_warehouse and not self.skip_transfer:
 			frappe.throw(_("Work-in-Progress Warehouse is required before Submit"))
 		if not self.fg_warehouse:
 			frappe.throw(_("For Warehouse is required before Submit"))
@@ -483,25 +483,24 @@
 
 
 		self.set('operations', [])
-		if not self.bom_no:
+		if not self.bom_no or not frappe.get_cached_value('BOM', self.bom_no, 'with_operations'):
 			return
 
 		operations = []
-		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:
+
+		if self.use_multi_level_bom:
 			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
+			bom_traversal = reversed(bom_tree.level_order_traversal())
 
-			for d in bom_traversal:
-				if d.is_bom:
-					operations.extend(_get_operations(d.name, qty=d.exploded_qty))
+			for node in bom_traversal:
+				if node.is_bom:
+					operations.extend(_get_operations(node.name, qty=node.exploded_qty))
 
-			for correct_index, operation in enumerate(operations, start=1):
-				operation.idx = correct_index
+		bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
+		operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
 
+		for correct_index, operation in enumerate(operations, start=1):
+			operation.idx = correct_index
 
 		self.set('operations', operations)
 		self.calculate_time()
@@ -590,6 +589,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):
@@ -655,7 +655,7 @@
 				for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
 					self.append('required_items', {
 						'rate': item.rate,
-						'amount': item.amount,
+						'amount': item.rate * item.qty,
 						'operation': item.operation or operation,
 						'item_code': item.item_code,
 						'item_name': item.item_name,
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_preview.html b/erpnext/manufacturing/doctype/work_order/work_order_preview.html
new file mode 100644
index 0000000..a4bf93e
--- /dev/null
+++ b/erpnext/manufacturing/doctype/work_order/work_order_preview.html
@@ -0,0 +1,33 @@
+<div style="padding: 15px;">
+	<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">
+			<div style="padding-top: 10px;">
+				<b> Status </b> {{ data.status }}
+			</div>
+			<div style="padding-top: 10px;">
+				<b> Qty to Produce </b> {{ data.qty }}
+			</div>
+			<div style="padding-top: 10px;">
+				<b> Produced Qty </b> {{ data.produced_qty }}
+			</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/Work Order/{{ data.value }}">
+					{{ __("Open Work Order {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>
+</div>
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
index 48907ad..858b554 100644
--- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
+++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py
@@ -20,17 +20,20 @@
 		fields= ['qty','bom_no','qty','scrap','item_code','item_name','description','uom'])
 
 	for item in exploded_items:
+		print(item.bom_no, indent)
 		item["indent"] = indent
 		data.append({
 			'item_code': item.item_code,
 			'item_name': item.item_name,
 			'indent': indent,
+			'bom_level': (frappe.get_cached_value("BOM", item.bom_no, "bom_level")
+				if item.bom_no else ""),
 			'bom': item.bom_no,
 			'qty': item.qty * qty,
 			'uom': item.uom,
 			'description': item.description,
 			'scrap': item.scrap
-			})
+		})
 		if item.bom_no:
 			get_exploded_items(item.bom_no, data, indent=indent+1, qty=item.qty)
 
@@ -69,6 +72,12 @@
 			"width": 100
 		},
 		{
+			"label": "BOM Level",
+			"fieldtype": "Data",
+			"fieldname": "bom_level",
+			"width": 100
+		},
+		{
 			"label": "Standard Description",
 			"fieldtype": "data",
 			"fieldname": "description",
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
index 1c6758e..ed8b939 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -70,12 +70,12 @@
 					ON bom_item.item_code = ledger.item_code
 				{conditions}
 			WHERE
-				bom_item.parent = '{bom}' and bom_item.parenttype='BOM'
+				bom_item.parent = {bom} and bom_item.parenttype='BOM'
 
 			GROUP BY bom_item.item_code""".format(
 				qty_field=qty_field,
 				table=table,
 				conditions=conditions,
-				bom=bom,
+				bom=frappe.db.escape(bom),
 				qty_to_produce=qty_to_produce or 1)
 			)
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
index bd68db1..cb771e4 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js
@@ -68,6 +68,18 @@
 			get_data: function(txt) {
 				return frappe.db.get_link_options('Item', txt);
 			}
+		},
+		{
+			label: __("Workstation"),
+			fieldname: "workstation",
+			fieldtype: "Link",
+			options: "Workstation"
+		},
+		{
+			label: __("Operation"),
+			fieldname: "operation",
+			fieldtype: "Link",
+			options: "Operation"
 		}
 	]
 };
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.json b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json
index 9f08fc3..ecf2b74 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.json
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json
@@ -1,14 +1,16 @@
 {
- "add_total_row": 0,
+ "add_total_row": 1,
+ "columns": [],
  "creation": "2020-04-20 12:00:21.436619",
  "disable_prepared_report": 0,
  "disabled": 0,
  "docstatus": 0,
  "doctype": "Report",
+ "filters": [],
  "idx": 0,
  "is_standard": "Yes",
- "letter_head": "Gadgets International",
- "modified": "2020-04-20 12:00:21.436619",
+ "letter_head": "",
+ "modified": "2020-12-30 11:49:21.713561",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card Summary",
diff --git a/erpnext/accounts/accounts b/erpnext/manufacturing/report/production_plan_summary/__init__.py
similarity index 100%
rename from erpnext/accounts/accounts
rename to erpnext/manufacturing/report/production_plan_summary/__init__.py
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
new file mode 100644
index 0000000..59396fe
--- /dev/null
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
@@ -0,0 +1,32 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Production Plan Summary"] = {
+	"filters": [
+		{
+			fieldname: "production_plan",
+			label: __("Production Plan"),
+			fieldtype: "Link",
+			options: "Production Plan",
+			reqd: 1,
+			get_query: function() {
+				return {
+					filters: {
+						"docstatus": 1
+					}
+				};
+			}
+		}
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+
+		if (column.fieldname == "document_name") {
+			var color = data.pending_qty > 0 ? 'red': 'green';
+			value = `<a style='color:${color}' href="#Form/${data['document_type']}/${data['document_name']}" data-doctype="${data['document_type']}">${data['document_name']}</a>`;
+		}
+
+		return value;
+	},
+};
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.json b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.json
new file mode 100644
index 0000000..33aca21
--- /dev/null
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-12-27 11:43:39.781793",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-12-27 11:43:42.677584",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Production Plan",
+ "report_name": "Production Plan Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Manufacturing User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
new file mode 100644
index 0000000..81b1791
--- /dev/null
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -0,0 +1,136 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import flt
+
+def execute(filters=None):
+	columns, data = [], []
+	data = get_data(filters)
+	columns = get_column(filters)
+
+	return columns, data
+
+def get_data(filters):
+	data = []
+
+	order_details = {}
+	get_work_order_details(filters, order_details)
+	get_purchase_order_details(filters, order_details)
+	get_production_plan_item_details(filters, data, order_details)
+
+	return data
+
+def get_production_plan_item_details(filters, data, order_details):
+	itemwise_indent = {}
+
+	production_plan_doc = frappe.get_cached_doc("Production Plan", filters.get("production_plan"))
+	for row in production_plan_doc.po_items:
+		work_order = frappe.get_cached_value("Work Order", {"production_plan_item": row.name,
+			"bom_no": row.bom_no, "production_item": row.item_code}, "name")
+
+		if row.item_code not in itemwise_indent:
+			itemwise_indent.setdefault(row.item_code, {})
+
+		data.append({
+			"indent": 0,
+			"item_code": row.item_code,
+			"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
+			"qty": row.planned_qty,
+			"document_type": "Work Order",
+			"document_name": work_order,
+			"bom_level": frappe.get_cached_value("BOM", row.bom_no, "bom_level"),
+			"produced_qty": order_details.get((work_order, row.item_code)).get("produced_qty"),
+			"pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code)).get("produced_qty"))
+		})
+
+		get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details)
+
+def get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details):
+	for item in production_plan_doc.sub_assembly_items:
+		if row.name == item.production_plan_item:
+			subcontracted_item = (item.type_of_manufacturing == 'Subcontract')
+
+			if subcontracted_item:
+				docname = frappe.get_cached_value("Purchase Order Item",
+					{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "parent")
+			else:
+				docname = frappe.get_cached_value("Work Order",
+					{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "name")
+
+			data.append({
+				"indent": 1,
+				"item_code": item.production_item,
+				"item_name": item.item_name,
+				"qty": item.qty,
+				"document_type": "Work Order" if not subcontracted_item else "Purchase Order",
+				"document_name": docname,
+				"bom_level": item.bom_level,
+				"produced_qty": order_details.get((docname, item.production_item)).get("produced_qty"),
+				"pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item)).get("produced_qty"))
+			})
+
+def get_work_order_details(filters, order_details):
+	for row in frappe.get_all("Work Order", filters = {"production_plan": filters.get("production_plan")},
+		fields=["name", "produced_qty", "production_plan", "production_item"]):
+		order_details.setdefault((row.name, row.production_item), row)
+
+def get_purchase_order_details(filters, order_details):
+	for row in frappe.get_all("Purchase Order Item", filters = {"production_plan": filters.get("production_plan")},
+		fields=["parent", "received_qty as produced_qty", "item_code"]):
+		order_details.setdefault((row.parent, row.item_code), row)
+
+def get_column(filters):
+	return [
+		{
+			"label": "Finished Good",
+			"fieldtype": "Link",
+			"fieldname": "item_code",
+			"width": 300,
+			"options": "Item"
+		},
+		{
+			"label": "Item Name",
+			"fieldtype": "data",
+			"fieldname": "item_name",
+			"width": 100
+		},
+		{
+			"label": "Document Type",
+			"fieldtype": "Link",
+			"fieldname": "document_type",
+			"width": 150,
+			"options": "DocType"
+		},
+		{
+			"label": "Document Name",
+			"fieldtype": "Dynamic Link",
+			"fieldname": "document_name",
+			"width": 150
+		},
+		{
+			"label": "BOM Level",
+			"fieldtype": "Int",
+			"fieldname": "bom_level",
+			"width": 100
+		},
+		{
+			"label": "Order Qty",
+			"fieldtype": "Float",
+			"fieldname": "qty",
+			"width": 120
+		},
+		{
+			"label": "Received Qty",
+			"fieldtype": "Float",
+			"fieldname": "produced_qty",
+			"width": 160
+		},
+		{
+			"label": "Pending Qty",
+			"fieldtype": "Float",
+			"fieldname": "pending_qty",
+			"width": 110
+		}
+	]
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
index fb047b2..612dad0 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -19,7 +19,7 @@
 	return columns, data, None, chart_data
 
 def get_data(filters):
-	query_filters = {"docstatus": 1}
+	query_filters = {"docstatus": ("<", 2)}
 
 	fields = ["name", "status", "sales_order", "production_item", "qty", "produced_qty",
 		"planned_start_date", "planned_end_date", "actual_start_date", "actual_end_date", "lead_time"]
@@ -62,7 +62,8 @@
 		"Not Started": 0,
 		"In Process": 0,
 		"Stopped": 0,
-		"Completed": 0
+		"Completed": 0,
+		"Draft": 0
 	}
 
 	for d in data:
diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json
index f190cfa..7c1baf1 100644
--- a/erpnext/non_profit/doctype/member/member.json
+++ b/erpnext/non_profit/doctype/member/member.json
@@ -26,7 +26,7 @@
   "razorpay_details_section",
   "subscription_id",
   "customer_id",
-  "subscription_activated",
+  "subscription_status",
   "column_break_21",
   "subscription_start",
   "subscription_end"
@@ -152,12 +152,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "0",
-   "fieldname": "subscription_activated",
-   "fieldtype": "Check",
-   "label": "Subscription Activated"
-  },
-  {
    "fieldname": "subscription_start",
    "fieldtype": "Date",
    "label": "Subscription Start "
@@ -166,11 +160,17 @@
    "fieldname": "subscription_end",
    "fieldtype": "Date",
    "label": "Subscription End"
+  },
+  {
+   "fieldname": "subscription_status",
+   "fieldtype": "Select",
+   "label": "Subscription Status",
+   "options": "\nActive\nHalted"
   }
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-11-09 12:12:10.174647",
+ "modified": "2021-07-11 14:27:26.368039",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Member",
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index 30be585..67828d6 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -84,7 +84,9 @@
 		"email_id": user_details.email,
 		"pan_number": user_details.pan or None,
 		"membership_type": user_details.plan_id,
-		"subscription_id": user_details.subscription_id or None
+		"customer_id": user_details.customer_id or None,
+		"subscription_id": user_details.subscription_id or None,
+		"subscription_status": user_details.subscription_status or ""
 	})
 
 	member.insert(ignore_permissions=True)
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index e8ae618..b584116 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -196,11 +196,14 @@
 	return invoice
 
 
-def get_member_based_on_subscription(subscription_id, email):
-	members = frappe.get_all("Member", filters={
-					"subscription_id": subscription_id,
-					"email_id": email
-				}, order_by="creation desc")
+def get_member_based_on_subscription(subscription_id, email=None, customer_id=None):
+	filters = {"subscription_id": subscription_id}
+	if email:
+		filters.update({"email_id": email})
+	if customer_id:
+		filters.update({"customer_id": customer_id})
+
+	members = frappe.get_all("Member", filters=filters, order_by="creation desc")
 
 	try:
 		return frappe.get_doc("Member", members[0]["name"])
@@ -209,8 +212,6 @@
 
 
 def verify_signature(data, endpoint="Membership"):
-	if frappe.flags.in_test or os.environ.get("CI"):
-		return True
 	signature = frappe.request.headers.get("X-Razorpay-Signature")
 
 	settings = frappe.get_doc("Non Profit Settings")
@@ -225,16 +226,7 @@
 @frappe.whitelist(allow_guest=True)
 def trigger_razorpay_subscription(*args, **kwargs):
 	data = frappe.request.get_data(as_text=True)
-	try:
-		verify_signature(data)
-	except Exception as e:
-		log = frappe.log_error(e, "Membership Webhook Verification Error")
-		notify_failure(log)
-		return { "status": "Failed", "reason": e}
-
-	if isinstance(data, six.string_types):
-		data = json.loads(data)
-	data = frappe._dict(data)
+	data = process_request_data(data)
 
 	subscription = data.payload.get("subscription", {}).get("entity", {})
 	subscription = frappe._dict(subscription)
@@ -281,7 +273,7 @@
 		# Update membership values
 		member.subscription_start = datetime.fromtimestamp(subscription.start_at)
 		member.subscription_end = datetime.fromtimestamp(subscription.end_at)
-		member.subscription_activated = 1
+		member.subscription_status = "Active"
 		member.flags.ignore_mandatory = True
 		member.save()
 
@@ -294,9 +286,67 @@
 		message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
 		log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
 		notify_failure(log)
-		return { "status": "Failed", "reason": e}
+		return {"status": "Failed", "reason": e}
 
-	return { "status": "Success" }
+	return {"status": "Success"}
+
+
+@frappe.whitelist(allow_guest=True)
+def update_halted_razorpay_subscription(*args, **kwargs):
+	"""
+	When all retries have been exhausted, Razorpay moves the subscription to the halted state.
+	The customer has to manually retry the charge or change the card linked to the subscription,
+	for the subscription to move back to the active state.
+	"""
+	if frappe.request:
+		data = frappe.request.get_data(as_text=True)
+		data = process_request_data(data)
+	elif frappe.flags.in_test:
+		data = kwargs.get("data")
+		data = frappe._dict(data)
+	else:
+		return
+
+	if not data.event == "subscription.halted":
+		return
+
+	subscription = data.payload.get("subscription", {}).get("entity", {})
+	subscription = frappe._dict(subscription)
+
+	try:
+		member = get_member_based_on_subscription(subscription.id, customer_id=subscription.customer_id)
+		if not member:
+			frappe.throw(_("Member with Razorpay Subscription ID {0} not found").format(subscription.id))
+
+		member.subscription_status = "Halted"
+		member.flags.ignore_mandatory = True
+		member.save()
+
+		if subscription.get("notes"):
+			member = get_additional_notes(member, subscription)
+
+	except Exception as e:
+		message = "{0}\n\n{1}".format(e, frappe.get_traceback())
+		log = frappe.log_error(message, _("Error updating halted status for member {0}").format(member.name))
+		notify_failure(log)
+		return {"status": "Failed", "reason": e}
+
+	return {"status": "Success"}
+
+
+def process_request_data(data):
+	try:
+		verify_signature(data)
+	except Exception as e:
+		log = frappe.log_error(e, "Membership Webhook Verification Error")
+		notify_failure(log)
+		return {"status": "Failed", "reason": e}
+
+	if isinstance(data, six.string_types):
+		data = json.loads(data)
+	data = frappe._dict(data)
+
+	return data
 
 
 def get_company_for_memberships():
@@ -362,4 +412,4 @@
 			`tabMembership` SET `status` = 'Expired'
 		WHERE
 			`status` not in ('Cancelled') AND `to_date` < %s
-		""", (nowdate()))
\ No newline at end of file
+		""", (nowdate()))
diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py
index 31da792..0f5a9be 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -6,6 +6,7 @@
 import frappe
 import erpnext
 from erpnext.non_profit.doctype.member.member import create_member
+from erpnext.non_profit.doctype.membership.membership import update_halted_razorpay_subscription
 from frappe.utils import nowdate, add_months
 
 class TestMembership(unittest.TestCase):
@@ -13,11 +14,16 @@
 		plan = setup_membership()
 
 		# make test member
-		self.member_doc = create_member(frappe._dict({
-				'fullname': "_Test_Member",
-				'email': "_test_member_erpnext@example.com",
-				'plan_id': plan.name
-		}))
+		self.member_doc = create_member(
+			frappe._dict({
+				"fullname": "_Test_Member",
+				"email": "_test_member_erpnext@example.com",
+				"plan_id": plan.name,
+				"subscription_id": "sub_DEX6xcJ1HSW4CR",
+				"customer_id": "cust_C0WlbKhp3aLA7W",
+				"subscription_status": "Active"
+			})
+		)
 		self.member_doc.make_customer_and_link()
 		self.member = self.member_doc.name
 
@@ -51,6 +57,20 @@
 			"to_date": add_months(nowdate(), 3),
 		})
 
+	def test_halted_memberships(self):
+		make_membership(self.member, {
+			"from_date": add_months(nowdate(), 2),
+			"to_date": add_months(nowdate(), 3)
+		})
+
+		self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Active")
+		payload = get_subscription_payload()
+		update_halted_razorpay_subscription(data=payload)
+		self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Halted")
+
+	def tearDown(self):
+		frappe.db.rollback()
+
 def set_config(key, value):
 	frappe.db.set_value("Non Profit Settings", None, key, value)
 
@@ -115,4 +135,28 @@
 	else:
 		plan = frappe.get_doc("Membership Type", "_rzpy_test_milythm")
 
-	return plan
\ No newline at end of file
+	return plan
+
+def get_subscription_payload():
+	return {
+		"entity": "event",
+		"account_id": "acc_BFQ7uQEaa7j2z7",
+		"event": "subscription.halted",
+		"contains": [
+			"subscription"
+		],
+		"payload": {
+			"subscription": {
+				"entity": {
+					"id": "sub_DEX6xcJ1HSW4CR",
+					"entity": "subscription",
+					"plan_id": "_rzpy_test_milythm",
+					"customer_id": "cust_C0WlbKhp3aLA7W",
+					"status": "halted",
+					"notes": {
+						"Important": "Notes for Internal Reference"
+					},
+				}
+			}
+		}
+	}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 986b0c5..a029627a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -290,5 +290,10 @@
 erpnext.patches.v13_0.set_training_event_attendance
 erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice
 erpnext.patches.v13_0.rename_issue_status_hold_to_on_hold
+erpnext.patches.v13_0.update_response_by_variance
 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
+erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
+erpnext.patches.v13_0.update_subscription_status_in_memberships
+erpnext.patches.v13_0.update_amt_in_work_order_required_items
diff --git a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
new file mode 100644
index 0000000..d7ad1fc
--- /dev/null
+++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py
@@ -0,0 +1,111 @@
+# Copyright (c) 2020, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.utils import cstr, flt, cint
+from erpnext.stock.stock_ledger import make_sl_entries
+from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
+
+def execute():
+	if not frappe.db.has_column('Work Order', 'has_batch_no'):
+		return
+
+	frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings')
+	if cint(frappe.db.get_single_value('Manufacturing Settings', 'make_serial_no_batch_from_work_order')):
+		return
+
+	frappe.reload_doc('manufacturing', 'doctype', 'work_order')
+	filters = {
+		'docstatus': 1,
+		'produced_qty': ('>', 0),
+		'creation': ('>=', '2021-06-29 00:00:00'),
+		'has_batch_no': 1
+	}
+
+	fields = ['name', 'production_item']
+
+	work_orders = [d.name for d in frappe.get_all('Work Order', filters = filters, fields=fields)]
+
+	if not work_orders:
+		return
+
+	repost_stock_entries = []
+	stock_entries = frappe.db.sql_list('''
+		SELECT
+			se.name
+		FROM
+			`tabStock Entry` se
+		WHERE
+			se.purpose = 'Manufacture' and se.docstatus < 2 and se.work_order in {work_orders}
+			and not exists(
+				select name from `tabStock Entry Detail` sed where sed.parent = se.name and sed.is_finished_item = 1
+			)
+		Order BY
+			se.posting_date, se.posting_time
+	'''.format(work_orders=tuple(work_orders)))
+
+	if stock_entries:
+		print('Length of stock entries', len(stock_entries))
+
+	for stock_entry in stock_entries:
+		doc = frappe.get_doc('Stock Entry', stock_entry)
+		doc.set_work_order_details()
+		doc.load_items_from_bom()
+		doc.calculate_rate_and_amount()
+		set_expense_account(doc)
+		doc.make_batches('t_warehouse')
+
+		if doc.docstatus == 0:
+			doc.save()
+		else:
+			repost_stock_entry(doc)
+			repost_stock_entries.append(doc)
+
+	for repost_doc in repost_stock_entries:
+		repost_future_sle_and_gle(repost_doc)
+
+def set_expense_account(doc):
+	for row in doc.items:
+		if row.is_finished_item and not row.expense_account:
+			row.expense_account = frappe.get_cached_value('Company', doc.company, 'stock_adjustment_account')
+
+def repost_stock_entry(doc):
+	doc.db_update()
+	for child_row in doc.items:
+		if child_row.is_finished_item:
+			child_row.db_update()
+
+	sl_entries = []
+	finished_item_row = doc.get_finished_item_row()
+	get_sle_for_target_warehouse(doc, sl_entries, finished_item_row)
+
+	if sl_entries:
+		try:
+			make_sl_entries(sl_entries, True)
+		except Exception:
+			print(f'SLE entries not posted for the stock entry {doc.name}')
+			traceback = frappe.get_traceback()
+			frappe.log_error(traceback)
+
+def get_sle_for_target_warehouse(doc, sl_entries, finished_item_row):
+	for d in doc.get('items'):
+		if cstr(d.t_warehouse) and finished_item_row and d.name == finished_item_row.name:
+			sle = doc.get_sl_entries(d, {
+				"warehouse": cstr(d.t_warehouse),
+				"actual_qty": flt(d.transfer_qty),
+				"incoming_rate": flt(d.valuation_rate)
+			})
+
+			sle.recalculate_rate = 1
+			sl_entries.append(sle)
+
+def repost_future_sle_and_gle(doc):
+	args = frappe._dict({
+		"posting_date": doc.posting_date,
+		"posting_time": doc.posting_time,
+		"voucher_type": doc.doctype,
+		"voucher_no": doc.name,
+		"company": doc.company
+	})
+
+	create_repost_item_valuation_entry(args)
diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
new file mode 100644
index 0000000..eae5ff6
--- /dev/null
+++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
@@ -0,0 +1,10 @@
+import frappe
+
+def execute():
+	""" Correct amount in child table of required items table."""
+
+	frappe.reload_doc("manufacturing", "doctype", "work_order")
+	frappe.reload_doc("manufacturing", "doctype", "work_order_item")
+
+	frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""")
+
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/v13_0/update_response_by_variance.py b/erpnext/patches/v13_0/update_response_by_variance.py
new file mode 100644
index 0000000..ef4d976
--- /dev/null
+++ b/erpnext/patches/v13_0/update_response_by_variance.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2020, 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('DocType', 'Issue') and frappe.db.count('Issue'):
+		invalid_issues = frappe.get_all('Issue', {
+			'first_responded_on': ['is', 'set'],
+			'response_by_variance': ['<', 0]
+		}, ["name", "response_by_variance", "timestampdiff(Second, `first_responded_on`, `response_by`) as variance"])
+
+		# issues which has response_by_variance set as -ve
+		# but diff between first_responded_on & response_by is +ve i.e SLA isn't failed
+		invalid_issues = [d for d in invalid_issues if d.get('variance') > 0]
+
+		for issue in invalid_issues:
+			frappe.db.set_value('Issue', issue.get('name'), 'response_by_variance', issue.get('variance'), update_modified=False)
+
+		invalid_issues = frappe.get_all('Issue', {
+			'resolution_date': ['is', 'set'],
+			'resolution_by_variance': ['<', 0]
+		}, ["name", "resolution_by_variance", "timestampdiff(Second, `resolution_date`, `resolution_by`) as variance"])
+
+		# issues which has resolution_by_variance set as -ve
+		# but diff between resolution_date & resolution_by is +ve i.e SLA isn't failed
+		invalid_issues = [d for d in invalid_issues if d.get('variance') > 0]
+
+		for issue in invalid_issues:
+			frappe.db.set_value('Issue', issue.get('name'), 'resolution_by_variance', issue.get('variance'), update_modified=False)
diff --git a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
new file mode 100644
index 0000000..28e650e
--- /dev/null
+++ b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+	if frappe.db.exists('DocType', 'Member'):
+		frappe.reload_doc('Non Profit', 'doctype', 'Member')
+
+		if frappe.db.has_column('Member', 'subscription_activated'):
+			frappe.db.sql('UPDATE `tabMember` SET subscription_status = "Active" WHERE subscription_activated = 1')
+			frappe.db.sql_ddl('ALTER table `tabMember` DROP COLUMN subscription_activated')
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js
index dbf7514..e9e6f81 100644
--- a/erpnext/payroll/doctype/salary_component/salary_component.js
+++ b/erpnext/payroll/doctype/salary_component/salary_component.js
@@ -4,11 +4,18 @@
 frappe.ui.form.on('Salary Component', {
 	setup: function(frm) {
 		frm.set_query("account", "accounts", function(doc, cdt, cdn) {
-			var d = locals[cdt][cdn];
+			let d = frappe.get_doc(cdt, cdn);
+
+			let root_type = "Liability";
+			if (frm.doc.type == "Deduction") {
+				root_type = "Expense";
+			}
+
 			return {
 				filters: {
 					"is_group": 0,
-					"company": d.company
+					"company": d.company,
+					"root_type": root_type
 				}
 			};
 		});
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index c55bec8..f82b0d5 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -1088,6 +1088,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 ce88cc3..d730fcf 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -481,15 +481,19 @@
 	if not salary_structure:
 		salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
 
+	employee = frappe.db.get_value("Employee",
+					{
+						"user_id": user
+					},
+					["name", "company", "employee_name"],
+					as_dict=True)
 
-	employee = frappe.db.get_value("Employee", {"user_id": user})
-	salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee)
+	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 e7d123c..374dd7e 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -119,26 +119,25 @@
 	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(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()
+	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:
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
index 500543c..05a5366 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.py
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -95,6 +95,7 @@
 				"amount": salary.net_pay,
 			}
 			data.append(row)
+
 	return columns, data
 
 def get_bank_accounts():
@@ -116,7 +117,7 @@
 	entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"])
 
 	payment_accounts = [d.payment_account for d in entries]
-	set_company_account(payment_accounts, entries)
+	entries = set_company_account(payment_accounts, entries)
 	return entries
 
 def get_salary_slips(payroll_entries):
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index d77eb2c..d60b1a2 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -2,6 +2,7 @@
 from frappe.utils import cint
 from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager
 from erpnext.shopping_cart.product_info import get_product_info_for_website
+from erpnext.setup.doctype.item_group.item_group import get_child_groups
 
 def get_field_filter_data():
 	product_settings = get_product_settings()
@@ -89,6 +90,7 @@
 def get_products_html_for_website(field_filters=None, attribute_filters=None):
 	field_filters = frappe.parse_json(field_filters)
 	attribute_filters = frappe.parse_json(attribute_filters)
+	set_item_group_filters(field_filters)
 
 	items = get_products_for_website(field_filters, attribute_filters)
 	html = ''.join(get_html_for_items(items))
@@ -98,6 +100,10 @@
 
 	return html
 
+def set_item_group_filters(field_filters):
+	if field_filters is not None and 'item_group' in field_filters:
+		field_filters['item_group'] = [ig[0] for ig in get_child_groups(field_filters['item_group'])]
+
 
 def get_item_codes_by_attributes(attribute_filters, template_item_code=None):
 	items = []
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 39a6024..5976e01 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -77,9 +77,6 @@
 		if flt(self.progress or 0) > 100:
 			frappe.throw(_("Progress % for a task cannot be more than 100."))
 
-		if flt(self.progress) == 100:
-			self.status = 'Completed'
-
 		if self.status == 'Completed':
 			self.progress = 100
 
diff --git a/erpnext/public/js/contact.js b/erpnext/public/js/contact.js
new file mode 100644
index 0000000..41a0e8a
--- /dev/null
+++ b/erpnext/public/js/contact.js
@@ -0,0 +1,16 @@
+
+
+frappe.ui.form.on("Contact", {
+	refresh(frm) {
+		frm.set_query('link_doctype', "links", function() {
+			return {
+				query: "frappe.contacts.address_and_contact.filter_dynamic_link_doctypes",
+				filters: {
+					fieldtype: ["in", ["HTML", "Text Editor"]],
+					fieldname: ["in", ["contact_html", "company_description"]],
+				}
+			};
+		});
+		frm.refresh_field("links");
+	}
+});
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 0471704..a495a9b 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -65,26 +65,25 @@
 		this.frm.refresh_fields();
 	}
 
-	calculate_discount_amount(){
+	calculate_discount_amount() {
 		if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
+			this.calculate_item_values();
+			this.calculate_net_total();
 			this.set_discount_amount();
 			this.apply_discount_amount();
 		}
 	}
 
 	_calculate_taxes_and_totals() {
-		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()
-		]);
+		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();
 	}
 
 	validate_conversion_rate() {
@@ -268,46 +267,6 @@
 		frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
 	}
 
-	update_item_tax_map() {
-		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(item_tax_map) {
 		let me = this;
 
@@ -632,8 +591,6 @@
 				tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
 			});
 		}
-
-		this.frm.refresh_fields();
 	}
 
 	set_discount_amount() {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 8360337..33366db 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -846,9 +846,9 @@
 
 				frappe.run_serially([
 					() => me.frm.script_manager.trigger("currency"),
+					() => me.update_item_tax_map(),
 					() => me.apply_default_taxes(),
-					() => me.apply_pricing_rule(),
-					() => me.calculate_taxes_and_totals()
+					() => me.apply_pricing_rule()
 				]);
 			}
 		}
@@ -1807,6 +1807,46 @@
 		]);
 	}
 
+	update_item_tax_map() {
+		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);
+							}
+						});
+					}
+				}
+			});
+		}
+	}
+
 	item_tax_template(doc, cdt, cdn) {
 		var me = this;
 		if(me.frm.updating_party_details) return;
diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js
index 5c9a453..d0c935f 100644
--- a/erpnext/public/js/help_links.js
+++ b/erpnext/public/js/help_links.js
@@ -54,7 +54,7 @@
 
 frappe.help.help_links["Form/System Settings"] = [
 	{
-		label: "Naming Series",
+		label: "System Settings",
 		url: docsUrl + "user/manual/en/setting-up/settings/system-settings",
 	},
 ];
@@ -206,7 +206,7 @@
 		label: "PayPal Settings",
 		url:
 			docsUrl +
-			"user/manual/en/setting-up/integrations/paypal-integration",
+			"user/manual/en/erpnext_integration/paypal-integration",
 	},
 ];
 
@@ -215,14 +215,14 @@
 		label: "Razorpay Settings",
 		url:
 			docsUrl +
-			"user/manual/en/setting-up/integrations/razorpay-integration",
+			"user/manual/en/erpnext_integration/razorpay-integration",
 	},
 ];
 
 frappe.help.help_links["Form/Dropbox Settings"] = [
 	{
 		label: "Dropbox Settings",
-		url: docsUrl + "user/manual/en/setting-up/integrations/dropbox-backup",
+		url: docsUrl + "user/manual/en/erpnext_integration/dropbox-backup",
 	},
 ];
 
@@ -230,7 +230,7 @@
 	{
 		label: "LDAP Settings",
 		url:
-			docsUrl + "user/manual/en/setting-up/integrations/ldap-integration",
+			docsUrl + "user/manual/en/erpnext_integration/ldap-integration",
 	},
 ];
 
@@ -239,7 +239,7 @@
 		label: "Stripe Settings",
 		url:
 			docsUrl +
-			"user/manual/en/setting-up/integrations/stripe-integration",
+			"user/manual/en/erpnext_integration/stripe-integration",
 	},
 ];
 
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/customer_quick_entry.js b/erpnext/public/js/utils/customer_quick_entry.js
index efb8dd9..d2c5c72 100644
--- a/erpnext/public/js/utils/customer_quick_entry.js
+++ b/erpnext/public/js/utils/customer_quick_entry.js
@@ -1,8 +1,8 @@
 frappe.provide('frappe.ui.form');
 
 frappe.ui.form.CustomerQuickEntryForm = class CustomerQuickEntryForm extends frappe.ui.form.QuickEntryForm {
-	constructor(doctype, after_insert) {
-		super(doctype, after_insert);
+	constructor(doctype, after_insert, init_callback, doc, force) {
+		super(doctype, after_insert, init_callback, doc, force);
 		this.skip_redirect_on_error = true;
 	}
 
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/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index 6415204..ea39fe1 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -214,9 +214,8 @@
 
 			for d in item_details:
 				if d.item_code not in self.invoice_items.get(d.parent, {}):
-					self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
-						sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in item_details
-							if i.item_code == d.item_code and i.parent == d.parent))
+					self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+					self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
 
 				if d.is_nil_exempt and d.item_code not in self.is_nil_exempt:
 					self.is_nil_exempt.append(d.item_code)
@@ -322,6 +321,9 @@
 									inter_state_supply_details[(gst_category, place_of_supply)]['txval'] += taxable_value
 									inter_state_supply_details[(gst_category, place_of_supply)]['iamt'] += (taxable_value * rate /100)
 
+			if self.invoice_cess.get(inv):
+				self.report_dict['sup_details']['osup_det']['csamt'] += flt(self.invoice_cess.get(inv), 2)
+
 		self.set_inter_state_supply(inter_state_supply_details)
 
 	def set_supplies_liable_to_reverse_charge(self):
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/setup.py b/erpnext/regional/india/setup.py
index 3e0b9b7..9265460 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -12,7 +12,10 @@
 from frappe.utils import today
 
 def setup(company=None, patch=True):
-	setup_company_independent_fixtures(patch=patch)
+	# Company independent fixtures should be called only once at the first company setup
+	if frappe.db.count('Company', {'country': 'India'}) <=1:
+		setup_company_independent_fixtures(patch=patch)
+
 	if not patch:
 		make_fixtures(company)
 
@@ -25,6 +28,7 @@
 	frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
 	create_gratuity_rule()
 	add_print_formats()
+	update_accounts_settings_for_taxes()
 
 def add_hsn_sac_codes():
 	if frappe.flags.in_test and frappe.flags.created_hsn_codes:
@@ -121,10 +125,12 @@
 def make_property_setters(patch=False):
 	# GST rules do not allow for an invoice no. bigger than 16 characters
 	journal_entry_types = frappe.get_meta("Journal Entry").get_options("voucher_type").split("\n") + ['Reversal Of ITC']
+	sales_invoice_series = ['SINV-.YY.-', 'SRET-.YY.-', ''] + frappe.get_meta("Sales Invoice").get_options("naming_series").split("\n")
+	purchase_invoice_series = ['PINV-.YY.-', 'PRET-.YY.-', ''] + frappe.get_meta("Purchase Invoice").get_options("naming_series").split("\n")
 
 	if not patch:
-		make_property_setter('Sales Invoice', 'naming_series', 'options', 'SINV-.YY.-\nSRET-.YY.-', '')
-		make_property_setter('Purchase Invoice', 'naming_series', 'options', 'PINV-.YY.-\nPRET-.YY.-', '')
+		make_property_setter('Sales Invoice', 'naming_series', 'options', '\n'.join(sales_invoice_series), '')
+		make_property_setter('Purchase Invoice', 'naming_series', 'options', '\n'.join(purchase_invoice_series), '')
 		make_property_setter('Journal Entry', 'voucher_type', 'options', '\n'.join(journal_entry_types), '')
 
 def make_custom_fields(update=True):
@@ -680,7 +686,7 @@
 
 def make_fixtures(company=None):
 	docs = []
-	company = company.name if company else frappe.db.get_value("Global Defaults", None, "default_company")
+	company = company or frappe.db.get_value("Global Defaults", None, "default_company")
 
 	set_salary_components(docs)
 	set_tds_account(docs, company)
@@ -698,6 +704,53 @@
 	# create records for Tax Withholding Category
 	set_tax_withholding_category(company)
 
+def update_regional_tax_settings(country, company):
+	# Will only add default GST accounts if present
+	input_account_names = ['Input Tax CGST', 'Input Tax SGST', 'Input Tax IGST']
+	output_account_names = ['Output Tax CGST', 'Output Tax SGST', 'Output Tax IGST']
+	rcm_accounts = ['Input Tax CGST RCM', 'Input Tax SGST RCM', 'Input Tax IGST RCM']
+	gst_settings = frappe.get_single('GST Settings')
+	existing_account_list = []
+
+	for account in gst_settings.get('gst_accounts'):
+		for key in ['cgst_account', 'sgst_account', 'igst_account']:
+			existing_account_list.append(account.get(key))
+
+	gst_accounts = frappe._dict(frappe.get_all("Account",
+		{'company': company, 'account_name': ('in', input_account_names +
+			output_account_names + rcm_accounts)}, ['account_name', 'name'], as_list=1))
+
+	add_accounts_in_gst_settings(company,  input_account_names, gst_accounts,
+		existing_account_list, gst_settings)
+	add_accounts_in_gst_settings(company, output_account_names, gst_accounts,
+		existing_account_list, gst_settings)
+	add_accounts_in_gst_settings(company, rcm_accounts, gst_accounts,
+		existing_account_list, gst_settings, is_reverse_charge=1)
+
+	gst_settings.save()
+
+def add_accounts_in_gst_settings(company, account_names, gst_accounts,
+	existing_account_list, gst_settings, is_reverse_charge=0):
+	accounts_not_added = 1
+
+	for account in account_names:
+		# Default Account Added does not exists
+		if not gst_accounts.get(account):
+			accounts_not_added = 0
+
+		# Check if already added in GST Settings
+		if gst_accounts.get(account) in existing_account_list:
+			accounts_not_added = 0
+
+	if accounts_not_added:
+		gst_settings.append('gst_accounts', {
+			'company': company,
+			'cgst_account': gst_accounts.get(account_names[0]),
+			'sgst_account': gst_accounts.get(account_names[1]),
+			'igst_account': gst_accounts.get(account_names[2]),
+			'is_reverse_charge_account': is_reverse_charge
+		})
+
 def set_salary_components(docs):
 	docs.extend([
 		{'doctype': 'Salary Component', 'salary_component': 'Professional Tax',
@@ -731,13 +784,14 @@
 	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:
-			doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
+		else:
+			doc = frappe.get_doc("Tax Withholding Category", d.get("name"), for_update=True)
 
 			if accounts:
 				doc.append("accounts", accounts[0])
@@ -749,11 +803,12 @@
 					doc.append("rates", d.get('rates')[0])
 
 			doc.flags.ignore_permissions = True
+			doc.flags.ignore_validate = True
 			doc.flags.ignore_mandatory = True
+			doc.flags.ignore_links = True
 			doc.save()
 
 def set_tds_account(docs, company):
-	abbr = frappe.get_value("Company", company, "abbr")
 	parent_account = frappe.db.get_value("Account", filters = {"account_name": "Duties and Taxes", "company": company})
 	if parent_account:
 		docs.extend([
@@ -912,7 +967,6 @@
 	]
 
 def create_gratuity_rule():
-
 	# Standard Indain Gratuity Rule
 	if not frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"):
 		rule = frappe.new_doc("Gratuity Rule")
@@ -930,3 +984,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 81c0918..fbe47d0 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -431,9 +431,11 @@
 		company_address = frappe.get_doc('Address', doc.company_address)
 		billing_address = frappe.get_doc('Address', doc.customer_address)
 
+		#added dispatch address
+		dispatch_address = frappe.get_doc('Address', doc.dispatch_address_name) if doc.dispatch_address_name else company_address
 		shipping_address = frappe.get_doc('Address', doc.shipping_address_name)
 
-		data = get_address_details(data, doc, company_address, billing_address)
+		data = get_address_details(data, doc, company_address, billing_address, dispatch_address)
 
 		data.itemList = []
 		data.totalValue = doc.total
@@ -519,10 +521,10 @@
 			`tabDynamic Link`.link_name = %(company)s""", {"company": company})
 	return company_gstins
 
-def get_address_details(data, doc, company_address, billing_address):
+def get_address_details(data, doc, company_address, billing_address, dispatch_address):
 	data.fromPincode = validate_pincode(company_address.pincode, 'Company Address')
-	data.fromStateCode = data.actualFromStateCode = validate_state_code(
-		company_address.gst_state_number, 'Company Address')
+	data.fromStateCode = validate_state_code(company_address.gst_state_number, 'Company Address')
+	data.actualFromStateCode = validate_state_code(dispatch_address.gst_state_number, 'Dispatch Address')
 
 	if not doc.billing_address_gstin or len(doc.billing_address_gstin) < 15:
 		data.toGstin = 'URP'
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 1096159..b81fa81 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -217,9 +217,8 @@
 
 		for d in items:
 			if d.item_code not in self.invoice_items.get(d.parent, {}):
-				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
-					sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items
-						if i.item_code == d.item_code and i.parent == d.parent))
+				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
+				self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0)
 
 				item_tax_rate = {}
 
@@ -584,7 +583,7 @@
 def get_json(filters, report_name, data):
 	filters = json.loads(filters)
 	report_data = json.loads(data)
-	gstin = get_company_gstin_number(filters["company"], filters["company_address"])
+	gstin = get_company_gstin_number(filters.get("company"), filters.get("company_address"))
 
 	fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
 
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 825b170..2849466 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -130,6 +130,10 @@
 				erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
 			}, __('Create'));
 
+			frm.add_custom_button(__('Get Customer Group Details'), function () {
+				frm.trigger("get_customer_group_details");
+			}, __('Actions'));
+
 			// indicator
 			erpnext.utils.set_party_dashboard_indicators(frm);
 
@@ -145,4 +149,15 @@
 		if(frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name);
 
 	},
-});
\ No newline at end of file
+	get_customer_group_details: function(frm) {
+		frappe.call({
+			method: "get_customer_group_details",
+			doc: frm.doc,
+			callback: function() {
+				frm.refresh();
+			}
+		});
+
+	}
+});
+
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 818888c..3b62081 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -78,6 +78,29 @@
 			if sum(member.allocated_percentage or 0 for member in self.sales_team) != 100:
 				frappe.throw(_("Total contribution percentage should be equal to 100"))
 
+	@frappe.whitelist()
+	def get_customer_group_details(self):
+		doc = frappe.get_doc('Customer Group', self.customer_group)
+		self.accounts = self.credit_limits = []
+		self.payment_terms = self.default_price_list = ""
+
+		tables = [["accounts", "account"], ["credit_limits", "credit_limit"]]
+		fields = ["payment_terms", "default_price_list"]
+
+		for row in tables:
+			table, field = row[0], row[1]
+			if not doc.get(table): continue
+
+			for entry in doc.get(table):
+				child = self.append(table)
+				child.update({"company": entry.company, field: entry.get(field)})
+
+		for field in fields:
+			if not doc.get(field): continue
+			self.update({field: doc.get(field)})
+
+		self.save()
+
 	def check_customer_group_change(self):
 		frappe.flags.customer_group_changed = False
 
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 7761aa7..b1a5b52 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -27,6 +27,42 @@
 	def tearDown(self):
 		set_credit_limit('_Test Customer', '_Test Company', 0)
 
+	def test_get_customer_group_details(self):
+		doc = frappe.new_doc("Customer Group")
+		doc.customer_group_name = "_Testing Customer Group"
+		doc.payment_terms = "_Test Payment Term Template 3"
+		doc.accounts = []
+		doc.default_price_list = "Standard Buying"
+		doc.credit_limits = []
+		test_account_details = {
+			"company": "_Test Company",
+			"account": "Creditors - _TC",
+		}
+		test_credit_limits = {
+			"company": "_Test Company",
+			"credit_limit": 350000
+		}
+		doc.append("accounts", test_account_details)
+		doc.append("credit_limits", test_credit_limits)
+		doc.insert()
+
+		c_doc = frappe.new_doc("Customer")
+		c_doc.customer_name = "Testing Customer"
+		c_doc.customer_group = "_Testing Customer Group"
+		c_doc.payment_terms = c_doc.default_price_list = ""
+		c_doc.accounts = c_doc.credit_limits= []
+		c_doc.insert()
+		c_doc.get_customer_group_details()
+		self.assertEqual(c_doc.payment_terms, "_Test Payment Term Template 3")
+
+		self.assertEqual(c_doc.accounts[0].company, "_Test Company")
+		self.assertEqual(c_doc.accounts[0].account, "Creditors - _TC")
+
+		self.assertEqual(c_doc.credit_limits[0].company, "_Test Company")
+		self.assertEqual(c_doc.credit_limits[0].credit_limit, 350000)
+		c_doc.delete()
+		doc.delete()
+
 	def test_party_details(self):
 		from erpnext.accounts.party import get_party_details
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 762b6f1..d31db82 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -38,6 +38,8 @@
   "col_break46",
   "shipping_address_name",
   "shipping_address",
+  "dispatch_address_name",
+  "dispatch_address",
   "customer_group",
   "territory",
   "currency_and_price_list",
@@ -1486,13 +1488,29 @@
    "fieldname": "disable_rounded_total",
    "fieldtype": "Check",
    "label": "Disable Rounded Total"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "dispatch_address_name",
+   "fieldtype": "Link",
+   "label": "Dispatch Address Name",
+   "options": "Address",
+   "print_hide": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "depends_on": "dispatch_address_name",
+   "fieldname": "dispatch_address",
+   "fieldtype": "Small Text",
+   "label": "Dispatch Address",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-15 23:55:13.439068",
+ "modified": "2021-07-08 21:37:44.177493",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
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 38508c2..6e36d28 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -367,15 +367,16 @@
 			`<div class="add-discount-field"></div>`
 		);
 		const me = this;
+		const frm = me.events.get_frm();
+		let discount = frm.doc.additional_discount_percentage;
 
 		this.discount_field = frappe.ui.form.make_control({
 			df: {
 				label: __('Discount'),
 				fieldtype: 'Data',
-				placeholder: __('Enter discount percentage.'),
+				placeholder: ( discount ? discount + '%' :  __('Enter discount percentage.') ),
 				input_class: 'input-xs',
 				onchange: function() {
-					const frm = me.events.get_frm();
 					if (flt(this.value) != 0) {
 						frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', flt(this.value));
 						me.hide_discount_control(this.value);
@@ -965,8 +966,23 @@
 		});
 	}
 
+	attach_refresh_field_event(frm) {
+		$(frm.wrapper).off('refresh-fields');
+		$(frm.wrapper).on('refresh-fields', () => {
+			if (frm.doc.items.length) {
+				frm.doc.items.forEach(item => {
+					this.update_item_html(item);
+				});
+			}
+			this.update_totals_section(frm);
+		});
+	}
+
 	load_invoice() {
 		const frm = this.events.get_frm();
+		
+		this.attach_refresh_field_event(frm);
+
 		this.fetch_customer_details(frm.doc.customer).then(() => {
 			this.events.customer_details_updated(this.customer_info);
 			this.update_customer_section();
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.css b/erpnext/selling/page/sales_funnel/sales_funnel.css
index 89e904f..455d37c 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.css
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.css
@@ -1,3 +1,4 @@
 .funnel-wrapper {
 	margin: 15px;
+	width: 100%;
 }
\ No newline at end of file
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index eb02867..f515baf 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -26,7 +26,7 @@
 				}
 			};
 		});
-	}
+	}	
 
 	setup_queries() {
 		var me = this;
@@ -85,7 +85,7 @@
 
 	refresh() {
 		super.refresh();
-
+		
 		frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
 
 		this.frm.toggle_display("customer_name",
@@ -114,6 +114,10 @@
 		erpnext.utils.set_taxes_from_address(this.frm, "shipping_address_name", "customer_address", "shipping_address_name");
 	}
 
+	dispatch_address_name() {
+		erpnext.utils.get_address_display(this.frm, "dispatch_address_name", "dispatch_address");
+	}
+
 	sales_partner() {
 		this.apply_pricing_rule();
 	}
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 0427abe..8755125 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -110,7 +110,7 @@
 				self.create_default_warehouses()
 
 		if frappe.flags.country_change:
-			install_country_fixtures(self.name)
+			install_country_fixtures(self.name, self.country)
 			self.create_default_tax_template()
 
 		if not frappe.db.get_value("Department", {"company": self.name}):
@@ -291,7 +291,7 @@
 		cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name')
 		if cash and self.default_cash_account \
 			and not frappe.db.get_value('Mode of Payment Account', {'company': self.name, 'parent': cash}):
-			mode_of_payment = frappe.get_doc('Mode of Payment', cash)
+			mode_of_payment = frappe.get_doc('Mode of Payment', cash, for_update=True)
 			mode_of_payment.append('accounts', {
 				'company': self.name,
 				'default_account': self.default_cash_account
@@ -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)
 
 
@@ -440,16 +440,15 @@
 
 	return " - ".join(parts)
 
-def install_country_fixtures(company):
-	company_doc = frappe.get_doc("Company", company)
-	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country))
+def install_country_fixtures(company, country):
+	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
 	if os.path.exists(path.encode("utf-8")):
 		try:
-			module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country))
-			frappe.get_attr(module_name)(company_doc, False)
+			module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(country))
+			frappe.get_attr(module_name)(company, False)
 		except Exception as e:
 			frappe.log_error()
-			frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country)))
+			frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(country)))
 
 
 def update_company_current_month_sales(company):
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 1a83cb6..c46b6cc 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -87,8 +87,8 @@
 		if not field_filters:
 			field_filters = {}
 
-		# Ensure the query remains within current item group
-		field_filters['item_group'] = self.name
+		# Ensure the query remains within current item group & sub group
+		field_filters['item_group'] = [ig[0] for ig in get_child_groups(self.name)]
 
 		engine = ProductQuery()
 		context.items = engine.query(attribute_filters, field_filters, search, start, item_group=self.name)
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 ece9fb5..691d331 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,14 @@
 class TransactionDeletionRecord(Document):
 	def validate(self):
 		frappe.only_for('System Manager')
+		self.validate_doctypes_to_be_ignored()
+
+	def validate_doctypes_to_be_ignored(self):
 		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:
-				frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it. "), title=_("Not Allowed"))
+				frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it. "), 
+					title=_("Not Allowed"))
 
 	def before_submit(self):
 		if not self.doctypes_to_be_ignored:
@@ -23,54 +27,9 @@
 
 		self.delete_bins()
 		self.delete_lead_addresses()
-		
-		company_obj = frappe.get_doc('Company', self.company)
-		# reset company values
-		company_obj.total_monthly_sales = 0
-		company_obj.sales_monthly_history = None
-		company_obj.save()
-		# Clear notification counts
+		self.reset_company_values()
 		clear_notifications()
-
-		singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
-		tables = frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name')
-		doctypes_to_be_ignored_list = singles
-		for doctype in self.doctypes_to_be_ignored:
-			doctypes_to_be_ignored_list.append(doctype.doctype_name)
-
-		docfields = frappe.get_all('DocField', 
-			filters = {
-				'fieldtype': 'Link', 
-				'options': 'Company',
-				'parent': ['not in', doctypes_to_be_ignored_list]},
-			fields=['parent', 'fieldname'])
-	
-		for docfield in docfields:
-			if docfield['parent'] != self.doctype:
-				no_of_docs = frappe.db.count(docfield['parent'], {
-							docfield['fieldname'] : self.company
-						})
-
-				if no_of_docs > 0:
-					self.delete_version_log(docfield['parent'], docfield['fieldname'])
-					self.delete_communications(docfield['parent'], docfield['fieldname'])
-
-					# populate DocTypes table
-					if docfield['parent'] not in tables:
-						self.append('doctypes', {
-							'doctype_name' : docfield['parent'],
-							'no_of_docs' : no_of_docs
-						})
-
-					# delete the docs linked with the specified company
-					frappe.db.delete(docfield['parent'], {
-						docfield['fieldname'] : self.company
-					})
-
-					naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
-					if naming_series:
-						if '#' in naming_series:
-							self.update_naming_series(naming_series, docfield['parent'])	
+		self.delete_company_transactions()
 
 	def populate_doctypes_to_be_ignored_table(self):		
 		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
@@ -79,6 +38,111 @@
 						'doctype_name' : doctype
 					})
 
+	def delete_bins(self):
+		frappe.db.sql("""delete from tabBin where warehouse in
+				(select name from tabWarehouse where company=%s)""", self.company)
+
+	def delete_lead_addresses(self):
+		"""Delete addresses to which leads are linked"""
+		leads = frappe.get_all('Lead', filters={'company': self.company})
+		leads = ["'%s'" % row.get("name") for row in leads]
+		addresses = []
+		if leads:
+			addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
+				in ({leads})""".format(leads=",".join(leads)))
+
+			if addresses:
+				addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
+
+				frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
+					name not in (select distinct dl1.parent from `tabDynamic Link` dl1
+					inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
+					and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
+
+				frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
+					and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
+
+			frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
+
+	def reset_company_values(self):
+		company_obj = frappe.get_doc('Company', self.company)
+		company_obj.total_monthly_sales = 0
+		company_obj.sales_monthly_history = None
+		company_obj.save()
+
+	def delete_company_transactions(self):
+		doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list()
+		docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list)
+
+		tables = self.get_all_child_doctypes()	
+		for docfield in docfields:
+			if docfield['parent'] != self.doctype:
+				no_of_docs = self.get_number_of_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
+
+				if no_of_docs > 0:
+					self.delete_version_log(docfield['parent'], docfield['fieldname'])
+					self.delete_communications(docfield['parent'], docfield['fieldname'])
+					self.populate_doctypes_table(tables, docfield['parent'], no_of_docs)
+
+					self.delete_child_tables(docfield['parent'], docfield['fieldname'])
+					self.delete_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
+
+					naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
+					if naming_series:
+						if '#' in naming_series:
+							self.update_naming_series(naming_series, docfield['parent'])	
+
+	def get_doctypes_to_be_ignored_list(self):
+		singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
+		doctypes_to_be_ignored_list = singles
+		for doctype in self.doctypes_to_be_ignored:
+			doctypes_to_be_ignored_list.append(doctype.doctype_name)
+
+		return doctypes_to_be_ignored_list
+
+	def get_doctypes_with_company_field(self, doctypes_to_be_ignored_list):
+		docfields = frappe.get_all('DocField', 
+			filters = {
+				'fieldtype': 'Link', 
+				'options': 'Company',
+				'parent': ['not in', doctypes_to_be_ignored_list]},
+			fields=['parent', 'fieldname'])
+
+		return docfields
+
+	def get_all_child_doctypes(self):
+		return frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name')
+
+	def get_number_of_docs_linked_with_specified_company(self, doctype, company_fieldname):
+		return frappe.db.count(doctype, {company_fieldname : self.company})
+
+	def populate_doctypes_table(self, tables, doctype, no_of_docs):
+		if doctype not in tables:
+			self.append('doctypes', {
+				'doctype_name' : doctype,
+				'no_of_docs' : no_of_docs
+			})		
+
+	def delete_child_tables(self, doctype, company_fieldname):
+		parent_docs_to_be_deleted = frappe.get_all(doctype, {
+			company_fieldname : self.company
+		}, pluck = 'name')
+
+		child_tables = frappe.get_all('DocField', filters = {
+			'fieldtype': 'Table', 
+			'parent': doctype
+		}, pluck = 'options')
+
+		for table in child_tables:
+			frappe.db.delete(table, {
+				'parent': ['in', parent_docs_to_be_deleted]
+			})
+
+	def delete_docs_linked_with_specified_company(self, doctype, company_fieldname):
+		frappe.db.delete(doctype, {
+			company_fieldname : self.company
+		})
+
 	def update_naming_series(self, naming_series, doctype_name):
 		if '.' in naming_series:
 			prefix, hashes = naming_series.rsplit('.', 1)
@@ -107,32 +171,6 @@
 
 		frappe.delete_doc('Communication', communication_names, ignore_permissions=True)
 
-	def delete_bins(self):
-		frappe.db.sql("""delete from tabBin where warehouse in
-				(select name from tabWarehouse where company=%s)""", self.company)
-
-	def delete_lead_addresses(self):
-		"""Delete addresses to which leads are linked"""
-		leads = frappe.get_all('Lead', filters={'company': self.company})
-		leads = ["'%s'" % row.get("name") for row in leads]
-		addresses = []
-		if leads:
-			addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
-				in ({leads})""".format(leads=",".join(leads)))
-
-			if addresses:
-				addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
-
-				frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
-					name not in (select distinct dl1.parent from `tabDynamic Link` dl1
-					inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
-					and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
-
-				frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
-					and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
-
-			frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
-
 @frappe.whitelist()
 def get_doctypes_to_be_ignored():
 	doctypes_to_be_ignored_list = ['Account', 'Cost Center', 'Warehouse', 'Budget',
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index daaa626..34af093 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -1164,33 +1164,292 @@
 	},
 
 	"India": {
+		"tax_categories": [
+			{
+				"title": "In-State",
+				"is_inter_state": 0,
+				"gst_state": ""
+			},
+			{
+				"title": "Out-State",
+				"is_inter_state": 1,
+				"gst_state": ""
+			},
+			{
+				"title": "Reverse Charge In-State",
+				"is_inter_state": 0,
+				"gst_state": ""
+			},
+			{
+				"title": "Reverse Charge Out-State",
+				"is_inter_state": 1,
+				"gst_state": ""
+			},
+			{
+				"title": "Registered Composition",
+				"is_inter_state": 0,
+				"gst_state": ""
+			}
+		],
 		"chart_of_accounts": {
 			"*": {
 				"item_tax_templates": [
 					{
-						"title": "In State GST",
+						"title": "GST 9%",
 						"taxes": [
 							{
 								"tax_type": {
-									"account_name": "SGST",
+									"account_name": "Output Tax SGST",
 									"tax_rate": 9.00
 								}
 							},
 							{
 								"tax_type": {
-									"account_name": "CGST",
+									"account_name": "Output Tax CGST",
 									"tax_rate": 9.00
 								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 18.00
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 18.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 18.00,
+									"root_type": "Asset"
+								}
 							}
 						]
 					},
 					{
-						"title": "Out of State GST",
+						"title": "GST 5%",
 						"taxes": [
 							{
 								"tax_type": {
-									"account_name": "IGST",
-									"tax_rate": 18.00
+									"account_name": "Output Tax SGST",
+									"tax_rate": 2.5
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax CGST",
+									"tax_rate": 2.5
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 5.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 2.5,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 2.5,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 5.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 2.50,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 2.50,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 5.00,
+									"root_type": "Asset"
+								}
+							}
+						]
+					},
+					{
+						"title": "GST 12%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Output Tax SGST",
+									"tax_rate": 6.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax CGST",
+									"tax_rate": 6.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 12.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 6.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 6.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 12.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 6.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 6.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 12.00,
+									"root_type": "Asset"
+								}
+							}
+						]
+					},
+					{
+						"title": "GST 28%",
+						"taxes": [
+							{
+								"tax_type": {
+									"account_name": "Output Tax SGST",
+									"tax_rate": 14.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax CGST",
+									"tax_rate": 14.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Output Tax IGST",
+									"tax_rate": 28.0
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 14.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 14.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 28.0,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 14.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 14.00,
+									"root_type": "Asset"
+								}
+							},
+							{
+								"tax_type": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 28.00,
+									"root_type": "Asset"
 								}
 							}
 						]
@@ -1229,36 +1488,117 @@
 						]
 					}
 				],
-				"*": [
+				"sales_tax_templates": [
 					{
-						"title": "In State GST",
+						"title": "Output GST In-state",
 						"taxes": [
 							{
 								"account_head": {
-									"account_name": "SGST",
-									"tax_rate": 9.00
+									"account_name": "Output Tax SGST",
+									"tax_rate": 9.00,
+									"account_type": "Tax"
 								}
 							},
 							{
 								"account_head": {
-									"account_name": "CGST",
-									"tax_rate": 9.00
+									"account_name": "Output Tax CGST",
+									"tax_rate": 9.00,
+									"account_type": "Tax"
 								}
 							}
-						]
+						],
+						"tax_category": "In-State"
 					},
 					{
-						"title": "Out of State GST",
+						"title": "Output GST Out-state",
 						"taxes": [
 							{
 								"account_head": {
-									"account_name": "IGST",
-									"tax_rate": 18.00
+									"account_name": "Output Tax IGST",
+									"tax_rate": 18.00,
+									"account_type": "Tax"
 								}
 							}
-						]
+						],
+						"tax_category": "Out-State"
+					}
+				],
+				"purchase_tax_templates": [
+					{
+						"title": "Input GST In-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax SGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							},
+							{
+								"account_head": {
+									"account_name": "Input Tax CGST",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "In-State"
 					},
 					{
+						"title": "Input GST Out-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax IGST",
+									"tax_rate": 18.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "Out-State"
+					},
+					{
+						"title": "Input GST RCM In-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax SGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							},
+							{
+								"account_head": {
+									"account_name": "Input Tax CGST RCM",
+									"tax_rate": 9.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "Reverse Charge In-State"
+					},
+					{
+						"title": "Input GST RCM Out-state",
+						"taxes": [
+							{
+								"account_head": {
+									"account_name": "Input Tax IGST RCM",
+									"tax_rate": 18.00,
+									"root_type": "Asset",
+									"account_type": "Tax"
+								}
+							}
+						],
+						"tax_category": "Reverse Charge Out-State"
+					}
+				],
+				"*": [
+					{
 						"title": "VAT 5%",
 						"taxes": [
 							{
@@ -1349,7 +1689,7 @@
 		"Italy VAT 4%":{
 			"account_name": "IVA 4%",
 			"tax_rate": 4.00
-		}		
+		}
 	},
 
 	"Ivory Coast": {
diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py
index 3f0bb14..4edf948 100644
--- a/erpnext/setup/setup_wizard/operations/company_setup.py
+++ b/erpnext/setup/setup_wizard/operations/company_setup.py
@@ -42,29 +42,6 @@
 		'quotation_series': "QTN-",
 	}).insert()
 
-def create_bank_account(args):
-	if args.get("bank_account"):
-		company_name = args.get('company_name')
-		bank_account_group =  frappe.db.get_value("Account",
-			{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
-				"company": company_name})
-		if bank_account_group:
-			bank_account = frappe.get_doc({
-				"doctype": "Account",
-				'account_name': args.get("bank_account"),
-				'parent_account': bank_account_group,
-				'is_group':0,
-				'company': company_name,
-				"account_type": "Bank",
-			})
-			try:
-				return bank_account.insert()
-			except RootNotEditable:
-				frappe.throw(_("Bank account cannot be named as {0}").format(args.get("bank_account")))
-			except frappe.DuplicateEntryError:
-				# bank account same as a CoA entry
-				pass
-
 def create_email_digest():
 	from frappe.utils.user import get_system_managers
 	system_managers = get_system_managers(only_name=True)
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 7ae81d7..cd49a18 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -448,6 +448,8 @@
 	set_active_domains(args)
 	update_stock_settings()
 	update_shopping_cart_settings(args)
+
+	args.update({"set_default": 1})
 	create_bank_account(args)
 
 def set_global_defaults(args):
@@ -479,17 +481,17 @@
 	stock_settings.save()
 
 def create_bank_account(args):
-	if not args.bank_account:
+	if not args.get('bank_account'):
 		return
 
-	company_name = args.company_name
+	company_name = args.get('company_name')
 	bank_account_group =  frappe.db.get_value("Account",
 		{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
 			"company": company_name})
 	if bank_account_group:
 		bank_account = frappe.get_doc({
 			"doctype": "Account",
-			'account_name': args.bank_account,
+			'account_name': args.get('bank_account'),
 			'parent_account': bank_account_group,
 			'is_group':0,
 			'company': company_name,
@@ -498,10 +500,13 @@
 		try:
 			doc = bank_account.insert()
 
-			frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False)
+			if args.get('set_default'):
+				frappe.db.set_value("Company", args.get('company_name'), "default_bank_account", bank_account.name, update_modified=False)
+
+			return doc
 
 		except RootNotEditable:
-			frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account))
+			frappe.throw(_("Bank account cannot be named as {0}").format(args.get('bank_account')))
 		except frappe.DuplicateEntryError:
 			# bank account same as a CoA entry
 			pass
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index f4fe18e..cbb3dc8 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -27,6 +27,7 @@
 		country_wise_tax = simple_to_detailed(country_wise_tax)
 
 	from_detailed_data(company_name, country_wise_tax)
+	update_regional_tax_settings(country, company_name)
 
 
 def simple_to_detailed(templates):
@@ -86,7 +87,7 @@
 
 	if tax_categories:
 		for tax_category in tax_categories:
-			make_tax_catgory(tax_category)
+			make_tax_category(tax_category)
 
 	if sales_tax_templates:
 		for template in sales_tax_templates:
@@ -101,6 +102,17 @@
 			make_item_tax_template(company_name, template)
 
 
+def update_regional_tax_settings(country, company):
+	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
+	if os.path.exists(path.encode("utf-8")):
+		try:
+			module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(frappe.scrub(country))
+			frappe.get_attr(module_name)(country, company)
+		except Exception as e:
+			# Log error and ignore if failed to setup regional tax settings
+			frappe.log_error()
+			pass
+
 def make_taxes_and_charges_template(company_name, doctype, template):
 	template['company'] = company_name
 	template['doctype'] = doctype
@@ -130,8 +142,14 @@
 			if fieldname not in tax_row:
 				tax_row[fieldname] = default_value
 
-	return frappe.get_doc(template).insert(ignore_permissions=True)
+	doc = frappe.get_doc(template)
 
+	# Data in country wise json is already pre validated, hence validations can be ignored 
+	# Ingone validations to make doctypes faster
+	doc.flags.ignore_links = True
+	doc.flags.ignore_validate = True
+	doc.insert(ignore_permissions=True)
+	return doc
 
 def make_item_tax_template(company_name, template):
 	"""Create an Item Tax Template.
@@ -156,8 +174,14 @@
 			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 get_or_create_account(company_name, account):
 	"""
@@ -175,8 +199,7 @@
 		or_filters={
 			'account_name': account.get('account_name'),
 			'account_number': account.get('account_number')
-		}
-	)
+		})
 
 	if existing_accounts:
 		return frappe.get_doc('Account', existing_accounts[0].name)
@@ -191,8 +214,11 @@
 	account['root_type'] = root_type
 	account['is_group'] = 0
 
-	return frappe.get_doc(account).insert(ignore_permissions=True, ignore_mandatory=True)
-
+	doc = frappe.get_doc(account)
+	doc.flags.ignore_links = True
+	doc.flags.ignore_validate = True
+	doc.insert(ignore_permissions=True, ignore_mandatory=True)
+	return doc
 
 def get_or_create_tax_group(company_name, root_type):
 	# Look for a group account of type 'Tax'
@@ -237,14 +263,18 @@
 		'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):
+def make_tax_category(tax_category):
 	doctype = 'Tax Category'
 	if isinstance(tax_category, str):
 		tax_category = {'title': tax_category}
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index e6d2e13..fc4cf1d 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -193,7 +193,7 @@
  "image_field": "image",
  "links": [],
  "max_attachments": 5,
- "modified": "2021-01-07 11:10:09.149170",
+ "modified": "2021-07-08 16:22:01.343105",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Batch",
@@ -217,5 +217,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
- "title_field": "batch_id"
+ "title_field": "batch_id",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index f20e76f..dbfeb4a 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -32,6 +32,8 @@
   "contact_info",
   "shipping_address_name",
   "shipping_address",
+  "dispatch_address_name",
+  "dispatch_address",
   "contact_person",
   "contact_display",
   "contact_mobile",
@@ -1282,13 +1284,28 @@
    "fieldname": "disable_rounded_total",
    "fieldtype": "Check",
    "label": "Disable Rounded Total"
+  },
+  {
+   "fieldname": "dispatch_address_name",
+   "fieldtype": "Link",
+   "label": "Dispatch Address Name",
+   "options": "Address",
+   "print_hide": 1
+  },
+  {
+   "depends_on": "dispatch_address_name",
+   "fieldname": "dispatch_address",
+   "fieldtype": "Small Text",
+   "label": "Dispatch Address",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "icon": "fa fa-truck",
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-06-11 19:27:30.901112",
+ "modified": "2021-07-08 21:37:20.802652",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 568f0ef..87bd9e6 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -100,10 +100,11 @@
 
 		frm.add_custom_button(__('Duplicate'), function() {
 			var new_item = frappe.model.copy_doc(frm.doc);
-			if(new_item.item_name===new_item.item_code) {
+			// Duplicate item could have different name, causing "copy paste" error.
+			if (new_item.item_name===new_item.item_code) {
 				new_item.item_name = null;
 			}
-			if(new_item.description===new_item.description) {
+			if (new_item.item_code===new_item.description || new_item.item_code===new_item.description) {
 				new_item.description = null;
 			}
 			frappe.set_route('Form', 'Item', new_item.name);
@@ -186,8 +187,6 @@
 	item_code: function(frm) {
 		if(!frm.doc.item_name)
 			frm.set_value("item_name", frm.doc.item_code);
-		if(!frm.doc.description)
-			frm.set_value("description", frm.doc.item_code);
 	},
 
 	is_stock_item: function(frm) {
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index c7467a5..922049f 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -587,8 +587,8 @@
 test_records = frappe.get_test_records('Item')
 
 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"):
+		is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=0, is_fixed_asset=0,
+		asset_category=None, company="_Test Company"):
 	if not frappe.db.exists("Item", item_code):
 		item = frappe.new_doc("Item")
 		item.item_code = item_code
@@ -596,6 +596,8 @@
 		item.description = item_code
 		item.item_group = "All Item Groups"
 		item.is_stock_item = is_stock_item
+		item.is_fixed_asset = is_fixed_asset
+		item.asset_category = asset_category
 		item.opening_stock = opening_stock
 		item.valuation_rate = valuation_rate
 		item.is_purchase_item = is_purchase_item
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/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 55f2ebb..5f31d9c 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -133,6 +133,6 @@
 
 def get_repost_item_valuation_entries():
 	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
-		WHERE status != 'Completed' and creation <= %s and docstatus = 1
+		WHERE status in ('Queued', 'In Progress') and creation <= %s and docstatus = 1
 		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
 	""", now(), as_dict=1)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 8f27ef4..fcb6f0f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -72,7 +72,7 @@
 		self.validate_with_material_request()
 		self.validate_batch()
 		self.validate_inspection()
-		self.validate_fg_completed_qty()
+		# self.validate_fg_completed_qty()
 		self.validate_difference_account()
 		self.set_job_card_data()
 		self.set_purpose_for_stock_entry()
@@ -529,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()])
 
@@ -719,6 +719,10 @@
 			frappe.throw(_("Multiple items cannot be marked as finished item"))
 
 		if self.purpose == "Manufacture":
+			if not finished_items:
+				frappe.throw(_('Finished Good has not set in the stock entry {0}')
+					.format(self.name))
+
 			allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
 				"overproduction_percentage_for_work_order"))
 
@@ -1090,13 +1094,13 @@
 			"is_finished_item": 1
 		}
 
-		if self.work_order and self.pro_doc.has_batch_no:
+		if self.work_order and self.pro_doc.has_batch_no and cint(frappe.db.get_single_value('Manufacturing Settings',
+			'make_serial_no_batch_from_work_order', cache=True)):
 			self.set_batchwise_finished_goods(args, item)
 		else:
-			self.add_finisged_goods(args, item)
+			self.add_finished_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,
@@ -1105,7 +1109,17 @@
 
 		fields = ["qty_to_produce as qty", "produced_qty", "name"]
 
-		for row in frappe.get_all("Batch", filters = filters, fields = fields, order_by="creation asc"):
+		data = frappe.get_all("Batch", filters = filters, fields = fields, order_by="creation asc")
+
+		if not data:
+			self.add_finished_goods(args, item)
+		else:
+			self.add_batchwise_finished_good(data, args, item)
+
+	def add_batchwise_finished_good(self, data, args, item):
+		qty = flt(self.fg_completed_qty)
+
+		for row in data:
 			batch_qty = flt(row.qty) - flt(row.produced_qty)
 			if not batch_qty:
 				continue
@@ -1121,9 +1135,9 @@
 			args["qty"] = fg_qty
 			args["batch_no"] = row.name
 
-			self.add_finisged_goods(args, item)
+			self.add_finished_goods(args, item)
 
-	def add_finisged_goods(self, args, item):
+	def add_finished_goods(self, args, item):
 		self.add_to_stock_entry_detail({
 			item.name: args
 		}, bom_no = self.bom_no)
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 0febcb6..93482e8 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -89,17 +89,16 @@
 		if item_det.is_stock_item != 1:
 			frappe.throw(_("Item {0} must be a stock Item").format(self.item_code))
 
-		# check if batch number is required
-		if self.voucher_type != 'Stock Reconciliation':
-			if item_det.has_batch_no == 1:
-				batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" +  item_det.item_name
-				if not self.batch_no:
-					frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item))
-				elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
-					frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item))
+		# check if batch number is valid
+		if item_det.has_batch_no == 1:
+			batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name
+			if not self.batch_no:
+				frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item))
+			elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
+				frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item))
 
-			elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0:
-				frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
+		elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0:
+			frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
 
 		if item_det.has_variants:
 			frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code),
@@ -178,3 +177,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_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 76a3f1a..4540954 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -17,6 +17,14 @@
 				}
 			}
 		});
+		frm.set_query("batch_no", "items", function(doc, cdt, cdn) {
+			var item = locals[cdt][cdn];
+			return {
+				filters: {
+					'item': item.item_code
+				}
+			};
+		});
 
 		if (frm.doc.company) {
 			erpnext.queries.setup_queries(frm, "Warehouse", function() {
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 3e15d54..9875491 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -405,17 +405,18 @@
 
 			key = (d.item_code, d.warehouse)
 			if key not in merge_similar_entries:
+				d.total_amount = (d.actual_qty * d.valuation_rate)
 				merge_similar_entries[key] = d
 			elif d.serial_no:
 				data = merge_similar_entries[key]
 				data.actual_qty += d.actual_qty
 				data.qty_after_transaction += d.qty_after_transaction
 
-				data.valuation_rate = (data.valuation_rate + d.valuation_rate) / data.actual_qty
+				data.total_amount += (d.actual_qty * d.valuation_rate)
+				data.valuation_rate = (data.total_amount) / data.actual_qty
 				data.serial_no += '\n' + d.serial_no
 
-				if data.incoming_rate:
-					data.incoming_rate = (data.incoming_rate + d.incoming_rate) / data.actual_qty
+				data.incoming_rate = (data.total_amount) / data.actual_qty
 
 		for key, value in merge_similar_entries.items():
 			new_sl_entries.append(value)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 7b98c7b..c192582 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, add_days
+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
@@ -16,6 +16,7 @@
 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
 	def setUpClass(self):
@@ -151,6 +152,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 = []
@@ -316,6 +353,26 @@
 		dn2.cancel()
 		pr1.cancel()
 
+	def test_valid_batch(self):
+		create_batch_item_with_batch("Testing Batch Item 1", "001")
+		create_batch_item_with_batch("Testing Batch Item 2", "002")
+		sr = create_stock_reconciliation(item_code="Testing Batch Item 1", qty=1, rate=100, batch_no="002"
+			, do_not_submit=True)
+		self.assertRaises(frappe.ValidationError, sr.submit)
+
+def create_batch_item_with_batch(item_name, batch_id):
+	batch_item_doc = create_item(item_name, is_stock_item=1)
+	if not batch_item_doc.has_batch_no:
+		batch_item_doc.has_batch_no = 1
+		batch_item_doc.create_new_batch = 1
+		batch_item_doc.save(ignore_permissions=True)
+
+	if not frappe.db.exists('Batch', batch_id):
+		b = frappe.new_doc('Batch')
+		b.item = item_name
+		b.batch_id = batch_id
+		b.save()
+
 def insert_existing_sle(warehouse):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
@@ -343,6 +400,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/get_item_details.py b/erpnext/stock/get_item_details.py
index ca174a3..cf52803 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -74,9 +74,8 @@
 
 	update_party_blanket_order(args, out)
 
-	if not doc or cint(doc.get('is_return')) == 0:
-		# get price list rate only if the invoice is not a credit or debit note
-		get_price_list_rate(args, item, out)
+	
+	get_price_list_rate(args, item, out)
 
 	if args.customer and cint(args.is_pos):
 		out.update(get_pos_profile_item_details(args.company, args, update_data=True))
@@ -441,7 +440,7 @@
 
 	if item_tax_templates is None:
 		item_tax_templates = {}
-	
+
 	if item_rates is None:
 		item_rates = {}
 
@@ -807,10 +806,14 @@
 def validate_conversion_rate(args, meta):
 	from erpnext.controllers.accounts_controller import validate_conversion_rate
 
-	if (not args.conversion_rate
-		and args.currency==frappe.get_cached_value('Company',  args.company,  "default_currency")):
+	company_currency = frappe.get_cached_value('Company',  args.company,  "default_currency")
+	if (not args.conversion_rate and args.currency==company_currency):
 		args.conversion_rate = 1.0
 
+	if (not args.ignore_conversion_rate and args.conversion_rate == 1 and args.currency!=company_currency):
+		args.conversion_rate = get_exchange_rate(args.currency,
+			company_currency, args.transaction_date, "for_buying") or 1.0
+
 	# validate currency conversion rate
 	validate_conversion_rate(args.currency, args.conversion_rate,
 		meta.get_label("conversion_rate"), args.company)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 4e9c768..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):
@@ -130,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):
@@ -139,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):
@@ -186,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):
@@ -296,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 \
@@ -393,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"):
@@ -442,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:
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 8a6a3a3..b57b2aa 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -314,13 +314,16 @@
 	for row_idx, row in enumerate(result):
 		data = row.items() if is_dict_obj else enumerate(row)
 		for key, value in data:
-			if key not in convertible_columns or not conversion_factors[row_idx-1]:
+			if key not in convertible_columns:
 				continue
+			# If no conversion factor for the UOM, defaults to 1
+			if not conversion_factors[row_idx]:
+				conversion_factors[row_idx] = 1
 
 			if convertible_columns.get(key) == 'rate':
-				new_value = flt(value) * conversion_factors[row_idx-1]
+				new_value = flt(value) * conversion_factors[row_idx]
 			else:
-				new_value = flt(value) / conversion_factors[row_idx-1]
+				new_value = flt(value) / conversion_factors[row_idx]
 
 			if not is_dict_obj:
 				row.insert(key+1, new_value)
@@ -386,4 +389,4 @@
 	reposting_in_progress = frappe.db.exists("Repost Item Valuation",
 		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
 	if reposting_in_progress:
-		frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1)
\ No newline at end of file
+		frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1)
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index dd6d647..b9a65b6 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -5,10 +5,10 @@
 import frappe
 import json
 from frappe import _
-from frappe import utils
 from frappe.model.document import Document
-from frappe.utils import now_datetime
-from datetime import datetime, timedelta
+from frappe.utils import now_datetime, time_diff_in_seconds, get_datetime, date_diff
+from frappe.core.utils import get_parent_doc
+from datetime import timedelta
 from frappe.model.mapper import get_mapped_doc
 from frappe.utils.user import is_website_user
 from frappe.email.inbox import link_communication_to_document
@@ -212,7 +212,129 @@
 
 	return issue.name
 
+def get_time_in_timedelta(time):
+	"""
+		Converts datetime.time(10, 36, 55, 961454) to datetime.timedelta(seconds=38215)
+	"""
+	return timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
+
+def set_first_response_time(communication, method):
+	if communication.get('reference_doctype') == "Issue":
+		issue = get_parent_doc(communication)
+		if is_first_response(issue):
+			first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
+			issue.db_set("first_response_time", first_response_time)
+
+def is_first_response(issue):
+	responses = frappe.get_all('Communication', filters = {'reference_name': issue.name, 'sent_or_received': 'Sent'})
+	if len(responses) == 1: 
+		return True
+	return False
+
+def calculate_first_response_time(issue, first_responded_on):
+	issue_creation_date = issue.creation
+	issue_creation_time = get_time_in_seconds(issue_creation_date)
+	first_responded_on_in_seconds = get_time_in_seconds(first_responded_on)
+	support_hours = frappe.get_cached_doc("Service Level Agreement", issue.service_level_agreement).support_and_resolution
+
+	if issue_creation_date.day == first_responded_on.day:
+		if is_work_day(issue_creation_date, support_hours):
+			start_time, end_time = get_working_hours(issue_creation_date, support_hours)
+
+			# issue creation and response on the same day during working hours
+			if is_during_working_hours(issue_creation_date, support_hours) and is_during_working_hours(first_responded_on, support_hours):
+				return get_elapsed_time(issue_creation_date, first_responded_on)
+
+			# issue creation is during working hours, but first response was after working hours
+			elif is_during_working_hours(issue_creation_date, support_hours):
+				return get_elapsed_time(issue_creation_time, end_time)
+
+			# issue creation was before working hours but first response is during working hours
+			elif is_during_working_hours(first_responded_on, support_hours):
+				return get_elapsed_time(start_time, first_responded_on_in_seconds)
+
+			# both issue creation and first response were after working hours
+			else:
+				return 1.0		# this should ideally be zero, but it gets reset when the next response is sent if the value is zero
+			
+		else:
+			return 1.0
+
+	else:
+		# response on the next day
+		if date_diff(first_responded_on, issue_creation_date) == 1:
+			first_response_time = 0
+		else:
+			first_response_time = calculate_initial_frt(issue_creation_date, date_diff(first_responded_on, issue_creation_date)- 1, support_hours)
+
+		# time taken on day of issue creation
+		if is_work_day(issue_creation_date, support_hours):
+			start_time, end_time = get_working_hours(issue_creation_date, support_hours)
+
+			if is_during_working_hours(issue_creation_date, support_hours):
+				first_response_time += get_elapsed_time(issue_creation_time, end_time)
+			elif is_before_working_hours(issue_creation_date, support_hours):
+				first_response_time += get_elapsed_time(start_time, end_time)
+
+		# time taken on day of first response
+		if is_work_day(first_responded_on, support_hours):
+			start_time, end_time = get_working_hours(first_responded_on, support_hours)
+
+			if is_during_working_hours(first_responded_on, support_hours):
+				first_response_time += get_elapsed_time(start_time, first_responded_on_in_seconds)
+			elif not is_before_working_hours(first_responded_on, support_hours):
+				first_response_time += get_elapsed_time(start_time, end_time)
+
+		if first_response_time:
+			return first_response_time
+		else:
+			return 1.0
+
+def get_time_in_seconds(date):
+	return timedelta(hours=date.hour, minutes=date.minute, seconds=date.second)
+
+def get_working_hours(date, support_hours):
+	if is_work_day(date, support_hours):
+		weekday = frappe.utils.get_weekday(date)
+		for day in support_hours:
+			if day.workday == weekday:
+				return day.start_time, day.end_time
+
+def is_work_day(date, support_hours):
+	weekday = frappe.utils.get_weekday(date)
+	for day in support_hours:
+		if day.workday == weekday:
+			return True
+	return False
+
+def is_during_working_hours(date, support_hours):
+	start_time, end_time = get_working_hours(date, support_hours)
+	time = get_time_in_seconds(date)
+	if time >= start_time and time <= end_time:
+		return True
+	return False
+
+def get_elapsed_time(start_time, end_time):
+	return round(time_diff_in_seconds(end_time, start_time), 2)
+
+def calculate_initial_frt(issue_creation_date, days_in_between, support_hours):
+	initial_frt = 0
+	for i in range(days_in_between):
+		date = issue_creation_date + timedelta(days = (i+1))
+		if is_work_day(date, support_hours):
+			start_time, end_time = get_working_hours(date, support_hours)
+			initial_frt += get_elapsed_time(start_time, end_time)
+
+	return initial_frt
+
+def is_before_working_hours(date, support_hours):
+	start_time, end_time = get_working_hours(date, support_hours)
+	time = get_time_in_seconds(date)
+	if time < start_time:
+		return True
+	return False
+
 def get_holidays(holiday_list_name):
 	holiday_list = frappe.get_cached_doc("Holiday List", holiday_list_name)
 	holidays = [holiday.holiday_date for holiday in holiday_list.holidays]
-	return holidays
\ No newline at end of file
+	return holidays
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 7b9b144..84f8c39 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -5,16 +5,18 @@
 import frappe
 import unittest
 from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
-from frappe.utils import now_datetime, get_datetime, flt
+from frappe.core.doctype.user_permission.test_user_permission import create_user
+from frappe.utils import get_datetime, flt
 import datetime
 from datetime import timedelta
 
-class TestIssue(unittest.TestCase):
+class TestSetUp(unittest.TestCase):
 	def setUp(self):
 		frappe.db.sql("delete from `tabService Level Agreement`")
 		frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
 		create_service_level_agreements_for_issues()
 
+class TestIssue(TestSetUp):
 	def test_response_time_and_resolution_time_based_on_different_sla(self):
 		creation = datetime.datetime(2019, 3, 4, 12, 0)
 
@@ -133,6 +135,223 @@
 		issue.reload()
 		self.assertEqual(flt(issue.total_hold_time, 2), 2700)
 
+class TestFirstResponseTime(TestSetUp):
+	# working hours used in all cases: Mon-Fri, 10am to 6pm
+	# all dates are in the mm-dd-yyyy format
+
+	# issue creation and first response are on the same day
+	def test_first_response_time_case1(self):
+		"""
+			Test frt when issue creation and first response are during working hours on the same day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 11:00"), get_datetime("06-28-2021 12:00"))
+		self.assertEqual(issue.first_response_time, 3600.0)
+
+	def test_first_response_time_case2(self):
+		"""
+			Test frt when issue creation was during working hours, but first response is sent after working hours on the same day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-28-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 21600.0)
+
+	def test_first_response_time_case3(self):
+		"""
+			Test frt when issue creation was before working hours but first response is sent during working hours on the same day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-28-2021 12:00"))
+		self.assertEqual(issue.first_response_time, 7200.0)
+
+	def test_first_response_time_case4(self):
+		"""
+			Test frt when both issue creation and first response were after working hours on the same day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 19:00"), get_datetime("06-28-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 1.0)
+
+	def test_first_response_time_case5(self):
+		"""
+			Test frt when both issue creation and first response are on the same day, but it's not a work day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-27-2021 10:00"), get_datetime("06-27-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 1.0)
+
+	# issue creation and first response are on consecutive days
+	def test_first_response_time_case6(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is also sent before working hours, but on the next day. 
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 6:00"))
+		self.assertEqual(issue.first_response_time, 28800.0)
+
+	def test_first_response_time_case7(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is sent during working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 32400.0)
+
+	def test_first_response_time_case8(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is sent after working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 57600.0)
+
+	def test_first_response_time_case9(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is sent on the next day, which is not a work day.
+		""" 
+		issue = create_issue_and_communication(get_datetime("06-25-2021 6:00"), get_datetime("06-26-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 28800.0)
+
+	def test_first_response_time_case10(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is sent before working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 6:00"))
+		self.assertEqual(issue.first_response_time, 21600.0)
+
+	def test_first_response_time_case11(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is also sent during working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 25200.0)
+
+	def test_first_response_time_case12(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is sent after working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("06-29-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 50400.0)
+
+	def test_first_response_time_case13(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is sent on the next day, which is not a work day.
+		""" 
+		issue = create_issue_and_communication(get_datetime("06-25-2021 12:00"), get_datetime("06-26-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 21600.0)
+
+	def test_first_response_time_case14(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is sent before working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 6:00"))
+		self.assertEqual(issue.first_response_time, 1.0)
+
+	def test_first_response_time_case15(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is sent during working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 3600.0)
+
+	def test_first_response_time_case16(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is also sent after working hours, but on the next day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("06-29-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 28800.0)
+
+	def test_first_response_time_case17(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is sent on the next day, which is not a work day.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-25-2021 20:00"), get_datetime("06-26-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 1.0)
+
+	# issue creation and first response are a few days apart
+	def test_first_response_time_case18(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is also sent before working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 6:00"))
+		self.assertEqual(issue.first_response_time, 86400.0)
+
+	def test_first_response_time_case19(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is sent during working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 90000.0)
+
+	def test_first_response_time_case20(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is sent after working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("07-01-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 115200.0)
+
+	def test_first_response_time_case21(self):
+		"""
+			Test frt when the issue was created before working hours and the first response is sent after a few days, on a holiday.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-25-2021 6:00"), get_datetime("06-27-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 28800.0)
+
+	def test_first_response_time_case22(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is sent before working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 6:00"))
+		self.assertEqual(issue.first_response_time, 79200.0)
+
+	def test_first_response_time_case23(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is also sent during working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 82800.0)
+
+	def test_first_response_time_case24(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is sent after working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 12:00"), get_datetime("07-01-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 108000.0)
+
+	def test_first_response_time_case25(self):
+		"""
+			Test frt when the issue was created during working hours and the first response is sent after a few days, on a holiday.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-25-2021 12:00"), get_datetime("06-27-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 21600.0)
+
+	def test_first_response_time_case26(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is sent before working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 6:00"))
+		self.assertEqual(issue.first_response_time, 57600.0)
+
+	def test_first_response_time_case27(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is sent during working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 61200.0)
+
+	def test_first_response_time_case28(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is also sent after working hours, but after a few days.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-28-2021 20:00"), get_datetime("07-01-2021 20:00"))
+		self.assertEqual(issue.first_response_time, 86400.0)
+
+	def test_first_response_time_case29(self):
+		"""
+			Test frt when the issue was created after working hours and the first response is sent after a few days, on a holiday.
+		"""
+		issue = create_issue_and_communication(get_datetime("06-25-2021 20:00"), get_datetime("06-27-2021 11:00"))
+		self.assertEqual(issue.first_response_time, 1.0)
+	
+def create_issue_and_communication(issue_creation, first_responded_on):
+	issue = make_issue(issue_creation, index=1)
+	sender = create_user("test@admin.com")
+	create_communication(issue.name, sender.email, "Sent", first_responded_on)
+	issue.reload()
+
+	return issue
 
 def make_issue(creation=None, customer=None, index=0, priority=None, issue_type=None):
 	issue = frappe.get_doc({
@@ -185,7 +404,7 @@
 
 
 def create_communication(reference_name, sender, sent_or_received, creation):
-	issue = frappe.get_doc({
+	communication = frappe.get_doc({
 		"doctype": "Communication",
 		"communication_type": "Communication",
 		"communication_medium": "Email",
@@ -199,4 +418,4 @@
 		"creation": creation,
 		"reference_name": reference_name
 	})
-	issue.save()
+	communication.save()
\ No newline at end of file
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
index 308bce4..ae2080c 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
@@ -5,15 +5,15 @@
 	setup: function(frm) {
 		if (cint(frm.doc.apply_sla_for_resolution) === 1) {
 			frm.get_field('priorities').grid.editable_fields = [
-				{fieldname: 'priority', columns: 1},
 				{fieldname: 'default_priority', columns: 1},
+				{fieldname: 'priority', columns: 2},
 				{fieldname: 'response_time', columns: 2},
 				{fieldname: 'resolution_time', columns: 2}
 			];
 		} else {
 			frm.get_field('priorities').grid.editable_fields = [
-				{fieldname: 'priority', columns: 1},
 				{fieldname: 'default_priority', columns: 1},
+				{fieldname: 'priority', columns: 2},
 				{fieldname: 'response_time', columns: 3},
 			];
 		}
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
index de3389a..ef14b29 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -1,6 +1,6 @@
 {
  "actions": [],
- "autoname": "format:SLA-{document_type}-{service_level}-{####}",
+ "autoname": "format:SLA-{document_type}-{service_level}",
  "creation": "2018-12-26 21:08:15.448812",
  "doctype": "DocType",
  "editable_grid": 1,
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 60e5fbe..8739cb2 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -797,7 +797,7 @@
 	if meta.has_field("response_by"):
 		doc.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
 
-	if meta.has_field("response_by_variance"):
+	if meta.has_field("response_by_variance") and not doc.get('first_responded_on'):
 		now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
 		doc.response_by_variance = round(time_diff_in_seconds(doc.response_by, now_time), 2)
 
@@ -805,7 +805,7 @@
 	if meta.has_field("resolution_by"):
 		doc.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
 
-	if meta.has_field("resolution_by_variance"):
+	if meta.has_field("resolution_by_variance") and not doc.get("resolution_date"):
 		now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
 		doc.resolution_by_variance = round(time_diff_in_seconds(doc.resolution_by, now_time), 2)
 
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index 7c18a65..7bc97d6 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -217,6 +217,42 @@
 		lead.reload()
 		self.assertEqual(lead.agreement_status, 'Fulfilled')
 
+	def test_changing_of_variance_after_response(self):
+		# create lead
+		doctype = "Lead"
+		lead_sla = create_service_level_agreement(
+			default_service_level_agreement=1,
+			holiday_list="__Test Holiday List",
+			entity_type=None, entity=None,
+			response_time=14400,
+			doctype=doctype,
+			sla_fulfilled_on=[{"status": "Replied"}],
+			apply_sla_for_resolution=0
+		)
+		creation = datetime.datetime(2019, 3, 4, 12, 0)
+		lead = make_lead(creation=creation, index=2)
+		self.assertEqual(lead.service_level_agreement, lead_sla.name)
+
+		# set lead as replied to set first responded on
+		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 15, 30)
+		lead.reload()
+		lead.status = 'Replied'
+		lead.save()
+		lead.reload()
+		self.assertEqual(lead.agreement_status, 'Fulfilled')
+
+		# check response_by_variance
+		self.assertEqual(lead.first_responded_on, frappe.flags.current_time)
+		self.assertEqual(lead.response_by_variance, 1800.0)
+
+		# make a change on the document &
+		# check response_by_variance is unchanged
+		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 18, 30)
+		lead.status = 'Open'
+		lead.save()
+		lead.reload()
+		self.assertEqual(lead.response_by_variance, 1800.0)
+
 	def tearDown(self):
 		for d in frappe.get_all("Service Level Agreement"):
 			frappe.delete_doc("Service Level Agreement", d.name, force=1)
@@ -249,7 +285,7 @@
 		"doctype": "Service Level Agreement",
 		"enabled": 1,
 		"document_type": doctype,
-		"service_level": "__Test Service Level",
+		"service_level": "__Test {} SLA".format(entity_type if entity_type else "Default"),
 		"default_service_level_agreement": default_service_level_agreement,
 		"default_priority": "Medium",
 		"holiday_list": holiday_list,
@@ -303,16 +339,6 @@
 				"workday": "Friday",
 				"start_time": "10:00:00",
 				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Saturday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Sunday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
 			}
 		]
 	})
diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json
index 0367fc6..b410fe6 100644
--- a/erpnext/support/doctype/service_level_priority/service_level_priority.json
+++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json
@@ -5,9 +5,9 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "priority",
-  "cb_01",
   "default_priority",
+  "cb_01",
+  "priority",
   "sb_00",
   "response_time",
   "cb_00",
@@ -15,7 +15,7 @@
  ],
  "fields": [
   {
-   "columns": 1,
+   "columns": 2,
    "fieldname": "priority",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -64,7 +64,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-05-29 19:52:51.733248",
+ "modified": "2021-06-21 12:00:58.089962",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Service Level Priority",
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index 393c3a4..9050cc3 100644
--- a/erpnext/templates/generators/item_group.html
+++ b/erpnext/templates/generators/item_group.html
@@ -9,7 +9,7 @@
 {% endblock %}
 
 {% block page_content %}
-<div class="item-group-content" itemscope itemtype="http://schema.org/Product">
+<div class="item-group-content" itemscope itemtype="http://schema.org/Product" data-item-group="{{ name }}">
 	<div class="item-group-slideshow">
 		{% if slideshow %}<!-- slideshow -->
 			{{ web_block(
@@ -127,15 +127,36 @@
 			</script>
 		</div>
 	</div>
-	<div class="row">
-		<div class="col-12">
+	<div class="row mt-6">
+		<div class="col-3">
+		</div>
+		<div class="col-9">
 			{% if frappe.form_dict.start|int > 0 %}
-			<button class="btn btn-outline-secondary btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</button>
+			<button class="btn btn-outline-secondary btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">
+				{{ _("Prev") }}
+			</button>
 			{% endif %}
 			{% if items|length >= page_length %}
-			<button class="btn btn-outline-secondary btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
+			<button class="btn btn-outline-secondary btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}"
+				style="float: right;">
+				{{ _("Next") }}
+			</button>
 			{% endif %}
 		</div>
 	</div>
 </div>
+
+<script>
+	frappe.ready(() => {
+		$('.btn-prev, .btn-next').click((e) => {
+			const $btn = $(e.target);
+			$btn.prop('disabled', true);
+			const start = $btn.data('start');
+			let query_params = frappe.utils.get_query_params();
+			query_params.start = start;
+			let path = window.location.pathname + '?' + frappe.utils.get_url_from_dict(query_params);
+			window.location.href = path;
+		});
+	});
+</script>
 {% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/includes/projects/project_timesheets.html b/erpnext/templates/includes/projects/project_timesheets.html
index fa5b2f9..b8e0682 100644
--- a/erpnext/templates/includes/projects/project_timesheets.html
+++ b/erpnext/templates/includes/projects/project_timesheets.html
@@ -12,7 +12,7 @@
           .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 }}">
diff --git a/erpnext/www/all-products/index.js b/erpnext/www/all-products/index.js
index 0721056..1c641b5 100644
--- a/erpnext/www/all-products/index.js
+++ b/erpnext/www/all-products/index.js
@@ -124,6 +124,10 @@
 				attribute_filters: if_key_exists(attribute_filters)
 			};
 
+			const item_group = $(".item-group-content").data('item-group');
+			if (item_group) {
+				Object.assign(field_filters, { item_group });
+			}
 			return new Promise((resolve, reject) => {
 				frappe.call('erpnext.portal.product_configurator.utils.get_products_html_for_website', args)
 					.then(r => {