Merge pull request #27783 from hrwX/subscription_generation_fix_develop

fix(Subscription): reorder updation of end date
diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict
index c8337a9..a79137d 100644
--- a/.github/helper/.flake8_strict
+++ b/.github/helper/.flake8_strict
@@ -1,6 +1,8 @@
 [flake8]
 ignore =
     B007,
+    B009,
+    B010,
     B950,
     E101,
     E111,
@@ -65,11 +67,6 @@
     E713,
     E712,
 
-enable-extensions =
-    M90
-
-select =
-    M511
 
 max-line-length = 200
 exclude=.github/helper/semgrep_rules,test_*.py
diff --git a/.github/helper/semgrep_rules/frappe_correctness.yml b/.github/helper/semgrep_rules/frappe_correctness.yml
index d9603e8..166e98a 100644
--- a/.github/helper/semgrep_rules/frappe_correctness.yml
+++ b/.github/helper/semgrep_rules/frappe_correctness.yml
@@ -131,3 +131,21 @@
       key `$X` is uselessly assigned twice. This could be a potential bug.
   languages: [python]
   severity: ERROR
+
+
+- id: frappe-manual-commit
+  patterns:
+    - pattern: frappe.db.commit()
+    - pattern-not-inside: |
+        try:
+          ...
+        except ...:
+          ...
+  message: |
+    Manually commiting a transaction is highly discouraged. Read about the transaction model implemented by Frappe Framework before adding manual commits: https://frappeframework.com/docs/user/en/api/database#database-transaction-model If you think manual commit is required then add a comment explaining why and `// nosemgrep` on the same line.
+  paths:
+      exclude:
+        - "**/patches/**"
+        - "**/demo/**"
+  languages: [python]
+  severity: ERROR
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e411f11..b74d9a6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -21,9 +21,9 @@
     hooks:
       - id: flake8
         additional_dependencies: [
-          'flake8-mutable',
+          'flake8-bugbear',
         ]
-        args: ['--select=M511', '--config', '.github/helper/.flake8_strict']
+        args: ['--config', '.github/helper/.flake8_strict']
         exclude: ".*setup.py$"
 
   - repo: https://github.com/timothycrosley/isort
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index f6198eb..605262f 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -8,6 +8,8 @@
 from frappe.utils import cint, cstr
 from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
 
+import erpnext
+
 
 class RootNotEditable(frappe.ValidationError): pass
 class BalanceMismatchError(frappe.ValidationError): pass
@@ -196,7 +198,7 @@
 					"company": company,
 					# parent account's currency should be passed down to child account's curreny
 					# if it is None, it picks it up from default company currency, which might be unintended
-					"account_currency": self.account_currency,
+					"account_currency": erpnext.get_company_currency(company),
 					"parent_account": parent_acc_name_map[company]
 				})
 
@@ -207,8 +209,7 @@
 				# update the parent company's value in child companies
 				doc = frappe.get_doc("Account", child_account)
 				parent_value_changed = False
-				for field in ['account_type', 'account_currency',
-					'freeze_account', 'balance_must_be']:
+				for field in ['account_type', 'freeze_account', 'balance_must_be']:
 					if doc.get(field) != self.get(field):
 						parent_value_changed = True
 						doc.set(field, self.get(field))
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 7d0ecfb..55ea571 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -174,7 +174,7 @@
    "default": "0",
    "fieldname": "automatically_fetch_payment_terms",
    "fieldtype": "Check",
-   "label": "Automatically Fetch Payment Terms"
+   "label": "Automatically Fetch Payment Terms from Order"
   },
   {
    "description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
@@ -282,7 +282,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-08-19 11:17:38.788054",
+ "modified": "2021-10-11 17:42:36.427699",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
index 0813926..003389e 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -16,7 +16,7 @@
 
 def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=None):
 	if not expiry_date:
