Merge branch 'develop' into subscription_fix_new
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 5a5c448..199a183 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -109,7 +109,7 @@
 	'''
 	if company or frappe.flags.company:
 		return frappe.get_cached_value('Company',
-			company or frappe.flags.company,  'country')
+			company or frappe.flags.company, 'country')
 	elif frappe.flags.country:
 		return frappe.flags.country
 	else:
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index dd26c4c..1bd42f5 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -43,11 +43,11 @@
 		if frappe.flags.in_test:
 			make_dimension_in_accounting_doctypes(doc=self)
 		else:
-			frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self)
+			frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long')
 
 	def on_trash(self):
 		if frappe.flags.in_test:
-			delete_accounting_dimension(doc=self)
+			delete_accounting_dimension(doc=self, queue='long')
 		else:
 			frappe.enqueue(delete_accounting_dimension, doc=self)
 
@@ -58,6 +58,9 @@
 		if not self.fieldname:
 			self.fieldname = scrub(self.label)
 
+	def on_update(self):
+		frappe.flags.accounting_dimensions = None
+
 def make_dimension_in_accounting_doctypes(doc):
 	doclist = get_doctypes_with_dimensions()
 	doc_count = len(get_accounting_dimensions())
@@ -186,12 +189,14 @@
 	return doclist
 
 def get_accounting_dimensions(as_list=True):
-	accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"])
+	if frappe.flags.accounting_dimensions is None:
+		frappe.flags.accounting_dimensions = frappe.get_all("Accounting Dimension",
+			fields=["label", "fieldname", "disabled", "document_type"])
 
 	if as_list:
-		return [d.fieldname for d in accounting_dimensions]
+		return [d.fieldname for d in frappe.flags.accounting_dimensions]
 	else:
-		return accounting_dimensions
+		return frappe.flags.accounting_dimensions
 
 def get_checks_for_pl_and_bs_accounts():
 	dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json
index b42f1f9..de67ab1 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.json
+++ b/erpnext/accounts/doctype/bank_account/bank_account.json
@@ -86,6 +86,7 @@
   },
   {
    "default": "0",
+   "description": "Setting the account as a Company Account is necessary for Bank Reconciliation",
    "fieldname": "is_company_account",
    "fieldtype": "Check",
    "label": "Is Company Account"
@@ -207,7 +208,7 @@
   }
  ],
  "links": [],
- "modified": "2020-07-17 13:59:50.795412",
+ "modified": "2020-10-23 16:48:06.303658",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Account",
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/accounts/doctype/bank_reconciliation_tool/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/accounts/doctype/bank_reconciliation_tool/__init__.py
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
new file mode 100644
index 0000000..297dd43
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
@@ -0,0 +1,162 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+frappe.ui.form.on("Bank Reconciliation Tool", {
+	setup: function (frm) {
+		frm.set_query("bank_account", function () {
+			return {
+				filters: {
+					company: ["in", frm.doc.company],
+				},
+			};
+		});
+	},
+
+	refresh: function (frm) {
+		frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
+			frm.trigger("make_reconciliation_tool")
+		);
+		frm.upload_statement_button = frm.page.set_secondary_action(
+			__("Upload Bank Statement"),
+			() =>
+				frappe.call({
+					method:
+						"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
+					args: {
+						dt: frm.doc.doctype,
+						dn: frm.doc.name,
+						company: frm.doc.company,
+						bank_account: frm.doc.bank_account,
+					},
+					callback: function (r) {
+						if (!r.exc) {
+							var doc = frappe.model.sync(r.message);
+							frappe.set_route(
+								"Form",
+								doc[0].doctype,
+								doc[0].name
+							);
+						}
+					},
+				})
+		);
+	},
+
+	after_save: function (frm) {
+		frm.trigger("make_reconciliation_tool");
+	},
+
+	bank_account: function (frm) {
+		frappe.db.get_value(
+			"Bank Account",
+			frm.bank_account,
+			"account",
+			(r) => {
+				frappe.db.get_value(
+					"Account",
+					r.account,
+					"account_currency",
+					(r) => {
+						frm.currency = r.account_currency;
+					}
+				);
+			}
+		);
+		frm.trigger("get_account_opening_balance");
+	},
+
+	bank_statement_from_date: function (frm) {
+		frm.trigger("get_account_opening_balance");
+	},
+
+	make_reconciliation_tool(frm) {
+		frm.get_field("reconciliation_tool_cards").$wrapper.empty();
+		if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
+			frm.trigger("get_cleared_balance").then(() => {
+				if (
+					frm.doc.bank_account &&
+					frm.doc.bank_statement_from_date &&
+					frm.doc.bank_statement_to_date &&
+					frm.doc.bank_statement_closing_balance
+				) {
+					frm.trigger("render_chart");
+					frm.trigger("render");
+					frappe.utils.scroll_to(
+						frm.get_field("reconciliation_tool_cards").$wrapper,
+						true,
+						30
+					);
+				}
+			});
+		}
+	},
+
+	get_account_opening_balance(frm) {
+		if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
+			frappe.call({
+				method:
+					"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+				args: {
+					bank_account: frm.doc.bank_account,
+					till_date: frm.doc.bank_statement_from_date,
+				},
+				callback: (response) => {
+					frm.set_value("account_opening_balance", response.message);
+				},
+			});
+		}
+	},
+
+	get_cleared_balance(frm) {
+		if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
+			return frappe.call({
+				method:
+					"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+				args: {
+					bank_account: frm.doc.bank_account,
+					till_date: frm.doc.bank_statement_to_date,
+				},
+				callback: (response) => {
+					frm.cleared_balance = response.message;
+				},
+			});
+		}
+	},
+
+	render_chart(frm) {
+		frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
+			{
+				$reconciliation_tool_cards: frm.get_field(
+					"reconciliation_tool_cards"
+				).$wrapper,
+				bank_statement_closing_balance:
+					frm.doc.bank_statement_closing_balance,
+				cleared_balance: frm.cleared_balance,
+				currency: frm.currency,
+			}
+		);
+	},
+
+	render(frm) {
+		if (frm.doc.bank_account) {
+			frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager(
+				{
+					company: frm.doc.company,
+					bank_account: frm.doc.bank_account,
+					$reconciliation_tool_dt: frm.get_field(
+						"reconciliation_tool_dt"
+					).$wrapper,
+					$no_bank_transactions: frm.get_field(
+						"no_bank_transactions"
+					).$wrapper,
+					bank_statement_from_date: frm.doc.bank_statement_from_date,
+					bank_statement_to_date: frm.doc.bank_statement_to_date,
+					bank_statement_closing_balance:
+						frm.doc.bank_statement_closing_balance,
+					cards_manager: frm.cards_manager,
+				}
+			);
+		}
+	},
+});
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
new file mode 100644
index 0000000..4837db3
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
@@ -0,0 +1,113 @@
+{
+ "actions": [],
+ "creation": "2020-12-02 10:13:02.148040",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "bank_account",
+  "column_break_1",
+  "bank_statement_from_date",
+  "bank_statement_to_date",
+  "column_break_2",
+  "account_opening_balance",
+  "bank_statement_closing_balance",
+  "section_break_1",
+  "reconciliation_tool_cards",
+  "reconciliation_tool_dt",
+  "no_bank_transactions"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "fieldname": "bank_account",
+   "fieldtype": "Link",
+   "label": "Bank Account",
+   "options": "Bank Account"
+  },
+  {
+   "fieldname": "column_break_1",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval: doc.bank_account",
+   "fieldname": "bank_statement_from_date",
+   "fieldtype": "Date",
+   "label": "Bank Statement From Date"
+  },
+  {
+   "depends_on": "eval: doc.bank_statement_from_date",
+   "fieldname": "bank_statement_to_date",
+   "fieldtype": "Date",
+   "label": "Bank Statement To Date"
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval: doc.bank_statement_from_date",
+   "fieldname": "account_opening_balance",
+   "fieldtype": "Currency",
+   "label": "Account Opening Balance",
+   "options": "Currency",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.bank_statement_to_date",
+   "fieldname": "bank_statement_closing_balance",
+   "fieldtype": "Currency",
+   "label": "Bank Statement Closing Balance",
+   "options": "Currency"
+  },
+  {
+   "depends_on": "eval: doc.bank_statement_closing_balance",
+   "fieldname": "section_break_1",
+   "fieldtype": "Section Break",
+   "label": "Reconcile"
+  },
+  {
+   "fieldname": "reconciliation_tool_cards",
+   "fieldtype": "HTML"
+  },
+  {
+   "fieldname": "reconciliation_tool_dt",
+   "fieldtype": "HTML"
+  },
+  {
+   "fieldname": "no_bank_transactions",
+   "fieldtype": "HTML",
+   "options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
+  }
+ ],
+ "hide_toolbar": 1,
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-02-02 01:35:53.043578",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Reconciliation Tool",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
new file mode 100644
index 0000000..8a17233
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import json
+
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import flt
+
+from erpnext import get_company_currency
+from erpnext.accounts.utils import get_balance_on
+from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import get_entries, get_amounts_not_reflected_in_system
+from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
+
+
+class BankReconciliationTool(Document):
+	pass
+
+@frappe.whitelist()
+def get_bank_transactions(bank_account, from_date = None, to_date = None):
+	# returns bank transactions for a bank account
+	filters = []
+	filters.append(['bank_account', '=', bank_account])
+	filters.append(['docstatus', '=', 1])
+	filters.append(['unallocated_amount', '>', 0])
+	if to_date:
+		filters.append(['date', '<=', to_date])
+	if from_date:
+		filters.append(['date', '>=', from_date])
+	transactions = frappe.get_all(
+		'Bank Transaction',
+		fields = ['date', 'deposit', 'withdrawal', 'currency',
+		'description', 'name', 'bank_account', 'company',
+		'unallocated_amount', 'reference_number', 'party_type', 'party'],
+		filters = filters
+	)
+	return transactions
+
+@frappe.whitelist()
+def get_account_balance(bank_account, till_date):
+	# returns account balance till the specified date
+	account = frappe.db.get_value('Bank Account', bank_account, 'account')
+	filters = frappe._dict({
+		"account": account,
+		"report_date": till_date,
+		"include_pos_transactions": 1
+	})
+	data = get_entries(filters)
+
+	balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
+
+	total_debit, total_credit = 0,0
+	for d in data:
+		total_debit += flt(d.debit)
+		total_credit += flt(d.credit)
+
+	amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
+
+	bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+		+ amounts_not_reflected_in_system
+
+	return bank_bal
+
+
+@frappe.whitelist()
+def update_bank_transaction(bank_transaction_name, reference_number, party_type=None, party=None):
+	# updates bank transaction based on the new parameters provided by the user from Vouchers
+	bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+	bank_transaction.reference_number = reference_number
+	bank_transaction.party_type = party_type
+	bank_transaction.party = party
+	bank_transaction.save()
+	return frappe.db.get_all('Bank Transaction',
+		filters={
+			'name': bank_transaction_name
+		},
+		fields=['date', 'deposit', 'withdrawal', 'currency',
+			'description', 'name', 'bank_account', 'company',
+			'unallocated_amount', 'reference_number',
+			 'party_type', 'party'],
+	)[0]
+
+
+@frappe.whitelist()
+def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None,
+	second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None):
+	# Create a new journal entry based on the bank transaction
+	bank_transaction = frappe.db.get_values(
+		"Bank Transaction", bank_transaction_name,
+		fieldname=["name", "deposit", "withdrawal", "bank_account"] ,
+		as_dict=True
+	)[0]
+	company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
+	account_type = frappe.db.get_value("Account", second_account, "account_type")
+	if account_type in ["Receivable", "Payable"]:
+		if not (party_type and party):
+			frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account))
+	accounts = []
+	# Multi Currency?
+	accounts.append({
+			"account": second_account,
+			"credit_in_account_currency": bank_transaction.deposit
+				if  bank_transaction.deposit > 0
+				else 0,
+			"debit_in_account_currency":bank_transaction.withdrawal
+				if  bank_transaction.withdrawal > 0
+				else 0,
+			"party_type":party_type,
+			"party":party,
+		})
+
+	accounts.append({
+			"account": company_account,
+			"bank_account": bank_transaction.bank_account,
+			"credit_in_account_currency": bank_transaction.withdrawal
+				if  bank_transaction.withdrawal > 0
+				else 0,
+			"debit_in_account_currency":bank_transaction.deposit
+				if  bank_transaction.deposit > 0
+				else 0,
+		})
+
+	company = frappe.get_value("Account", company_account, "company")
+
+	journal_entry_dict = {
+		"voucher_type" : entry_type,
+		"company" : company,
+		"posting_date" : posting_date,
+		"cheque_date" : reference_date,
+		"cheque_no" : reference_number,
+		"mode_of_payment" : mode_of_payment
+	}
+	journal_entry = frappe.new_doc('Journal Entry')
+	journal_entry.update(journal_entry_dict)
+	journal_entry.set("accounts", accounts)
+
+
+	if allow_edit:
+		return journal_entry
+
+	journal_entry.insert()
+	journal_entry.submit()
+
+	if bank_transaction.deposit > 0:
+		paid_amount = bank_transaction.deposit
+	else:
+		paid_amount = bank_transaction.withdrawal
+
+	vouchers = json.dumps([{
+		"payment_doctype":"Journal Entry",
+		"payment_name":journal_entry.name,
+		"amount":paid_amount}])
+
+	return reconcile_vouchers(bank_transaction.name, vouchers)
+
+@frappe.whitelist()
+def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None,
+	mode_of_payment=None, project=None, cost_center=None, allow_edit=None):
+	# Create a new payment entry based on the bank transaction
+	bank_transaction = frappe.db.get_values(
+		"Bank Transaction", bank_transaction_name,
+		fieldname=["name", "unallocated_amount", "deposit", "bank_account"] ,
+		as_dict=True
+	)[0]
+	paid_amount = bank_transaction.unallocated_amount
+	payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
+
+	company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
+	company = frappe.get_value("Account", company_account, "company")
+	payment_entry_dict = {
+		"company" : company,
+		"payment_type" : payment_type,
+		"reference_no" :  reference_number,
+		"reference_date" :  reference_date,
+		"party_type" :  party_type,
+		"party" :  party,
+		"posting_date" :  posting_date,
+		"paid_amount": paid_amount,
+		"received_amount": paid_amount
+	}
+	payment_entry = frappe.new_doc("Payment Entry")
+
+
+	payment_entry.update(payment_entry_dict)
+
+	if mode_of_payment:
+		payment_entry.mode_of_payment =  mode_of_payment
+	if project:
+		payment_entry.project =  project
+	if cost_center:
+		payment_entry.cost_center =  cost_center
+	if payment_type == "Receive":
+		payment_entry.paid_to = company_account
+	else:
+		payment_entry.paid_from = company_account
+
+	payment_entry.validate()
+
+	if allow_edit:
+		return payment_entry
+
+	payment_entry.insert()
+
+	payment_entry.submit()
+	vouchers = json.dumps([{
+		"payment_doctype":"Payment Entry",
+		"payment_name":payment_entry.name,
+		"amount":paid_amount}])
+	return reconcile_vouchers(bank_transaction.name, vouchers)
+
+@frappe.whitelist()
+def reconcile_vouchers(bank_transaction_name, vouchers):
+	# updated clear date of all the vouchers based on the bank transaction
+	vouchers = json.loads(vouchers)
+	transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+	if transaction.unallocated_amount == 0:
+		frappe.throw(_("This bank transaction is already fully reconciled"))
+	total_amount = 0
+	for voucher in vouchers:
+		voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name'])
+		total_amount += get_paid_amount(frappe._dict({
+			'payment_document': voucher['payment_doctype'],
+			'payment_entry': voucher['payment_name'],
+		}), transaction.currency)
+
+	if total_amount > transaction.unallocated_amount:
+		frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
+	account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
+
+	for voucher in vouchers:
+		gl_entry = frappe.db.get_value("GL Entry", dict(account=account, voucher_type=voucher['payment_doctype'], voucher_no=voucher['payment_name']), ['credit', 'debit'], as_dict=1)
+		gl_amount, transaction_amount = (gl_entry.credit, transaction.deposit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.withdrawal)
+		allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
+
+		transaction.append("payment_entries", {
+			"payment_document": voucher['payment_entry'].doctype,
+			"payment_entry": voucher['payment_entry'].name,
+			"allocated_amount": allocated_amount
+		})
+
+	transaction.save()
+	transaction.update_allocations()
+	return frappe.get_doc("Bank Transaction", bank_transaction_name)
+
+@frappe.whitelist()
+def get_linked_payments(bank_transaction_name, document_types = None):
+	# get all matching payments for a bank transaction
+	transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
+	bank_account = frappe.db.get_values(
+		"Bank Account",
+		transaction.bank_account,
+		["account", "company"],
+		as_dict=True)[0]
+	(account, company) = (bank_account.account, bank_account.company)
+	matching = check_matching(account, company, transaction, document_types)
+	return matching
+
+def check_matching(bank_account, company, transaction, document_types):
+	# combine all types of vocuhers
+	subquery = get_queries(bank_account, company, transaction, document_types)
+	filters = {
+			"amount": transaction.unallocated_amount,
+			"payment_type" : "Receive" if transaction.deposit > 0 else "Pay",
+			"reference_no": transaction.reference_number,
+			"party_type": transaction.party_type,
+			"party": transaction.party,
+			"bank_account":  bank_account
+		}
+
+	matching_vouchers = []
+	for query in subquery:
+		matching_vouchers.extend(
+			frappe.db.sql(query, filters,)
+		)
+
+	return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else []
+
+def get_queries(bank_account, company, transaction, document_types):
+	# get queries to get matching vouchers
+	amount_condition = "=" if "exact_match" in document_types else "<="
+	account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
+	queries = []
+
+	if "payment_entry" in document_types:
+		pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
+		queries.extend([pe_amount_matching])
+
+	if "journal_entry" in document_types:
+		je_amount_matching = get_je_matching_query(amount_condition, transaction)
+		queries.extend([je_amount_matching])
+
+	if transaction.deposit > 0 and "sales_invoice" in document_types:
+		si_amount_matching =  get_si_matching_query(amount_condition)
+		queries.extend([si_amount_matching])
+
+	if transaction.withdrawal > 0:
+		if "purchase_invoice" in document_types:
+			pi_amount_matching = get_pi_matching_query(amount_condition)
+			queries.extend([pi_amount_matching])
+
+		if "expense_claim" in document_types:
+			ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition)
+			queries.extend([ec_amount_matching])
+
+	return queries
+
+def get_pe_matching_query(amount_condition, account_from_to, transaction):
+	# get matching payment entries query
+	if transaction.deposit > 0:
+		currency_field = "paid_to_account_currency as currency"
+	else:
+		currency_field = "paid_from_account_currency as currency"
+	return  f"""
+	SELECT
+		(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+		+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0  END
+		+ 1 ) AS rank,
+		'Payment Entry' as doctype,
+		name,
+		paid_amount,
+		reference_no,
+		reference_date,
+		party,
+		party_type,
+		posting_date,
+		{currency_field}
+	FROM
+		`tabPayment Entry`
+	WHERE
+		paid_amount {amount_condition} %(amount)s
+		AND docstatus = 1
+		AND payment_type IN (%(payment_type)s, 'Internal Transfer')
+		AND ifnull(clearance_date, '') = ""
+		AND {account_from_to} = %(bank_account)s
+	"""
+
+
+def get_je_matching_query(amount_condition, transaction):
+	# get matching journal entry query
+	cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
+	return f"""
+
+		SELECT
+			(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
+			+ 1) AS rank ,
+			'Journal Entry' as doctype,
+			je.name,
+			jea.{cr_or_dr}_in_account_currency as paid_amount,
+			je.cheque_no as reference_no,
+			je.cheque_date as reference_date,
+			je.pay_to_recd_from as party,
+			jea.party_type,
+			je.posting_date,
+			jea.account_currency as currency
+		FROM
+			`tabJournal Entry Account` as jea
+		JOIN
+			`tabJournal Entry` as je
+		ON
+			jea.parent = je.name
+		WHERE
+			(je.clearance_date is null or je.clearance_date='0000-00-00')
+			AND jea.account = %(bank_account)s
+			AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
+			AND je.docstatus = 1
+	"""
+
+
+def get_si_matching_query(amount_condition):
+	# get matchin sales invoice query
+	return f"""
+		SELECT
+			( CASE WHEN si.customer = %(party)s  THEN 1 ELSE 0  END
+			+ 1 ) AS rank,
+			'Sales Invoice' as doctype,
+			si.name,
+			sip.amount as paid_amount,
+			'' as reference_no,
+			'' as reference_date,
+			si.customer as party,
+			'Customer' as party_type,
+			si.posting_date,
+			si.currency
+
+		FROM
+			`tabSales Invoice Payment` as sip
+		JOIN
+			`tabSales Invoice` as si
+		ON
+			sip.parent = si.name
+		WHERE (sip.clearance_date is null or sip.clearance_date='0000-00-00')
+			AND sip.account = %(bank_account)s
+			AND sip.amount {amount_condition} %(amount)s
+			AND si.docstatus = 1
+	"""
+
+def get_pi_matching_query(amount_condition):
+	# get matching purchase invoice query
+	return f"""
+		SELECT
+			( CASE WHEN supplier = %(party)s THEN 1 ELSE 0 END
+			+ 1 ) AS rank,
+			'Purchase Invoice' as doctype,
+			name,
+			paid_amount,
+			'' as reference_no,
+			'' as reference_date,
+			supplier as party,
+			'Supplier' as party_type,
+			posting_date,
+			currency
+		FROM
+			`tabPurchase Invoice`
+		WHERE
+			paid_amount {amount_condition} %(amount)s
+			AND docstatus = 1
+			AND is_paid = 1
+			AND ifnull(clearance_date, '') = ""
+			AND cash_bank_account  = %(bank_account)s
+	"""
+
+def get_ec_matching_query(bank_account, company, amount_condition):
+	# get matching Expense Claim query
+	mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
+			filters={"default_account": bank_account}, fields=["parent"])]
+	mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
+	company_currency = get_company_currency(company)
+	return f"""
+		SELECT
+			( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END
+			+ 1 ) AS rank,
+			'Expense Claim' as doctype,
+			name,
+			total_sanctioned_amount as paid_amount,
+			'' as reference_no,
+			'' as reference_date,
+			employee as party,
+			'Employee' as party_type,
+			posting_date,
+			'{company_currency}' as currency
+		FROM
+			`tabExpense Claim`
+		WHERE
+			total_sanctioned_amount {amount_condition} %(amount)s
+			AND docstatus = 1
+			AND is_paid = 1
+			AND ifnull(clearance_date, '') = ""
+			AND mode_of_payment in {mode_of_payments}
+	"""
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
new file mode 100644
index 0000000..d96950a
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestBankReconciliationTool(unittest.TestCase):
+	pass
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/accounts/doctype/bank_statement_import/__init__.py
similarity index 100%
rename from erpnext/accounts/doctype/bank_statement_settings/__init__.py
rename to erpnext/accounts/doctype/bank_statement_import/__init__.py
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css
new file mode 100644
index 0000000..5206540
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.css
@@ -0,0 +1,3 @@
+.warnings .warning {
+	margin-bottom: 40px;
+}
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
new file mode 100644
index 0000000..ad4ff9e
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -0,0 +1,574 @@
+// Copyright (c) 2019, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Bank Statement Import", {
+	setup(frm) {
+		frappe.realtime.on("data_import_refresh", ({ data_import }) => {
+			frm.import_in_progress = false;
+			if (data_import !== frm.doc.name) return;
+			frappe.model.clear_doc("Bank Statement Import", frm.doc.name);
+			frappe.model
+				.with_doc("Bank Statement Import", frm.doc.name)
+				.then(() => {
+					frm.refresh();
+				});
+		});
+		frappe.realtime.on("data_import_progress", (data) => {
+			frm.import_in_progress = true;
+			if (data.data_import !== frm.doc.name) {
+				return;
+			}
+			let percent = Math.floor((data.current * 100) / data.total);
+			let seconds = Math.floor(data.eta);
+			let minutes = Math.floor(data.eta / 60);
+			let eta_message =
+				// prettier-ignore
+				seconds < 60
+					? __('About {0} seconds remaining', [seconds])
+					: minutes === 1
+						? __('About {0} minute remaining', [minutes])
+						: __('About {0} minutes remaining', [minutes]);
+
+			let message;
+			if (data.success) {
+				let message_args = [data.current, data.total, eta_message];
+				message =
+					frm.doc.import_type === "Insert New Records"
+						? __("Importing {0} of {1}, {2}", message_args)
+						: __("Updating {0} of {1}, {2}", message_args);
+			}
+			if (data.skipping) {
+				message = __(
+					"Skipping {0} of {1}, {2}",
+					[
+						data.current,
+						data.total,
+						eta_message,
+					]
+				);
+			}
+			frm.dashboard.show_progress(
+				__("Import Progress"),
+				percent,
+				message
+			);
+			frm.page.set_indicator(__("In Progress"), "orange");
+
+			// hide progress when complete
+			if (data.current === data.total) {
+				setTimeout(() => {
+					frm.dashboard.hide();
+					frm.refresh();
+				}, 2000);
+			}
+		});
+
+		frm.set_query("reference_doctype", () => {
+			return {
+				filters: {
+					name: ["in", frappe.boot.user.can_import],
+				},
+			};
+		});
+
+		frm.get_field("import_file").df.options = {
+			restrictions: {
+				allowed_file_types: [".csv", ".xls", ".xlsx"],
+			},
+		};
+
+		frm.has_import_file = () => {
+			return frm.doc.import_file || frm.doc.google_sheets_url;
+		};
+	},
+
+	refresh(frm) {
+		frm.page.hide_icon_group();
+		frm.trigger("update_indicators");
+		frm.trigger("import_file");
+		frm.trigger("show_import_log");
+		frm.trigger("show_import_warnings");
+		frm.trigger("toggle_submit_after_import");
+		frm.trigger("show_import_status");
+		frm.trigger("show_report_error_button");
+
+		if (frm.doc.status === "Partial Success") {
+			frm.add_custom_button(__("Export Errored Rows"), () =>
+				frm.trigger("export_errored_rows")
+			);
+		}
+
+		if (frm.doc.status.includes("Success")) {
+			frm.add_custom_button(
+				__("Go to {0} List", [frm.doc.reference_doctype]),
+				() => frappe.set_route("List", frm.doc.reference_doctype)
+			);
+		}
+	},
+
+	onload_post_render(frm) {
+		frm.trigger("update_primary_action");
+	},
+
+	update_primary_action(frm) {
+		if (frm.is_dirty()) {
+			frm.enable_save();
+			return;
+		}
+		frm.disable_save();
+		if (frm.doc.status !== "Success") {
+			if (!frm.is_new() && frm.has_import_file()) {
+				let label =
+					frm.doc.status === "Pending"
+						? __("Start Import")
+						: __("Retry");
+				frm.page.set_primary_action(label, () =>
+					frm.events.start_import(frm)
+				);
+			} else {
+				frm.page.set_primary_action(__("Save"), () => frm.save());
+			}
+		}
+	},
+
+	update_indicators(frm) {
+		const indicator = frappe.get_indicator(frm.doc);
+		if (indicator) {
+			frm.page.set_indicator(indicator[0], indicator[1]);
+		} else {
+			frm.page.clear_indicator();
+		}
+	},
+
+	show_import_status(frm) {
+		let import_log = JSON.parse(frm.doc.import_log || "[]");
+		let successful_records = import_log.filter((log) => log.success);
+		let failed_records = import_log.filter((log) => !log.success);
+		if (successful_records.length === 0) return;
+
+		let message;
+		if (failed_records.length === 0) {
+			let message_args = [successful_records.length];
+			if (frm.doc.import_type === "Insert New Records") {
+				message =
+					successful_records.length > 1
+						? __("Successfully imported {0} records.", message_args)
+						: __("Successfully imported {0} record.", message_args);
+			} else {
+				message =
+					successful_records.length > 1
+						? __("Successfully updated {0} records.", message_args)
+						: __("Successfully updated {0} record.", message_args);
+			}
+		} else {
+			let message_args = [successful_records.length, import_log.length];
+			if (frm.doc.import_type === "Insert New Records") {
+				message =
+					successful_records.length > 1
+						? __(
+							"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+							message_args
+						)
+						: __(
+							"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+							message_args
+						);
+			} else {
+				message =
+					successful_records.length > 1
+						? __(
+							"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+							message_args
+						)
+						: __(
+							"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
+							message_args
+						);
+			}
+		}
+		frm.dashboard.set_headline(message);
+	},
+
+	show_report_error_button(frm) {
+		if (frm.doc.status === "Error") {
+			frappe.db
+				.get_list("Error Log", {
+					filters: { method: frm.doc.name },
+					fields: ["method", "error"],
+					order_by: "creation desc",
+					limit: 1,
+				})
+				.then((result) => {
+					if (result.length > 0) {
+						frm.add_custom_button("Report Error", () => {
+							let fake_xhr = {
+								responseText: JSON.stringify({
+									exc: result[0].error,
+								}),
+							};
+							frappe.request.report_error(fake_xhr, {});
+						});
+					}
+				});
+		}
+	},
+
+	start_import(frm) {
+		frm.call({
+			method: "form_start_import",
+			args: { data_import: frm.doc.name },
+			btn: frm.page.btn_primary,
+		}).then((r) => {
+			if (r.message === true) {
+				frm.disable_save();
+			}
+		});
+	},
+
+	download_template() {
+		let method =
+			"/api/method/frappe.core.doctype.data_import.data_import.download_template";
+
+		open_url_post(method, {
+			doctype: "Bank Transaction",
+			export_records: "5_records",
+			export_fields: {
+				"Bank Transaction": [
+					"date",
+					"deposit",
+					"withdrawal",
+					"description",
+					"reference_number",
+				],
+			},
+		});
+	},
+
+	reference_doctype(frm) {
+		frm.trigger("toggle_submit_after_import");
+	},
+
+	toggle_submit_after_import(frm) {
+		frm.toggle_display("submit_after_import", false);
+		let doctype = frm.doc.reference_doctype;
+		if (doctype) {
+			frappe.model.with_doctype(doctype, () => {
+				let meta = frappe.get_meta(doctype);
+				frm.toggle_display("submit_after_import", meta.is_submittable);
+			});
+		}
+	},
+
+	google_sheets_url(frm) {
+		if (!frm.is_dirty()) {
+			frm.trigger("import_file");
+		} else {
+			frm.trigger("update_primary_action");
+		}
+	},
+
+	refresh_google_sheet(frm) {
+		frm.trigger("import_file");
+	},
+
+	import_file(frm) {
+		frm.toggle_display("section_import_preview", frm.has_import_file());
+		if (!frm.has_import_file()) {
+			frm.get_field("import_preview").$wrapper.empty();
+			return;
+		} else {
+			frm.trigger("update_primary_action");
+		}
+
+		// load import preview
+		frm.get_field("import_preview").$wrapper.empty();
+		$('<span class="text-muted">')
+			.html(__("Loading import file..."))
+			.appendTo(frm.get_field("import_preview").$wrapper);
+
+		frm.call({
+			method: "get_preview_from_template",
+			args: {
+				data_import: frm.doc.name,
+				import_file: frm.doc.import_file,
+				google_sheets_url: frm.doc.google_sheets_url,
+			},
+			error_handlers: {
+				TimestampMismatchError() {
+					// ignore this error
+				},
+			},
+		}).then((r) => {
+			let preview_data = r.message;
+			frm.events.show_import_preview(frm, preview_data);
+			frm.events.show_import_warnings(frm, preview_data);
+		});
+	},
+	// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
+
+	show_import_preview(frm, preview_data) {
+		let import_log = JSON.parse(frm.doc.import_log || "[]");
+
+		if (
+			frm.import_preview &&
+			frm.import_preview.doctype === frm.doc.reference_doctype
+		) {
+			frm.import_preview.preview_data = preview_data;
+			frm.import_preview.import_log = import_log;
+			frm.import_preview.refresh();
+			return;
+		}
+
+		frappe.require("/assets/js/data_import_tools.min.js", () => {
+			frm.import_preview = new frappe.data_import.ImportPreview({
+				wrapper: frm.get_field("import_preview").$wrapper,
+				doctype: frm.doc.reference_doctype,
+				preview_data,
+				import_log,
+				frm,
+				events: {
+					remap_column(changed_map) {
+						let template_options = JSON.parse(
+							frm.doc.template_options || "{}"
+						);
+						template_options.column_to_field_map =
+							template_options.column_to_field_map || {};
+						Object.assign(
+							template_options.column_to_field_map,
+							changed_map
+						);
+						frm.set_value(
+							"template_options",
+							JSON.stringify(template_options)
+						);
+						frm.save().then(() => frm.trigger("import_file"));
+					},
+				},
+			});
+		});
+	},
+
+	export_errored_rows(frm) {
+		open_url_post(
+			"/api/method/frappe.core.doctype.data_import.data_import.download_errored_template",
+			{
+				data_import_name: frm.doc.name,
+			}
+		);
+	},
+
+	show_import_warnings(frm, preview_data) {
+		let columns = preview_data.columns;
+		let warnings = JSON.parse(frm.doc.template_warnings || "[]");
+		warnings = warnings.concat(preview_data.warnings || []);
+
+		frm.toggle_display("import_warnings_section", warnings.length > 0);
+		if (warnings.length === 0) {
+			frm.get_field("import_warnings").$wrapper.html("");
+			return;
+		}
+
+		// group warnings by row
+		let warnings_by_row = {};
+		let other_warnings = [];
+		for (let warning of warnings) {
+			if (warning.row) {
+				warnings_by_row[warning.row] =
+					warnings_by_row[warning.row] || [];
+				warnings_by_row[warning.row].push(warning);
+			} else {
+				other_warnings.push(warning);
+			}
+		}
+
+		let html = "";
+		html += Object.keys(warnings_by_row)
+			.map((row_number) => {
+				let message = warnings_by_row[row_number]
+					.map((w) => {
+						if (w.field) {
+							let label =
+								w.field.label +
+								(w.field.parent !== frm.doc.reference_doctype
+									? ` (${w.field.parent})`
+									: "");
+							return `<li>${label}: ${w.message}</li>`;
+						}
+						return `<li>${w.message}</li>`;
+					})
+					.join("");
+				return `
+				<div class="warning" data-row="${row_number}">
+					<h5 class="text-uppercase">${__("Row {0}", [row_number])}</h5>
+					<div class="body"><ul>${message}</ul></div>
+				</div>
+			`;
+			})
+			.join("");
+
+		html += other_warnings
+			.map((warning) => {
+				let header = "";
+				if (warning.col) {
+					let column_number = `<span class="text-uppercase">${__(
+						"Column {0}",
+						[warning.col]
+					)}</span>`;
+					let column_header = columns[warning.col].header_title;
+					header = `${column_number} (${column_header})`;
+				}
+				return `
+					<div class="warning" data-col="${warning.col}">
+						<h5>${header}</h5>
+						<div class="body">${warning.message}</div>
+					</div>
+				`;
+			})
+			.join("");
+		frm.get_field("import_warnings").$wrapper.html(`
+			<div class="row">
+				<div class="col-sm-10 warnings">${html}</div>
+			</div>
+		`);
+	},
+
+	show_failed_logs(frm) {
+		frm.trigger("show_import_log");
+	},
+
+	show_import_log(frm) {
+		let import_log = JSON.parse(frm.doc.import_log || "[]");
+		let logs = import_log;
+		frm.toggle_display("import_log", false);
+		frm.toggle_display("import_log_section", logs.length > 0);
+
+		if (logs.length === 0) {
+			frm.get_field("import_log_preview").$wrapper.empty();
+			return;
+		}
+
+		let rows = logs
+			.map((log) => {
+				let html = "";
+				if (log.success) {
+					if (frm.doc.import_type === "Insert New Records") {
+						html = __(
+							"Successfully imported {0}", [
+								`<span class="underline">${frappe.utils.get_form_link(
+									frm.doc.reference_doctype,
+									log.docname,
+									true
+								)}<span>`,
+							]
+						);
+					} else {
+						html = __(
+							"Successfully updated {0}", [
+								`<span class="underline">${frappe.utils.get_form_link(
+									frm.doc.reference_doctype,
+									log.docname,
+									true
+								)}<span>`,
+							]
+						);
+					}
+				} else {
+					let messages = log.messages
+						.map(JSON.parse)
+						.map((m) => {
+							let title = m.title
+								? `<strong>${m.title}</strong>`
+								: "";
+							let message = m.message
+								? `<div>${m.message}</div>`
+								: "";
+							return title + message;
+						})
+						.join("");
+					let id = frappe.dom.get_unique_id();
+					html = `${messages}
+						<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false" aria-controls="${id}" style="margin-top: 15px;">
+							${__("Show Traceback")}
+						</button>
+						<div class="collapse" id="${id}" style="margin-top: 15px;">
+							<div class="well">
+								<pre>${log.exception}</pre>
+							</div>
+						</div>`;
+				}
+				let indicator_color = log.success ? "green" : "red";
+				let title = log.success ? __("Success") : __("Failure");
+
+				if (frm.doc.show_failed_logs && log.success) {
+					return "";
+				}
+
+				return `<tr>
+					<td>${log.row_indexes.join(", ")}</td>
+					<td>
+						<div class="indicator ${indicator_color}">${title}</div>
+					</td>
+					<td>
+						${html}
+					</td>
+				</tr>`;
+			})
+			.join("");
+
+		if (!rows && frm.doc.show_failed_logs) {
+			rows = `<tr><td class="text-center text-muted" colspan=3>
+				${__("No failed logs")}
+			</td></tr>`;
+		}
+
+		frm.get_field("import_log_preview").$wrapper.html(`
+			<table class="table table-bordered">
+				<tr class="text-muted">
+					<th width="10%">${__("Row Number")}</th>
+					<th width="10%">${__("Status")}</th>
+					<th width="80%">${__("Message")}</th>
+				</tr>
+				${rows}
+			</table>
+		`);
+	},
+
+	show_missing_link_values(frm, missing_link_values) {
+		let can_be_created_automatically = missing_link_values.every(
+			(d) => d.has_one_mandatory_field
+		);
+
+		let html = missing_link_values
+			.map((d) => {
+				let doctype = d.doctype;
+				let values = d.missing_values;
+				return `
+					<h5>${doctype}</h5>
+					<ul>${values.map((v) => `<li>${v}</li>`).join("")}</ul>
+				`;
+			})
+			.join("");
+
+		if (can_be_created_automatically) {
+			// prettier-ignore
+			let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?');
+			frappe.confirm(message + html, () => {
+				frm.call("create_missing_link_values", {
+					missing_link_values,
+				}).then((r) => {
+					let records = r.message;
+					frappe.msgprint(__(
+						"Created {0} records successfully.", [
+							records.length,
+						]
+					));
+				});
+			});
+		} else {
+			frappe.msgprint(
+				// prettier-ignore
+				__('The following records needs to be created before we can import your file.') + html
+			);
+		}
+	},
+});
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
new file mode 100644
index 0000000..5e913cc
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
@@ -0,0 +1,227 @@
+{
+ "actions": [],
+ "autoname": "format:Bank Statement Import on {creation}",
+ "beta": 1,
+ "creation": "2019-08-04 14:16:08.318714",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "bank_account",
+  "bank",
+  "column_break_4",
+  "google_sheets_url",
+  "refresh_google_sheet",
+  "html_5",
+  "import_file",
+  "download_template",
+  "status",
+  "template_options",
+  "import_warnings_section",
+  "template_warnings",
+  "import_warnings",
+  "section_import_preview",
+  "import_preview",
+  "import_log_section",
+  "import_log",
+  "show_failed_logs",
+  "import_log_preview",
+  "reference_doctype",
+  "import_type",
+  "submit_after_import",
+  "mute_emails"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1,
+   "set_only_once": 1
+  },
+  {
+   "fieldname": "bank_account",
+   "fieldtype": "Link",
+   "label": "Bank Account",
+   "options": "Bank Account",
+   "reqd": 1,
+   "set_only_once": 1
+  },
+  {
+   "depends_on": "eval:doc.bank_account",
+   "fetch_from": "bank_account.bank",
+   "fieldname": "bank",
+   "fieldtype": "Link",
+   "label": "Bank",
+   "options": "Bank",
+   "read_only": 1,
+   "set_only_once": 1
+  },
+  {
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "download_template",
+   "fieldtype": "Button",
+   "label": "Download Template"
+  },
+  {
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "import_file",
+   "fieldtype": "Attach",
+   "in_list_view": 1,
+   "label": "Import File"
+  },
+  {
+   "fieldname": "import_preview",
+   "fieldtype": "HTML",
+   "label": "Import Preview"
+  },
+  {
+   "fieldname": "section_import_preview",
+   "fieldtype": "Section Break",
+   "label": "Preview"
+  },
+  {
+   "fieldname": "template_options",
+   "fieldtype": "Code",
+   "hidden": 1,
+   "label": "Template Options",
+   "options": "JSON",
+   "read_only": 1
+  },
+  {
+   "fieldname": "import_log",
+   "fieldtype": "Code",
+   "label": "Import Log",
+   "options": "JSON"
+  },
+  {
+   "fieldname": "import_log_section",
+   "fieldtype": "Section Break",
+   "label": "Import Log"
+  },
+  {
+   "fieldname": "import_log_preview",
+   "fieldtype": "HTML",
+   "label": "Import Log Preview"
+  },
+  {
+   "default": "Pending",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Status",
+   "options": "Pending\nSuccess\nPartial Success\nError",
+   "read_only": 1
+  },
+  {
+   "fieldname": "template_warnings",
+   "fieldtype": "Code",
+   "hidden": 1,
+   "label": "Template Warnings",
+   "options": "JSON"
+  },
+  {
+   "fieldname": "import_warnings_section",
+   "fieldtype": "Section Break",
+   "label": "Import File Errors and Warnings"
+  },
+  {
+   "fieldname": "import_warnings",
+   "fieldtype": "HTML",
+   "label": "Import Warnings"
+  },
+  {
+   "default": "0",
+   "fieldname": "show_failed_logs",
+   "fieldtype": "Check",
+   "label": "Show Failed Logs"
+  },
+  {
+   "depends_on": "eval:!doc.__islocal && !doc.import_file",
+   "fieldname": "html_5",
+   "fieldtype": "HTML",
+   "options": "<h5 class=\"text-muted uppercase\">Or</h5>"
+  },
+  {
+   "depends_on": "eval:!doc.__islocal && !doc.import_file\n",
+   "description": "Must be a publicly accessible Google Sheets URL",
+   "fieldname": "google_sheets_url",
+   "fieldtype": "Data",
+   "label": "Import from Google Sheets"
+  },
+  {
+   "depends_on": "eval:doc.google_sheets_url && !doc.__unsaved",
+   "fieldname": "refresh_google_sheet",
+   "fieldtype": "Button",
+   "label": "Refresh Google Sheet"
+  },
+  {
+   "default": "Bank Transaction",
+   "fieldname": "reference_doctype",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "in_list_view": 1,
+   "label": "Document Type",
+   "options": "DocType",
+   "reqd": 1,
+   "set_only_once": 1
+  },
+  {
+   "default": "Insert New Records",
+   "fieldname": "import_type",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "in_list_view": 1,
+   "label": "Import Type",
+   "options": "\nInsert New Records\nUpdate Existing Records",
+   "reqd": 1,
+   "set_only_once": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "submit_after_import",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Submit After Import",
+   "set_only_once": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "mute_emails",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Don't Send Emails",
+   "set_only_once": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "hide_toolbar": 1,
+ "links": [],
+ "modified": "2021-02-10 19:29:59.027325",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Statement Import",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
new file mode 100644
index 0000000..9f41b13
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import csv
+import json
+import re
+
+import openpyxl
+from openpyxl.styles import Font
+from openpyxl.utils import get_column_letter
+from six import string_types
+
+import frappe
+from frappe.core.doctype.data_import.importer import Importer, ImportFile
+from frappe.utils.background_jobs import enqueue
+from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE
+from frappe import _
+
+from frappe.core.doctype.data_import.data_import import DataImport
+
+class BankStatementImport(DataImport):
+	def __init__(self, *args, **kwargs):
+		super(BankStatementImport, self).__init__(*args, **kwargs)
+
+	def validate(self):
+		doc_before_save = self.get_doc_before_save()
+		if (
+			not (self.import_file or self.google_sheets_url)
+			or (doc_before_save and doc_before_save.import_file != self.import_file)
+			or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
+		):
+
+			template_options_dict = {}
+			column_to_field_map = {}
+			bank = frappe.get_doc("Bank", self.bank)
+			for i in bank.bank_transaction_mapping:
+				column_to_field_map[i.file_field] = i.bank_transaction_field
+			template_options_dict["column_to_field_map"] = column_to_field_map
+			self.template_options = json.dumps(template_options_dict)
+
+			self.template_warnings = ""
+
+		self.validate_import_file()
+		self.validate_google_sheets_url()
+
+	def start_import(self):
+
+		from frappe.core.page.background_jobs.background_jobs import get_info
+		from frappe.utils.scheduler import is_scheduler_inactive
+
+		if is_scheduler_inactive() and not frappe.flags.in_test:
+			frappe.throw(
+				_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")
+			)
+
+		enqueued_jobs = [d.get("job_name") for d in get_info()]
+
+		if self.name not in enqueued_jobs:
+			enqueue(
+				start_import,
+				queue="default",
+				timeout=6000,
+				event="data_import",
+				job_name=self.name,
+				data_import=self.name,
+				bank_account=self.bank_account,
+				import_file_path=self.import_file,
+				bank=self.bank,
+				template_options=self.template_options,
+				now=frappe.conf.developer_mode or frappe.flags.in_test,
+			)
+			return True
+
+		return False
+
+@frappe.whitelist()
+def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
+	return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
+		import_file, google_sheets_url
+	)
+
+@frappe.whitelist()
+def form_start_import(data_import):
+	return frappe.get_doc("Bank Statement Import", data_import).start_import()
+
+@frappe.whitelist()
+def download_errored_template(data_import_name):
+	data_import = frappe.get_doc("Bank Statement Import", data_import_name)
+	data_import.export_errored_rows()
+
+def start_import(data_import, bank_account, import_file_path, bank, template_options):
+	"""This method runs in background job"""
+
+	update_mapping_db(bank, template_options)
+
+	data_import = frappe.get_doc("Bank Statement Import", data_import)
+
+	import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records")
+	data = import_file.raw_data
+
+	add_bank_account(data, bank_account)
+	write_files(import_file, data)
+
+	try:
+		i = Importer(data_import.reference_doctype, data_import=data_import)
+		i.import_data()
+	except Exception:
+		frappe.db.rollback()
+		data_import.db_set("status", "Error")
+		frappe.log_error(title=data_import.name)
+	finally:
+		frappe.flags.in_import = False
+
+	frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name})
+
+def update_mapping_db(bank, template_options):
+	bank = frappe.get_doc("Bank", bank)
+	for d in bank.bank_transaction_mapping:
+		d.delete()
+
+	for d in json.loads(template_options)["column_to_field_map"].items():
+		bank.append("bank_transaction_mapping", {"bank_transaction_field":  d[1] ,"file_field": d[0]} )
+
+	bank.save()
+
+def add_bank_account(data, bank_account):
+	bank_account_loc = None
+	if "Bank Account" not in data[0]:
+		data[0].append("Bank Account")
+	else:
+		for loc, header in enumerate(data[0]):
+			if header == "Bank Account":
+				bank_account_loc = loc
+
+	for row in data[1:]:
+		if bank_account_loc:
+			row[bank_account_loc] = bank_account
+		else:
+			row.append(bank_account)
+
+def write_files(import_file, data):
+	full_file_path = import_file.file_doc.get_full_path()
+	parts = import_file.file_doc.get_extension()
+	extension = parts[1]
+	extension = extension.lstrip(".")
+
+	if extension == "csv":
+		with open(full_file_path, 'w', newline='') as file:
+			writer = csv.writer(file)
+			writer.writerows(data)
+	elif extension == "xlsx" or "xls":
+		write_xlsx(data, "trans", file_path = full_file_path)
+
+def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
+	# from xlsx utils with changes
+	column_widths = column_widths or []
+	if wb is None:
+		wb = openpyxl.Workbook(write_only=True)
+
+	ws = wb.create_sheet(sheet_name, 0)
+
+	for i, column_width in enumerate(column_widths):
+		if column_width:
+			ws.column_dimensions[get_column_letter(i + 1)].width = column_width
+
+	row1 = ws.row_dimensions[1]
+	row1.font = Font(name='Calibri', bold=True)
+
+	for row in data:
+		clean_row = []
+		for item in row:
+			if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']):
+				value = handle_html(item)
+			else:
+				value = item
+
+			if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
+				# Remove illegal characters from the string
+				value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
+
+			clean_row.append(value)
+
+		ws.append(clean_row)
+
+	wb.save(file_path)
+	return True
+
+@frappe.whitelist()
+def upload_bank_statement(**args):
+	args = frappe._dict(args)
+	bsi = frappe.new_doc("Bank Statement Import")
+
+	if args.company:
+		bsi.update({
+			"company": args.company,
+		})
+
+	if args.bank_account:
+		bsi.update({
+			"bank_account": args.bank_account
+		})
+
+	return bsi
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js
new file mode 100644
index 0000000..6c75402
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js
@@ -0,0 +1,36 @@
+let imports_in_progress = [];
+
+frappe.listview_settings['Bank Statement Import'] = {
+	onload(listview) {
+		frappe.realtime.on('data_import_progress', data => {
+			if (!imports_in_progress.includes(data.data_import)) {
+				imports_in_progress.push(data.data_import);
+			}
+		});
+		frappe.realtime.on('data_import_refresh', data => {
+			imports_in_progress = imports_in_progress.filter(
+				d => d !== data.data_import
+			);
+			listview.refresh();
+		});
+	},
+	get_indicator: function(doc) {
+		var colors = {
+			'Pending': 'orange',
+			'Not Started': 'orange',
+			'Partial Success': 'orange',
+			'Success': 'green',
+			'In Progress': 'orange',
+			'Error': 'red'
+		};
+		let status = doc.status;
+		if (imports_in_progress.includes(doc.name)) {
+			status = 'In Progress';
+		}
+		if (status == 'Pending') {
+			status = 'Not Started';
+		}
+		return [__(status), colors[status], 'status,=,' + doc.status];
+	},
+	hide_name_column: true
+};
diff --git a/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
new file mode 100644
index 0000000..cd58314
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement_import/test_bank_statement_import.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestBankStatementImport(unittest.TestCase):
+	pass
diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js
deleted file mode 100644
index 46aa4f2..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2017, sathishpy@gmail.com and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Bank Statement Settings', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json
deleted file mode 100644
index 53fbf7d..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.json
+++ /dev/null
@@ -1,272 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "beta": 0, 
- "creation": "2017-11-13 13:38:10.863592", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bank Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "'%d/%m/%Y'", 
-   "fieldname": "date_format", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Date Format", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "statement_header_mapping", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Statement Header Mapping", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "header_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Statement Headers", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Settings Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "transaction_data_mapping", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Transaction Data Mapping", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "mapped_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Mapped Items", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Transaction Settings Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-04-07 18:57:04.048423", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Bank Statement Settings", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }, 
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py
deleted file mode 100644
index 6c4dd1b..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/bank_statement_settings.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementSettings(Document):
-	def autoname(self):
-		self.name = self.bank + "-Statement-Settings"
diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js
deleted file mode 100644
index f2381c0..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Statement Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Statement Settings
-		() => frappe.tests.make('Bank Statement Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py b/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py
deleted file mode 100644
index aa7fe83..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings/test_bank_statement_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestBankStatementSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json
deleted file mode 100644
index 7c93f26..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.json
+++ /dev/null
@@ -1,101 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-01-08 00:16:42.762980", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "mapped_header", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Mapped Header", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "stmt_header", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bank Header", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-01-08 00:19:14.841134", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Bank Statement Settings Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py b/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py
deleted file mode 100644
index 9438e9a..0000000
--- a/erpnext/accounts/doctype/bank_statement_settings_item/bank_statement_settings_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementSettingsItem(Document):
-	pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js
deleted file mode 100644
index 736ed35..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2017, sathishpy@gmail.com and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Bank Statement Transaction Entry', {
-	setup: function(frm) {
-		frm.events.account_filters(frm)
-		frm.events.invoice_filter(frm)
-	},
-	refresh: function(frm) {
-		frm.set_df_property("bank_account", "read_only", frm.doc.__islocal ? 0 : 1);
-		frm.set_df_property("from_date", "read_only", frm.doc.__islocal ? 0 : 1);
-		frm.set_df_property("to_date", "read_only", frm.doc.__islocal ? 0 : 1);
-	},
-	invoke_doc_function(frm, method) {
-		frappe.call({
-			doc: frm.doc,
-			method: method,
-			callback: function(r) {
-				if(!r.exe) {
-					frm.refresh_fields();
-				}
-			}
-		});
-	},
-	account_filters: function(frm) {
-		frm.fields_dict['bank_account'].get_query = function(doc, dt, dn) {
-			return {
-				filters:[
-					["Account", "account_type", "in", ["Bank"]]
-				]
-			}
-		};
-		frm.fields_dict['receivable_account'].get_query = function(doc, dt, dn) {
-			return {
-				filters: {"account_type": "Receivable"}
-			}
-		};
-		frm.fields_dict['payable_account'].get_query = function(doc, dt, dn) {
-			return {
-				filters: {"account_type": "Payable"}
-			}
-		};
-	},
-
-	invoice_filter: function(frm) {
-		frm.set_query("invoice", "payment_invoice_items", function(doc, cdt, cdn) {
-			let row = locals[cdt][cdn]
-			if (row.party_type == "Customer") {
-				return {
-					filters:[[row.invoice_type, "customer", "in", [row.party]],
-									[row.invoice_type, "status", "!=", "Cancelled" ],
-									[row.invoice_type, "posting_date", "<", row.transaction_date ],
-									[row.invoice_type, "outstanding_amount", ">", 0 ]]
-				}
-			} else if (row.party_type == "Supplier") {
-				return {
-					filters:[[row.invoice_type, "supplier", "in", [row.party]],
-									[row.invoice_type, "status", "!=", "Cancelled" ],
-									[row.invoice_type, "posting_date", "<", row.transaction_date ],
-									[row.invoice_type, "outstanding_amount", ">", 0 ]]
-				}
-			}
-		});
-	},
-
-	match_invoices: function(frm) {
-		frm.events.invoke_doc_function(frm, "populate_matching_invoices");
-	},
-	create_payments: function(frm) {
-		frm.events.invoke_doc_function(frm, "create_payment_entries");
-	},
-	submit_payments: function(frm) {
-		frm.events.invoke_doc_function(frm, "submit_payment_entries");
-	},
-});
-
-
-frappe.ui.form.on('Bank Statement Transaction Invoice Item', {
-	party_type: function(frm, cdt, cdn) {
-		let row = locals[cdt][cdn];
-		if (row.party_type == "Customer") {
-			row.invoice_type = "Sales Invoice";
-		} else if (row.party_type == "Supplier") {
-			row.invoice_type = "Purchase Invoice";
-		} else if (row.party_type == "Account") {
-			row.invoice_type = "Journal Entry";
-		}
-		refresh_field("invoice_type", row.name, "payment_invoice_items");
-
-	},
-	invoice_type: function(frm, cdt, cdn) {
-		let row = locals[cdt][cdn];
-		if (row.invoice_type == "Purchase Invoice") {
-			row.party_type = "Supplier";
-		} else if (row.invoice_type == "Sales Invoice") {
-			row.party_type = "Customer";
-		}
-		refresh_field("party_type", row.name, "payment_invoice_items");
-	}
-});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json
deleted file mode 100644
index fb80169..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.json
+++ /dev/null
@@ -1,792 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "beta": 0, 
- "creation": "2017-11-07 13:48:13.123185", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank_account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bank Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "from_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "From Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "to_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "To Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank_settings", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Bank Statement Settings", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Settings", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Bank", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "receivable_account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Receivable Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "payable_account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Payable Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank_statement", 
-   "fieldtype": "Attach", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Bank Statement", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "section_break_6", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Bank Transaction Entries", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "new_transaction_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "New Transactions", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Transaction Payment Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length", 
-   "fieldname": "section_break_9", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "match_invoices", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Match Transaction to Invoices", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_14", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "create_payments", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Create New Payment/Journal Entry", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_16", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "submit_payments", 
-   "fieldtype": "Button", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Submit/Reconcile Payments", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length", 
-   "fieldname": "section_break_18", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Matching Invoices", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "payment_invoice_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Payment Invoice Items", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Transaction Invoice Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reconciled_transactions", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Reconciled Transactions", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reconciled_transaction_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Reconciled Transactions", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Transaction Payment Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "amended_from", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Amended From", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Bank Statement Transaction Entry", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 1, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-09-14 18:04:44.170455", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Bank Statement Transaction Entry", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
-   "write": 1
-  }, 
-  {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
-   "write": 1
-  }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py
deleted file mode 100644
index 27dd8e4..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py
+++ /dev/null
@@ -1,443 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from erpnext.accounts.utils import get_outstanding_invoices
-from frappe.utils import nowdate
-from datetime import datetime
-import csv, os, re, io
-import difflib
-import copy
-
-class BankStatementTransactionEntry(Document):
-	def autoname(self):
-		self.name = self.bank_account + "-" + self.from_date + "-" + self.to_date
-		if self.bank:
-			mapper_name = self.bank + "-Statement-Settings"
-			if not frappe.db.exists("Bank Statement Settings", mapper_name):
-				self.create_settings(self.bank)
-			self.bank_settings = mapper_name
-
-	def create_settings(self, bank):
-		mapper = frappe.new_doc("Bank Statement Settings")
-		mapper.bank = bank
-		mapper.date_format = "%Y-%m-%d"
-		mapper.bank_account = self.bank_account
-		for header in ["Date", "Particulars", "Withdrawals", "Deposits", "Balance"]:
-			header_item = mapper.append("header_items", {})
-			header_item.mapped_header = header_item.stmt_header = header
-		mapper.save()
-
-	def on_update(self):
-		if (not self.bank_statement):
-			self.reconciled_transaction_items = self.new_transaction_items = []
-			return
-
-		if len(self.new_transaction_items + self.reconciled_transaction_items) == 0:
-			self.populate_payment_entries()
-		else:
-			self.match_invoice_to_payment()
-
-	def validate(self):
-		if not self.new_transaction_items:
-			self.populate_payment_entries()
-
-	def get_statement_headers(self):
-		if not self.bank_settings:
-			frappe.throw(_("Bank Data mapper doesn't exist"))
-		mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings)
-		headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items}
-		return headers
-
-	def populate_payment_entries(self):
-		if self.bank_statement is None: return
-		file_url = self.bank_statement
-		if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
-			frappe.throw(_("Transactions already retreived from the statement"))
-
-		date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format")
-		if (date_format is None):
-			date_format = '%Y-%m-%d'
-		if self.bank_settings:
-			mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
-		statement_headers = self.get_statement_headers()
-		transactions = get_transaction_entries(file_url, statement_headers)
-		for entry in transactions:
-			date = entry[statement_headers["Date"]].strip()
-			#print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"]))
-			if (not date): continue
-			transaction_date = datetime.strptime(date, date_format).date()
-			if (self.from_date and transaction_date < datetime.strptime(self.from_date, '%Y-%m-%d').date()): continue
-			if (self.to_date and transaction_date > datetime.strptime(self.to_date, '%Y-%m-%d').date()): continue
-			bank_entry = self.append('new_transaction_items', {})
-			bank_entry.transaction_date = transaction_date
-			bank_entry.description = entry[statement_headers["Particulars"]]
-
-			mapped_item = next((entry for entry in mapped_items if entry.mapping_type == "Transaction" and frappe.safe_decode(entry.bank_data.lower()) in frappe.safe_decode(bank_entry.description.lower())), None)
-			if (mapped_item is not None):
-				bank_entry.party_type = mapped_item.mapped_data_type
-				bank_entry.party = mapped_item.mapped_data
-			else:
-				bank_entry.party_type = "Supplier" if not entry[statement_headers["Deposits"]].strip() else "Customer"
-				party_list = frappe.get_all(bank_entry.party_type, fields=["name"])
-				parties = [party.name for party in party_list]
-				matches = difflib.get_close_matches(frappe.safe_decode(bank_entry.description.lower()), parties, 1, 0.4)
-				if len(matches) > 0: bank_entry.party = matches[0]
-			bank_entry.amount = -float(entry[statement_headers["Withdrawals"]]) if not entry[statement_headers["Deposits"]].strip() else float(entry[statement_headers["Deposits"]])
-		self.map_unknown_transactions()
-		self.map_transactions_on_journal_entry()
-
-	def map_transactions_on_journal_entry(self):
-		for entry in self.new_transaction_items:
-			vouchers = frappe.db.sql("""select name, posting_date from `tabJournal Entry`
-										where posting_date='{0}' and total_credit={1} and cheque_no='{2}' and docstatus != 2
-									""".format(entry.transaction_date, abs(entry.amount), frappe.safe_decode(entry.description)), as_dict=True)
-			if (len(vouchers) == 1):
-				entry.reference_name = vouchers[0].name
-
-	def populate_matching_invoices(self):
-		self.payment_invoice_items = []
-		self.map_unknown_transactions()
-		added_invoices = []
-		for entry in self.new_transaction_items:
-			if (not entry.party or entry.party_type == "Account"): continue
-			account = self.receivable_account if entry.party_type == "Customer" else self.payable_account
-			invoices = get_outstanding_invoices(entry.party_type, entry.party, account)
-			transaction_date = datetime.strptime(entry.transaction_date, "%Y-%m-%d").date()
-			outstanding_invoices = [invoice for invoice in invoices if invoice.posting_date <= transaction_date]
-			amount = abs(entry.amount)
-			matching_invoices = [invoice for invoice in outstanding_invoices if invoice.outstanding_amount == amount]
-			sorted(outstanding_invoices, key=lambda k: k['posting_date'])
-			for e in (matching_invoices + outstanding_invoices):
-				added = next((inv for inv in added_invoices if inv == e.get('voucher_no')), None)
-				if (added is not None): continue
-				ent = self.append('payment_invoice_items', {})
-				ent.transaction_date = entry.transaction_date
-				ent.payment_description = frappe.safe_decode(entry.description)
-				ent.party_type = entry.party_type
-				ent.party = entry.party
-				ent.invoice = e.get('voucher_no')
-				added_invoices += [ent.invoice]
-				ent.invoice_type = "Sales Invoice" if entry.party_type == "Customer" else "Purchase Invoice"
-				ent.invoice_date = e.get('posting_date')
-				ent.outstanding_amount = e.get('outstanding_amount')
-				ent.allocated_amount = min(float(e.get('outstanding_amount')), amount)
-				amount -= float(e.get('outstanding_amount'))
-				if (amount <= 5): break
-		self.match_invoice_to_payment()
-		self.populate_matching_vouchers()
-		self.map_transactions_on_journal_entry()
-
-	def match_invoice_to_payment(self):
-		added_payments = []
-		for entry in self.new_transaction_items:
-			if (not entry.party or entry.party_type == "Account"): continue
-			entry.account = self.receivable_account if entry.party_type == "Customer" else self.payable_account
-			amount = abs(entry.amount)
-			payment, matching_invoices = None, []
-			for inv_entry in self.payment_invoice_items:
-				if (inv_entry.payment_description != frappe.safe_decode(entry.description) or inv_entry.transaction_date != entry.transaction_date): continue
-				if (inv_entry.party != entry.party): continue
-				matching_invoices += [inv_entry.invoice_type + "|" + inv_entry.invoice]
-				payment = get_payments_matching_invoice(inv_entry.invoice, entry.amount, entry.transaction_date)
-				doc = frappe.get_doc(inv_entry.invoice_type, inv_entry.invoice)
-				inv_entry.invoice_date = doc.posting_date
-				inv_entry.outstanding_amount = doc.outstanding_amount
-				inv_entry.allocated_amount = min(float(doc.outstanding_amount), amount)
-				amount -= inv_entry.allocated_amount
-				if (amount < 0): break
-
-			amount = abs(entry.amount)
-			if (payment is None):
-				order_doctype = "Sales Order" if entry.party_type=="Customer" else "Purchase Order"
-				from erpnext.controllers.accounts_controller import get_advance_payment_entries
-				payment_entries = get_advance_payment_entries(entry.party_type, entry.party, entry.account, order_doctype, against_all_orders=True)
-				payment_entries += self.get_matching_payments(entry.party, amount, entry.transaction_date)
-				payment = next((payment for payment in payment_entries if payment.amount == amount and payment not in added_payments), None)
-				if (payment is None):
-					print("Failed to find payments for {0}:{1}".format(entry.party, amount))
-					continue
-			added_payments += [payment]
-			entry.reference_type = payment.reference_type
-			entry.reference_name = payment.reference_name
-			entry.mode_of_payment = "Wire Transfer"
-			entry.outstanding_amount = min(amount, 0)
-			if (entry.payment_reference is None):
-				entry.payment_reference = frappe.safe_decode(entry.description)
-			entry.invoices = ",".join(matching_invoices)
-			#print("Matching payment is {0}:{1}".format(entry.reference_type, entry.reference_name))
-
-	def get_matching_payments(self, party, amount, pay_date):
-		query = """select 'Payment Entry' as reference_type, name as reference_name, paid_amount as amount
-					from `tabPayment Entry` where party='{0}' and paid_amount={1} and posting_date='{2}' and docstatus != 2
-					""".format(party, amount, pay_date)
-		matching_payments = frappe.db.sql(query, as_dict=True)
-		return matching_payments
-
-	def map_unknown_transactions(self):
-		for entry in self.new_transaction_items:
-			if (entry.party): continue
-			inv_type = "Sales Invoice" if (entry.amount > 0) else "Purchase Invoice"
-			party_type = "customer" if (entry.amount > 0) else "supplier"
-
-			query = """select posting_date, name, {0}, outstanding_amount
-							from `tab{1}` where ROUND(outstanding_amount)={2} and posting_date < '{3}'
-							""".format(party_type, inv_type, round(abs(entry.amount)), entry.transaction_date)
-			invoices = frappe.db.sql(query, as_dict = True)
-			if(len(invoices) > 0):
-				entry.party = invoices[0].get(party_type)
-
-	def populate_matching_vouchers(self):
-		for entry in self.new_transaction_items:
-			if (not entry.party or entry.reference_name): continue
-			print("Finding matching voucher for {0}".format(frappe.safe_decode(entry.description)))
-			amount = abs(entry.amount)
-			invoices = []
-			vouchers = get_matching_journal_entries(self.from_date, self.to_date, entry.party, self.bank_account, amount)
-			if len(vouchers) == 0: continue
-			for voucher in vouchers:
-				added = next((entry.invoice for entry in self.payment_invoice_items if entry.invoice == voucher.voucher_no), None)
-				if (added):
-					print("Found voucher {0}".format(added))
-					continue
-				print("Adding voucher {0} {1} {2}".format(voucher.voucher_no, voucher.posting_date, voucher.debit))
-				ent = self.append('payment_invoice_items', {})
-				ent.invoice_date = voucher.posting_date
-				ent.invoice_type = "Journal Entry"
-				ent.invoice = voucher.voucher_no
-				ent.payment_description = frappe.safe_decode(entry.description)
-				ent.allocated_amount = max(voucher.debit, voucher.credit)
-
-				invoices += [ent.invoice_type + "|" + ent.invoice]
-				entry.reference_type = "Journal Entry"
-				entry.mode_of_payment = "Wire Transfer"
-				entry.reference_name = ent.invoice
-				#entry.account = entry.party
-				entry.invoices = ",".join(invoices)
-				break
-
-
-	def create_payment_entries(self):
-		for payment_entry in self.new_transaction_items:
-			if (not payment_entry.party): continue
-			if (payment_entry.reference_name): continue
-			print("Creating payment entry for {0}".format(frappe.safe_decode(payment_entry.description)))
-			if (payment_entry.party_type == "Account"):
-				payment = self.create_journal_entry(payment_entry)
-				invoices = [payment.doctype + "|" + payment.name]
-				payment_entry.invoices = ",".join(invoices)
-			else:
-				payment = self.create_payment_entry(payment_entry)
-				invoices = [entry.reference_doctype + "|" + entry.reference_name for entry in payment.references if entry is not None]
-				payment_entry.invoices = ",".join(invoices)
-				payment_entry.mode_of_payment = payment.mode_of_payment
-				payment_entry.account = self.receivable_account if payment_entry.party_type == "Customer" else self.payable_account
-			payment_entry.reference_name = payment.name
-			payment_entry.reference_type = payment.doctype
-		frappe.msgprint(_("Successfully created payment entries"))
-
-	def create_payment_entry(self, pe):
-		payment = frappe.new_doc("Payment Entry")
-		payment.posting_date = pe.transaction_date
-		payment.payment_type = "Receive" if pe.party_type == "Customer" else "Pay"
-		payment.mode_of_payment = "Wire Transfer"
-		payment.party_type = pe.party_type
-		payment.party = pe.party
-		payment.paid_to = self.bank_account if pe.party_type == "Customer" else self.payable_account
-		payment.paid_from = self.receivable_account if pe.party_type == "Customer" else self.bank_account
-		payment.paid_amount = payment.received_amount = abs(pe.amount)
-		payment.reference_no = pe.description
-		payment.reference_date = pe.transaction_date
-		payment.save()
-		for inv_entry in self.payment_invoice_items:
-			if (pe.description != inv_entry.payment_description or pe.transaction_date != inv_entry.transaction_date): continue
-			if (pe.party != inv_entry.party): continue
-			reference = payment.append("references", {})
-			reference.reference_doctype = inv_entry.invoice_type
-			reference.reference_name = inv_entry.invoice
-			reference.allocated_amount = inv_entry.allocated_amount
-			print ("Adding invoice {0} {1}".format(reference.reference_name, reference.allocated_amount))
-		payment.setup_party_account_field()
-		payment.set_missing_values()
-		#payment.set_exchange_rate()
-		#payment.set_amounts()
-		#print("Created payment entry {0}".format(payment.as_dict()))
-		payment.save()
-		return payment
-
-	def create_journal_entry(self, pe):
-		je = frappe.new_doc("Journal Entry")
-		je.is_opening = "No"
-		je.voucher_type = "Bank Entry"
-		je.cheque_no = pe.description
-		je.cheque_date = pe.transaction_date
-		je.remark = pe.description
-		je.posting_date = pe.transaction_date
-		if (pe.amount < 0):
-			je.append("accounts", {"account": pe.party, "debit_in_account_currency": abs(pe.amount)})
-			je.append("accounts", {"account": self.bank_account, "credit_in_account_currency": abs(pe.amount)})
-		else:
-			je.append("accounts", {"account": pe.party, "credit_in_account_currency": pe.amount})
-			je.append("accounts", {"account": self.bank_account, "debit_in_account_currency": pe.amount})
-		je.save()
-		return je
-
-	def update_payment_entry(self, payment):
-		lst = []
-		invoices = payment.invoices.strip().split(',')
-		if (len(invoices) == 0): return
-		amount = float(abs(payment.amount))
-		for invoice_entry in invoices:
-			if (not invoice_entry.strip()): continue
-			invs = invoice_entry.split('|')
-			invoice_type, invoice = invs[0], invs[1]
-			outstanding_amount = frappe.get_value(invoice_type, invoice, 'outstanding_amount')
-
-			lst.append(frappe._dict({
-				'voucher_type': payment.reference_type,
-				'voucher_no' : payment.reference_name,
-				'against_voucher_type' : invoice_type,
-				'against_voucher'  : invoice,
-				'account' : payment.account,
-				'party_type': payment.party_type,
-				'party': frappe.get_value("Payment Entry", payment.reference_name, "party"),
-				'unadjusted_amount' : float(amount),
-				'allocated_amount' : min(outstanding_amount, amount)
-			}))
-			amount -= outstanding_amount
-		if lst:
-			from erpnext.accounts.utils import reconcile_against_document
-			try:
-				reconcile_against_document(lst)
-			except:
-				frappe.throw(_("Exception occurred while reconciling {0}").format(payment.reference_name))
-
-	def submit_payment_entries(self):
-		for payment in self.new_transaction_items:
-			if payment.reference_name is None: continue
-			doc = frappe.get_doc(payment.reference_type, payment.reference_name)
-			if doc.docstatus == 1:
-				if (payment.reference_type == "Journal Entry"): continue
-				if doc.unallocated_amount == 0: continue
-				print("Reconciling payment {0}".format(payment.reference_name))
-				self.update_payment_entry(payment)
-			else:
-				print("Submitting payment {0}".format(payment.reference_name))
-				if (payment.reference_type == "Payment Entry"):
-					if (payment.payment_reference):
-						doc.reference_no = payment.payment_reference
-					doc.mode_of_payment = payment.mode_of_payment
-				doc.save()
-				doc.submit()
-		self.move_reconciled_entries()
-		self.populate_matching_invoices()
-
-	def move_reconciled_entries(self):
-		idx = 0
-		while idx < len(self.new_transaction_items):
-			entry = self.new_transaction_items[idx]
-			try:
-				print("Checking transaction {0}: {2} in {1} entries".format(idx, len(self.new_transaction_items), frappe.safe_decode(entry.description)))
-			except UnicodeEncodeError:
-				pass
-			idx += 1
-			if entry.reference_name is None: continue
-			doc = frappe.get_doc(entry.reference_type, entry.reference_name)
-			if doc.docstatus == 1 and (entry.reference_type == "Journal Entry" or doc.unallocated_amount == 0):
-				self.remove(entry)
-				rc_entry = self.append('reconciled_transaction_items', {})
-				dentry = entry.as_dict()
-				dentry.pop('idx', None)
-				rc_entry.update(dentry)
-				idx -= 1
-
-
-def get_matching_journal_entries(from_date, to_date, account, against, amount):
-	query = """select voucher_no, posting_date, account, against, debit_in_account_currency as debit, credit_in_account_currency as credit
-							      from `tabGL Entry`
-								  where posting_date between '{0}' and '{1}' and account = '{2}' and against = '{3}' and debit = '{4}'
-								  """.format(from_date, to_date, account, against, amount)
-	jv_entries = frappe.db.sql(query, as_dict=True)
-	#print("voucher query:{0}\n Returned {1} entries".format(query, len(jv_entries)))
-	return jv_entries
-
-def get_payments_matching_invoice(invoice, amount, pay_date):
-	query = """select pe.name as reference_name, per.reference_doctype as reference_type, per.outstanding_amount, per.allocated_amount
-				from `tabPayment Entry Reference` as per JOIN `tabPayment Entry` as pe on pe.name = per.parent
-				where per.reference_name='{0}' and (posting_date='{1}' or reference_date='{1}') and pe.docstatus != 2
-				""".format(invoice, pay_date)
-	payments = frappe.db.sql(query, as_dict=True)
-	if (len(payments) == 0): return
-	payment = next((payment for payment in payments if payment.allocated_amount == amount), payments[0])
-	#Hack: Update the reference type which is set to invoice type
-	payment.reference_type = "Payment Entry"
-	return payment
-
-def is_headers_present(headers, row):
-	for header in headers:
-		if header not in row:
-			return False
-	return True
-
-def get_header_index(headers, row):
-	header_index = {}
-	for header in headers:
-		if header in row:
-			header_index[header] = row.index(header)
-	return header_index
-
-def get_transaction_info(headers, header_index, row):
-	transaction = {}
-	for header in headers:
-		transaction[header] = row[header_index[header]]
-		if (transaction[header] == None):
-			transaction[header] = ""
-	return transaction
-
-def get_transaction_entries(file_url, headers):
-	header_index = {}
-	rows, transactions = [], []
-
-	if (file_url.lower().endswith("xlsx")):
-		from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
-		rows = read_xlsx_file_from_attached_file(file_url=file_url)
-	elif (file_url.lower().endswith("csv")):
-		from frappe.utils.csvutils import read_csv_content
-		_file = frappe.get_doc("File", {"file_url": file_url})
-		filepath = _file.get_full_path()
-		with open(filepath,'rb') as csvfile:
-			rows = read_csv_content(csvfile.read())
-	elif (file_url.lower().endswith("xls")):
-		filename = file_url.split("/")[-1]
-		rows = get_rows_from_xls_file(filename)
-	else:
-		frappe.throw(_("Only .csv and .xlsx files are supported currently"))
-
-	stmt_headers = headers.values()
-	for row in rows:
-		if len(row) == 0 or row[0] == None or not row[0]: continue
-		#print("Processing row {0}".format(row))
-		if header_index:
-			transaction = get_transaction_info(stmt_headers, header_index, row)
-			transactions.append(transaction)
-		elif is_headers_present(stmt_headers, row):
-			header_index = get_header_index(stmt_headers, row)
-	return transactions
-
-def get_rows_from_xls_file(filename):
-	_file = frappe.get_doc("File", {"file_name": filename})
-	filepath = _file.get_full_path()
-	import xlrd
-	book = xlrd.open_workbook(filepath)
-	sheets = book.sheets()
-	rows = []
-	for row in range(1, sheets[0].nrows):
-		row_values = []
-		for col in range(1, sheets[0].ncols):
-			row_values.append(sheets[0].cell_value(row, col))
-		rows.append(row_values)
-	return rows
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js
deleted file mode 100644
index 46d570f..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Statement Transaction Entry", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Statement Transaction Entry
-		() => frappe.tests.make('Bank Statement Transaction Entry', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py
deleted file mode 100644
index 4589483..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_entry/test_bank_statement_transaction_entry.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestBankStatementTransactionEntry(unittest.TestCase):
-	pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json
deleted file mode 100644
index d96c94d..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.json
+++ /dev/null
@@ -1,365 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-11-07 13:58:53.827058", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "transaction_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Transaction Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 4, 
-   "fieldname": "payment_description", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Payment Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "party_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Party Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Customer\nSupplier\nAccount", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "party", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Party", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "party_type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_4", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "invoice_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Invoice Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "invoice_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Invoice Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "invoice", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "invoice", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "invoice_type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "fieldname": "outstanding_amount", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Outstanding Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "fieldname": "allocated_amount", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Allocated Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-09-14 19:03:30.949831", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Bank Statement Transaction Invoice Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py b/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py
deleted file mode 100644
index cb1b158..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_invoice_item/bank_statement_transaction_invoice_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementTransactionInvoiceItem(Document):
-	pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json
deleted file mode 100644
index 177dccd..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.json
+++ /dev/null
@@ -1,494 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-11-07 14:03:05.651413",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 1,
-   "fieldname": "transaction_date",
-   "fieldtype": "Date",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Transaction Date",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 4,
-   "fieldname": "description",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Description",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 1,
-   "fieldname": "amount",
-   "fieldtype": "Currency",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Amount",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 1,
-   "fieldname": "party_type",
-   "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Party Type",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Customer\nSupplier\nAccount",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 2,
-   "fieldname": "party",
-   "fieldtype": "Dynamic Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Party",
-   "length": 0,
-   "no_copy": 0,
-   "options": "party_type",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "reference_type",
-   "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Reference Type",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Payment Entry\nJournal Entry",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "account",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Account",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Account",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "mode_of_payment",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Mode of Payment",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Mode of Payment",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "outstanding_amount",
-   "fieldtype": "Currency",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "outstanding_amount",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "column_break_10",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 2,
-   "fieldname": "reference_name",
-   "fieldtype": "Dynamic Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Reference Name",
-   "length": 0,
-   "no_copy": 0,
-   "options": "reference_type",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "payment_reference",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Payment Reference",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "invoices",
-   "fieldtype": "Text",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Invoices",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2017-11-15 19:18:52.876221",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Statement Transaction Payment Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py b/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py
deleted file mode 100644
index 9840c0d..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_payment_item/bank_statement_transaction_payment_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementTransactionPaymentItem(Document):
-	pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js
deleted file mode 100644
index 46aa4f2..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2017, sathishpy@gmail.com and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Bank Statement Settings', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json
deleted file mode 100644
index 474bb90..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.json
+++ /dev/null
@@ -1,266 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "beta": 0, 
- "creation": "2017-11-13 13:38:10.863592", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank_account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bank Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "'%d/%m/%Y'", 
-   "fieldname": "date_format", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Date Format", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "statement_header_mapping", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Statement Header Mapping", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "header_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Statement Headers", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Settings Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "transaction_data_mapping", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Transaction Data Mapping", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "mapped_items", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Mapped Items", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Bank Statement Transaction Settings Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-01-12 10:34:32.840487", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Bank Statement Settings", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }, 
-  {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py
deleted file mode 100644
index de9a85f..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/bank_statement_transaction_settings.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementSettings(Document):
-	def autoname(self):
-		self.name = self.bank_account + "-Mappings"
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js
deleted file mode 100644
index f2381c0..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Bank Statement Settings", function (assert) {
-	let done = assert.async();
-
-	// number of asserts
-	assert.expect(1);
-
-	frappe.run_serially([
-		// insert a new Bank Statement Settings
-		() => frappe.tests.make('Bank Statement Settings', [
-			// values to be set
-			{key: 'value'}
-		]),
-		() => {
-			assert.equal(cur_frm.doc.key, 'value');
-		},
-		() => done()
-	]);
-
-});
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py b/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py
deleted file mode 100644
index aa7fe83..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings/test_bank_statement_transaction_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestBankStatementSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json
deleted file mode 100644
index 47c3209..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.json
+++ /dev/null
@@ -1,166 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-11-13 13:42:00.335432", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "Transaction", 
-   "fieldname": "mapping_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Mapping Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Transaction", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "bank_data", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bank Data", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "Account", 
-   "fieldname": "mapped_data_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Mapped Data Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account\nCustomer\nSupplier\nAccount", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "mapped_data", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Mapped Data", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "mapped_data_type", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-01-08 00:13:49.973501", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Bank Statement Transaction Settings Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py b/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py
deleted file mode 100644
index bf0a590..0000000
--- a/erpnext/accounts/doctype/bank_statement_transaction_settings_item/bank_statement_transaction_settings_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017, sathishpy@gmail.com and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class BankStatementTransactionSettingsItem(Document):
-	pass
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js
index 8b1bab1..3758b52 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js
@@ -1,32 +1,70 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-frappe.ui.form.on('Bank Transaction', {
+frappe.ui.form.on("Bank Transaction", {
 	onload(frm) {
-		frm.set_query('payment_document', 'payment_entries', function() {
+		frm.set_query("payment_document", "payment_entries", function () {
 			return {
-				"filters": {
-					"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
-				}
+				filters: {
+					name: [
+						"in",
+						[
+							"Payment Entry",
+							"Journal Entry",
+							"Sales Invoice",
+							"Purchase Invoice",
+							"Expense Claim",
+						],
+					],
+				},
 			};
 		});
-	}
+	},
+	bank_account: function (frm) {
+		set_bank_statement_filter(frm);
+	},
+
+	setup: function (frm) {
+		frm.set_query("party_type", function () {
+			return {
+				filters: {
+					name: ["in", Object.keys(frappe.boot.party_account_types)],
+				},
+			};
+		});
+	},
 });
 
-frappe.ui.form.on('Bank Transaction Payments', {
-	payment_entries_remove: function(frm, cdt, cdn) {
+frappe.ui.form.on("Bank Transaction Payments", {
+	payment_entries_remove: function (frm, cdt, cdn) {
 		update_clearance_date(frm, cdt, cdn);
-	}
+	},
 });
 
 const update_clearance_date = (frm, cdt, cdn) => {
 	if (frm.doc.docstatus === 1) {
-		frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment',
-			{doctype: cdt, docname: cdn})
-			.then(e => {
+		frappe
+			.xcall(
+				"erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
+				{ doctype: cdt, docname: cdn }
+			)
+			.then((e) => {
 				if (e == "success") {
-					frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'});
+					frappe.show_alert({
+						message: __("Document {0} successfully uncleared", [e]),
+						indicator: "green",
+					});
 				}
 			});
 	}
-};
\ No newline at end of file
+};
+
+function set_bank_statement_filter(frm) {
+	frm.set_query("bank_statement", function () {
+		return {
+			filters: {
+				bank_account: frm.doc.bank_account,
+			},
+		};
+	});
+}
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 39937bb..69ee497 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -1,833 +1,245 @@
 {
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
  "allow_import": 1,
- "allow_rename": 0,
  "autoname": "naming_series:",
- "beta": 0,
  "creation": "2018-10-22 18:19:02.784533",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
- "document_type": "",
  "editable_grid": 1,
  "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "date",
+  "column_break_2",
+  "status",
+  "bank_account",
+  "company",
+  "section_break_4",
+  "deposit",
+  "withdrawal",
+  "column_break_7",
+  "currency",
+  "section_break_10",
+  "description",
+  "section_break_14",
+  "reference_number",
+  "transaction_id",
+  "payment_entries",
+  "section_break_18",
+  "allocated_amount",
+  "amended_from",
+  "column_break_17",
+  "unallocated_amount",
+  "party_section",
+  "party_type",
+  "party"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "ACC-BTN-.YYYY.-",
-   "fetch_if_empty": 0,
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "hidden": 1,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Series",
-   "length": 0,
    "no_copy": 1,
    "options": "ACC-BTN-.YYYY.-",
-   "permlevel": 0,
-   "precision": "",
    "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
    "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 1,
-   "translatable": 0,
-   "unique": 0
+   "set_only_once": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "date",
    "fieldtype": "Date",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Date",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Date"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "column_break_2",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "Pending",
-   "fetch_if_empty": 0,
    "fieldname": "status",
    "fieldtype": "Select",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Status",
-   "length": 0,
-   "no_copy": 0,
-   "options": "\nPending\nSettled\nUnreconciled\nReconciled",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "\nPending\nSettled\nUnreconciled\nReconciled"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "bank_account",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Bank Account",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Bank Account",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Bank Account"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "default": "",
    "fetch_from": "bank_account.company",
-   "fetch_if_empty": 0,
    "fieldname": "company",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
    "in_standard_filter": 1,
    "label": "Company",
-   "length": 0,
-   "no_copy": 0,
    "options": "Company",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "section_break_4",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
-   "fieldname": "debit",
-   "fieldtype": "Currency",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Debit",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
-   "fieldname": "credit",
-   "fieldtype": "Currency",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Credit",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "column_break_7",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "currency",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Currency",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Currency",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Currency"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "section_break_10",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "description",
    "fieldtype": "Small Text",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Description",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Description"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "section_break_14",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
+   "allow_on_submit": 1,
    "fieldname": "reference_number",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Reference Number",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Reference Number"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "transaction_id",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Transaction ID",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
    "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
    "unique": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
    "allow_on_submit": 1,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "payment_entries",
    "fieldtype": "Table",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Payment Entries",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Bank Transaction Payments",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "options": "Bank Transaction Payments"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "section_break_18",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "allocated_amount",
    "fieldtype": "Currency",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Allocated Amount",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Allocated Amount"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "amended_from",
    "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
    "label": "Amended From",
-   "length": 0,
    "no_copy": 1,
    "options": "Bank Transaction",
-   "permlevel": 0,
    "print_hide": 1,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "read_only": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
    "fieldname": "column_break_17",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "depends_on": "",
-   "fetch_if_empty": 0,
    "fieldname": "unallocated_amount",
    "fieldtype": "Currency",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Unallocated Amount",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Unallocated Amount"
+  },
+  {
+   "fieldname": "party_section",
+   "fieldtype": "Section Break",
+   "label": "Payment From / To"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "party_type",
+   "fieldtype": "Link",
+   "label": "Party Type",
+   "options": "DocType"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "party",
+   "fieldtype": "Dynamic Link",
+   "label": "Party",
+   "options": "party_type"
+  },
+  {
+   "fieldname": "deposit",
+   "oldfieldname": "debit",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Deposit"
+  },
+  {
+   "fieldname": "withdrawal",
+   "oldfieldname": "credit",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Withdrawal"
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
  "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-05-11 05:27:55.244721",
+ "links": [],
+ "modified": "2020-12-30 19:40:54.221070",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Transaction",
- "name_case": "",
  "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0,
    "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "System Manager",
-   "set_user_permissions": 0,
    "share": 1,
    "submit": 1,
    "write": 1
   },
   {
-   "amend": 0,
    "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "Accounts Manager",
-   "set_user_permissions": 0,
    "share": 1,
    "submit": 1,
    "write": 1
   },
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "Accounts User",
-   "set_user_permissions": 0,
    "share": 1,
    "submit": 1,
    "write": 1
   }
  ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
  "sort_field": "date",
  "sort_order": "DESC",
  "title_field": "bank_account",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index 0e45db3..5246baa 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -11,7 +11,7 @@
 
 class BankTransaction(StatusUpdater):
 	def after_insert(self):
-		self.unallocated_amount = abs(flt(self.credit) - flt(self.debit))
+		self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit))
 
 	def on_submit(self):
 		self.clear_linked_payment_entries()
@@ -30,13 +30,13 @@
 
 		if allocated_amount:
 			frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
-			frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount))
+			frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount))
 
 		else:
 			frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
-			frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
+			frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)))
 
-		amount = self.debit or self.credit
+		amount = self.deposit or self.withdrawal
 		if amount == self.allocated_amount:
 			frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
 
@@ -44,18 +44,11 @@
 
 	def clear_linked_payment_entries(self):
 		for payment_entry in self.payment_entries:
-			allocated_amount = get_total_allocated_amount(payment_entry)
-			paid_amount = get_paid_amount(payment_entry, self.currency)
+			if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
+				self.clear_simple_entry(payment_entry)
 
-			if paid_amount and allocated_amount:
-				if  flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
-					frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).").format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))
-				else:
-					if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
-						self.clear_simple_entry(payment_entry)
-
-					elif payment_entry.payment_document == "Sales Invoice":
-						self.clear_sales_invoice(payment_entry)
+			elif payment_entry.payment_document == "Sales Invoice":
+				self.clear_sales_invoice(payment_entry)
 
 	def clear_simple_entry(self, payment_entry):
 		frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
@@ -112,3 +105,4 @@
 			frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
 
 		return doc.payment_entry
+
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index e9fc5f0..3b14e4e 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -5,10 +5,11 @@
 
 import frappe
 import unittest
+import json
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
-from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
+from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import reconcile_vouchers, get_linked_payments
 from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
 
 test_dependencies = ["Item", "Cost Center"]
@@ -17,7 +18,7 @@
 	def setUp(self):
 		make_pos_profile()
 		add_transactions()
-		add_payments()
+		add_vouchers()
 
 	def tearDown(self):
 		for bt in frappe.get_all("Bank Transaction"):
@@ -38,14 +39,18 @@
 	# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
 	def test_linked_payments(self):
 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
-		linked_payments = get_linked_payments(bank_transaction.name)
-		self.assertTrue(linked_payments[0].party == "Conrad Electronic")
+		linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+		self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
 
 	# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
 	def test_reconcile(self):
 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
-		reconcile(bank_transaction.name, "Payment Entry", payment.name)
+		vouchers = json.dumps([{
+		"payment_doctype":"Payment Entry",
+		"payment_name":payment.name,
+		"amount":bank_transaction.unallocated_amount}])
+		reconcile_vouchers(bank_transaction.name, vouchers)
 
 		unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
 		self.assertTrue(unallocated_amount == 0)
@@ -53,45 +58,40 @@
 		clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
 		self.assertTrue(clearance_date is not None)
 
-	# Check if ERPNext can correctly fetch a linked payment based on the party
-	def test_linked_payments_based_on_party(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
-		linked_payments = get_linked_payments(bank_transaction.name)
-		self.assertTrue(len(linked_payments)==1)
-
 	# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
 	def test_debit_credit_output(self):
 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
-		linked_payments = get_linked_payments(bank_transaction.name)
-		self.assertTrue(linked_payments[0].payment_type == "Pay")
+		linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
+		print(linked_payments)
+		self.assertTrue(linked_payments[0][3])
 
 	# Check error if already reconciled
 	def test_already_reconciled(self):
 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
-		reconcile(bank_transaction.name, "Payment Entry", payment.name)
+		vouchers = json.dumps([{
+			"payment_doctype":"Payment Entry",
+			"payment_name":payment.name,
+			"amount":bank_transaction.unallocated_amount}])
+		reconcile_vouchers(bank_transaction.name, vouchers)
 
 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
-		self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
-
-	# Raise an error if creditor transaction vs creditor payment
-	def test_invalid_creditor_reconcilation(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
-		payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690))
-		self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
-
-	# Raise an error if debitor transaction vs debitor payment
-	def test_invalid_debitor_reconcilation(self):
-		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
-		payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080))
-		self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
+		vouchers = json.dumps([{
+			"payment_doctype":"Payment Entry",
+			"payment_name":payment.name,
+			"amount":bank_transaction.unallocated_amount}])
+		self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
 
 	# Raise an error if debitor transaction vs debitor payment
 	def test_clear_sales_invoice(self):
 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
 		payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
-		reconcile(bank_transaction.name, "Sales Invoice", payment.name)
+		vouchers = json.dumps([{
+			"payment_doctype":"Sales Invoice",
+			"payment_name":payment.name,
+			"amount":bank_transaction.unallocated_amount}])
+		reconcile_vouchers(bank_transaction.name, vouchers=vouchers)
 
 		self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
 		self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
@@ -126,7 +126,7 @@
 		"doctype": "Bank Transaction",
 		"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
 		"date": "2018-10-23",
-		"debit": 1200,
+		"deposit": 1200,
 		"currency": "INR",
 		"bank_account": "Checking Account - Citi Bank"
 	}).insert()
@@ -136,7 +136,7 @@
 		"doctype": "Bank Transaction",
 		"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
 		"date": "2018-10-23",
-		"debit": 1700,
+		"deposit": 1700,
 		"currency": "INR",
 		"bank_account": "Checking Account - Citi Bank"
 	}).insert()
@@ -146,7 +146,7 @@
 		"doctype": "Bank Transaction",
 		"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
 		"date": "2018-10-26",
-		"debit": 690,
+		"withdrawal": 690,
 		"currency": "INR",
 		"bank_account": "Checking Account - Citi Bank"
 	}).insert()
@@ -156,7 +156,7 @@
 		"doctype": "Bank Transaction",
 		"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
 		"date": "2018-10-27",
-		"debit": 3900,
+		"deposit": 3900,
 		"currency": "INR",
 		"bank_account": "Checking Account - Citi Bank"
 	}).insert()
@@ -166,7 +166,7 @@
 		"doctype": "Bank Transaction",
 		"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
 		"date": "2018-10-27",
-		"credit": 109080,
+		"withdrawal": 109080,
 		"currency": "INR",
 		"bank_account": "Checking Account - Citi Bank"
 	}).insert()
@@ -174,7 +174,7 @@
 
 	frappe.flags.test_bank_transactions_created = True
 
-def add_payments():
+def add_vouchers():
 	if frappe.flags.test_payments_created:
 		return
 
@@ -192,6 +192,7 @@
 		pass
 
 	pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
+
 	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
 	pe.reference_no = "Conrad Oct 18"
 	pe.reference_date = "2018-10-24"
@@ -242,10 +243,15 @@
 	except frappe.DuplicateEntryError:
 		pass
 
-	pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900)
+	pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1)
+	pi.cash_bank_account = "_Test Bank - _TC"
+	pi.insert()
+	pi.submit()
 	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
 	pe.reference_no = "Poore Simon's Oct 18"
 	pe.reference_date = "2018-10-28"
+	pe.paid_amount = 690
+	pe.received_amount = 690
 	pe.insert()
 	pe.submit()
 
@@ -295,4 +301,4 @@
 	si.save()
 	si.submit()
 
-	frappe.flags.test_payments_created = True
\ No newline at end of file
+	frappe.flags.test_payments_created = True
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index b0a864f..ce76d0a 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -27,30 +27,30 @@
 
 	def validate(self):
 		self.flags.ignore_submit_comment = True
-		self.check_mandatory()
 		self.validate_and_set_fiscal_year()
 		self.pl_must_have_cost_center()
-		self.validate_cost_center()
 
 		if not self.flags.from_repost:
+			self.check_mandatory()
+			self.validate_cost_center()
 			self.check_pl_account()
 			self.validate_party()
 			self.validate_currency()
 
-	def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
-		if not from_repost:
+	def on_update(self):
+		adv_adj = self.flags.adv_adj
+		if not self.flags.from_repost:
 			self.validate_account_details(adv_adj)
 			self.validate_dimensions_for_pl_and_bs()
 			self.validate_allowed_dimensions()
+			validate_balance_type(self.account, adv_adj)
+			validate_frozen_account(self.account, adv_adj)
 
-		validate_frozen_account(self.account, adv_adj)
-		validate_balance_type(self.account, adv_adj)
-
-		# Update outstanding amt on against voucher
-		if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
-			and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
-				update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
-					self.against_voucher)
+			# Update outstanding amt on against voucher
+			if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
+				and self.against_voucher and self.flags.update_outstanding == 'Yes'):
+					update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
+						self.against_voucher)
 
 	def check_mandatory(self):
 		mandatory = ['account','voucher_type','voucher_no','company']
@@ -58,7 +58,7 @@
 			if not self.get(k):
 				frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
 
-		account_type = frappe.db.get_value("Account", self.account, "account_type")
+		account_type = frappe.get_cached_value("Account", self.account, "account_type")
 		if not (self.party_type and self.party):
 			if account_type == "Receivable":
 				frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
@@ -73,7 +73,7 @@
 				.format(self.voucher_type, self.voucher_no, self.account))
 
 	def pl_must_have_cost_center(self):
-		if frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss":
+		if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
 			if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
 				frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.")
 					.format(self.voucher_type, self.voucher_no, self.account))
@@ -140,25 +140,16 @@
 				.format(self.voucher_type, self.voucher_no, self.account, self.company))
 
 	def validate_cost_center(self):
-		if not hasattr(self, "cost_center_company"):
-			self.cost_center_company = {}
+		if not self.cost_center: return
 
-		def _get_cost_center_company():
-			if not self.cost_center_company.get(self.cost_center):
-				self.cost_center_company[self.cost_center] = frappe.db.get_value(
-					"Cost Center", self.cost_center, "company")
+		is_group, company = frappe.get_cached_value('Cost Center',
+			self.cost_center, ['is_group', 'company'])
 
-			return self.cost_center_company[self.cost_center]
-
-		def _check_is_group():
-			return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
-
-		if self.cost_center and _get_cost_center_company() != self.company:
+		if company != self.company:
 			frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
 				.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
 
-		if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \
-			and self.cost_center and _check_is_group():
+		if (self.voucher_type != 'Period Closing Voucher' and is_group):
 			frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
 				self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
 
@@ -184,7 +175,6 @@
 		if not self.fiscal_year:
 			self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
 
-
 def validate_balance_type(account, adv_adj=False):
 	if not adv_adj and account:
 		balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
@@ -250,7 +240,7 @@
 
 
 def validate_frozen_account(account, adv_adj=None):
-	frozen_account = frappe.db.get_value("Account", account, "freeze_account")
+	frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
 	if frozen_account == 'Yes' and not adv_adj:
 		frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
 			'frozen_accounts_modifier')
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index cb90f80..3419bb6 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -102,7 +102,7 @@
 			if account_currency == previous_account_currency:
 				if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
 					frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
-	
+
 	def validate_stock_accounts(self):
 		stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
 		for account in stock_accounts:
@@ -229,11 +229,11 @@
 			if d.reference_type=="Journal Entry":
 				account_root_type = frappe.db.get_value("Account", d.account, "root_type")
 				if account_root_type == "Asset" and flt(d.debit) > 0:
-					frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
-						.format(d.account))
+					frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
+						.format(d.idx, d.account))
 				elif account_root_type == "Liability" and flt(d.credit) > 0:
-					frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
-						.format(d.account))
+					frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
+						.format(d.idx, d.account))
 
 				if d.reference_name == self.name:
 					frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
@@ -1077,4 +1077,4 @@
 		},
 	}, target_doc)
 
-	return doclist
\ No newline at end of file
+	return doclist
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 1b97050..53ac996 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -3,6 +3,7 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
+import json
 import frappe
 from frappe import _
 from frappe.model.document import Document
@@ -82,18 +83,37 @@
 			self.make_communication_entry()
 
 		elif self.payment_channel == "Phone":
-			controller = get_payment_gateway_controller(self.payment_gateway)
-			payment_record = dict(
-				reference_doctype="Payment Request",
-				reference_docname=self.name,
-				payment_reference=self.reference_name,
-				grand_total=self.grand_total,
-				sender=self.email_to,
-				currency=self.currency,
-				payment_gateway=self.payment_gateway
-			)
-			controller.validate_transaction_currency(self.currency)
-			controller.request_for_payment(**payment_record)
+			self.request_phone_payment()
+
+	def request_phone_payment(self):
+		controller = get_payment_gateway_controller(self.payment_gateway)
+		request_amount = self.get_request_amount()
+
+		payment_record = dict(
+			reference_doctype="Payment Request",
+			reference_docname=self.name,
+			payment_reference=self.reference_name,
+			request_amount=request_amount,
+			sender=self.email_to,
+			currency=self.currency,
+			payment_gateway=self.payment_gateway
+		)
+
+		controller.validate_transaction_currency(self.currency)
+		controller.request_for_payment(**payment_record)
+	
+	def get_request_amount(self):
+		data_of_completed_requests = frappe.get_all("Integration Request", filters={
+			'reference_doctype': self.doctype,
+			'reference_docname': self.name,
+			'status': 'Completed'
+		}, pluck="data")
+
+		if not data_of_completed_requests:
+			return self.grand_total
+
+		request_amounts = sum([json.loads(d).get('request_amount') for d in data_of_completed_requests])
+		return request_amounts
 
 	def on_cancel(self):
 		self.check_if_payment_entry_exists()
@@ -351,8 +371,8 @@
 		if args.order_type == "Shopping Cart" or args.mute_email:
 			pr.flags.mute_email = True
 
+		pr.insert(ignore_permissions=True)
 		if args.submit_doc:
-			pr.insert(ignore_permissions=True)
 			pr.submit()
 
 	if args.order_type == "Shopping Cart":
@@ -412,8 +432,8 @@
 
 def get_gateway_details(args):
 	"""return gateway and payment account of default payment gateway"""
-	if args.get("payment_gateway"):
-		return get_payment_gateway_account(args.get("payment_gateway"))
+	if args.get("payment_gateway_account"):
+		return get_payment_gateway_account(args.get("payment_gateway_account"))
 
 	if args.order_type == "Shopping Cart":
 		payment_gateway_account = frappe.get_doc("Shopping Cart Settings").payment_gateway_account
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index 8a10e2c..5eba62c 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -45,7 +45,8 @@
 
 	def test_payment_request_linkings(self):
 		so_inr = make_sales_order(currency="INR")
-		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com")
+		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
+			payment_gateway_account="_Test Gateway - INR")
 
 		self.assertEqual(pr.reference_doctype, "Sales Order")
 		self.assertEqual(pr.reference_name, so_inr.name)
@@ -54,7 +55,8 @@
 		conversion_rate = get_exchange_rate("USD", "INR")
 
 		si_usd = create_sales_invoice(currency="USD", conversion_rate=conversion_rate)
-		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com")
+		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
+			payment_gateway_account="_Test Gateway - USD")
 
 		self.assertEqual(pr.reference_doctype, "Sales Invoice")
 		self.assertEqual(pr.reference_name, si_usd.name)
@@ -68,7 +70,7 @@
 
 		so_inr = make_sales_order(currency="INR")
 		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, submit_doc=1, return_doc=1)
+			mute_email=1, payment_gateway_account="_Test Gateway - INR", submit_doc=1, return_doc=1)
 		pe = pr.set_as_paid()
 
 		so_inr = frappe.get_doc("Sales Order", so_inr.name)
@@ -79,7 +81,7 @@
 			currency="USD", conversion_rate=50)
 
 		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1)
+			mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1)
 
 		pe = pr.set_as_paid()
 
@@ -106,7 +108,7 @@
 			currency="USD", conversion_rate=50)
 
 		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1)
+			mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1)
 
 		pe = pr.create_payment_entry()
 		pr.load_from_db()
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 73367fd..9ea616f 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -21,7 +21,7 @@
 			return { filters: { 'status': 'Open', 'docstatus': 1 } };
 		});
 		
-		if (frm.doc.docstatus === 0) frm.set_value("period_end_date", frappe.datetime.now_datetime());
+		if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime());
 		if (frm.doc.docstatus === 1) set_html_data(frm);
 	},
 
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index edf3d5a..f5224a2 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -20,11 +20,16 @@
 		self.validate_pos_invoices()
 	
 	def validate_pos_closing(self):
-		user = frappe.get_all("POS Closing Entry", 
-			filters = { "user": self.user, "docstatus": 1, "pos_profile": self.pos_profile },
-			or_filters = {
-				"period_start_date": ("between", [self.period_start_date, self.period_end_date]),
-				"period_end_date": ("between", [self.period_start_date, self.period_end_date])
+		user = frappe.db.sql("""
+			SELECT name FROM `tabPOS Closing Entry`
+			WHERE
+				user = %(user)s AND docstatus = 1 AND pos_profile = %(profile)s AND
+				(period_start_date between %(start)s and %(end)s OR period_end_date between %(start)s and %(end)s)
+			""", {
+				'user': self.user,
+				'profile': self.pos_profile,
+				'start': self.period_start_date,
+				'end': self.period_end_date
 			})
 
 		if user:
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index 07c8e44..493bd44 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -195,18 +195,43 @@
 	},
 
 	request_for_payment: function (frm) {
+		if (!frm.doc.contact_mobile) {
+			frappe.throw(__('Please enter mobile number first.'));
+		}
+		frm.dirty();
 		frm.save().then(() => {
-			frappe.dom.freeze();
-			frappe.call({
-				method: 'create_payment_request',
-				doc: frm.doc,
-			})
+			frappe.dom.freeze(__('Waiting for payment...'));
+			frappe
+				.call({
+					method: 'create_payment_request',
+					doc: frm.doc
+				})
 				.fail(() => {
 					frappe.dom.unfreeze();
-					frappe.msgprint('Payment request failed');
+					frappe.msgprint(__('Payment request failed'));
 				})
-				.then(() => {
-					frappe.msgprint('Payment request sent successfully');
+				.then(({ message }) => {
+					const payment_request_name = message.name;
+					setTimeout(() => {
+						frappe.db.get_value('Payment Request', payment_request_name, ['status', 'grand_total']).then(({ message }) => {
+							if (message.status != 'Paid') {
+								frappe.dom.unfreeze();
+								frappe.msgprint({
+									message: __('Payment Request took too long to respond. Please try requesting for payment again.'),
+									title: __('Request Timeout')
+								});
+							} else if (frappe.dom.freeze_count != 0) {
+								frappe.dom.unfreeze();
+								cur_frm.reload_doc();
+								cur_pos.payment.events.submit_invoice();
+
+								frappe.show_alert({
+									message: __("Payment of {0} received successfully.", [format_currency(message.grand_total, frm.doc.currency, 0)]),
+									indicator: 'green'
+								});
+							}
+						});
+					}, 60000);
 				});
 		});
 	}
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 8d8babb..76e0092 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -179,10 +179,18 @@
 			if d.get("serial_no"):
 				serial_nos = get_serial_nos(d.serial_no)
 				for sr in serial_nos:
-					serial_no_exists = frappe.db.exists("POS Invoice Item", {
-						"parent": self.return_against, 
-						"serial_no": ["like", d.get("serial_no")]
-					})
+					serial_no_exists = frappe.db.sql("""
+						SELECT name
+						FROM `tabPOS Invoice Item`
+						WHERE
+							parent = %s
+							and (serial_no = %s
+								or serial_no like %s
+								or serial_no like %s
+								or serial_no like %s
+							)
+					""", (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%'))
+
 					if not serial_no_exists:
 						bold_return_against = frappe.bold(self.return_against)
 						bold_serial_no = frappe.bold(sr)
@@ -190,7 +198,7 @@
 							_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
 							.format(d.idx, bold_serial_no, bold_return_against)
 						)
-	
+
 	def validate_non_stock_items(self):
 		for d in self.get("items"):
 			is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
@@ -292,7 +300,7 @@
 
 		if not self.get('payments') and not for_validate:
 			update_multi_mode_option(self, profile)
-		
+
 		if self.is_return and not for_validate:
 			add_return_modes(self, profile)
 
@@ -317,13 +325,14 @@
 				)
 				customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list')
 				selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list')
+				if customer_currency != profile.get('currency'):
+					self.set('currency', customer_currency)
+
 			else:
 				selling_price_list = profile.get('selling_price_list')
 
 			if selling_price_list:
 				self.set('selling_price_list', selling_price_list)
-			if customer_currency != profile.get('currency'):
-				self.set('currency', customer_currency)
 
 			# set pos values in items
 			for item in self.get("items"):
@@ -383,22 +392,48 @@
 				if not self.contact_mobile:
 					frappe.throw(_("Please enter the phone number first"))
 
-				payment_gateway = frappe.db.get_value("Payment Gateway Account", {
-					"payment_account": pay.account,
-				})
-				record = {
-					"payment_gateway": payment_gateway,
-					"dt": "POS Invoice",
-					"dn": self.name,
-					"payment_request_type": "Inward",
-					"party_type": "Customer",
-					"party": self.customer,
-					"mode_of_payment": pay.mode_of_payment,
-					"recipient_id": self.contact_mobile,
-					"submit_doc": True
-				}
+				pay_req = self.get_existing_payment_request(pay)
+				if not pay_req:
+					pay_req = self.get_new_payment_request(pay)
+					pay_req.submit()
+				else:
+					pay_req.request_phone_payment()
 
-				return make_payment_request(**record)
+				return pay_req
+	
+	def get_new_payment_request(self, mop):
+		payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
+			"payment_account": mop.account,
+		}, ["name"])
+
+		args = {
+			"dt": "POS Invoice",
+			"dn": self.name,
+			"recipient_id": self.contact_mobile,
+			"mode_of_payment": mop.mode_of_payment,
+			"payment_gateway_account": payment_gateway_account,
+			"payment_request_type": "Inward",
+			"party_type": "Customer",
+			"party": self.customer,
+			"return_doc": True
+		}
+		return make_payment_request(**args)
+
+	def get_existing_payment_request(self, pay):
+		payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
+			"payment_account": pay.account,
+		}, ["name"])
+
+		args = {
+			'doctype': 'Payment Request',
+			'reference_doctype': 'POS Invoice',
+			'reference_name': self.name,
+			'payment_gateway_account': payment_gateway_account,
+			'email_to': self.contact_mobile
+		}
+		pr = frappe.db.exists(args)
+		if pr:
+			return frappe.get_doc('Payment Request', pr[0][0])
 
 @frappe.whitelist()
 def get_stock_availability(item_code, warehouse):
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 57a23af..15875af 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -198,6 +198,65 @@
 		self.assertEqual(pos_return.get('payments')[0].amount, -500)
 		self.assertEqual(pos_return.get('payments')[1].amount, -500)
 
+	def test_pos_return_for_serialized_item(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		se = make_serialized_item(company='_Test Company',
+			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+
+		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
+
+		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
+			item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
+
+		pos.get("items")[0].serial_no = serial_nos[0]
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
+
+		pos.insert()
+		pos.submit()
+
+		pos_return = make_sales_return(pos.name)
+
+		pos_return.insert()
+		pos_return.submit()
+		self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0])
+
+	def test_partial_pos_returns(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		se = make_serialized_item(company='_Test Company',
+			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+
+		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
+
+		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
+			item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1)
+
+		pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1]
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
+
+		pos.insert()
+		pos.submit()
+
+		pos_return1 = make_sales_return(pos.name)
+
+		# partial return 1
+		pos_return1.get('items')[0].qty = -1
+		pos_return1.get('items')[0].serial_no = serial_nos[0]
+		pos_return1.insert()
+		pos_return1.submit()
+
+		# partial return 2
+		pos_return2 = make_sales_return(pos.name)
+		self.assertEqual(pos_return2.get('items')[0].qty, -1)
+		self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1])
+
 	def test_pos_change_amount(self):
 		pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC",
 			income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index 2b6e7de..8b71eb0 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -87,6 +87,7 @@
   "edit_references",
   "sales_order",
   "so_detail",
+  "pos_invoice_item",
   "column_break_74",
   "delivery_note",
   "dn_detail",
@@ -790,11 +791,20 @@
    "fieldtype": "Link",
    "label": "Project",
    "options": "Project"
+  },
+  {
+   "fieldname": "pos_invoice_item",
+   "fieldtype": "Data",
+   "ignore_user_permissions": 1,
+   "label": "POS Invoice Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-07-22 13:40:34.418346",
+ "modified": "2021-01-04 17:34:49.924531",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Item",
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 58409cd..40f77b4 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -29,7 +29,7 @@
 		for d in self.pos_invoices:
 			status, docstatus, is_return, return_against = frappe.db.get_value(
 				'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
-			
+
 			bold_pos_invoice = frappe.bold(d.pos_invoice)
 			bold_status = frappe.bold(status)
 			if docstatus != 1:
@@ -58,7 +58,7 @@
 		sales_invoice, credit_note = "", ""
 		if sales:
 			sales_invoice = self.process_merging_into_sales_invoice(sales)
-		
+
 		if returns:
 			credit_note = self.process_merging_into_credit_note(returns)
 
@@ -74,7 +74,7 @@
 
 	def process_merging_into_sales_invoice(self, data):
 		sales_invoice = self.get_new_sales_invoice()
-		
+
 		sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
 
 		sales_invoice.is_consolidated = 1
@@ -98,19 +98,19 @@
 		self.consolidated_credit_note = credit_note.name
 
 		return credit_note.name
-	
+
 	def merge_pos_invoice_into(self, invoice, data):
 		items, payments, taxes = [], [], []
 		loyalty_amount_sum, loyalty_points_sum = 0, 0
 		for doc in data:
 			map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
-			
+
 			if doc.redeem_loyalty_points:
 				invoice.loyalty_redemption_account = doc.loyalty_redemption_account
 				invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
 				loyalty_points_sum += doc.loyalty_points
 				loyalty_amount_sum += doc.loyalty_amount
-			
+
 			for item in doc.get('items'):
 				found = False
 				for i in items:
@@ -118,12 +118,13 @@
 						i.uom == item.uom and i.net_rate == item.net_rate):
 						found = True
 						i.qty = i.qty + item.qty
+
 				if not found:
 					item.rate = item.net_rate
 					item.price_list_rate = 0
 					si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
 					items.append(si_item)
-			
+
 			for tax in doc.get('taxes'):
 				found = False
 				for t in taxes:
@@ -162,7 +163,7 @@
 		invoice.ignore_pricing_rule = 1
 
 		return invoice
-	
+
 	def get_new_sales_invoice(self):
 		sales_invoice = frappe.new_doc('Sales Invoice')
 		sales_invoice.customer = self.customer
@@ -194,7 +195,7 @@
 	}
 	pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
 		fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
-	
+
 	return pos_invoices
 
 def get_invoice_customer_map(pos_invoices):
@@ -204,7 +205,7 @@
 		customer = invoice.get('customer')
 		pos_invoice_customer_map.setdefault(customer, [])
 		pos_invoice_customer_map[customer].append(invoice)
-	
+
 	return pos_invoice_customer_map
 
 def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
@@ -212,8 +213,8 @@
 	invoice_by_customer = get_invoice_customer_map(invoices)
 
 	if len(invoices) >= 5 and closing_entry:
-		enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
 		closing_entry.set_status(update=True, status='Queued')
+		enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
 	else:
 		create_merge_logs(invoice_by_customer, closing_entry)
 
@@ -225,8 +226,8 @@
 	)
 
 	if len(merge_logs) >= 5:
-		enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
 		closing_entry.set_status(update=True, status='Queued')
+		enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
 	else:
 		cancel_merge_logs(merge_logs, closing_entry)
 
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 1f7853d..07e75ac 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -40,6 +40,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_22",
   "net_rate",
@@ -784,6 +785,14 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
    "fieldname": "sales_invoice_item",
    "fieldtype": "Data",
    "label": "Sales Invoice Item",
@@ -795,7 +804,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-26 17:20:36.415791",
+ "modified": "2021-01-30 21:43:21.488258",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 9599d4e..4217711 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -21,6 +21,7 @@
 from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
 	get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
 from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 from frappe.model.utils import get_fetch_values
 from frappe.contacts.doctype.address.address import get_address_display
 
@@ -75,6 +76,8 @@
 
 		if not self.is_pos:
 			self.so_dn_required()
+		
+		self.set_tax_withholding()
 
 		self.validate_proj_cust()
 		self.validate_pos_return()
@@ -153,6 +156,32 @@
 			if cost_center_company != self.company:
 				frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
 
+	def set_tax_withholding(self):
+		tax_withholding_details = get_party_tax_withholding_details(self)
+
+		if not tax_withholding_details:
+			return
+
+		accounts = []
+		tax_withholding_account = tax_withholding_details.get("account_head")
+
+		for d in self.taxes:
+			if d.account_head == tax_withholding_account:
+				d.update(tax_withholding_details)
+			accounts.append(d.account_head)
+
+		if not accounts or tax_withholding_account not in accounts:
+			self.append("taxes", tax_withholding_details)
+
+		to_remove = [d for d in self.taxes
+			if not d.tax_amount and d.charge_type == "Actual" and d.account_head == tax_withholding_account]
+
+		for d in to_remove:
+			self.remove(d)
+
+		# calculate totals again after applying TDS
+		self.calculate_taxes_and_totals()
+
 	def before_save(self):
 		set_account_for_mode_of_payment(self)
 
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 7a98aff..b403c7b 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -45,6 +45,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_21",
   "net_rate",
@@ -811,12 +812,20 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-26 17:25:04.090630",
+ "modified": "2021-01-30 21:42:37.796771",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 32ad4cb..961bdb1 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -12,37 +12,62 @@
 class TaxWithholdingCategory(Document):
 	pass
 
-def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
+def get_party_details(inv):
+	party_type, party = '', ''
 
+	if inv.doctype == 'Sales Invoice':
+		party_type = 'Customer'
+		party = inv.customer
+	else:
+		party_type = 'Supplier'
+		party = inv.supplier
+	
+	return party_type, party
+
+def get_party_tax_withholding_details(inv, tax_withholding_category=None):
 	pan_no = ''
-	suppliers = []
+	parties = []
+	party_type, party = get_party_details(inv)
 
 	if not tax_withholding_category:
-		tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
+		tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan'])
 
 	if not tax_withholding_category:
 		return
 
+	# if tax_withholding_category passed as an argument but not pan_no
 	if not pan_no:
-		pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
+		pan_no = frappe.db.get_value(party_type, party, 'pan')
 
 	# Get others suppliers with the same PAN No
 	if pan_no:
-		suppliers = [d.name for d in  frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
+		parties = frappe.get_all(party_type, filters={ 'pan': pan_no }, pluck='name')
 
-	if not suppliers:
-		suppliers.append(ref_doc.supplier)
+	if not parties:
+		parties.append(party)
 
-	fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
-	tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
+	fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company)
+	tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
+
 	if not tax_details:
 		frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
-			.format(tax_withholding_category, ref_doc.company))
+			.format(tax_withholding_category, inv.company))
 
-	tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
-		tax_details, fy,  ref_doc.posting_date, pan_no)
+	if party_type == 'Customer' and not tax_details.cumulative_threshold:
+		# TCS is only chargeable on sum of invoiced value
+		frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.')
+			.format(tax_withholding_category, inv.company, party))
 
-	tax_row = get_tax_row(tax_details, tds_amount)
+	tax_amount, tax_deducted = get_tax_amount(
+		party_type, parties,
+		inv, tax_details,
+		fiscal_year, pan_no
+	)
+
+	if party_type == 'Supplier':
+		tax_row = get_tax_row_for_tds(tax_details, tax_amount)
+	else:
+		tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
 
 	return tax_row
 
@@ -69,147 +94,254 @@
 
 	frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
 
-def get_tax_row(tax_details, tds_amount):
-
-	return {
+def get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted):
+	row = {
 		"category": "Total",
-		"add_deduct_tax": "Deduct",
 		"charge_type": "Actual",
-		"account_head": tax_details.account_head,
+		"tax_amount": tax_amount,
 		"description": tax_details.description,
-		"tax_amount": tds_amount
+		"account_head": tax_details.account_head
 	}
 
-def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
-	fiscal_year, year_start_date, year_end_date = fiscal_year_details
-	tds_amount = 0
-	tds_deducted = 0
+	if tax_deducted:
+		# TCS already deducted on previous invoices
+		# So, TCS will be calculated by 'Previous Row Total'
 
-	def _get_tds(amount, rate):
-		if amount <= 0:
-			return 0
-
-		return amount * rate / 100
-
-	ldc_name = frappe.db.get_value('Lower Deduction Certificate',
-		{
-			'pan_no': pan_no,
-			'fiscal_year': fiscal_year
-		}, 'name')
-	ldc = ''
-
-	if ldc_name:
-		ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
-
-	entries = frappe.db.sql("""
-			select voucher_no, credit
-			from `tabGL Entry`
-			where company = %s and
-			party in %s and fiscal_year=%s and credit > 0
-			and is_opening = 'No'
-		""", (company, tuple(suppliers), fiscal_year), as_dict=1)
-
-	vouchers = [d.voucher_no for d in entries]
-	advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
-
-	tds_vouchers = vouchers + advance_vouchers
-
-	if tds_vouchers:
-		tds_deducted = frappe.db.sql("""
-			SELECT sum(credit) FROM `tabGL Entry`
-			WHERE
-				account=%s and fiscal_year=%s and credit > 0
-				and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))),
-				((tax_details.account_head, fiscal_year) + tuple(tds_vouchers)))
-
-		tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
-
-	if tds_deducted:
-		if ldc:
-			limit_consumed = frappe.db.get_value('Purchase Invoice',
-				{
-					'supplier': ('in', suppliers),
-					'apply_tds': 1,
-					'docstatus': 1
-				}, 'sum(net_total)')
-
-		if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
-			ldc.certificate_limit):
-
-			tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+		taxes_excluding_tcs = [d for d in inv.taxes if d.account_head != tax_details.account_head]
+		if taxes_excluding_tcs:
+			# chargeable amount is the total amount after other charges are applied
+			row.update({
+				"charge_type": "On Previous Row Total",
+				"row_id": len(taxes_excluding_tcs),
+				"rate": tax_details.rate
+			})
 		else:
-			tds_amount = _get_tds(net_total, tax_details.rate)
-	else:
-		supplier_credit_amount = frappe.get_all('Purchase Invoice',
-			fields = ['sum(net_total)'],
-			filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1)
+			# if only TCS is to be charged, then net total is chargeable amount
+			row.update({
+				"charge_type": "On Net Total",
+				"rate": tax_details.rate
+			})
 
-		supplier_credit_amount = (supplier_credit_amount[0][0]
-			if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
+	return row
 
-		jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
-			fields = ['sum(credit_in_account_currency)'],
-			filters = {
-				'parent': ('in', vouchers), 'docstatus': 1,
-				'party': ('in', suppliers),
-				'reference_type': ('not in', ['Purchase Invoice'])
-			}, as_list=1)
+def get_tax_row_for_tds(tax_details, tax_amount):
+	return {
+		"category": "Total",
+		"charge_type": "Actual",
+		"tax_amount": tax_amount,
+		"add_deduct_tax": "Deduct",
+		"description": tax_details.description,
+		"account_head": tax_details.account_head
+	}
 
-		supplier_credit_amount += (jv_supplier_credit_amt[0][0]
-			if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
+def get_lower_deduction_certificate(fiscal_year, pan_no):
+	ldc_name = frappe.db.get_value('Lower Deduction Certificate', { 'pan_no': pan_no, 'fiscal_year': fiscal_year }, 'name')
+	if ldc_name:
+		return frappe.get_doc('Lower Deduction Certificate', ldc_name)
 
-		supplier_credit_amount += net_total
+def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
+	fiscal_year = fiscal_year_details[0]
 
-		debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
-		supplier_credit_amount -= debit_note_amount
+	vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+	advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
+	taxable_vouchers = vouchers + advance_vouchers
 
-		if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
-			or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
+	tax_deducted = 0
+	if taxable_vouchers:
+		tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
 
-			if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
-				ldc.certificate_limit):
-				tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
-					tax_details)
+	tax_amount = 0
+	posting_date = inv.posting_date
+	if party_type == 'Supplier':
+		ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
+		if tax_deducted:
+			net_total = inv.net_total
+			if ldc:
+				tax_amount = get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total)
 			else:
-				tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
+				tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
+		else:
+			tax_amount = get_tds_amount(
+				ldc, parties, inv, tax_details,
+				fiscal_year_details, tax_deducted, vouchers
+			)
+
+	elif party_type == 'Customer':
+		if tax_deducted:
+			# if already TCS is charged, then amount will be calculated based on 'Previous Row Total'
+			tax_amount = 0
+		else:
+			#  if no TCS has been charged in FY,
+			# then chargeable value is "prev invoices + advances" value which cross the threshold
+			tax_amount = get_tcs_amount(
+				parties, inv, tax_details,
+				fiscal_year_details, vouchers, advance_vouchers
+			)
+
+	return tax_amount, tax_deducted
+
+def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'):
+	dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
+
+	filters = {
+		dr_or_cr: ['>', 0],
+		'company': company,
+		'party_type': party_type,
+		'party': ['in', parties],
+		'fiscal_year': fiscal_year,
+		'is_opening': 'No',
+		'is_cancelled': 0
+	}
+
+	return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
+
+def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None, to_date=None, party_type='Supplier'):
+	# for advance vouchers, debit and credit is reversed
+	dr_or_cr = 'debit' if party_type == 'Supplier' else 'credit'
+
+	filters = {
+		dr_or_cr: ['>', 0],
+		'is_opening': 'No',
+		'is_cancelled': 0,
+		'party_type': party_type,
+		'party': ['in', parties],
+		'against_voucher': ['is', 'not set']
+	}
+
+	if fiscal_year:
+		filters['fiscal_year'] = fiscal_year
+	if company:
+		filters['company'] = company
+	if from_date and to_date:
+		filters['posting_date'] = ['between', (from_date, to_date)]
+
+	return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""]
+
+def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
+	# check if TDS / TCS account is already charged on taxable vouchers
+	filters = {
+		'is_cancelled': 0,
+		'credit': ['>', 0],
+		'fiscal_year': fiscal_year,
+		'account': tax_details.account_head,
+		'voucher_no': ['in', taxable_vouchers],
+	}
+	field = "sum(credit)"
+
+	return frappe.db.get_value('GL Entry', filters, field) or 0.0
+
+def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
+	tds_amount = 0
+
+	supp_credit_amt = frappe.db.get_value('Purchase Invoice', {
+		'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1
+	}, 'sum(net_total)') or 0.0
+
+	supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
+		'parent': ('in', vouchers), 'docstatus': 1,
+		'party': ('in', parties), 'reference_type': ('!=', 'Purchase Invoice')
+	}, 'sum(credit_in_account_currency)') or 0.0
+
+	supp_credit_amt += supp_jv_credit_amt
+	supp_credit_amt += inv.net_total
+
+	debit_note_amount = get_debit_note_amount(parties, fiscal_year_details, inv.company)
+	supp_credit_amt -= debit_note_amount
+
+	threshold = tax_details.get('threshold', 0)
+	cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+
+	if ((threshold and supp_credit_amt >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
+		if ldc and is_valid_certificate(
+			ldc.valid_from, ldc.valid_upto,
+			inv.posting_date, tax_deducted,
+			inv.net_total, ldc.certificate_limit
+		):
+			tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
+		else:
+			tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
 
 	return tds_amount
 
-def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
-	condition = "fiscal_year=%s" % fiscal_year
+def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
+	tcs_amount = 0
+	fiscal_year, _, _ = fiscal_year_details
+
+	# sum of debit entries made from sales invoices
+	invoiced_amt = frappe.db.get_value('GL Entry', {
+		'is_cancelled': 0,
+		'party': ['in', parties],
+		'company': inv.company,
+		'voucher_no': ['in', vouchers],
+	}, 'sum(debit)') or 0.0
+
+	# sum of credit entries made from PE / JV with unset 'against voucher'
+	advance_amt = frappe.db.get_value('GL Entry', {
+		'is_cancelled': 0,
+		'party': ['in', parties],
+		'company': inv.company,
+		'voucher_no': ['in', adv_vouchers],
+	}, 'sum(credit)') or 0.0
+
+	# sum of credit entries made from sales invoice
+	credit_note_amt = frappe.db.get_value('GL Entry', {
+		'is_cancelled': 0,
+		'credit': ['>', 0],
+		'party': ['in', parties],
+		'fiscal_year': fiscal_year,
+		'company': inv.company,
+		'voucher_type': 'Sales Invoice',
+	}, 'sum(credit)') or 0.0
+
+	cumulative_threshold = tax_details.get('cumulative_threshold', 0)
+
+	current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
+	total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
+
+	if ((cumulative_threshold and total_invoiced_amt >= cumulative_threshold)):
+		chargeable_amt = total_invoiced_amt - cumulative_threshold
+		tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0
+
+	return tcs_amount
+
+def get_invoice_total_without_tcs(inv, tax_details):
+	tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head]
+	tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0
+
+	return inv.grand_total - tcs_tax_row_amount
+
+def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total):
+	tds_amount = 0
+	limit_consumed = frappe.db.get_value('Purchase Invoice', {
+		'supplier': ('in', parties),
+		'apply_tds': 1,
+		'docstatus': 1
+	}, 'sum(net_total)')
+
+	if is_valid_certificate(
+		ldc.valid_from, ldc.valid_upto,
+		posting_date, limit_consumed,
+		net_total, ldc.certificate_limit
+	):
+		tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
+	
+	return tds_amount
+
+def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
+	_, year_start_date, year_end_date = fiscal_year_details
+
+	filters = {
+		'supplier': ['in', suppliers],
+		'is_return': 1,
+		'docstatus': 1,
+		'posting_date': ['between', (year_start_date, year_end_date)]
+	}
+	fields = ['abs(sum(net_total)) as net_total']
 
 	if company:
-		condition += "and company =%s" % (company)
-	if from_date and to_date:
-		condition += "and posting_date between %s and %s" % (from_date, to_date)
+		filters['company'] = company
 
-	## Appending the same supplier again if length of suppliers list is 1
-	## since tuple of single element list contains None, For example ('Test Supplier 1', )
-	## and the below query fails
-	if len(suppliers) == 1:
-		suppliers.append(suppliers[0])
-
-	return frappe.db.sql_list("""
-		select distinct voucher_no
-		from `tabGL Entry`
-		where party in %s and %s and debit > 0
-		and is_opening = 'No'
-	""", (tuple(suppliers), condition)) or []
-
-def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
-	condition = "and 1=1"
-	if company:
-		condition = " and company=%s " % company
-
-	if len(suppliers) == 1:
-		suppliers.append(suppliers[0])
-
-	return flt(frappe.db.sql("""
-		select abs(sum(net_total))
-		from `tabPurchase Invoice`
-		where supplier in %s and is_return=1 and docstatus=1
-			and posting_date between %s and %s %s
-	""", (tuple(suppliers), year_start_date, year_end_date, condition)))
+	return frappe.get_all('Purchase Invoice', filters, fields)[0].get('net_total') or 0.0
 
 def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
 	if current_amount < (certificate_limit - deducted_amount):
@@ -227,4 +359,4 @@
 			certificate_limit > deducted_amount):
 		valid = True
 
-	return valid
\ No newline at end of file
+	return valid
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index ef77674..9ce8e3f 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -9,7 +9,7 @@
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.buying.doctype.supplier.test_supplier import create_supplier
 
-test_dependencies = ["Supplier Group"]
+test_dependencies = ["Supplier Group", "Customer Group"]
 
 class TestTaxWithholdingCategory(unittest.TestCase):
 	@classmethod
@@ -18,6 +18,9 @@
 		create_records()
 		create_tax_with_holding_category()
 
+	def tearDown(self):
+		cancel_invoices()
+
 	def test_cumulative_threshold_tds(self):
 		frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
 		invoices = []
@@ -128,9 +131,59 @@
 		for d in invoices:
 			d.cancel()
 
+	def test_cumulative_threshold_tcs(self):
+		frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
+		invoices = []
+
+		# create invoices for lower than single threshold tax rate
+		for _ in range(2):
+			si = create_sales_invoice(customer = "Test TCS Customer")
+			si.submit()
+			invoices.append(si)
+
+		# create another invoice whose total when added to previously created invoice,
+		# surpasses cumulative threshhold
+		si = create_sales_invoice(customer = "Test TCS Customer", rate=12000)
+		si.submit()
+
+		# assert tax collection on total invoice amount created until now
+		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+		self.assertEqual(tcs_charged, 200)
+		self.assertEqual(si.grand_total, 12200)
+		invoices.append(si)
+
+		# TCS is already collected once, so going forward system will collect TCS on every invoice
+		si = create_sales_invoice(customer = "Test TCS Customer", rate=5000)
+		si.submit()
+
+		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
+		self.assertEqual(tcs_charged, 500)
+		invoices.append(si)
+
+		#delete invoices to avoid clashing
+		for d in invoices:
+			d.cancel()
+
+def cancel_invoices():
+	purchase_invoices = frappe.get_all("Purchase Invoice", {
+		'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
+		'docstatus': 1
+	}, pluck="name")
+
+	sales_invoices = frappe.get_all("Sales Invoice", {
+		'customer': 'Test TCS Customer',
+		'docstatus': 1
+	}, pluck="name")
+
+	for d in purchase_invoices:
+		frappe.get_doc('Purchase Invoice', d).cancel()
+	
+	for d in sales_invoices:
+		frappe.get_doc('Sales Invoice', d).cancel()
+
 def create_purchase_invoice(**args):
 	# return sales invoice doc object
-	item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
+	item = frappe.db.get_value('Item', {'item_name': 'TDS Item'}, "name")
 
 	args = frappe._dict(args)
 	pi = frappe.get_doc({
@@ -145,7 +198,7 @@
 		"taxes": [],
 		"items": [{
 			'doctype': 'Purchase Invoice Item',
-			'item_code': item.name,
+			'item_code': item,
 			'qty': args.qty or 1,
 			'rate': args.rate or 10000,
 			'cost_center': 'Main - _TC',
@@ -156,6 +209,33 @@
 	pi.save()
 	return pi
 
+def create_sales_invoice(**args):
+	# return sales invoice doc object
+	item = frappe.db.get_value('Item', {'item_name': 'TCS Item'}, "name")
+
+	args = frappe._dict(args)
+	si = frappe.get_doc({
+		"doctype": "Sales Invoice",
+		"posting_date": today(),
+		"customer": args.customer,
+		"company": '_Test Company',
+		"taxes_and_charges": "",
+		"currency": "INR",
+		"debit_to": "Debtors - _TC",
+		"taxes": [],
+		"items": [{
+			'doctype': 'Sales Invoice Item',
+			'item_code': item,
+			'qty': args.qty or 1,
+			'rate': args.rate or 10000,
+			'cost_center': 'Main - _TC',
+			'expense_account': 'Cost of Goods Sold - _TC'
+		}]
+	})
+
+	si.save()
+	return si
+
 def create_records():
 	# create a new suppliers
 	for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
@@ -168,7 +248,17 @@
 			"doctype": "Supplier",
 		}).insert()
 
-	# create an item
+	for name in ['Test TCS Customer']:
+		if frappe.db.exists('Customer', name):
+			continue
+
+		frappe.get_doc({
+			"customer_group": "_Test Customer Group",
+			"customer_name": name,
+			"doctype": "Customer"
+		}).insert()
+
+	# create item
 	if not frappe.db.exists('Item', "TDS Item"):
 		frappe.get_doc({
 			"doctype": "Item",
@@ -178,7 +268,16 @@
 			"is_stock_item": 0,
 		}).insert()
 
-	# create an account
+	if not frappe.db.exists('Item', "TCS Item"):
+		frappe.get_doc({
+			"doctype": "Item",
+			"item_code": "TCS Item",
+			"item_name": "TCS Item",
+			"item_group": "All Item Groups",
+			"is_stock_item": 1
+		}).insert()
+
+	# create tds account
 	if not frappe.db.exists("Account", "TDS - _TC"):
 		frappe.get_doc({
 			'doctype': 'Account',
@@ -189,6 +288,17 @@
 			'root_type': 'Asset'
 		}).insert()
 
+	# create tcs account
+	if not frappe.db.exists("Account", "TCS - _TC"):
+		frappe.get_doc({
+			'doctype': 'Account',
+			'company': '_Test Company',
+			'account_name': 'TCS',
+			'parent_account': 'Duties and Taxes - _TC',
+			'report_type': 'Balance Sheet',
+			'root_type': 'Liability'
+		}).insert()
+
 def create_tax_with_holding_category():
 	fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
 
@@ -210,6 +320,23 @@
 			}]
 		}).insert()
 
+	if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
+		frappe.get_doc({
+			"doctype": "Tax Withholding Category",
+			"name": "Cumulative Threshold TCS",
+			"category_name": "10% TCS",
+			"rates": [{
+				'fiscal_year': fiscal_year,
+				'tax_withholding_rate': 10,
+				'single_threshold': 0,
+				'cumulative_threshold': 30000.00
+			}],
+			"accounts": [{
+				'company': '_Test Company',
+				'account': 'TCS - _TC'
+			}]
+		}).insert()
+
 	# Single thresold
 	if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
 		frappe.get_doc({
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 287c79f..dac0c21 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -44,9 +44,9 @@
 		frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
 			.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
 
-def process_gl_map(gl_map, merge_entries=True):
+def process_gl_map(gl_map, merge_entries=True, precision=None):
 	if merge_entries:
-		gl_map = merge_similar_entries(gl_map)
+		gl_map = merge_similar_entries(gl_map, precision)
 	for entry in gl_map:
 		# toggle debit, credit if negative entry
 		if flt(entry.debit) < 0:
@@ -69,7 +69,7 @@
 
 	return gl_map
 
-def merge_similar_entries(gl_map):
+def merge_similar_entries(gl_map, precision=None):
 	merged_gl_map = []
 	accounting_dimensions = get_accounting_dimensions()
 	for entry in gl_map:
@@ -88,7 +88,9 @@
 
 	company = gl_map[0].company if gl_map else erpnext.get_default_company()
 	company_currency = erpnext.get_company_currency(company)
-	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
+
+	if not precision:
+		precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
 
 	# filter zero debit and credit entries
 	merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
@@ -132,8 +134,8 @@
 	gle.update(args)
 	gle.flags.ignore_permissions = 1
 	gle.flags.from_repost = from_repost
-	gle.insert()
-	gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
+	gle.flags.adv_adj = adv_adj
+	gle.flags.update_outstanding = update_outstanding or 'Yes'
 	gle.submit()
 
 	if not from_repost:
@@ -194,7 +196,7 @@
 
 	if not round_off_gle:
 		for k in ["voucher_type", "voucher_no", "company",
-			"posting_date", "remarks", "is_opening"]:
+			"posting_date", "remarks"]:
 				round_off_gle[k] = gl_map[0][k]
 
 	round_off_gle.update({
@@ -206,6 +208,7 @@
 		"cost_center": round_off_cost_center,
 		"party_type": None,
 		"party": None,
+		"is_opening": "No",
 		"against_voucher_type": None,
 		"against_voucher": None
 	})
diff --git a/erpnext/accounts/page/bank_reconciliation/__init__.py b/erpnext/accounts/page/bank_reconciliation/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/accounts/page/bank_reconciliation/__init__.py
+++ /dev/null
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
deleted file mode 100644
index 6ae81d7..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ /dev/null
@@ -1,583 +0,0 @@
-frappe.provide("erpnext.accounts");
-
-frappe.pages['bank-reconciliation'].on_page_load = function(wrapper) {
-	new erpnext.accounts.bankReconciliation(wrapper);
-}
-
-erpnext.accounts.bankReconciliation = class BankReconciliation {
-	constructor(wrapper) {
-		this.page = frappe.ui.make_app_page({
-			parent: wrapper,
-			title: __("Bank Reconciliation"),
-			single_column: true
-		});
-		this.parent = wrapper;
-		this.page = this.parent.page;
-
-		this.check_plaid_status();
-		this.make();
-	}
-
-	make() {
-		const me = this;
-
-		me.$main_section = $(`<div class="reconciliation page-main-content"></div>`).appendTo(me.page.main);
-		const empty_state = __("Upload a bank statement, link or reconcile a bank account")
-		me.$main_section.append(`<div class="flex justify-center align-center text-muted"
-			style="height: 50vh; display: flex;"><h5 class="text-muted">${empty_state}</h5></div>`)
-
-		me.page.add_field({
-			fieldtype: 'Link',
-			label: __('Company'),
-			fieldname: 'company',
-			options: "Company",
-			onchange: function() {
-				if (this.value) {
-					me.company = this.value;
-				} else {
-					me.company = null;
-					me.bank_account = null;
-				}
-			}
-		})
-		me.page.add_field({
-			fieldtype: 'Link',
-			label: __('Bank Account'),
-			fieldname: 'bank_account',
-			options: "Bank Account",
-			get_query: function() {
-				if(!me.company) {
-					frappe.throw(__("Please select company first"));
-					return
-				}
-
-				return {
-					filters: {
-						"company": me.company
-					}
-				}
-			},
-			onchange: function() {
-				if (this.value) {
-					me.bank_account = this.value;
-					me.add_actions();
-				} else {
-					me.bank_account = null;
-					me.page.hide_actions_menu();
-				}
-			}
-		})
-	}
-
-	check_plaid_status() {
-		const me = this;
-		frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
-			if (r && r.enabled === "1") {
-				me.plaid_status = "active"
-			} else {
-				me.plaid_status = "inactive"
-			}
-		})
-	}
-
-	add_actions() {
-		const me = this;
-
-		me.page.show_menu()
-
-		me.page.add_menu_item(__("Upload a statement"), function() {
-			me.clear_page_content();
-			new erpnext.accounts.bankTransactionUpload(me);
-		}, true)
-
-		if (me.plaid_status==="active") {
-			me.page.add_menu_item(__("Synchronize this account"), function() {
-				me.clear_page_content();
-				new erpnext.accounts.bankTransactionSync(me);
-			}, true)
-		}
-
-		me.page.add_menu_item(__("Reconcile this account"), function() {
-			me.clear_page_content();
-			me.make_reconciliation_tool();
-		}, true)
-	}
-
-	clear_page_content() {
-		const me = this;
-		$(me.page.body).find('.frappe-list').remove();
-		me.$main_section.empty();
-	}
-
-	make_reconciliation_tool() {
-		const me = this;
-		frappe.model.with_doctype("Bank Transaction", () => {
-			erpnext.accounts.ReconciliationList = new erpnext.accounts.ReconciliationTool({
-				parent: me.parent,
-				doctype: "Bank Transaction"
-			});
-		})
-	}
-}
-
-
-erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
-	constructor(parent) {
-		this.parent = parent;
-		this.data = [];
-
-		const assets = [
-			"/assets/frappe/css/frappe-datatable.css",
-			"/assets/frappe/js/lib/clusterize.min.js",
-			"/assets/frappe/js/lib/Sortable.min.js",
-			"/assets/frappe/js/lib/frappe-datatable.js"
-		];
-
-		frappe.require(assets, () => {
-			this.make();
-		});
-	}
-
-	make() {
-		const me = this;
-		new frappe.ui.FileUploader({
-			method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
-			allow_multiple: 0,
-			on_success: function(attachment, r) {
-				if (!r.exc && r.message) {
-					me.data = r.message;
-					me.setup_transactions_dom();
-					me.create_datatable();
-					me.add_primary_action();
-				}
-			}
-		})
-	}
-
-	setup_transactions_dom() {
-		const me = this;
-		me.parent.$main_section.append('<div class="transactions-table"></div>');
-	}
-
-	create_datatable() {
-		try {
-			this.datatable = new DataTable('.transactions-table', {
-				columns: this.data.columns,
-				data: this.data.data
-			})
-		}
-		catch(err) {
-			let msg = __("Your file could not be processed. It should be a standard CSV or XLSX file with headers in the first row.");
-			frappe.throw(msg)
-		}
-
-	}
-
-	add_primary_action() {
-		const me = this;
-		me.parent.page.set_primary_action(__("Submit"), function() {
-			me.add_bank_entries()
-		}, null, __("Creating bank entries..."))
-	}
-
-	add_bank_entries() {
-		const me = this;
-		frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries',
-			{columns: this.datatable.datamanager.columns, data: this.datatable.datamanager.data, bank_account: me.parent.bank_account}
-		).then((result) => {
-			let result_title = result.errors == 0 ? __("{0} bank transaction(s) created", [result.success]) : __("{0} bank transaction(s) created and {1} errors", [result.success, result.errors])
-			let result_msg = `
-				<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
-					<h5 class="text-muted">${result_title}</h5>
-				</div>`
-			me.parent.page.clear_primary_action();
-			me.parent.$main_section.empty();
-			me.parent.$main_section.append(result_msg);
-			if (result.errors == 0) {
-				frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'});
-			} else {
-				frappe.show_alert({message:__("Please check the error log for details about the import errors"), indicator:'red'});
-			}
-		})
-	}
-}
-
-erpnext.accounts.bankTransactionSync = class bankTransactionSync {
-	constructor(parent) {
-		this.parent = parent;
-		this.data = [];
-
-		this.init_config()
-	}
-
-	init_config() {
-		const me = this;
-		frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
-			.then(result => {
-				me.plaid_env = result.plaid_env;
-				me.client_name = result.client_name;
-				me.link_token = result.link_token;
-				me.sync_transactions();
-			})
-	}
-
-	sync_transactions() {
-		const me = this;
-		frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => {
-			frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
-				bank: r.bank,
-				bank_account: me.parent.bank_account,
-				freeze: true
-			})
-			.then((result) => {
-				let result_title = (result && result.length > 0)
-					? __("{0} bank transaction(s) created", [result.length])
-					: __("This bank account is already synchronized");
-
-				let result_msg = `
-				<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
-					<h5 class="text-muted">${result_title}</h5>
-				</div>`
-
-				this.parent.$main_section.append(result_msg)
-				frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
-			})
-		})
-	}
-}
-
-
-erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.views.BaseList {
-	constructor(opts) {
-		super(opts);
-		this.show();
-	}
-
-	setup_defaults() {
-		super.setup_defaults();
-
-		this.page_title = __("Bank Reconciliation");
-		this.doctype = 'Bank Transaction';
-		this.fields = ['date', 'description', 'debit', 'credit', 'currency']
-
-	}
-
-	setup_view() {
-		this.render_header();
-	}
-
-	setup_side_bar() {
-		//
-	}
-
-	make_standard_filters() {
-		//
-	}
-
-	freeze() {
-		this.$result.find('.list-count').html(`<span>${__('Refreshing')}...</span>`);
-	}
-
-	get_args() {
-		const args = super.get_args();
-
-		return Object.assign({}, args, {
-			...args.filters.push(["Bank Transaction", "docstatus", "=", 1],
-				["Bank Transaction", "unallocated_amount", ">", 0])
-		});
-
-	}
-
-	update_data(r) {
-		let data = r.message || [];
-
-		if (this.start === 0) {
-			this.data = data;
-		} else {
-			this.data = this.data.concat(data);
-		}
-	}
-
-	render() {
-		const me = this;
-		this.$result.find('.list-row-container').remove();
-		$('[data-fieldname="name"]').remove();
-		me.data.map((value) => {
-			const row = $('<div class="list-row-container">').data("data", value).appendTo(me.$result).get(0);
-			new erpnext.accounts.ReconciliationRow(row, value);
-		})
-	}
-
-	render_header() {
-		const me = this;
-		if ($(this.wrapper).find('.transaction-header').length === 0) {
-			me.$result.append(frappe.render_template("bank_transaction_header"));
-		}
-	}
-}
-
-erpnext.accounts.ReconciliationRow = class ReconciliationRow {
-	constructor(row, data) {
-		this.data = data;
-		this.row = row;
-		this.make();
-		this.bind_events();
-	}
-
-	make() {
-		$(this.row).append(frappe.render_template("bank_transaction_row", this.data))
-	}
-
-	bind_events() {
-		const me = this;
-		$(me.row).on('click', '.clickable-section', function() {
-			me.bank_entry = $(this).attr("data-name");
-			me.show_dialog($(this).attr("data-name"));
-		})
-
-		$(me.row).on('click', '.new-reconciliation', function() {
-			me.bank_entry = $(this).attr("data-name");
-			me.show_dialog($(this).attr("data-name"));
-		})
-
-		$(me.row).on('click', '.new-payment', function() {
-			me.bank_entry = $(this).attr("data-name");
-			me.new_payment();
-		})
-
-		$(me.row).on('click', '.new-invoice', function() {
-			me.bank_entry = $(this).attr("data-name");
-			me.new_invoice();
-		})
-
-		$(me.row).on('click', '.new-expense', function() {
-			me.bank_entry = $(this).attr("data-name");
-			me.new_expense();
-		})
-	}
-
-	new_payment() {
-		const me = this;
-		const paid_amount = me.data.credit > 0 ? me.data.credit : me.data.debit;
-		const payment_type = me.data.credit > 0 ? "Receive": "Pay";
-		const party_type = me.data.credit > 0 ? "Customer": "Supplier";
-
-		frappe.new_doc("Payment Entry", {"payment_type": payment_type, "paid_amount": paid_amount,
-			"party_type": party_type, "paid_from": me.data.bank_account})
-	}
-
-	new_invoice() {
-		const me = this;
-		const invoice_type = me.data.credit > 0 ? "Sales Invoice" : "Purchase Invoice";
-
-		frappe.new_doc(invoice_type)
-	}
-
-	new_expense() {
-		frappe.new_doc("Expense Claim")
-	}
-
-
-	show_dialog(data) {
-		const me = this;
-
-		frappe.db.get_value("Bank Account", me.data.bank_account, "account", (r) => {
-			me.gl_account = r.account;
-		})
-
-		frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
-			{ bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
-		).then((result) => {
-			me.make_dialog(result)
-		})
-	}
-
-	make_dialog(data) {
-		const me = this;
-		me.selected_payment = null;
-
-		const fields = [
-			{
-				fieldtype: 'Section Break',
-				fieldname: 'section_break_1',
-				label: __('Automatic Reconciliation')
-			},
-			{
-				fieldtype: 'HTML',
-				fieldname: 'payment_proposals'
-			},
-			{
-				fieldtype: 'Section Break',
-				fieldname: 'section_break_2',
-				label: __('Search for a payment')
-			},
-			{
-				fieldtype: 'Link',
-				fieldname: 'payment_doctype',
-				options: 'DocType',
-				label: 'Payment DocType',
-				get_query: () => {
-					return {
-						filters : {
-							"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
-						}
-					}
-				},
-			},
-			{
-				fieldtype: 'Column Break',
-				fieldname: 'column_break_1',
-			},
-			{
-				fieldtype: 'Dynamic Link',
-				fieldname: 'payment_entry',
-				options: 'payment_doctype',
-				label: 'Payment Document',
-				get_query: () => {
-					let dt = this.dialog.fields_dict.payment_doctype.value;
-					if (dt === "Payment Entry") {
-						return {
-							query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.payment_entry_query",
-							filters : {
-								"bank_account": this.data.bank_account,
-								"company": this.data.company
-							}
-						}
-					} else if (dt === "Journal Entry") {
-						return {
-							query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.journal_entry_query",
-							filters : {
-								"bank_account": this.data.bank_account,
-								"company": this.data.company
-							}
-						}
-					} else if (dt === "Sales Invoice") {
-						return {
-							query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.sales_invoices_query"
-						}
-					} else if (dt === "Purchase Invoice") {
-						return {
-							filters : [
-								["Purchase Invoice", "ifnull(clearance_date, '')", "=", ""],
-								["Purchase Invoice", "docstatus", "=", 1],
-								["Purchase Invoice", "company", "=", this.data.company]
-							]
-						}
-					} else if (dt === "Expense Claim") {
-						return {
-							filters : [
-								["Expense Claim", "ifnull(clearance_date, '')", "=", ""],
-								["Expense Claim", "docstatus", "=", 1],
-								["Expense Claim", "company", "=", this.data.company]
-							]
-						}
-					}
-				},
-				onchange: function() {
-					if (me.selected_payment !== this.value) {
-						me.selected_payment = this.value;
-						me.display_payment_details(this);
-					}
-				}
-			},
-			{
-				fieldtype: 'Section Break',
-				fieldname: 'section_break_3'
-			},
-			{
-				fieldtype: 'HTML',
-				fieldname: 'payment_details'
-			},
-		];
-
-		me.dialog = new frappe.ui.Dialog({
-			title: __("Choose a corresponding payment"),
-			fields: fields,
-			size: "large"
-		});
-
-		const proposals_wrapper = me.dialog.fields_dict.payment_proposals.$wrapper;
-		if (data && data.length > 0) {
-			proposals_wrapper.append(frappe.render_template("linked_payment_header"));
-			data.map(value => {
-				proposals_wrapper.append(frappe.render_template("linked_payment_row", value))
-			})
-		} else {
-			const empty_data_msg = __("ERPNext could not find any matching payment entry")
-			proposals_wrapper.append(`<div class="text-center"><h5 class="text-muted">${empty_data_msg}</h5></div>`)
-		}
-
-		$(me.dialog.body).on('click', '.reconciliation-btn', (e) => {
-			const payment_entry = $(e.target).attr('data-name');
-			const payment_doctype = $(e.target).attr('data-doctype');
-			frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile',
-				{bank_transaction: me.bank_entry, payment_doctype: payment_doctype, payment_name: payment_entry})
-			.then((result) => {
-				setTimeout(function(){
-					erpnext.accounts.ReconciliationList.refresh();
-				}, 2000);
-				me.dialog.hide();
-			})
-		})
-
-		me.dialog.show();
-	}
-
-	display_payment_details(event) {
-		const me = this;
-		if (event.value) {
-			let dt = me.dialog.fields_dict.payment_doctype.value;
-			me.dialog.fields_dict['payment_details'].$wrapper.empty();
-			frappe.db.get_doc(dt, event.value)
-			.then(doc => {
-				let displayed_docs = []
-				let payment = []
-				if (dt === "Payment Entry") {
-					payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
-					payment.doctype = dt
-					payment.posting_date = doc.posting_date;
-					payment.party = doc.party;
-					payment.reference_no = doc.reference_no;
-					payment.reference_date = doc.reference_date;
-					payment.paid_amount = doc.paid_amount;
-					payment.name = doc.name;
-					displayed_docs.push(payment);
-				} else if (dt === "Journal Entry") {
-					doc.accounts.forEach(payment => {
-						if (payment.account === me.gl_account) {
-							payment.doctype = dt;
-							payment.posting_date = doc.posting_date;
-							payment.party = doc.pay_to_recd_from;
-							payment.reference_no = doc.cheque_no;
-							payment.reference_date = doc.cheque_date;
-							payment.currency = payment.account_currency;
-							payment.paid_amount = payment.credit > 0 ? payment.credit : payment.debit;
-							payment.name = doc.name;
-							displayed_docs.push(payment);
-						}
-					})
-				} else if (dt === "Sales Invoice") {
-					doc.payments.forEach(payment => {
-						if (payment.clearance_date === null || payment.clearance_date === "") {
-							payment.doctype = dt;
-							payment.posting_date = doc.posting_date;
-							payment.party = doc.customer;
-							payment.reference_no = doc.remarks;
-							payment.currency = doc.currency;
-							payment.paid_amount = payment.amount;
-							payment.name = doc.name;
-							displayed_docs.push(payment);
-						}
-					})
-				}
-
-				const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
-				details_wrapper.append(frappe.render_template("linked_payment_header"));
-				displayed_docs.forEach(payment => {
-					details_wrapper.append(frappe.render_template("linked_payment_row", payment));
-				})
-			})
-		}
-
-	}
-}
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json
deleted file mode 100644
index feea368..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "content": null, 
- "creation": "2018-11-24 12:03:14.646669", 
- "docstatus": 0, 
- "doctype": "Page", 
- "idx": 0, 
- "modified": "2018-11-24 12:03:14.646669", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "bank-reconciliation", 
- "owner": "Administrator", 
- "page_name": "bank-reconciliation", 
- "roles": [
-  {
-   "role": "System Manager"
-  }, 
-  {
-   "role": "Accounts Manager"
-  }, 
-  {
-   "role": "Accounts User"
-  }
- ], 
- "script": null, 
- "standard": "Yes", 
- "style": null, 
- "system_page": 0, 
- "title": "Bank Reconciliation"
-}
\ No newline at end of file
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
deleted file mode 100644
index 8abe20c..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe import _
-import difflib
-from frappe.utils import flt
-from six import iteritems
-from erpnext import get_company_currency
-
-@frappe.whitelist()
-def reconcile(bank_transaction, payment_doctype, payment_name):
-	transaction = frappe.get_doc("Bank Transaction", bank_transaction)
-	payment_entry = frappe.get_doc(payment_doctype, payment_name)
-
-	account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
-	gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name))
-
-	if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount:
-		frappe.throw(_("The unallocated amount of Payment Entry {0} is greater than the Bank Transaction's unallocated amount").format(payment_name))
-
-	if transaction.unallocated_amount == 0:
-		frappe.throw(_("This bank transaction is already fully reconciled"))
-
-	if transaction.credit > 0 and gl_entry.credit > 0:
-		frappe.throw(_("The selected payment entry should be linked with a debtor bank transaction"))
-
-	if transaction.debit > 0 and gl_entry.debit > 0:
-		frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction"))
-
-	add_payment_to_transaction(transaction, payment_entry, gl_entry)
-
-	return 'reconciled'
-
-def add_payment_to_transaction(transaction, payment_entry, gl_entry):
-	gl_amount, transaction_amount = (gl_entry.credit, transaction.debit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.credit)
-	allocated_amount = gl_amount if gl_amount <= transaction_amount else transaction_amount
-	transaction.append("payment_entries", {
-		"payment_document": payment_entry.doctype,
-		"payment_entry": payment_entry.name,
-		"allocated_amount": allocated_amount
-	})
-
-	transaction.save()
-	transaction.update_allocations()
-
-@frappe.whitelist()
-def get_linked_payments(bank_transaction):
-	transaction = frappe.get_doc("Bank Transaction", bank_transaction)
-	bank_account = frappe.db.get_values("Bank Account", transaction.bank_account, ["account", "company"], as_dict=True)
-
-	# Get all payment entries with a matching amount
-	amount_matching = check_matching_amount(bank_account[0].account, bank_account[0].company, transaction)
-
-	# Get some data from payment entries linked to a corresponding bank transaction
-	description_matching = get_matching_descriptions_data(bank_account[0].company, transaction)
-
-	if amount_matching:
-		return check_amount_vs_description(amount_matching, description_matching)
-
-	elif description_matching:
-		description_matching = filter(lambda x: not x.get('clearance_date'), description_matching)
-		if not description_matching:
-			return []
-
-		return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True)
-
-	else:
-		return []
-
-def check_matching_amount(bank_account, company, transaction):
-	payments = []
-	amount = transaction.credit if transaction.credit > 0 else transaction.debit
-
-	payment_type = "Receive" if transaction.credit > 0 else "Pay"
-	account_from_to = "paid_to" if transaction.credit > 0 else "paid_from"
-	currency_field = "paid_to_account_currency as currency" if transaction.credit > 0 else "paid_from_account_currency as currency"
-
-	payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
-		"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
-		["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
-
-	jea_side = "debit" if transaction.credit > 0 else "credit"
-	journal_entries = frappe.db.sql(f"""
-		SELECT
-			'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
-			jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date,
-			jea.{jea_side}_in_account_currency as paid_amount
-		FROM
-			`tabJournal Entry Account` as jea
-		JOIN
-			`tabJournal Entry` as je
-		ON
-			jea.parent = je.name
-		WHERE
-			(je.clearance_date is null or je.clearance_date='0000-00-00')
-		AND
-			jea.account = %(bank_account)s
-		AND
-			jea.{jea_side}_in_account_currency like %(txt)s
-		AND
-			je.docstatus = 1
-	""", {
-		'bank_account': bank_account,
-		'txt': '%%%s%%' % amount
-	}, as_dict=True)
-
-	if transaction.credit > 0:
-		sales_invoices = frappe.db.sql("""
-			SELECT
-				'Sales Invoice' as doctype, si.name, si.customer as party,
-				si.posting_date, sip.amount as paid_amount
-			FROM
-				`tabSales Invoice Payment` as sip
-			JOIN
-				`tabSales Invoice` as si
-			ON
-				sip.parent = si.name
-			WHERE
-				(sip.clearance_date is null or sip.clearance_date='0000-00-00')
-			AND
-				sip.account = %s
-			AND
-				sip.amount like %s
-			AND
-				si.docstatus = 1
-		""", (bank_account, amount), as_dict=True)
-	else:
-		sales_invoices = []
-
-	if transaction.debit > 0:
-		purchase_invoices = frappe.get_all("Purchase Invoice",
-			fields = ["'Purchase Invoice' as doctype", "name", "paid_amount", "supplier as party", "posting_date", "currency"],
-			filters=[
-				["paid_amount", "like", "{0}%".format(amount)],
-				["docstatus", "=", "1"],
-				["is_paid", "=", "1"],
-				["ifnull(clearance_date, '')", "=", ""],
-				["cash_bank_account", "=", "{0}".format(bank_account)]
-			]
-		)
-
-		mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
-			filters={"default_account": bank_account}, fields=["parent"])]
-
-		company_currency = get_company_currency(company)
-
-		expense_claims = frappe.get_all("Expense Claim",
-			fields=["'Expense Claim' as doctype", "name", "total_sanctioned_amount as paid_amount",
-				"employee as party", "posting_date", "'{0}' as currency".format(company_currency)],
-			filters=[
-				["total_sanctioned_amount", "like", "{0}%".format(amount)],
-				["docstatus", "=", "1"],
-				["is_paid", "=", "1"],
-				["ifnull(clearance_date, '')", "=", ""],
-				["mode_of_payment", "in", "{0}".format(tuple(mode_of_payments))]
-			]
-		)
-	else:
-		purchase_invoices = expense_claims = []
-
-	for data in [payment_entries, journal_entries, sales_invoices, purchase_invoices, expense_claims]:
-		if data:
-			payments.extend(data)
-
-	return payments
-
-def get_matching_descriptions_data(company, transaction):
-	if not transaction.description :
-		return []
-
-	bank_transactions = frappe.db.sql("""
-		SELECT
-			bt.name, bt.description, bt.date, btp.payment_document, btp.payment_entry
-		FROM
-			`tabBank Transaction` as bt
-		LEFT JOIN
-			`tabBank Transaction Payments` as btp
-		ON
-			bt.name = btp.parent
-		WHERE
-			bt.allocated_amount > 0
-		AND
-			bt.docstatus = 1
-		""", as_dict=True)
-
-	selection = []
-	for bank_transaction in bank_transactions:
-		if bank_transaction.description:
-			seq=difflib.SequenceMatcher(lambda x: x == " ", transaction.description, bank_transaction.description)
-
-			if seq.ratio() > 0.6:
-				bank_transaction["ratio"] = seq.ratio()
-				selection.append(bank_transaction)
-
-	document_types = set([x["payment_document"] for x in selection])
-
-	links = {}
-	for document_type in document_types:
-		links[document_type] = [x["payment_entry"] for x in selection if x["payment_document"]==document_type]
-
-
-	data = []
-	company_currency = get_company_currency(company)
-	for key, value in iteritems(links):
-		if key == "Payment Entry":
-			data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]],
-				fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no",
-					"reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"]))
-		if key == "Journal Entry":
-			journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]],
-				fields=["name", "'Journal Entry' as doctype", "posting_date",
-					"pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date",
-					"total_credit as paid_amount", "clearance_date"])
-			for journal_entry in journal_entries:
-				journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"])
-				journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency
-			data.extend(journal_entries)
-		if key == "Sales Invoice":
-			data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party", "paid_amount", "currency"]))
-		if key == "Purchase Invoice":
-			data.extend(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party", "paid_amount", "currency"]))
-		if key == "Expense Claim":
-			expense_claims = frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party", "total_amount_reimbursed as paid_amount"])
-			data.extend([dict(x,**{"currency": company_currency}) for x in expense_claims])
-
-	return data
-
-def check_amount_vs_description(amount_matching, description_matching):
-	result = []
-
-	if description_matching:
-		for am_match in amount_matching:
-			for des_match in description_matching:
-				if des_match.get("clearance_date"):
-					continue
-
-				if am_match["party"] == des_match["party"]:
-					if am_match not in result:
-						result.append(am_match)
-						continue
-
-				if "reference_no" in am_match and "reference_no" in des_match:
-					# Sequence Matcher does not handle None as input
-					am_reference = am_match["reference_no"] or ""
-					des_reference = des_match["reference_no"] or ""
-
-					if difflib.SequenceMatcher(lambda x: x == " ", am_reference, des_reference).ratio() > 70:
-						if am_match not in result:
-							result.append(am_match)
-		if result:
-			return sorted(result, key = lambda x: x["posting_date"], reverse=True)
-		else:
-			return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True)
-
-	else:
-		return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True)
-
-def get_matching_transactions_payments(description_matching):
-	payments = [x["payment_entry"] for x in description_matching]
-
-	payment_by_ratio = {x["payment_entry"]: x["ratio"] for x in description_matching}
-
-	if payments:
-		reference_payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date",
-			"party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["name", "in", payments]])
-
-		return sorted(reference_payment_list, key=lambda x: payment_by_ratio[x["name"]])
-
-	else:
-		return []
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
-	account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
-	if not account:
-		return
-
-	return frappe.db.sql("""
-		SELECT
-			name, party, paid_amount, received_amount, reference_no
-		FROM
-			`tabPayment Entry`
-		WHERE
-			(clearance_date is null or clearance_date='0000-00-00')
-			AND (paid_from = %(account)s or paid_to = %(account)s)
-			AND (name like %(txt)s or party like %(txt)s)
-			AND docstatus = 1
-		ORDER BY
-			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name
-		LIMIT
-			%(start)s, %(page_len)s""",
-		{
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len,
-			'account': account
-		}
-	)
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def journal_entry_query(doctype, txt, searchfield, start, page_len, filters):
-	account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account")
-
-	return frappe.db.sql("""
-		SELECT
-			jea.parent, je.pay_to_recd_from,
-			if(jea.debit_in_account_currency > 0, jea.debit_in_account_currency, jea.credit_in_account_currency)
-		FROM
-			`tabJournal Entry Account` as jea
-		LEFT JOIN
-			`tabJournal Entry` as je
-		ON
-			jea.parent = je.name
-		WHERE
-			(je.clearance_date is null or je.clearance_date='0000-00-00')
-		AND
-			jea.account = %(account)s
-		AND
-			(jea.parent like %(txt)s or je.pay_to_recd_from like %(txt)s)
-		AND
-			je.docstatus = 1
-		ORDER BY
-			if(locate(%(_txt)s, jea.parent), locate(%(_txt)s, jea.parent), 99999),
-			jea.parent
-		LIMIT
-			%(start)s, %(page_len)s""",
-		{
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len,
-			'account': account
-		}
-	)
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
-	return frappe.db.sql("""
-		SELECT
-			sip.parent, si.customer, sip.amount, sip.mode_of_payment
-		FROM
-			`tabSales Invoice Payment` as sip
-		LEFT JOIN
-			`tabSales Invoice` as si
-		ON
-			sip.parent = si.name
-		WHERE
-			(sip.clearance_date is null or sip.clearance_date='0000-00-00')
-		AND
-			(sip.parent like %(txt)s or si.customer like %(txt)s)
-		ORDER BY
-			if(locate(%(_txt)s, sip.parent), locate(%(_txt)s, sip.parent), 99999),
-			sip.parent
-		LIMIT
-			%(start)s, %(page_len)s""",
-		{
-			'txt': "%%%s%%" % txt,
-			'_txt': txt.replace("%", ""),
-			'start': start,
-			'page_len': page_len
-		}
-	)
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html
deleted file mode 100644
index 94f183b..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_header.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="transaction-header">
-	<div class="level list-row list-row-head text-muted small">
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{{ __("Date") }}
-		</div>
-		<div class="col-xs-11 col-sm-4 ellipsis list-subject">
-			{{ __("Description") }}
-		</div>
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{{ __("Debit") }}
-		</div>
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{{ __("Credit") }}
-		</div>
-		<div class="col-sm-1 ellipsis hidden-xs">
-			{{ __("Currency") }}
-		</div>
-		<div class="col-sm-1 ellipsis">
-		</div>
-	</div>
-</div>
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html b/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html
deleted file mode 100644
index 742b84c..0000000
--- a/erpnext/accounts/page/bank_reconciliation/bank_transaction_row.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<div class="list-row transaction-item">
-	<div>
-		<div class="clickable-section" data-name={{ name }}>
-			<div class="col-sm-2 ellipsis hidden-xs">
-				{%= frappe.datetime.str_to_user(date) %}
-			</div>
-			<div class="col-xs-8 col-sm-4 ellipsis list-subject">
-				{{ description }}
-			</div>
-			<div class="col-sm-2 ellipsis hidden-xs">
-				{%= format_currency(debit, currency) %}
-			</div>
-			<div class="col-sm-2 ellipsis hidden-xs">
-				{%= format_currency(credit, currency) %}
-			</div>
-			<div class="col-sm-1 ellipsis hidden-xs">
-				{{ currency }}
-			</div>
-		</div>
-		<div class="col-xs-3 col-sm-1">
-			<div class="btn-group">
-				<a class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-					<span>Actions </span>
-					<span class="caret"></span>
-				</a>
-				<ul class="dropdown-menu reports-dropdown" style="max-height: 300px; overflow-y: auto; right: 0px; left: auto;">
-					<li><a class="new-reconciliation" data-name={{ name }}>{{ __("Reconcile") }}</a></li>
-					<li class="divider"></li>
-					<li><a class="new-payment" data-name={{ name }}>{{ __("New Payment") }}</a></li>
-					<li><a class="new-invoice" data-name={{ name }}>{{ __("New Invoice") }}</a></li>
-					<li><a class="new-expense" data-name={{ name }}>{{ __("New Expense") }}</a></li>
-				</ul>
-			</div>
-		</div>
-	</div>
-</div>
diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html
deleted file mode 100644
index 4542c36..0000000
--- a/erpnext/accounts/page/bank_reconciliation/linked_payment_header.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="transaction-header">
-	<div class="level list-row list-row-head text-muted small">
-		<div class="col-xs-3 col-sm-2 ellipsis">
-			{{ __("Payment Name") }}
-		</div>
-		<div class="col-xs-3 col-sm-2 ellipsis">
-			{{ __("Reference Date") }}
-		</div>
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{{ __("Amount") }}
-		</div>
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{{ __("Party") }}
-		</div>
-		<div class="col-xs-3 col-sm-2 ellipsis">
-			{{ __("Reference Number") }}
-		</div>
-		<div class="col-xs-2 col-sm-2">
-		</div>
-	</div>
-</div>
diff --git a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html b/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html
deleted file mode 100644
index bdbc9fc..0000000
--- a/erpnext/accounts/page/bank_reconciliation/linked_payment_row.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<div class="list-row">
-	<div>
-		<div class="col-xs-3 col-sm-2 ellipsis">
-			{{ name }}
-		</div>
-		<div class="col-xs-3 col-sm-2 ellipsis">
-			{% if (typeof reference_date !== "undefined") %}
-				{%= frappe.datetime.str_to_user(reference_date) %}
-			{% else %}
-				{% if (typeof posting_date !== "undefined") %}
-					{%= frappe.datetime.str_to_user(posting_date) %}
-				{% endif %}
-			{% endif %}
-		</div>
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{{ format_currency(paid_amount, currency) }}
-		</div>
-		<div class="col-sm-2 ellipsis hidden-xs">
-			{% if (typeof party !== "undefined") %}
-				{{ party }}
-			{% endif %}
-		</div>
-		<div class="col-xs-3 col-sm-2 ellipsis">
-			{% if (typeof reference_no !== "undefined") %}
-				{{ reference_no }}
-			{% else %}
-				{{ "" }}
-			{% endif %}
-		</div>
-		<div class="col-xs-2 col-sm-2">
-			<div class="text-right margin-bottom">
-				<button class="btn btn-primary btn-xs reconciliation-btn" data-doctype="{{ doctype }}" data-name="{{ name }}">{{ __("Reconcile") }}</button>
-			</div>
-		</div>
-	</div>
-</div>
\ No newline at end of file
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index 0861b20..79b0a6f 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -15,15 +15,51 @@
 	return columns, data
 
 def get_columns():
-	return [
-		_("Payment Document") + "::130",
-		_("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110",
-		_("Posting Date") + ":Date:100",
-		_("Cheque/Reference No") + "::120",
-		_("Clearance Date") + ":Date:100",
-		_("Against Account") + ":Link/Account:170",
-		_("Amount") + ":Currency:120"
-	]
+	columns = [{
+			"label": _("Payment Document Type"),
+			"fieldname": "payment_document_type",
+			"fieldtype": "Link",
+			"options": "Doctype",
+			"width": 130
+		},
+		{
+			"label": _("Payment Entry"),
+			"fieldname": "payment_entry",
+			"fieldtype": "Dynamic Link",
+			"options": "payment_document_type",
+			"width": 140
+		},
+		{
+			"label": _("Posting Date"),
+			"fieldname": "posting_date",
+			"fieldtype": "Date",
+			"width": 100
+		},
+		{
+			"label": _("Cheque/Reference No"),
+			"fieldname": "cheque_no",
+			"width": 120
+		},
+		{
+			"label": _("Clearance Date"),
+			"fieldname": "clearance_date",
+			"fieldtype": "Date",
+			"width": 100
+		},
+		{
+			"label": _("Against Account"),
+			"fieldname": "against",
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 170
+		},
+		{
+			"label": _("Amount"),
+			"fieldname": "amount",
+			"width": 120
+		}]
+
+	return columns
 
 def get_conditions(filters):
 	conditions = ""
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 d011689..76f3c50 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -222,7 +222,7 @@
 
 		set_gl_entries_by_account(start_date,
 			end_date, root.lft, root.rgt, filters,
-			gl_entries_by_account, accounts_by_name, ignore_closing_entries=False)
+			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)
 	accumulate_values_into_parents(accounts, accounts_by_name, companies)
@@ -339,7 +339,7 @@
 	return data
 
 def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
-	accounts_by_name, ignore_closing_entries=False):
+	accounts_by_name, accounts, ignore_closing_entries=False):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 
 	company_lft, company_rgt = frappe.get_cached_value('Company',
@@ -382,15 +382,31 @@
 
 		for entry in gl_entries:
 			key = entry.account_number or entry.account_name
-			validate_entries(key, entry, accounts_by_name)
+			validate_entries(key, entry, accounts_by_name, accounts)
 			gl_entries_by_account.setdefault(key, []).append(entry)
 
 	return gl_entries_by_account
 
-def validate_entries(key, entry, accounts_by_name):
+def get_account_details(account):
+	return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company',
+		'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
+
+def validate_entries(key, entry, accounts_by_name, accounts):
 	if key not in accounts_by_name:
-		field = "Account number" if entry.account_number else "Account name"
-		frappe.throw(_("{0} {1} is not present in the parent company").format(field, key))
+		args = get_account_details(entry.account)
+
+		if args.parent_account:
+			parent_args = get_account_details(args.parent_account)
+
+			args.update({
+				'lft': parent_args.lft + 1,
+				'rgt': parent_args.rgt - 1,
+				'root_type': parent_args.root_type,
+				'report_type': parent_args.report_type
+			})
+
+		accounts_by_name.setdefault(key, args)
+		accounts.append(args)
 
 def get_additional_conditions(from_date, ignore_closing_entries, filters):
 	additional_conditions = []
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 67c7fd2..89a05b1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -82,7 +82,7 @@
 	error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
 	if company:
 		error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))
-	
+
 	if verbose==1: frappe.msgprint(error_msg)
 	raise FiscalYearError(error_msg)
 
@@ -888,6 +888,11 @@
 
 def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
 		warehouse_account=None, company=None):
+	stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items, company)
+	repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company, warehouse_account)
+
+
+def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, warehouse_account=None):
 	def _delete_gl_entries(voucher_type, voucher_no):
 		frappe.db.sql("""delete from `tabGL Entry`
 			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
@@ -895,21 +900,21 @@
 	if not warehouse_account:
 		warehouse_account = get_warehouse_account_map(company)
 
-	future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
-	gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
+	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit")) or 2
 
-	for voucher_type, voucher_no in future_stock_vouchers:
+	gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
+	for voucher_type, voucher_no in stock_vouchers:
 		existing_gle = gle.get((voucher_type, voucher_no), [])
-		voucher_obj = frappe.get_doc(voucher_type, voucher_no)
+		voucher_obj = frappe.get_cached_doc(voucher_type, voucher_no)
 		expected_gle = voucher_obj.get_gl_entries(warehouse_account)
 		if expected_gle:
-			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
+			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 				_delete_gl_entries(voucher_type, voucher_no)
 				voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
 		else:
 			_delete_gl_entries(voucher_type, voucher_no)
 
-def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
+def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
 	future_stock_vouchers = []
 
 	values = []
@@ -922,6 +927,10 @@
 		condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
 		values += for_warehouses
 
+	if company:
+		condition += " and company = %s"
+		values.append(company)
+
 	for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
 		from `tabStock Ledger Entry` sle
 		where
@@ -945,16 +954,17 @@
 
 	return gl_entries
 
-def compare_existing_and_expected_gle(existing_gle, expected_gle):
+def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 	matched = True
 	for entry in expected_gle:
 		account_existed = False
 		for e in existing_gle:
 			if entry.account == e.account:
 				account_existed = True
-			if entry.account == e.account and entry.against_account == e.against_account \
-					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
-					and (entry.debit != e.debit or entry.credit != e.credit):
+			if (entry.account == e.account and entry.against_account == e.against_account
+					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
+					and ( flt(entry.debit, precision) != flt(e.debit, precision) or
+						flt(entry.credit, precision) != flt(e.credit, precision))):
 				matched = False
 				break
 		if not account_existed:
@@ -982,7 +992,7 @@
 			error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format(
 				stock_bal, account_bal, frappe.bold(account), posting_date)
 			error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\
-				.format(frappe.bold(diff), frappe.bold(posting_date))			
+				.format(frappe.bold(diff), frappe.bold(posting_date))
 
 			frappe.msgprint(
 				msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
diff --git a/erpnext/assets/doctype/asset_category/asset_category.json b/erpnext/assets/doctype/asset_category/asset_category.json
index b7d1226..a25f546 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.json
+++ b/erpnext/assets/doctype/asset_category/asset_category.json
@@ -19,7 +19,6 @@
  ],
  "fields": [
   {
-   "depends_on": "eval:!doc.asset_category_name",
    "fieldname": "asset_category_name",
    "fieldtype": "Data",
    "in_list_view": 1,
@@ -67,7 +66,7 @@
   }
  ],
  "links": [],
- "modified": "2021-01-22 12:31:14.425319",
+ "modified": "2021-02-24 15:05:38.621803",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Category",
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index c691e9f..75b2954 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -40,6 +40,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_29",
   "net_rate",
@@ -726,13 +727,21 @@
    "fieldname": "more_info_section_break",
    "fieldtype": "Section Break",
    "label": "More Information"
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-07 11:59:47.670951",
+ "modified": "2021-01-30 21:44:41.816974",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index a51498e..7cf22f8 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -127,6 +127,10 @@
 				'link_doctype': 'Supplier',
 				'link_name': rfq_supplier.supplier
 			})
+			contact.append('email_ids', {
+				'email_id': user.name,
+				'is_primary': 1
+			})
 
 		if not contact.email_id and not contact.user:
 			contact.email_id = user.name
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 40362b1..4cc5753 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -26,7 +26,6 @@
   "supplier_group",
   "supplier_type",
   "pan",
-  "language",
   "allow_purchase_invoice_creation_without_purchase_order",
   "allow_purchase_invoice_creation_without_purchase_receipt",
   "disabled",
@@ -57,6 +56,7 @@
   "website",
   "supplier_details",
   "column_break_30",
+  "language",
   "is_frozen"
  ],
  "fields": [
@@ -384,7 +384,7 @@
  "idx": 370,
  "image_field": "image",
  "links": [],
- "modified": "2020-06-17 23:18:20",
+ "modified": "2021-01-06 19:51:40.939087",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 21874fe..12a81c7 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1309,45 +1309,28 @@
 					})
 				tax_row.db_insert()
 
-def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
+def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item):
 	"""
-	Returns a Sales Order Item child item containing the default values
+	Returns a Sales/Purchase Order Item child item containing the default values
 	"""
 	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
-	child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
+	child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
 	item = frappe.get_doc("Item", trans_item.get('item_code'))
-	child_item.item_code = item.item_code
-	child_item.item_name = item.item_name
-	child_item.description = item.description
-	child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
+	for field in ("item_code", "item_name", "description", "item_group"):
+	    child_item.update({field: item.get(field)})
+	date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
+	child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
 	child_item.uom = trans_item.get("uom") or item.stock_uom
 	conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
 	child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
-	set_child_tax_template_and_map(item, child_item, p_doc)
-	add_taxes_from_tax_template(child_item, p_doc)
-	child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
-	if not child_item.warehouse:
-		frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
-			.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
-	return child_item
-
-
-def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
-	"""
-	Returns a Purchase Order Item child item containing the default values
-	"""
-	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
-	child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
-	item = frappe.get_doc("Item", trans_item.get('item_code'))
-	child_item.item_code = item.item_code
-	child_item.item_name = item.item_name
-	child_item.description = item.description
-	child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
-	child_item.uom = trans_item.get("uom") or item.stock_uom
-	conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
-	child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
-	child_item.base_rate = 1 # Initiallize value will update in parent validation
-	child_item.base_amount = 1 # Initiallize value will update in parent validation
+	if child_doctype == "Purchase Order Item":
+		child_item.base_rate = 1 # Initiallize value will update in parent validation
+		child_item.base_amount = 1 # Initiallize value will update in parent validation
+	if child_doctype == "Sales Order Item":
+		child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
+		if not child_item.warehouse:
+			frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
+				.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
 	set_child_tax_template_and_map(item, child_item, p_doc)
 	add_taxes_from_tax_template(child_item, p_doc)
 	return child_item
@@ -1411,8 +1394,8 @@
 			)
 
 	def get_new_child_item(item_row):
-		new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
-		return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row)
+		child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" 
+		return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
 
 	def validate_quantity(child_item, d):
 		if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
@@ -1521,6 +1504,7 @@
 	parent.flags.ignore_validate_update_after_submit = True
 	parent.set_qty_as_per_stock_uom()
 	parent.calculate_taxes_and_totals()
+	parent.set_total_in_words()
 	if parent_doctype == "Sales Order":
 		make_packing_list(parent)
 		parent.set_gross_profit()
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 0e1829a..de61b35 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -204,8 +204,6 @@
 	return items
 
 def get_returned_qty_map_for_row(row_name, doctype):
-	if doctype == "POS Invoice": return {}
-
 	child_doctype = doctype + " Item"
 	reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
 
@@ -354,7 +352,12 @@
 			target_doc.so_detail = source_doc.so_detail
 			target_doc.dn_detail = source_doc.dn_detail
 			target_doc.expense_account = source_doc.expense_account
-			target_doc.sales_invoice_item = source_doc.name
+
+			if doctype == "Sales Invoice":
+				target_doc.sales_invoice_item = source_doc.name
+			else:
+				target_doc.pos_invoice_item = source_doc.name
+
 			target_doc.price_list_rate = 0
 			if default_warehouse_for_sales_return:
 				target_doc.warehouse = default_warehouse_for_sales_return
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 6abfe04..c61b67b 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -446,9 +446,13 @@
 		check_list, chk_dupl_itm = [], []
 		if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
 			return
+		if self.doctype == "Sales Invoice" and self.is_consolidated:
+			return
+		if self.doctype == "POS Invoice":
+			return
 
 		for d in self.get('items'):
-			if self.doctype in ["POS Invoice","Sales Invoice"]:
+			if self.doctype == "Sales Invoice":
 				stock_items = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
 				non_stock_items = [d.item_code, d.description, d.sales_order or d.delivery_note]
 			elif self.doctype == "Delivery Note":
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4b5e347..e0031c9 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -24,6 +24,7 @@
 			self.validate_inspection()
 		self.validate_serialized_batch()
 		self.validate_customer_provided_item()
+		self.set_rate_of_stock_uom()
 		self.validate_internal_transfer()
 		self.validate_putaway_capacity()
 
@@ -73,7 +74,7 @@
 
 		gl_list = []
 		warehouse_with_no_account = []
-		precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+		precision = self.get_debit_field_precision()
 		for item_row in voucher_details:
 
 			sle_list = sle_map.get(item_row.name)
@@ -130,7 +131,13 @@
 				if frappe.db.get_value("Warehouse", wh, "company"):
 					frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
 
-		return process_gl_map(gl_list)
+		return process_gl_map(gl_list, precision=precision)
+
+	def get_debit_field_precision(self):
+		if not frappe.flags.debit_field_precision:
+			frappe.flags.debit_field_precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+
+		return frappe.flags.debit_field_precision
 
 	def update_stock_ledger_entries(self, sle):
 		sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
@@ -243,7 +250,7 @@
 				.format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
 
 		else:
-			is_expense_account = frappe.db.get_value("Account",
+			is_expense_account = frappe.get_cached_value("Account",
 				item.get("expense_account"), "report_type")=="Profit and Loss"
 			if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
 				frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
@@ -313,7 +320,7 @@
 		return serialized_items
 
 	def validate_warehouse(self):
-		from erpnext.stock.utils import validate_warehouse_company
+		from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
 
 		warehouses = list(set([d.warehouse for d in
 			self.get("items") if getattr(d, "warehouse", None)]))
@@ -329,6 +336,7 @@
 		warehouses.extend(from_warehouse)
 
 		for w in warehouses:
+			validate_disabled_warehouse(w)
 			validate_warehouse_company(w, self.company)
 
 	def update_billing_percentage(self, update_modified=True):
@@ -395,6 +403,11 @@
 			if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
 				d.allow_zero_valuation_rate = 1
 
+	def set_rate_of_stock_uom(self):
+		if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
+			for d in self.get("items"):
+				d.stock_uom_rate = d.rate / d.conversion_factor
+
 	def validate_internal_transfer(self):
 		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
 			and self.is_internal_transfer():
@@ -481,7 +494,6 @@
 			"voucher_no": self.name,
 			"company": self.company
 		})
-
 		if check_if_future_sle_exists(args):
 			create_repost_item_valuation_entry(args)
 		elif not is_reposting_pending():
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index cfa4991..10271cb 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -15,6 +15,8 @@
 class calculate_taxes_and_totals(object):
 	def __init__(self, doc):
 		self.doc = doc
+		frappe.flags.round_off_applicable_accounts = []
+		get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
 		self.calculate()
 
 	def calculate(self):
@@ -332,10 +334,18 @@
 		elif tax.charge_type == "On Item Quantity":
 			current_tax_amount = tax_rate * item.qty
 
+		current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount)
 		self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
 
 		return current_tax_amount
 
+	def get_final_current_tax_amount(self, tax, current_tax_amount):
+		# Some countries need individual tax components to be rounded
+		# Handeled via regional doctypess
+		if tax.account_head in frappe.flags.round_off_applicable_accounts:
+			current_tax_amount = round(current_tax_amount, 0)
+		return current_tax_amount
+
 	def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
 		# store tax breakup for each item
 		key = item.item_code or item.item_name
@@ -693,6 +703,15 @@
 		)
 	)
 
+@frappe.whitelist()
+def get_round_off_applicable_accounts(company, account_list):
+	account_list = get_regional_round_off_accounts(company, account_list)
+
+	return account_list
+
+@erpnext.allow_regional
+def get_regional_round_off_accounts(company, account_list):
+	pass
 
 @erpnext.allow_regional
 def update_itemised_tax_data(doc):
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 2df1793..1b33fd7 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -49,6 +49,7 @@
   "phone",
   "mobile_no",
   "fax",
+  "website",
   "more_info",
   "type",
   "market_segment",
@@ -56,8 +57,8 @@
   "request_type",
   "column_break3",
   "company",
-  "website",
   "territory",
+  "language",
   "unsubscribed",
   "blog_subscriber",
   "title"
@@ -447,13 +448,19 @@
    "fieldtype": "Select",
    "label": "Address Type",
    "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther"
+  },
+  {
+   "fieldname": "language",
+   "fieldtype": "Link",
+   "label": "Print Language",
+   "options": "Language"
   }
  ],
  "icon": "fa fa-user",
  "idx": 5,
  "image_field": "image",
  "links": [],
- "modified": "2020-10-13 15:24:00.094811",
+ "modified": "2021-01-06 19:39:58.748978",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Lead",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index 08958b7..ac374a9 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -24,6 +24,12 @@
 			frm.trigger('set_contact_link');
 		}
 	},
+	contact_date: function(frm) {
+		if(frm.doc.contact_date < frappe.datetime.now_datetime()){
+			frm.set_value("contact_date", "");
+			frappe.throw(__("Next follow up date should be greater than now."))
+		}
+	},
 
 	onload_post_render: function(frm) {
 		frm.get_field("items").grid.set_multiple_add("item_code", "qty");
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index eee13f7..2e09a76 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -54,6 +54,7 @@
   "campaign",
   "column_break1",
   "transaction_date",
+  "language",
   "amended_from",
   "lost_reasons"
  ],
@@ -419,12 +420,18 @@
    "fieldtype": "Duration",
    "label": "First Response Time",
    "read_only": 1
+  },
+  {
+   "fieldname": "language",
+   "fieldtype": "Link",
+   "label": "Print Language",
+   "options": "Language"
   }
  ],
  "icon": "fa fa-info-sign",
  "idx": 195,
  "links": [],
- "modified": "2020-08-12 17:34:35.066961",
+ "modified": "2021-01-06 19:42:46.190051",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Opportunity",
diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py
index 4ccd9bd..f244daf 100644
--- a/erpnext/crm/doctype/utils.py
+++ b/erpnext/crm/doctype/utils.py
@@ -78,7 +78,9 @@
 
 def strip_number(number):
 	if not number: return
-	# strip 0 from the start of the number for proper number comparisions
+	# strip + and 0 from the start of the number for proper number comparisions
+	# eg. +7888383332 should match with 7888383332
 	# eg. 07888383332 should match with 7888383332
+	number = number.lstrip('+')
 	number = number.lstrip('0')
 	return number
diff --git a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py
index b538a58..3a9d57d 100644
--- a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py
+++ b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py
@@ -19,15 +19,50 @@
 	if not filters.get('lead_age'): filters["lead_age"] = 60
 
 def get_columns():
-	return [
-		_("Lead") + ":Link/Lead:100",
-		_("Name") + "::100",
-		_("Organization") + "::100",
-		_("Reference Document") + "::150",
-		_("Reference Name") + ":Dynamic Link/"+_("Reference Document")+":120",
-		_("Last Communication") + ":Data:200",
-		_("Last Communication Date") + ":Date:180"
-	]
+	columns = [{
+			"label": _("Lead"),
+			"fieldname": "lead",
+			"fieldtype": "Link",
+			"options": "Lead",
+			"width": 130
+		},
+		{
+			"label": _("Name"),
+			"fieldname": "name",
+			"width": 120
+		},
+		{
+			"label": _("Organization"),
+			"fieldname": "organization",
+			"width": 120
+		},
+		{
+			"label": _("Reference Document Type"),
+			"fieldname": "reference_document_type",
+			"fieldtype": "Link",
+			"options": "Doctype",
+			"width": 100
+		},
+		{
+			"label": _("Reference Name"),
+			"fieldname": "reference_name",
+			"fieldtype": "Dynamic Link",
+			"options": "reference_document_type",
+			"width": 140
+		},
+		{
+			"label": _("Last Communication"),
+			"fieldname": "last_communication",
+			"fieldtype": "Data",
+			"width": 200
+		},
+		{
+			"label": _("Last Communication Date"),
+			"fieldname": "last_communication_date",
+			"fieldtype": "Date",
+			"width": 100
+		}]
+	return columns
 
 def get_data(filters):
 	lead_details = []
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
index d33b0a7..554c6b0 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
@@ -5,7 +5,7 @@
 
 class MpesaConnector():
 	def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke",
-		live_url="https://safaricom.co.ke"):
+		live_url="https://api.safaricom.co.ke"):
 		"""Setup configuration for Mpesa connector and generate new access token."""
 		self.env = env
 		self.app_key = app_key
@@ -102,14 +102,14 @@
 			"BusinessShortCode": business_shortcode,
 			"Password": encoded.decode("utf-8"),
 			"Timestamp": time,
-			"TransactionType": "CustomerPayBillOnline",
 			"Amount": amount,
 			"PartyA": int(phone_number),
-			"PartyB": business_shortcode,
+			"PartyB": reference_code,
 			"PhoneNumber": int(phone_number),
 			"CallBackURL": callback_url,
 			"AccountReference": reference_code,
-			"TransactionDesc": description
+			"TransactionDesc": description,
+			"TransactionType": "CustomerPayBillOnline" if self.env == "sandbox" else "CustomerBuyGoodsOnline"
 		}
 		headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"}
 
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
index fc7b310..407f826 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
@@ -11,8 +11,10 @@
   "consumer_secret",
   "initiator_name",
   "till_number",
+  "transaction_limit",
   "sandbox",
   "column_break_4",
+  "business_shortcode",
   "online_passkey",
   "security_credential",
   "get_account_balance",
@@ -84,10 +86,24 @@
    "fieldname": "get_account_balance",
    "fieldtype": "Button",
    "label": "Get Account Balance"
+  },
+  {
+   "depends_on": "eval:(doc.sandbox==0)",
+   "fieldname": "business_shortcode",
+   "fieldtype": "Data",
+   "label": "Business Shortcode",
+   "mandatory_depends_on": "eval:(doc.sandbox==0)"
+  },
+  {
+   "default": "150000",
+   "fieldname": "transaction_limit",
+   "fieldtype": "Float",
+   "label": "Transaction Limit",
+   "non_negative": 1
   }
  ],
  "links": [],
- "modified": "2020-09-25 20:21:38.215494",
+ "modified": "2021-01-29 12:02:16.106942",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "Mpesa Settings",
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index 1cad84d..b571802 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -33,13 +33,34 @@
 		create_mode_of_payment('Mpesa-' + self.payment_gateway_name, payment_type="Phone")
 
 	def request_for_payment(self, **kwargs):
-		if frappe.flags.in_test:
-			from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload
-			response = frappe._dict(get_payment_request_response_payload())
-		else:
-			response = frappe._dict(generate_stk_push(**kwargs))
+		args = frappe._dict(kwargs)
+		request_amounts = self.split_request_amount_according_to_transaction_limit(args)
 
-		self.handle_api_response("CheckoutRequestID", kwargs, response)
+		for i, amount in enumerate(request_amounts):
+			args.request_amount = amount
+			if frappe.flags.in_test:
+				from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload
+				response = frappe._dict(get_payment_request_response_payload(amount))
+			else:
+				response = frappe._dict(generate_stk_push(**args))
+
+			self.handle_api_response("CheckoutRequestID", args, response)
+
+	def split_request_amount_according_to_transaction_limit(self, args):
+		request_amount = args.request_amount
+		if request_amount > self.transaction_limit:
+			# make multiple requests
+			request_amounts = []
+			requests_to_be_made = frappe.utils.ceil(request_amount / self.transaction_limit) # 480/150 = ceil(3.2) = 4
+			for i in range(requests_to_be_made):
+				amount = self.transaction_limit
+				if i == requests_to_be_made - 1:
+					amount = request_amount - (self.transaction_limit * i) # for 4th request, 480 - (150 * 3) = 30
+				request_amounts.append(amount)
+		else:
+			request_amounts = [request_amount]
+		
+		return request_amounts
 
 	def get_account_balance_info(self):
 		payload = dict(
@@ -67,7 +88,8 @@
 			req_name = getattr(response, global_id)
 			error = None
 
-		create_request_log(request_dict, "Host", "Mpesa", req_name, error)
+		if not frappe.db.exists('Integration Request', req_name):
+			create_request_log(request_dict, "Host", "Mpesa", req_name, error)
 
 		if error:
 			frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error"))
@@ -80,6 +102,8 @@
 
 		mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:])
 		env = "production" if not mpesa_settings.sandbox else "sandbox"
+		# for sandbox, business shortcode is same as till number
+		business_shortcode = mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number
 
 		connector = MpesaConnector(env=env,
 			app_key=mpesa_settings.consumer_key,
@@ -87,10 +111,12 @@
 
 		mobile_number = sanitize_mobile_number(args.sender)
 
-		response = connector.stk_push(business_shortcode=mpesa_settings.till_number,
-			passcode=mpesa_settings.get_password("online_passkey"), amount=args.grand_total,
+		response = connector.stk_push(
+			business_shortcode=business_shortcode, amount=args.request_amount,
+			passcode=mpesa_settings.get_password("online_passkey"),
 			callback_url=callback_url, reference_code=mpesa_settings.till_number,
-			phone_number=mobile_number, description="POS Payment")
+			phone_number=mobile_number, description="POS Payment"
+		)
 
 		return response
 
@@ -108,29 +134,72 @@
 	transaction_response = frappe._dict(kwargs["Body"]["stkCallback"])
 
 	checkout_id = getattr(transaction_response, "CheckoutRequestID", "")
-	request = frappe.get_doc("Integration Request", checkout_id)
-	transaction_data = frappe._dict(loads(request.data))
+	integration_request = frappe.get_doc("Integration Request", checkout_id)
+	transaction_data = frappe._dict(loads(integration_request.data))
+	total_paid = 0 # for multiple integration request made against a pos invoice
+	success = False # for reporting successfull callback to point of sale ui
 
 	if transaction_response['ResultCode'] == 0:
-		if request.reference_doctype and request.reference_docname:
+		if integration_request.reference_doctype and integration_request.reference_docname:
 			try:
-				doc = frappe.get_doc(request.reference_doctype,
-					request.reference_docname)
-				doc.run_method("on_payment_authorized", 'Completed')
-
 				item_response = transaction_response["CallbackMetadata"]["Item"]
+				amount = fetch_param_value(item_response, "Amount", "Name")
 				mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
-				frappe.db.set_value("POS Invoice", doc.reference_name, "mpesa_receipt_number", mpesa_receipt)
-				request.handle_success(transaction_response)
+				pr = frappe.get_doc(integration_request.reference_doctype, integration_request.reference_docname)
+
+				mpesa_receipts, completed_payments = get_completed_integration_requests_info(
+					integration_request.reference_doctype,
+					integration_request.reference_docname,
+					checkout_id
+				)
+
+				total_paid = amount + sum(completed_payments)
+				mpesa_receipts = ', '.join(mpesa_receipts + [mpesa_receipt])
+
+				if total_paid >= pr.grand_total:
+					pr.run_method("on_payment_authorized", 'Completed')
+					success = True
+
+				frappe.db.set_value("POS Invoice", pr.reference_name, "mpesa_receipt_number", mpesa_receipts)
+				integration_request.handle_success(transaction_response)
 			except Exception:
-				request.handle_failure(transaction_response)
+				integration_request.handle_failure(transaction_response)
 				frappe.log_error(frappe.get_traceback())
 
 	else:
-		request.handle_failure(transaction_response)
+		integration_request.handle_failure(transaction_response)
 
-	frappe.publish_realtime('process_phone_payment', doctype="POS Invoice",
-		docname=transaction_data.payment_reference, user=request.owner, message=transaction_response)
+	frappe.publish_realtime(
+		event='process_phone_payment',
+		doctype="POS Invoice",
+		docname=transaction_data.payment_reference,
+		user=integration_request.owner,
+		message={
+			'amount': total_paid,
+			'success': success,
+			'failure_message': transaction_response["ResultDesc"] if transaction_response['ResultCode'] != 0 else ''
+		},
+	)
+
+def get_completed_integration_requests_info(reference_doctype, reference_docname, checkout_id):
+	output_of_other_completed_requests = frappe.get_all("Integration Request", filters={
+		'name': ['!=', checkout_id],
+		'reference_doctype': reference_doctype,
+		'reference_docname': reference_docname,
+		'status': 'Completed'
+	}, pluck="output")
+
+	mpesa_receipts, completed_payments = [], []
+
+	for out in output_of_other_completed_requests:
+		out = frappe._dict(loads(out))
+		item_response = out["CallbackMetadata"]["Item"]
+		completed_amount = fetch_param_value(item_response, "Amount", "Name")
+		completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
+		completed_payments.append(completed_amount)
+		mpesa_receipts.append(completed_mpesa_receipt)
+	
+	return mpesa_receipts, completed_payments
 
 def get_account_balance(request_payload):
 	"""Call account balance API to send the request to the Mpesa Servers."""
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
index 49f6d95..2948796 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -9,6 +9,10 @@
 from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
 
 class TestMpesaSettings(unittest.TestCase):
+	def tearDown(self):
+		frappe.db.sql('delete from `tabMpesa Settings`')
+		frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"')
+
 	def test_creation_of_payment_gateway(self):
 		create_mpesa_settings(payment_gateway_name="_Test")
 
@@ -40,6 +44,8 @@
 			}
 		}))
 
+		integration_request.delete()
+
 	def test_processing_of_callback_payload(self):
 		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
@@ -56,10 +62,16 @@
 		# test payment request creation
 		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
 
-		callback_response = get_payment_callback_payload()
+		# submitting payment request creates integration requests with random id
+		integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+		}, pluck="name")
+
+		callback_response = get_payment_callback_payload(Amount=500, CheckoutRequestID=integration_req_ids[0])
 		verify_transaction(**callback_response)
 		# test creation of integration request
-		integration_request = frappe.get_doc("Integration Request", "ws_CO_061020201133231972")
+		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
 
 		# test integration request creation and successful update of the status on receiving callback response
 		self.assertTrue(integration_request)
@@ -69,8 +81,120 @@
 		integration_request.reload()
 		self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
 		self.assertEquals(integration_request.status, "Completed")
+		
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
+		integration_request.delete()
+		pr.reload()
+		pr.cancel()
+		pr.delete()
+		pos_invoice.delete()
+
+	def test_processing_of_multiple_callback_payload(self):
+		create_mpesa_settings(payment_gateway_name="Payment")
+		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
+		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
+
+		pos_invoice = create_pos_invoice(do_not_submit=1)
+		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000})
+		pos_invoice.contact_mobile = "093456543894"
+		pos_invoice.currency = "KES"
+		pos_invoice.save()
+
+		pr = pos_invoice.create_payment_request()
+		# test payment request creation
+		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+
+		# submitting payment request creates integration requests with random id
+		integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+		}, pluck="name")
+
+		# create random receipt nos and send it as response to callback handler
+		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
+
+		integration_requests = []
+		for i in range(len(integration_req_ids)):
+			callback_response = get_payment_callback_payload(
+				Amount=500,
+				CheckoutRequestID=integration_req_ids[i],
+				MpesaReceiptNumber=mpesa_receipt_numbers[i]
+			)
+			# handle response manually
+			verify_transaction(**callback_response)
+			# test completion of integration request
+			integration_request = frappe.get_doc("Integration Request", integration_req_ids[i])
+			self.assertEquals(integration_request.status, "Completed")
+			integration_requests.append(integration_request)
+
+		# check receipt number once all the integration requests are completed
+		pos_invoice.reload()
+		self.assertEquals(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers))
 
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
+		[d.delete() for d in integration_requests]
+		pr.reload()
+		pr.cancel()
+		pr.delete()
+		pos_invoice.delete()
+	
+	def test_processing_of_only_one_succes_callback_payload(self):
+		create_mpesa_settings(payment_gateway_name="Payment")
+		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
+		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
+
+		pos_invoice = create_pos_invoice(do_not_submit=1)
+		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000})
+		pos_invoice.contact_mobile = "093456543894"
+		pos_invoice.currency = "KES"
+		pos_invoice.save()
+
+		pr = pos_invoice.create_payment_request()
+		# test payment request creation
+		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+
+		# submitting payment request creates integration requests with random id
+		integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+		}, pluck="name")
+
+		# create random receipt nos and send it as response to callback handler
+		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
+
+		callback_response = get_payment_callback_payload(
+			Amount=500,
+			CheckoutRequestID=integration_req_ids[0],
+			MpesaReceiptNumber=mpesa_receipt_numbers[0]
+		)
+		# handle response manually
+		verify_transaction(**callback_response)
+		# test completion of integration request
+		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
+		self.assertEquals(integration_request.status, "Completed")
+
+		# now one request is completed
+		# second integration request fails
+		# now retrying payment request should make only one integration request again
+		pr = pos_invoice.create_payment_request()
+		new_integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+			'name': ['not in', integration_req_ids]
+		}, pluck="name")
+
+		self.assertEquals(len(new_integration_req_ids), 1)
+
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
+		frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")
+		pr.reload()
+		pr.cancel()
+		pr.delete()
+		pos_invoice.delete()
 
 def create_mpesa_settings(payment_gateway_name="Express"):
 	if frappe.db.exists("Mpesa Settings", payment_gateway_name):
@@ -160,16 +284,19 @@
 	}
 		}
 
-def get_payment_request_response_payload():
+def get_payment_request_response_payload(Amount=500):
 	"""Response received after successfully calling the stk push process request API."""
+
+	CheckoutRequestID = frappe.utils.random_string(10)
+
 	return {
 		"MerchantRequestID": "8071-27184008-1",
-		"CheckoutRequestID": "ws_CO_061020201133231972",
+		"CheckoutRequestID": CheckoutRequestID,
 		"ResultCode": 0,
 		"ResultDesc": "The service request is processed successfully.",
 		"CallbackMetadata": {
 			"Item": [
-				{ "Name": "Amount", "Value": 500.0 },
+				{ "Name": "Amount", "Value": Amount },
 				{ "Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R" },
 				{ "Name": "TransactionDate", "Value": 20201006113336 },
 				{ "Name": "PhoneNumber", "Value": 254723575670 }
@@ -177,41 +304,26 @@
 		}
 	}
 
-
-def get_payment_callback_payload():
+def get_payment_callback_payload(Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"):
 	"""Response received from the server as callback after calling the stkpush process request API."""
 	return {
 		"Body":{
-		"stkCallback":{
-			"MerchantRequestID":"19465-780693-1",
-			"CheckoutRequestID":"ws_CO_061020201133231972",
-			"ResultCode":0,
-			"ResultDesc":"The service request is processed successfully.",
-			"CallbackMetadata":{
-			"Item":[
-				{
-				"Name":"Amount",
-				"Value":500
-				},
-				{
-				"Name":"MpesaReceiptNumber",
-				"Value":"LGR7OWQX0R"
-				},
-				{
-				"Name":"Balance"
-				},
-				{
-				"Name":"TransactionDate",
-				"Value":20170727154800
-				},
-				{
-				"Name":"PhoneNumber",
-				"Value":254721566839
+			"stkCallback":{
+				"MerchantRequestID":"19465-780693-1",
+				"CheckoutRequestID":CheckoutRequestID,
+				"ResultCode":0,
+				"ResultDesc":"The service request is processed successfully.",
+				"CallbackMetadata":{
+					"Item":[
+						{ "Name":"Amount", "Value":Amount },
+						{ "Name":"MpesaReceiptNumber", "Value":MpesaReceiptNumber },
+						{ "Name":"Balance" },
+						{ "Name":"TransactionDate", "Value":20170727154800 },
+						{ "Name":"PhoneNumber", "Value":254721566839 }
+					]
 				}
-			]
 			}
 		}
-		}
 	}
 
 def get_account_balance_callback_payload():
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 66d0e5f..5f990cd 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -20,7 +20,7 @@
 			client_id=self.settings.plaid_client_id,
 			secret=self.settings.get_password("plaid_secret"),
 			environment=self.settings.plaid_env,
-			api_version="2019-05-29"
+			api_version="2020-09-14"
 		)
 
 	def get_access_token(self, public_token):
@@ -29,7 +29,7 @@
 		response = self.client.Item.public_token.exchange(public_token)
 		access_token = response["access_token"]
 		return access_token
-	
+
 	def get_token_request(self, update_mode=False):
 		country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"]
 		args = {
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 70c7f3f..21f6fee 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -204,8 +204,8 @@
 				"date": getdate(transaction["date"]),
 				"status": status,
 				"bank_account": bank_account,
-				"debit": debit,
-				"credit": credit,
+				"deposit": debit,
+				"withdrawal": credit,
 				"currency": transaction["iso_currency_code"],
 				"transaction_id": transaction["transaction_id"],
 				"reference_number": transaction["payment_meta"]["reference_number"],
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
index 15916a5..861675a 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
@@ -2,4 +2,82 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Appointment Type', {
+	refresh: function(frm) {
+		frm.set_query('price_list', function() {
+			return {
+				filters: {'selling': 1}
+			};
+		});
+
+		frm.set_query('medical_department', 'items', function(doc) {
+			let item_list = doc.items.map(({medical_department}) => medical_department);
+			return {
+				filters: [
+					['Medical Department', 'name', 'not in', item_list]
+				]
+			};
+		});
+
+		frm.set_query('op_consulting_charge_item', 'items', function() {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
+
+		frm.set_query('inpatient_visit_charge_item', 'items', function() {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
+	}
 });
+
+frappe.ui.form.on('Appointment Type Service Item', {
+	op_consulting_charge_item: function(frm, cdt, cdn) {
+		let d = locals[cdt][cdn];
+		if (frm.doc.price_list && d.op_consulting_charge_item) {
+			frappe.call({
+				'method': 'frappe.client.get_value',
+				args: {
+					'doctype': 'Item Price',
+					'filters': {
+						'item_code': d.op_consulting_charge_item,
+						'price_list': frm.doc.price_list
+					},
+					'fieldname': ['price_list_rate']
+				},
+				callback: function(data) {
+					if (data.message.price_list_rate) {
+						frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate);
+					}
+				}
+			});
+		}
+	},
+
+	inpatient_visit_charge_item: function(frm, cdt, cdn) {
+		let d = locals[cdt][cdn];
+		if (frm.doc.price_list && d.inpatient_visit_charge_item) {
+			frappe.call({
+				'method': 'frappe.client.get_value',
+				args: {
+					'doctype': 'Item Price',
+					'filters': {
+						'item_code': d.inpatient_visit_charge_item,
+						'price_list': frm.doc.price_list
+					},
+					'fieldname': ['price_list_rate']
+				},
+				callback: function (data) {
+					if (data.message.price_list_rate) {
+						frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate);
+					}
+				}
+			});
+		}
+	}
+});
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
index 58753bb..3872318 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
@@ -12,7 +12,10 @@
   "appointment_type",
   "ip",
   "default_duration",
-  "color"
+  "color",
+  "billing_section",
+  "price_list",
+  "items"
  ],
  "fields": [
   {
@@ -52,10 +55,27 @@
    "label": "Color",
    "no_copy": 1,
    "report_hide": 1
+  },
+  {
+   "fieldname": "billing_section",
+   "fieldtype": "Section Break",
+   "label": "Billing"
+  },
+  {
+   "fieldname": "price_list",
+   "fieldtype": "Link",
+   "label": "Price List",
+   "options": "Price List"
+  },
+  {
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "label": "Appointment Type Service Items",
+   "options": "Appointment Type Service Item"
   }
  ],
  "links": [],
- "modified": "2020-02-03 21:06:05.833050",
+ "modified": "2021-01-22 09:41:05.010524",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Appointment Type",
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
index 1dacffa..67a24f3 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.py
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
@@ -4,6 +4,53 @@
 
 from __future__ import unicode_literals
 from frappe.model.document import Document
+import frappe
 
 class AppointmentType(Document):
-	pass
+	def validate(self):
+		if self.items and self.price_list:
+			for item in self.items:
+				existing_op_item_price = frappe.db.exists('Item Price', {
+					'item_code': item.op_consulting_charge_item,
+					'price_list': self.price_list
+				})
+
+				if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge:
+					make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge)
+
+				existing_ip_item_price = frappe.db.exists('Item Price', {
+					'item_code': item.inpatient_visit_charge_item,
+					'price_list': self.price_list
+				})
+
+				if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge:
+					make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge)
+
+@frappe.whitelist()
+def get_service_item_based_on_department(appointment_type, department):
+	item_list = frappe.db.get_value('Appointment Type Service Item',
+		filters = {'medical_department': department, 'parent': appointment_type},
+		fieldname = ['op_consulting_charge_item',
+			'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
+		as_dict = 1
+	)
+
+	# if department wise items are not set up
+	# use the generic items
+	if not item_list:
+		item_list = frappe.db.get_value('Appointment Type Service Item',
+			filters = {'parent': appointment_type},
+			fieldname = ['op_consulting_charge_item',
+				'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
+			as_dict = 1
+		)
+
+	return item_list
+
+def make_item_price(price_list, item, item_price):
+	frappe.get_doc({
+		'doctype': 'Item Price',
+		'price_list': price_list,
+		'item_code': item,
+		'price_list_rate': item_price
+	}).insert(ignore_permissions=True, ignore_mandatory=True)
diff --git a/erpnext/accounts/doctype/bank_statement_settings_item/__init__.py b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
similarity index 100%
rename from erpnext/accounts/doctype/bank_statement_settings_item/__init__.py
rename to erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
new file mode 100644
index 0000000..5ff68cd
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
@@ -0,0 +1,67 @@
+{
+ "actions": [],
+ "creation": "2021-01-22 09:34:53.373105",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "medical_department",
+  "op_consulting_charge_item",
+  "op_consulting_charge",
+  "column_break_4",
+  "inpatient_visit_charge_item",
+  "inpatient_visit_charge"
+ ],
+ "fields": [
+  {
+   "fieldname": "medical_department",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Medical Department",
+   "options": "Medical Department"
+  },
+  {
+   "fieldname": "op_consulting_charge_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Out Patient Consulting Charge Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "op_consulting_charge",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Out Patient Consulting Charge"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "inpatient_visit_charge_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Inpatient Visit Charge Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "inpatient_visit_charge",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Inpatient Visit Charge Item"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-01-22 09:35:26.503443",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Appointment Type Service Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
new file mode 100644
index 0000000..b2e0e82
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class AppointmentTypeServiceItem(Document):
+	pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
index c324228..325c209 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
@@ -121,6 +121,7 @@
 
 		stock_entry.stock_entry_type = 'Material Receipt'
 		stock_entry.to_warehouse = self.warehouse
+		stock_entry.company = self.company
 		expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
 		for item in self.items:
 			if item.qty > item.actual_qty:
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
index 4ee5f6b..fb72073 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+	# -*- coding: utf-8 -*-
 # Copyright (c) 2017, ESS LLP and Contributors
 # See license.txt
 from __future__ import unicode_literals
@@ -60,6 +60,7 @@
 	procedure.practitioner = practitioner
 	procedure.consume_stock = procedure_template.allow_stock_consumption
 	procedure.items = procedure_template.items
-	procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse')
+	procedure.company = "_Test Company"
+	procedure.warehouse = "_Test Warehouse - _TC"
 	procedure.submit()
 	return procedure
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
index cb747f9..8162f03 100644
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
+++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
@@ -159,6 +159,7 @@
    "fieldname": "op_consulting_charge",
    "fieldtype": "Currency",
    "label": "Out Patient Consulting Charge",
+   "mandatory_depends_on": "op_consulting_charge_item",
    "options": "Currency"
   },
   {
@@ -174,7 +175,8 @@
   {
    "fieldname": "inpatient_visit_charge",
    "fieldtype": "Currency",
-   "label": "Inpatient Visit Charge"
+   "label": "Inpatient Visit Charge",
+   "mandatory_depends_on": "inpatient_visit_charge_item"
   },
   {
    "depends_on": "eval: !doc.__islocal",
@@ -280,7 +282,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-04-06 13:44:24.759623",
+ "modified": "2021-01-22 10:14:43.187675",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Healthcare Practitioner",
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 3d5073b..0354733 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -24,11 +24,13 @@
 		});
 
 		frm.set_query('practitioner', function() {
-			return {
-				filters: {
-					'department': frm.doc.department
-				}
-			};
+			if (frm.doc.department) {
+				return {
+					filters: {
+						'department': frm.doc.department
+					}
+				};
+			}
 		});
 
 		frm.set_query('service_unit', function() {
@@ -140,6 +142,20 @@
 	patient: function(frm) {
 		if (frm.doc.patient) {
 			frm.trigger('toggle_payment_fields');
+			frappe.call({
+				method: 'frappe.client.get',
+				args: {
+					doctype: 'Patient',
+					name: frm.doc.patient
+				},
+				callback: function (data) {
+					let age = null;
+					if (data.message.dob) {
+						age = calculate_age(data.message.dob);
+					}
+					frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age);
+				}
+			});
 		} else {
 			frm.set_value('patient_name', '');
 			frm.set_value('patient_sex', '');
@@ -148,6 +164,37 @@
 		}
 	},
 
+	practitioner: function(frm) {
+		if (frm.doc.practitioner ) {
+			frm.events.set_payment_details(frm);
+		}
+	},
+
+	appointment_type: function(frm) {
+		if (frm.doc.appointment_type) {
+			frm.events.set_payment_details(frm);
+		}
+	},
+
+	set_payment_details: function(frm) {
+		frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing').then(val => {
+			if (val) {
+				frappe.call({
+					method: 'erpnext.healthcare.utils.get_service_item_and_practitioner_charge',
+					args: {
+						doc: frm.doc
+					},
+					callback: function(data) {
+						if (data.message) {
+							frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.practitioner_charge);
+							frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.service_item);
+						}
+					}
+				});
+			}
+		});
+	},
+
 	therapy_plan: function(frm) {
 		frm.trigger('set_therapy_type_filter');
 	},
@@ -190,14 +237,18 @@
 					// show payment fields as non-mandatory
 					frm.toggle_display('mode_of_payment', 0);
 					frm.toggle_display('paid_amount', 0);
+					frm.toggle_display('billing_item', 0);
 					frm.toggle_reqd('mode_of_payment', 0);
 					frm.toggle_reqd('paid_amount', 0);
+					frm.toggle_reqd('billing_item', 0);
 				} else {
 					// if automated appointment invoicing is disabled, hide fields
 					frm.toggle_display('mode_of_payment', data.message ? 1 : 0);
 					frm.toggle_display('paid_amount', data.message ? 1 : 0);
+					frm.toggle_display('billing_item', data.message ? 1 : 0);
 					frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
 					frm.toggle_reqd('paid_amount', data.message ? 1 :0);
+					frm.toggle_reqd('billing_item', data.message ? 1 : 0);
 				}
 			}
 		});
@@ -540,57 +591,6 @@
 	);
 };
 
-frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) {
-	if (frm.doc.practitioner) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Healthcare Practitioner',
-				name: frm.doc.practitioner
-			},
-			callback: function (data) {
-				frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department);
-				frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge);
-				frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Patient Appointment', 'patient', function(frm) {
-	if (frm.doc.patient) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Patient',
-				name: frm.doc.patient
-			},
-			callback: function (data) {
-				let age = null;
-				if (data.message.dob) {
-					age = calculate_age(data.message.dob);
-				}
-				frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) {
-	if (frm.doc.appointment_type) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Appointment Type',
-				name: frm.doc.appointment_type
-			},
-			callback: function(data) {
-				frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration);
-			}
-		});
-	}
-});
-
 let calculate_age = function(birth) {
 	let ageMS = Date.parse(Date()) - Date.parse(birth);
 	let age = new Date();
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
index 35600e4..83c92af 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
@@ -19,19 +19,19 @@
   "inpatient_record",
   "column_break_1",
   "company",
+  "practitioner",
+  "practitioner_name",
+  "department",
   "service_unit",
+  "section_break_12",
+  "appointment_type",
+  "duration",
   "procedure_template",
   "get_procedure_from_encounter",
   "procedure_prescription",
   "therapy_plan",
   "therapy_type",
   "get_prescribed_therapies",
-  "practitioner",
-  "practitioner_name",
-  "department",
-  "section_break_12",
-  "appointment_type",
-  "duration",
   "column_break_17",
   "appointment_date",
   "appointment_time",
@@ -79,6 +79,7 @@
    "set_only_once": 1
   },
   {
+   "fetch_from": "appointment_type.default_duration",
    "fieldname": "duration",
    "fieldtype": "Int",
    "in_filter": 1,
@@ -144,7 +145,6 @@
    "in_standard_filter": 1,
    "label": "Healthcare Practitioner",
    "options": "Healthcare Practitioner",
-   "read_only": 1,
    "reqd": 1,
    "search_index": 1,
    "set_only_once": 1
@@ -158,7 +158,6 @@
    "in_standard_filter": 1,
    "label": "Department",
    "options": "Medical Department",
-   "read_only": 1,
    "search_index": 1,
    "set_only_once": 1
   },
@@ -227,12 +226,14 @@
    "fieldname": "mode_of_payment",
    "fieldtype": "Link",
    "label": "Mode of Payment",
-   "options": "Mode of Payment"
+   "options": "Mode of Payment",
+   "read_only_depends_on": "invoiced"
   },
   {
    "fieldname": "paid_amount",
    "fieldtype": "Currency",
-   "label": "Paid Amount"
+   "label": "Paid Amount",
+   "read_only_depends_on": "invoiced"
   },
   {
    "fieldname": "column_break_2",
@@ -302,7 +303,8 @@
    "fieldname": "therapy_plan",
    "fieldtype": "Link",
    "label": "Therapy Plan",
-   "options": "Therapy Plan"
+   "options": "Therapy Plan",
+   "set_only_once": 1
   },
   {
    "fieldname": "ref_sales_invoice",
@@ -347,7 +349,7 @@
   }
  ],
  "links": [],
- "modified": "2020-12-16 13:16:58.578503",
+ "modified": "2021-02-08 13:13:15.116833",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Patient Appointment",
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index f2b94b8..1f76cd6 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -26,6 +26,7 @@
 
 	def after_insert(self):
 		self.update_prescription_details()
+		self.set_payment_details()
 		invoice_appointment(self)
 		self.update_fee_validity()
 		send_confirmation_msg(self)
@@ -85,6 +86,13 @@
 	def set_appointment_datetime(self):
 		self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
 
+	def set_payment_details(self):
+		if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
+			details = get_service_item_and_practitioner_charge(self)
+			self.db_set('billing_item', details.get('service_item'))
+			if not self.paid_amount:
+				self.db_set('paid_amount', details.get('practitioner_charge'))
+
 	def validate_customer_created(self):
 		if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
 			if not frappe.db.get_value('Patient', self.patient, 'customer'):
@@ -148,31 +156,37 @@
 		fee_validity = None
 
 	if automate_invoicing and not appointment_invoiced and not fee_validity:
-		sales_invoice = frappe.new_doc('Sales Invoice')
-		sales_invoice.patient = appointment_doc.patient
-		sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
-		sales_invoice.appointment = appointment_doc.name
-		sales_invoice.due_date = getdate()
-		sales_invoice.company = appointment_doc.company
-		sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
+		create_sales_invoice(appointment_doc)
 
-		item = sales_invoice.append('items', {})
-		item = get_appointment_item(appointment_doc, item)
 
-		# Add payments if payment details are supplied else proceed to create invoice as Unpaid
-		if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
-			sales_invoice.is_pos = 1
-			payment = sales_invoice.append('payments', {})
-			payment.mode_of_payment = appointment_doc.mode_of_payment
-			payment.amount = appointment_doc.paid_amount
+def create_sales_invoice(appointment_doc):
+	sales_invoice = frappe.new_doc('Sales Invoice')
+	sales_invoice.patient = appointment_doc.patient
+	sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
+	sales_invoice.appointment = appointment_doc.name
+	sales_invoice.due_date = getdate()
+	sales_invoice.company = appointment_doc.company
+	sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
 
-		sales_invoice.set_missing_values(for_validate=True)
-		sales_invoice.flags.ignore_mandatory = True
-		sales_invoice.save(ignore_permissions=True)
-		sales_invoice.submit()
-		frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True)
-		frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1)
-		frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name)
+	item = sales_invoice.append('items', {})
+	item = get_appointment_item(appointment_doc, item)
+
+	# Add payments if payment details are supplied else proceed to create invoice as Unpaid
+	if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
+		sales_invoice.is_pos = 1
+		payment = sales_invoice.append('payments', {})
+		payment.mode_of_payment = appointment_doc.mode_of_payment
+		payment.amount = appointment_doc.paid_amount
+
+	sales_invoice.set_missing_values(for_validate=True)
+	sales_invoice.flags.ignore_mandatory = True
+	sales_invoice.save(ignore_permissions=True)
+	sales_invoice.submit()
+	frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True)
+	frappe.db.set_value('Patient Appointment', appointment_doc.name, {
+		'invoiced': 1,
+		'ref_sales_invoice': sales_invoice.name
+	})
 
 
 def check_is_new_patient(patient, name=None):
@@ -187,13 +201,14 @@
 
 
 def get_appointment_item(appointment_doc, item):
-	service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc)
-	item.item_code = service_item
+	details = get_service_item_and_practitioner_charge(appointment_doc)
+	charge = appointment_doc.paid_amount or details.get('practitioner_charge')
+	item.item_code = details.get('service_item')
 	item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner)
 	item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company)
 	item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center')
-	item.rate = practitioner_charge
-	item.amount = practitioner_charge
+	item.rate = charge
+	item.amount = charge
 	item.qty = 1
 	item.reference_dt = 'Patient Appointment'
 	item.reference_dn = appointment_doc.name
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index f7ec6f5..2bb8a53 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -32,7 +32,8 @@
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
 		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
-		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
+		appointment.reload()
+		self.assertEqual(appointment.invoiced, 1)
 		encounter = make_encounter(appointment.name)
 		self.assertTrue(encounter)
 		self.assertEqual(encounter.company, appointment.company)
@@ -41,7 +42,7 @@
 		# invoiced flag mapped from appointment
 		self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
 
-	def test_invoicing(self):
+	def test_auto_invoicing(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
@@ -57,6 +58,50 @@
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient)
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
 
+	def test_auto_invoicing_based_on_department(self):
+		patient, medical_department, practitioner = create_healthcare_docs()
+		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
+		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+		appointment_type = create_appointment_type()
+
+		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
+			invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department')
+		appointment.reload()
+
+		self.assertEqual(appointment.invoiced, 1)
+		self.assertEqual(appointment.billing_item, 'HLC-SI-001')
+		self.assertEqual(appointment.paid_amount, 200)
+
+		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+		self.assertTrue(sales_invoice_name)
+		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
+
+	def test_auto_invoicing_according_to_appointment_type_charge(self):
+		patient, medical_department, practitioner = create_healthcare_docs()
+		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
+		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+
+		item = create_healthcare_service_items()
+		items = [{
+				'op_consulting_charge_item': item,
+				'op_consulting_charge': 300
+		}]
+		appointment_type = create_appointment_type(args={
+				'name': 'Generic Appointment Type charge',
+				'items': items
+			})
+
+		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
+			invoice=1, appointment_type=appointment_type.name)
+		appointment.reload()
+
+		self.assertEqual(appointment.invoiced, 1)
+		self.assertEqual(appointment.billing_item, item)
+		self.assertEqual(appointment.paid_amount, 300)
+
+		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+		self.assertTrue(sales_invoice_name)
+
 	def test_appointment_cancel(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
@@ -178,14 +223,15 @@
 		encounter.submit()
 		return encounter
 
-def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0, service_unit=None, save=1):
+def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0,
+	service_unit=None, appointment_type=None, save=1, department=None):
 	item = create_healthcare_service_items()
 	frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item)
 	frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item)
 	appointment = frappe.new_doc('Patient Appointment')
 	appointment.patient = patient
 	appointment.practitioner = practitioner
-	appointment.department = '_Test Medical Department'
+	appointment.department = department or '_Test Medical Department'
 	appointment.appointment_date = appointment_date
 	appointment.company = '_Test Company'
 	appointment.duration = 15
@@ -193,7 +239,8 @@
 		appointment.service_unit = service_unit
 	if invoice:
 		appointment.mode_of_payment = 'Cash'
-		appointment.paid_amount = 500
+	if appointment_type:
+		appointment.appointment_type = appointment_type
 	if procedure_template:
 		appointment.procedure_template = create_clinical_procedure_template().get('name')
 	if save:
@@ -223,4 +270,29 @@
 	template.description = 'Knee Surgery and Rehab'
 	template.rate = 50000
 	template.save()
-	return template
\ No newline at end of file
+	return template
+
+def create_appointment_type(args=None):
+	if not args:
+		args =  frappe.local.form_dict
+
+	name = args.get('name') or 'Test Appointment Type wise Charge'
+
+	if frappe.db.exists('Appointment Type', name):
+		return frappe.get_doc('Appointment Type', name)
+
+	else:
+		item = create_healthcare_service_items()
+		items = [{
+				'medical_department': '_Test Medical Department',
+				'op_consulting_charge_item': item,
+				'op_consulting_charge': 200
+		}]
+		return frappe.get_doc({
+			'doctype': 'Appointment Type',
+			'appointment_type': args.get('name') or 'Test Appointment Type wise Charge',
+			'default_duration': args.get('default_duration') or 20,
+			'color': args.get('color') or '#7575ff',
+			'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}),
+			'items': args.get('items') or items
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py
index d4027df..d3d22c8 100644
--- a/erpnext/healthcare/utils.py
+++ b/erpnext/healthcare/utils.py
@@ -5,9 +5,11 @@
 from __future__ import unicode_literals
 import math
 import frappe
+import json
 from frappe import _
 from frappe.utils.formatters import format_value
 from frappe.utils import time_diff_in_hours, rounded
+from six import string_types
 from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
 from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
 from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
@@ -64,7 +66,9 @@
 			income_account = None
 			service_item = None
 			if appointment.practitioner:
-				service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
+				details = get_service_item_and_practitioner_charge(appointment)
+				service_item = details.get('service_item')
+				practitioner_charge = details.get('practitioner_charge')
 				income_account = get_income_account(appointment.practitioner, appointment.company)
 			appointments_to_invoice.append({
 				'reference_type': 'Patient Appointment',
@@ -97,7 +101,9 @@
 						frappe.db.get_single_value('Healthcare Settings', 'do_not_bill_inpatient_encounters'):
 						continue
 
-					service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter)
+					details = get_service_item_and_practitioner_charge(encounter)
+					service_item = details.get('service_item')
+					practitioner_charge = details.get('practitioner_charge')
 					income_account = get_income_account(encounter.practitioner, encounter.company)
 
 				encounters_to_invoice.append({
@@ -173,7 +179,7 @@
 		if procedure.invoice_separately_as_consumables and procedure.consume_stock \
 			and procedure.status == 'Completed' and not procedure.consumption_invoiced:
 
-			service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
+			service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
 			if not service_item:
 				msg = _('Please Configure Clinical Procedure Consumable Item in ')
 				msg += '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>'''
@@ -304,24 +310,50 @@
 
 	return therapy_sessions_to_invoice
 
-
+@frappe.whitelist()
 def get_service_item_and_practitioner_charge(doc):
+	if isinstance(doc, string_types):
+		doc = json.loads(doc)
+		doc = frappe.get_doc(doc)
+
+	service_item = None
+	practitioner_charge = None
+	department = doc.medical_department if doc.doctype == 'Patient Encounter' else doc.department
+
 	is_inpatient = doc.inpatient_record
-	if is_inpatient:
-		service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item')
+
+	if doc.get('appointment_type'):
+		service_item, practitioner_charge = get_appointment_type_service_item(doc.appointment_type, department, is_inpatient)
+
+	if not service_item and not practitioner_charge:
+		service_item, practitioner_charge = get_practitioner_service_item(doc.practitioner, is_inpatient)
 		if not service_item:
-			service_item = get_healthcare_service_item('inpatient_visit_charge_item')
-	else:
-		service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item')
-		if not service_item:
-			service_item = get_healthcare_service_item('op_consulting_charge_item')
+			service_item = get_healthcare_service_item(is_inpatient)
+
 	if not service_item:
 		throw_config_service_item(is_inpatient)
 
-	practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient)
 	if not practitioner_charge:
 		throw_config_practitioner_charge(is_inpatient, doc.practitioner)
 
+	return {'service_item': service_item, 'practitioner_charge': practitioner_charge}
+
+
+def get_appointment_type_service_item(appointment_type, department, is_inpatient):
+	from erpnext.healthcare.doctype.appointment_type.appointment_type import get_service_item_based_on_department
+
+	item_list = get_service_item_based_on_department(appointment_type, department)
+	service_item = None
+	practitioner_charge = None
+
+	if item_list:
+		if is_inpatient:
+			service_item = item_list.get('inpatient_visit_charge_item')
+			practitioner_charge = item_list.get('inpatient_visit_charge')
+		else:
+			service_item = item_list.get('op_consulting_charge_item')
+			practitioner_charge = item_list.get('op_consulting_charge')
+
 	return service_item, practitioner_charge
 
 
@@ -345,12 +377,27 @@
 	frappe.throw(msg, title=_('Missing Configuration'))
 
 
-def get_practitioner_service_item(practitioner, service_item_field):
-	return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field)
+def get_practitioner_service_item(practitioner, is_inpatient):
+	service_item = None
+	practitioner_charge = None
+
+	if is_inpatient:
+		service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['inpatient_visit_charge_item', 'inpatient_visit_charge'])
+	else:
+		service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['op_consulting_charge_item', 'op_consulting_charge'])
+
+	return service_item, practitioner_charge
 
 
-def get_healthcare_service_item(service_item_field):
-	return frappe.db.get_single_value('Healthcare Settings', service_item_field)
+def get_healthcare_service_item(is_inpatient):
+	service_item = None
+
+	if is_inpatient:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'inpatient_visit_charge_item')
+	else:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'op_consulting_charge_item')
+
+	return service_item
 
 
 def get_practitioner_charge(practitioner, is_inpatient):
@@ -381,7 +428,8 @@
 		invoiced = True
 
 	if item.reference_dt == 'Clinical Procedure':
-		if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
+		if service_item == item.item_code:
 			frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced)
 		else:
 			frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
@@ -403,7 +451,8 @@
 
 
 def validate_invoiced_on_submit(item):
-	if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
+	if item.reference_dt == 'Clinical Procedure' and \
+		frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') == item.item_code:
 		is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced')
 	else:
 		is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced')
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 109d921..39d3659 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -272,6 +272,9 @@
 	'Address': {
 		'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category']
 	},
+	'Supplier': {
+		'validate': 'erpnext.regional.india.utils.validate_pan_for_india'
+	},
 	('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
 		'validate': ['erpnext.regional.india.utils.set_place_of_supply']
 	},
@@ -399,6 +402,7 @@
 		'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header': 'erpnext.regional.india.utils.get_itemised_tax_breakup_header',
 		'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data',
 		'erpnext.accounts.party.get_regional_address_details': 'erpnext.regional.india.utils.get_regional_address_details',
+		'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts',
 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
 		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py
index c397a3f..7e650f7 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -16,7 +16,7 @@
 
 	def validate(self):
 		self.validate_vacancies()
-		job_offer = frappe.db.exists("Job Offer",{"job_applicant": self.job_applicant})
+		job_offer = frappe.db.exists("Job Offer",{"job_applicant": self.job_applicant, "docstatus": ["!=", 2]})
 		if job_offer and job_offer != self.name:
 			frappe.throw(_("Job Offer: {0} is already for Job Applicant: {1}").format(frappe.bold(job_offer), frappe.bold(self.job_applicant)))
 
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
index 6324b04..9f667a6 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
@@ -4,11 +4,11 @@
 	<thead>
 		<tr>
 			<th style="width: 16%">{{ __("Leave Type") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Total Allocated Leaves") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Expired Leaves") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Used Leaves") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Pending Leaves") }}</th>
-			<th style="width: 16%" class="text-right">{{ __("Available Leaves") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Total Allocated Leave") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Expired Leave") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Used Leave") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Pending Leave") }}</th>
+			<th style="width: 16%" class="text-right">{{ __("Available Leave") }}</th>
 		</tr>
 	</thead>
 	<tbody>
@@ -25,5 +25,5 @@
 	</tbody>
 </table>
 {% else %}
-<p style="margin-top: 30px;"> No Leaves have been allocated. </p>
-{% endif %}
\ No newline at end of file
+<p style="margin-top: 30px;"> No Leave has been allocated. </p>
+{% endif %}
diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js
index cbb4b73..a3c03b1 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_list.js
+++ b/erpnext/hr/doctype/leave_application/leave_application_list.js
@@ -1,5 +1,6 @@
 frappe.listview_settings['Leave Application'] = {
 	add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"],
+	has_indicator_for_draft: 1,
 	get_indicator: function (doc) {
 		if (doc.status === "Approved") {
 			return [__("Approved"), "green", "status,=,Approved"];
diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
index 1b92358..06f9160 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -40,17 +40,17 @@
 		'fieldname': 'opening_balance',
 		'width': 130,
 	}, {
-		'label': _('Leaves Allocated'),
+		'label': _('Leave Allocated'),
 		'fieldtype': 'float',
 		'fieldname': 'leaves_allocated',
 		'width': 130,
 	}, {
-		'label': _('Leaves Taken'),
+		'label': _('Leave Taken'),
 		'fieldtype': 'float',
 		'fieldname': 'leaves_taken',
 		'width': 130,
 	}, {
-		'label': _('Leaves Expired'),
+		'label': _('Leave Expired'),
 		'fieldtype': 'float',
 		'fieldname': 'leaves_expired',
 		'width': 130,
diff --git a/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json
new file mode 100644
index 0000000..b8abf21
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/loan_disbursements/loan_disbursements.json
@@ -0,0 +1,29 @@
+{
+ "based_on": "disbursement_date",
+ "chart_name": "Loan Disbursements",
+ "chart_type": "Sum",
+ "creation": "2021-02-06 18:40:36.148470",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "Loan Disbursement",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "modified": "2021-02-06 18:40:49.308663",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Disbursements",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "",
+ "time_interval": "Daily",
+ "timeseries": 1,
+ "timespan": "Last Month",
+ "type": "Line",
+ "use_report_chart": 0,
+ "value_based_on": "disbursed_amount",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json
new file mode 100644
index 0000000..aa0f78a
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/loan_interest_accrual/loan_interest_accrual.json
@@ -0,0 +1,31 @@
+{
+ "based_on": "posting_date",
+ "chart_name": "Loan Interest Accrual",
+ "chart_type": "Sum",
+ "color": "#39E4A5",
+ "creation": "2021-02-18 20:07:04.843876",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "Loan Interest Accrual",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Interest Accrual\",\"docstatus\",\"=\",\"1\",false]]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "last_synced_on": "2021-02-21 21:01:26.022634",
+ "modified": "2021-02-21 21:01:44.930712",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Interest Accrual",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "",
+ "time_interval": "Monthly",
+ "timeseries": 1,
+ "timespan": "Last Year",
+ "type": "Line",
+ "use_report_chart": 0,
+ "value_based_on": "interest_amount",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json
new file mode 100644
index 0000000..35bd43b
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/new_loans/new_loans.json
@@ -0,0 +1,31 @@
+{
+ "based_on": "creation",
+ "chart_name": "New Loans",
+ "chart_type": "Count",
+ "color": "#449CF0",
+ "creation": "2021-02-06 16:59:27.509170",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false]]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "last_synced_on": "2021-02-21 20:55:33.515025",
+ "modified": "2021-02-21 21:00:33.900821",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "New Loans",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "",
+ "time_interval": "Daily",
+ "timeseries": 1,
+ "timespan": "Last Month",
+ "type": "Bar",
+ "use_report_chart": 0,
+ "value_based_on": "",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
new file mode 100644
index 0000000..76c27b0
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
@@ -0,0 +1,31 @@
+{
+ "based_on": "",
+ "chart_name": "Top 10 Pledged Loan Securities",
+ "chart_type": "Custom",
+ "color": "#EC864B",
+ "creation": "2021-02-06 22:02:46.284479",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart",
+ "document_type": "",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[]",
+ "group_by_type": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "last_synced_on": "2021-02-21 21:00:57.043034",
+ "modified": "2021-02-21 21:01:10.048623",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Top 10 Pledged Loan Securities",
+ "number_of_groups": 0,
+ "owner": "Administrator",
+ "source": "Top 10 Pledged Loan Securities",
+ "time_interval": "Yearly",
+ "timeseries": 0,
+ "timespan": "Last Year",
+ "type": "Bar",
+ "use_report_chart": 0,
+ "value_based_on": "",
+ "y_axis": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/loan_management/dashboard_chart_source/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/loan_management/dashboard_chart_source/__init__.py
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/__init__.py
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
new file mode 100644
index 0000000..cf75cc8
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js
@@ -0,0 +1,14 @@
+frappe.provide('frappe.dashboards.chart_sources');
+
+frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = {
+	method: "erpnext.loan_management.dashboard_chart_source.top_10_pledged_loan_securities.top_10_pledged_loan_securities.get_data",
+	filters: [
+		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company")
+		}
+	]
+};
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
new file mode 100644
index 0000000..42c9b1c
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.json
@@ -0,0 +1,13 @@
+{
+ "creation": "2021-02-06 22:01:01.332628",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart Source",
+ "idx": 0,
+ "modified": "2021-02-06 22:01:01.332628",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Top 10 Pledged Loan Securities",
+ "owner": "Administrator",
+ "source_name": "Top 10 Pledged Loan Securities ",
+ "timeseries": 0
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
new file mode 100644
index 0000000..6bb0440
--- /dev/null
+++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py
@@ -0,0 +1,76 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils.dashboard import cache_source
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
+	 import get_loan_security_details
+from six import iteritems
+
+@frappe.whitelist()
+@cache_source
+def get_data(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
+	to_date = None, timespan = None, time_interval = None, heatmap_year = None):
+	if chart_name:
+		chart = frappe.get_doc('Dashboard Chart', chart_name)
+	else:
+		chart = frappe._dict(frappe.parse_json(chart))
+
+	filters = {}
+	current_pledges = {}
+
+	if filters:
+		filters = frappe.parse_json(filters)[0]
+
+	conditions = ""
+	labels = []
+	values = []
+
+	if filters.get('company'):
+		conditions = "AND company = %(company)s"
+
+	loan_security_details = get_loan_security_details()
+
+	unpledges = frappe._dict(frappe.db.sql("""
+		SELECT u.loan_security, sum(u.qty) as qty
+		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
+		WHERE u.parent = up.name
+		AND up.status = 'Approved'
+		{conditions}
+		GROUP BY u.loan_security
+	""".format(conditions=conditions), filters, as_list=1))
+
+	pledges = frappe._dict(frappe.db.sql("""
+		SELECT p.loan_security, sum(p.qty) as qty
+		FROM `tabLoan Security Pledge` lp, `tabPledge`p
+		WHERE p.parent = lp.name
+		AND lp.status = 'Pledged'
+		{conditions}
+		GROUP BY p.loan_security
+	""".format(conditions=conditions), filters, as_list=1))
+
+	for security, qty in iteritems(pledges):
+		current_pledges.setdefault(security, qty)
+		current_pledges[security] -= unpledges.get(security, 0.0)
+
+	sorted_pledges = dict(sorted(current_pledges.items(), key=lambda item: item[1], reverse=True))
+
+	count = 0
+	for security, qty in iteritems(sorted_pledges):
+		values.append(qty * loan_security_details.get(security, {}).get('latest_price', 0))
+		labels.append(security)
+		count +=1
+
+		## Just need top 10 securities
+		if count == 10:
+			break
+
+	return {
+		'labels': labels,
+		'datasets': [{
+			'name': 'Top 10 Securities',
+			'chartType': 'bar',
+			'values': values
+		}]
+	}
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index e607d4f..83a813f 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -201,7 +201,9 @@
 	write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount')
 
 	# checking greater than 0 as there may be some minor precision error
-	if pending_amount < write_off_limit:
+	if not pending_amount:
+		frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
+	elif pending_amount < write_off_limit:
 		# Auto create loan write off and update status as loan closure requested
 		write_off = make_loan_write_off(loan)
 		write_off.submit()
@@ -348,3 +350,13 @@
 	if employee_currency != company_currency:
 		frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
 			.format(applicant, employee_currency))
+
+@frappe.whitelist()
+def get_shortfall_applicants():
+	loans = frappe.get_all('Loan Security Shortfall', {'status': 'Pending'}, pluck='loan')
+	applicants = set(frappe.get_all('Loan', {'name': ('in', loans)}, pluck='name'))
+
+	return {
+		"value": len(applicants),
+		"fieldtype": "Int"
+	}
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index f3c9db6..13a2094 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -547,7 +547,7 @@
 
 		# 30 days - grace period
 		penalty_days = 30 - 4
-		penalty_applicable_amount = flt(amounts['interest_amount']/2, 2)
+		penalty_applicable_amount = flt(amounts['interest_amount']/2)
 		penalty_amount = flt((((penalty_applicable_amount * 25) / 100) * penalty_days), 2)
 		process = process_loan_interest_accrual_for_demand_loans(posting_date = '2019-11-30')
 
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index e59db4c..9c0147e 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -197,7 +197,7 @@
 			security.qty = cint(security.amount/security.loan_security_price)
 
 		security.amount = security.qty * security.loan_security_price
-		security.post_haircut_amount = security.amount - (security.amount * security.haircut/100)
+		security.post_haircut_amount = cint(security.amount - (security.amount * security.haircut/100))
 
 		maximum_loan_amount += security.post_haircut_amount
 
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 7d7992d..7978350 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -246,7 +246,5 @@
 	if not posting_date:
 		posting_date = getdate()
 
-	precision = cint(frappe.db.get_default("currency_precision")) or 2
-
-	return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100), precision)
+	return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100))
 
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index ac30c91..bac06c4 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -81,8 +81,8 @@
 				last_accrual_date = get_last_accrual_date(self.against_loan)
 
 				# get posting date upto which interest has to be accrued
-				per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
-					self.rate_of_interest, self.posting_date), 2)
+				per_day_interest = get_per_day_interest(self.pending_principal_amount,
+					self.rate_of_interest, self.posting_date)
 
 				no_of_days = flt(flt(self.total_interest_paid - self.interest_payable,
 					precision)/per_day_interest, 0) - 1
@@ -105,8 +105,6 @@
 				})
 
 	def update_paid_amount(self):
-		precision = cint(frappe.db.get_default("currency_precision")) or 2
-
 		loan = frappe.get_doc("Loan", self.against_loan)
 
 		for payment in self.repayment_details:
@@ -114,7 +112,7 @@
 				SET paid_principal_amount = `paid_principal_amount` + %s,
 					paid_interest_amount = `paid_interest_amount` + %s
 				WHERE name = %s""",
-				(flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual))
+				(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
 
 		frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
 			WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
@@ -148,8 +146,6 @@
 			frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
 
 	def allocate_amounts(self, repayment_details):
-		precision = cint(frappe.db.get_default("currency_precision")) or 2
-
 		self.set('repayment_details', [])
 		self.principal_amount_paid = 0
 		total_interest_paid = 0
@@ -185,21 +181,18 @@
 			# no of days for which to accrue interest
 			# Interest can only be accrued for an entire day and not partial
 			if interest_paid > repayment_details['unaccrued_interest']:
-				per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
-					self.rate_of_interest, self.posting_date), precision)
 				interest_paid -= repayment_details['unaccrued_interest']
 				total_interest_paid += repayment_details['unaccrued_interest']
 			else:
 				# get no of days for which interest can be paid
-				per_day_interest = flt(get_per_day_interest(self.pending_principal_amount,
-					self.rate_of_interest, self.posting_date), precision)
+				per_day_interest = get_per_day_interest(self.pending_principal_amount,
+					self.rate_of_interest, self.posting_date)
 
 				no_of_days = cint(interest_paid/per_day_interest)
 				total_interest_paid += no_of_days * per_day_interest
 				interest_paid -= no_of_days * per_day_interest
 
 		self.total_interest_paid = total_interest_paid
-
 		if interest_paid:
 			self.principal_amount_paid += interest_paid
 
@@ -369,7 +362,7 @@
 	if pending_days > 0:
 		principal_amount = flt(pending_principal_amount, precision)
 		per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date)
-		unaccrued_interest += (pending_days * flt(per_day_interest, precision))
+		unaccrued_interest += (pending_days * per_day_interest)
 
 	amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
 	amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
diff --git a/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json
new file mode 100644
index 0000000..e060253
--- /dev/null
+++ b/erpnext/loan_management/loan_management_dashboard/loan_dashboard/loan_dashboard.json
@@ -0,0 +1,70 @@
+{
+ "cards": [
+  {
+   "card": "New Loans"
+  },
+  {
+   "card": "Active Loans"
+  },
+  {
+   "card": "Closed Loans"
+  },
+  {
+   "card": "Total Disbursed"
+  },
+  {
+   "card": "Open Loan Applications"
+  },
+  {
+   "card": "New Loan Applications"
+  },
+  {
+   "card": "Total Sanctioned Amount"
+  },
+  {
+   "card": "Active Securities"
+  },
+  {
+   "card": "Applicants With Unpaid Shortfall"
+  },
+  {
+   "card": "Total Shortfall Amount"
+  },
+  {
+   "card": "Total Repayment"
+  },
+  {
+   "card": "Total Write Off"
+  }
+ ],
+ "charts": [
+  {
+   "chart": "New Loans",
+   "width": "Half"
+  },
+  {
+   "chart": "Loan Disbursements",
+   "width": "Half"
+  },
+  {
+   "chart": "Top 10 Pledged Loan Securities",
+   "width": "Half"
+  },
+  {
+   "chart": "Loan Interest Accrual",
+   "width": "Half"
+  }
+ ],
+ "creation": "2021-02-06 16:52:43.484752",
+ "dashboard_name": "Loan Dashboard",
+ "docstatus": 0,
+ "doctype": "Dashboard",
+ "idx": 0,
+ "is_default": 0,
+ "is_standard": 1,
+ "modified": "2021-02-21 20:53:47.531699",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Dashboard",
+ "owner": "Administrator"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/active_loans/active_loans.json b/erpnext/loan_management/number_card/active_loans/active_loans.json
new file mode 100644
index 0000000..7e0db47
--- /dev/null
+++ b/erpnext/loan_management/number_card/active_loans/active_loans.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:10:26.132493",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"in\",[\"Disbursed\",\"Partially Disbursed\",null],false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Active Loans",
+ "modified": "2021-02-06 17:29:20.304087",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Active Loans",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/active_securities/active_securities.json b/erpnext/loan_management/number_card/active_securities/active_securities.json
new file mode 100644
index 0000000..298e410
--- /dev/null
+++ b/erpnext/loan_management/number_card/active_securities/active_securities.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 19:07:21.344199",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Security",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Security\",\"disabled\",\"=\",0,false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Active Securities",
+ "modified": "2021-02-06 19:07:26.671516",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Active Securities",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json
new file mode 100644
index 0000000..3b9eba1
--- /dev/null
+++ b/erpnext/loan_management/number_card/applicants_with_unpaid_shortfall/applicants_with_unpaid_shortfall.json
@@ -0,0 +1,21 @@
+{
+ "creation": "2021-02-07 18:55:12.632616",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "filters_json": "null",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Applicants With Unpaid Shortfall",
+ "method": "erpnext.loan_management.doctype.loan.loan.get_shortfall_applicants",
+ "modified": "2021-02-07 21:46:27.369795",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Applicants With Unpaid Shortfall",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Custom"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/closed_loans/closed_loans.json b/erpnext/loan_management/number_card/closed_loans/closed_loans.json
new file mode 100644
index 0000000..c2f2244
--- /dev/null
+++ b/erpnext/loan_management/number_card/closed_loans/closed_loans.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-21 19:51:49.261813",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Closed\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Closed Loans",
+ "modified": "2021-02-21 19:51:54.087903",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Closed Loans",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json
new file mode 100644
index 0000000..65c8ce6
--- /dev/null
+++ b/erpnext/loan_management/number_card/last_interest_accrual/last_interest_accrual.json
@@ -0,0 +1,21 @@
+{
+ "creation": "2021-02-07 21:57:14.758007",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "filters_json": "null",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Last Interest Accrual",
+ "method": "erpnext.loan_management.doctype.loan.loan.get_last_accrual_date",
+ "modified": "2021-02-07 21:59:47.525197",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Last Interest Accrual",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Custom"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json
new file mode 100644
index 0000000..7e655ff
--- /dev/null
+++ b/erpnext/loan_management/number_card/new_loan_applications/new_loan_applications.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:59:10.051269",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Application",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"creation\",\"Timespan\",\"today\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "New Loan Applications",
+ "modified": "2021-02-06 17:59:21.880979",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "New Loan Applications",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/new_loans/new_loans.json b/erpnext/loan_management/number_card/new_loans/new_loans.json
new file mode 100644
index 0000000..424f0f1
--- /dev/null
+++ b/erpnext/loan_management/number_card/new_loans/new_loans.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:56:34.624031",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"creation\",\"Timespan\",\"today\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "New Loans",
+ "modified": "2021-02-06 17:58:20.209166",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "New Loans",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json
new file mode 100644
index 0000000..1d5e84e
--- /dev/null
+++ b/erpnext/loan_management/number_card/open_loan_applications/open_loan_applications.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "",
+ "creation": "2021-02-06 17:23:32.509899",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Application",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Application\",\"docstatus\",\"=\",\"1\",false],[\"Loan Application\",\"status\",\"=\",\"Open\",false]]",
+ "function": "Count",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Open Loan Applications",
+ "modified": "2021-02-06 17:29:09.761011",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Open Loan Applications",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json
new file mode 100644
index 0000000..4a3f869
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_disbursed/total_disbursed.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "disbursed_amount",
+ "creation": "2021-02-06 16:52:19.505462",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Disbursement",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Disbursement\",\"docstatus\",\"=\",\"1\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Disbursed Amount",
+ "modified": "2021-02-06 17:29:38.453870",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Disbursed",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_repayment/total_repayment.json b/erpnext/loan_management/number_card/total_repayment/total_repayment.json
new file mode 100644
index 0000000..38de42b
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_repayment/total_repayment.json
@@ -0,0 +1,24 @@
+{
+ "aggregate_function_based_on": "amount_paid",
+ "color": "#29CD42",
+ "creation": "2021-02-21 19:27:45.989222",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Repayment",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Repayment\",\"docstatus\",\"=\",\"1\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Repayment",
+ "modified": "2021-02-21 19:34:59.656546",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Repayment",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json
new file mode 100644
index 0000000..dfb9d24
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_sanctioned_amount/total_sanctioned_amount.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "loan_amount",
+ "creation": "2021-02-06 17:05:04.704162",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan\",\"docstatus\",\"=\",\"1\",false],[\"Loan\",\"status\",\"=\",\"Sanctioned\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Sanctioned Amount",
+ "modified": "2021-02-06 17:29:29.930557",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Sanctioned Amount",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Monthly",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json
new file mode 100644
index 0000000..aa6b093
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_shortfall_amount/total_shortfall_amount.json
@@ -0,0 +1,23 @@
+{
+ "aggregate_function_based_on": "shortfall_amount",
+ "creation": "2021-02-09 08:07:20.096995",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Security Shortfall",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Unpaid Shortfall Amount",
+ "modified": "2021-02-09 08:09:00.355547",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Shortfall Amount",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/number_card/total_write_off/total_write_off.json b/erpnext/loan_management/number_card/total_write_off/total_write_off.json
new file mode 100644
index 0000000..c85169a
--- /dev/null
+++ b/erpnext/loan_management/number_card/total_write_off/total_write_off.json
@@ -0,0 +1,24 @@
+{
+ "aggregate_function_based_on": "write_off_amount",
+ "color": "#CB2929",
+ "creation": "2021-02-21 19:48:29.004429",
+ "docstatus": 0,
+ "doctype": "Number Card",
+ "document_type": "Loan Write Off",
+ "dynamic_filters_json": "[]",
+ "filters_json": "[[\"Loan Write Off\",\"docstatus\",\"=\",\"1\",false]]",
+ "function": "Sum",
+ "idx": 0,
+ "is_public": 0,
+ "is_standard": 1,
+ "label": "Total Write Off",
+ "modified": "2021-02-21 19:48:58.604159",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Total Write Off",
+ "owner": "Administrator",
+ "report_function": "Sum",
+ "show_percentage_stats": 1,
+ "stats_time_interval": "Daily",
+ "type": "Document Type"
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
index ab586bc..0ccd149 100644
--- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
+++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
@@ -36,7 +36,7 @@
 
 def get_data(filters):
 	data = []
-	loan_security_details = get_loan_security_details(filters)
+	loan_security_details = get_loan_security_details()
 	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
 		loan_security_details)
 
@@ -64,7 +64,7 @@
 
 	return data
 
-def get_loan_security_details(filters):
+def get_loan_security_details():
 	security_detail_map = {}
 	loan_security_price_map = {}
 	lsp_validity_map = {}
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
index a3e69bb..0f72c3c 100644
--- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
@@ -171,7 +171,7 @@
 	return current_pledges
 
 def get_loan_wise_security_value(filters, current_pledges):
-	loan_security_details = get_loan_security_details(filters)
+	loan_security_details = get_loan_security_details()
 	loan_wise_security_value = {}
 
 	for key in current_pledges:
diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
index adc8013..887a86a 100644
--- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
+++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
@@ -35,7 +35,7 @@
 
 def get_data(filters):
 	data = []
-	loan_security_details = get_loan_security_details(filters)
+	loan_security_details = get_loan_security_details()
 	current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details)
 	currency = erpnext.get_company_currency(filters.get('company'))
 
@@ -76,7 +76,7 @@
 		if qty:
 			security_wise_map[key[1]]['applicant_count'] += 1
 
-		total_portfolio_value += flt(qty * loan_security_details.get(key[1])['latest_price'])
+		total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
 
 	return security_wise_map, total_portfolio_value
 
diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json
index 2e8b5bf..18559dc 100644
--- a/erpnext/loan_management/workspace/loan_management/loan_management.json
+++ b/erpnext/loan_management/workspace/loan_management/loan_management.json
@@ -10,6 +10,7 @@
  "hide_custom": 0,
  "icon": "loan",
  "idx": 0,
+ "is_default": 0,
  "is_standard": 1,
  "label": "Loan Management",
  "links": [
@@ -219,7 +220,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-01-12 11:27:56.079724",
+ "modified": "2021-02-18 17:31:53.586508",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Management",
@@ -239,6 +240,12 @@
    "label": "Loan",
    "link_to": "Loan",
    "type": "DocType"
+  },
+  {
+   "doc_view": "",
+   "label": "Dashboard",
+   "link_to": "Loan Dashboard",
+   "type": "Dashboard"
   }
  ]
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index ec28eb7..662a06b 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -267,6 +267,17 @@
 			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
 			filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id})
 
+	def set_transferred_qty_in_job_card(self, ste_doc):
+		for row in ste_doc.items:
+			if not row.job_card_item: continue
+
+			qty = frappe.db.sql(""" SELECT SUM(qty) from `tabStock Entry Detail` sed, `tabStock Entry` se
+				WHERE  sed.job_card_item = %s and se.docstatus = 1 and sed.parent = se.name and
+				se.purpose = 'Material Transfer for Manufacture'
+			""", (row.job_card_item))[0][0]
+
+			frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty))
+
 	def set_transferred_qty(self, update_status=False):
 		if not self.items:
 			self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
@@ -279,7 +290,8 @@
 			self.transferred_qty = frappe.db.get_value('Stock Entry', {
 				'job_card': self.name,
 				'work_order': self.work_order,
-				'docstatus': 1
+				'docstatus': 1,
+				'purpose': 'Material Transfer for Manufacture'
 			}, 'sum(fg_completed_qty)') or 0
 
 		self.db_set("transferred_qty", self.transferred_qty)
@@ -420,6 +432,7 @@
 		target.purpose = "Material Transfer for Manufacture"
 		target.from_bom = 1
 		target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
+		target.set_transfer_qty()
 		target.calculate_rate_and_amount()
 		target.set_missing_values()
 		target.set_stock_entry_type()
@@ -437,9 +450,10 @@
 			"field_map": {
 				"source_warehouse": "s_warehouse",
 				"required_qty": "qty",
-				"uom": "stock_uom"
+				"name": "job_card_item"
 			},
 			"postprocess": update_item,
+			"condition": lambda doc: doc.required_qty > 0
 		}
 	}, target_doc, set_missing_values)
 
diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
index bc9fe10..100ef4c 100644
--- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
+++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
@@ -1,363 +1,120 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-07-09 17:20:44.737289", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2018-07-09 17:20:44.737289",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "source_warehouse",
+  "uom",
+  "item_group",
+  "column_break_3",
+  "stock_uom",
+  "item_name",
+  "description",
+  "qty_section",
+  "required_qty",
+  "column_break_9",
+  "transferred_qty",
+  "allow_alternative_item"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "source_warehouse", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Source Warehouse", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Warehouse", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "source_warehouse",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "in_list_view": 1,
+   "label": "Source Warehouse",
+   "options": "Warehouse"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "uom", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "UOM", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "qty_section", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "qty_section",
+   "fieldtype": "Section Break",
+   "label": "Qty"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "required_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Required Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "required_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Required Qty",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_9", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "allow_alternative_item", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Allow Alternative Item", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "default": "0",
+   "fieldname": "allow_alternative_item",
+   "fieldtype": "Check",
+   "label": "Allow Alternative Item"
+  },
+  {
+   "fetch_from": "item_code.item_group",
+   "fieldname": "item_group",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "item_code.stock_uom",
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM"
+  },
+  {
+   "fieldname": "transferred_qty",
+   "fieldtype": "Float",
+   "label": "Transferred Qty",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-08-28 15:23:48.099459", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Job Card Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-02-11 13:50:13.804108",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Job Card Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 06a8e19..00e8c54 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -94,11 +94,11 @@
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
 			source_warehouse=warehouse, skip_transfer=1)
 
-		bin1_on_submit = get_bin(item, warehouse)
+		reserved_qty_on_submission = cint(get_bin(item, warehouse).reserved_qty_for_production)
 
 		# reserved qty for production is updated
-		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2,
-			cint(bin1_on_submit.reserved_qty_for_production))
+		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, reserved_qty_on_submission)
+
 
 		test_stock_entry.make_stock_entry(item_code="_Test Item",
 			target=warehouse, qty=100, basic_rate=100)
@@ -109,9 +109,9 @@
 		s.submit()
 
 		bin1_at_completion = get_bin(item, warehouse)
-
+		
 		self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
-			cint(bin1_on_submit.reserved_qty_for_production) - 1)
+			reserved_qty_on_submission - 1)
 
 	def test_production_item(self):
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
index f7b407b..ffd9242 100644
--- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
+++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py
@@ -88,11 +88,11 @@
 			GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
 
 def get_manufacturer_records():
-	details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"])
+	details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
 	manufacture_details = frappe._dict()
 	for detail in details:
 		dic = manufacture_details.setdefault(detail.get('parent'), {})
 		dic.setdefault('manufacturer', []).append(detail.get('manufacturer'))
 		dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no'))
 
-	return manufacture_details
\ No newline at end of file
+	return manufacture_details
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 3b7c6ab..ba31fee 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -752,3 +752,7 @@
 erpnext.patches.v13_0.convert_qi_parameter_to_link_field
 erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
 erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
+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.v13_0.update_vehicle_no_reqd_condition
diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py
index 5dc5d3b..b997ba2 100644
--- a/erpnext/patches/v11_0/refactor_autoname_naming.py
+++ b/erpnext/patches/v11_0/refactor_autoname_naming.py
@@ -20,7 +20,7 @@
 	'Certified Consultant': 'NPO-CONS-.YYYY.-.#####',
 	'Chat Room': 'CHAT-ROOM-.#####',
 	'Compensatory Leave Request': 'HR-CMP-.YY.-.MM.-.#####',
-	'Custom Script': 'SYS-SCR-.#####',
+	'Client Script': 'SYS-SCR-.#####',
 	'Employee Benefit Application': 'HR-BEN-APP-.YY.-.MM.-.#####',
 	'Employee Benefit Application Detail': '',
 	'Employee Benefit Claim': 'HR-BEN-CLM-.YY.-.MM.-.#####',
diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py
index 1acdfcc..544bc5e 100644
--- a/erpnext/patches/v11_1/update_bank_transaction_status.py
+++ b/erpnext/patches/v11_1/update_bank_transaction_status.py
@@ -7,9 +7,20 @@
 def execute():
     frappe.reload_doc("accounts", "doctype", "bank_transaction")
 
-    frappe.db.sql(""" UPDATE `tabBank Transaction`
-        SET status = 'Reconciled'
-        WHERE
-            status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
-            and ifnull(allocated_amount, 0) > 0
-    """)
\ No newline at end of file
+    bank_transaction_fields = frappe.get_meta("Bank Transaction").get_valid_columns()
+
+    if 'debit' in bank_transaction_fields:
+        frappe.db.sql(""" UPDATE `tabBank Transaction`
+            SET status = 'Reconciled'
+            WHERE
+                status = 'Settled' and (debit = allocated_amount or credit = allocated_amount)
+                and ifnull(allocated_amount, 0) > 0
+        """)
+
+    elif 'deposit' in bank_transaction_fields:
+        frappe.db.sql(""" UPDATE `tabBank Transaction`
+            SET status = 'Reconciled'
+            WHERE
+                status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount)
+                and ifnull(allocated_amount, 0) > 0
+        """)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
new file mode 100644
index 0000000..d41101c
--- /dev/null
+++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
@@ -0,0 +1,16 @@
+import frappe
+from erpnext.regional.india import states
+
+def execute():
+
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = ['Address-gst_state', 'Tax Category-gst_state']
+
+	# Update options in gst_state custom fields
+	for field in custom_fields:
+		gst_state_field = frappe.get_doc('Custom Field', field)
+		gst_state_field.options = '\n'.join(states)
+		gst_state_field.save()
diff --git a/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
new file mode 100644
index 0000000..af1f6e7
--- /dev/null
+++ b/erpnext/patches/v13_0/delete_old_bank_reconciliation_doctypes.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	doctypes = [
+		"Bank Statement Settings",
+		"Bank Statement Settings Item",
+		"Bank Statement Transaction Entry",
+		"Bank Statement Transaction Invoice Item",
+		"Bank Statement Transaction Payment Item",
+		"Bank Statement Transaction Settings Item",
+		"Bank Statement Transaction Settings",
+	]
+
+	for doctype in doctypes:
+		frappe.delete_doc("DocType", doctype, force=1)
+
+	frappe.delete_doc("Page", "bank-reconciliation", force=1)
+
+	rename_field("Bank Transaction", "debit", "deposit")
+	rename_field("Bank Transaction", "credit", "withdrawal")
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
new file mode 100644
index 0000000..ca04e8a
--- /dev/null
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -0,0 +1,50 @@
+import frappe
+from frappe import _
+from frappe.utils import getdate, get_time
+from erpnext.stock.stock_ledger import update_entries_after
+from erpnext.accounts.utils import update_gl_entries_after
+
+def execute():
+	frappe.reload_doc('stock', 'doctype', 'repost_item_valuation')
+
+	reposting_project_deployed_on = frappe.db.get_value("DocType", "Repost Item Valuation", "creation")
+
+	data = frappe.db.sql('''
+		SELECT
+			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
+		FROM
+			`tabStock Ledger Entry`
+		WHERE
+			creation > %s
+			and is_cancelled = 0
+		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+	''', reposting_project_deployed_on, as_dict=1)
+
+	frappe.db.auto_commit_on_many_writes = 1
+	print("Reposting Stock Ledger Entries...")
+	total_sle = len(data)
+	i = 0
+	for d in data:
+		update_entries_after({
+			"item_code": d.item_code,
+			"warehouse": d.warehouse,
+			"posting_date": d.posting_date,
+			"posting_time": d.posting_time,
+			"voucher_type": d.voucher_type,
+			"voucher_no": d.voucher_no,
+			"sle_id": d.name
+		}, allow_negative_stock=True)
+
+		i += 1
+		if i%100 == 0:
+			print(i, "/", total_sle)
+
+
+	print("Reposting General Ledger Entries...")
+	posting_date = getdate(reposting_project_deployed_on)
+	posting_time = get_time(reposting_project_deployed_on)
+
+	for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		update_gl_entries_after(posting_date, posting_time, company=row.name)
+
+	frappe.db.auto_commit_on_many_writes = 0
diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
new file mode 100644
index 0000000..c26cddb
--- /dev/null
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }):
+		frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '')
diff --git a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
index ef3f1d6..c564f8b 100644
--- a/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
+++ b/erpnext/patches/v5_0/replace_renamed_fields_in_custom_scripts_and_print_formats.py
@@ -9,7 +9,7 @@
 	# NOTE: sequence is important
 	renamed_fields = get_all_renamed_fields()
 
-	for dt, script_field, ref_dt_field in (("Custom Script", "script", "dt"), ("Print Format", "html", "doc_type")):
+	for dt, script_field, ref_dt_field in (("Client Script", "script", "dt"), ("Print Format", "html", "doc_type")):
 
 		cond1 = " or ".join("""{0} like "%%{1}%%" """.format(script_field, d[0].replace("_", "\\_")) for d in renamed_fields)
 		cond2 = " and standard = 'No'" if dt == "Print Format" else ""
diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
index 746cae0..2356e2f 100644
--- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py
+++ b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
@@ -7,7 +7,7 @@
 			where name in('Time Log Batch', 'Time Log Batch Detail', 'Time Log')""")
 
 	frappe.db.sql("""delete from `tabDocField` where parent in ('Time Log', 'Time Log Batch')""")
-	frappe.db.sql("""update `tabCustom Script` set dt = 'Timesheet' where dt = 'Time Log'""")
+	frappe.db.sql("""update `tabClient Script` set dt = 'Timesheet' where dt = 'Time Log'""")
 
 	for data in frappe.db.sql(""" select label, fieldname from  `tabCustom Field` where dt = 'Time Log'""", as_dict=1):
 		custom_field = frappe.get_doc({
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 2d3bc57..60aff02 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -1103,10 +1103,10 @@
 			self.calculate_total_for_salary_slip_based_on_timesheet()
 		else:
 			self.total_deduction = 0.0
-			if self.earnings:
+			if hasattr(self, "earnings"):
 				for earning in self.earnings:
 					self.gross_pay += flt(earning.amount, earning.precision("amount"))
-			if self.deductions:
+			if hasattr(self, "deductions"):
 				for deduction in self.deductions:
 					self.total_deduction += flt(deduction.amount, deduction.precision("amount"))
 			self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment)
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 3570a0f..077011a 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -75,24 +75,27 @@
 			frm.add_custom_button(__('Cancelled'), () => {
 				frm.events.set_status(frm, 'Cancelled');
 			}, __('Set Status'));
-		}
 
-		if (frappe.model.can_read("Task")) {
-			frm.add_custom_button(__("Gantt Chart"), function () {
-				frappe.route_options = {
-					"project": frm.doc.name
-				};
-				frappe.set_route("List", "Task", "Gantt");
-			});
 
-			frm.add_custom_button(__("Kanban Board"), () => {
-				frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
-					project: frm.doc.project_name
-				}).then(() => {
-					frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
+			if (frappe.model.can_read("Task")) {
+				frm.add_custom_button(__("Gantt Chart"), function () {
+					frappe.route_options = {
+						"project": frm.doc.name
+					};
+					frappe.set_route("List", "Task", "Gantt");
 				});
-			});
+
+				frm.add_custom_button(__("Kanban Board"), () => {
+					frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
+						project: frm.doc.project_name
+					}).then(() => {
+						frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
+					});
+				});
+			}
 		}
+
+
 	},
 
 	create_duplicate: function(frm) {
@@ -135,4 +138,4 @@
 		frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
 	});
 
-}
\ No newline at end of file
+}
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index d85c826..6290538 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -37,7 +37,7 @@
 
 		task1 = task_exists("Test Template Task Parent")
 		if not task1:
-			task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=1)
+			task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=4)
 
 		task2 = task_exists("Test Template Task Child 1")
 		if not task2:
@@ -52,7 +52,7 @@
 		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
 
 		self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
-		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 1))
+		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 4))
 
 		self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
 		self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index a2095c9..7116348 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -30,6 +30,7 @@
 
     def validate(self):
         self.validate_dates()
+        self.validate_parent_expected_end_date()
         self.validate_parent_project_dates()
         self.validate_progress()
         self.validate_status()
@@ -45,6 +46,12 @@
             frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
                 frappe.bold("Actual End Date")))
 
+    def validate_parent_expected_end_date(self):
+        if self.parent_task:
+            parent_exp_end_date = frappe.db.get_value("Task", self.parent_task, "exp_end_date")
+            if parent_exp_end_date and getdate(self.get("exp_end_date")) > getdate(parent_exp_end_date):
+                frappe.throw(_("Expected End Date should be less than or equal to parent task's Expected End Date {0}.").format(getdate(parent_exp_end_date)))
+
     def validate_parent_project_dates(self):
         if not self.project or frappe.flags.in_test:
             return
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 7326238..7a3cb83 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -61,5 +61,10 @@
 		"selling/page/point_of_sale/pos_past_order_list.js",
 		"selling/page/point_of_sale/pos_past_order_summary.js",
 		"selling/page/point_of_sale/pos_controller.js"
+	],
+	"js/bank-reconciliation-tool.min.js": [
+		"public/js/bank_reconciliation_tool/data_table_manager.js",
+		"public/js/bank_reconciliation_tool/number_card.js",
+		"public/js/bank_reconciliation_tool/dialog_manager.js"
 	]
 }
diff --git a/erpnext/public/images/erpnext-logo.png b/erpnext/public/images/erpnext-logo.png
new file mode 100644
index 0000000..3090727
--- /dev/null
+++ b/erpnext/public/images/erpnext-logo.png
Binary files differ
diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
new file mode 100644
index 0000000..5bb58fa
--- /dev/null
+++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
@@ -0,0 +1,220 @@
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
+	constructor(opts) {
+		Object.assign(this, opts);
+		this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager(
+			this.company,
+			this.bank_account
+		);
+		this.make_dt();
+	}
+
+	make_dt() {
+		var me = this;
+		frappe.call({
+			method:
+				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
+			args: {
+				bank_account: this.bank_account,
+			},
+			callback: function (response) {
+				me.format_data(response.message);
+				me.get_dt_columns();
+				me.get_datatable();
+				me.set_listeners();
+			},
+		});
+	}
+
+	get_dt_columns() {
+		this.columns = [
+			{
+				name: "Date",
+				editable: false,
+				width: 100,
+			},
+
+			{
+				name: "Party Type",
+				editable: false,
+				width: 95,
+			},
+			{
+				name: "Party",
+				editable: false,
+				width: 100,
+			},
+			{
+				name: "Description",
+				editable: false,
+				width: 350,
+			},
+			{
+				name: "Deposit",
+				editable: false,
+				width: 100,
+				format: (value) =>
+					"<span style='color:green;'>" +
+					format_currency(value, this.currency) +
+					"</span>",
+			},
+			{
+				name: "Withdrawal",
+				editable: false,
+				width: 100,
+				format: (value) =>
+					"<span style='color:red;'>" +
+					format_currency(value, this.currency) +
+					"</span>",
+			},
+			{
+				name: "Unallocated Amount",
+				editable: false,
+				width: 100,
+				format: (value) =>
+					"<span style='color:blue;'>" +
+					format_currency(value, this.currency) +
+					"</span>",
+			},
+			{
+				name: "Reference Number",
+				editable: false,
+				width: 140,
+			},
+			{
+				name: "Actions",
+				editable: false,
+				sortable: false,
+				focusable: false,
+				dropdown: false,
+				width: 80,
+			},
+		];
+	}
+
+	format_data(transactions) {
+		this.transactions = [];
+		if (transactions[0]) {
+			this.currency = transactions[0]["currency"];
+		}
+		this.transaction_dt_map = {};
+		let length;
+		transactions.forEach((row) => {
+			length = this.transactions.push(this.format_row(row));
+			this.transaction_dt_map[row["name"]] = length - 1;
+		});
+	}
+
+	format_row(row) {
+		return [
+			row["date"],
+			row["party_type"],
+			row["party"],
+			row["description"],
+			row["deposit"],
+			row["withdrawal"],
+			row["unallocated_amount"],
+			row["reference_number"],
+			`
+			<Button class="btn btn-primary btn-xs center"  data-name = ${row["name"]} >
+				Actions
+			</a>
+			`,
+		];
+	}
+
+	get_datatable() {
+		const datatable_options = {
+			columns: this.columns,
+			data: this.transactions,
+			dynamicRowHeight: true,
+			checkboxColumn: false,
+			inlineFilters: true,
+		};
+		this.datatable = new frappe.DataTable(
+			this.$reconciliation_tool_dt.get(0),
+			datatable_options
+		);
+		$(`.${this.datatable.style.scopeClass} .dt-scrollable`).css(
+			"max-height",
+			"calc(100vh - 400px)"
+		);
+
+		if (this.transactions.length > 0) {
+			this.$reconciliation_tool_dt.show();
+			this.$no_bank_transactions.hide();
+		} else {
+			this.$reconciliation_tool_dt.hide();
+			this.$no_bank_transactions.show();
+		}
+	}
+
+	set_listeners() {
+		var me = this;
+		$(`.${this.datatable.style.scopeClass} .dt-scrollable`).on(
+			"click",
+			`.btn`,
+			function () {
+				me.dialog_manager.show_dialog(
+					$(this).attr("data-name"),
+					(bank_transaction) => me.update_dt_cards(bank_transaction)
+				);
+				return true;
+			}
+		);
+	}
+
+	update_dt_cards(bank_transaction) {
+		const transaction_index = this.transaction_dt_map[
+			bank_transaction.name
+		];
+		if (bank_transaction.unallocated_amount > 0) {
+			this.transactions[transaction_index] = this.format_row(
+				bank_transaction
+			);
+		} else {
+			this.transactions.splice(transaction_index, 1);
+		}
+		this.datatable.refresh(this.transactions, this.columns);
+
+		if (this.transactions.length == 0) {
+			this.$reconciliation_tool_dt.hide();
+			this.$no_bank_transactions.show();
+		}
+
+		// this.make_dt();
+		this.get_cleared_balance().then(() => {
+			this.cards_manager.$cards[1].set_value(
+				format_currency(this.cleared_balance),
+				this.currency
+			);
+			this.cards_manager.$cards[2].set_value(
+				format_currency(
+					this.bank_statement_closing_balance - this.cleared_balance
+				),
+				this.currency
+			);
+			this.cards_manager.$cards[2].set_value_color(
+				this.bank_statement_closing_balance - this.cleared_balance == 0
+					? "text-success"
+					: "text-danger"
+			);
+		});
+	}
+
+	get_cleared_balance() {
+		if (this.bank_account && this.bank_statement_to_date) {
+			return frappe.call({
+				method:
+					"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+				args: {
+					bank_account: this.bank_account,
+					till_date: this.bank_statement_to_date,
+				},
+				callback: (response) =>
+					(this.cleared_balance = response.message),
+			});
+		}
+	}
+};
diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
new file mode 100644
index 0000000..142fe79
--- /dev/null
+++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
@@ -0,0 +1,594 @@
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
+	constructor(company, bank_account) {
+		this.bank_account = bank_account;
+		this.company = company;
+		this.make_dialog();
+	}
+
+	show_dialog(bank_transaction_name, update_dt_cards) {
+		this.bank_transaction_name = bank_transaction_name;
+		this.update_dt_cards = update_dt_cards;
+		frappe.call({
+			method: "frappe.client.get_value",
+			args: {
+				doctype: "Bank Transaction",
+				filters: { name: this.bank_transaction_name },
+				fieldname: [
+					"date",
+					"deposit",
+					"withdrawal",
+					"currency",
+					"description",
+					"name",
+					"bank_account",
+					"company",
+					"reference_number",
+					"party_type",
+					"party",
+					"unallocated_amount",
+					"allocated_amount",
+				],
+			},
+			callback: (r) => {
+				if (r.message) {
+					this.bank_transaction = r.message;
+					r.message.payment_entry = 1;
+					this.dialog.set_values(r.message);
+					this.dialog.show();
+				}
+			},
+		});
+	}
+
+	get_linked_vouchers(document_types) {
+		frappe.call({
+			method:
+				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_linked_payments",
+			args: {
+				bank_transaction_name: this.bank_transaction_name,
+				document_types: document_types,
+			},
+
+			callback: (result) => {
+				const data = result.message;
+
+
+				if (data && data.length > 0) {
+					const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper;
+					proposals_wrapper.show();
+					this.dialog.fields_dict.no_matching_vouchers.$wrapper.hide();
+					this.data = [];
+					data.forEach((row) => {
+						const reference_date = row[5] ? row[5] : row[8];
+						this.data.push([
+							row[1],
+							row[2],
+							reference_date,
+							format_currency(row[3], row[9]),
+							row[6],
+							row[4],
+						]);
+					});
+					this.get_dt_columns();
+					this.get_datatable(proposals_wrapper);
+				} else {
+					const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper;
+					proposals_wrapper.hide();
+					this.dialog.fields_dict.no_matching_vouchers.$wrapper.show();
+
+				}
+				this.dialog.show();
+			},
+		});
+	}
+
+	get_dt_columns() {
+		this.columns = [
+			{
+				name: "Document Type",
+				editable: false,
+				width: 125,
+			},
+			{
+				name: "Document Name",
+				editable: false,
+				width: 150,
+			},
+			{
+				name: "Reference Date",
+				editable: false,
+				width: 120,
+			},
+			{
+				name: "Amount",
+				editable: false,
+				width: 100,
+			},
+			{
+				name: "Party",
+				editable: false,
+				width: 120,
+			},
+
+			{
+				name: "Reference Number",
+				editable: false,
+				width: 140,
+			},
+		];
+	}
+
+	get_datatable(proposals_wrapper) {
+		if (!this.datatable) {
+			const datatable_options = {
+				columns: this.columns,
+				data: this.data,
+				dynamicRowHeight: true,
+				checkboxColumn: true,
+				inlineFilters: true,
+			};
+			this.datatable = new frappe.DataTable(
+				proposals_wrapper.get(0),
+				datatable_options
+			);
+		} else {
+			this.datatable.refresh(this.data, this.columns);
+			this.datatable.rowmanager.checkMap = [];
+		}
+	}
+
+	make_dialog() {
+		const me = this;
+		me.selected_payment = null;
+
+		const fields = [
+			{
+				label: __("Action"),
+				fieldname: "action",
+				fieldtype: "Select",
+				options: `Match Against Voucher\nCreate Voucher\nUpdate Bank Transaction`,
+				default: "Match Against Voucher",
+			},
+			{
+				fieldname: "column_break_4",
+				fieldtype: "Column Break",
+			},
+			{
+				label: __("Document Type"),
+				fieldname: "document_type",
+				fieldtype: "Select",
+				options: `Payment Entry\nJournal Entry`,
+				default: "Payment Entry",
+				depends_on: "eval:doc.action=='Create Voucher'",
+			},
+			{
+				fieldtype: "Section Break",
+				fieldname: "section_break_1",
+				label: __("Filters"),
+				depends_on: "eval:doc.action=='Match Against Voucher'",
+			},
+			{
+				fieldtype: "Check",
+				label: "Payment Entry",
+				fieldname: "payment_entry",
+				onchange: () => this.update_options(),
+			},
+			{
+				fieldtype: "Check",
+				label: "Journal Entry",
+				fieldname: "journal_entry",
+				onchange: () => this.update_options(),
+			},
+			{
+				fieldname: "column_break_5",
+				fieldtype: "Column Break",
+			},
+			{
+				fieldtype: "Check",
+				label: "Sales Invoice",
+				fieldname: "sales_invoice",
+				onchange: () => this.update_options(),
+			},
+
+			{
+				fieldtype: "Check",
+				label: "Purchase Invoice",
+				fieldname: "purchase_invoice",
+				onchange: () => this.update_options(),
+			},
+			{
+				fieldname: "column_break_5",
+				fieldtype: "Column Break",
+			},
+			{
+				fieldtype: "Check",
+				label: "Expense Claim",
+				fieldname: "expense_claim",
+				onchange: () => this.update_options(),
+			},
+			{
+				fieldtype: "Check",
+				label: "Show Only Exact Amount",
+				fieldname: "exact_match",
+				onchange: () => this.update_options(),
+			},
+			{
+				fieldtype: "Section Break",
+				fieldname: "section_break_1",
+				label: __("Select Vouchers to Match"),
+				depends_on: "eval:doc.action=='Match Against Voucher'",
+			},
+			{
+				fieldtype: "HTML",
+				fieldname: "payment_proposals",
+			},
+			{
+				fieldtype: "HTML",
+				fieldname: "no_matching_vouchers",
+				options: "<div class='text-muted text-center'>No Matching Vouchers Found</div>"
+			},
+			{
+				fieldtype: "Section Break",
+				fieldname: "details",
+				label: "Details",
+				depends_on: "eval:doc.action!='Match Against Voucher'",
+			},
+			{
+				fieldname: "reference_number",
+				fieldtype: "Data",
+				label: "Reference Number",
+				mandatory_depends_on: "eval:doc.action=='Create Voucher'",
+			},
+			{
+				default: "Today",
+				fieldname: "posting_date",
+				fieldtype: "Date",
+				label: "Posting Date",
+				reqd: 1,
+				depends_on: "eval:doc.action=='Create Voucher'",
+			},
+			{
+				fieldname: "reference_date",
+				fieldtype: "Date",
+				label: "Cheque/Reference Date",
+				mandatory_depends_on: "eval:doc.action=='Create Voucher'",
+				depends_on: "eval:doc.action=='Create Voucher'",
+				reqd: 1,
+			},
+			{
+				fieldname: "mode_of_payment",
+				fieldtype: "Link",
+				label: "Mode of Payment",
+				options: "Mode of Payment",
+				depends_on: "eval:doc.action=='Create Voucher'",
+			},
+			{
+				fieldname: "edit_in_full_page",
+				fieldtype: "Button",
+				label: "Edit in Full Page",
+				click: () => {
+					this.edit_in_full_page();
+				},
+				depends_on:
+					"eval:doc.action=='Create Voucher'",
+			},
+			{
+				fieldname: "column_break_7",
+				fieldtype: "Column Break",
+			},
+			{
+				default: "Journal Entry Type",
+				fieldname: "journal_entry_type",
+				fieldtype: "Select",
+				label: "Journal Entry Type",
+				options:
+					"Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense",
+				depends_on:
+					"eval:doc.action=='Create Voucher' &&  doc.document_type=='Journal Entry'",
+				mandatory_depends_on:
+					"eval:doc.action=='Create Voucher' &&  doc.document_type=='Journal Entry'",
+			},
+			{
+				fieldname: "second_account",
+				fieldtype: "Link",
+				label: "Account",
+				options: "Account",
+				depends_on:
+					"eval:doc.action=='Create Voucher' &&  doc.document_type=='Journal Entry'",
+				mandatory_depends_on:
+					"eval:doc.action=='Create Voucher' &&  doc.document_type=='Journal Entry'",
+				get_query: () => {
+					return {
+						filters: {
+							is_group: 0,
+							company: this.company,
+						},
+					};
+				},
+			},
+			{
+				fieldname: "party_type",
+				fieldtype: "Link",
+				label: "Party Type",
+				options: "DocType",
+				mandatory_depends_on:
+				"eval:doc.action=='Create Voucher' &&  doc.document_type=='Payment Entry'",
+				get_query: function () {
+					return {
+						filters: {
+							name: [
+								"in",
+								Object.keys(frappe.boot.party_account_types),
+							],
+						},
+					};
+				},
+			},
+			{
+				fieldname: "party",
+				fieldtype: "Dynamic Link",
+				label: "Party",
+				options: "party_type",
+				mandatory_depends_on:
+					"eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+			},
+			{
+				fieldname: "project",
+				fieldtype: "Link",
+				label: "Project",
+				options: "Project",
+				depends_on:
+					"eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+			},
+			{
+				fieldname: "cost_center",
+				fieldtype: "Link",
+				label: "Cost Center",
+				options: "Cost Center",
+				depends_on:
+					"eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'",
+			},
+			{
+				fieldtype: "Section Break",
+				fieldname: "details_section",
+				label: "Transaction Details",
+				collapsible: 1,
+			},
+			{
+				fieldname: "deposit",
+				fieldtype: "Currency",
+				label: "Deposit",
+				read_only: 1,
+			},
+			{
+				fieldname: "withdrawal",
+				fieldtype: "Currency",
+				label: "Withdrawal",
+				read_only: 1,
+			},
+			{
+				fieldname: "description",
+				fieldtype: "Small Text",
+				label: "Description",
+				read_only: 1,
+			},
+			{
+				fieldname: "column_break_17",
+				fieldtype: "Column Break",
+				read_only: 1,
+			},
+			{
+				fieldname: "allocated_amount",
+				fieldtype: "Currency",
+				label: "Allocated Amount",
+				read_only: 1,
+			},
+
+			{
+				fieldname: "unallocated_amount",
+				fieldtype: "Currency",
+				label: "Unallocated Amount",
+				read_only: 1,
+			},
+		];
+
+		me.dialog = new frappe.ui.Dialog({
+			title: __("Reconcile the Bank Transaction"),
+			fields: fields,
+			size: "large",
+			primary_action: (values) =>
+				this.reconciliation_dialog_primary_action(values),
+		});
+	}
+
+	get_selected_attributes() {
+		let selected_attributes = [];
+		this.dialog.$wrapper.find(".checkbox input").each((i, col) => {
+			if ($(col).is(":checked")) {
+				selected_attributes.push($(col).attr("data-fieldname"));
+			}
+		});
+
+		return selected_attributes;
+	}
+
+	update_options() {
+		let selected_attributes = this.get_selected_attributes();
+		this.get_linked_vouchers(selected_attributes);
+	}
+
+	reconciliation_dialog_primary_action(values) {
+		if (values.action == "Match Against Voucher") this.match(values);
+		if (
+			values.action == "Create Voucher" &&
+			values.document_type == "Payment Entry"
+		)
+			this.add_payment_entry(values);
+		if (
+			values.action == "Create Voucher" &&
+			values.document_type == "Journal Entry"
+		)
+			this.add_journal_entry(values);
+		else if (values.action == "Update Bank Transaction")
+			this.update_transaction(values);
+	}
+
+	match() {
+		var selected_map = this.datatable.rowmanager.checkMap;
+		let rows = [];
+		selected_map.forEach((val, index) => {
+			if (val == 1) rows.push(this.datatable.datamanager.rows[index]);
+		});
+		let vouchers = [];
+		rows.forEach((x) => {
+			vouchers.push({
+				payment_doctype: x[2].content,
+				payment_name: x[3].content,
+				amount: x[5].content,
+			});
+		});
+		frappe.call({
+			method:
+				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.reconcile_vouchers",
+			args: {
+				bank_transaction_name: this.bank_transaction.name,
+				vouchers: vouchers,
+			},
+			callback: (response) => {
+				const alert_string =
+					"Bank Transaction " +
+					this.bank_transaction.name +
+					" Matched";
+				frappe.show_alert(alert_string);
+				this.update_dt_cards(response.message);
+				this.dialog.hide();
+			},
+		});
+	}
+
+	add_payment_entry(values) {
+		frappe.call({
+			method:
+				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts",
+			args: {
+				bank_transaction_name: this.bank_transaction.name,
+				reference_number: values.reference_number,
+				reference_date: values.reference_date,
+				party_type: values.party_type,
+				party: values.party,
+				posting_date: values.posting_date,
+				mode_of_payment: values.mode_of_payment,
+				project: values.project,
+				cost_center: values.cost_center,
+			},
+			callback: (response) => {
+				const alert_string =
+					"Bank Transaction " +
+					this.bank_transaction.name +
+					" added as Payment Entry";
+				frappe.show_alert(alert_string);
+				this.update_dt_cards(response.message);
+				this.dialog.hide();
+			},
+		});
+	}
+
+	add_journal_entry(values) {
+		frappe.call({
+			method:
+				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts",
+			args: {
+				bank_transaction_name: this.bank_transaction.name,
+				reference_number: values.reference_number,
+				reference_date: values.reference_date,
+				party_type: values.party_type,
+				party: values.party,
+				posting_date: values.posting_date,
+				mode_of_payment: values.mode_of_payment,
+				entry_type: values.journal_entry_type,
+				second_account: values.second_account,
+			},
+			callback: (response) => {
+				const alert_string =
+					"Bank Transaction " +
+					this.bank_transaction.name +
+					" added as Journal Entry";
+				frappe.show_alert(alert_string);
+				this.update_dt_cards(response.message);
+				this.dialog.hide();
+			},
+		});
+	}
+
+	update_transaction(values) {
+		frappe.call({
+			method:
+				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.update_bank_transaction",
+			args: {
+				bank_transaction_name: this.bank_transaction.name,
+				reference_number: values.reference_number,
+				party_type: values.party_type,
+				party: values.party,
+			},
+			callback: (response) => {
+				const alert_string =
+					"Bank Transaction " +
+					this.bank_transaction.name +
+					" updated";
+				frappe.show_alert(alert_string);
+				this.update_dt_cards(response.message);
+				this.dialog.hide();
+			},
+		});
+	}
+
+	edit_in_full_page() {
+		const values = this.dialog.get_values(true);
+		if (values.document_type == "Payment Entry") {
+			frappe.call({
+				method:
+					"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts",
+				args: {
+					bank_transaction_name: this.bank_transaction.name,
+					reference_number: values.reference_number,
+					reference_date: values.reference_date,
+					party_type: values.party_type,
+					party: values.party,
+					posting_date: values.posting_date,
+					mode_of_payment: values.mode_of_payment,
+					project: values.project,
+					cost_center: values.cost_center,
+					allow_edit: true
+				},
+				callback: (r) => {
+					const doc = frappe.model.sync(r.message);
+					frappe.set_route("Form", doc[0].doctype, doc[0].name);
+				},
+			});
+		} else {
+			frappe.call({
+				method:
+					"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts",
+				args: {
+					bank_transaction_name: this.bank_transaction.name,
+					reference_number: values.reference_number,
+					reference_date: values.reference_date,
+					party_type: values.party_type,
+					party: values.party,
+					posting_date: values.posting_date,
+					mode_of_payment: values.mode_of_payment,
+					entry_type: values.journal_entry_type,
+					second_account: values.second_account,
+					allow_edit: true
+				},
+				callback: (r) => {
+					var doc = frappe.model.sync(r.message);
+					frappe.set_route("Form", doc[0].doctype, doc[0].name);
+				},
+			});
+		}
+	}
+
+};
diff --git a/erpnext/public/js/bank_reconciliation_tool/number_card.js b/erpnext/public/js/bank_reconciliation_tool/number_card.js
new file mode 100644
index 0000000..e10d109
--- /dev/null
+++ b/erpnext/public/js/bank_reconciliation_tool/number_card.js
@@ -0,0 +1,75 @@
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+erpnext.accounts.bank_reconciliation.NumberCardManager = class NumberCardManager {
+	constructor(opts) {
+		Object.assign(this, opts);
+		this.make_cards();
+	}
+
+	make_cards() {
+		this.$reconciliation_tool_cards.empty();
+		this.$cards = [];
+		this.$summary = $(`<div class="report-summary"></div>`)
+			.hide()
+			.appendTo(this.$reconciliation_tool_cards);
+		var chart_data = [
+			{
+				value: this.bank_statement_closing_balance,
+				label: "Closing Balance as per Bank Statement",
+				datatype: "Currency",
+				currency: this.currency,
+			},
+			{
+				value: this.cleared_balance,
+				label: "Closing Balance as per ERP",
+				datatype: "Currency",
+				currency: this.currency,
+			},
+			{
+				value:
+					this.bank_statement_closing_balance - this.cleared_balance,
+				label: "Difference",
+				datatype: "Currency",
+				currency: this.currency,
+			},
+		];
+
+		chart_data.forEach((summary) => {
+			let number_card = new erpnext.accounts.NumberCard(summary);
+			this.$cards.push(number_card);
+
+			number_card.$card.appendTo(this.$summary);
+		});
+		this.$cards[2].set_value_color(
+			this.bank_statement_closing_balance - this.cleared_balance == 0
+				? "text-success"
+				: "text-danger"
+		);
+		this.$summary.css({"border-bottom": "0px", "margin-left": "0px", "margin-right": "0px"});
+		this.$summary.show();
+	}
+};
+
+erpnext.accounts.NumberCard = class NumberCard {
+	constructor(options) {
+		this.$card = frappe.utils.build_summary_item(options);
+	}
+
+	set_value(value) {
+		this.$card.find("div").text(value);
+	}
+
+	set_value_color(color) {
+		this.$card
+			.find("div")
+			.removeClass("text-danger text-success")
+			.addClass(`${color}`);
+	}
+
+	set_indicator(color) {
+		this.$card
+			.find("span")
+			.removeClass("indicator red green")
+			.addClass(`indicator ${color}`);
+	}
+};
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index a2a723d..c963866 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -191,7 +191,6 @@
 			item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
 			item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
 		}
-
 		this._super(doc, cdt, cdn);
 	},
 
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 22e7578..d81321b 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -2,9 +2,11 @@
 // License: GNU General Public License v3. See license.txt
 
 erpnext.taxes_and_totals = erpnext.payments.extend({
-	setup: function() {},
+	setup: function() {
+		this.fetch_round_off_accounts();
+	},
 
-	apply_pricing_rule_on_item: function(item){
+	apply_pricing_rule_on_item: function(item) {
 		let effective_item_rate = item.price_list_rate;
 		let item_rate = item.rate;
 		if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
@@ -26,6 +28,7 @@
 
 		if (item.discount_amount) {
 			item_rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
+			item.discount_percentage = 100 * flt(item.discount_amount) / flt(item.rate_with_margin);
 		}
 
 		frappe.model.set_value(item.doctype, item.name, "rate", item_rate);
@@ -151,6 +154,22 @@
 		});
 	},
 
+	fetch_round_off_accounts: function() {
+		let me = this;
+		frappe.flags.round_off_applicable_accounts = [];
+
+		return frappe.call({
+			"method": "erpnext.controllers.taxes_and_totals.get_round_off_applicable_accounts",
+			"args": {
+				"company": me.frm.doc.company,
+				"account_list": frappe.flags.round_off_applicable_accounts
+			},
+			callback: function(r) {
+				frappe.flags.round_off_applicable_accounts.push(...r.message);
+			}
+		});
+	},
+
 	determine_exclusive_rate: function() {
 		var me = this;
 
@@ -371,11 +390,21 @@
 		} else if (tax.charge_type == "On Item Quantity") {
 			current_tax_amount = tax_rate * item.qty;
 		}
+
+		current_tax_amount = this.get_final_tax_amount(tax, current_tax_amount);
 		this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
 
 		return current_tax_amount;
 	},
 
+	get_final_tax_amount: function(tax, current_tax_amount) {
+		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
+			current_tax_amount = Math.round(current_tax_amount);
+		}
+
+		return current_tax_amount;
+	},
+
 	set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
 		// store tax breakup for each item
 		let tax_detail = tax.item_wise_tax_detail;
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 1db0f5f..e5f9049 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -40,7 +40,7 @@
 
 			cur_frm.cscript.set_gross_profit(item);
 			cur_frm.cscript.calculate_taxes_and_totals();
-
+			cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn);
 		});
 
 
@@ -1122,6 +1122,7 @@
 				}
 			});
 		}
+		me.calculate_stock_uom_rate(doc, cdt, cdn);
 	},
 
 	conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
@@ -1142,6 +1143,7 @@
 				frappe.meta.has_field(doc.doctype, "price_list_currency")) {
 				this.apply_price_list(item, true);
 			}
+			this.calculate_stock_uom_rate(doc, cdt, cdn);
 		}
 	},
 
@@ -1162,9 +1164,15 @@
 	qty: function(doc, cdt, cdn) {
 		let item = frappe.get_doc(cdt, cdn);
 		this.conversion_factor(doc, cdt, cdn, true);
+		this.calculate_stock_uom_rate(doc, cdt, cdn);
 		this.apply_pricing_rule(item, true);
 	},
 
+	calculate_stock_uom_rate: function(doc, cdt, cdn) {
+		let item = frappe.get_doc(cdt, cdn);
+		item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);	
+		refresh_field("stock_uom_rate", item.name, item.parentfield);
+	},
 	service_stop_date: function(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
 
@@ -1275,7 +1283,7 @@
 		this.frm.set_currency_labels(["base_rate", "base_net_rate", "base_price_list_rate", "base_amount", "base_net_amount"],
 			company_currency, "items");
 
-		this.frm.set_currency_labels(["rate", "net_rate", "price_list_rate", "amount", "net_amount"],
+		this.frm.set_currency_labels(["rate", "net_rate", "price_list_rate", "amount", "net_amount", "stock_uom_rate"],
 			this.frm.doc.currency, "items");
 
 		if(this.frm.fields_dict["operations"]) {
diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js
index 6cb1207..b66126c 100644
--- a/erpnext/public/js/telephony.js
+++ b/erpnext/public/js/telephony.js
@@ -1,13 +1,16 @@
 frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( {
 	make_input() {
-		this._super();
+		if (!this.df.read_only) {
+			this._super();
+		}
 		if (this.df.options == 'Phone') {
 			this.setup_phone();
 		}
 	},
 	setup_phone() {
 		if (frappe.phone_call.handler) {
-			this.$wrapper.find('.control-input')
+			let control = this.df.read_only ? '.control-value' : '.control-input';
+			this.$wrapper.find(control)
 				.append(`
 					<span class="phone-btn">
 						<a class="btn-open no-decoration" title="${__('Make a call')}">
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index c39609b..e5bd4d7 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -513,6 +513,7 @@
 	}, {
 		fieldtype:'Currency',
 		fieldname:"rate",
+		options: "currency",
 		default: 0,
 		read_only: 0,
 		in_list_view: 1,
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 2623c3c..d49a813 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -140,6 +140,7 @@
 					() => me.update_batch_serial_no_items(),
 					() => {
 						refresh_field("items");
+						refresh_field("packed_items");
 						if (me.callback) {
 							return me.callback(me.item);
 						}
@@ -154,7 +155,7 @@
 			if (this.item.serial_no) {
 				this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
 			}
-			
+
 			if (this.has_batch && !this.has_serial_no && d.batch_no) {
 				this.frm.doc.items.forEach(data => {
 					if(data.item_code == d.item_code) {
@@ -231,7 +232,7 @@
 				this.map_row_values(row, batch, 'batch_no',
 					'selected_qty', this.values.warehouse);
 			});
-		} 
+		}
 	},
 
 	update_serial_no_item() {
@@ -250,7 +251,7 @@
 				filters: { 'name': ["in", selected_serial_nos]},
 				fields: ["batch_no", "name"]
 			}).then((data) => {
-				// data = [{batch_no: 'batch-1', name: "SR-001"}, 
+				// data = [{batch_no: 'batch-1', name: "SR-001"},
 				// 	{batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}]
 				const batch_serial_map = data.reduce((acc, d) => {
 					if (!acc[d['batch_no']]) acc[d['batch_no']] = [];
@@ -298,6 +299,8 @@
 		} else {
 			row.warehouse = values.warehouse || warehouse;
 		}
+
+		this.frm.dirty();
 	},
 
 	update_total_qty: function() {
diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less
index 8cb7a9c..29deada 100644
--- a/erpnext/public/less/hub.less
+++ b/erpnext/public/less/hub.less
@@ -32,7 +32,12 @@
 	}
 
 	.hub-image-loading, .hub-image-broken {
-		.img-background();
+		content: " ";
+		position: absolute;
+		left: 0;
+		height: 100%;
+		width: 100%;
+		background-color: var(--bg-light-gray);
 		display: flex;
 		align-items: center;
 		justify-content: center;
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 98c33ad..95b930c 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -1,222 +1,86 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-06-27 15:09:01.318003", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2017-06-27 15:09:01.318003",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "gst_summary",
+  "column_break_2",
+  "round_off_gst_values",
+  "gstin_email_sent_on",
+  "section_break_4",
+  "gst_accounts",
+  "b2c_limit"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gst_summary", 
-   "fieldtype": "HTML", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "GST Summary", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "gst_summary",
+   "fieldtype": "HTML",
+   "label": "GST Summary",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gstin_email_sent_on", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "GSTIN Email Sent On", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "gstin_email_sent_on",
+   "fieldtype": "Date",
+   "label": "GSTIN Email Sent On",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_4", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gst_accounts", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "GST Accounts", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "GST Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "gst_accounts",
+   "fieldtype": "Table",
+   "label": "GST Accounts",
+   "options": "GST Account",
+   "show_days": 1,
+   "show_seconds": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "250000", 
-   "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.", 
-   "fieldname": "b2c_limit", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "B2C Limit", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "default": "250000",
+   "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.",
+   "fieldname": "b2c_limit",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "B2C Limit",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 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
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-02-14 08:14:15.375181", 
- "modified_by": "Administrator", 
- "module": "Regional", 
- "name": "GST Settings", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-01-28 17:19:47.969260",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "GST Settings",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+   }
\ No newline at end of file
diff --git a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
index 8174da2..023b4ed 100644
--- a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
@@ -14,8 +14,20 @@
 test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
 
 class TestGSTR3BReport(unittest.TestCase):
-	def test_gstr_3b_report(self):
+	def setUp(self):
+		frappe.set_user("Administrator")
 
+		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
+		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
+		frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
+
+		make_company()
+		make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
+		set_account_heads()
+		make_customers()
+		make_suppliers()
+
+	def test_gstr_3b_report(self):
 		month_number_mapping = {
 			1: "January",
 			2: "February",
@@ -31,17 +43,6 @@
 			12: "December"
 		}
 
-		frappe.set_user("Administrator")
-
-		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
-		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
-		frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
-
-		make_company()
-		make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
-		set_account_heads()
-		make_customers()
-		make_suppliers()
 		make_sales_invoice()
 		create_purchase_invoices()
 
@@ -67,6 +68,42 @@
 		self.assertEqual(output["itc_elg"]["itc_avl"][4]["samt"], 22.50)
 		self.assertEqual(output["itc_elg"]["itc_avl"][4]["camt"], 22.50)
 
+	def test_gst_rounding(self):
+		gst_settings = frappe.get_doc('GST Settings')
+		gst_settings.round_off_gst_values = 1
+		gst_settings.save()
+
+		current_country = frappe.flags.country
+		frappe.flags.country = 'India'
+
+		si = create_sales_invoice(company="_Test Company GST",
+			customer = '_Test GST Customer',
+			currency = 'INR',
+			warehouse = 'Finished Goods - _GST',
+			debit_to = 'Debtors - _GST',
+			income_account = 'Sales - _GST',
+			expense_account = 'Cost of Goods Sold - _GST',
+			cost_center = 'Main - _GST',
+			rate=216,
+			do_not_save=1
+		)
+
+		si.append("taxes", {
+			"charge_type": "On Net Total",
+			"account_head": "IGST - _GST",
+			"cost_center": "Main - _GST",
+			"description": "IGST @ 18.0",
+			"rate": 18
+		})
+
+		si.save()
+		# Check for 39 instead of 38.88
+		self.assertEqual(si.taxes[0].base_tax_amount_after_discount_amount, 39)
+
+		frappe.flags.country = current_country
+		gst_settings.round_off_gst_values = 1
+		gst_settings.save()
+
 def make_sales_invoice():
 	si = create_sales_invoice(company="_Test Company GST",
 			customer = '_Test GST Customer',
@@ -145,7 +182,6 @@
 	si3.submit()
 
 def create_purchase_invoices():
-
 	pi = make_purchase_invoice(
 			company="_Test Company GST",
 			supplier = '_Test Registered Supplier',
@@ -193,7 +229,6 @@
 	pi1.submit()
 
 def make_suppliers():
-
 	if not frappe.db.exists("Supplier", "_Test Registered Supplier"):
 		frappe.get_doc({
 			"supplier_group": "_Test Supplier Group",
@@ -257,7 +292,6 @@
 		address.save()
 
 def make_customers():
-
 	if not frappe.db.exists("Customer", "_Test GST Customer"):
 		frappe.get_doc({
 			"customer_group": "_Test Customer Group",
@@ -354,9 +388,9 @@
 		address.save()
 
 def make_company():
-
 	if frappe.db.exists("Company", "_Test Company GST"):
 		return
+
 	company = frappe.new_doc("Company")
 	company.company_name = "_Test Company GST"
 	company.abbr = "_GST"
@@ -388,7 +422,6 @@
 		address.save()
 
 def set_account_heads():
-
 	gst_settings = frappe.get_doc("GST Settings")
 
 	gst_account = frappe.get_all(
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index e8a8ed8..ad60db0 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -5,12 +5,16 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import getdate
+from frappe.utils import getdate, get_link_to_form
 from frappe.model.document import Document
 from erpnext.accounts.utils import get_fiscal_year
 
 class LowerDeductionCertificate(Document):
 	def validate(self):
+		self.validate_dates()
+		self.validate_supplier_against_section_code()
+		
+	def validate_dates(self):
 		if getdate(self.valid_upto) < getdate(self.valid_from):
 			frappe.throw(_("Valid Upto date cannot be before Valid From date"))
 
@@ -24,3 +28,20 @@
 			<= fiscal_year.year_end_date):
 			frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
 
+	def validate_supplier_against_section_code(self):
+		duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'section_code': self.section_code}, ['name', 'valid_from', 'valid_upto'], as_dict=True)
+		if duplicate_certificate and self.are_dates_overlapping(duplicate_certificate):
+			certificate_link = get_link_to_form('Lower Deduction Certificate', duplicate_certificate.name)
+			frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against Section Code {2} for this time period.")
+				.format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.section_code)))
+
+	def are_dates_overlapping(self,duplicate_certificate):
+		valid_from = duplicate_certificate.valid_from
+		valid_upto = duplicate_certificate.valid_upto
+		if valid_from <= getdate(self.valid_from) <= valid_upto:
+			return True
+		elif valid_from <= getdate(self.valid_upto) <= valid_upto:
+			return True
+		elif getdate(self.valid_from) <= valid_from and valid_upto <= getdate(self.valid_upto):
+			return True
+		return False
\ No newline at end of file
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index d6221a8..378b735 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -20,6 +20,7 @@
  'Jharkhand',
  'Karnataka',
  'Kerala',
+ 'Ladakh',
  'Lakshadweep Islands',
  'Madhya Pradesh',
  'Maharashtra',
@@ -59,6 +60,7 @@
  "Jharkhand": "20",
  "Karnataka": "29",
  "Kerala": "32",
+ "Ladakh": "38",
  "Lakshadweep Islands": "31",
  "Madhya Pradesh": "23",
  "Maharashtra": "27",
@@ -80,4 +82,4 @@
  "West Bengal": "19",
 }
 
-number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
\ No newline at end of file
+number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 9fa94c4..a756b57 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -188,7 +188,6 @@
 			'fieldname': 'vehicle_no',
 			'label': 'Vehicle No',
 			'fieldtype': 'Data',
-			'depends_on': 'eval:(doc.mode_of_transport === "Road")',
 			'default': frm.doc.vehicle_no
 		},
 		{
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 5f701f2..eea85cd 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -10,6 +10,7 @@
 import json
 import base64
 import frappe
+import six
 import traceback
 import io
 from frappe import _, bold
@@ -23,7 +24,7 @@
 	invalid_doctype = doc.doctype != 'Sales Invoice'
 	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
 	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
-	no_taxes_applied = len(doc.get('taxes', [])) == 0
+	no_taxes_applied = not doc.get('taxes')
 
 	if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
 		return
@@ -37,7 +38,7 @@
 	elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
 		frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
 
-	elif doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
+	elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
 		frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed'))
 
 def raise_document_name_too_long_error():
@@ -63,7 +64,7 @@
 	elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
 	elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
 
-	if not supply_type: 
+	if not supply_type:
 		rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
 		frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
 			title=_('Invalid Supply Type'))
@@ -108,11 +109,13 @@
 		pincode = 999999
 
 	return frappe._dict(dict(
-		gstin=d.gstin, legal_name=d.address_title,
-		location=d.city, pincode=d.pincode,
+		gstin=d.gstin,
+		legal_name=sanitize_for_json(d.address_title),
+		location=sanitize_for_json(d.city),
+		pincode=d.pincode,
 		state_code=d.gst_state_number,
-		address_line1=d.address_line1,
-		address_line2=d.address_line2
+		address_line1=sanitize_for_json(d.address_line1),
+		address_line2=sanitize_for_json(d.address_line2)
 	))
 
 def get_gstin_details(gstin):
@@ -128,7 +131,7 @@
 	if details:
 		frappe.local.gstin_cache[key] = details
 		return details
-	
+
 	if not details:
 		return GSPConnector.get_gstin_details(gstin)
 
@@ -146,8 +149,11 @@
 		)
 
 	return frappe._dict(dict(
-		gstin='URP', legal_name=address_title, location=city,
-		address_line1=address_line1, address_line2=address_line2,
+		gstin='URP',
+		legal_name=sanitize_for_json(address_title),
+		location=city,
+		address_line1=sanitize_for_json(address_line1),
+		address_line2=sanitize_for_json(address_line2),
 		pincode=999999, state_code=96, place_of_supply=96
 	))
 
@@ -160,7 +166,7 @@
 		item.update(d.as_dict())
 
 		item.sr_no = d.idx
-		item.description = d.item_name.replace('"', '\\"')
+		item.description = sanitize_for_json(d.item_name)
 
 		item.qty = abs(item.qty)
 		item.discount_amount = 0
@@ -174,7 +180,7 @@
 		item.serial_no = ""
 
 		item = update_item_taxes(invoice, item)
-		
+
 		item.total_value = abs(
 			item.taxable_value + item.igst_amount + item.sgst_amount +
 			item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
@@ -232,9 +238,9 @@
 	invoice_value_details.round_off = invoice.base_rounding_adjustment
 	invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
 	invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
-	
+
 	invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
-	
+
 	return invoice_value_details
 
 def update_invoice_taxes(invoice, invoice_value_details):
@@ -251,13 +257,13 @@
 			if t.account_head in gst_accounts.cess_account:
 				# using after discount amt since item also uses after discount amt for cess calc
 				invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
-			
+
 			for tax_type in ['igst', 'cgst', 'sgst']:
 				if t.account_head in gst_accounts[f'{tax_type}_account']:
 					invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount)
 		else:
 			invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount)
-	
+
 	return invoice_value_details
 
 def get_payment_details(invoice):
@@ -326,26 +332,26 @@
 		buyer_details = get_overseas_address_details(invoice.customer_address)
 	else:
 		buyer_details = get_party_details(invoice.customer_address)
-		place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin
+		place_of_supply = get_place_of_supply(invoice, invoice.doctype) or sanitize_for_json(invoice.billing_address_gstin)
 		place_of_supply = place_of_supply[:2]
 		buyer_details.update(dict(place_of_supply=place_of_supply))
-	
+
 	shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
 	if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
 		if invoice.gst_category == 'Overseas':
 			shipping_details = get_overseas_address_details(invoice.shipping_address_name)
 		else:
 			shipping_details = get_party_details(invoice.shipping_address_name)
-	
+
 	if invoice.is_pos and invoice.base_paid_amount:
 		payment_details = get_payment_details(invoice)
-	
+
 	if invoice.is_return and invoice.return_against:
 		prev_doc_details = get_return_doc_reference(invoice)
-	
+
 	if invoice.transporter:
 		eway_bill_details = get_eway_bill_details(invoice)
-	
+
 	# not yet implemented
 	dispatch_details = period_details = export_details = frappe._dict({})
 
@@ -356,8 +362,8 @@
 		period_details=period_details, prev_doc_details=prev_doc_details,
 		export_details=export_details, eway_bill_details=eway_bill_details
 	)
-	einvoice = json.loads(einvoice)
-	
+	einvoice = safe_json_load(einvoice)
+
 	validations = json.loads(read_json('einv_validation'))
 	errors = validate_einvoice(validations, einvoice)
 	if errors:
@@ -371,6 +377,18 @@
 
 	return einvoice
 
+def safe_json_load(json_string):
+	JSONDecodeError = ValueError if six.PY2 else json.JSONDecodeError
+
+	try:
+		return json.loads(json_string)
+	except JSONDecodeError as e:
+		# print a snippet of 40 characters around the location where error occured
+		pos = e.pos
+		start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
+		snippet = json_string[start:end]
+		frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
+
 def validate_einvoice(validations, einvoice, errors=[]):
 	for fieldname, field_validation in validations.items():
 		value = einvoice.get(fieldname, None)
@@ -419,7 +437,7 @@
 			errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum))
 		if pattern_str and not pattern.match(value):
 			errors.append(field_validation.get('validationMsg'))
-	
+
 	return errors
 
 class RequestFailed(Exception): pass
@@ -452,19 +470,19 @@
 		else:
 			credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
 		return credentials
-	
+
 	def get_seller_gstin(self):
 		gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
 		if not gstin:
 			frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
 		return gstin
-	
+
 	def get_auth_token(self):
 		if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0:
 			self.fetch_auth_token()
-		
+
 		return self.e_invoice_settings.auth_token
-	
+
 	def make_request(self, request_type, url, headers=None, data=None):
 		if request_type == 'post':
 			res = make_post_request(url, headers=headers, data=data)
@@ -473,7 +491,7 @@
 
 		self.log_request(url, headers, data, res)
 		return res
-	
+
 	def log_request(self, url, headers, data, res):
 		headers.update({ 'password': self.credentials.password })
 		request_log = frappe.get_doc({
@@ -504,7 +522,7 @@
 		except Exception:
 			self.log_error(res)
 			self.raise_error(True)
-	
+
 	def get_headers(self):
 		return {
 			'content-type': 'application/json',
@@ -526,7 +544,7 @@
 			else:
 				self.log_error(res)
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			self.raise_error()
 
@@ -571,7 +589,7 @@
 
 			else:
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
 			self.raise_error(errors=errors)
@@ -579,7 +597,7 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
+
 	def get_irn_details(self, irn):
 		headers = self.get_headers()
 
@@ -590,7 +608,7 @@
 				return res.get('result')
 			else:
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
 			self.raise_error(errors=errors)
@@ -598,7 +616,7 @@
 		except Exception:
 			self.log_error()
 			self.raise_error(True)
-	
+
 	def cancel_irn(self, irn, reason, remark):
 		headers = self.get_headers()
 		data = json.dumps({
@@ -620,7 +638,7 @@
 
 			else:
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
 			self.raise_error(errors=errors)
@@ -669,7 +687,7 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
+
 	def cancel_eway_bill(self, eway_bill, reason, remark):
 		headers = self.get_headers()
 		data = json.dumps({
@@ -701,7 +719,7 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
+
 	def sanitize_error_message(self, message):
 		'''
 			On validation errors, response message looks something like this:
@@ -740,7 +758,7 @@
 			"Exception:", err_tb
 		])
 		frappe.log_error(title=_('E Invoice Request Failed'), message=message)
-	
+
 	def raise_error(self, raise_exception=False, errors=[]):
 		title = _('E Invoice Request Failed')
 		if errors:
@@ -753,7 +771,7 @@
 				raise_exception=raise_exception,
 				indicator='red'
 			)
-	
+
 	def set_einvoice_data(self, res):
 		enc_signed_invoice = res.get('SignedInvoice')
 		dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
@@ -792,12 +810,19 @@
 		_file.save()
 		frappe.db.commit()
 		self.invoice.qrcode_image = _file.file_url
-	
+
 	def update_invoice(self):
 		self.invoice.flags.ignore_validate_update_after_submit = True
 		self.invoice.flags.ignore_validate = True
 		self.invoice.save()
 
+
+def sanitize_for_json(string):
+	"""Escape JSON specific characters from a string."""
+
+	# json.dumps adds double-quotes to the string. Indexing to remove them.
+	return json.dumps(string)[1:-1]
+
 @frappe.whitelist()
 def get_einvoice(doctype, docname):
 	invoice = frappe.get_doc(doctype, docname)
diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json
index ff88e0f..8481c27 100644
--- a/erpnext/regional/india/gst_state_code_data.json
+++ b/erpnext/regional/india/gst_state_code_data.json
@@ -168,5 +168,10 @@
   "state_number": "37",
   "state_code": "AD",
   "state_name": "Andhra Pradesh (New)"
+ },
+ {
+  "state_number": "38",
+  "state_code": "LA",
+  "state_name": "Ladakh"
  }
 ]
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index e89885f..cb30605 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -55,6 +55,14 @@
 			frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
 				.format(doc.gst_state_number))
 
+def validate_pan_for_india(doc, method):
+	if doc.get('country') != 'India' or not doc.pan:
+		return
+
+	p = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}")
+	if not p.match(doc.pan):
+		frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN."))
+
 def validate_tax_category(doc, method):
 	if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}):
 		if doc.is_inter_state:
@@ -772,3 +780,24 @@
 				)
 
 	return gl_entries
+
+@frappe.whitelist()
+def get_regional_round_off_accounts(company, account_list):
+	country = frappe.get_cached_value('Company', company, 'country')
+
+	if country != 'India':
+		return
+
+	if isinstance(account_list, string_types):
+		account_list = json.loads(account_list)
+
+	if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'):
+		return
+
+	gst_accounts = get_gst_accounts(company)
+	gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
+		+ gst_accounts.get('igst_account')
+
+	account_list.extend(gst_account_list)
+
+	return account_list
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 96dc3f7..09b04ff 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -236,6 +236,7 @@
 		self.cgst_sgst_invoices = []
 
 		unidentified_gst_accounts = []
+		unidentified_gst_accounts_invoice = []
 		for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
 			if account in self.gst_accounts.cess_account:
 				self.invoice_cess.setdefault(parent, tax_amount)
@@ -251,6 +252,7 @@
 						if not (cgst_or_sgst or account in self.gst_accounts.igst_account):
 							if "gst" in account.lower() and account not in unidentified_gst_accounts:
 								unidentified_gst_accounts.append(account)
+								unidentified_gst_accounts_invoice.append(parent)
 							continue
 
 						for item_code, tax_amounts in item_wise_tax_detail.items():
@@ -273,7 +275,7 @@
 
 		# Build itemised tax for export invoices where tax table is blank
 		for invoice, items in iteritems(self.invoice_items):
-			if invoice not in self.items_based_on_tax_rate \
+			if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
 				and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
 					self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
 
diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py
index c1c8aed..4e57ff7 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.py
+++ b/erpnext/regional/report/irs_1099/irs_1099.py
@@ -32,6 +32,10 @@
 
 	data = []
 	columns = get_columns()
+	conditions = ""
+	if filters.supplier_group:
+		conditions += "AND s.supplier_group = %s" %frappe.db.escape(filters.get("supplier_group"))
+
 	data = frappe.db.sql("""
 		SELECT
 			s.supplier_group as "supplier_group",
@@ -46,15 +50,17 @@
 				AND s.irs_1099 = 1
 				AND gl.fiscal_year = %(fiscal_year)s
 				AND gl.party_type = "Supplier"
+				AND gl.company = %(company)s
+				{conditions}
+			
 		GROUP BY
 			gl.party
+
 		ORDER BY
-			gl.party DESC
-	""", {
-		"fiscal_year": filters.fiscal_year,
-		"supplier_group": filters.supplier_group,
-		"company": filters.company
-	}, as_dict=True)
+			gl.party DESC""".format(conditions=conditions), {
+				"fiscal_year": filters.fiscal_year,
+				"company": filters.company
+			}, as_dict=True)
 
 	return columns, data
 
@@ -79,13 +85,13 @@
 			"fieldname": "tax_id",
 			"label": _("Tax ID"),
 			"fieldtype": "Data",
-			"width": 120
+			"width": 200
 		},
 		{
 			"fieldname": "payments",
 			"label": _("Total Payments"),
 			"fieldtype": "Currency",
-			"width": 120
+			"width": 200
 		}
 	]
 
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 557c715..a048928 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -16,6 +16,8 @@
   "customer_name",
   "gender",
   "customer_type",
+  "pan",
+  "tax_withholding_category",
   "default_bank_account",
   "lead_name",
   "image",
@@ -34,9 +36,8 @@
   "companies",
   "currency_and_price_list",
   "default_currency",
-  "default_price_list",
   "column_break_14",
-  "language",
+  "default_price_list",
   "address_contacts",
   "address_html",
   "website",
@@ -59,6 +60,7 @@
   "column_break_45",
   "market_segment",
   "industry",
+  "language",
   "is_frozen",
   "column_break_38",
   "loyalty_program",
@@ -210,7 +212,8 @@
    "fieldtype": "Link",
    "ignore_user_permissions": 1,
    "label": "Represents Company",
-   "options": "Company"
+   "options": "Company",
+   "unique": 1
   },
   {
    "depends_on": "represents_company",
@@ -479,13 +482,25 @@
    "fieldname": "dn_required",
    "fieldtype": "Check",
    "label": "Allow Sales Invoice Creation Without Delivery Note"
+  },
+  {
+   "fieldname": "pan",
+   "fieldtype": "Data",
+   "label": "PAN"
+  },
+  {
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "label": "Tax Withholding Category",
+   "options": "Tax Withholding Category"
   }
  ],
  "icon": "fa fa-user",
  "idx": 363,
  "image_field": "image",
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-03-17 11:03:42.706907",
+ "modified": "2021-01-27 12:54:57.258959",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index bf8b7fc..c452594 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -126,7 +126,9 @@
 		'''If Customer created from Lead, update lead status to "Converted"
 		update Customer link in Quotation, Opportunity'''
 		if self.lead_name:
-			frappe.db.set_value('Lead', self.lead_name, 'status', 'Converted', update_modified=False)
+			lead = frappe.get_doc('Lead', self.lead_name)
+			lead.status = 'Converted'
+			lead.save()
 
 	def create_lead_address_contact(self):
 		if self.lead_name:
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index 59ae7b2..a6785f7 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -47,6 +47,7 @@
   "base_amount",
   "base_net_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_43",
   "valuation_rate",
@@ -634,12 +635,20 @@
    "print_hide": 1,
    "read_only": 1,
    "report_hide": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-05-19 20:48:43.222229",
+ "modified": "2021-01-30 21:39:40.174551",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation Item",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index e492377..e3b41e6 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -514,7 +514,7 @@
 	make_delivery_note: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
-			frm: me.frm
+			frm: this.frm
 		})
 	},
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 1516dd6..e561291 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -180,6 +180,7 @@
 			update_coupon_code_count(self.coupon_code,'used')
 
 	def on_cancel(self):
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
 		super(SalesOrder, self).on_cancel()
 
 		# Cannot cancel closed SO
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index e259367..52a0174 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -17,6 +17,18 @@
 from erpnext.stock.doctype.item.test_item import make_item
 
 class TestSalesOrder(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order"))
+
+	@classmethod
+	def tearDownClass(cls) -> None:
+		# reset config to previous state
+		frappe.db.set_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
+
 	def tearDown(self):
 		frappe.set_user("Administrator")
 
@@ -325,6 +337,9 @@
 		create_dn_against_so(so.name, 4)
 		make_sales_invoice(so.name)
 
+		prev_total = so.get("base_total")
+		prev_total_in_words = so.get("base_in_words")
+
 		first_item_of_so = so.get("items")[0]
 		trans_item = json.dumps([
 			{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
@@ -340,6 +355,12 @@
 		self.assertEqual(so.get("items")[-1].amount, 1400)
 		self.assertEqual(so.status, 'To Deliver and Bill')
 
+		updated_total = so.get("base_total")
+		updated_total_in_words = so.get("base_in_words")
+
+		self.assertEqual(updated_total, prev_total+1400)
+		self.assertNotEqual(updated_total_in_words, prev_total_in_words)
+
 	def test_update_child_removing_item(self):
 		so = make_sales_order(**{
 			"item_list": [{
@@ -1040,6 +1061,38 @@
 
 		self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
 
+	def test_cancel_sales_order_after_cancel_payment_entry(self):
+		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+		# make a sales order
+		so = make_sales_order()
+
+		# disable unlinking of payment entry
+		frappe.db.set_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order", 0)
+
+		# create a payment entry against sales order
+		pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC")
+		pe.reference_no = "1"
+		pe.reference_date = nowdate()
+		pe.paid_from_account_currency = so.currency
+		pe.paid_to_account_currency = so.currency
+		pe.source_exchange_rate = 1
+		pe.target_exchange_rate = 1
+		pe.paid_amount = so.grand_total
+		pe.save(ignore_permissions=True)
+		pe.submit()
+
+		# Cancel payment entry
+		po_doc = frappe.get_doc("Payment Entry", pe.name)
+		po_doc.cancel()
+
+		# Cancel sales order
+		try:
+			so_doc = frappe.get_doc('Sales Order', so.name)
+			so_doc.cancel()
+		except Exception:
+			self.fail("Can not cancel sales order with linked cancelled payment entry")
+
 	def test_request_for_raw_materials(self):
 		item = make_item("_Test Finished Item", {"is_stock_item": 1,
 			"maintain_stock": 1,
@@ -1198,4 +1251,4 @@
 	))
 	workflow.insert(ignore_permissions=True)
 
-	return workflow
\ No newline at end of file
+	return workflow
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 159655b..37e47a9 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -46,6 +46,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_24",
   "net_rate",
@@ -214,7 +215,6 @@
    "fieldtype": "Link",
    "label": "UOM",
    "options": "UOM",
-   "print_hide": 0,
    "reqd": 1
   },
   {
@@ -780,12 +780,20 @@
    "fieldname": "manufacturing_section_section",
    "fieldtype": "Section Break",
    "label": "Manufacturing Section"
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-012-07 20:54:32.309460",
+ "modified": "2021-01-30 21:35:07.617320",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index f753b6d..044e803 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -477,7 +477,7 @@
 		const taxes = frm.doc.taxes.map(t => {
 			return {
 				description: t.description, rate: t.rate
-			}
+			};
 		});
 		this.render_taxes(frm.doc.total_taxes_and_charges, taxes);
 	}
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index c9b8ad9..bcbac3b 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -153,32 +153,23 @@
 				me.$payment_modes.find(`.${mode}-amount`).css('display', 'none');
 				me.$payment_modes.find(`.${mode}-name`).css('display', 'inline');
 
-				const doc = me.events.get_frm().doc;
 				me.selected_mode = me[`${mode}_control`];
 				me.selected_mode && me.selected_mode.$input.get(0).focus();
-				const current_value = me.selected_mode ? me.selected_mode.get_value() : undefined;
-				!current_value && doc.grand_total > doc.paid_amount && me.selected_mode ?
-					me.selected_mode.set_value(doc.grand_total - doc.paid_amount) : '';
+				me.auto_set_remaining_amount();
 			}
 		});
 
-		frappe.realtime.on("process_phone_payment", function(data) {
-			frappe.dom.unfreeze();
-			cur_frm.reload_doc();
-			let message = data["ResultDesc"];
-			let title = __("Payment Failed");
+		frappe.ui.form.on('POS Invoice', 'contact_mobile', (frm) => {
+			const contact = frm.doc.contact_mobile;
+			const request_button = $(this.request_for_payment_field.$input[0]);
+			if (contact) {
+				request_button.removeClass('btn-default').addClass('btn-primary');
+			} else {
+				request_button.removeClass('btn-primary').addClass('btn-default');
+      }
+    });
 
-			if (data["ResultCode"] == 0) {
-				title = __("Payment Received");
-				$('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`);
-				me.events.submit_invoice();
-			}
-
-			frappe.msgprint({
-				"message": message,
-				"title": title
-			});
-		});
+		this.setup_listener_for_payments();
 
 		this.$payment_modes.on('click', '.shortcut', () => {
 			const value = $(this).attr('data-value');
@@ -224,6 +215,41 @@
 		});
 	}
 
+	setup_listener_for_payments() {
+		frappe.realtime.on("process_phone_payment", (data) => {
+			const doc = this.events.get_frm().doc;
+			const { response, amount, success, failure_message } = data;
+			let message, title;
+
+			if (success) {
+				title = __("Payment Received");
+				if (amount >= doc.grand_total) {
+					frappe.dom.unfreeze();
+					message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]);
+					this.events.submit_invoice();
+					cur_frm.reload_doc();
+
+				} else {
+					message = __("Payment of {0} received successfully. Waiting for other requests to complete...", [format_currency(amount, doc.currency, 0)]);
+				}
+			} else if (failure_message) {
+				message = failure_message;
+				title = __("Payment Failed");
+			}
+
+			frappe.msgprint({ "message": message, "title": title });
+		});
+	}
+
+	auto_set_remaining_amount() {
+		const doc = this.events.get_frm().doc;
+		const remaining_amount = doc.grand_total - doc.paid_amount;
+		const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined;
+		if (!current_value && remaining_amount > 0 && this.selected_mode) {
+			this.selected_mode.set_value(remaining_amount);
+		}
+	}
+
 	attach_shortcuts() {
 		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
 		this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`);
@@ -333,9 +359,11 @@
 					fieldtype: 'Currency',
 					placeholder: __('Enter {0} amount.', [p.mode_of_payment]),
 					onchange: function() {
-						if (this.value || this.value == 0) {
-							frappe.model.set_value(p.doctype, p.name, 'amount', flt(this.value))
-								.then(() => me.update_totals_section());
+						const current_value = frappe.model.get_value(p.doctype, p.name, 'amount');
+						if (current_value != this.value) {
+							frappe.model
+								.set_value(p.doctype, p.name, 'amount', flt(this.value))
+								.then(() => me.update_totals_section())
 
 							const formatted_currency = format_currency(this.value, currency);
 							me.$payment_modes.find(`.${mode}-amount`).html(formatted_currency);
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 7a72fe3..0df4c87 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -28,7 +28,7 @@
 			"Party Account", "Employee", "Sales Taxes and Charges Template",
 			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
 			"Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
-			"Item Default", "Customer", "Supplier"):
+			"Item Default", "Customer", "Supplier", "GST Account"):
 				delete_for_doctype(doctype, company_name)
 
 	# reset company values
diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json
index 10f9bd0..0e2ed9e 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.json
+++ b/erpnext/setup/doctype/customer_group/customer_group.json
@@ -139,7 +139,7 @@
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2020-03-18 18:10:13.048492",
+ "modified": "2021-02-08 17:01:52.162202",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Customer Group",
@@ -189,6 +189,15 @@
    "permlevel": 1,
    "read": 1,
    "role": "Sales Manager"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "report": 1,
+   "role": "Customer",
+   "select": 1,
+   "share": 1
   }
  ],
  "search_fields": "parent_customer_group",
diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json
index 31624ed..e835214 100644
--- a/erpnext/setup/doctype/item_group/item_group.json
+++ b/erpnext/setup/doctype/item_group/item_group.json
@@ -214,7 +214,7 @@
  "is_tree": 1,
  "links": [],
  "max_attachments": 3,
- "modified": "2020-12-30 12:57:38.876956",
+ "modified": "2021-02-08 17:02:44.951572",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Item Group",
@@ -271,6 +271,15 @@
    "read": 1,
    "report": 1,
    "role": "Accounts User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "report": 1,
+   "role": "Customer",
+   "select": 1,
+   "share": 1
   }
  ],
  "search_fields": "parent_item_group",
diff --git a/erpnext/setup/doctype/territory/territory.json b/erpnext/setup/doctype/territory/territory.json
index aa8e048..a25bda0 100644
--- a/erpnext/setup/doctype/territory/territory.json
+++ b/erpnext/setup/doctype/territory/territory.json
@@ -123,7 +123,7 @@
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2020-03-18 18:11:36.623555",
+ "modified": "2021-02-08 17:10:03.767426",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Territory",
@@ -166,6 +166,15 @@
   {
    "read": 1,
    "role": "Maintenance User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "report": 1,
+   "role": "Customer",
+   "select": 1,
+   "share": 1
   }
  ],
  "search_fields": "parent_territory,territory_manager",
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index 3c9f849..681d161 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -462,6 +462,9 @@
 		return customer
 
 def get_debtors_account(cart_settings):
+	if not cart_settings.payment_gateway_account:
+		frappe.throw(_("Payment Gateway Account not set"), _("Mandatory"))
+
 	payment_gateway_account_currency = \
 		frappe.get_doc("Payment Gateway Account", cart_settings.payment_gateway_account).currency
 
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json
index 702583a..3691721 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json
@@ -26,10 +26,10 @@
   "quotation_series",
   "section_break_8",
   "enable_checkout",
-  "payment_success_url",
-  "column_break_11",
   "save_quotations_as_draft",
-  "payment_gateway_account"
+  "column_break_11",
+  "payment_gateway_account",
+  "payment_success_url"
  ],
  "fields": [
   {
@@ -143,10 +143,12 @@
   },
   {
    "default": "Orders",
+   "depends_on": "enable_checkout",
    "description": "After payment completion redirect user to selected page.",
    "fieldname": "payment_success_url",
    "fieldtype": "Select",
    "label": "Payment Success Url",
+   "mandatory_depends_on": "enable_checkout",
    "options": "\nOrders\nInvoices\nMy Account"
   },
   {
@@ -154,9 +156,11 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "enable_checkout",
    "fieldname": "payment_gateway_account",
    "fieldtype": "Link",
    "label": "Payment Gateway Account",
+   "mandatory_depends_on": "enable_checkout",
    "options": "Payment Gateway Account"
   },
   {
@@ -186,7 +190,7 @@
  "idx": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-02-01 18:18:54.606535",
+ "modified": "2021-02-11 18:48:30.433058",
  "modified_by": "Administrator",
  "module": "Shopping Cart",
  "name": "Shopping Cart Settings",
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index 8d64efe..9e240cc 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -64,10 +64,10 @@
 	if not account and warehouse.company:
 		account = get_company_default_inventory_account(warehouse.company)
 
-	if not account and warehouse.company:
+	if not account and warehouse.company and not warehouse.is_group:
 		frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}")
 			.format(warehouse.name, warehouse.company))
 	return account
 
 def get_company_default_inventory_account(company):
-	return frappe.get_cached_value('Company',  company,  'default_inventory_account')
+	return frappe.get_cached_value('Company',  company, 'default_inventory_account')
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 97f85ba..cbd272d 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -298,9 +298,9 @@
 		self.assertEqual(details.get('price_list_rate'), 400)
 
 def create_batch(item_code, rate, create_item_price_for_batch):
-	pi = make_purchase_invoice(company="_Test Company with perpetual inventory",
-		warehouse= "Stores - TCP1", cost_center = "Main - TCP1", update_stock=1,
-		expense_account ="_Test Account Cost for Goods Sold - TCP1", item_code=item_code)
+	pi = make_purchase_invoice(company="_Test Company",
+		warehouse= "Stores - _TC", cost_center = "Main - _TC", update_stock=1,
+		expense_account ="_Test Account Cost for Goods Sold - _TC", item_code=item_code)
 
 	batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name})
 
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 1088b41..0514bd2 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -16,8 +16,9 @@
 	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, validate_negative_qty_in_future_sle
+			from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
 
 			if not args.get("posting_date"):
 				args["posting_date"] = nowdate()
@@ -34,11 +35,13 @@
 				"posting_time": args.get("posting_time"),
 				"voucher_type": args.get("voucher_type"),
 				"voucher_no": args.get("voucher_no"),
-				"sle_id": args.name
+				"sle_id": args.name,
+				"creation": args.creation
 			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
-			# Validate negative qty in future transactions
-			validate_negative_qty_in_future_sle(args)
+			# 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)
@@ -51,7 +54,7 @@
 		self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
 		self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
 		self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-
+		
 		self.set_projected_qty()
 		self.db_update()
 
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 559f8be..d39b229 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -489,7 +489,10 @@
 	def test_closed_delivery_note(self):
 		from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
 
-		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
+		make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
+
+		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1',
+			cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
 
 		dn.submit()
 
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 9de088d..1799624 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -47,6 +47,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_25",
   "net_rate",
@@ -743,13 +744,21 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-26 17:31:27.029803",
+ "modified": "2021-01-30 21:42:03.767968",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index b661570..7b7d2da 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -177,7 +177,7 @@
 		if not self.valuation_rate and self.standard_rate:
 			self.valuation_rate = self.standard_rate
 
-		if not self.valuation_rate:
+		if not self.valuation_rate and not self.is_customer_provided_item:
 			frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered"))
 
 		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
index 471e685..9b1a47e 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
@@ -7,6 +7,7 @@
  "engine": "InnoDB",
  "field_order": [
   "specification",
+  "parameter_group",
   "value",
   "numeric",
   "column_break_3",
@@ -75,12 +76,20 @@
    "in_list_view": 1,
    "label": "Numeric",
    "width": "80px"
+  },
+  {
+   "fetch_from": "specification.parameter_group",
+   "fieldname": "parameter_group",
+   "fieldtype": "Link",
+   "label": "Parameter Group",
+   "options": "Quality Inspection Parameter Group",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-01 19:18:46.924399",
+ "modified": "2021-02-04 18:50:02.056173",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Quality Inspection Parameter",
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 144101c..984ae46 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
@@ -148,7 +148,6 @@
 
 
 	def test_landed_cost_voucher_for_odd_numbers (self):
-
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", do_not_save=True)
 		pr.items[0].cost_center = "Main - TCP1"
 		for x in range(2):
@@ -208,6 +207,10 @@
 		self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100)
 
 	def test_multi_currency_lcv(self):
+		from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
+
+		save_new_records(test_records)
+
 		## Create USD Shipping charges_account
 		usd_shipping = create_account(account_name="Shipping Charges USD",
 			parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 550c849..d721014 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -295,7 +295,8 @@
 								"against": warehouse_account[d.warehouse]["account"],
 								"cost_center": d.cost_center,
 								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"credit": flt(amount["base_amount"]),
+								"credit": (flt(amount["base_amount"]) if (amount["base_amount"] or
+									account_currency!=self.company_currency) else flt(amount["amount"])),
 								"credit_in_account_currency": flt(amount["amount"]),
 								"project": d.project
 							}, item=d))
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index ca58ab2..7741ee7 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -94,10 +94,15 @@
 		frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete()
 
 	def test_purchase_receipt_no_gl_entry(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
 		company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
 
-		existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC"}, "stock_value")
+		existing_bin_qty, existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
+			"warehouse": "_Test Warehouse - _TC"}, ["actual_qty", "stock_value"])
+
+		if existing_bin_qty < 0:
+			make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=abs(existing_bin_qty))
 
 		pr = make_purchase_receipt()
 
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index e991192..8974ad9 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -48,6 +48,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_29",
   "net_rate",
@@ -875,6 +876,14 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
    "fieldname": "delivery_note_item",
    "fieldtype": "Data",
    "label": "Delivery Note Item",
@@ -886,7 +895,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-26 16:50:56.479347",
+ "modified": "2021-01-30 21:44:06.918515",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
index 0b5a9b5..418b482 100644
--- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
@@ -7,24 +7,34 @@
  "engine": "InnoDB",
  "field_order": [
   "parameter",
+  "parameter_group",
   "description"
  ],
  "fields": [
   {
    "fieldname": "parameter",
    "fieldtype": "Data",
+   "in_list_view": 1,
    "label": "Parameter",
+   "reqd": 1,
    "unique": 1
   },
   {
    "fieldname": "description",
    "fieldtype": "Text Editor",
    "label": "Description"
+  },
+  {
+   "fieldname": "parameter_group",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Parameter Group",
+   "options": "Quality Inspection Parameter Group"
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-12-28 18:06:54.897317",
+ "modified": "2021-02-19 20:33:30.657406",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection Parameter",
diff --git a/erpnext/accounts/doctype/bank_statement_settings/__init__.py b/erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py
similarity index 100%
copy from erpnext/accounts/doctype/bank_statement_settings/__init__.py
copy to erpnext/stock/doctype/quality_inspection_parameter_group/__init__.py
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js
new file mode 100644
index 0000000..8716a29
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Quality Inspection Parameter Group', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json
new file mode 100644
index 0000000..5726474
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.json
@@ -0,0 +1,82 @@
+{
+ "actions": [],
+ "autoname": "field:group_name",
+ "creation": "2021-02-04 18:44:12.223295",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "group_name"
+ ],
+ "fields": [
+  {
+   "fieldname": "group_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Parameter Group Name",
+   "reqd": 1,
+   "unique": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-02-04 18:44:12.223295",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Quality Inspection Parameter Group",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Quality Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Manufacturing User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
new file mode 100644
index 0000000..1a3b1a0
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class QualityInspectionParameterGroup(Document):
+	pass
diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
new file mode 100644
index 0000000..212d4b8
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter_group/test_quality_inspection_parameter_group.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestQualityInspectionParameterGroup(unittest.TestCase):
+	pass
diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
index 35d58ef..0eff5a8 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
@@ -7,6 +7,7 @@
  "engine": "InnoDB",
  "field_order": [
   "specification",
+  "parameter_group",
   "status",
   "value",
   "numeric",
@@ -210,12 +211,20 @@
    "fieldtype": "Check",
    "in_list_view": 1,
    "label": "Numeric"
+  },
+  {
+   "fetch_from": "specification.parameter_group",
+   "fieldname": "parameter_group",
+   "fieldtype": "Link",
+   "label": "Parameter Group",
+   "options": "Quality Inspection Parameter Group",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-01 19:46:22.138018",
+ "modified": "2021-02-04 19:15:37.991221",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection Reading",
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index ba2c2c6..8436acb 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -46,6 +46,9 @@
 
 def repost(doc):
 	try:
+		if not frappe.db.exists("Repost Item Valuation", doc.name):
+			return
+
 		doc.set_status('In Progress')
 		frappe.db.commit()
 
@@ -64,7 +67,7 @@
 			message += "<br>" + "Traceback: <br>" + traceback
 		frappe.db.set_value(doc.doctype, doc.name, 'error_log', message)
 
-		notify_error_to_stock_managers(doc)
+		notify_error_to_stock_managers(doc, message)
 		doc.set_status('Failed')
 		raise
 	finally:
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index e1fa207..9c3e22f 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -190,6 +190,7 @@
 	company.abbr = abbr
 	company.default_currency = 'EUR'
 	company.country = 'Germany'
+	company.enable_perpetual_inventory = 0
 	company.insert()
 	return company
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d77b70f..9cdc3cf 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -163,7 +163,7 @@
 		if self.purpose not in valid_purposes:
 			frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
 
-		if self.job_card and self.purpose != 'Material Transfer for Manufacture':
+		if self.job_card and self.purpose not in ['Material Transfer for Manufacture', 'Repack']:
 			frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
 				.format(self.job_card))
 
@@ -823,6 +823,7 @@
 		if self.job_card:
 			job_doc = frappe.get_doc('Job Card', self.job_card)
 			job_doc.set_transferred_qty(update_status=True)
+			job_doc.set_transferred_qty_in_job_card(self)
 
 		if self.work_order:
 			pro_doc = frappe.get_doc("Work Order", self.work_order)
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index 988ae92..864ff48 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -69,7 +69,8 @@
   "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
-  "quality_inspection"
+  "quality_inspection",
+  "job_card_item"
  ],
  "fields": [
   {
@@ -532,13 +533,22 @@
    "fieldname": "is_finished_item",
    "fieldtype": "Check",
    "label": "Is Finished Item"
+  },
+  {
+   "fieldname": "job_card_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Job Card Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-30 15:00:44.489442",
+ "modified": "2021-02-11 13:47:50.158754",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index a5c303c..b0e7440 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -27,16 +27,18 @@
 
 	def validate(self):
 		self.flags.ignore_submit_comment = True
-		from erpnext.stock.utils import validate_warehouse_company
+		from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
 		self.validate_mandatory()
 		self.validate_item()
 		self.validate_batch()
+		validate_disabled_warehouse(self.warehouse)
 		validate_warehouse_company(self.warehouse, self.company)
 		self.scrub_posting_time()
 		self.validate_and_set_fiscal_year()
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
 
+
 	def on_submit(self):
 		self.check_stock_frozen_date()
 		self.actual_amt_check()
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 46919c8..f54b3c1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -23,6 +23,7 @@
 
 		cancel = sl_entries[0].get("is_cancelled")
 		if cancel:
+			validate_cancellation(sl_entries)
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 		for sle in sl_entries:
@@ -45,6 +46,21 @@
 			args = sle_doc.as_dict()
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
+def validate_cancellation(args):
+	if args[0].get("is_cancelled"):
+		repost_entry = frappe.db.get_value("Repost Item Valuation", {
+			'voucher_type': args[0].voucher_type,
+			'voucher_no': args[0].voucher_no,
+			'docstatus': 1
+		}, ['name', 'status'], as_dict=1)
+
+		if repost_entry:
+			if repost_entry.status == 'In Progress':
+				frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."))
+			if repost_entry.status == 'Queued':
+				doc = frappe.get_doc("Repost Item Valuation", repost_entry.name)
+				doc.cancel()
+				doc.delete()
 
 def set_as_cancel(voucher_type, voucher_no):
 	frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1,
@@ -74,7 +90,8 @@
 			"item_code": args[i].item_code,
 			"warehouse": args[i].warehouse,
 			"posting_date": args[i].posting_date,
-			"posting_time": args[i].posting_time
+			"posting_time": args[i].posting_time,
+			"creation": args[i].get("creation")
 		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
 		for item_wh, new_sle in iteritems(obj.new_items):
@@ -86,7 +103,7 @@
 def get_args_for_voucher(voucher_type, voucher_no):
 	return frappe.db.get_all("Stock Ledger Entry",
 		filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
-		fields=["item_code", "warehouse", "posting_date", "posting_time"],
+		fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"],
 		order_by="creation asc",
 		group_by="item_code, warehouse"
 	)
@@ -155,7 +172,7 @@
 		"""
 		self.data.setdefault(args.warehouse, frappe._dict())
 		warehouse_dict = self.data[args.warehouse]
-		previous_sle = self.get_sle_before_datetime(args)
+		previous_sle = self.get_previous_sle_of_current_voucher(args)
 		warehouse_dict.previous_sle = previous_sle
 
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
@@ -167,9 +184,35 @@
 			"stock_value_difference": 0.0
 		})
 
+	def get_previous_sle_of_current_voucher(self, args):
+		"""get stock ledger entries filtered by specific posting datetime conditions"""
+
+		args['time_format'] = '%H:%i:%s'
+		if not args.get("posting_date"):
+			args["posting_date"] = "1900-01-01"
+		if not args.get("posting_time"):
+			args["posting_time"] = "00:00"
+
+		sle = frappe.db.sql("""
+			select *, timestamp(posting_date, posting_time) as "timestamp"
+			from `tabStock Ledger Entry`
+			where item_code = %(item_code)s
+				and warehouse = %(warehouse)s
+				and is_cancelled = 0
+				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+			order by timestamp(posting_date, posting_time) desc, creation desc
+			limit 1""", args, as_dict=1)
+
+		return sle[0] if sle else frappe._dict()
+
+
 	def build(self):
+		from erpnext.controllers.stock_controller import check_if_future_sle_exists
+
 		if self.args.get("sle_id"):
-			self.process_sle_against_current_voucher()
+			self.process_sle_against_current_timestamp()
+			if not check_if_future_sle_exists(self.args):
+				self.update_bin()
 		else:
 			entries_to_fix = self.get_future_entries_to_fix()
 
@@ -183,17 +226,19 @@
 				if sle.dependant_sle_voucher_detail_no:
 					entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle)
 
+			self.update_bin()
+
 		if self.exceptions:
 			self.raise_exceptions()
 
-		self.update_bin()
-
-	def process_sle_against_current_voucher(self):
+	def process_sle_against_current_timestamp(self):
 		sl_entries = self.get_sle_against_current_voucher()
 		for sle in sl_entries:
 			self.process_sle(sle)
 
 	def get_sle_against_current_voucher(self):
+		self.args['time_format'] = '%H:%i:%s'
+
 		return frappe.db.sql("""
 			select
 				*, timestamp(posting_date, posting_time) as "timestamp"
@@ -202,8 +247,8 @@
 			where
 				item_code = %(item_code)s
 				and warehouse = %(warehouse)s
-				and voucher_type = %(voucher_type)s
-				and voucher_no = %(voucher_no)s
+				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+
 			order by
 				creation ASC
 			for update
@@ -230,7 +275,6 @@
 			return entries_to_fix
 		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data:
 			return entries_to_fix
-
 		self.initialize_previous_data(dependant_sle)
 
 		args = self.data[dependant_sle.warehouse].previous_sle \
@@ -396,7 +440,7 @@
 
 		# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
 		if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
-			doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no)
+			doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
 			doc.update_valuation_rate(reset_outgoing_rate=False)
 			for d in (doc.items + doc.supplied_items):
 				d.db_update()
@@ -637,7 +681,6 @@
 		# update bin for each warehouse
 		for warehouse, data in iteritems(self.data):
 			bin_doc = get_bin(self.item_code, warehouse)
-
 			bin_doc.update({
 				"valuation_rate": data.valuation_rate,
 				"actual_qty": data.qty_after_transaction,
@@ -763,6 +806,25 @@
 
 	return valuation_rate
 
+def update_qty_in_future_sle(args, allow_negative_stock=None):
+	frappe.db.sql("""
+		update `tabStock Ledger Entry`
+		set qty_after_transaction = qty_after_transaction + {qty}
+		where
+			item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and voucher_no != %(voucher_no)s
+			and is_cancelled = 0
+			and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
+				or (
+					timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
+					and creation > %(creation)s
+				)
+			)
+	""".format(qty=args.actual_qty), args)
+
+	validate_negative_qty_in_future_sle(args, allow_negative_stock)
+
 def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
 	allow_negative_stock = allow_negative_stock \
 		or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
@@ -791,7 +853,7 @@
 			and voucher_no != %(voucher_no)s
 			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
 			and is_cancelled = 0
-			and qty_after_transaction + {0} < 0
+			and qty_after_transaction < 0
 		order by timestamp(posting_date, posting_time) asc
 		limit 1
-	""".format(args.actual_qty), args, as_dict=1)
\ No newline at end of file
+	""", args, as_dict=1)
\ No newline at end of file
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 4ea7e4f..0af3d90 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -5,7 +5,7 @@
 import frappe, erpnext
 from frappe import _
 import json
-from frappe.utils import flt, cstr, nowdate, nowtime
+from frappe.utils import flt, cstr, nowdate, nowtime, get_link_to_form
 
 from six import string_types
 
@@ -284,6 +284,10 @@
 	if frappe.db.get_value("Warehouse", warehouse, "is_group"):
 		frappe.throw(_("Group node warehouse is not allowed to select for transactions"))
 
+def validate_disabled_warehouse(warehouse):
+	if frappe.db.get_value("Warehouse", warehouse, "disabled"):
+		frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse)))
+
 def update_included_uom_in_report(columns, result, include_uom, conversion_factors):
 	if not include_uom or not conversion_factors:
 		return
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 167e80f..9fe12f9 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -49,8 +49,8 @@
 	},
 
 	refresh: function (frm) {
-		if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") {
-			if (frm.doc.service_level_agreement) {
+		if (frm.doc.status !== "Closed") {
+			if (frm.doc.service_level_agreement && frm.doc.agreement_status === "Ongoing") {
 				frappe.call({
 					"method": "frappe.client.get",
 					args: {
diff --git a/requirements.txt b/requirements.txt
index 4511aa5..5a35236 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@
 gocardless-pro==1.11.0
 googlemaps==3.1.1
 pandas>=1.0.5
-plaid-python==6.0.0
+plaid-python>=7.0.0
 pycountry==19.8.18
 PyGithub==1.44.1
 python-stdnum==1.12
@@ -12,4 +12,4 @@
 tweepy==3.8.0
 Unidecode==1.1.1
 WooCommerce==2.1.1
-pycryptodome==3.9.8
\ No newline at end of file
+pycryptodome==3.9.8