-		date = today()
+		expiry_date = today()
 
 	return frappe.db.sql('''
 		select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 8037ca1..9b4a91d 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -505,12 +505,13 @@
 
 	def validate_received_amount(self):
 		if self.paid_from_account_currency == self.paid_to_account_currency:
-			if self.paid_amount != self.received_amount:
+			if self.paid_amount < self.received_amount:
 				frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
 
 	def set_received_amount(self):
 		self.base_received_amount = self.base_paid_amount
-		if self.paid_from_account_currency == self.paid_to_account_currency:
+		if self.paid_from_account_currency == self.paid_to_account_currency \
+			and not self.payment_type == 'Internal Transfer':
 			self.received_amount = self.paid_amount
 
 	def set_amounts_after_tax(self):
@@ -712,10 +713,14 @@
 			dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
 
 			for d in self.get("references"):
+				cost_center = self.cost_center
+				if d.reference_doctype == "Sales Invoice" and not cost_center:
+					cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
 				gle = party_gl_dict.copy()
 				gle.update({
 					"against_voucher_type": d.reference_doctype,
-					"against_voucher": d.reference_name
+					"against_voucher": d.reference_name,
+					"cost_center": cost_center
 				})
 
 				allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 55e288e..03cbc4a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -149,16 +149,18 @@
   "cb_17",
   "hold_comment",
   "more_info",
+  "status",
+  "inter_company_invoice_reference",
+  "represents_company",
+  "column_break_147",
+  "is_internal_supplier",
+  "accounting_details_section",
   "credit_to",
   "party_account_currency",
   "is_opening",
   "against_expense_account",
   "column_break_63",
   "unrealized_profit_loss_account",
-  "status",
-  "inter_company_invoice_reference",
-  "is_internal_supplier",
-  "represents_company",
   "remarks",
   "subscription_section",
   "from_date",
@@ -1172,6 +1174,15 @@
    "print_hide": 1
   },
   {
+   "default": "0",
+   "fetch_from": "supplier.is_internal_supplier",
+   "fieldname": "is_internal_supplier",
+   "fieldtype": "Check",
+   "ignore_user_permissions": 1,
+   "label": "Is Internal Supplier",
+   "read_only": 1
+  },
+  {
    "fieldname": "credit_to",
    "fieldtype": "Link",
    "label": "Credit To",
@@ -1196,7 +1207,7 @@
    "default": "No",
    "fieldname": "is_opening",
    "fieldtype": "Select",
-   "label": "Is Opening",
+   "label": "Is Opening Entry",
    "oldfieldname": "is_opening",
    "oldfieldtype": "Select",
    "options": "No\nYes",
@@ -1299,15 +1310,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "0",
-   "fetch_from": "supplier.is_internal_supplier",
-   "fieldname": "is_internal_supplier",
-   "fieldtype": "Check",
-   "ignore_user_permissions": 1,
-   "label": "Is Internal Supplier",
-   "read_only": 1
-  },
-  {
    "fieldname": "tax_withholding_category",
    "fieldtype": "Link",
    "hidden": 1,
@@ -1395,13 +1397,24 @@
    "hidden": 1,
    "label": "Ignore Default Payment Terms Template",
    "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_details_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "column_break_147",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-28 13:10:28.351810",
+ "modified": "2021-10-12 20:55:16.145651",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index f3adb89..93e32f1 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -124,6 +124,13 @@
   "total_advance",
   "outstanding_amount",
   "disable_rounded_total",
+  "column_break4",
+  "write_off_amount",
+  "base_write_off_amount",
+  "write_off_outstanding_amount_automatically",
+  "column_break_74",
+  "write_off_account",
+  "write_off_cost_center",
   "advances_section",
   "allocate_advances_automatically",
   "get_advances",
@@ -144,13 +151,6 @@
   "column_break_90",
   "change_amount",
   "account_for_change_amount",
-  "column_break4",
-  "write_off_amount",
-  "base_write_off_amount",
-  "write_off_outstanding_amount_automatically",
-  "column_break_74",
-  "write_off_account",
-  "write_off_cost_center",
   "terms_section_break",
   "tc_name",
   "terms",
@@ -161,14 +161,14 @@
   "column_break_84",
   "language",
   "more_information",
+  "status",
   "inter_company_invoice_reference",
-  "is_internal_customer",
   "represents_company",
   "customer_group",
   "campaign",
-  "is_discounted",
   "col_break23",
-  "status",
+  "is_internal_customer",
+  "is_discounted",
   "source",
   "more_info",
   "debit_to",
@@ -2031,7 +2031,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2021-10-02 03:36:10.251715",
+ "modified": "2021-10-11 20:19:38.667508",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index f492a03..e11fe13 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1087,8 +1087,6 @@
 
 		actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
 
-		frappe.db.commit()
-
 		self.assertEqual(actual_qty_0 - 5, actual_qty_1)
 
 		# outgoing_rate
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
index 6a8301a..e24a5f9 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -103,8 +103,11 @@
 				column.is_tree = true;
 			}
 
-			value = default_formatter(value, row, column, data);
+			if (data && data.account && column.apply_currency_formatter) {
+				data.currency = erpnext.get_currency(column.company_name);
+			}
 
+			value = default_formatter(value, row, column, data);
 			if (!data.parent_account) {
 				value = $(`<span>${value}</span>`);
 
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index b0cfbac..a600ead 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -3,12 +3,14 @@
 
 from __future__ import unicode_literals
 
+from collections import defaultdict
+
 import frappe
 from frappe import _
 from frappe.utils import cint, flt, getdate
 
+import erpnext
 from erpnext.accounts.report.balance_sheet.balance_sheet import (
-	check_opening_balance,
 	get_chart_data,
 	get_provisional_profit_loss,
 )
@@ -31,7 +33,7 @@
 from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
 	get_report_summary as get_pl_summary,
 )
-from erpnext.accounts.report.utils import convert_to_presentation_currency
+from erpnext.accounts.report.utils import convert, convert_to_presentation_currency
 
 
 def execute(filters=None):
@@ -42,7 +44,7 @@
 
 	fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year'))
 	companies_column, companies = get_companies(filters)
-	columns = get_columns(companies_column)
+	columns = get_columns(companies_column, filters)
 
 	if filters.get('report') == "Balance Sheet":
 		data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
@@ -73,21 +75,24 @@
 	provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
 		companies, filters.get('company'), company_currency, True)
 
-	message, opening_balance = check_opening_balance(asset, liability, equity)
+	message, opening_balance = prepare_companywise_opening_balance(asset, liability, equity, companies)
 
-	if opening_balance and round(opening_balance,2) !=0:
-		unclosed ={
+	if opening_balance:
+		unclosed = {
 			"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"warn_if_negative": True,
 			"currency": company_currency
 		}
-		for company in companies:
-			unclosed[company] = opening_balance
-			if provisional_profit_loss:
-				provisional_profit_loss[company] = provisional_profit_loss[company] - opening_balance
 
-		unclosed["total"]=opening_balance
+		for company in companies:
+			unclosed[company] = opening_balance.get(company)
+			if provisional_profit_loss and provisional_profit_loss.get(company):
+				provisional_profit_loss[company] = (
+					flt(provisional_profit_loss[company]) - flt(opening_balance.get(company))
+				)
+
+		unclosed["total"] = opening_balance.get(company)
 		data.append(unclosed)
 
 	if provisional_profit_loss:
@@ -102,6 +107,37 @@
 
 	return data, message, chart, report_summary
 
+def prepare_companywise_opening_balance(asset_data, liability_data, equity_data, companies):
+	opening_balance = {}
+	for company in companies:
+		opening_value = 0
+
+		# opening_value = Aseet - liability - equity
+		for data in [asset_data, liability_data, equity_data]:
+			account_name = get_root_account_name(data[0].root_type, company)
+			opening_value += get_opening_balance(account_name, data, company)
+
+		opening_balance[company] = opening_value
+
+	if opening_balance:
+		return _("Previous Financial Year is not closed"), opening_balance
+
+	return '', {}
+
+def get_opening_balance(account_name, data, company):
+	for row in data:
+		if row.get('account_name') == account_name:
+			return row.get('company_wise_opening_bal', {}).get(company, 0.0)
+
+def get_root_account_name(root_type, company):
+	return frappe.get_all(
+		'Account',
+		fields=['account_name'],
+		filters = {'root_type': root_type, 'is_group': 1,
+			'company': company, 'parent_account': ('is', 'not set')},
+		as_list=1
+	)[0][0]
+
 def get_profit_loss_data(fiscal_year, companies, columns, filters):
 	income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
 	company_currency = get_company_currency(filters)
@@ -193,30 +229,37 @@
 	data["total"] = total
 	return data
 
-def get_columns(companies):
-	columns = [{
-		"fieldname": "account",
-		"label": _("Account"),
-		"fieldtype": "Link",
-		"options": "Account",
-		"width": 300
-	}]
-
-	columns.append({
-		"fieldname": "currency",
-		"label": _("Currency"),
-		"fieldtype": "Link",
-		"options": "Currency",
-		"hidden": 1
-	})
+def get_columns(companies, filters):
+	columns = [
+		{
+			"fieldname": "account",
+			"label": _("Account"),
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 300
+		}, {
+			"fieldname": "currency",
+			"label": _("Currency"),
+			"fieldtype": "Link",
+			"options": "Currency",
+			"hidden": 1
+		}
+	]
 
 	for company in companies:
+		apply_currency_formatter = 1 if not filters.presentation_currency else 0
+		currency = filters.presentation_currency
+		if not currency:
+			currency = erpnext.get_company_currency(company)
+
 		columns.append({
 			"fieldname": company,
-			"label": company,
+			"label": f'{company} ({currency})',
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 150
+			"width": 150,
+			"apply_currency_formatter": apply_currency_formatter,
+			"company_name": company
 		})
 
 	return columns
@@ -236,6 +279,8 @@
 		start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
 		end_date = filters.period_end_date
 
+	filters.end_date = end_date
+
 	gl_entries_by_account = {}
 	for root in frappe.db.sql("""select lft, rgt from tabAccount
 			where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
@@ -244,9 +289,10 @@
 			end_date, root.lft, root.rgt, filters,
 			gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
 
-	calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters)
+	calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year)
 	accumulate_values_into_parents(accounts, accounts_by_name, companies)
-	out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency)
+
+	out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters)
 
 	if out:
 		add_total_row(out, root_type, balance_must_be, companies, company_currency)
@@ -257,7 +303,10 @@
 	return (filters.get('presentation_currency')
 		or frappe.get_cached_value('Company',  filters.company,  "default_currency"))
 
-def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters):
+def calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year):
+	start_date = (fiscal_year.year_start_date
+		if filters.filter_based_on == 'Fiscal Year' else filters.period_start_date)
+
 	for entries in gl_entries_by_account.values():
 		for entry in entries:
 			if entry.account_number:
@@ -266,15 +315,32 @@
 				account_name =  entry.account_name
 
 			d = accounts_by_name.get(account_name)
+
 			if d:
+				debit, credit = 0, 0
 				for company in companies:
 					# check if posting date is within the period
 					if (entry.company == company or (filters.get('accumulated_in_group_company'))
 						and entry.company in companies.get(company)):
-						d[company] = d.get(company, 0.0) + flt(entry.debit) - flt(entry.credit)
+						parent_company_currency = erpnext.get_company_currency(d.company)
+						child_company_currency = erpnext.get_company_currency(entry.company)
+
+						debit, credit = flt(entry.debit), flt(entry.credit)
+
+						if (not filters.get('presentation_currency')
+							and entry.company != company
+							and parent_company_currency != child_company_currency
+							and filters.get('accumulated_in_group_company')):
+							debit = convert(debit, parent_company_currency, child_company_currency, filters.end_date)
+							credit = convert(credit, parent_company_currency, child_company_currency, filters.end_date)
+
+						d[company] = d.get(company, 0.0) + flt(debit) - flt(credit)
+
+						if entry.posting_date < getdate(start_date):
+							d['company_wise_opening_bal'][company] += (flt(debit) - flt(credit))
 
 				if entry.posting_date < getdate(start_date):
-					d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
+					d["opening_balance"] = d.get("opening_balance", 0.0) + flt(debit) - flt(credit)
 
 def accumulate_values_into_parents(accounts, accounts_by_name, companies):
 	"""accumulate children's values in parent accounts"""
@@ -282,17 +348,18 @@
 		if d.parent_account:
 			account = d.parent_account_name
 
-			if not accounts_by_name.get(account):
-				continue
+			# if not accounts_by_name.get(account):
+			# 	continue
 
 			for company in companies:
 				accounts_by_name[account][company] = \
 					accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
 
+				accounts_by_name[account]['company_wise_opening_bal'][company] += d.get('company_wise_opening_bal', {}).get(company, 0.0)
+
 			accounts_by_name[account]["opening_balance"] = \
 				accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
 
-
 def get_account_heads(root_type, companies, filters):
 	accounts = get_accounts(root_type, filters)
 
@@ -353,7 +420,7 @@
 			`tabAccount` where company = %s and root_type = %s
 		""" , (filters.get('company'), root_type), as_dict=1)
 
-def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency):
+def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters):
 	data = []
 
 	for d in accounts:
@@ -367,10 +434,13 @@
 			"parent_account": _(d.parent_account),
 			"indent": flt(d.indent),
 			"year_start_date": start_date,
+			"root_type": d.root_type,
 			"year_end_date": end_date,
-			"currency": company_currency,
+			"currency": filters.presentation_currency,
+			"company_wise_opening_bal": d.company_wise_opening_bal,
 			"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
 		})
+
 		for company in companies:
 			if d.get(company) and balance_must_be == "Credit":
 				# change sign based on Debit or Credit, since calculation is done using (debit - credit)
@@ -385,6 +455,7 @@
 
 		row["has_value"] = has_value
 		row["total"] = total
+
 		data.append(row)
 
 	return data
@@ -447,6 +518,7 @@
 		'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
 
 def validate_entries(key, entry, accounts_by_name, accounts):
+	# If an account present in the child company and not in the parent company
 	if key not in accounts_by_name:
 		args = get_account_details(entry.account)
 
@@ -456,12 +528,23 @@
 			args.update({
 				'lft': parent_args.lft + 1,
 				'rgt': parent_args.rgt - 1,
+				'indent': 3,
 				'root_type': parent_args.root_type,
-				'report_type': parent_args.report_type
+				'report_type': parent_args.report_type,
+				'parent_account_name': parent_args.account_name,
+				'company_wise_opening_bal': defaultdict(float)
 			})
 
 		accounts_by_name.setdefault(key, args)
-		accounts.append(args)
+
+		idx = len(accounts)
+		# To identify parent account index
+		for index, row in enumerate(accounts):
+			if row.parent_account_name == args.parent_account_name:
+				idx = index
+				break
+
+		accounts.insert(idx+1, args)
 
 def get_additional_conditions(from_date, ignore_closing_entries, filters):
 	additional_conditions = []
@@ -491,7 +574,6 @@
 			for company in companies:
 				total_row.setdefault(company, 0.0)
 				total_row[company] += row.get(company, 0.0)
-				row[company] = 0.0
 
 			total_row.setdefault("total", 0.0)
 			total_row["total"] += flt(row["total"])
@@ -511,6 +593,7 @@
 			account_name = d.account_number + ' - ' + d.account_name
 		else:
 			account_name =  d.account_name
+		d['company_wise_opening_bal'] = defaultdict(float)
 		accounts_by_name[account_name] = d
 
 		parent_children_map.setdefault(d.parent_account or None, []).append(d)
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index 2b26ac5..30ed58b 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -534,6 +534,17 @@
    "type": "Link"
   },
   {
+    "dependencies": "GL Entry",
+    "hidden": 0,
+    "is_query_report": 1,
+    "label": "KSA VAT Report",
+    "link_to": "KSA VAT",
+    "link_type": "Report",
+    "onboard": 0,
+    "only_for": "Saudi Arabia",
+    "type": "Link"
+   },
+  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Financial Statements",
@@ -1154,6 +1165,16 @@
    "type": "Link"
   },
   {
+    "hidden": 0,
+    "is_query_report": 0,
+    "label": "KSA VAT Setting",
+    "link_to": "KSA VAT Setting",
+    "link_type": "DocType",
+    "onboard": 0,
+    "only_for": "Saudi Arabia",
+    "type": "Link"
+   },
+  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Profitability",
@@ -1206,7 +1227,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-27 12:15:52.872470",
+ "modified": "2021-08-26 13:15:52.872470",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js
index 944bb61..32431fc 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.js
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.js
@@ -11,7 +11,7 @@
 	{
 		fieldname: "supp_master_name",
 		title: "Supplier Naming By",
-		description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a  ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
+		description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a <a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a> choose the 'Naming Series' option."),
 	},
 	{
 		fieldname: "buying_price_list",
diff --git a/erpnext/buying/form_tour/buying_settings/buying_settings.json b/erpnext/buying/form_tour/buying_settings/buying_settings.json
new file mode 100644
index 0000000..fa8c80d
--- /dev/null
+++ b/erpnext/buying/form_tour/buying_settings/buying_settings.json
@@ -0,0 +1,77 @@
+{
+ "creation": "2021-07-28 11:51:42.319984",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:06:56.414584",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Buying Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Buying Settings",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "When a Supplier is saved, system generates a unique identity or name for that Supplier which can be used to refer the Supplier in various Buying transactions.",
+   "field": "",
+   "fieldname": "supp_master_name",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Supplier Naming By",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Supplier Naming By"
+  },
+  {
+   "description": "Configure what should be the default value of Supplier Group when creating a new Supplier.",
+   "field": "",
+   "fieldname": "supplier_group",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Default Supplier Group",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Default Supplier Group"
+  },
+  {
+   "description": "Item prices will be fetched from this Price List.",
+   "field": "",
+   "fieldname": "buying_price_list",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Default Buying Price List",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Default Buying Price List"
+  },
+  {
+   "description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice or a Purchase Receipt directly without creating a Purchase Order first.",
+   "field": "",
+   "fieldname": "po_required",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Purchase Order Required for Purchase Invoice & Receipt Creation?",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purchase Order Required"
+  },
+  {
+   "description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first.",
+   "field": "",
+   "fieldname": "pr_required",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Purchase Receipt Required for Purchase Invoice Creation?",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purchase Receipt Required"
+  }
+ ],
+ "title": "Buying Settings"
+}
\ No newline at end of file
diff --git a/erpnext/buying/form_tour/purchase_order/purchase_order.json b/erpnext/buying/form_tour/purchase_order/purchase_order.json
new file mode 100644
index 0000000..3cc88fb
--- /dev/null
+++ b/erpnext/buying/form_tour/purchase_order/purchase_order.json
@@ -0,0 +1,82 @@
+{
+ "creation": "2021-07-29 14:11:58.271113",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:11:31.436135",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Order",
+ "save_on_complete": 1,
+ "steps": [
+  {
+   "description": "Select a Supplier",
+   "field": "",
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Supplier",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Supplier"
+  },
+  {
+   "description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
+   "field": "",
+   "fieldname": "schedule_date",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Required By",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Required By"
+  },
+  {
+   "description": "Items to be purchased can be added here.",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Items Table"
+  },
+  {
+   "child_doctype": "Purchase Order Item",
+   "description": "Enter the Item Code.",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 1,
+   "label": "Item Code",
+   "next_step_condition": "eval: doc.item_code",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Item Code"
+  },
+  {
+   "child_doctype": "Purchase Order Item",
+   "description": "Enter the required quantity for the material.",
+   "field": "",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Quantity",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Bottom",
+   "title": "Quantity"
+  }
+ ],
+ "title": "Purchase Order"
+}
\ No newline at end of file
diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json
index 887f85b..84e97a2 100644
--- a/erpnext/buying/module_onboarding/buying/buying.json
+++ b/erpnext/buying/module_onboarding/buying/buying.json
@@ -19,7 +19,7 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-07-08 14:05:28.273641",
+ "modified": "2021-08-24 18:13:42.463776",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying",
@@ -29,22 +29,10 @@
    "step": "Introduction to Buying"
   },
   {
-   "step": "Create a Supplier"
-  },
-  {
-   "step": "Setup your Warehouse"
-  },
-  {
-   "step": "Create a Product"
-  },
-  {
    "step": "Create a Material Request"
   },
   {
    "step": "Create your first Purchase Order"
-  },
-  {
-   "step": "Buying Settings"
   }
  ],
  "subtitle": "Products, Purchases, Analysis, and more.",
diff --git a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
index 9dc493d..28e86ab 100644
--- a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
+++ b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
@@ -1,19 +1,21 @@
 {
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Material Request",
  "creation": "2020-05-15 14:39:09.818764",
+ "description": "# Track Material Request\n\n\nAlso known as Purchase Request or an Indent, is a document identifying a requirement of a set of items (products or services) for various purposes like procurement, transfer, issue, or manufacturing. Once the Material Request is validated, a purchase manager can take the next actions for purchasing items like requesting RFQ from a supplier or directly placing an order with an identified Supplier.\n\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 1,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-15 14:39:09.818764",
+ "modified": "2021-08-24 18:08:08.347501",
  "modified_by": "Administrator",
  "name": "Create a Material Request",
  "owner": "Administrator",
  "reference_document": "Material Request",
+ "show_form_tour": 1,
  "show_full_form": 1,
- "title": "Create a Material Request",
+ "title": "Track Material Request",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
index 9dbed23..18a3931 100644
--- a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
+++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
@@ -1,19 +1,21 @@
 {
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Purchase Order",
  "creation": "2020-05-12 18:17:49.976035",
+ "description": "# Create first Purchase Order\n\nPurchase Order is at the heart of your buying transactions. In ERPNext, Purchase Order can can be created against a Purchase Material Request (indent) and Supplier Quotation as well.  Purchase Orders is also linked to Purchase Receipt and Purchase Invoices, allowing you to keep a birds-eye view on your purchase deals.\n\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-12 18:31:56.856112",
+ "modified": "2021-08-24 18:08:08.936484",
  "modified_by": "Administrator",
  "name": "Create your first Purchase Order",
  "owner": "Administrator",
  "reference_document": "Purchase Order",
+ "show_form_tour": 0,
  "show_full_form": 0,
- "title": "Create your first Purchase Order",
+ "title": "Create first Purchase Order",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
index fd98fdd..01ac8b8 100644
--- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
+++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
@@ -1,19 +1,22 @@
 {
- "action": "Watch Video",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s walk-through few Buying Settings",
  "creation": "2020-05-06 15:37:09.477765",
+ "description": "# Buying Settings\n\n\nBuying module\u2019s features are highly configurable as per your business needs. Buying Settings is the place where you can set your preferences for:\n\n- Supplier naming and default values\n- Billing and shipping preference in buying transactions\n\n\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
+ "is_single": 1,
  "is_skipped": 0,
- "modified": "2020-05-12 18:25:08.509900",
+ "modified": "2021-08-24 18:08:08.345735",
  "modified_by": "Administrator",
  "name": "Introduction to Buying",
  "owner": "Administrator",
- "show_full_form": 0,
- "title": "Introduction to Buying",
+ "reference_document": "Buying Settings",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Buying Settings",
  "validate_action": 1,
  "video_url": "https://youtu.be/efFajTTQBa8"
 }
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
index a5b0947..fd23795 100644
--- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -45,7 +45,6 @@
 		pr = make_purchase_receipt(po.name)
 		pr.get("items")[0].cost_center = "Main - _TPC"
 		pr.submit()
-		frappe.db.commit()
 		date_obj = datetime.date(datetime.now())
 
 		po.load_from_db()
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 78a6e52..4697205 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -591,7 +591,7 @@
 
 	data = frappe.db.sql("""
 		select item_code, warehouse, count(name) as total_row
-		from `tabStock Ledger Entry`
+		from `tabStock Ledger Entry` force index (item_warehouse)
 		where
 			({})
 			and timestamp(posting_date, posting_time)
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py b/erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json
new file mode 100644
index 0000000..d4d4a51
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json
@@ -0,0 +1,51 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-09-11 05:09:53.773838",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "region",
+  "region_code",
+  "country",
+  "country_code"
+ ],
+ "fields": [
+  {
+   "fieldname": "region",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Region"
+  },
+  {
+   "fieldname": "region_code",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Region Code"
+  },
+  {
+   "fieldname": "country",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Country"
+  },
+  {
+   "fieldname": "country_code",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Country Code"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-14 05:33:06.444710",
+ "modified_by": "Administrator",
+ "module": "ERPNext Integrations",
+ "name": "TaxJar Nexus",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py
new file mode 100644
index 0000000..c24aa8c
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TaxJarNexus(Document):
+	pass
diff --git a/erpnext/regional/united_states/product_tax_category_data.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/product_tax_category_data.json
similarity index 100%
rename from erpnext/regional/united_states/product_tax_category_data.json
rename to erpnext/erpnext_integrations/doctype/taxjar_settings/product_tax_category_data.json
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
index 62d5709..d495989 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
@@ -5,5 +5,16 @@
 	is_sandbox: (frm) => {
 		frm.toggle_reqd("api_key", !frm.doc.is_sandbox);
 		frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox);
-	}
+	},
+
+	refresh: (frm) => {
+		frm.add_custom_button(__('Update Nexus List'), function() {
+			frm.call({
+				doc: frm.doc,
+				method: 'update_nexus_list'
+			});
+		});
+	},
+
+
 });
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
index c0d60f7..2d17f2e 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
@@ -6,8 +6,8 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "is_sandbox",
   "taxjar_calculate_tax",
+  "is_sandbox",
   "taxjar_create_transactions",
   "credentials",
   "api_key",
@@ -16,7 +16,10 @@
   "configuration",
   "tax_account_head",
   "configuration_cb",
-  "shipping_account_head"
+  "shipping_account_head",
+  "section_break_12",
+  "nexus_address",
+  "nexus"
  ],
  "fields": [
   {
@@ -54,6 +57,7 @@
   },
   {
    "default": "0",
+   "depends_on": "taxjar_calculate_tax",
    "fieldname": "is_sandbox",
    "fieldtype": "Check",
    "label": "Sandbox Mode"
@@ -69,6 +73,7 @@
   },
   {
    "default": "0",
+   "depends_on": "taxjar_calculate_tax",
    "fieldname": "taxjar_create_transactions",
    "fieldtype": "Check",
    "label": "Create TaxJar Transaction"
@@ -82,11 +87,28 @@
   {
    "fieldname": "cb_keys",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_12",
+   "fieldtype": "Section Break",
+   "label": "Nexus List"
+  },
+  {
+   "fieldname": "nexus_address",
+   "fieldtype": "HTML",
+   "label": "Nexus Address"
+  },
+  {
+   "fieldname": "nexus",
+   "fieldtype": "Table",
+   "label": "Nexus",
+   "options": "TaxJar Nexus",
+   "read_only": 1
   }
  ],
  "issingle": 1,
  "links": [],
- "modified": "2020-04-30 04:38:03.311089",
+ "modified": "2021-10-06 10:59:13.475442",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "TaxJar Settings",
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
index 9dd4817..f430a9e 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
@@ -4,9 +4,98 @@
 
 from __future__ import unicode_literals
 
-# import frappe
+import json
+import os
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.model.document import Document
+from frappe.permissions import add_permission, update_permission_property
+
+from erpnext.erpnext_integrations.taxjar_integration import get_client
 
 
 class TaxJarSettings(Document):
-	pass
+
+	def on_update(self):
+		TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+		TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
+		TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
+
+		fields_already_exist = frappe.db.exists('Custom Field', {'dt': ('in', ['Item','Sales Invoice Item']), 'fieldname':'product_tax_category'})
+		fields_hidden = frappe.get_value('Custom Field', {'dt': ('in', ['Sales Invoice Item'])}, 'hidden')
+
+		if (TAXJAR_CREATE_TRANSACTIONS or TAXJAR_CALCULATE_TAX or TAXJAR_SANDBOX_MODE):
+			if not fields_already_exist:
+				add_product_tax_categories()
+				make_custom_fields()
+				add_permissions()
+				frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
+
+			elif fields_already_exist and fields_hidden:
+				toggle_tax_category_fields(hidden='0')
+
+		elif fields_already_exist:
+			toggle_tax_category_fields(hidden='1')
+
+	def validate(self):
+		self.calculate_taxes_validation_for_create_transactions()
+
+	@frappe.whitelist()
+	def update_nexus_list(self):
+		client = get_client()
+		nexus = client.nexus_regions()
+
+		new_nexus_list = [frappe._dict(address) for address in nexus]
+
+		self.set('nexus', [])
+		self.set('nexus', new_nexus_list)
+		self.save()
+
+	def calculate_taxes_validation_for_create_transactions(self):
+		if not self.taxjar_calculate_tax and (self.taxjar_create_transactions or self.is_sandbox):
+			frappe.throw(frappe._('Before enabling <b>Create Transaction</b> or <b>Sandbox Mode</b>, you need to check the <b>Enable Tax Calculation</b> box'))
+
+
+def toggle_tax_category_fields(hidden):
+	frappe.set_value('Custom Field', {'dt':'Sales Invoice Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+	frappe.set_value('Custom Field', {'dt':'Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+
+
+def add_product_tax_categories():
+	with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
+		tax_categories = json.loads(f.read())
+	create_tax_categories(tax_categories['categories'])
+
+def create_tax_categories(data):
+	for d in data:
+		if not frappe.db.exists('Product Tax Category',{'product_tax_code':d.get('product_tax_code')}):
+			tax_category = frappe.new_doc('Product Tax Category')
+			tax_category.description = d.get("description")
+			tax_category.product_tax_code = d.get("product_tax_code")
+			tax_category.category_name = d.get("name")
+			tax_category.db_insert()
+
+def make_custom_fields(update=True):
+	custom_fields = {
+		'Sales Invoice Item': [
+			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
+				label='Product Tax Category', fetch_from='item_code.product_tax_category'),
+			dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
+				label='Tax Collectable', read_only=1),
+			dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
+				label='Taxable Amount', read_only=1)
+		],
+		'Item': [
+			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
+				label='Product Tax Category')
+		]
+	}
+	create_custom_fields(custom_fields, update=update)
+
+def add_permissions():
+	doctype = "Product Tax Category"
+	for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
+		add_permission(doctype, role, 0)
+		update_permission_property(doctype, role, 0, 'write', 1)
+		update_permission_property(doctype, role, 0, 'create', 1)
diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py
index 870a4ef..2a7243c 100644
--- a/erpnext/erpnext_integrations/taxjar_integration.py
+++ b/erpnext/erpnext_integrations/taxjar_integration.py
@@ -4,7 +4,7 @@
 import taxjar
 from frappe import _
 from frappe.contacts.doctype.address.address import get_company_address
-from frappe.utils import cint
+from frappe.utils import cint, flt
 
 from erpnext import get_default_company
 
@@ -103,7 +103,7 @@
 
 	shipping = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == SHIP_ACCOUNT_HEAD])
 
-	line_items = [get_line_item_dict(item) for item in doc.items]
+	line_items = [get_line_item_dict(item, doc.docstatus) for item in doc.items]
 
 	if from_shipping_state not in SUPPORTED_STATE_CODES:
 		from_shipping_state = get_state_code(from_address, 'Company')
@@ -139,14 +139,21 @@
 
 	return state_code
 
-def get_line_item_dict(item):
-	return dict(
+def get_line_item_dict(item, docstatus):
+	tax_dict = dict(
 		id = item.get('idx'),
 		quantity = item.get('qty'),
 		unit_price = item.get('rate'),
 		product_tax_code = item.get('product_tax_category')
 	)
 
+	if docstatus == 1:
+		tax_dict.update({
+			'sales_tax':item.get('tax_collectable')
+		})
+
+	return tax_dict
+
 def set_sales_tax(doc, method):
 	if not TAXJAR_CALCULATE_TAX:
 		return
@@ -164,6 +171,9 @@
 		setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
 		return
 
+	# check if delivering within a nexus
+	check_for_nexus(doc, tax_dict)
+
 	tax_data = validate_tax_request(tax_dict)
 	if tax_data is not None:
 		if not tax_data.amount_to_collect:
@@ -191,6 +201,17 @@
 
 			doc.run_method("calculate_taxes_and_totals")
 
+def check_for_nexus(doc, tax_dict):
+	if not frappe.db.get_value('TaxJar Nexus', {'region_code': tax_dict["to_state"]}):
+		for item in doc.get("items"):
+			item.tax_collectable = flt(0)
+			item.taxable_amount = flt(0)
+
+		for tax in doc.taxes:
+			if tax.account_head == TAX_ACCOUNT_HEAD:
+				doc.taxes.remove(tax)
+		return
+
 def check_sales_tax_exemption(doc):
 	# if the party is exempt from sales tax, then set all tax account heads to zero
 	sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d708f92..05f07f5 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -250,6 +250,7 @@
 		"validate": "erpnext.regional.india.utils.validate_tax_category"
 	},
 	"Sales Invoice": {
+		"after_insert": "erpnext.regional.saudi_arabia.utils.create_qr_code",
 		"on_submit": [
 			"erpnext.regional.create_transaction_log",
 			"erpnext.regional.italy.utils.sales_invoice_on_submit",
@@ -259,7 +260,10 @@
 			"erpnext.regional.italy.utils.sales_invoice_on_cancel",
 			"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
 		],
-		"on_trash": "erpnext.regional.check_deletion_permission",
+		"on_trash": [
+			"erpnext.regional.check_deletion_permission",
+			"erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
+		],
 		"validate": [
 			"erpnext.regional.india.utils.validate_document_name",
 			"erpnext.regional.india.utils.update_taxable_values"
diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
index bed12e3..8a23682 100644
--- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
@@ -74,7 +74,6 @@
 			from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
 			where q.name = r.parent""", as_dict=1)
 
-		frappe.db.commit()
 
 	def setup_groups(self, hour=None):
 		# setup email to trigger at this hour
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 3c4c672..218e97d 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -10,6 +10,26 @@
 	},
 	company: function(frm) {
 		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+		var expenses = frm.doc.expenses;
+		for (var i = 0; i < expenses.length; i++) {
+			var expense = expenses[i];
+			if (!expense.expense_type) {
+				continue;
+			}
+			frappe.call({
+				method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center",
+				args: {
+					"expense_claim_type": expense.expense_type,
+					"company": frm.doc.company
+				},
+				callback: function(r) {
+					if (r.message) {
+						expense.default_account = r.message.account;
+						expense.cost_center = r.message.cost_center;
+					}
+				}
+			});
+		}
 	},
 });
 
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index c9f23ca..5979992 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -334,7 +334,6 @@
   },
   {
    "depends_on": "eval:doc.is_secured_loan",
-   "fetch_from": "loan_application.maximum_loan_amount",
    "fieldname": "maximum_loan_amount",
    "fieldtype": "Currency",
    "label": "Maximum Loan Amount",
@@ -360,7 +359,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-19 18:10:32.360818",
+ "modified": "2021-10-12 18:10:32.360818",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index 7dbd422..0f2c3cf 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -137,16 +137,23 @@
 			frappe.throw(_("Loan amount is mandatory"))
 
 	def link_loan_security_pledge(self):
-		if self.is_secured_loan:
-			loan_security_pledge = frappe.db.get_value('Loan Security Pledge', {'loan_application': self.loan_application},
-				'name')
+		if self.is_secured_loan and self.loan_application:
+			maximum_loan_value = frappe.db.get_value('Loan Security Pledge',
+				{
+					'loan_application': self.loan_application,
+					'status': 'Requested'
+				},
+				'sum(maximum_loan_value)'
+			)
 
-			if loan_security_pledge:
-				frappe.db.set_value('Loan Security Pledge', loan_security_pledge, {
-					'loan': self.name,
-					'status': 'Pledged',
-					'pledge_time': now_datetime()
-				})
+			if maximum_loan_value:
+				frappe.db.sql("""
+					UPDATE `tabLoan Security Pledge`
+					SET loan = %s, pledge_time = %s, status = 'Pledged'
+					WHERE status = 'Requested' and loan_application = %s
+				""", (self.name, now_datetime(), self.loan_application))
+
+				self.db_set('maximum_loan_amount', maximum_loan_value)
 
 	def unlink_loan_security_pledge(self):
 		pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index e492920..ede0467 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -130,10 +130,11 @@
 def create_loan(source_name, target_doc=None, submit=0):
 	def update_accounts(source_doc, target_doc, source_parent):
 		account_details = frappe.get_all("Loan Type",
-		 fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
-		 filters = {'name': source_doc.loan_type}
-		)[0]
+			fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
+			filters = {'name': source_doc.loan_type})[0]
 
+		if source_doc.is_secured_loan:
+			target_doc.maximum_loan_amount = 0
 
 		target_doc.mode_of_payment = account_details.mode_of_payment
 		target_doc.payment_account = account_details.payment_account
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 6d9d4f4..99f0d25 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -198,7 +198,7 @@
 		security_value = get_total_pledged_security_value(loan)
 
 	if loan_details.is_secured_loan and not on_current_security_price:
-		security_value = flt(loan_details.maximum_loan_amount)
+		security_value = get_maximum_amount_as_per_pledged_security(loan)
 
 	if not security_value and not loan_details.is_secured_loan:
 		security_value = flt(loan_details.loan_amount)
@@ -209,3 +209,6 @@
 		disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
 
 	return disbursal_amount
+
+def get_maximum_amount_as_per_pledged_security(loan):
+	return flt(frappe.db.get_value('Loan Security Pledge', {'loan': loan}, 'sum(maximum_loan_value)'))
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 13b7357..40bb581 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -411,7 +411,7 @@
 		if due_date and not final_due_date:
 			final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
 
-	if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
+	if against_loan_doc.status in ('Disbursed', 'Closed') or against_loan_doc.disbursed_amount >= against_loan_doc.loan_amount:
 		pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
 			- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
 	else:
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 7cfec97..232e3a0 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1133,8 +1133,7 @@
 			query_filters["has_variants"] = 0
 
 	if filters and filters.get("is_stock_item"):
-		or_cond_filters["is_stock_item"] = 1
-		or_cond_filters["has_variants"] = 1
+		query_filters["is_stock_item"] = 1
 
 	return frappe.get_list("Item",
 		fields = fields, filters=query_filters,
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 706ea26..4c03230 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -4,13 +4,14 @@
 
 import unittest
 from collections import deque
+from functools import partial
 
 import frappe
 from frappe.test_runner import make_test_records
 from frappe.utils import cstr, flt
 
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.manufacturing.doctype.bom.bom import make_variant_bom
+from erpnext.manufacturing.doctype.bom.bom import item_query, make_variant_bom
 from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
@@ -375,6 +376,16 @@
 		# FG Items in Scrap/Loss Table should have Is Process Loss set
 		self.assertRaises(frappe.ValidationError, bom_doc.submit)
 
+	def test_bom_item_query(self):
+		query = partial(item_query, doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters={"is_stock_item": 1})
+
+		test_items = query(txt="_Test")
+		filtered = query(txt="_Test Item 2")
+
+		self.assertNotEqual(len(test_items), len(filtered), msg="Item filtering showing excessive results")
+		self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
+
+
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 22a6313..86480be 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -246,7 +246,7 @@
 erpnext.patches.v12_0.add_state_code_for_ladakh
 erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
 erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
-erpnext.patches.v12_0.update_vehicle_no_reqd_condition
+erpnext.patches.v13_0.update_vehicle_no_reqd_condition
 erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
 erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
 erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
@@ -286,6 +286,8 @@
 erpnext.patches.v13_0.migrate_stripe_api
 erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
 erpnext.patches.v13_0.einvoicing_deprecation_warning
+execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings")
+execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category")
 erpnext.patches.v13_0.custom_fields_for_taxjar_integration
 erpnext.patches.v14_0.delete_einvoicing_doctypes
 erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
@@ -299,7 +301,7 @@
 erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
 erpnext.patches.v13_0.trim_sales_invoice_custom_field_length
 erpnext.patches.v13_0.create_custom_field_for_finance_book
-erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries
+erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries #2
 erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
 erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
-erpnext.patches.v13_0.add_default_interview_notification_templates
\ No newline at end of file
+erpnext.patches.v13_0.add_default_interview_notification_templates
diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
index eee9f11..e136d64 100644
--- a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
+++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
@@ -3,7 +3,7 @@
 import frappe
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
-from erpnext.regional.united_states.setup import add_permissions
+from erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings import add_permissions
 
 
 def execute():
@@ -11,7 +11,12 @@
 	if not company:
 		return
 
-	frappe.reload_doc("regional", "doctype", "product_tax_category")
+	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+	TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
+	TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
+
+	if (not TAXJAR_CREATE_TRANSACTIONS and not TAXJAR_CALCULATE_TAX and not TAXJAR_SANDBOX_MODE):
+		return
 
 	custom_fields = {
 		'Sales Invoice Item': [
@@ -29,4 +34,4 @@
 	}
 	create_custom_fields(custom_fields, update=True)
 	add_permissions()
-	frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=True)
+	frappe.enqueue('erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings.add_product_tax_categories', now=True)
diff --git a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
index fa8a864..3af7dac 100644
--- a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
+++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
@@ -17,7 +17,7 @@
 		where
 			ref_exchange_rate = 1
 			and docstatus = 1
-			and ifnull(exchange_gain_loss, '') != ''
+			and ifnull(exchange_gain_loss, 0) != 0
 		group by
 			parent
 	""", as_dict=1)
@@ -30,7 +30,7 @@
 		where
 			ref_exchange_rate = 1
 			and docstatus = 1
-			and ifnull(exchange_gain_loss, '') != ''
+			and ifnull(exchange_gain_loss, 0) != 0
 		group by
 			parent
 	""", as_dict=1)
@@ -38,12 +38,24 @@
 	if purchase_invoices + sales_invoices:
 		frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
 
+	acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
+	if acc_frozen_upto:
+		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+
 	for invoice in purchase_invoices + sales_invoices:
-		doc = frappe.get_doc(invoice.type, invoice.name)
-		doc.docstatus = 2
-		doc.make_gl_entries()
-		for advance in doc.advances:
-			if advance.ref_exchange_rate == 1:
-				advance.db_set('exchange_gain_loss', 0, False)
-		doc.docstatus = 1
-		doc.make_gl_entries()
\ No newline at end of file
+		try:
+			doc = frappe.get_doc(invoice.type, invoice.name)
+			doc.docstatus = 2
+			doc.make_gl_entries()
+			for advance in doc.advances:
+				if advance.ref_exchange_rate == 1:
+					advance.db_set('exchange_gain_loss', 0, False)
+			doc.docstatus = 1
+			doc.make_gl_entries()
+			frappe.db.commit()
+		except Exception:
+			frappe.db.rollback()
+			print(f'Failed to correct gl entries of {invoice.name}')
+
+	if acc_frozen_upto:
+		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', acc_frozen_upto)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
similarity index 81%
rename from erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
rename to erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
index 69bfaaa..902707b 100644
--- a/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -2,7 +2,7 @@
 
 
 def execute():
-	frappe.reload_doc('custom', 'doctype', 'custom_field')
+	frappe.reload_doc('custom', 'doctype', 'custom_field', force=True)
 	company = frappe.get_all('Company', filters = {'country': 'India'})
 	if not company:
 		return
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index 45a689e..d7dcbf4 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -31,3 +31,4 @@
 		"document_name": doc.name,
 		"data": data
 	}).insert(ignore_permissions=True)
+
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 95b930c..fc579d4 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -6,8 +6,10 @@
  "engine": "InnoDB",
  "field_order": [
   "gst_summary",
-  "column_break_2",
+  "gst_tax_settings_section",
   "round_off_gst_values",
+  "column_break_4",
+  "hsn_wise_tax_breakup",
   "gstin_email_sent_on",
   "section_break_4",
   "gst_accounts",
@@ -17,37 +19,23 @@
   {
    "fieldname": "gst_summary",
    "fieldtype": "HTML",
-   "label": "GST Summary",
-   "show_days": 1,
-   "show_seconds": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "GST Summary"
   },
   {
    "fieldname": "gstin_email_sent_on",
    "fieldtype": "Date",
    "label": "GSTIN Email Sent On",
-   "read_only": 1,
-   "show_days": 1,
-   "show_seconds": 1
+   "read_only": 1
   },
   {
    "fieldname": "section_break_4",
-   "fieldtype": "Section Break",
-   "show_days": 1,
-   "show_seconds": 1
+   "fieldtype": "Section Break"
   },
   {
    "fieldname": "gst_accounts",
    "fieldtype": "Table",
    "label": "GST Accounts",
-   "options": "GST Account",
-   "show_days": 1,
-   "show_seconds": 1
+   "options": "GST Account"
   },
   {
    "default": "250000",
@@ -56,24 +44,35 @@
    "fieldtype": "Data",
    "in_list_view": 1,
    "label": "B2C Limit",
-   "reqd": 1,
-   "show_days": 1,
-   "show_seconds": 1
+   "reqd": 1
   },
   {
    "default": "0",
    "description": "Enabling this option will round off individual GST components in all the Invoices",
    "fieldname": "round_off_gst_values",
    "fieldtype": "Check",
-   "label": "Round Off GST Values",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "Round Off GST Values"
+  },
+  {
+   "default": "0",
+   "fieldname": "hsn_wise_tax_breakup",
+   "fieldtype": "Check",
+   "label": "Tax Breakup Table Based On HSN Code"
+  },
+  {
+   "fieldname": "gst_tax_settings_section",
+   "fieldtype": "Section Break",
+   "label": "GST Tax Settings"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-01-28 17:19:47.969260",
+ "modified": "2021-10-11 18:10:14.242614",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "GST Settings",
@@ -83,4 +82,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-   }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py b/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
new file mode 100644
index 0000000..89ba3e9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "creation": "2021-07-13 09:17:09.862163",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "title",
+  "item_tax_template",
+  "account"
+ ],
+ "fields": [
+  {
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Title",
+   "reqd": 1
+  },
+  {
+   "fieldname": "item_tax_template",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Tax Template",
+   "options": "Item Tax Template",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-04 06:42:38.205597",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Purchase Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
new file mode 100644
index 0000000..3920bc5
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATPurchaseAccount(Document):
+	pass
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py b/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
new file mode 100644
index 0000000..72613f4
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Sales Account', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
new file mode 100644
index 0000000..df27478
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "creation": "2021-07-13 08:46:33.820968",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "title",
+  "item_tax_template",
+  "account"
+ ],
+ "fields": [
+  {
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Title",
+   "reqd": 1
+  },
+  {
+   "fieldname": "item_tax_template",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Tax Template",
+   "options": "Item Tax Template",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-04 06:42:00.081407",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Sales Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
new file mode 100644
index 0000000..7c2689f
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSalesAccount(Document):
+	pass
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
new file mode 100644
index 0000000..1d6a6a7
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSalesAccount(unittest.TestCase):
+	pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/__init__.py b/erpnext/regional/doctype/ksa_vat_setting/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
new file mode 100644
index 0000000..00b62b9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Setting', {
+	onload: function () {
+		frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting');
+	}
+});
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
new file mode 100644
index 0000000..3361946
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2021-07-13 08:49:01.100356",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "ksa_vat_sales_accounts",
+  "ksa_vat_purchase_accounts"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "ksa_vat_sales_accounts",
+   "fieldtype": "Table",
+   "label": "KSA VAT Sales Accounts",
+   "options": "KSA VAT Sales Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "ksa_vat_purchase_accounts",
+   "fieldtype": "Table",
+   "label": "KSA VAT Purchase Accounts",
+   "options": "KSA VAT Purchase Account",
+   "reqd": 1
+  }
+ ],
+ "links": [],
+ "modified": "2021-08-26 04:29:06.499378",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Setting",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "company",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
new file mode 100644
index 0000000..bdae116
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSetting(Document):
+	pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
new file mode 100644
index 0000000..269cbec
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
@@ -0,0 +1,5 @@
+frappe.listview_settings['KSA VAT Setting'] = {
+	onload () {
+		frappe.breadcrumbs.add('Accounts');
+	}
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
new file mode 100644
index 0000000..7207901
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSetting(unittest.TestCase):
+	pass
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 9493614..0e41280 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -112,7 +112,11 @@
 		frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label))
 
 def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
-	return [_("Item"), _("Taxable Amount")] + tax_accounts
+	hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
+	if frappe.get_meta(item_doctype).has_field('gst_hsn_code') and hsn_wise_in_gst_settings:
+		return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
+	else:
+		return [_("Item"), _("Taxable Amount")] + tax_accounts
 
 def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
 	itemised_tax = get_itemised_tax(doc.taxes, with_tax_account=account_wise)
@@ -122,14 +126,17 @@
 	if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
 		return itemised_tax, itemised_taxable_amount
 
-	if hsn_wise:
+	hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
+
+	tax_breakup_hsn_wise = hsn_wise or hsn_wise_in_gst_settings
+	if tax_breakup_hsn_wise:
 		item_hsn_map = frappe._dict()
 		for d in doc.items:
 			item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
 
 	hsn_tax = {}
 	for item, taxes in itemised_tax.items():
-		item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
+		item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
 		hsn_tax.setdefault(item_or_hsn, frappe._dict())
 		for tax_desc, tax_detail in taxes.items():
 			key = tax_desc
@@ -142,7 +149,7 @@
 	# set taxable amount
 	hsn_taxable_amount = frappe._dict()
 	for item in itemised_taxable_amount:
-		item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
+		item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
 		hsn_taxable_amount.setdefault(item_or_hsn, 0)
 		hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)
 
diff --git a/erpnext/regional/report/ksa_vat/__init__.py b/erpnext/regional/report/ksa_vat/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/__init__.py
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.js b/erpnext/regional/report/ksa_vat/ksa_vat.js
new file mode 100644
index 0000000..d46d260
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.js
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, Havenir Solutions and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["KSA VAT"] = {
+	onload() {
+		frappe.breadcrumbs.add('Accounts');
+	},
+	"filters": [
+		{
+			"fieldname": "company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"reqd": 1,
+			"default": frappe.defaults.get_user_default("Company")
+		},
+		{
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.get_today()
+		}
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		if (data
+			&& (data.title=='VAT on Sales' || data.title=='VAT on Purchases')
+			&& data.title==value) {
+			value = $(`<span>${value}</span>`);
+			var $value = $(value).css("font-weight", "bold");
+			value = $value.wrap("<p></p>").parent().html();
+			return value
+		}else if (data.title=='Grand Total'){
+			if (data.title==value) {
+				value = $(`<span>${value}</span>`);
+				var $value = $(value).css("font-weight", "bold");
+				value = $value.wrap("<p></p>").parent().html();
+				return value
+			}else{
+				value = default_formatter(value, row, column, data);
+				value = $(`<span>${value}</span>`);
+				var $value = $(value).css("font-weight", "bold");
+				value = $value.wrap("<p></p>").parent().html();
+				console.log($value)
+				return value
+			}
+		}else{
+			value = default_formatter(value, row, column, data);
+			return value;
+		}
+	},
+};
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.json b/erpnext/regional/report/ksa_vat/ksa_vat.json
new file mode 100644
index 0000000..036e260
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-07-13 08:54:38.000949",
+ "disable_prepared_report": 1,
+ "disabled": 1,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-08-26 04:14:37.202594",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT",
+ "owner": "Administrator",
+ "prepared_report": 1,
+ "ref_doctype": "GL Entry",
+ "report_name": "KSA VAT",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Accounts Manager"
+  },
+  {
+   "role": "Accounts User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py
new file mode 100644
index 0000000..a42ebc9
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2013, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import json
+
+import frappe
+from frappe import _
+from frappe.utils import get_url_to_list
+
+
+def execute(filters=None):
+	columns = columns = get_columns()
+	data = get_data(filters)
+	return columns, data
+
+def get_columns():
+	return [
+		{
+			"fieldname": "title",
+			"label": _("Title"),
+			"fieldtype": "Data",
+			"width": 300
+		},
+		{
+			"fieldname": "amount",
+			"label": _("Amount (SAR)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		},
+		{
+			"fieldname": "adjustment_amount",
+			"label": _("Adjustment (SAR)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		},
+		{
+			"fieldname": "vat_amount",
+			"label": _("VAT Amount (SAR)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		}
+	]
+
+def get_data(filters):
+	data = []
+
+	# Validate if vat settings exist
+	company = filters.get('company')
+	if frappe.db.exists('KSA VAT Setting', company) is None:
+		url = get_url_to_list('KSA VAT Setting')
+		frappe.msgprint(_('Create <a href="{}">KSA VAT Setting</a> for this company').format(url))
+		return data
+
+	ksa_vat_setting = frappe.get_doc('KSA VAT Setting', company)
+
+	# Sales Heading
+	append_data(data, 'VAT on Sales', '', '', '')
+
+	grand_total_taxable_amount = 0
+	grand_total_taxable_adjustment_amount = 0
+	grand_total_tax = 0
+
+	for vat_setting in ksa_vat_setting.ksa_vat_sales_accounts:
+		total_taxable_amount, total_taxable_adjustment_amount, \
+			total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Sales Invoice')
+
+		# Adding results to data
+		append_data(data, vat_setting.title, total_taxable_amount,
+			total_taxable_adjustment_amount, total_tax)
+
+		grand_total_taxable_amount += total_taxable_amount
+		grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
+		grand_total_tax += total_tax
+
+	# Sales Grand Total
+	append_data(data, 'Grand Total', grand_total_taxable_amount,
+		grand_total_taxable_adjustment_amount, grand_total_tax)
+
+	# Blank Line
+	append_data(data, '', '', '', '')
+
+	# Purchase Heading
+	append_data(data, 'VAT on Purchases', '', '', '')
+
+	grand_total_taxable_amount = 0
+	grand_total_taxable_adjustment_amount = 0
+	grand_total_tax = 0
+
+	for vat_setting in ksa_vat_setting.ksa_vat_purchase_accounts:
+		total_taxable_amount, total_taxable_adjustment_amount, \
+			total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Purchase Invoice')
+
+		# Adding results to data
+		append_data(data, vat_setting.title, total_taxable_amount,
+			total_taxable_adjustment_amount, total_tax)
+
+		grand_total_taxable_amount += total_taxable_amount
+		grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
+		grand_total_tax += total_tax
+
+	# Purchase Grand Total
+	append_data(data, 'Grand Total', grand_total_taxable_amount,
+		grand_total_taxable_adjustment_amount, grand_total_tax)
+
+	return data
+
+def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype):
+	'''
+	(KSA, {filters}, 'Sales Invoice') => 500, 153, 10 \n
+	calculates and returns \n
+	total_taxable_amount, total_taxable_adjustment_amount, total_tax'''
+	from_date = filters.get('from_date')
+	to_date = filters.get('to_date')
+
+	# Initiate variables
+	total_taxable_amount = 0
+	total_taxable_adjustment_amount = 0
+	total_tax = 0
+	# Fetch All Invoices
+	invoices = frappe.get_list(doctype,
+	filters ={
+		'docstatus': 1,
+		'posting_date': ['between', [from_date, to_date]]
+	}, fields =['name', 'is_return'])
+
+	for invoice in invoices:
+		invoice_items = frappe.get_list(f'{doctype} Item',
+		filters ={
+			'docstatus': 1,
+			'parent': invoice.name,
+			'item_tax_template': vat_setting.item_tax_template
+		}, fields =['item_code', 'net_amount'])
+
+		for item in invoice_items:
+			# Summing up total taxable amount
+			if invoice.is_return == 0:
+				total_taxable_amount += item.net_amount
+
+			if invoice.is_return == 1:
+				total_taxable_adjustment_amount += item.net_amount
+
+			# Summing up total tax
+			total_tax += get_tax_amount(item.item_code, vat_setting.account, doctype, invoice.name)
+
+	return total_taxable_amount, total_taxable_adjustment_amount, total_tax
+
+
+
+def append_data(data, title, amount, adjustment_amount, vat_amount):
+	"""Returns data with appended value."""
+	data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount})
+
+def get_tax_amount(item_code, account_head, doctype, parent):
+	if doctype == 'Sales Invoice':
+		tax_doctype = 'Sales Taxes and Charges'
+
+	elif doctype == 'Purchase Invoice':
+		tax_doctype = 'Purchase Taxes and Charges'
+
+	item_wise_tax_detail = frappe.get_value(tax_doctype, {
+		'docstatus': 1,
+		'parent': parent,
+		'account_head': account_head
+	}, 'item_wise_tax_detail')
+
+	tax_amount = 0
+	if item_wise_tax_detail and len(item_wise_tax_detail) > 0:
+		item_wise_tax_detail = json.loads(item_wise_tax_detail)
+		for key, value in item_wise_tax_detail.items():
+			if key == item_code:
+				tax_amount = value[1]
+				break
+
+	return tax_amount
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index 9b3677d..6113f48 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -2,10 +2,36 @@
 # License: GNU General Public License v3. See license.txt
 
 from __future__ import unicode_literals
-
-from erpnext.regional.united_arab_emirates.setup import make_custom_fields, add_print_formats
-
+import frappe
+from frappe.permissions import add_permission, update_permission_property
+from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields, add_print_formats
+from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
 
 def setup(company=None, patch=True):
-	make_custom_fields()
+	uae_custom_fields()
 	add_print_formats()
+	add_permissions()
+	create_ksa_vat_setting(company)
+	make_qrcode_field()
+
+def add_permissions():
+	"""Add Permissions for KSA VAT Setting."""
+	add_permission('KSA VAT Setting', 'All', 0)
+	for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+		add_permission('KSA VAT Setting', role, 0)
+		update_permission_property('KSA VAT Setting', role, 0, 'write', 1)
+		update_permission_property('KSA VAT Setting', role, 0, 'create', 1)
+
+	"""Enable KSA VAT Report"""
+	frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0)
+
+def make_qrcode_field():
+	"""Created QR code Image file"""
+	qr_code_field = dict(
+		fieldname='qr_code',
+		label='QR Code',
+		fieldtype='Attach Image',
+		read_only=1, no_copy=1, hidden=1)
+
+	create_custom_field('Sales Invoice', qr_code_field)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
new file mode 100644
index 0000000..cc6c0af
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -0,0 +1,77 @@
+import io
+import os
+
+import frappe
+from pyqrcode import create as qr_create
+
+from erpnext import get_region
+
+
+def create_qr_code(doc, method):
+	"""Create QR Code after inserting Sales Inv
+	"""
+
+	region = get_region(doc.company)
+	if region not in ['Saudi Arabia']:
+		return
+
+	# if QR Code field not present, do nothing
+	if not hasattr(doc, 'qr_code'):
+		return
+
+	# Don't create QR Code if it already exists
+	qr_code = doc.get("qr_code")
+	if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
+		return
+
+	meta = frappe.get_meta('Sales Invoice')
+
+	for field in meta.get_image_fields():
+		if field.fieldname == 'qr_code':
+			# Creating public url to print format
+			default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=doc.doctype), "value")
+
+			# System Language
+			language = frappe.get_system_settings('language')
+
+			# creating qr code for the url
+			url = f"{ frappe.utils.get_url() }/{ doc.doctype }/{ doc.name }?format={ default_print_format or 'Standard' }&_lang={ language }&key={ doc.get_signature() }"
+			qr_image = io.BytesIO()
+			url = qr_create(url, error='L')
+			url.png(qr_image, scale=2, quiet_zone=1)
+
+			# making file
+			filename = f"QR-CODE-{doc.name}.png".replace(os.path.sep, "__")
+			_file = frappe.get_doc({
+				"doctype": "File",
+				"file_name": filename,
+				"is_private": 0,
+				"content": qr_image.getvalue(),
+				"attached_to_doctype": doc.get("doctype"),
+				"attached_to_name": doc.get("name"),
+				"attached_to_field": "qr_code"
+			})
+
+			_file.save()
+
+			# assigning to document
+			doc.db_set('qr_code', _file.file_url)
+			doc.notify_update()
+
+			break
+
+
+def delete_qr_code_file(doc, method):
+	"""Delete QR Code on deleted sales invoice"""
+
+	region = get_region(doc.company)
+	if region not in ['Saudi Arabia']:
+		return
+
+	if hasattr(doc, 'qr_code'):
+		if doc.get('qr_code'):
+			file_doc = frappe.get_list('File', {
+				'file_url': doc.get('qr_code')
+			})
+			if len(file_doc):
+				frappe.delete_doc('File', file_doc[0].name)
\ No newline at end of file
diff --git a/erpnext/regional/saudi_arabia/wizard/__init__.py b/erpnext/regional/saudi_arabia/wizard/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/data/__init__.py b/erpnext/regional/saudi_arabia/wizard/data/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/data/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
new file mode 100644
index 0000000..709d65b
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
@@ -0,0 +1,47 @@
+[
+    {
+        "type": "Sales Account",
+        "accounts": [
+            {
+                "title": "Standard rated Sales",
+                "item_tax_template": "KSA VAT 5%",
+                "account": "VAT 5%"
+            },
+            {
+                "title": "Zero rated domestic sales",
+                "item_tax_template": "KSA VAT Zero",
+                "account": "VAT Zero"
+            },
+            {
+                "title": "Exempted sales",
+                "item_tax_template": "KSA VAT Exempted",
+                "account": "VAT Zero"
+            }
+        ]
+    },
+    {
+        "type": "Purchase Account",
+        "accounts": [
+            {
+                "title": "Standard rated domestic purchases",
+                "item_tax_template": "KSA VAT 5%",
+                "account": "VAT 5%"
+            },
+            {
+                "title": "Imports subject to VAT paid at customs",
+                "item_tax_template": "KSA Excise 50%",
+                "account": "Excise 50%"
+            },
+            {
+                "title": "Zero rated purchases",
+                "item_tax_template": "KSA VAT Zero",
+                "account": "VAT Zero"
+            },
+            {
+                "title": "Exempted purchases",
+                "item_tax_template": "KSA VAT Exempted",
+                "account": "VAT Zero"
+            }
+        ]
+    }
+]
\ No newline at end of file
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/__init__.py b/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
new file mode 100644
index 0000000..3c89edd
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
@@ -0,0 +1,46 @@
+import json
+import os
+
+import frappe
+
+from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
+
+
+def create_ksa_vat_setting(company):
+    """On creation of first company. Creates KSA VAT Setting"""
+
+    company = frappe.get_doc('Company', company)
+    setup_taxes_and_charges(company.name, company.country)
+
+    file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json')
+    with open(file_path, 'r') as json_file:
+        account_data = json.load(json_file)
+
+    # Creating KSA VAT Setting
+    ksa_vat_setting = frappe.get_doc({
+        'doctype': 'KSA VAT Setting',
+        'company': company.name
+    })
+
+    for data in account_data:
+        if data['type'] == 'Sales Account':
+            for row in data['accounts']:
+                item_tax_template = row['item_tax_template']
+                account = row['account']
+                ksa_vat_setting.append('ksa_vat_sales_accounts', {
+                    'title': row['title'],
+                    'item_tax_template': f'{item_tax_template} - {company.abbr}',
+                    'account': f'{account} - {company.abbr}'
+                })
+
+        elif data['type'] == 'Purchase Account':
+            for row in data['accounts']:
+                item_tax_template = row['item_tax_template']
+                account = row['account']
+                ksa_vat_setting.append('ksa_vat_purchase_accounts', {
+                    'title': row['title'],
+                    'item_tax_template': f'{item_tax_template} - {company.abbr}',
+                    'account': f'{account} - {company.abbr}'
+                })
+
+    ksa_vat_setting.save()
diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py
index 9c183af..cf78f92 100644
--- a/erpnext/regional/united_states/setup.py
+++ b/erpnext/regional/united_states/setup.py
@@ -14,30 +14,9 @@
 		setup_company_independent_fixtures(patch=patch)
 
 def setup_company_independent_fixtures(company=None, patch=True):
-	add_product_tax_categories()
 	make_custom_fields()
-	add_permissions()
-	frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
 	add_print_formats()
 
-# Product Tax categories imported from taxjar api
-def add_product_tax_categories():
-	with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
-		tax_categories = json.loads(f.read())
-	create_tax_categories(tax_categories['categories'])
-
-def create_tax_categories(data):
-	for d in data:
-		tax_category = frappe.new_doc('Product Tax Category')
-		tax_category.description = d.get("description")
-		tax_category.product_tax_code = d.get("product_tax_code")
-		tax_category.category_name = d.get("name")
-		try:
-			tax_category.db_insert()
-		except frappe.DuplicateEntryError:
-			pass
-
-
 def make_custom_fields(update=True):
 	custom_fields = {
 		'Supplier': [
@@ -59,29 +38,10 @@
 		'Quotation': [
 			dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
 				label='Is customer exempted from sales tax?')
-		],
-		'Sales Invoice Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
-				label='Product Tax Category', fetch_from='item_code.product_tax_category'),
-			dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
-				label='Tax Collectable', read_only=1),
-			dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
-				label='Taxable Amount', read_only=1)
-		],
-		'Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
-				label='Product Tax Category')
 		]
 	}
 	create_custom_fields(custom_fields, update=update)
 
-def add_permissions():
-	doctype = "Product Tax Category"
-	for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
-		add_permission(doctype, role, 0)
-		update_permission_property(doctype, role, 0, 'write', 1)
-		update_permission_property(doctype, role, 0, 'create', 1)
-
 def add_print_formats():
 	frappe.reload_doc("regional", "print_format", "irs_1099_form")
 	frappe.db.set_value("Print Format", "IRS 1099 Form", "disabled", 0)
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index bbfe7c0..222e74e 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1382,7 +1382,6 @@
 
 	frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
 	frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
-	frappe.db.commit()
 	frappe.cache().hdel('roles', frappe.session.user)
 
 	workflow = frappe.get_doc({
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
index f8a22b0..1164a5d 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
@@ -44,7 +44,6 @@
 
 	def test_tax_rule_validation(self):
 		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
-		frappe.db.commit()
 
 		cart_settings = self.get_cart_settings()
 		cart_settings.enabled = 1
diff --git a/erpnext/shopping_cart/utils.py b/erpnext/shopping_cart/utils.py
index f412e61..5f0c792 100644
--- a/erpnext/shopping_cart/utils.py
+++ b/erpnext/shopping_cart/utils.py
@@ -1,8 +1,5 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
 import frappe
 
 from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
@@ -18,10 +15,19 @@
 	return False
 
 def set_cart_count(login_manager):
-	role, parties = check_customer_or_supplier()
-	if role == 'Supplier': return
+	# since this is run only on hooks login event
+	# make sure user is already a customer
+	# before trying to set cart count
+	user_is_customer = is_customer()
+	if not user_is_customer:
+		return
+
 	if show_cart_count():
 		from erpnext.shopping_cart.cart import set_cart_count
+
+		# set_cart_count will try to fetch existing cart quotation
+		# or create one if non existent (and create a customer too)
+		# cart count is calculated from this quotation's items
 		set_cart_count()
 
 def clear_cart_count(login_manager):
@@ -32,13 +38,13 @@
 	cart_enabled = is_cart_enabled()
 	context["shopping_cart_enabled"] = cart_enabled
 
-def check_customer_or_supplier():
-	if frappe.session.user:
+def is_customer():
+	if frappe.session.user and frappe.session.user != "Guest":
 		contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
 		if contact_name:
 			contact = frappe.get_doc('Contact', contact_name)
 			for link in contact.links:
-				if link.link_doctype in ('Customer', 'Supplier'):
-					return link.link_doctype, link.link_name
+				if link.link_doctype == 'Customer':
+					return True
 
-		return 'Customer', None
+		return False
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 7998930..0a663c2 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -1,8 +1,5 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-
-import unittest
 
 import frappe
 from frappe.exceptions import ValidationError
@@ -11,9 +8,10 @@
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
 from erpnext.stock.get_item_details import get_item_details
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestBatch(unittest.TestCase):
+class TestBatch(ERPNextTestCase):
 	def test_item_has_batch_enabled(self):
 		self.assertRaises(ValidationError, frappe.get_doc({
 			"doctype": "Batch",
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 5fbc2d8..4be0415 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -14,51 +14,6 @@
 			self.stock_uom = frappe.get_cached_value('Item', self.item_code, 'stock_uom')
 		self.set_projected_qty()
 
-	def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
-		'''Called from erpnext.stock.utils.update_bin'''
-		self.update_qty(args)
-
-		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
-			from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
-
-			if not args.get("posting_date"):
-				args["posting_date"] = nowdate()
-
-			if args.get("is_cancelled") and via_landed_cost_voucher:
-				return
-
-			# Reposts only current voucher SL Entries
-			# Updates valuation rate, stock value, stock queue for current transaction
-			update_entries_after({
-				"item_code": self.item_code,
-				"warehouse": self.warehouse,
-				"posting_date": args.get("posting_date"),
-				"posting_time": args.get("posting_time"),
-				"voucher_type": args.get("voucher_type"),
-				"voucher_no": args.get("voucher_no"),
-				"sle_id": args.name,
-				"creation": args.creation
-			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
-
-			# update qty in future ale and Validate negative qty
-			update_qty_in_future_sle(args, allow_negative_stock)
-
-
-	def update_qty(self, args):
-		# update the stock values (for current quantities)
-		if args.get("voucher_type")=="Stock Reconciliation":
-			self.actual_qty = args.get("qty_after_transaction")
-		else:
-			self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
-
-		self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
-		self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
-		self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
-		self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-
-		self.set_projected_qty()
-		self.db_update()
-
 	def set_projected_qty(self):
 		self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
 			+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
@@ -143,3 +98,67 @@
 
 def on_doctype_update():
 	frappe.db.add_index("Bin", ["item_code", "warehouse"])
+
+
+def update_stock(bin_name, args, allow_negative_stock=False, via_landed_cost_voucher=False):
+	'''Called from erpnext.stock.utils.update_bin'''
+	update_qty(bin_name, args)
+
+	if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
+		from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
+
+		if not args.get("posting_date"):
+			args["posting_date"] = nowdate()
+
+		if args.get("is_cancelled") and via_landed_cost_voucher:
+			return
+
+		# Reposts only current voucher SL Entries
+		# Updates valuation rate, stock value, stock queue for current transaction
+		update_entries_after({
+			"item_code": args.get('item_code'),
+			"warehouse": args.get('warehouse'),
+			"posting_date": args.get("posting_date"),
+			"posting_time": args.get("posting_time"),
+			"voucher_type": args.get("voucher_type"),
+			"voucher_no": args.get("voucher_no"),
+			"sle_id": args.get('name'),
+			"creation": args.get('creation')
+		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
+
+		# update qty in future sle and Validate negative qty
+		update_qty_in_future_sle(args, allow_negative_stock)
+
+def get_bin_details(bin_name):
+	return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty',
+	'reserved_qty', 'indented_qty', 'planned_qty', 'reserved_qty_for_production',
+	'reserved_qty_for_sub_contract'], as_dict=1)
+
+def update_qty(bin_name, args):
+	bin_details = get_bin_details(bin_name)
+
+	# update the stock values (for current quantities)
+	if args.get("voucher_type")=="Stock Reconciliation":
+		actual_qty = args.get('qty_after_transaction')
+	else:
+		actual_qty = bin_details.actual_qty + flt(args.get("actual_qty"))
+
+	ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty"))
+	reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty"))
+	indented_qty = flt(bin_details.indented_qty) + flt(args.get("indented_qty"))
+	planned_qty = flt(bin_details.planned_qty) + flt(args.get("planned_qty"))
+
+
+	# compute projected qty
+	projected_qty = (flt(actual_qty) + flt(ordered_qty)
+		+ flt(indented_qty) + flt(planned_qty) - flt(reserved_qty)
+		- flt(bin_details.reserved_qty_for_production) - flt(bin_details.reserved_qty_for_sub_contract))
+
+	frappe.db.set_value('Bin', bin_name, {
+		'actual_qty': actual_qty,
+		'ordered_qty': ordered_qty,
+		'reserved_qty': reserved_qty,
+		'indented_qty': indented_qty,
+		'planned_qty': planned_qty,
+		'projected_qty': projected_qty
+	})
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 7fda94b..f58b586 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -5,7 +5,6 @@
 from __future__ import unicode_literals
 
 import json
-import unittest
 
 import frappe
 from frappe.utils import cstr, flt, nowdate, nowtime
@@ -37,9 +36,10 @@
 )
 from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestDeliveryNote(unittest.TestCase):
+class TestDeliveryNote(ERPNextTestCase):
 	def test_over_billing_against_dn(self):
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index c9081c9..c6ff73e 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -14,11 +14,12 @@
 	make_expense_claim,
 	notify_customers,
 )
-from erpnext.tests.utils import create_test_contact_and_address
+from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
 
 
-class TestDeliveryTrip(unittest.TestCase):
+class TestDeliveryTrip(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		driver = create_driver()
 		create_vehicle()
 		create_delivery_notification()
@@ -32,6 +33,7 @@
 		frappe.db.sql("delete from `tabVehicle`")
 		frappe.db.sql("delete from `tabEmail Template`")
 		frappe.db.sql("delete from `tabDelivery Trip`")
+		return super().tearDown()
 
 	def test_expense_claim_fields_are_fetched_properly(self):
 		expense_claim = make_expense_claim(self.delivery_trip.name)
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index e911d35..9198272 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -4,7 +4,6 @@
 from __future__ import unicode_literals
 
 import json
-import unittest
 
 import frappe
 from frappe.test_runner import make_test_objects
@@ -25,7 +24,7 @@
 )
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.get_item_details import get_item_details
-from erpnext.tests.utils import change_settings
+from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 test_ignore = ["BOM"]
 test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -53,8 +52,9 @@
 
 	return item
 
-class TestItem(unittest.TestCase):
+class TestItem(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		frappe.flags.attribute_values = None
 
 	def get_item(self, idx):
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index 2be8ef7..af6cc47 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -4,7 +4,6 @@
 from __future__ import unicode_literals
 
 import json
-import unittest
 
 import frappe
 from frappe.utils import flt
@@ -21,10 +20,12 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemAlternative(unittest.TestCase):
+class TestItemAlternative(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		make_items()
 
 	def test_alternative_item_for_subcontract_rm(self):
diff --git a/erpnext/stock/doctype/item_attribute/test_item_attribute.py b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
index fc809f4..2cd711b 100644
--- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
@@ -3,17 +3,17 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 test_records = frappe.get_test_records('Item Attribute')
 
 from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemAttribute(unittest.TestCase):
+class TestItemAttribute(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		if frappe.db.exists("Item Attribute", "_Test_Length"):
 			frappe.delete_doc("Item Attribute", "_Test_Length")
 
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 5ed8092..3a51fbb 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -3,17 +3,17 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.test_runner import make_test_records_for_doctype
 
 from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
 from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemPrice(unittest.TestCase):
+class TestItemPrice(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		frappe.db.sql("delete from `tabItem Price`")
 		make_test_records_for_doctype("Item Price", force=True)
 
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 58a72f7..339eaaa 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -4,8 +4,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.utils import flt
 
@@ -16,9 +14,10 @@
 	get_gl_entries,
 	make_purchase_receipt,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestLandedCostVoucher(unittest.TestCase):
+class TestLandedCostVoucher(ERPNextTestCase):
 	def test_landed_cost_voucher(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 5c2ac25..f66a228 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -6,8 +6,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.utils import flt, today
 
@@ -18,9 +16,10 @@
 	make_supplier_quotation,
 	raise_work_orders,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestMaterialRequest(unittest.TestCase):
+class TestMaterialRequest(ERPNextTestCase):
 	def test_make_purchase_order(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
diff --git a/erpnext/stock/doctype/packing_slip/test_packing_slip.py b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
index 193adfc..c70cba6 100644
--- a/erpnext/stock/doctype/packing_slip/test_packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
@@ -6,6 +6,8 @@
 import unittest
 
 # test_records = frappe.get_test_records('Packing Slip')
+from erpnext.tests.utils import ERPNextTestCase
+
 
 class TestPackingSlip(unittest.TestCase):
 	pass
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index aa710ad..fd0b368 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -3,8 +3,6 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
@@ -15,9 +13,10 @@
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
 	EmptyStockReconciliationItemsError,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPickList(unittest.TestCase):
+class TestPickList(ERPNextTestCase):
 
 	def test_pick_list_picks_warehouse_for_each_item(self):
 		try:
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 044856c..de17744 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -17,9 +17,10 @@
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPurchaseReceipt(unittest.TestCase):
+class TestPurchaseReceipt(ERPNextTestCase):
 	def setUp(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
index 0aa7610..c25bca9 100644
--- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -3,8 +3,6 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 from erpnext.stock.doctype.batch.test_batch import make_new_batch
@@ -13,9 +11,10 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.get_item_details import get_conversion_factor
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPutawayRule(unittest.TestCase):
+class TestPutawayRule(ERPNextTestCase):
 	def setUp(self):
 		if not frappe.db.exists("Item", "_Rice"):
 			make_item("_Rice", {
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index f5d076a..308c628 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -1,8 +1,6 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
 # See license.txt
 
-import unittest
-
 import frappe
 from frappe.utils import nowdate
 
@@ -15,12 +13,14 @@
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.tests.utils import ERPNextTestCase
 
 # test_records = frappe.get_test_records('Quality Inspection')
 
 
-class TestQualityInspection(unittest.TestCase):
+class TestQualityInspection(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		create_item("_Test Item with QA")
 		frappe.db.set_value(
 			"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index 818c163..546e21b 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -6,8 +6,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
@@ -20,9 +18,10 @@
 test_records = frappe.get_test_records('Serial No')
 
 from erpnext.stock.doctype.serial_no.serial_no import *
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestSerialNo(unittest.TestCase):
+class TestSerialNo(ERPNextTestCase):
 	def test_cannot_create_direct(self):
 		frappe.delete_doc_if_exists("Serial No", "_TCSER0001")
 
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index 9914cf8..dcd0b7c 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -3,15 +3,15 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
 from datetime import date, timedelta
 
 import frappe
 
 from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestShipment(unittest.TestCase):
+class TestShipment(ERPNextTestCase):
 	def test_shipment_from_delivery_note(self):
 		delivery_note = create_test_delivery_note()
 		delivery_note.submit()
@@ -47,7 +47,6 @@
 		}
 	)
 	delivery_note.insert()
-	frappe.db.commit()
 	return delivery_note
 
 
@@ -91,7 +90,6 @@
 		}
 	)
 	shipment.insert()
-	frappe.db.commit()
 	return shipment
 
 
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index 40ae340..2651407 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -317,7 +317,7 @@
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-10-08 12:42:51.857631",
+ "modified": "2021-10-08 13:42:51.857631",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Ledger Entry",
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 382fdfa..2cf71ac 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -181,3 +181,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", ["warehouse", "item_code"], "item_warehouse")
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 61bae49..ff33c27 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -3,8 +3,6 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.core.page.permission_manager.permission_manager import reset
 from frappe.utils import add_days, today
@@ -21,9 +19,10 @@
 	create_stock_reconciliation,
 )
 from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockLedgerEntry(unittest.TestCase):
+class TestStockLedgerEntry(ERPNextTestCase):
 	def setUp(self):
 		items = create_items()
 		reset('Stock Entry')
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 8647bee..415ac5e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -6,8 +6,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.utils import add_days, flt, nowdate, nowtime, random_string
 
@@ -22,12 +20,13 @@
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
-from erpnext.tests.utils import change_settings
+from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 
-class TestStockReconciliation(unittest.TestCase):
+class TestStockReconciliation(ERPNextTestCase):
 	@classmethod
 	def setUpClass(self):
+		super().setUpClass()
 		create_batch_or_serial_no_items()
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
@@ -372,7 +371,6 @@
 		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 		from erpnext.stock.stock_ledger import NegativeStockError
-		frappe.db.commit()
 
 		item_code = "Backdated-Reco-Cancellation-Item"
 		warehouse = "_Test Warehouse - _TC"
@@ -395,10 +393,6 @@
 		repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
 		self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
 
-		# teardown
-		frappe.db.rollback()
-
-
 	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")
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.py b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
index 7e80904..bf8ac5d 100644
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
@@ -7,9 +7,12 @@
 
 import frappe
 
+from erpnext.tests.utils import ERPNextTestCase
 
-class TestStockSettings(unittest.TestCase):
+
+class TestStockSettings(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
 
 	def test_settings(self):
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 1ca7181..98317ec 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -2,8 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.test_runner import make_test_records
 from frappe.utils import cint
@@ -12,11 +10,13 @@
 from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.tests.utils import ERPNextTestCase
 
 test_records = frappe.get_test_records('Warehouse')
 
-class TestWarehouse(unittest.TestCase):
+class TestWarehouse(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		if not frappe.get_value('Item', '_Test Item'):
 			make_test_records('Item')
 
diff --git a/erpnext/stock/form_tour/material_request/material_request.json b/erpnext/stock/form_tour/material_request/material_request.json
new file mode 100644
index 0000000..145b4a0
--- /dev/null
+++ b/erpnext/stock/form_tour/material_request/material_request.json
@@ -0,0 +1,97 @@
+{
+ "creation": "2021-07-29 12:32:08.929900",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:11:13.119453",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Material Request",
+ "owner": "Administrator",
+ "reference_doctype": "Material Request",
+ "save_on_complete": 1,
+ "steps": [
+  {
+   "description": "The purpose of the material request can be selected here. For now select \"Purchase\" as the purpose.",
+   "field": "",
+   "fieldname": "material_request_type",
+   "fieldtype": "Select",
+   "has_next_condition": 1,
+   "is_table_field": 0,
+   "label": "Purpose",
+   "next_step_condition": "eval: doc.material_request_type == \"Purchase\"",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purpose"
+  },
+  {
+   "description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
+   "field": "",
+   "fieldname": "schedule_date",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Required By",
+   "next_step_condition": "",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Required By"
+  },
+  {
+   "description": "Setting the target warehouse sets it for all the items.",
+   "field": "",
+   "fieldname": "set_warehouse",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Set Target Warehouse",
+   "next_step_condition": "",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Target Warehouse"
+  },
+  {
+   "description": "Items table",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Items"
+  },
+  {
+   "child_doctype": "Material Request Item",
+   "description": "Select an Item code. Item details will be fetched automatically.",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 1,
+   "label": "Item Code",
+   "next_step_condition": "eval: doc.item_code",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Item Code"
+  },
+  {
+   "child_doctype": "Material Request Item",
+   "description": "Enter the required quantity for the material.",
+   "field": "",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Quantity",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Bottom",
+   "title": "Quantity"
+  }
+ ],
+ "title": "Material Request"
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
index 21e1205..32df585 100644
--- a/erpnext/stock/report/stock_analytics/test_stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -5,9 +5,10 @@
 
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockAnalyticsReport(unittest.TestCase):
+class TestStockAnalyticsReport(ERPNextTestCase):
 	def test_get_period_date_ranges(self):
 
 		filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 1b5b792..e9d5b6a 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -13,8 +13,8 @@
 
 import erpnext
 from erpnext.stock.utils import (
-	get_bin,
 	get_incoming_outgoing_rate_for_cancel,
+	get_or_make_bin,
 	get_valuation_method,
 )
 
@@ -805,14 +805,13 @@
 	def update_bin(self):
 		# update bin for each warehouse
 		for warehouse, data in iteritems(self.data):
-			bin_doc = get_bin(self.item_code, warehouse)
-			bin_doc.update({
+			bin_record = get_or_make_bin(self.item_code, warehouse)
+
+			frappe.db.set_value('Bin', bin_record, {
 				"valuation_rate": data.valuation_rate,
 				"actual_qty": data.qty_after_transaction,
 				"stock_value": data.stock_value
 			})
-			bin_doc.flags.via_stock_ledger_entry = True
-			bin_doc.save(ignore_permissions=True)
 
 
 def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
@@ -918,7 +917,7 @@
 		company = erpnext.get_default_company()
 
 	last_valuation_rate = frappe.db.sql("""select valuation_rate
-		from `tabStock Ledger Entry`
+		from `tabStock Ledger Entry` force index (item_warehouse)
 		where
 			item_code = %s
 			AND warehouse = %s
@@ -929,7 +928,7 @@
 	if not last_valuation_rate:
 		# Get valuation rate from last sle for the item against any warehouse
 		last_valuation_rate = frappe.db.sql("""select valuation_rate
-			from `tabStock Ledger Entry`
+			from `tabStock Ledger Entry` force index (item_code)
 			where
 				item_code = %s
 				AND valuation_rate > 0
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index aeb06e9..c4a0497 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -180,12 +180,27 @@
 	bin_obj.flags.ignore_permissions = True
 	return bin_obj
 
+def get_or_make_bin(item_code, warehouse) -> str:
+	bin_record = frappe.db.get_value('Bin', {'item_code': item_code, 'warehouse': warehouse})
+
+	if not bin_record:
+		bin_obj = frappe.get_doc({
+			"doctype": "Bin",
+			"item_code": item_code,
+			"warehouse": warehouse,
+		})
+		bin_obj.flags.ignore_permissions = 1
+		bin_obj.insert()
+		bin_record = bin_obj.name
+
+	return bin_record
+
 def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
+	from erpnext.stock.doctype.bin.bin import update_stock
 	is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
 	if is_stock_item:
-		bin = get_bin(args.get("item_code"), args.get("warehouse"))
-		bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
-		return bin
+		bin_record = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
+		update_stock(bin_record, args, allow_negative_stock, via_landed_cost_voucher)
 	else:
 		frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
 
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index a3cab4b..91df548 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -2,6 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import copy
+import unittest
 from contextlib import contextmanager
 from typing import Any, Dict, NewType, Optional
 
@@ -12,6 +13,21 @@
 ReportName = NewType("ReportName", str)
 
 
+class ERPNextTestCase(unittest.TestCase):
+	"""A sane default test class for ERPNext tests."""
+
+
+	@classmethod
+	def setUpClass(cls) -> None:
+		frappe.db.commit()
+		return super().setUpClass()
+
+	@classmethod
+	def tearDownClass(cls) -> None:
+		frappe.db.rollback()
+		return super().tearDownClass()
+
+
 def create_test_contact_and_address():
 	frappe.db.sql('delete from tabContact')
 	frappe.db.sql('delete from `tabContact Email`')