Merge branch 'develop' of https://github.com/frappe/erpnext into rebrand-ui
diff --git a/README.md b/README.md
index 80ebdb6..0f6a521 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
 ERPNext as a monolith includes the following areas for managing businesses:
 
 1. [Accounting](https://erpnext.com/open-source-accounting)
-1. [Inventory](https://erpnext.com/distribution/inventory-management-system)
+1. [Warehouse Management](https://erpnext.com/distribution/warehouse-management-system)
 1. [CRM](https://erpnext.com/open-source-crm)
 1. [Sales](https://erpnext.com/open-source-sales-purchase)
 1. [Purchase](https://erpnext.com/open-source-sales-purchase)
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index 19df43f..6cf5624 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -99,7 +99,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Accounting",
- "modified": "2020-06-30 18:33:06.526514",
+ "modified": "2020-09-09 11:45:33.766400",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
@@ -149,14 +149,9 @@
    "type": "Report"
   },
   {
-   "label": "Point of Sale",
-   "link_to": "point-of-sale",
-   "type": "Page"
-  },
-  {
    "label": "Dashboard",
    "link_to": "Accounts",
    "type": "Dashboard"
   }
  ]
-}
+}
\ 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
index 5b6eb9d..27dd8e4 100644
--- 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
@@ -55,7 +55,7 @@
 
 	def populate_payment_entries(self):
 		if self.bank_statement is None: return
-		filename = self.bank_statement.split("/")[-1]
+		file_url = self.bank_statement
 		if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
 			frappe.throw(_("Transactions already retreived from the statement"))
 
@@ -65,7 +65,7 @@
 		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(filename, 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"]))
@@ -398,20 +398,21 @@
 			transaction[header] = ""
 	return transaction
 
-def get_transaction_entries(filename, headers):
+def get_transaction_entries(file_url, headers):
 	header_index = {}
 	rows, transactions = [], []
 
-	if (filename.lower().endswith("xlsx")):
+	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_id=filename)
-	elif (filename.lower().endswith("csv")):
+		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_name": filename})
+		_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 (filename.lower().endswith("xls")):
+	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"))
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index 0fe57c3..2754633 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -91,15 +91,11 @@
 		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)
 
-def add_transactions():
-	if frappe.flags.test_bank_transactions_created:
-		return
-
-	frappe.set_user("Administrator")
+def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
 	try:
 		frappe.get_doc({
 			"doctype": "Bank",
-			"bank_name":"Citi Bank",
+			"bank_name":bank_name,
 		}).insert()
 	except frappe.DuplicateEntryError:
 		pass
@@ -108,12 +104,19 @@
 		frappe.get_doc({
 			"doctype": "Bank Account",
 			"account_name":"Checking Account",
-			"bank": "Citi Bank",
-			"account": "_Test Bank - _TC"
+			"bank": bank_name,
+			"account": account_name
 		}).insert()
 	except frappe.DuplicateEntryError:
 		pass
 
+def add_transactions():
+	if frappe.flags.test_bank_transactions_created:
+		return
+
+	frappe.set_user("Administrator")
+	create_bank_account()
+
 	doc = frappe.get_doc({
 		"doctype": "Bank Transaction",
 		"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json
index 856c371..8915f79 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json
@@ -38,8 +38,8 @@
    "reqd": 1
   }
  ],
- "modified": "2020-06-18 20:27:42.615842",
- "modified_by": "ahmad@havenir.com",
+ "modified": "2020-09-18 17:26:09.703215",
+ "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Item Tax Template",
  "owner": "Administrator",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index a09face..409c15f 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -638,20 +638,12 @@
 		return { filters: filters };
 	},
 
-	reverse_journal_entry: function(frm) {
-		var me = frm.doc;
-		for(var i=0; i<me.accounts.length; i++) {
-			me.accounts[i].credit += me.accounts[i].debit;
-			me.accounts[i].debit = me.accounts[i].credit - me.accounts[i].debit;
-			me.accounts[i].credit -= me.accounts[i].debit;
-			me.accounts[i].credit_in_account_currency = me.accounts[i].credit;
-			me.accounts[i].debit_in_account_currency = me.accounts[i].debit;
-			me.accounts[i].reference_type = "Journal Entry";
-			me.accounts[i].reference_name = me.name
-		}
-		frm.copy_doc();
-		cur_frm.reload_doc();
-	}
+	reverse_journal_entry: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
+			frm: cur_frm
+		})
+	},
 });
 
 $.extend(erpnext.journal_entry, {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index dda1708..34c262e 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -1021,3 +1021,34 @@
 	journal_entry.posting_date = nowdate()
 	journal_entry.inter_company_journal_entry_reference = name
 	return journal_entry.as_dict()
+
+@frappe.whitelist()
+def make_reverse_journal_entry(source_name, target_doc=None):
+	from frappe.model.mapper import get_mapped_doc
+
+	def update_accounts(source, target, source_parent):
+		target.reference_type = "Journal Entry"
+		target.reference_name = source_parent.name
+
+	doclist = get_mapped_doc("Journal Entry", source_name, {
+		"Journal Entry": {
+			"doctype": "Journal Entry",
+			"validation": {
+				"docstatus": ["=", 1]
+			}
+		},
+		"Journal Entry Account": {
+			"doctype": "Journal Entry Account",
+			"field_map": {
+				"account_currency": "account_currency",
+				"exchange_rate": "exchange_rate",
+				"debit_in_account_currency": "credit_in_account_currency",
+				"debit": "credit",
+				"credit_in_account_currency": "debit_in_account_currency",
+				"credit": "debit",
+			},
+			"postprocess": update_accounts,
+		},
+	}, target_doc)
+
+	return doclist
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index 479d4b6..53c0758 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -167,6 +167,49 @@
 
 		self.assertFalse(gle)
 
+	def test_reverse_journal_entry(self):
+		from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry 
+		jv = make_journal_entry("_Test Bank USD - _TC",
+			"Sales - _TC", 100, exchange_rate=50, save=False)
+
+		jv.get("accounts")[1].credit_in_account_currency = 5000
+		jv.get("accounts")[1].exchange_rate = 1
+		jv.submit()
+
+		rjv = make_reverse_journal_entry(jv.name)
+		rjv.posting_date = nowdate()
+		rjv.submit()
+
+
+		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+			debit_in_account_currency, credit_in_account_currency
+			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
+			order by account asc""", rjv.name, as_dict=1)
+
+		self.assertTrue(gl_entries)
+
+
+		expected_values = {
+			"_Test Bank USD - _TC": {
+				"account_currency": "USD",
+				"debit": 0,
+				"debit_in_account_currency": 0,
+				"credit": 5000,
+				"credit_in_account_currency": 100,
+			},
+			"Sales - _TC": {
+				"account_currency": "INR",
+				"debit": 5000,
+				"debit_in_account_currency": 5000,
+				"credit": 0,
+				"credit_in_account_currency": 0,
+			}
+		}
+
+		for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+			for i, gle in enumerate(gl_entries):
+				self.assertEqual(expected_values[gle.account][field], gle[field])
+
 	def test_disallow_change_in_account_currency_for_a_party(self):
 		# create jv in USD
 		jv = make_journal_entry("_Test Bank USD - _TC",
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
index f3df1f0..50fc3bb 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
@@ -45,11 +45,11 @@
  ],
  "icon": "fa fa-credit-card",
  "idx": 1,
- "modified": "2019-08-14 14:58:42.079115",
- "modified_by": "sammish.thundiyil@gmail.com",
+ "modified": "2020-09-18 17:26:09.703215",
+ "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Mode of Payment",
- "owner": "harshada@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "create": 1,
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 9fc44bc..e117471 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -12,9 +12,10 @@
 
 	setup: function(frm) {
 		frm.set_query("paid_from", function() {
+			frm.events.validate_company(frm);
+
 			var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
 				["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
-
 			return {
 				filters: {
 					"account_type": ["in", account_types],
@@ -23,13 +24,16 @@
 				}
 			}
 		});
+
 		frm.set_query("party_type", function() {
+			frm.events.validate_company(frm);
 			return{
 				filters: {
 					"name": ["in", Object.keys(frappe.boot.party_account_types)],
 				}
 			}
 		});
+
 		frm.set_query("party_bank_account", function() {
 			return {
 				filters: {
@@ -39,6 +43,7 @@
 				}
 			}
 		});
+
 		frm.set_query("bank_account", function() {
 			return {
 				filters: {
@@ -47,6 +52,7 @@
 				}
 			}
 		});
+
 		frm.set_query("contact_person", function() {
 			if (frm.doc.party) {
 				return {
@@ -58,10 +64,12 @@
 				};
 			}
 		});
+
 		frm.set_query("paid_to", function() {
+			frm.events.validate_company(frm);
+
 			var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
 				["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
-
 			return {
 				filters: {
 					"account_type": ["in", account_types],
@@ -150,6 +158,12 @@
 		frm.events.show_general_ledger(frm);
 	},
 
+	validate_company: (frm) => {
+		if (!frm.doc.company){
+			frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")});
+		}
+	},
+
 	company: function(frm) {
 		frm.events.hide_unhide_fields(frm);
 		frm.events.set_dynamic_labels(frm);
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 9979377..72149a6 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2016-06-01 14:38:51.012597",
@@ -63,6 +64,7 @@
   "cost_center",
   "section_break_12",
   "status",
+  "custom_remarks",
   "remarks",
   "column_break_16",
   "letter_head",
@@ -462,7 +464,8 @@
    "fieldname": "remarks",
    "fieldtype": "Small Text",
    "label": "Remarks",
-   "no_copy": 1
+   "no_copy": 1,
+   "read_only_depends_on": "eval:doc.custom_remarks == 0"
   },
   {
    "fieldname": "column_break_16",
@@ -573,10 +576,18 @@
    "label": "Status",
    "options": "\nDraft\nSubmitted\nCancelled",
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "custom_remarks",
+   "fieldtype": "Check",
+   "label": "Custom Remarks"
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
- "modified": "2019-12-08 13:02:30.016610",
+ "links": [],
+ "modified": "2020-09-02 13:39:43.383705",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 842c64f..11ab020 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -453,7 +453,7 @@
 				frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
 
 	def set_remarks(self):
-		if self.remarks: return
+		if self.custom_remarks: return
 
 		if self.payment_type=="Internal Transfer":
 			remarks = [_("Amount {0} {1} transferred from {2} to {3}")
@@ -1172,30 +1172,23 @@
 	from frappe.model.mapper import get_mapped_doc
 	def set_missing_values(source, target):
 		target.payment_order_type = "Payment Entry"
+		target.append('references', dict(
+			reference_doctype="Payment Entry",
+			reference_name=source.name,
+			bank_account=source.party_bank_account,
+			amount=source.paid_amount,
+			account=source.paid_to,
+			supplier=source.party,
+			mode_of_payment=source.mode_of_payment,
+		))
 
-	def update_item(source_doc, target_doc, source_parent):
-		target_doc.bank_account = source_parent.party_bank_account
-		target_doc.amount = source_doc.allocated_amount
-		target_doc.account = source_parent.paid_to
-		target_doc.payment_entry = source_parent.name
-		target_doc.supplier = source_parent.party
-		target_doc.mode_of_payment = source_parent.mode_of_payment
-
-
-	doclist = get_mapped_doc("Payment Entry", source_name,	{
+	doclist = get_mapped_doc("Payment Entry", source_name, {
 		"Payment Entry": {
 			"doctype": "Payment Order",
 			"validation": {
 				"docstatus": ["=", 1]
-			}
-		},
-		"Payment Entry Reference": {
-			"doctype": "Payment Order Reference",
-			"validation": {
-				"docstatus": ["=", 1]
 			},
-			"postprocess": update_item
-		},
+		}
 
 	}, target_doc, set_missing_values)
 
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py
index e5880aa..8d29ae7 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/payment_order.py
@@ -21,10 +21,15 @@
 		if cancel:
 			status = 'Initiated'
 
-		ref_field = "status" if self.payment_order_type == "Payment Request" else "payment_order_status"
+		if self.payment_order_type == "Payment Request":
+			ref_field = "status"
+			ref_doc_field = frappe.scrub(self.payment_order_type)
+		else:
+			ref_field = "payment_order_status"
+			ref_doc_field = "reference_name"
 
 		for d in self.references:
-			frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status)
+			frappe.db.set_value(self.payment_order_type, d.get(ref_doc_field), ref_field, status)
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py
index 711c4cc..1c23e2a 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -5,6 +5,45 @@
 
 import frappe
 import unittest
+from frappe.utils import getdate
+from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, make_payment_order
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 
 class TestPaymentOrder(unittest.TestCase):
-	pass
+	def setUp(self):
+		create_bank_account()
+
+	def tearDown(self):
+		for bt in frappe.get_all("Payment Order"):
+			doc = frappe.get_doc("Payment Order", bt.name)
+			doc.cancel()
+			doc.delete()
+
+	def test_payment_order_creation_against_payment_entry(self):
+		purchase_invoice = make_purchase_invoice()
+		payment_entry = get_payment_entry("Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC")
+		payment_entry.reference_no = "_Test_Payment_Order"
+		payment_entry.reference_date = getdate()
+		payment_entry.party_bank_account = "Checking Account - Citi Bank"
+		payment_entry.insert()
+		payment_entry.submit()
+
+		doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
+		reference_doc = doc.get("references")[0]
+		self.assertEquals(reference_doc.reference_name, payment_entry.name)
+		self.assertEquals(reference_doc.reference_doctype, "Payment Entry")
+		self.assertEquals(reference_doc.supplier, "_Test Supplier")
+		self.assertEquals(reference_doc.amount, 250)
+
+def create_payment_order_against_payment_entry(ref_doc, order_type):
+	payment_order = frappe.get_doc(dict(
+		doctype="Payment Order",
+		company="_Test Company",
+		payment_order_type=order_type,
+		company_bank_account="Checking Account - Citi Bank"
+	))
+	doc = make_payment_order(ref_doc.name, payment_order)
+	doc.save()
+	doc.submit()
+	return doc
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.json b/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.json
index db0b761..d94ba74 100644
--- a/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.json
+++ b/erpnext/accounts/doctype/payment_order_reference/payment_order_reference.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2018-07-20 16:38:06.630813",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -10,7 +11,6 @@
   "column_break_4",
   "supplier",
   "payment_request",
-  "payment_entry",
   "mode_of_payment",
   "bank_account_details",
   "bank_account",
@@ -103,17 +103,12 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
-  },
-  {
-   "fieldname": "payment_entry",
-   "fieldtype": "Link",
-   "label": "Payment Entry",
-   "options": "Payment Entry",
-   "read_only": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-05-08 13:56:25.724557",
+ "links": [],
+ "modified": "2020-09-04 08:29:51.014390",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Order Reference",
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
index d04f25b..47546c0 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
@@ -291,11 +291,11 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-08-21 16:15:49.089450", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Period Closing Voucher", 
- "owner": "jai@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 1, 
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 8dcd2e4..9336fc3 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -55,14 +55,48 @@
 			},
 			callback: (r) => {
 				let pos_docs = r.message;
-				set_form_data(pos_docs, frm)
-				refresh_fields(frm)
-				set_html_data(frm)
+				set_form_data(pos_docs, frm);
+				refresh_fields(frm);
+				set_html_data(frm);
 			}
 		})
 	}
 });
 
+cur_frm.cscript.before_pos_transactions_remove = function(doc, cdt, cdn) {
+	const removed_row = locals[cdt][cdn];
+
+	if (!removed_row.pos_invoice) return;
+
+	frappe.db.get_doc("POS Invoice", removed_row.pos_invoice).then(doc => {
+		cur_frm.doc.grand_total -= flt(doc.grand_total);
+		cur_frm.doc.net_total -= flt(doc.net_total);
+		cur_frm.doc.total_quantity -= flt(doc.total_qty);
+		refresh_payments(doc, cur_frm, 1);
+		refresh_taxes(doc, cur_frm, 1);
+		refresh_fields(cur_frm);
+		set_html_data(cur_frm);
+	});
+}
+
+frappe.ui.form.on('POS Invoice Reference', {
+	pos_invoice(frm, cdt, cdn) {
+		const added_row = locals[cdt][cdn];
+
+		if (!added_row.pos_invoice) return;
+
+		frappe.db.get_doc("POS Invoice", added_row.pos_invoice).then(doc => {
+			frm.doc.grand_total += flt(doc.grand_total);
+			frm.doc.net_total += flt(doc.net_total);
+			frm.doc.total_quantity += flt(doc.total_qty);
+			refresh_payments(doc, frm);
+			refresh_taxes(doc, frm);
+			refresh_fields(frm);
+			set_html_data(frm);
+		});
+	}
+})
+
 frappe.ui.form.on('POS Closing Entry Detail', {
 	closing_amount: (frm, cdt, cdn) => {
 		const row = locals[cdt][cdn];
@@ -76,8 +110,8 @@
 		frm.doc.grand_total += flt(d.grand_total);
 		frm.doc.net_total += flt(d.net_total);
 		frm.doc.total_quantity += flt(d.total_qty);
-		add_to_payments(d, frm);
-		add_to_taxes(d, frm);
+		refresh_payments(d, frm);
+		refresh_taxes(d, frm);
 	});
 }
 
@@ -90,11 +124,12 @@
 	})
 }
 
-function add_to_payments(d, frm) {
+function refresh_payments(d, frm, remove) {
 	d.payments.forEach(p => {
 		const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
 		if (payment) {
-			payment.expected_amount += flt(p.amount);
+			if (!remove) payment.expected_amount += flt(p.amount);
+			else payment.expected_amount -= flt(p.amount);
 		} else {
 			frm.add_child("payment_reconciliation", {
 				mode_of_payment: p.mode_of_payment,
@@ -105,11 +140,12 @@
 	})
 }
 
-function add_to_taxes(d, frm) {
+function refresh_taxes(d, frm, remove) {
 	d.taxes.forEach(t => {
 		const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
 		if (tax) {
-			tax.amount += flt(t.tax_amount); 
+			if (!remove) tax.amount += flt(t.tax_amount);
+			else tax.amount -= flt(t.tax_amount);
 		} else {
 			frm.add_child("taxes", {
 				account_head: t.account_head,
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 2a2e3df..4780688 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -279,7 +279,8 @@
    "fieldtype": "Check",
    "label": "Is Return (Credit Note)",
    "no_copy": 1,
-   "print_hide": 1
+   "print_hide": 1,
+   "set_only_once": 1
   },
   {
    "fieldname": "column_break1",
@@ -1578,9 +1579,10 @@
   }
  ],
  "icon": "fa fa-file-text",
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-29 15:08:39.337385",
+ "modified": "2020-09-07 12:43:09.138720",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 9c62a87..514a2ac 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -182,8 +182,9 @@
 	def test_pos_returns_with_repayment(self):
 		pos = create_pos_invoice(qty = 10, do_not_save=True)
 
+		pos.set('payments', [])
 		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
 		pos.insert()
 		pos.submit()
 
@@ -200,8 +201,9 @@
 			income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
 			cost_center = "Main - _TC", do_not_save=True)
 
+		pos.set('payments', [])
 		pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
-		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60})
+		pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
 
 		pos.insert()
 		pos.submit()
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 00dbad5..11b9d25 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
@@ -24,11 +24,20 @@
 
 	def validate_pos_invoice_status(self):
 		for d in self.pos_invoices:
-			status, docstatus = frappe.db.get_value('POS Invoice', d.pos_invoice, ['status', 'docstatus'])
+			status, docstatus, is_return, return_against = frappe.db.get_value(
+				'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
+
 			if docstatus != 1:
 				frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
-			if status in ['Consolidated']:
+			if status == "Consolidated":
 				frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
+			if is_return and return_against not in [d.pos_invoice for d in self.pos_invoices] and status != "Consolidated":
+				# if return entry is not getting merged in the current pos closing and if it is not consolidated
+				frappe.throw(
+					_("Row #{}: Return Invoice {} cannot be made against unconsolidated invoice. \
+					You can add original invoice {} manually to proceed.")
+					.format(d.idx, frappe.bold(d.pos_invoice), frappe.bold(return_against))
+				)
 
 	def on_submit(self):
 		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
@@ -36,12 +45,12 @@
 		returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
 		sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
 
-		sales_invoice = self.process_merging_into_sales_invoice(sales)
+		sales_invoice, credit_note = "", ""
+		if sales:
+			sales_invoice = self.process_merging_into_sales_invoice(sales)
 		
-		if len(returns):
+		if returns:
 			credit_note = self.process_merging_into_credit_note(returns)
-		else:
-			credit_note = ""
 
 		self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
 
diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
index 15f23b6..b9e07b8 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -5,13 +5,14 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import cint
+from frappe.utils import cint, get_link_to_form
 from frappe.model.document import Document
 from erpnext.controllers.status_updater import StatusUpdater
 
 class POSOpeningEntry(StatusUpdater):
 	def validate(self):
 		self.validate_pos_profile_and_cashier()
+		self.validate_payment_method_account()
 		self.set_status()
 
 	def validate_pos_profile_and_cashier(self):
@@ -20,6 +21,14 @@
 
 		if not cint(frappe.db.get_value("User", self.user, "enabled")):
 			frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
+	
+	def validate_payment_method_account(self):
+		for d in self.balance_details:
+			account = frappe.db.get_value("Mode of Payment Account", 
+				{"parent": d.mode_of_payment, "company": self.company}, "default_account")
+			if not account:
+				frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
+					.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
 
 	def on_submit(self):
 		self.set_status(update=True)
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 789b4c3..1386b70 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import msgprint, _
-from frappe.utils import cint, now
+from frappe.utils import cint, now, get_link_to_form
 from six import iteritems
 from frappe.model.document import Document
 
@@ -13,7 +13,7 @@
 		self.validate_default_profile()
 		self.validate_all_link_fields()
 		self.validate_duplicate_groups()
-		self.check_default_payment()
+		self.validate_payment_methods()
 
 	def validate_default_profile(self):
 		for row in self.applicable_for_users:
@@ -52,14 +52,23 @@
 		if len(customer_groups) != len(set(customer_groups)):
 			frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
 
-	def check_default_payment(self):
-		if self.payments:
-			default_mode_of_payment = [d.default for d in self.payments if d.default]
-			if not default_mode_of_payment:
-				frappe.throw(_("Set default mode of payment"))
+	def validate_payment_methods(self):
+		if not self.payments:
+			frappe.throw(_("Payment methods are mandatory. Please add at least one payment method."))
 
-			if len(default_mode_of_payment) > 1:
-				frappe.throw(_("Multiple default mode of payment is not allowed"))
+		default_mode_of_payment = [d.default for d in self.payments if d.default]
+		if not default_mode_of_payment:
+			frappe.throw(_("Please select a default mode of payment"))
+
+		if len(default_mode_of_payment) > 1:
+			frappe.throw(_("You can only select one mode of payment as default"))
+		
+		for d in self.payments:
+			account = frappe.db.get_value("Mode of Payment Account", 
+				{"parent": d.mode_of_payment, "company": self.company}, "default_account")
+			if not account:
+				frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
+					.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
 
 	def on_update(self):
 		self.set_defaults()
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js
index 504941d..05cb7f0 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.js
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js
@@ -7,10 +7,10 @@
 	},
 
 	get_invoice_fields: function(frm) {
-		frappe.model.with_doctype("Sales Invoice", () => {
-			var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
+		frappe.model.with_doctype("POS Invoice", () => {
+			var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
 				if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
-					d.fieldtype === 'Table') {
+					['Table', 'Button'].includes(d.fieldtype)) {
 					return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
 				} else {
 					return null;
@@ -25,7 +25,7 @@
 frappe.ui.form.on("POS Field", {
 	fieldname: function(frm, doctype, name) {
 		var doc = frappe.get_doc(doctype, name);
-		var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
+		var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
 			return doc.fieldname == d.fieldname ? d : null;
 		})[0];
 
diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js
index 2800c19..1ec6805 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js
@@ -10,13 +10,15 @@
 				}
 			};
 		});
+	},
 
-		if (frm.doc.company) {
+	type: function(frm) {
+		if (frm.doc.company && frm.doc.type) {
 			frm.set_query("account", function() {
 				return {
 					filters: {
 						'company': frm.doc.company,
-						'root_type': 'Liability',
+						'root_type': frm.doc.type === 'Income' ? 'Liability' : 'Asset',
 						'is_group': 0
 					}
 				};
diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json
index 4daafef..457e98c 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.json
@@ -60,6 +60,7 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval: doc.type",
    "fieldname": "account",
    "fieldtype": "Link",
    "label": "Account",
@@ -73,9 +74,10 @@
    "reqd": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-02-06 18:18:09.852844",
+ "modified": "2020-09-03 18:07:02.463754",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Process Deferred Accounting",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 4f6be59..fe5301d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -25,6 +25,12 @@
 				this.frm.set_df_property("credit_to", "print_hide", 0);
 			}
 		}
+
+		// Trigger supplier event on load if supplier is available
+		// The reason for this is PI can be created from PR or PO and supplier is pre populated
+		if (this.frm.doc.supplier && this.frm.doc.__islocal) {
+			this.frm.trigger('supplier');
+		}
 	},
 
 	refresh: function(doc) {
@@ -135,6 +141,8 @@
 				}
 			});
 		}
+
+		this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
 	},
 
 	unblock_invoice: function() {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 7b1062f..079f599 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -132,6 +132,11 @@
 		if not self.due_date:
 			self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company,  self.bill_date)
 
+		tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
+		if tds_category and not for_validate:
+			self.apply_tds = 1
+			self.tax_withholding_category = tds_category
+
 		super(PurchaseInvoice, self).set_missing_values(for_validate)
 
 	def check_conversion_rate(self):
@@ -400,8 +405,6 @@
 		update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
 
 	def make_gl_entries(self, gl_entries=None):
-		if not self.grand_total:
-			return
 		if not gl_entries:
 			gl_entries = self.get_gl_entries()
 
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
index 0e748f8..f9fdc4b 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
@@ -210,7 +210,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-03-12 14:53:47.679439",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Taxes and Charges",
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
index a18fec6..b46d2e3 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json
@@ -78,7 +78,7 @@
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Taxes and Charges Template",
- "owner": "wasim@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "email": 1,
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 31613e5..2397b7d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -447,7 +447,7 @@
   {
    "allow_on_submit": 1,
    "fieldname": "po_no",
-   "fieldtype": "Small Text",
+   "fieldtype": "Data",
    "hide_days": 1,
    "hide_seconds": 1,
    "label": "Customer's Purchase Order",
@@ -1946,7 +1946,7 @@
  "idx": 181,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-08-03 23:31:12.675040",
+ "modified": "2020-08-27 01:56:28.532140",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index cd39438..4f278af 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 import frappe.defaults
-from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
+from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form
 from frappe import _, msgprint, throw
 from erpnext.accounts.party import get_party_account, get_due_date
 from frappe.model.mapper import get_mapped_doc
@@ -1372,7 +1372,7 @@
 		{"parent": mode_of_payment, "company": company}, "default_account")
 	if not account:
 		frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
-			.format(mode_of_payment))
+			.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
 	return {
 		"account": account
 	}
@@ -1612,18 +1612,16 @@
 		payment.type = payment_mode.type
 
 	doc.set('payments', [])
-	if not pos_profile or not pos_profile.get('payments'):
-		for payment_mode in get_all_mode_of_payments(doc):
-			append_payment(payment_mode)
-		return
-
 	for pos_payment_method in pos_profile.get('payments'):
 		pos_payment_method = pos_payment_method.as_dict()
 
 		payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
-		if payment_mode:
-			payment_mode[0].default = pos_payment_method.default
-			append_payment(payment_mode[0])
+		if not payment_mode:
+			frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
+				.format(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)), title=_("Missing Account"))
+
+		payment_mode[0].default = pos_payment_method.default
+		append_payment(payment_mode[0])
 
 def get_all_mode_of_payments(doc):
 	return frappe.db.sql("""
diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js
index 53ee08a..d0904ee 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js
@@ -3,6 +3,22 @@
 
 frappe.ui.form.on('Shipping Rule', {
 	refresh: function(frm) {
+		frm.set_query("cost_center", function() {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			}
+		})
+
+		frm.set_query("account", function() {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			}
+		})
+
 		frm.trigger('toggle_reqd');
 	},
 	calculate_based_on: function(frm) {
@@ -12,4 +28,4 @@
 		frm.toggle_reqd("shipping_amount", frm.doc.calculate_based_on === 'Fixed');
 		frm.toggle_reqd("conditions", frm.doc.calculate_based_on !== 'Fixed');
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/doctype/tax_category/tax_category.json b/erpnext/accounts/doctype/tax_category/tax_category.json
index 1e3ae45..6f682a0 100644
--- a/erpnext/accounts/doctype/tax_category/tax_category.json
+++ b/erpnext/accounts/doctype/tax_category/tax_category.json
@@ -1,134 +1,66 @@
 {
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
+ "actions": [],
+ "allow_rename": 1,
  "autoname": "field:title",
- "beta": 0,
  "creation": "2018-11-22 23:38:39.668804",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
- "document_type": "",
  "editable_grid": 1,
  "engine": "InnoDB",
+ "field_order": [
+  "title"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "title",
    "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": "Title",
-   "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": 1
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2020-01-15 17:14:28.951793", 
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-08-30 19:41:25.783852",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Tax Category",
- "name_case": "",
  "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "System Manager",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "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 Manager",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "write": 1
   },
   {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
    "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": 0,
-   "write": 0
+   "share": 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,
- "track_views": 0
-}
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index cf3deb8..c12e006d 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -45,8 +45,8 @@
 			}, as_dict=1)
 
 	if accounting_periods:
-		frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
-			.format(accounting_periods[0].name), ClosedAccountingPeriod)
+		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):
 	if merge_entries:
@@ -301,8 +301,9 @@
 			})
 
 	if gl_entries:
-		set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
+		validate_accounting_period(gl_entries)
 		check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
+		set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
 
 		for entry in gl_entries:
 			entry['name'] = None
@@ -319,7 +320,6 @@
 
 			entry['remarks'] = "On cancellation of " + entry['voucher_no']
 			entry['is_cancelled'] = 1
-			entry['posting_date'] = today()
 
 			if entry['debit'] or entry['credit']:
 				make_entry(entry, adv_adj, "Yes")
@@ -342,7 +342,7 @@
 	"""
 		Set is_cancelled=1 in all original gl entries for the voucher
 	"""
-	frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
+	frappe.db.sql("""UPDATE `tabGL Entry` SET is_cancelled = 1,
 		modified=%s, modified_by=%s
 		where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
 		(now(), frappe.session.user, voucher_type, voucher_no))
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
index efc76f9..9703527 100644
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
@@ -72,7 +72,7 @@
 	check_plaid_status() {
 		const me = this;
 		frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
-			if (r && r.enabled == "1") {
+			if (r && r.enabled === "1") {
 				me.plaid_status = "active"
 			} else {
 				me.plaid_status = "inactive"
@@ -139,7 +139,7 @@
 	}
 
 	make() {
-		const me = this;	
+		const me = this;
 		new frappe.ui.FileUploader({
 			method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
 			allow_multiple: 0,
@@ -214,31 +214,35 @@
 
 	init_config() {
 		const me = this;
-		frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
-		.then(result => {
-			me.plaid_env = result.plaid_env;
-			me.plaid_public_key = result.plaid_public_key;
-			me.client_name = result.client_name;
-			me.sync_transactions()
-		})
+		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", (v) => {
+		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: v['bank'],
+				bank: r.bank,
 				bank_account: me.parent.bank_account,
 				freeze: true
 			})
 			.then((result) => {
-				let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized")
+				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>`
+				<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'});
+				frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
 			})
 		})
 	}
@@ -384,7 +388,7 @@
 		})
 
 		frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
-			{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")}
+			{ bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
 		).then((result) => {
 			me.make_dialog(result)
 		})
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index c999eb9..29c4f7d 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -34,7 +34,7 @@
 					filters: {
 						'company': company
 					}
-				}
+				};
 			}
 		},
 		{
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 59117c8..044fc1d 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -617,9 +617,19 @@
 		elif party_type_field=="supplier":
 			self.add_supplier_filters(conditions, values)
 
+		if self.filters.cost_center:
+			self.get_cost_center_conditions(conditions)
+
 		self.add_accounting_dimensions_filters(conditions, values)
 		return " and ".join(conditions), values
 
+	def get_cost_center_conditions(self, conditions):
+		lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
+		cost_center_list = [center.name for center in frappe.get_list("Cost Center", filters = {'lft': (">=", lft), 'rgt': ("<=", rgt)})]
+
+		cost_center_string = '", "'.join(cost_center_list)
+		conditions.append('cost_center in ("{0}")'.format(cost_center_string))
+
 	def get_order_by_condition(self):
 		if self.filters.get('group_by_party'):
 			return "order by party, posting_date"
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
index 3ec4d30..f547ca6 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
@@ -71,7 +71,22 @@
 			fieldtype: "Check",
 			default: 0,
 		},
-	]
+	],
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+
+		if (column.fieldname.includes('variance')) {
+
+			if (data[column.fieldname] < 0) {
+				value = "<span style='color:red'>" + value + "</span>";
+			}
+			else if (data[column.fieldname] > 0) {
+				value = "<span style='color:green'>" + value + "</span>";
+			}
+		}
+
+		return value;
+	}
 }
 
 erpnext.dimension_filters.forEach((dimension) => {
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 219871b..d011689 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -256,7 +256,7 @@
 	"""accumulate children's values in parent accounts"""
 	for d in reversed(accounts):
 		if d.parent_account:
-			account = d.parent_account.split('-')[0].strip()
+			account = d.parent_account.split(' - ')[0].strip()
 			if not accounts_by_name.get(account):
 				continue
 
diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
index 2cb10b1..10b32fe 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
@@ -173,7 +173,7 @@
 			from `tabGL Entry` gle
 			{join}
 			where
-				gle.docstatus < 2 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
+				gle.docstatus < 2 and gle.is_cancelled = 0 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
 				and gle.posting_date <= %(to_date)s {conditions}
 			order by gle.posting_date
 		""".format(join=join, join_field=join_field, conditions=conditions), self.filters, as_dict=True)
@@ -248,7 +248,7 @@
 			from
 				`tabGL Entry`
 			where
-				docstatus < 2
+				docstatus < 2 and is_cancelled = 0
 				and (voucher_type, voucher_no) in (
 					select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
 					where acc.name = gle.account and acc.account_type = '{income_or_expense}'
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 0599707..f735d87 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -43,8 +43,11 @@
 
 
 def validate_filters(filters, account_details):
-	if not filters.get('company'):
-		frappe.throw(_('{0} is mandatory').format(_('Company')))
+	if not filters.get("company"):
+		frappe.throw(_("{0} is mandatory").format(_("Company")))
+
+	if not filters.get("from_date") and not filters.get("to_date"):
+		frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
 
 	if filters.get("account") and not account_details.get(filters.account):
 		frappe.throw(_("Account {0} does not exists").format(filters.account))
@@ -141,7 +144,7 @@
 	if filters and filters.get('cost_center'):
 		select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
 		credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """
-		
+
 		distributed_cost_center_query = """
 		UNION ALL
 		SELECT name as gl_entry,
@@ -155,7 +158,7 @@
 			against_voucher_type,
 			against_voucher,
 			account_currency,
-			remarks, against, 
+			remarks, against,
 			is_opening, `tabGL Entry`.creation {select_fields_with_percentage}
 		FROM `tabGL Entry`,
 		(
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.json b/erpnext/accounts/report/gross_profit/gross_profit.json
index 9cfb062..cd6bac2 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.json
+++ b/erpnext/accounts/report/gross_profit/gross_profit.json
@@ -1,24 +1,23 @@
 {
- "add_total_row": 0, 
- "apply_user_permissions": 1, 
- "creation": "2013-02-25 17:03:34", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 3, 
- "is_standard": "Yes", 
- "modified": "2017-02-24 20:12:22.464240", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Gross Profit", 
- "owner": "Administrator", 
- "ref_doctype": "Sales Invoice", 
- "report_name": "Gross Profit", 
- "report_type": "Script Report", 
+ "add_total_row": 1,
+ "creation": "2013-02-25 17:03:34",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2020-08-13 11:26:39.112352",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Gross Profit",
+ "owner": "Administrator",
+ "ref_doctype": "Sales Invoice",
+ "report_name": "Gross Profit",
+ "report_type": "Script Report",
  "roles": [
   {
    "role": "Accounts Manager"
-  }, 
+  },
   {
    "role": "Accounts User"
   }
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 2563b66..84c7454 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -268,9 +268,9 @@
 	def get_last_purchase_rate(self, item_code, row):
 		condition = ''
 		if row.project:
-			condition += " AND a.project='%s'" % (row.project)
+			condition += " AND a.project=%s" % (frappe.db.escape(row.project))
 		elif row.cost_center:
-			condition += " AND a.cost_center='%s'" % (row.cost_center)
+			condition += " AND a.cost_center=%s" % (frappe.db.escape(row.cost_center))
 		if self.filters.to_date:
 			condition += " AND modified='%s'" % (self.filters.to_date)
 
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index fba20c0..7ad164a 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -252,13 +252,6 @@
 		})
 	},
 
-	available_for_use_date: function(frm) {
-		$.each(frm.doc.finance_books || [], function(i, d) {
-			if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
-		});
-		refresh_field("finance_books");
-	},
-
 	is_existing_asset: function(frm) {
 		frm.trigger("toggle_reference_doc");
 		// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
@@ -438,6 +431,15 @@
 		}
 
 		frappe.flags.dont_change_rate = false;
+	},
+
+	depreciation_start_date: function(frm, cdt, cdn) {
+		const book = locals[cdt][cdn];
+		if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) {
+			frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`));
+			book.depreciation_start_date = "";
+			frm.refresh_field("finance_books");
+		}
 	}
 });
 
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 0bd03a8..efdbdb1 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -6,7 +6,7 @@
 import frappe, erpnext, math, json
 from frappe import _
 from six import string_types
-from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days
+from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days, get_last_day, get_datetime
 from frappe.model.document import Document
 from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
 from erpnext.assets.doctype.asset.depreciation \
@@ -83,6 +83,11 @@
 		if not self.available_for_use_date:
 			frappe.throw(_("Available for use date is required"))
 
+		for d in self.finance_books:
+			if d.depreciation_start_date == self.available_for_use_date:
+				frappe.throw(_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(d.idx),
+					title=_("Incorrect Date"))
+
 	def set_missing_values(self):
 		if not self.asset_category:
 			self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
@@ -135,6 +140,10 @@
 	def make_asset_movement(self):
 		reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
 		reference_docname = self.purchase_receipt or self.purchase_invoice
+		transaction_date = getdate(self.purchase_date)
+		if reference_docname:
+			posting_date, posting_time = frappe.db.get_value(reference_doctype, reference_docname, ["posting_date", "posting_time"])
+			transaction_date = get_datetime("{} {}".format(posting_date, posting_time))
 		assets = [{
 			'asset': self.name,
 			'asset_name': self.asset_name,
@@ -146,7 +155,7 @@
 			'assets': assets,
 			'purpose': 'Receipt',
 			'company': self.company,
-			'transaction_date': getdate(nowdate()),
+			'transaction_date': transaction_date,
 			'reference_doctype': reference_doctype,
 			'reference_name': reference_docname
 		}).insert()
@@ -294,7 +303,7 @@
 		if not row.depreciation_start_date:
 			if not self.available_for_use_date:
 				frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
-			row.depreciation_start_date = self.available_for_use_date
+			row.depreciation_start_date = get_last_day(self.available_for_use_date)
 
 		if not self.is_existing_asset:
 			self.opening_accumulated_depreciation = 0
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index aed78e7..52039c1 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -371,19 +371,18 @@
 		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
 		asset = frappe.get_doc('Asset', asset_name)
 		asset.calculate_depreciation = 1
-		asset.available_for_use_date = nowdate()
-		asset.purchase_date = nowdate()
+		asset.available_for_use_date = '2020-01-01'
+		asset.purchase_date = '2020-01-01'
 		asset.append("finance_books", {
 			"expected_value_after_useful_life": 10000,
 			"depreciation_method": "Straight Line",
-			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": nowdate()
+			"total_number_of_depreciations": 10,
+			"frequency_of_depreciation": 1
 		})
 		asset.insert()
 		asset.submit()
 
-		post_depreciation_entries(date=add_months(nowdate(), 10))
+		post_depreciation_entries(date=add_months('2020-01-01', 4))
 
 		scrap_asset(asset.name)
 
@@ -392,9 +391,9 @@
 		self.assertTrue(asset.journal_entry_for_scrap)
 
 		expected_gle = (
-			("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
+			("_Test Accumulated Depreciations - _TC", 36000.0, 0.0),
 			("_Test Fixed Asset - _TC", 0.0, 100000.0),
-			("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0)
+			("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0)
 		)
 
 		gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
@@ -466,8 +465,7 @@
 			"expected_value_after_useful_life": 10000,
 			"depreciation_method": "Straight Line",
 			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": "2020-06-06"
+			"frequency_of_depreciation": 10
 		})
 		asset.insert()
 		accumulated_depreciation_after_full_schedule = \
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index c80f95e..79fcb95 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -1,347 +1,99 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-05-08 14:44:37.095570", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2018-05-08 14:44:37.095570",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "finance_book",
+  "depreciation_method",
+  "total_number_of_depreciations",
+  "column_break_5",
+  "frequency_of_depreciation",
+  "depreciation_start_date",
+  "expected_value_after_useful_life",
+  "value_after_depreciation",
+  "rate_of_depreciation"
+ ],
  "fields": [
   {
-   "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": "finance_book", 
-   "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": "Finance Book", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Finance Book", 
-   "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": "finance_book",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Finance Book",
+   "options": "Finance Book"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_if_empty": 0, 
-   "fieldname": "depreciation_method", 
-   "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": "Depreciation Method", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "depreciation_method",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Depreciation Method",
+   "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual",
+   "reqd": 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": "total_number_of_depreciations", 
-   "fieldtype": "Int", 
-   "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": "Total Number of Depreciations", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "total_number_of_depreciations",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Total Number of Depreciations",
+   "reqd": 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_5", 
-   "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, 
-   "label": "", 
-   "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_5",
+   "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": "frequency_of_depreciation", 
-   "fieldtype": "Int", 
-   "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": "Frequency of Depreciation (Months)", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "frequency_of_depreciation",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Frequency of Depreciation (Months)",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.doctype == 'Asset'", 
-   "fetch_if_empty": 0, 
-   "fieldname": "depreciation_start_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": "Depreciation Start Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.doctype == 'Asset'",
+   "fieldname": "depreciation_start_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Depreciation Posting Date",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "depends_on": "eval:parent.doctype == 'Asset'", 
-   "fetch_if_empty": 0, 
-   "fieldname": "expected_value_after_useful_life", 
-   "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": "Expected Value After Useful Life", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_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
-  }, 
+   "default": "0",
+   "depends_on": "eval:parent.doctype == 'Asset'",
+   "fieldname": "expected_value_after_useful_life",
+   "fieldtype": "Currency",
+   "label": "Expected Value After Useful Life",
+   "options": "Company:company:default_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": "value_after_depreciation", 
-   "fieldtype": "Currency", 
-   "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": "Value After Depreciation", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Company:company:default_currency", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "value_after_depreciation",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Value After Depreciation",
+   "no_copy": 1,
+   "options": "Company:company:default_currency",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.depreciation_method == 'Written Down Value'", 
-   "description": "In Percentage", 
-   "fetch_if_empty": 0, 
-   "fieldname": "rate_of_depreciation", 
-   "fieldtype": "Percent", 
-   "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": "Rate of Depreciation", 
-   "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
+   "depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
+   "description": "In Percentage",
+   "fieldname": "rate_of_depreciation",
+   "fieldtype": "Percent",
+   "label": "Rate of Depreciation"
   }
- ], 
- "has_web_view": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-04-09 19:45:14.523488", 
- "modified_by": "Administrator", 
- "module": "Assets", 
- "name": "Asset Finance Book", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 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": "2020-09-16 12:11:30.631788",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Finance Book",
+ "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/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
index c3755a3..cddee5f 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -32,8 +32,7 @@
 			"next_depreciation_date": "2020-12-31",
 			"depreciation_method": "Straight Line",
 			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": "2020-06-06"
+			"frequency_of_depreciation": 10
 		})
 
 		if asset.docstatus == 0:
@@ -82,8 +81,7 @@
 			"next_depreciation_date": "2020-12-31",
 			"depreciation_method": "Straight Line",
 			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 10,
-			"depreciation_start_date": "2020-06-06"
+			"frequency_of_depreciation": 10
 		})
 		if asset.docstatus == 0:
 			asset.submit()
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 4201e0b..d1063b1 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1084,7 +1084,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-07-31 14:13:44.610190",
+ "modified": "2020-09-14 14:36:12.418690",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
@@ -1135,5 +1135,6 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "timeline_field": "supplier",
- "title_field": "supplier"
+ "title_field": "supplier",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 813286f..158799c 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -89,7 +89,7 @@
 		frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
 
 
-	def test_update_child_qty_rate(self):
+	def test_update_child(self):
 		mr = make_material_request(qty=10)
 		po = make_purchase_order(mr.name)
 		po.supplier = "_Test Supplier"
@@ -119,7 +119,7 @@
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
 
 
-	def test_add_new_item_in_update_child_qty_rate(self):
+	def test_update_child_adding_new_item(self):
 		po = create_purchase_order(do_not_save=1)
 		po.items[0].qty = 4
 		po.save()
@@ -145,7 +145,7 @@
 		self.assertEqual(po.status, 'To Receive and Bill')
 
 
-	def test_remove_item_in_update_child_qty_rate(self):
+	def test_update_child_removing_item(self):
 		po = create_purchase_order(do_not_save=1)
 		po.items[0].qty = 4
 		po.save()
@@ -185,7 +185,7 @@
 		self.assertEquals(len(po.get('items')), 1)
 		self.assertEqual(po.status, 'To Receive and Bill')
 
-	def test_update_child_qty_rate_perm(self):
+	def test_update_child_perm(self):
 		po = create_purchase_order(item_code= "_Test Item", qty=4)
 
 		user = 'test@example.com'
@@ -202,6 +202,72 @@
 		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
 		frappe.set_user("Administrator")
 
+	def test_update_child_with_tax_template(self):
+		tax_template = "_Test Account Excise Duty @ 10"
+		item =  "_Test Item Home Desktop 100"
+
+		if not frappe.db.exists("Item Tax", {"parent":item, "item_tax_template":tax_template}):
+			item_doc = frappe.get_doc("Item", item)
+			item_doc.append("taxes", {
+				"item_tax_template": tax_template,
+				"valid_from": nowdate()
+			})
+			item_doc.save()
+		else:
+			# update valid from
+			frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = CURDATE()
+				where parent = %(item)s and item_tax_template = %(tax)s""",
+					{"item": item, "tax": tax_template})
+
+		po = create_purchase_order(item_code=item, qty=1, do_not_save=1)
+
+		po.append("taxes", {
+			"account_head": "_Test Account Excise Duty - _TC",
+			"charge_type": "On Net Total",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Excise Duty",
+			"doctype": "Purchase Taxes and Charges",
+			"rate": 10
+		})
+		po.insert()
+		po.submit()
+
+		self.assertEqual(po.taxes[0].tax_amount, 50)
+		self.assertEqual(po.taxes[0].total, 550)
+
+		items = json.dumps([
+			{'item_code' : item, 'rate' : 500, 'qty' : 1, 'docname': po.items[0].name},
+			{'item_code' : item, 'rate' : 100, 'qty' : 1} # added item
+		])
+		update_child_qty_rate('Purchase Order', items, po.name)
+
+		po.reload()
+		self.assertEqual(po.taxes[0].tax_amount, 60)
+		self.assertEqual(po.taxes[0].total, 660)
+
+		frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = NULL
+				where parent = %(item)s and item_tax_template = %(tax)s""",
+					{"item": item, "tax": tax_template})
+
+	def test_update_child_uom_conv_factor_change(self):
+		po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
+		total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
+
+		trans_item = json.dumps([{
+			'item_code': po.get("items")[0].item_code,
+			'rate': po.get("items")[0].rate,
+			'qty': po.get("items")[0].qty,
+			'uom': "_Test UOM 1",
+			'conversion_factor': 2,
+			'docname': po.get("items")[0].name
+		}])
+		update_child_qty_rate('Purchase Order', trans_item, po.name)
+		po.reload()
+
+		total_reqd_qty_after_change = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
+
+		self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty)
+
 	def test_update_qty(self):
 		po = create_purchase_order()
 
diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
index c3e1bf5..d7ea9c1 100644
--- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
+++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json
@@ -148,11 +148,11 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-03-12 15:43:53.862897",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item Supplied",
- "owner": "dhanalekshmi@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC"
diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
index ea58630..dc00bca 100644
--- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
+++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json
@@ -188,11 +188,11 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-04-10 18:09:33.997618",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Receipt Item Supplied",
- "owner": "wasim@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
index 3de9526..019cefc 100644
--- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
@@ -11,6 +11,8 @@
 from erpnext.templates.pages.rfq import check_supplier_has_docname_access
 from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
 from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
+from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
+from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
 
 class TestRequestforQuotation(unittest.TestCase):
 	def test_quote_status(self):
@@ -110,6 +112,23 @@
 		self.assertEqual(supplier_quotation.items[0].qty, 5)
 		self.assertEqual(supplier_quotation.items[0].stock_qty, 10)
 
+	def test_make_rfq_from_opportunity(self):
+		opportunity = make_opportunity(with_items=1)
+		supplier_data = get_supplier_data()
+		rfq = make_rfq(opportunity.name)
+
+		self.assertEqual(len(rfq.get("items")), len(opportunity.get("items")))
+		rfq.message_for_supplier = 'Please supply the specified items at the best possible rates.'
+
+		for item in rfq.items:
+			item.warehouse = "_Test Warehouse - _TC"
+
+		for data in supplier_data:
+			rfq.append('suppliers', data)
+
+		rfq.status = 'Draft'
+		rfq.submit()
+
 def make_request_for_quotation(**args):
 	"""
 	:param supplier_data: List containing supplier data
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
index af109ba..e956afd 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -178,6 +178,7 @@
 			period_card = make_supplier_scorecard(docname, None)
 			period_card.start_date = start_date
 			period_card.end_date = end_date
+			period_card.insert(ignore_permissions=True)
 			period_card.submit()
 			scp_count = scp_count + 1
 			if start_date < first_start_date:
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
index 87f1033..9938710 100644
--- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
@@ -106,7 +106,7 @@
 			"doctype": "Supplier Scorecard Scoring Criteria",
 			"postprocess": update_criteria_fields,
 		}
-	}, target_doc, post_process)
+	}, target_doc, post_process, ignore_permissions=True)
 
 	return doc
 
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
index a76ffee..518d665 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
@@ -12,7 +12,22 @@
 			"reqd": 1
 		},
 		{
-			reqd: 1,
+			"fieldname":"from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"width": "80",
+			"reqd": 1,
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldname":"to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"width": "80",
+			"reqd": 1,
+			"default": frappe.datetime.get_today()
+		},
+		{
 			default: "",
 			options: "Item",
 			label: __("Item"),
@@ -45,13 +60,12 @@
 			}
 		},
 		{
-			fieldtype: "Link",
+			fieldtype: "MultiSelectList",
 			label: __("Supplier Quotation"),
-			options: "Supplier Quotation",
 			fieldname: "supplier_quotation",
 			default: "",
-			get_query: () => {
-				return { filters: { "docstatus": ["<", 2] } }
+			get_data: function(txt) {
+				return frappe.db.get_link_options('Supplier Quotation', txt, {'docstatus': ["<", 2]});
 			}
 		},
 		{
@@ -63,9 +77,30 @@
 			get_query: () => {
 				return { filters: { "docstatus": ["<", 2] } }
 			}
+		},
+		{
+			fieldtype: "Check",
+			label: __("Include Expired"),
+			fieldname: "include_expired",
+			default: 0
 		}
 	],
 
+	formatter: (value, row, column, data, default_formatter) => {
+		value = default_formatter(value, row, column, data);
+
+		if(column.fieldname === "valid_till" && data.valid_till){
+			if(frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 1){
+				value = `<div style="color:red">${value}</div>`;
+			}
+			else if (frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 7){
+				value = `<div style="color:darkorange">${value}</div>`;
+			}
+		}
+
+		return value;
+	},
+
 	onload: (report) => {
 		// Create a button for setting the default supplier
 		report.page.add_inner_button(__("Select Default Supplier"), () => {
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
index a33867a..4426560 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
@@ -16,44 +16,49 @@
 	supplier_quotation_data = get_data(filters, conditions)
 	columns = get_columns()
 
-	data, chart_data = prepare_data(supplier_quotation_data)
+	data, chart_data = prepare_data(supplier_quotation_data, filters)
+	message = get_message()
 
-	return columns, data, None, chart_data
+	return columns, data, message, chart_data
 
 def get_conditions(filters):
 	conditions = ""
+	if filters.get("item_code"):
+		conditions += " AND sqi.item_code = %(item_code)s"
+
 	if filters.get("supplier_quotation"):
-		conditions += " AND sqi.parent = %(supplier_quotation)s"
+		conditions += " AND sqi.parent in %(supplier_quotation)s"
 
 	if filters.get("request_for_quotation"):
 		conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
 
 	if filters.get("supplier"):
 		conditions += " AND sq.supplier in %(supplier)s"
+
+	if not filters.get("include_expired"):
+		conditions += " AND sq.status != 'Expired'"
+
 	return conditions
 
 def get_data(filters, conditions):
-	if not filters.get("item_code"):
-		return []
-
 	supplier_quotation_data = frappe.db.sql("""SELECT
-		sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
-		sq.supplier
+		sqi.parent, sqi.item_code, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
+		sqi.lead_time_days, sq.supplier, sq.valid_till
 		FROM
 			`tabSupplier Quotation Item` sqi,
 			`tabSupplier Quotation` sq
 		WHERE
-			sqi.item_code = %(item_code)s
-			AND sqi.parent = sq.name
+			sqi.parent = sq.name
 			AND sqi.docstatus < 2
 			AND sq.company = %(company)s
-			AND sq.status != 'Expired'
-			{0}""".format(conditions), filters, as_dict=1)
+			AND sq.transaction_date between %(from_date)s and %(to_date)s
+			{0}
+			order by sq.transaction_date, sqi.item_code""".format(conditions), filters, as_dict=1)
 
 	return supplier_quotation_data
 
-def prepare_data(supplier_quotation_data):
-	out, suppliers, qty_list = [], [], []
+def prepare_data(supplier_quotation_data, filters):
+	out, suppliers, qty_list, chart_data = [], [], [], []
 	supplier_wise_map = defaultdict(list)
 	supplier_qty_price_map = {}
 
@@ -70,20 +75,24 @@
 			exchange_rate = 1
 
 		row = {
+			"item_code": data.get('item_code'),
 			"quotation": data.get("parent"),
 			"qty": data.get("qty"),
 			"price": flt(data.get("rate") * exchange_rate, float_precision),
 			"uom": data.get("uom"),
 			"request_for_quotation": data.get("request_for_quotation"),
+			"valid_till": data.get('valid_till'),
+			"lead_time_days": data.get('lead_time_days')
 		}
 
 		# map for report view of form {'supplier1':[{},{},...]}
 		supplier_wise_map[supplier].append(row)
 
 		# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
-		if not supplier in supplier_qty_price_map:
-			supplier_qty_price_map[supplier] = {}
-		supplier_qty_price_map[supplier][row["qty"]] = row["price"]
+		if filters.get("item_code"):
+			if not supplier in supplier_qty_price_map:
+				supplier_qty_price_map[supplier] = {}
+			supplier_qty_price_map[supplier][row["qty"]] = row["price"]
 
 		suppliers.append(supplier)
 		qty_list.append(data.get("qty"))
@@ -97,7 +106,8 @@
 		for entry in supplier_wise_map[supplier]:
 			out.append(entry)
 
-	chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
+	if filters.get("item_code"):
+		chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
 
 	return out, chart_data
 
@@ -117,9 +127,10 @@
 				data_points_map[qty].append(None)
 
 	dataset = []
+	currency_symbol = frappe.db.get_value("Currency", frappe.db.get_default("currency"), "symbol")
 	for qty in qty_list:
 		datapoints = {
-			"name": _("Price for Qty ") + str(qty),
+			"name": currency_symbol + " (Qty " + str(qty) + " )",
 			"values": data_points_map[qty]
 		}
 		dataset.append(datapoints)
@@ -140,14 +151,21 @@
 		"label": _("Supplier"),
 		"fieldtype": "Link",
 		"options": "Supplier",
+		"width": 150
+	},
+	{
+		"fieldname": "item_code",
+		"label": _("Item"),
+		"fieldtype": "Link",
+		"options": "Item",
 		"width": 200
 	},
 	{
-		"fieldname": "quotation",
-		"label": _("Supplier Quotation"),
+		"fieldname": "uom",
+		"label": _("UOM"),
 		"fieldtype": "Link",
-		"options": "Supplier Quotation",
-		"width": 200
+		"options": "UOM",
+		"width": 90
 	},
 	{
 		"fieldname": "qty",
@@ -163,19 +181,43 @@
 		"width": 110
 	},
 	{
-		"fieldname": "uom",
-		"label": _("UOM"),
+		"fieldname": "quotation",
+		"label": _("Supplier Quotation"),
 		"fieldtype": "Link",
-		"options": "UOM",
-		"width": 90
+		"options": "Supplier Quotation",
+		"width": 200
+	},
+	{
+		"fieldname": "valid_till",
+		"label": _("Valid Till"),
+		"fieldtype": "Date",
+		"width": 100
+	},
+	{
+		"fieldname": "lead_time_days",
+		"label": _("Lead Time (Days)"),
+		"fieldtype": "Int",
+		"width": 100
 	},
 	{
 		"fieldname": "request_for_quotation",
 		"label": _("Request for Quotation"),
 		"fieldtype": "Link",
 		"options": "Request for Quotation",
-		"width": 200
+		"width": 150
 	}
 	]
 
-	return columns
\ No newline at end of file
+	return columns
+
+def get_message():
+	return  """<span class="indicator">
+		Valid till : &nbsp;&nbsp;
+		</span>
+		<span class="indicator orange">
+		Expires in a week or less
+		</span>
+		&nbsp;&nbsp;
+		<span class="indicator red">
+		Expires today / Already Expired
+		</span>"""
\ No newline at end of file
diff --git a/erpnext/communication/doctype/call_log/call_log.json b/erpnext/communication/doctype/call_log/call_log.json
index cfc08eb..31e79f1 100644
--- a/erpnext/communication/doctype/call_log/call_log.json
+++ b/erpnext/communication/doctype/call_log/call_log.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "field:id",
  "creation": "2019-06-05 12:07:02.634534",
  "doctype": "DocType",
@@ -14,6 +15,7 @@
   "contact",
   "contact_name",
   "column_break_10",
+  "customer",
   "lead",
   "lead_name",
   "section_break_5",
@@ -28,7 +30,8 @@
   },
   {
    "fieldname": "section_break_5",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Call Details"
   },
   {
    "fieldname": "id",
@@ -125,10 +128,19 @@
    "in_list_view": 1,
    "label": "Lead Name",
    "read_only": 1
+  },
+  {
+   "fieldname": "customer",
+   "fieldtype": "Link",
+   "label": "Customer",
+   "options": "Customer",
+   "read_only": 1
   }
  ],
  "in_create": 1,
- "modified": "2019-08-06 05:46:53.144683",
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-08-25 17:08:34.085731",
  "modified_by": "Administrator",
  "module": "Communication",
  "name": "Call Log",
diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py
index 5fe3c4e..b31b757 100644
--- a/erpnext/communication/doctype/call_log/call_log.py
+++ b/erpnext/communication/doctype/call_log/call_log.py
@@ -16,6 +16,9 @@
 		self.contact = get_contact_with_phone_number(number)
 		self.lead = get_lead_with_phone_number(number)
 
+		contact = frappe.get_doc("Contact", self.contact)
+		self.customer = contact.get_link_for("Customer")
+
 	def after_insert(self):
 		self.trigger_call_popup()
 
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index d61e44b..90c466b 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -7,6 +7,7 @@
 from frappe import _, throw
 from frappe.utils import (today, flt, cint, fmt_money, formatdate,
 	getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
+from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied, WorkflowPermissionError
 from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
 from erpnext.setup.utils import get_exchange_rate
 from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
@@ -19,7 +20,7 @@
 from erpnext.exceptions import InvalidCurrency
 from six import text_type
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
-from erpnext.stock.get_item_details import get_item_warehouse
+from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
 from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 
 force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
@@ -1157,6 +1158,18 @@
 	}
 	return info
 
+def set_child_tax_template_and_map(item, child_item, parent_doc):
+	args = {
+			'item_code': item.item_code,
+			'posting_date': parent_doc.transaction_date,
+			'tax_category': parent_doc.get('tax_category'),
+			'company': parent_doc.get('company')
+		}
+
+	child_item.item_tax_template = _get_item_tax_template(args, item.taxes)
+	if child_item.get("item_tax_template"):
+		child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True)
+
 def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
 	"""
 	Returns a Sales Order Item child item containing the default values
@@ -1168,8 +1181,10 @@
 	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
-	child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
-	child_item.uom = item.stock_uom
+	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)
 	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.")
@@ -1188,13 +1203,15 @@
 	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.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
-	child_item.uom = item.stock_uom
+	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
+	set_child_tax_template_and_map(item, child_item, p_doc)
 	return child_item
 
-def check_and_delete_children(parent, data):
+def validate_and_delete_children(parent, data):
 	deleted_children = []
 	updated_item_names = [d.get("docname") for d in data]
 	for item in parent.items:
@@ -1221,18 +1238,40 @@
 
 @frappe.whitelist()
 def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
-	def check_permissions(doc, perm_type='create'):
+	def check_doc_permissions(doc, perm_type='create'):
 		try:
 			doc.check_permission(perm_type)
-		except:
-			action = "add" if perm_type == 'create' else "update"
-			frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
+		except frappe.PermissionError:
+			actions = { 'create': 'add', 'write': 'update', 'cancel': 'remove' }
+
+			frappe.throw(_("You do not have permissions to {} items in a {}.")
+				.format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions"))
+
+	def validate_workflow_conditions(doc):
+		workflow = get_workflow_name(doc.doctype)
+		if not workflow:
+			return
+
+		workflow_doc = frappe.get_doc("Workflow", workflow)
+		current_state = doc.get(workflow_doc.workflow_state_field)
+		roles = frappe.get_roles()
+
+		transitions = []
+		for transition in workflow_doc.transitions:
+			if transition.next_state == current_state and transition.allowed in roles:
+				if not is_transition_condition_satisfied(transition, doc):
+					continue
+				transitions.append(transition.as_dict())
+
+		if not transitions:
+			frappe.throw(
+				_("You are not allowed to update as per the conditions set in {} Workflow.").format(get_link_to_form("Workflow", workflow)),
+				title=_("Insufficient Permissions")
+			)
 
 	def get_new_child_item(item_row):
-		if parent_doctype == "Sales Order":
-			return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
-		if parent_doctype == "Purchase Order":
-			return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, 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)
 
 	def validate_quantity(child_item, d):
 		if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
@@ -1246,21 +1285,23 @@
 	sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
 	parent = frappe.get_doc(parent_doctype, parent_doctype_name)
 
-	check_and_delete_children(parent, data)
+	check_doc_permissions(parent, 'cancel')
+	validate_and_delete_children(parent, data)
 
 	for d in data:
 		new_child_flag = False
 		if not d.get("docname"):
 			new_child_flag = True
-			check_permissions(parent, 'create')
+			check_doc_permissions(parent, 'create')
 			child_item = get_new_child_item(d)
 		else:
-			check_permissions(parent, 'write')
+			check_doc_permissions(parent, 'write')
 			child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
 
 			prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
 			prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
 			prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
+			prev_uom, new_uom = child_item.get("uom"), d.get("uom")
 
 			if parent_doctype == 'Sales Order':
 				prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
@@ -1269,9 +1310,10 @@
 
 			rate_unchanged = prev_rate == new_rate
 			qty_unchanged = prev_qty == new_qty
+			uom_unchanged = prev_uom == new_uom
 			conversion_factor_unchanged = prev_con_fac == new_con_fac
 			date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
-			if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
+			if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
 				continue
 
 		validate_quantity(child_item, d)
@@ -1291,6 +1333,11 @@
 			else:
 				child_item.conversion_factor = flt(d.get('conversion_factor'))
 
+		if d.get("uom"):
+			child_item.uom = d.get("uom")
+			conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor"))
+			child_item.conversion_factor = flt(d.get('conversion_factor')) or conversion_factor
+
 		if d.get("delivery_date") and parent_doctype == 'Sales Order':
 			child_item.delivery_date = d.get('delivery_date')
 
@@ -1356,12 +1403,17 @@
 		parent.update_receiving_percentage()
 		if parent.is_subcontracted == "Yes":
 			parent.update_reserved_qty_for_subcontract()
+			parent.create_raw_materials_supplied("supplied_items")
+			parent.save()
 	else:
 		parent.update_reserved_qty()
 		parent.update_project()
 		parent.update_prevdoc_status('submit')
 		parent.update_delivery_status()
 
+	parent.reload()
+	validate_workflow_conditions(parent)
+
 	parent.update_blanket_order()
 	parent.update_billing_percentage()
 	parent.set_status()
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index f982700..ac567b7 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -276,6 +276,9 @@
 		qty_to_be_received_map = get_qty_to_be_received(purchase_orders)
 
 		for item in self.get('items'):
+			if not item.purchase_order:
+				continue
+
 			# reset raw_material cost
 			item.rm_supp_cost = 0
 
@@ -288,6 +291,12 @@
 
 			fg_yet_to_be_received = qty_to_be_received_map.get(item_key)
 
+			if not fg_yet_to_be_received:
+				frappe.throw(_("Row #{0}: Item {1} is already fully received in Purchase Order {2}")
+					.format(item.idx, frappe.bold(item.item_code),
+						frappe.utils.get_link_to_form("Purchase Order", item.purchase_order)),
+					title=_("Limit Crossed"))
+
 			transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
 			backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
 
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index c88bf66..efd4944 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -165,9 +165,14 @@
 				AND company = %(company)s
 				AND account_currency = %(currency)s
 				AND `{searchfield}` LIKE %(txt)s
+				{mcond}
 			ORDER BY idx DESC, name
 			LIMIT %(offset)s, %(limit)s
-		""".format(account_type_condition=account_type_condition, searchfield=searchfield),
+		""".format(
+				account_type_condition=account_type_condition,
+				searchfield=searchfield,
+				mcond=get_match_cond(doctype)
+			),
 			dict(
 				account_types=filters.get("account_type"),
 				company=filters.get("company"),
@@ -359,9 +364,21 @@
 	if filters.get("is_return"):
 		having_clause = ""
 
+	meta = frappe.get_meta("Batch", cached=True)
+	searchfields = meta.get_search_fields()
+
+	search_columns = ''
+	if searchfields:
+		search_columns = ", " + ", ".join(searchfields)
+
 	if args.get('warehouse'):
+		searchfields = ['batch.' + field for field in searchfields]
+		if searchfields:
+			search_columns = ", " + ", ".join(searchfields)
+
 		batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
 				concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
+				{search_columns}
 			from `tabStock Ledger Entry` sle
 				INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
 			where
@@ -377,6 +394,7 @@
 			group by batch_no {having_clause}
 			order by batch.expiry_date, sle.batch_no desc
 			limit %(start)s, %(page_len)s""".format(
+				search_columns = search_columns,
 				cond=cond,
 				match_conditions=get_match_cond(doctype),
 				having_clause = having_clause
@@ -384,7 +402,9 @@
 
 		return batch_nos
 	else:
-		return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date) from `tabBatch` batch
+		return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date)
+			{search_columns}
+			from `tabBatch` batch
 			where batch.disabled = 0
 			and item = %(item_code)s
 			and (name like %(txt)s
@@ -394,7 +414,7 @@
 			{0}
 			{match_conditions}
 			order by expiry_date, name desc
-			limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
+			limit %(start)s, %(page_len)s""".format(cond, search_columns = search_columns, match_conditions=get_match_cond(doctype)), args)
 
 
 @frappe.whitelist()
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index a03dee1..afc5f81 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -242,7 +242,8 @@
 						'type': data.type,
 						'amount': -1 * paid_amount,
 						'base_amount': -1 * base_paid_amount,
-						'account': data.account
+						'account': data.account,
+						'default': data.default
 					})
 				if doc.is_pos:
 					doc.paid_amount = -1 * source.paid_amount
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 17f3ae5..7f7aae3 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -81,6 +81,7 @@
 			party_details = _get_party_details(customer,
 				ignore_permissions=self.flags.ignore_permissions,
 				doctype=self.doctype, company=self.company,
+				posting_date=self.get('posting_date'),
 				fetch_payment_terms_template=fetch_payment_terms_template,
 				party_address=self.customer_address, shipping_address=self.shipping_address_name)
 			if not self.meta.get_field("sales_team"):
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 0dc9878..9feac78 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -255,7 +255,7 @@
 				args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s)
 					from `tab%(second_source_dt)s`
 					where `%(second_join_field)s`="%(detail_id)s"
-					and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s), 0) """ % args
+					and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s FOR UPDATE), 0)""" % args
 
 			if args['detail_id']:
 				if not args.get("extra_cond"): args["extra_cond"] = ""
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 2a14be8..92cfdb7 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -9,6 +9,7 @@
 from erpnext.controllers.accounts_controller import validate_conversion_rate, \
 	validate_taxes_and_charges, validate_inclusive_tax
 from erpnext.stock.get_item_details import _get_item_tax_template
+from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
 
 class calculate_taxes_and_totals(object):
 	def __init__(self, doc):
@@ -209,7 +210,7 @@
 			elif tax.charge_type == "On Previous Row Total":
 				current_tax_fraction = (tax_rate / 100.0) * \
 					self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
-			
+
 			elif tax.charge_type == "On Item Quantity":
 				inclusive_tax_amount_per_qty = flt(tax_rate)
 
@@ -607,7 +608,7 @@
 		base_rate_with_margin = 0.0
 		if item.price_list_rate:
 			if item.pricing_rules and not self.doc.ignore_pricing_rule:
-				for d in json.loads(item.pricing_rules):
+				for d in get_applied_pricing_rules(item.pricing_rules):
 					pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
 
 					if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json
index 234008d..5497f3e 100644
--- a/erpnext/crm/desk_page/crm/crm.json
+++ b/erpnext/crm/desk_page/crm/crm.json
@@ -8,7 +8,7 @@
   {
    "hidden": 0,
    "label": "Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Lead Details\",\n        \"name\": \"Lead Details\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"icon\": \"fa fa-bar-chart\",\n        \"label\": \"Sales Funnel\",\n        \"name\": \"sales-funnel\",\n        \"onboard\": 1,\n        \"type\": \"page\"\n    },\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Prospects Engaged But Not Converted\",\n        \"name\": \"Prospects Engaged But Not Converted\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Opportunity\"\n        ],\n        \"doctype\": \"Opportunity\",\n        \"is_query_report\": true,\n        \"label\": \"Minutes to First Response for Opportunity\",\n        \"name\": \"Minutes to First Response for Opportunity\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Sales Order\"\n        ],\n        \"doctype\": \"Sales Order\",\n        \"is_query_report\": true,\n        \"label\": \"Inactive Customers\",\n        \"name\": \"Inactive Customers\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Campaign Efficiency\",\n        \"name\": \"Campaign Efficiency\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Lead Owner Efficiency\",\n        \"name\": \"Lead Owner Efficiency\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Lead Details\",\n        \"name\": \"Lead Details\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"icon\": \"fa fa-bar-chart\",\n        \"label\": \"Sales Funnel\",\n        \"name\": \"sales-funnel\",\n        \"onboard\": 1,\n        \"type\": \"page\"\n    },\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Prospects Engaged But Not Converted\",\n        \"name\": \"Prospects Engaged But Not Converted\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Opportunity\"\n        ],\n        \"doctype\": \"Opportunity\",\n        \"is_query_report\": true,\n        \"label\": \"First Response Time for Opportunity\",\n        \"name\": \"First Response Time for Opportunity\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Sales Order\"\n        ],\n        \"doctype\": \"Sales Order\",\n        \"is_query_report\": true,\n        \"label\": \"Inactive Customers\",\n        \"name\": \"Inactive Customers\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Campaign Efficiency\",\n        \"name\": \"Campaign Efficiency\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Lead\"\n        ],\n        \"doctype\": \"Lead\",\n        \"is_query_report\": true,\n        \"label\": \"Lead Owner Efficiency\",\n        \"name\": \"Lead Owner Efficiency\",\n        \"type\": \"report\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -43,7 +43,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "CRM",
- "modified": "2020-06-30 18:37:48.961922",
+ "modified": "2020-08-11 18:55:18.238900",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "CRM",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index b61cad3..eee13f7 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -24,7 +24,7 @@
   "converted_by",
   "sales_stage",
   "order_lost_reason",
-  "mins_to_first_response",
+  "first_response_time",
   "expected_closing",
   "next_contact",
   "contact_by",
@@ -153,13 +153,6 @@
    "read_only": 1
   },
   {
-   "bold": 1,
-   "fieldname": "mins_to_first_response",
-   "fieldtype": "Float",
-   "label": "Mins to first response",
-   "read_only": 1
-  },
-  {
    "fieldname": "expected_closing",
    "fieldtype": "Date",
    "label": "Expected Closing Date"
@@ -419,12 +412,19 @@
    "fieldtype": "Link",
    "label": "Converted By",
    "options": "User"
+  },
+  {
+   "bold": 1,
+   "fieldname": "first_response_time",
+   "fieldtype": "Duration",
+   "label": "First Response Time",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-info-sign",
  "idx": 195,
  "links": [],
- "modified": "2020-08-11 17:34:35.066961",
+ "modified": "2020-08-12 17:34:35.066961",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Opportunity",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 6096053..47b05f3 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -267,6 +267,9 @@
 
 @frappe.whitelist()
 def make_request_for_quotation(source_name, target_doc=None):
+	def update_item(obj, target, source_parent):
+		target.conversion_factor = 1.0
+
 	doclist = get_mapped_doc("Opportunity", source_name, {
 		"Opportunity": {
 			"doctype": "Request for Quotation"
@@ -277,7 +280,8 @@
 				["name", "opportunity_item"],
 				["parent", "opportunity"],
 				["uom", "uom"]
-			]
+			],
+			"postprocess": update_item
 		}
 	}, target_doc)
 
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py
index 33d9007..04cd8a2 100644
--- a/erpnext/crm/doctype/opportunity/test_opportunity.py
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.py
@@ -82,7 +82,8 @@
 	if args.with_items:
 		opp_doc.append('items', {
 			"item_code": args.item_code or "_Test Item",
-			"qty": args.qty or 1
+			"qty": args.qty or 1,
+			"uom": "_Test UOM"
 		})
 
 	opp_doc.insert()
diff --git a/erpnext/crm/report/minutes_to_first_response_for_opportunity/__init__.py b/erpnext/crm/report/first_response_time_for_opportunity/__init__.py
similarity index 100%
rename from erpnext/crm/report/minutes_to_first_response_for_opportunity/__init__.py
rename to erpnext/crm/report/first_response_time_for_opportunity/__init__.py
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
new file mode 100644
index 0000000..3f5c95a
--- /dev/null
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["First Response Time for Opportunity"] = {
+	"filters": [
+		{
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
+		},
+		{
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.nowdate()
+		},
+	],
+	get_chart_data: function (_columns, result) {
+		return {
+			data: {
+				labels: result.map(d => d[0]),
+				datasets: [{
+					name: "First Response Time",
+					values: result.map(d => d[1])
+				}]
+			},
+			type: "line",
+			tooltipOptions: {
+				formatTooltipY: d => {
+					let duration_options = {
+						hide_days: 0,
+						hide_seconds: 0
+					};
+					value = frappe.utils.get_formatted_duration(d, duration_options);
+					return value;
+				}
+			}
+		}
+	}
+};
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.json b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.json
new file mode 100644
index 0000000..1b3184f
--- /dev/null
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.json
@@ -0,0 +1,28 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-08-10 18:34:19.083872",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "Test 2",
+ "modified": "2020-08-10 18:34:19.083872",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "First Response Time for Opportunity",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Opportunity",
+ "report_name": "First Response Time for Opportunity",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Sales User"
+  },
+  {
+   "role": "Sales Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
new file mode 100644
index 0000000..2ffbc3e
--- /dev/null
+++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute(filters=None):
+	columns = [
+		{
+			'fieldname': 'creation_date',
+			'label': 'Date',
+			'fieldtype': 'Date',
+			'width': 300
+		},
+		{
+			'fieldname': 'first_response_time',
+			'fieldtype': 'Duration',
+			'label': 'First Response Time',
+			'width': 300
+		},
+	]
+
+	data = frappe.db.sql('''
+		SELECT
+			date(creation) as creation_date,
+			avg(first_response_time) as avg_response_time
+		FROM tabOpportunity
+		WHERE
+			date(creation) between %s and %s
+			and first_response_time > 0
+		GROUP BY creation_date
+		ORDER BY creation_date desc
+	''', (filters.from_date, filters.to_date))
+
+	return columns, data
diff --git a/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.js b/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.js
deleted file mode 100644
index 92d026a..0000000
--- a/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.query_reports["Minutes to First Response for Opportunity"] = {
-	"filters": [
-		{
-			"fieldname": "from_date",
-			"label": __("From Date"),
-			"fieldtype": "Date",
-			'reqd': 1,
-			"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
-		},
-		{
-			"fieldname": "to_date",
-			"label": __("To Date"),
-			"fieldtype": "Date",
-			'reqd': 1,
-			"default": frappe.datetime.nowdate()
-		},
-	],
-	get_chart_data: function (columns, result) {
-		return {
-			data: {
-				labels: result.map(d => d[0]),
-				datasets: [{
-					name: 'Mins to first response',
-					values: result.map(d => d[1])
-				}]
-			},
-			type: 'line',
-		}
-	}
-}
diff --git a/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.json b/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.json
deleted file mode 100644
index bcd092b..0000000
--- a/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "add_total_row": 0, 
- "apply_user_permissions": 0, 
- "creation": "2016-06-17 11:28:25.867258", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 2, 
- "is_standard": "Yes", 
- "modified": "2017-02-24 20:06:08.801109", 
- "modified_by": "Administrator", 
- "module": "CRM", 
- "name": "Minutes to First Response for Opportunity", 
- "owner": "Administrator", 
- "ref_doctype": "Opportunity", 
- "report_name": "Minutes to First Response for Opportunity", 
- "report_type": "Script Report", 
- "roles": [
-  {
-   "role": "Sales User"
-  }, 
-  {
-   "role": "Sales Manager"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.py b/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.py
deleted file mode 100644
index 54e3a60..0000000
--- a/erpnext/crm/report/minutes_to_first_response_for_opportunity/minutes_to_first_response_for_opportunity.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute(filters=None):
-	columns = [
-		{
-			'fieldname': 'creation_date',
-			'label': 'Date',
-			'fieldtype': 'Date'
-		},
-		{
-			'fieldname': 'mins',
-			'fieldtype': 'Float',
-			'label': 'Mins to First Response'
-		},
-	]
-
-	data = frappe.db.sql('''select date(creation) as creation_date,
-		avg(mins_to_first_response) as mins
-		from tabOpportunity
-			where date(creation) between %s and %s
-			and mins_to_first_response > 0
-		group by creation_date order by creation_date desc''', (filters.from_date, filters.to_date))
-
-	return columns, data
diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js
index 1936dcb..fd23ae4 100644
--- a/erpnext/education/doctype/student/student.js
+++ b/erpnext/education/doctype/student/student.js
@@ -3,15 +3,15 @@
 
 frappe.ui.form.on('Student', {
 	setup: function(frm) {
-		frm.add_fetch("guardian", "guardian_name", "guardian_name");
-		frm.add_fetch("student", "title", "full_name");
-		frm.add_fetch("student", "gender", "gender");
-		frm.add_fetch("student", "date_of_birth", "date_of_birth");
+		frm.add_fetch('guardian', 'guardian_name', 'guardian_name');
+		frm.add_fetch('student', 'title', 'full_name');
+		frm.add_fetch('student', 'gender', 'gender');
+		frm.add_fetch('student', 'date_of_birth', 'date_of_birth');
 
-		frm.set_query("student", "siblings", function(doc, cdt, cdn) {
+		frm.set_query('student', 'siblings', function(doc) {
 			return {
-				"filters": {
-					"name": ["!=", doc.name]
+				'filters': {
+					'name': ['!=', doc.name]
 				}
 			};
 		})
@@ -25,6 +25,12 @@
 					{party_type:'Student', party:frm.doc.name});
 			});
 		}
+
+		frappe.db.get_value('Education Settings', {name: 'Education Settings'}, 'user_creation_skip', (r) => {
+			if (cint(r.user_creation_skip) !== 1) {
+				frm.set_df_property('student_email_id', 'reqd', 1);
+			}
+		});
 	}
 });
 
diff --git a/erpnext/education/doctype/student/student.json b/erpnext/education/doctype/student/student.json
index ac65c0c..8ba9a7f 100644
--- a/erpnext/education/doctype/student/student.json
+++ b/erpnext/education/doctype/student/student.json
@@ -102,7 +102,6 @@
    "fieldtype": "Data",
    "in_global_search": 1,
    "label": "Student Email Address",
-   "reqd": 1,
    "unique": 1
   },
   {
@@ -255,7 +254,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-07-23 18:14:06.366442",
+ "modified": "2020-09-07 19:28:08.914568",
  "modified_by": "Administrator",
  "module": "Education",
  "name": "Student",
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.js b/erpnext/education/doctype/student_applicant/student_applicant.js
index 83621c5..b4cfdf1 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant.js
+++ b/erpnext/education/doctype/student_applicant/student_applicant.js
@@ -7,7 +7,7 @@
 	},
 
 	refresh: function(frm) {
-		if(frm.doc.application_status== "Applied" && frm.doc.docstatus== 1 ) {
+		if (frm.doc.application_status==="Applied" && frm.doc.docstatus===1 ) {
 			frm.add_custom_button(__("Approve"), function() {
 				frm.set_value("application_status", "Approved");
 				frm.save_or_update();
@@ -20,10 +20,11 @@
 			}, 'Actions');
 		}
 
-		if(frm.doc.application_status== "Approved" && frm.doc.docstatus== 1 ) {
+		if (frm.doc.application_status === "Approved" && frm.doc.docstatus === 1) {
 			frm.add_custom_button(__("Enroll"), function() {
 				frm.events.enroll(frm)
 			}).addClass("btn-primary");
+
 			frm.add_custom_button(__("Reject"), function() {
 				frm.set_value("application_status", "Rejected");
 				frm.save_or_update();
@@ -35,7 +36,13 @@
 				frappe.hide_msgprint(true);
 				frappe.show_progress(__("Enrolling student"), data.progress[0],data.progress[1]);
 			}
-		})
+		});
+
+		frappe.db.get_value("Education Settings", {name: "Education Settings"}, "user_creation_skip", (r) => {
+			if (cint(r.user_creation_skip) !== 1) {
+				frm.set_df_property("student_email_id", "reqd", 1);
+			}
+		});
 	},
 
 	enroll: function(frm) {
diff --git a/erpnext/education/doctype/student_applicant/student_applicant.json b/erpnext/education/doctype/student_applicant/student_applicant.json
index e5d0bd3..bca38fb 100644
--- a/erpnext/education/doctype/student_applicant/student_applicant.json
+++ b/erpnext/education/doctype/student_applicant/student_applicant.json
@@ -1,1233 +1,292 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 1, 
- "allow_rename": 0, 
- "autoname": "naming_series:", 
- "beta": 0, 
- "creation": "2015-09-11 11:50:09.740807", 
- "custom": 0, 
- "default_print_format": "", 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2015-09-11 11:50:09.740807",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+  "first_name",
+  "middle_name",
+  "last_name",
+  "program",
+  "lms_only",
+  "paid",
+  "column_break_8",
+  "naming_series",
+  "application_status",
+  "application_date",
+  "academic_year",
+  "academic_term",
+  "student_admission",
+  "image",
+  "section_break_4",
+  "date_of_birth",
+  "gender",
+  "blood_group",
+  "column_break_12",
+  "student_email_id",
+  "student_mobile_number",
+  "nationality",
+  "home_address",
+  "address_line_1",
+  "address_line_2",
+  "pincode",
+  "column_break_23",
+  "city",
+  "state",
+  "section_break_20",
+  "guardians",
+  "section_break_21",
+  "siblings",
+  "section_break_23",
+  "title",
+  "amended_from"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "first_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "First Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "first_name",
+   "fieldtype": "Data",
+   "in_global_search": 1,
+   "label": "First Name",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "middle_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": "Middle Name", 
-   "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": "middle_name",
+   "fieldtype": "Data",
+   "label": "Middle Name"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "last_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Last Name", 
-   "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": "last_name",
+   "fieldtype": "Data",
+   "in_global_search": 1,
+   "label": "Last Name"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "program", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 1, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Program", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Program", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "program",
+   "fieldtype": "Link",
+   "in_filter": 1,
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Program",
+   "options": "Program",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fieldname": "lms_only", 
-   "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": "LMS Only", 
-   "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": "lms_only",
+   "fieldtype": "Check",
+   "label": "LMS Only"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "paid", 
-   "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": "Paid", 
-   "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": "paid",
+   "fieldtype": "Check",
+   "label": "Paid"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_8", 
-   "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_8",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "", 
-   "fieldname": "naming_series", 
-   "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": "Naming Series", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "EDU-APP-.YYYY.-", 
-   "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": 1, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "no_copy": 1,
+   "options": "EDU-APP-.YYYY.-",
+   "set_only_once": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.docstatus != 0", 
-   "fieldname": "application_status", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 1, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Application Status", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Applied\nApproved\nRejected\nAdmitted", 
-   "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_on_submit": 1,
+   "depends_on": "eval:doc.docstatus != 0",
+   "fieldname": "application_status",
+   "fieldtype": "Select",
+   "in_filter": 1,
+   "label": "Application Status",
+   "no_copy": 1,
+   "options": "Applied\nApproved\nRejected\nAdmitted"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "Today", 
-   "fieldname": "application_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": "Application Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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": "Today",
+   "fieldname": "application_date",
+   "fieldtype": "Date",
+   "label": "Application Date"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_year", 
-   "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": "Academic Year", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Year", 
-   "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": "academic_year",
+   "fieldtype": "Link",
+   "label": "Academic Year",
+   "options": "Academic Year"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "academic_term", 
-   "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": "Academic Term", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Academic Term", 
-   "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": "academic_term",
+   "fieldtype": "Link",
+   "label": "Academic Term",
+   "options": "Academic Term"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_admission", 
-   "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": "Student Admission", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Admission", 
-   "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": "student_admission",
+   "fieldtype": "Link",
+   "label": "Student Admission",
+   "options": "Student Admission"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "image", 
-   "fieldtype": "Attach Image", 
-   "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": "Image", 
-   "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": "image",
+   "fieldtype": "Attach Image",
+   "hidden": 1,
+   "label": "Image"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "collapsible_depends_on": "", 
-   "columns": 0, 
-   "fieldname": "section_break_4", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Personal Details", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "label": "Personal Details"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "date_of_birth", 
-   "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 of Birth", 
-   "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": "date_of_birth",
+   "fieldtype": "Date",
+   "label": "Date of Birth"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "gender", 
-   "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": "Gender", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nMale\nFemale", 
-   "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": "gender",
+   "fieldtype": "Select",
+   "label": "Gender",
+   "options": "\nMale\nFemale"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "blood_group", 
-   "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": "Blood Group", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", 
-   "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": "blood_group",
+   "fieldtype": "Select",
+   "label": "Blood Group",
+   "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_12", 
-   "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_12",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_email_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": "Student Email Address", 
-   "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, 
+   "fieldname": "student_email_id",
+   "fieldtype": "Data",
+   "label": "Student Email Address",
    "unique": 1
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "student_mobile_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": "Student Mobile 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
-  }, 
+   "fieldname": "student_mobile_number",
+   "fieldtype": "Data",
+   "label": "Student Mobile Number"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "nationality", 
-   "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": "Nationality", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "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": "nationality",
+   "fieldtype": "Data",
+   "label": "Nationality"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "home_address", 
-   "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": "Home Address", 
-   "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": "home_address",
+   "fieldtype": "Section Break",
+   "label": "Home Address"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address_line_1", 
-   "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": "Address Line 1", 
-   "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": "address_line_1",
+   "fieldtype": "Data",
+   "label": "Address Line 1"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address_line_2", 
-   "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": "Address Line 2", 
-   "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": "address_line_2",
+   "fieldtype": "Data",
+   "label": "Address Line 2"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "pincode", 
-   "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": "Pincode", 
-   "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": "pincode",
+   "fieldtype": "Data",
+   "label": "Pincode"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_23", 
-   "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_23",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "city", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "City", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "city",
+   "fieldtype": "Data",
+   "label": "City"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "state", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "State", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "state",
+   "fieldtype": "Data",
+   "label": "State"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 1, 
-   "columns": 0, 
-   "fieldname": "section_break_20", 
-   "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": "Guardian Details", 
-   "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
-  }, 
+   "collapsible": 1,
+   "fieldname": "section_break_20",
+   "fieldtype": "Section Break",
+   "label": "Guardian Details"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "guardians", 
-   "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": "Guardians", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Guardian", 
-   "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": "guardians",
+   "fieldtype": "Table",
+   "label": "Guardians",
+   "options": "Student Guardian"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 1, 
-   "columns": 0, 
-   "fieldname": "section_break_21", 
-   "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": "Sibling Details", 
-   "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
-  }, 
+   "collapsible": 1,
+   "fieldname": "section_break_21",
+   "fieldtype": "Section Break",
+   "label": "Sibling Details"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "siblings", 
-   "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": "Siblings", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Student Sibling", 
-   "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": "siblings",
+   "fieldtype": "Table",
+   "label": "Siblings",
+   "options": "Student Sibling"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_23", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_23",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "title", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Title", 
-   "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": "title",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Title"
+  },
   {
-   "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": "Student Applicant", 
-   "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
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Student Applicant",
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_field": "image", 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 1, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2018-10-05 13:15:59.283862", 
- "modified_by": "Administrator", 
- "module": "Education", 
- "name": "Student Applicant", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "image_field": "image",
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-09-07 19:31:30.063563",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student Applicant",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 1, 
-   "cancel": 1, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 1, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Academics User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 1, 
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "import": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Academics User",
+   "share": 1,
+   "submit": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Education", 
- "show_name_in_global_search": 1, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "title", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
-}
+ ],
+ "restrict_to_domain": "Education",
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title"
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json
new file mode 100644
index 0000000..8dcc77d
--- /dev/null
+++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json
@@ -0,0 +1,40 @@
+{
+ "cards": [
+  {
+   "hidden": 0,
+   "label": "Marketplace",
+   "links": "[\n    {\n        \"description\": \"Woocommerce marketplace settings\",\n        \"label\": \"Woocommerce Settings\",\n        \"name\": \"Woocommerce Settings\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Amazon MWS settings\",\n        \"label\": \"Amazon MWS Settings\",\n        \"name\": \"Amazon MWS Settings\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Shopify settings\",\n        \"label\": \"Shopify Settings\",\n        \"name\": \"Shopify Settings\",\n        \"type\": \"doctype\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Payments",
+   "links": "[\n    {\n        \"description\": \"GoCardless payment gateway settings\",\n        \"label\": \"GoCardless Settings\",\n        \"name\": \"GoCardless Settings\",\n        \"type\": \"doctype\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Settings",
+   "links": "[\n    {\n        \"description\": \"Plaid settings\",\n        \"label\": \"Plaid Settings\",\n        \"name\": \"Plaid Settings\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Exotel settings\",\n        \"label\": \"Exotel Settings\",\n        \"name\": \"Exotel Settings\",\n        \"type\": \"doctype\"\n    }\n]"
+  }
+ ],
+ "category": "Modules",
+ "charts": [],
+ "creation": "2020-08-20 19:30:48.138801",
+ "developer_mode_only": 0,
+ "disable_user_customization": 0,
+ "docstatus": 0,
+ "doctype": "Desk Page",
+ "extends": "Integrations",
+ "extends_another_page": 1,
+ "hide_custom": 1,
+ "idx": 0,
+ "is_standard": 1,
+ "label": "ERPNext Integrations",
+ "modified": "2020-08-23 16:30:51.494655",
+ "modified_by": "Administrator",
+ "module": "ERPNext Integrations",
+ "name": "ERPNext Integrations",
+ "owner": "Administrator",
+ "pin_to_bottom": 0,
+ "pin_to_top": 0,
+ "shortcuts": []
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 532e19c..a033a2a 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -2,81 +2,90 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-from __future__ import unicode_literals
-from frappe import _
-from frappe.utils.password import get_decrypted_password
-from plaid import Client
-from plaid.errors import APIError, ItemError
+import plaid
+import requests
+from plaid.errors import APIError, ItemError, InvalidRequestError
 
 import frappe
-import requests
+from frappe import _
+
 
 class PlaidConnector():
 	def __init__(self, access_token=None):
-
-		plaid_settings = frappe.get_single("Plaid Settings")
-
-		self.config = {
-			"plaid_client_id": plaid_settings.plaid_client_id,
-			"plaid_secret": get_decrypted_password("Plaid Settings", "Plaid Settings", 'plaid_secret'),
-			"plaid_public_key": plaid_settings.plaid_public_key,
-			"plaid_env": plaid_settings.plaid_env
-		}
-
-		self.client = Client(client_id=self.config.get("plaid_client_id"),
-			secret=self.config.get("plaid_secret"),
-			public_key=self.config.get("plaid_public_key"),
-			environment=self.config.get("plaid_env")
-			)
-
 		self.access_token = access_token
+		self.settings = frappe.get_single("Plaid Settings")
+		self.products = ["auth", "transactions"]
+		self.client_name = frappe.local.site
+		self.client = plaid.Client(
+			client_id=self.settings.plaid_client_id,
+			secret=self.settings.get_password("plaid_secret"),
+			environment=self.settings.plaid_env,
+			api_version="2019-05-29"
+		)
 
 	def get_access_token(self, public_token):
 		if public_token is None:
 			frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
-
 		response = self.client.Item.public_token.exchange(public_token)
-		access_token = response['access_token']
-
+		access_token = response["access_token"]
 		return access_token
 
+	def get_link_token(self):
+		token_request = {
+			"client_name": self.client_name,
+			"client_id": self.settings.plaid_client_id,
+			"secret": self.settings.plaid_secret,
+			"products": self.products,
+			# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
+			"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
+			"country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"],
+			"user": {
+				"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
+			}
+		}
+
+		try:
+			response = self.client.LinkToken.create(token_request)
+		except InvalidRequestError:
+			frappe.log_error(frappe.get_traceback(), _("Plaid invalid request error"))
+			frappe.msgprint(_("Please check your Plaid client ID and secret values"))
+		except APIError as e:
+			frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
+			frappe.throw(_(str(e)), title=_("Authentication Failed"))
+		else:
+			return response["link_token"]
+
 	def auth(self):
 		try:
 			self.client.Auth.get(self.access_token)
-			print("Authentication successful.....")
 		except ItemError as e:
-			if e.code == 'ITEM_LOGIN_REQUIRED':
-				pass
-			else:
+			if e.code == "ITEM_LOGIN_REQUIRED":
 				pass
 		except APIError as e:
-			if e.code == 'PLANNED_MAINTENANCE':
-				pass
-			else:
+			if e.code == "PLANNED_MAINTENANCE":
 				pass
 		except requests.Timeout:
 			pass
 		except Exception as e:
-			print(e)
 			frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
-			frappe.msgprint({"title": _("Authentication Failed"), "message":e, "raise_exception":1, "indicator":'red'})
+			frappe.throw(_(str(e)), title=_("Authentication Failed"))
 
 	def get_transactions(self, start_date, end_date, account_id=None):
+		self.auth()
+		kwargs = dict(
+			access_token=self.access_token,
+			start_date=start_date,
+			end_date=end_date
+		)
+		if account_id:
+			kwargs.update(dict(account_ids=[account_id]))
+
 		try:
-			self.auth()
-			if account_id:
-				account_ids = [account_id]
-
-				response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids)
-
-			else:
-				response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date)
-
-			transactions = response['transactions']
-
-			while len(transactions) < response['total_transactions']:
+			response = self.client.Transactions.get(**kwargs)
+			transactions = response["transactions"]
+			while len(transactions) < response["total_transactions"]:
 				response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
-				transactions.extend(response['transactions'])
+				transactions.extend(response["transactions"])
 			return transactions
 		except Exception:
 			frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
index 0ffbb87..22a4004 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
@@ -4,14 +4,14 @@
 frappe.provide("erpnext.integrations");
 
 frappe.ui.form.on('Plaid Settings', {
-	enabled: function(frm) {
+	enabled: function (frm) {
 		frm.toggle_reqd('plaid_client_id', frm.doc.enabled);
 		frm.toggle_reqd('plaid_secret', frm.doc.enabled);
-		frm.toggle_reqd('plaid_public_key', frm.doc.enabled);
 		frm.toggle_reqd('plaid_env', frm.doc.enabled);
 	},
-	refresh: function(frm) {
-		if(frm.doc.enabled) {
+
+	refresh: function (frm) {
+		if (frm.doc.enabled) {
 			frm.add_custom_button('Link a new bank account', () => {
 				new erpnext.integrations.plaidLink(frm);
 			});
@@ -22,17 +22,16 @@
 erpnext.integrations.plaidLink = class plaidLink {
 	constructor(parent) {
 		this.frm = parent;
-		this.product = ["transactions", "auth"];
 		this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
 		this.init_config();
 	}
 
-	init_config() {
-		const me = this;
-		me.plaid_env = me.frm.doc.plaid_env;
-		me.plaid_public_key = me.frm.doc.plaid_public_key;
-		me.client_name = frappe.boot.sitename;
-		me.init_plaid();
+	async init_config() {
+		this.product = ["auth", "transactions"];
+		this.plaid_env = this.frm.doc.plaid_env;
+		this.client_name = frappe.boot.sitename;
+		this.token = await this.frm.call("get_link_token").then(resp => resp.message);
+		this.init_plaid();
 	}
 
 	init_plaid() {
@@ -69,17 +68,17 @@
 	}
 
 	onScriptLoaded(me) {
-		me.linkHandler = window.Plaid.create({
+		me.linkHandler = Plaid.create({
 			clientName: me.client_name,
+			product: me.product,
 			env: me.plaid_env,
-			key: me.plaid_public_key,
-			onSuccess: me.plaid_success,
-			product: me.product
+			token: me.token,
+			onSuccess: me.plaid_success
 		});
 	}
 
 	onScriptError(error) {
-		frappe.msgprint('There was an issue loading the link-initialize.js script');
+		frappe.msgprint("There was an issue connecting to Plaid's authentication server");
 		frappe.msgprint(error);
 	}
 
@@ -87,21 +86,25 @@
 		const me = this;
 
 		frappe.prompt({
-			fieldtype:"Link",
+			fieldtype: "Link",
 			options: "Company",
-			label:__("Company"),
-			fieldname:"company",
-			reqd:1
+			label: __("Company"),
+			fieldname: "company",
+			reqd: 1
 		}, (data) => {
 			me.company = data.company;
-			frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {token: token, response: response})
-				.then((result) => {
-					frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {response: response,
-						bank: result, company: me.company});
-				})
-				.then(() => {
-					frappe.show_alert({message:__("Bank accounts added"), indicator:'green'});
+			frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {
+				token: token,
+				response: response
+			}).then((result) => {
+				frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {
+					response: response,
+					bank: result,
+					company: me.company
 				});
+			}).then(() => {
+				frappe.show_alert({ message: __("Bank accounts added"), indicator: 'green' });
+			});
 		}, __("Select a company"), __("Continue"));
 	}
 };
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
index d8203d7..2706217 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json
@@ -1,5 +1,4 @@
 {
- "actions": [],
  "creation": "2018-10-25 10:02:48.656165",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -12,7 +11,6 @@
   "plaid_client_id",
   "plaid_secret",
   "column_break_7",
-  "plaid_public_key",
   "plaid_env"
  ],
  "fields": [
@@ -42,12 +40,6 @@
    "label": "Plaid Secret"
   },
   {
-   "fieldname": "plaid_public_key",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Plaid Public Key"
-  },
-  {
    "fieldname": "plaid_env",
    "fieldtype": "Select",
    "in_list_view": 1,
@@ -69,8 +61,7 @@
   }
  ],
  "issingle": 1,
- "links": [],
- "modified": "2020-02-07 15:21:11.616231",
+ "modified": "2020-09-12 02:31:44.542385",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "Plaid Settings",
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index c3371ed..e535e81 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -2,30 +2,36 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-from __future__ import unicode_literals
-import frappe
 import json
-from frappe import _
-from frappe.model.document import Document
+
+import frappe
 from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
 from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
-from frappe.utils import getdate, formatdate, today, add_months
+from frappe import _
 from frappe.desk.doctype.tag.tag import add_tag
+from frappe.model.document import Document
+from frappe.utils import add_months, formatdate, getdate, today
+
 
 class PlaidSettings(Document):
-	pass
+	@staticmethod
+	def get_link_token():
+		plaid = PlaidConnector()
+		return plaid.get_link_token()
+
 
 @frappe.whitelist()
-def plaid_configuration():
+def get_plaid_configuration():
 	if frappe.db.get_single_value("Plaid Settings", "enabled"):
 		plaid_settings = frappe.get_single("Plaid Settings")
 		return {
-			"plaid_public_key": plaid_settings.plaid_public_key,
 			"plaid_env": plaid_settings.plaid_env,
+			"link_token": plaid_settings.get_link_token(),
 			"client_name": frappe.local.site
 		}
-	else:
-		return "disabled"
+
+	return "disabled"
+
 
 @frappe.whitelist()
 def add_institution(token, response):
@@ -33,6 +39,7 @@
 
 	plaid = PlaidConnector()
 	access_token = plaid.get_access_token(token)
+	bank = None
 
 	if not frappe.db.exists("Bank", response["institution"]["name"]):
 		try:
@@ -44,7 +51,6 @@
 			bank.insert()
 		except Exception:
 			frappe.throw(frappe.get_traceback())
-
 	else:
 		bank = frappe.get_doc("Bank", response["institution"]["name"])
 		bank.plaid_access_token = access_token
@@ -52,6 +58,7 @@
 
 	return bank
 
+
 @frappe.whitelist()
 def add_bank_accounts(response, bank, company):
 	try:
@@ -92,9 +99,8 @@
 				new_account.insert()
 
 				result.append(new_account.name)
-
 			except frappe.UniqueValidationError:
-				frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(new_account.account_name))
+				frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
 			except Exception:
 				frappe.throw(frappe.get_traceback())
 
@@ -103,6 +109,7 @@
 
 	return result
 
+
 def add_account_type(account_type):
 	try:
 		frappe.get_doc({
@@ -122,10 +129,11 @@
 	except Exception:
 		frappe.throw(frappe.get_traceback())
 
+
 @frappe.whitelist()
 def sync_transactions(bank, bank_account):
-	'''Sync transactions based on the last integration date as the start date, after the sync is completed
-		add the transaction date of the oldest transaction as the last integration date'''
+	"""Sync transactions based on the last integration date as the start date, after sync is completed
+	add the transaction date of the oldest transaction as the last integration date."""
 	last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
 	if last_transaction_date:
 		start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
@@ -147,10 +155,10 @@
 				len(result), bank_account, start_date, end_date))
 
 			frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
-
 	except Exception:
 		frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
 
+
 def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
 	access_token = None
 
@@ -168,6 +176,7 @@
 
 	return transactions
 
+
 def new_bank_transaction(transaction):
 	result = []
 
@@ -182,8 +191,8 @@
 
 	status = "Pending" if transaction["pending"] == "True" else "Settled"
 
+	tags = []
 	try:
-		tags  = []
 		tags += transaction["category"]
 		tags += ["Plaid Cat. {}".format(transaction["category_id"])]
 	except KeyError:
@@ -216,6 +225,7 @@
 
 	return result
 
+
 def automatic_synchronization():
 	settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
 
@@ -223,4 +233,8 @@
 		plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
 
 		for plaid_account in plaid_accounts:
-			frappe.enqueue("erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", bank=plaid_account.bank, bank_account=plaid_account.name)
+			frappe.enqueue(
+				"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
+				bank=plaid_account.bank,
+				bank_account=plaid_account.name
+			)
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
index 1a063d6..3c90637 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
@@ -1,14 +1,17 @@
 # -*- coding: utf-8 -*-
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
-from __future__ import unicode_literals
 
-import unittest
-import frappe
-from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import plaid_configuration, add_account_type, add_account_subtype, new_bank_transaction, add_bank_accounts
 import json
-from frappe.utils.response import json_handler
+import unittest
+
+import frappe
 from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
+from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import (
+	add_account_subtype, add_account_type, add_bank_accounts,
+	new_bank_transaction, get_plaid_configuration)
+from frappe.utils.response import json_handler
+
 
 class TestPlaidSettings(unittest.TestCase):
 	def setUp(self):
@@ -31,7 +34,7 @@
 
 	def test_plaid_disabled(self):
 		frappe.db.set_value("Plaid Settings", None, "enabled", 0)
-		self.assertTrue(plaid_configuration() == "disabled")
+		self.assertTrue(get_plaid_configuration() == "disabled")
 
 	def test_add_account_type(self):
 		add_account_type("brokerage")
@@ -64,7 +67,7 @@
 				'mask': '0000',
 				'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
 				'name': 'Plaid Checking'
-				}],
+			}],
 			'institution': {
 				'institution_id': 'ins_6',
 				'name': 'Citi'
@@ -100,7 +103,7 @@
 				'mask': '0000',
 				'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
 				'name': 'Plaid Checking'
-				}],
+			}],
 			'institution': {
 				'institution_id': 'ins_6',
 				'name': 'Citi'
@@ -152,4 +155,4 @@
 
 		new_bank_transaction(transactions)
 
-		self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
\ No newline at end of file
+		self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
index 5339c99..2e10751 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
@@ -258,8 +258,8 @@
   }
  ],
  "issingle": 1,
- "modified": "2020-05-28 12:32:11.384757",
- "modified_by": "umair@erpnext.com",
+ "modified": "2020-09-18 17:26:09.703215",
+ "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "Shopify Settings",
  "owner": "Administrator",
diff --git a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json
index 9faa5b2..cf54e82 100644
--- a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json
+++ b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json
@@ -117,12 +117,12 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2018-05-08 13:45:57.226530", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Healthcare", 
  "name": "Healthcare Schedule Time Slot", 
  "name_case": "", 
- "owner": "rmehta@gmail.com", 
+ "owner": "Administrator", 
  "permissions": [], 
  "quick_entry": 1, 
  "read_only": 0, 
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js
index 8036c7d..f1634c1 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.js
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.js
@@ -34,10 +34,10 @@
 			if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') {
 				frm.add_custom_button(__('Approve'), function () {
 					status_update(1, frm);
-				});
+				}, __('Actions'));
 				frm.add_custom_button(__('Reject'), function () {
 					status_update(0, frm);
-				});
+				}, __('Actions'));
 			}
 		}
 
@@ -179,23 +179,6 @@
 	d.show();
 };
 
-cur_frm.cscript.custom_before_submit = function (doc) {
-	if (doc.normal_test_items) {
-		for (let result in doc.normal_test_items) {
-			if (!doc.normal_test_items[result].result_value && !doc.normal_test_items[result].allow_blank && doc.normal_test_items[result].require_result_value) {
-				frappe.throw(__('Please input all required result values'));
-			}
-		}
-	}
-	if (doc.descriptive_test_items) {
-		for (let result in doc.descriptive_test_items) {
-			if (!doc.descriptive_test_items[result].result_value && !doc.descriptive_test_items[result].allow_blank && doc.descriptive_test_items[result].require_result_value) {
-				frappe.throw(__('Please input all required result values'));
-			}
-		}
-	}
-};
-
 var make_dialog = function (frm, emailed, printed) {
 	var number = frm.doc.mobile;
 
@@ -203,7 +186,7 @@
 		title: 'Send SMS',
 		width: 400,
 		fields: [
-			{ fieldname: 'sms_type', fieldtype: 'Select', label: 'Type', options: ['Emailed', 'Printed'] },
+			{ fieldname: 'result_format', fieldtype: 'Select', label: 'Result Format', options: ['Emailed', 'Printed'] },
 			{ fieldname: 'number', fieldtype: 'Data', label: 'Mobile Number', reqd: 1 },
 			{ fieldname: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 }
 		],
@@ -217,22 +200,22 @@
 			dialog.hide();
 		}
 	});
-	if (frm.doc.report_preference == 'Print') {
+	if (frm.doc.report_preference === 'Print') {
 		dialog.set_values({
-			'sms_type': 'Printed',
+			'result_format': 'Printed',
 			'number': number,
 			'message': printed
 		});
 	} else {
 		dialog.set_values({
-			'sms_type': 'Emailed',
+			'result_format': 'Emailed',
 			'number': number,
 			'message': emailed
 		});
 	}
 	var fd = dialog.fields_dict;
-	$(fd.sms_type.input).change(function () {
-		if (dialog.get_value('sms_type') == 'Emailed') {
+	$(fd.result_format.input).change(function () {
+		if (dialog.get_value('result_format') === 'Emailed') {
 			dialog.set_values({
 				'number': number,
 				'message': emailed
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json
index 2eb8014..edf1d91 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.json
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.json
@@ -84,7 +84,7 @@
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "label": "Series",
-   "options": "LP-",
+   "options": "HLC-LAB-.YYYY.-",
    "print_hide": 1,
    "report_hide": 1,
    "reqd": 1
@@ -197,11 +197,10 @@
   {
    "fieldname": "status",
    "fieldtype": "Select",
+   "in_list_view": 1,
    "label": "Status",
    "options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
-   "print_hide": 1,
    "read_only": 1,
-   "report_hide": 1,
    "search_index": 1
   },
   {
@@ -249,8 +248,8 @@
   {
    "fieldname": "result_date",
    "fieldtype": "Date",
-   "hidden": 1,
    "label": "Result Date",
+   "read_only": 1,
    "search_index": 1
   },
   {
@@ -354,7 +353,8 @@
   },
   {
    "fieldname": "sb_normal",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Compound Test Result"
   },
   {
    "fieldname": "normal_test_items",
@@ -369,11 +369,13 @@
   {
    "depends_on": "descriptive_toggle",
    "fieldname": "organisms_section",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Organism Test Result"
   },
   {
    "fieldname": "sb_sensitivity",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Sensitivity Test Result"
   },
   {
    "fieldname": "sensitivity_test_items",
@@ -383,8 +385,10 @@
    "report_hide": 1
   },
   {
+   "collapsible": 1,
    "fieldname": "sb_comments",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Comments"
   },
   {
    "fieldname": "lab_test_comment",
@@ -531,7 +535,8 @@
   },
   {
    "fieldname": "sb_descriptive",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Descriptive Test Result"
   },
   {
    "default": "0",
@@ -550,7 +555,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-07-16 13:35:24.811062",
+ "modified": "2020-07-30 18:18:38.516215",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Lab Test",
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py
index 865f4a1..2db7743 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.py
@@ -6,23 +6,24 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import getdate, cstr
+from frappe.utils import getdate, cstr, get_link_to_form
 
 class LabTest(Document):
+	def validate(self):
+		if not self.is_new():
+			self.set_secondary_uom_result()
+
 	def on_submit(self):
+		self.validate_result_values()
 		self.db_set('submitted_date', getdate())
 		self.db_set('status', 'Completed')
 		insert_lab_test_to_medical_record(self)
 
 	def on_cancel(self):
-		delete_lab_test_from_medical_record(self)
 		self.db_set('status', 'Cancelled')
+		delete_lab_test_from_medical_record(self)
 		self.reload()
 
-	def validate(self):
-		if not self.is_new():
-			self.set_secondary_uom_result()
-
 	def on_update(self):
 		if self.sensitivity_test_items:
 			sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity)
@@ -51,7 +52,20 @@
 					item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
 				except:
 					item.secondary_uom_result = ''
-					frappe.msgprint(_('Result for Secondary UOM not calculated for row #{0}'.format(item.idx)), title = _('Warning'))
+					frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning'))
+
+	def validate_result_values(self):
+		if self.normal_test_items:
+			for item in self.normal_test_items:
+				if not item.result_value and not item.allow_blank and item.require_result_value:
+					frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
+						item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results'))
+
+		if self.descriptive_test_items:
+			for item in self.descriptive_test_items:
+				if not item.result_value and not item.allow_blank and item.require_result_value:
+					frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
+						item.idx, frappe.bold(item.lab_test_particulars)), title=_('Mandatory Results'))
 
 
 def create_test_from_template(lab_test):
@@ -89,7 +103,7 @@
 		lab_test_created = create_lab_test_from_encounter(docname)
 
 	if lab_test_created:
-		frappe.msgprint(_('Lab Test(s) {0} created'.format(lab_test_created)))
+		frappe.msgprint(_('Lab Test(s) {0} created successfully').format(lab_test_created), indicator='green')
 	else:
 		frappe.msgprint(_('No Lab Tests created'))
 
@@ -211,8 +225,9 @@
 			'docstatus': 0,
 			'sample': template.sample
 		})
+
 		if sample_exists:
-			# Update Sample Collection by adding quantity
+			# update sample collection by adding quantity
 			sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0])
 			quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
 			if template.sample_details:
@@ -238,7 +253,7 @@
 			sample_collection.company = company
 
 			if template.sample_details:
-				sample_collection.sample_details = 'Test :' + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
+				sample_collection.sample_details = _('Test :') + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
 			sample_collection.save(ignore_permissions=True)
 
 		return sample_collection
@@ -248,26 +263,31 @@
 		sample_collection = create_sample_doc(template, patient, invoice, lab_test.company)
 		if sample_collection:
 			lab_test.sample = sample_collection.name
-
+			sample_collection_doc = get_link_to_form('Sample Collection', sample_collection.name)
+			frappe.msgprint(_('Sample Collection {0} has been created').format(sample_collection_doc),
+				title=_('Sample Collection'), indicator='green')
 	return lab_test
 
 def load_result_format(lab_test, template, prescription, invoice):
 	if template.lab_test_template_type == 'Single':
 		create_normals(template, lab_test)
+
 	elif template.lab_test_template_type == 'Compound':
 		create_compounds(template, lab_test, False)
+
 	elif template.lab_test_template_type == 'Descriptive':
 		create_descriptives(template, lab_test)
+
 	elif template.lab_test_template_type == 'Grouped':
 		# Iterate for each template in the group and create one result for all.
 		for lab_test_group in template.lab_test_groups:
 			# Template_in_group = None
 			if lab_test_group.lab_test_template:
-				template_in_group = frappe.get_doc('Lab Test Template',
-								lab_test_group.lab_test_template)
+				template_in_group = frappe.get_doc('Lab Test Template', lab_test_group.lab_test_template)
 				if template_in_group:
 					if template_in_group.lab_test_template_type == 'Single':
 						create_normals(template_in_group, lab_test)
+
 					elif template_in_group.lab_test_template_type == 'Compound':
 						normal_heading = lab_test.append('normal_test_items')
 						normal_heading.lab_test_name = template_in_group.lab_test_name
@@ -275,6 +295,7 @@
 						normal_heading.allow_blank = 1
 						normal_heading.template = template_in_group.name
 						create_compounds(template_in_group, lab_test, True)
+
 					elif template_in_group.lab_test_template_type == 'Descriptive':
 						descriptive_heading = lab_test.append('descriptive_test_items')
 						descriptive_heading.lab_test_name = template_in_group.lab_test_name
@@ -282,6 +303,7 @@
 						descriptive_heading.allow_blank = 1
 						descriptive_heading.template = template_in_group.name
 						create_descriptives(template_in_group, lab_test)
+
 			else: # Lab Test Group - Add New Line
 				normal = lab_test.append('normal_test_items')
 				normal.lab_test_name = lab_test_group.group_event
@@ -292,6 +314,7 @@
 				normal.allow_blank = lab_test_group.allow_blank
 				normal.require_result_value = 1
 				normal.template = template.name
+
 	if template.lab_test_template_type != 'No Result':
 		if prescription:
 			lab_test.prescription = prescription
@@ -302,9 +325,10 @@
 
 @frappe.whitelist()
 def get_employee_by_user_id(user_id):
-	emp_id = frappe.db.get_value('Employee', { 'user_id': user_id })
-	employee = frappe.get_doc('Employee', emp_id)
-	return employee
+	emp_id = frappe.db.exists('Employee', { 'user_id': user_id })
+	if emp_id:
+		return frappe.get_doc('Employee', emp_id)
+	return None
 
 def insert_lab_test_to_medical_record(doc):
 	table_row = False
@@ -325,7 +349,7 @@
 			table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value
 
 		if item.normal_range:
-			table_row += ' ' + _('Normal Range:') + item.normal_range
+			table_row += ' ' + _('Normal Range: ') + item.normal_range
 		table_row += ' ' + comment
 
 	elif doc.descriptive_test_items:
@@ -356,7 +380,7 @@
 	medical_record.save(ignore_permissions = True)
 
 def delete_lab_test_from_medical_record(self):
-	medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name= %s', (self.name))
+	medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name))
 
 	if medical_record_id and medical_record_id[0][0]:
 		frappe.delete_doc('Patient Medical Record', medical_record_id[0][0])
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js
index b7f157c..7b5b9d9 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js
+++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js
@@ -3,13 +3,16 @@
 */
 frappe.listview_settings['Lab Test'] = {
 	add_fields: ['name', 'status', 'invoiced'],
-	filters: [['docstatus', '=', '0']],
+	filters: [['docstatus', '=', '1']],
 	get_indicator: function (doc) {
-		if (doc.status == 'Approved') {
-			return [__('Approved'), 'green', 'status, = ,Approved'];
-		}
-		if (doc.status == 'Rejected') {
+		if (doc.status === 'Approved') {
+			return [__('Approved'), 'green', 'status, =, Approved'];
+		} else if (doc.status === 'Rejected') {
 			return [__('Rejected'), 'orange', 'status, =, Rejected'];
+		} else if (doc.status === 'Completed') {
+			return [__('Completed'), 'green', 'status, =, Completed'];
+		} else if (doc.status === 'Cancelled') {
+			return [__('Cancelled'), 'red', 'status, =, Cancelled'];
 		}
 	},
 	onload: function (listview) {
@@ -21,7 +24,7 @@
 
 var create_multiple_dialog = function (listview) {
 	var dialog = new frappe.ui.Dialog({
-		title: 'Create Multiple Lab Test',
+		title: 'Create Multiple Lab Tests',
 		width: 100,
 		fields: [
 			{ fieldtype: 'Link', label: 'Patient', fieldname: 'patient', options: 'Patient', reqd: 1 },
@@ -41,7 +44,7 @@
 				}
 			}
 		],
-		primary_action_label: __('Create Lab Test'),
+		primary_action_label: __('Create'),
 		primary_action: function () {
 			frappe.call({
 				method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple',
diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
index 4131ad9..79ab8a4 100644
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
@@ -3,8 +3,204 @@
 # See license.txt
 from __future__ import unicode_literals
 import unittest
-
-# test_records = frappe.get_test_records('Lab Test')
+import frappe
+from frappe.utils import getdate, nowtime
+from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
+from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
+from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
+from erpnext.healthcare.doctype.patient_medical_record.test_patient_medical_record import create_lab_test_template as create_blood_test_template
 
 class TestLabTest(unittest.TestCase):
-	pass
+	def test_lab_test_item(self):
+		lab_template = create_lab_test_template()
+		self.assertTrue(frappe.db.exists('Item', lab_template.item))
+		self.assertEqual(frappe.db.get_value('Item Price', {'item_code':lab_template.item}, 'price_list_rate'), lab_template.lab_test_rate)
+
+		lab_template.disabled = 1
+		lab_template.save()
+		self.assertEquals(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1)
+
+		lab_template.reload()
+
+		lab_template.disabled = 0
+		lab_template.save()
+
+	def test_descriptive_lab_test(self):
+		lab_template = create_lab_test_template()
+
+		# blank result value not allowed as per template
+		lab_test = create_lab_test(lab_template)
+		lab_test.descriptive_test_items[0].result_value = 12
+		lab_test.descriptive_test_items[2].result_value = 1
+		lab_test.save()
+		self.assertRaises(frappe.ValidationError, lab_test.submit)
+
+	def test_sample_collection(self):
+		frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 1)
+		lab_template = create_lab_test_template()
+
+		lab_test = create_lab_test(lab_template)
+		lab_test.descriptive_test_items[0].result_value = 12
+		lab_test.descriptive_test_items[1].result_value = 1
+		lab_test.descriptive_test_items[2].result_value = 2.3
+		lab_test.save()
+
+		# check sample collection created
+		self.assertTrue(frappe.db.exists('Sample Collection', {'sample': lab_template.sample}))
+
+		frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 0)
+		lab_test = create_lab_test(lab_template)
+		lab_test.descriptive_test_items[0].result_value = 12
+		lab_test.descriptive_test_items[1].result_value = 1
+		lab_test.descriptive_test_items[2].result_value = 2.3
+		lab_test.save()
+
+		# sample collection should not be created
+		lab_test.reload()
+		self.assertEquals(lab_test.sample, None)
+
+	def test_create_lab_tests_from_sales_invoice(self):
+		sales_invoice = create_sales_invoice()
+		create_multiple('Sales Invoice', sales_invoice.name)
+		sales_invoice.reload()
+		self.assertIsNotNone(sales_invoice.items[0].reference_dn)
+		self.assertIsNotNone(sales_invoice.items[1].reference_dn)
+
+	def test_create_lab_tests_from_patient_encounter(self):
+		patient_encounter = create_patient_encounter()
+		create_multiple('Patient Encounter', patient_encounter.name)
+		patient_encounter.reload()
+		self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
+		self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
+
+
+def create_lab_test_template(test_sensitivity=0, sample_collection=1):
+	medical_department = create_medical_department()
+	if frappe.db.exists('Lab Test Template', 'Insulin Resistance'):
+		return frappe.get_doc('Lab Test Template', 'Insulin Resistance')
+	template = frappe.new_doc('Lab Test Template')
+	template.lab_test_name = 'Insulin Resistance'
+	template.lab_test_template_type = 'Descriptive'
+	template.lab_test_code = 'Insulin Resistance'
+	template.lab_test_group = 'Services'
+	template.department = medical_department
+	template.is_billable = 1
+	template.lab_test_description = 'Insulin Resistance'
+	template.lab_test_rate = 2000
+
+	for entry in ['FBS', 'Insulin', 'IR']:
+		template.append('descriptive_test_templates', {
+			'particulars': entry,
+			'allow_blank': 1 if entry=='IR' else 0
+		})
+
+	if test_sensitivity:
+		template.sensitivity = 1
+
+	if sample_collection:
+		template.sample = create_lab_test_sample()
+		template.sample_qty = 5.0
+
+	template.save()
+	return template
+
+def create_medical_department():
+	medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
+	if not medical_department:
+		medical_department = frappe.new_doc('Medical Department')
+		medical_department.department = '_Test Medical Department'
+		medical_department.save()
+		medical_department = medical_department.name
+
+	return medical_department
+
+def create_lab_test(lab_template):
+	patient = create_patient()
+	lab_test = frappe.new_doc('Lab Test')
+	lab_test.template = lab_template.name
+	lab_test.patient = patient
+	lab_test.patient_sex = 'Female'
+	lab_test.save()
+
+	return lab_test
+
+def create_lab_test_sample():
+	blood_sample = frappe.db.exists('Lab Test Sample', 'Blood Sample')
+	if blood_sample:
+		return blood_sample
+
+	sample = frappe.new_doc('Lab Test Sample')
+	sample.sample = 'Blood Sample'
+	sample.sample_uom = 'U/ml'
+	sample.save()
+
+	return sample.name
+
+def create_sales_invoice():
+	patient = create_patient()
+	medical_department = create_medical_department()
+	insulin_resistance_template = create_lab_test_template()
+	blood_test_template = create_blood_test_template(medical_department)
+
+	sales_invoice = frappe.new_doc('Sales Invoice')
+	sales_invoice.patient = patient
+	sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer')
+	sales_invoice.due_date = getdate()
+	sales_invoice.company = '_Test Company'
+	sales_invoice.debit_to = get_receivable_account('_Test Company')
+
+	tests = [insulin_resistance_template, blood_test_template]
+	for entry in tests:
+		sales_invoice.append('items', {
+			'item_code': entry.item,
+			'item_name': entry.lab_test_name,
+			'description': entry.lab_test_description,
+			'qty': 1,
+			'uom': 'Nos',
+			'conversion_factor': 1,
+			'income_account': get_income_account(None, '_Test Company'),
+			'rate': entry.lab_test_rate,
+			'amount': entry.lab_test_rate
+		})
+
+	sales_invoice.set_missing_values()
+
+	sales_invoice.submit()
+	return sales_invoice
+
+def create_patient_encounter():
+	patient = create_patient()
+	medical_department = create_medical_department()
+	insulin_resistance_template = create_lab_test_template()
+	blood_test_template = create_blood_test_template(medical_department)
+
+	patient_encounter = frappe.new_doc('Patient Encounter')
+	patient_encounter.patient = patient
+	patient_encounter.practitioner = create_practitioner()
+	patient_encounter.encounter_date = getdate()
+	patient_encounter.encounter_time = nowtime()
+
+	tests = [insulin_resistance_template, blood_test_template]
+	for entry in tests:
+		patient_encounter.append('lab_test_prescription', {
+			'lab_test_code': entry.item,
+			'lab_test_name': entry.lab_test_name
+		})
+
+	patient_encounter.submit()
+	return patient_encounter
+
+
+def create_practitioner():
+	practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
+
+	if not practitioner:
+		practitioner = frappe.new_doc('Healthcare Practitioner')
+		practitioner.first_name = '_Test Healthcare Practitioner'
+		practitioner.gender = 'Female'
+		practitioner.op_consulting_charge = 500
+		practitioner.inpatient_visit_charge = 500
+		practitioner.save(ignore_permissions=True)
+		practitioner = practitioner.name
+
+	return practitioner
diff --git a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
index beea7a3..2767f7e 100644
--- a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
+++ b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
@@ -93,7 +93,8 @@
    "depends_on": "secondary_uom",
    "fieldname": "conversion_factor",
    "fieldtype": "Float",
-   "label": "Conversion Factor"
+   "label": "Conversion Factor",
+   "mandatory_depends_on": "secondary_uom"
   },
   {
    "default": "0",
@@ -106,7 +107,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-06-24 10:59:01.921924",
+ "modified": "2020-07-30 12:36:03.082391",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Lab Test Group Template",
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
index db64297..c3fc842 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
@@ -34,14 +34,15 @@
   "descriptive_test_templates",
   "section_break_group",
   "lab_test_groups",
-  "medical_coding_section",
-  "medical_code_standard",
-  "medical_code",
   "sb_sample_collection",
   "sample",
   "sample_uom",
   "sample_qty",
+  "column_break_33",
   "sample_details",
+  "medical_coding_section",
+  "medical_code",
+  "medical_code_standard",
   "worksheet_section",
   "worksheet_instructions",
   "result_legend_section",
@@ -112,7 +113,7 @@
   {
    "default": "1",
    "depends_on": "eval:doc.lab_test_template_type != 'Grouped'",
-   "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ",
+   "description": "If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ",
    "fieldname": "is_billable",
    "fieldtype": "Check",
    "label": "Is Billable",
@@ -128,6 +129,7 @@
    "mandatory_depends_on": "eval:doc.is_billable == 1"
   },
   {
+   "collapsible": 1,
    "fieldname": "medical_coding_section",
    "fieldtype": "Section Break",
    "label": "Medical Coding"
@@ -184,7 +186,7 @@
    "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'",
    "fieldname": "section_break_special",
    "fieldtype": "Section Break",
-   "label": "Descriptive"
+   "label": "Descriptive Test"
   },
   {
    "default": "0",
@@ -196,7 +198,7 @@
    "depends_on": "eval:doc.lab_test_template_type == 'Grouped'",
    "fieldname": "section_break_group",
    "fieldtype": "Section Break",
-   "label": "Group"
+   "label": "Group Tests"
   },
   {
    "fieldname": "lab_test_groups",
@@ -217,7 +219,6 @@
    "no_copy": 1
   },
   {
-   "collapsible": 1,
    "fieldname": "sb_sample_collection",
    "fieldtype": "Section Break",
    "label": "Sample Collection"
@@ -311,10 +312,14 @@
    "fieldname": "descriptive_test_templates",
    "fieldtype": "Table",
    "options": "Descriptive Test Template"
+  },
+  {
+   "fieldname": "column_break_33",
+   "fieldtype": "Column Break"
   }
  ],
  "links": [],
- "modified": "2020-07-13 12:57:09.925436",
+ "modified": "2020-07-30 14:32:40.449818",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Lab Test Template",
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
index 6f0d08c..543dee2 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
@@ -15,7 +15,8 @@
 
 	def validate(self):
 		if self.is_billable and (not self.lab_test_rate or self.lab_test_rate <= 0.0):
-			frappe.throw(_("Standard Selling Rate should be greater than zero."))
+			frappe.throw(_('Standard Selling Rate should be greater than zero.'))
+
 		self.validate_conversion_factor()
 		self.enable_disable_item()
 
@@ -42,7 +43,9 @@
 		# Remove template reference from item and disable item
 		if self.item:
 			try:
-				frappe.delete_doc('Item', self.item)
+				item = self.item
+				self.db_set('item', '')
+				frappe.delete_doc('Item', item)
 			except Exception:
 				frappe.throw(_('Not permitted. Please disable the Lab Test Template'))
 
@@ -63,26 +66,26 @@
 				'standard_rate': self.lab_test_rate,
 				'description': self.lab_test_description
 			})
-			item.save()
+			item.flags.ignore_mandatory = True
+			item.save(ignore_permissions=True)
 
 	def item_price_exists(self):
 		item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code})
 		if item_price:
 			return item_price[0][0]
-		else:
-			return False
+		return False
 
 	def validate_conversion_factor(self):
-		if self.lab_test_template_type == "Single" and self.secondary_uom and not self.conversion_factor:
-			frappe.throw(_("Conversion Factor is mandatory"))
-		if self.lab_test_template_type == "Compound":
+		if self.lab_test_template_type == 'Single' and self.secondary_uom and not self.conversion_factor:
+			frappe.throw(_('Conversion Factor is mandatory'))
+		if self.lab_test_template_type == 'Compound':
 			for item in self.normal_test_templates:
 				if item.secondary_uom and not item.conversion_factor:
-					frappe.throw(_("Conversion Factor is mandatory"))
-		if self.lab_test_template_type == "Grouped":
+					frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(item.idx))
+		if self.lab_test_template_type == 'Grouped':
 			for group in self.lab_test_groups:
-				if group.template_or_new_line == "Add New Line" and group.secondary_uom and not group.conversion_factor:
-					frappe.throw(_("Conversion Factor is mandatory"))
+				if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor:
+					frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(group.idx))
 
 
 def create_item_from_template(doc):
@@ -101,9 +104,9 @@
 		'include_item_in_manufacturing': 0,
 		'show_in_website': 0,
 		'is_pro_applicable': 0,
-		'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled, 
+		'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled,
 		'stock_uom': uom
-	}).insert(ignore_permissions = True, ignore_mandatory = True)
+	}).insert(ignore_permissions=True, ignore_mandatory=True)
 
 	# Insert item price
 	if doc.is_billable and doc.lab_test_rate != 0.0:
@@ -123,7 +126,7 @@
 		'price_list': price_list_name,
 		'item_code': item,
 		'price_list_rate': item_price
-	}).insert(ignore_permissions = True, ignore_mandatory = True)
+	}).insert(ignore_permissions=True, ignore_mandatory=True)
 
 @frappe.whitelist()
 def change_test_code_from_template(lab_test_code, doc):
@@ -132,8 +135,8 @@
 	if frappe.db.exists({'doctype': 'Item', 'item_code': lab_test_code}):
 		frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code))
 	else:
-		rename_doc('Item', doc.name, lab_test_code, ignore_permissions = True)
+		rename_doc('Item', doc.name, lab_test_code, ignore_permissions=True)
 		frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_code', lab_test_code)
 		frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_name', lab_test_code)
-		rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions = True)
+		rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions=True)
 	return lab_test_code
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py
new file mode 100644
index 0000000..94dfeea
--- /dev/null
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'template',
+		'transactions': [
+			{
+				'label': _('Lab Tests'),
+				'items': ['Lab Test']
+			}
+		]
+	}
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
index a3417eb..08fc2cd 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
@@ -3,5 +3,5 @@
 */
 frappe.listview_settings['Lab Test Template'] = {
 	add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'],
-	filters: [['disabled', '=', 0]]
+	filters: [['disabled', '=', 'No']]
 };
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index f7ed31b..2d6b645 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -226,7 +226,9 @@
 			primary_action_label: __('Book'),
 			primary_action: function() {
 				frm.set_value('appointment_time', selected_slot);
-				frm.set_value('duration', duration);
+				if (!frm.doc.duration) {
+					frm.set_value('duration', duration);
+				}
 				frm.set_value('practitioner', d.get_value('practitioner'));
 				frm.set_value('department', d.get_value('department'));
 				frm.set_value('appointment_date', d.get_value('appointment_date'));
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
index e5a5e4c..aa85a23 100644
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
+++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
@@ -34,7 +34,7 @@
 		self.assertTrue(medical_rec)
 
 		template = create_lab_test_template(medical_department)
-		lab_test = create_lab_test(template, patient)
+		lab_test = create_lab_test(template.name, patient)
 		# check for lab test
 		medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
 		self.assertTrue(medical_rec)
@@ -66,7 +66,7 @@
 
 def create_lab_test_template(medical_department):
 	if frappe.db.exists('Lab Test Template', 'Blood Test'):
-		return 'Blood Test'
+		return frappe.get_doc('Lab Test Template', 'Blood Test')
 
 	template = frappe.new_doc('Lab Test Template')
 	template.lab_test_name = 'Blood Test'
@@ -76,7 +76,7 @@
 	template.is_billable = 1
 	template.lab_test_rate = 2000
 	template.save()
-	return template.name
+	return template
 
 def create_lab_test(template, patient):
 	lab_test = frappe.new_doc('Lab Test')
diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json
index cff100c..a21825e 100644
--- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json
+++ b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json
@@ -44,11 +44,11 @@
   }
  ],
  "links": [],
- "modified": "2020-01-31 12:21:45.975488",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Practitioner Schedule",
- "owner": "rmehta@gmail.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "create": 1,
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.js b/erpnext/healthcare/doctype/sample_collection/sample_collection.js
index 2f5278b..0390391 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.js
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.js
@@ -3,29 +3,29 @@
 
 frappe.ui.form.on('Sample Collection', {
 	refresh: function(frm) {
-		if(frappe.defaults.get_default("create_sample_collection_for_lab_test")){
-			frm.add_custom_button(__("View Lab Tests"), function() {
-				frappe.route_options = {"sample": frm.doc.name};
-				frappe.set_route("List", "Lab Test");
+		if (frappe.defaults.get_default('create_sample_collection_for_lab_test')) {
+			frm.add_custom_button(__('View Lab Tests'), function() {
+				frappe.route_options = {'sample': frm.doc.name};
+				frappe.set_route('List', 'Lab Test');
 			});
 		}
 	}
 });
 
-frappe.ui.form.on("Sample Collection", "patient", function(frm) {
+frappe.ui.form.on('Sample Collection', 'patient', function(frm) {
 	if(frm.doc.patient){
 		frappe.call({
-			"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
+			'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
 			args: {
 				patient: frm.doc.patient
 			},
 			callback: function (data) {
 				var age = null;
-				if(data.message.dob){
+				if (data.message.dob){
 					age = calculate_age(data.message.dob);
 				}
-				frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age);
-				frappe.model.set_value(frm.doctype,frm.docname, "patient_sex", data.message.sex);
+				frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
+				frappe.model.set_value(frm.doctype,frm.docname, 'patient_sex', data.message.sex);
 			}
 		});
 	}
@@ -36,5 +36,5 @@
 	var	age = new Date();
 	age.setTime(ageMS);
 	var	years =  age.getFullYear() - 1970;
-	return  years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
+	return  years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
 };
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
index 016cfbc..83383e3 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
@@ -9,8 +9,10 @@
  "document_type": "Document",
  "engine": "InnoDB",
  "field_order": [
+  "patient_details_section",
   "naming_series",
   "patient",
+  "patient_name",
   "patient_age",
   "patient_sex",
   "column_break_4",
@@ -25,15 +27,17 @@
   "collected_by",
   "collected_time",
   "num_print",
-  "amended_from",
   "section_break_15",
-  "sample_details"
+  "sample_details",
+  "amended_from"
  ],
  "fields": [
   {
    "fetch_from": "patient.inpatient_record",
    "fieldname": "inpatient_record",
    "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "label": "Inpatient Record",
    "options": "Inpatient Record",
    "read_only": 1
@@ -42,6 +46,8 @@
    "bold": 1,
    "fieldname": "naming_series",
    "fieldtype": "Select",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "label": "Series",
    "no_copy": 1,
    "options": "HLC-SC-.YYYY.-",
@@ -52,6 +58,8 @@
    "default": "0",
    "fieldname": "invoiced",
    "fieldtype": "Check",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "label": "Invoiced",
    "no_copy": 1,
    "read_only": 1,
@@ -61,41 +69,60 @@
    "fetch_from": "inpatient_record.patient",
    "fieldname": "patient",
    "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "ignore_user_permissions": 1,
    "in_standard_filter": 1,
    "label": "Patient",
    "options": "Patient",
+   "reqd": 1,
    "search_index": 1
   },
   {
    "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "hide_days": 1,
+   "hide_seconds": 1
   },
   {
    "fieldname": "patient_age",
    "fieldtype": "Data",
-   "label": "Age"
+   "hide_days": 1,
+   "hide_seconds": 1,
+   "label": "Age",
+   "read_only": 1
   },
   {
    "fetch_from": "patient.sex",
    "fieldname": "patient_sex",
-   "fieldtype": "Data",
-   "label": "Gender"
+   "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
+   "label": "Gender",
+   "options": "Gender",
+   "read_only": 1
   },
   {
    "fieldname": "company",
    "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "in_standard_filter": 1,
    "label": "Company",
    "options": "Company"
   },
   {
    "fieldname": "section_break_6",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "hide_days": 1,
+   "hide_seconds": 1,
+   "label": "Sample Details"
   },
   {
    "fieldname": "sample",
    "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "ignore_user_permissions": 1,
    "in_list_view": 1,
    "in_standard_filter": 1,
@@ -108,16 +135,23 @@
    "fetch_from": "sample.sample_uom",
    "fieldname": "sample_uom",
    "fieldtype": "Data",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "in_list_view": 1,
-   "label": "UOM"
+   "label": "UOM",
+   "read_only": 1
   },
   {
    "fieldname": "column_break_10",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "hide_days": 1,
+   "hide_seconds": 1
   },
   {
    "fieldname": "collected_by",
    "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "ignore_user_permissions": 1,
    "label": "Collected By",
    "options": "User"
@@ -125,20 +159,27 @@
   {
    "fieldname": "collected_time",
    "fieldtype": "Datetime",
-   "label": "Collected Time"
+   "hide_days": 1,
+   "hide_seconds": 1,
+   "label": "Collected On"
   },
   {
    "allow_on_submit": 1,
    "default": "1",
+   "description": "Number of prints required for labelling the samples",
    "fieldname": "num_print",
    "fieldtype": "Int",
-   "label": "No. of print",
+   "hide_days": 1,
+   "hide_seconds": 1,
+   "label": "No. of prints",
    "print_hide": 1,
    "report_hide": 1
   },
   {
    "fieldname": "amended_from",
    "fieldtype": "Link",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "label": "Amended From",
    "no_copy": 1,
    "options": "Sample Collection",
@@ -147,25 +188,43 @@
   },
   {
    "fieldname": "section_break_15",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "hide_days": 1,
+   "hide_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "sample_qty",
    "fieldtype": "Float",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "in_list_view": 1,
    "label": "Quantity"
   },
   {
    "fieldname": "sample_details",
    "fieldtype": "Long Text",
+   "hide_days": 1,
+   "hide_seconds": 1,
    "ignore_xss_filter": 1,
    "label": "Collection Details"
+  },
+  {
+   "fieldname": "patient_details_section",
+   "fieldtype": "Section Break",
+   "label": "Patient Details"
+  },
+  {
+   "fetch_from": "patient.patient_name",
+   "fieldname": "patient_name",
+   "fieldtype": "Data",
+   "label": "Patient Name",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-25 14:36:46.990469",
+ "modified": "2020-07-30 16:53:13.076104",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Sample Collection",
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.py b/erpnext/healthcare/doctype/sample_collection/sample_collection.py
index 2c64320..461f809 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.py
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.py
@@ -3,7 +3,12 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
+import frappe
 from frappe.model.document import Document
+from frappe.utils import flt
+from frappe import _
 
 class SampleCollection(Document):
-	pass
+	def validate(self):
+		if flt(self.sample_qty) <= 0:
+			frappe.throw(_('Sample Quantity cannot be negative or 0'), title=_('Invalid Quantity'))
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.js b/erpnext/healthcare/report/lab_test_report/lab_test_report.js
index 3128f81..7754e2e 100644
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.js
+++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.js
@@ -4,29 +4,54 @@
 frappe.query_reports["Lab Test Report"] = {
 	"filters": [
 		{
-			"fieldname":"from_date",
+			"fieldname": "from_date",
 			"label": __("From Date"),
 			"fieldtype": "Date",
-			"default": frappe.datetime.now_date(),
-			"width": "80"
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+			"reqd": 1
 		},
 		{
-			"fieldname":"to_date",
+			"fieldname": "to_date",
 			"label": __("To Date"),
 			"fieldtype": "Date",
-			"default": frappe.datetime.now_date()
+			"default": frappe.datetime.now_date(),
+			"reqd": 1
 		},
 		{
-			"fieldname":"patient",
+			"fieldname": "company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"default": frappe.defaults.get_default("Company"),
+			"options": "Company"
+		},
+		{
+			"fieldname": "template",
+			"label": __("Lab Test Template"),
+			"fieldtype": "Link",
+			"options": "Lab Test Template"
+		},
+		{
+			"fieldname": "patient",
 			"label": __("Patient"),
 			"fieldtype": "Link",
 			"options": "Patient"
 		},
 		{
-			"fieldname":"department",
+			"fieldname": "department",
 			"label": __("Medical Department"),
 			"fieldtype": "Link",
 			"options": "Medical Department"
+		},
+		{
+			"fieldname": "status",
+			"label": __("Status"),
+			"fieldtype": "Select",
+			"options": "\nCompleted\nApproved\nRejected"
+		},
+		{
+			"fieldname": "invoiced",
+			"label": __("Invoiced"),
+			"fieldtype": "Check"
 		}
 	]
 };
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.json b/erpnext/healthcare/report/lab_test_report/lab_test_report.json
index 30e5a5f..aeb4289 100644
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.json
+++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.json
@@ -1,30 +1,31 @@
 {
- "add_total_row": 1, 
- "creation": "2013-04-23 18:15:29", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 1, 
- "is_standard": "Yes", 
- "modified": "2018-08-06 11:41:50.218737", 
- "modified_by": "Administrator", 
- "module": "Healthcare", 
- "name": "Lab Test Report", 
- "owner": "Administrator", 
- "prepared_report": 0, 
- "ref_doctype": "Lab Test", 
- "report_name": "Lab Test Report", 
- "report_type": "Script Report", 
+ "add_total_row": 0,
+ "creation": "2013-04-23 18:15:29",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 1,
+ "is_standard": "Yes",
+ "modified": "2020-07-30 18:53:20.102873",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Lab Test Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Lab Test",
+ "report_name": "Lab Test Report",
+ "report_type": "Script Report",
  "roles": [
   {
    "role": "Laboratory User"
-  }, 
+  },
   {
    "role": "Nursing User"
-  }, 
+  },
   {
    "role": "LabTest Approver"
-  }, 
+  },
   {
    "role": "Healthcare Administrator"
   }
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py
index 17f25fa..2e59bed 100644
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py
+++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py
@@ -8,51 +8,204 @@
 def execute(filters=None):
 	if not filters: filters = {}
 
-	lab_test_list = get_lab_test(filters)
+	data, columns = [], []
+
 	columns = get_columns()
+	lab_test_list = get_lab_tests(filters)
 
 	if not lab_test_list:
-		msgprint(_("No record found"))
+		msgprint(_('No records found'))
 		return columns, lab_test_list
 
 	data = []
 	for lab_test in lab_test_list:
-		row = [ lab_test.lab_test_name, lab_test.patient, lab_test.practitioner, lab_test.invoiced, lab_test.status, lab_test.result_date, lab_test.department]
+		row = frappe._dict({
+				'test': lab_test.name,
+				'template': lab_test.template,
+				'company': lab_test.company,
+				'patient': lab_test.patient,
+				'patient_name': lab_test.patient_name,
+				'practitioner': lab_test.practitioner,
+				'employee': lab_test.employee,
+				'status': lab_test.status,
+				'invoiced': lab_test.invoiced,
+				'result_date': lab_test.result_date,
+				'department': lab_test.department
+			})
 		data.append(row)
 
-	return columns, data
+	chart = get_chart_data(data)
+	report_summary = get_report_summary(data)
+	return columns, data, None, chart, report_summary
 
 
 def get_columns():
-	columns = [
-		_("Test") + ":Data:120",
-		_("Patient") + ":Link/Patient:180",
-		_("Healthcare Practitioner") + ":Link/Healthcare Practitioner:120",
-		_("Invoiced") + ":Check:100",
-		_("Status") + ":Data:120",
-		_("Result Date") + ":Date:120",
-		_("Department") + ":Data:120",
+	return [
+		{
+			'fieldname': 'test',
+			'label': _('Lab Test'),
+			'fieldtype': 'Link',
+			'options': 'Lab Test',
+			'width': '120'
+		},
+		{
+			'fieldname': 'template',
+			'label': _('Lab Test Template'),
+			'fieldtype': 'Link',
+			'options': 'Lab Test Template',
+			'width': '120'
+		},
+		{
+			'fieldname': 'company',
+			'label': _('Company'),
+			'fieldtype': 'Link',
+			'options': 'Company',
+			'width': '120'
+		},
+		{
+			'fieldname': 'patient',
+			'label': _('Patient'),
+			'fieldtype': 'Link',
+			'options': 'Patient',
+			'width': '120'
+		},
+		{
+			'fieldname': 'patient_name',
+			'label': _('Patient Name'),
+			'fieldtype': 'Data',
+			'width': '120'
+		},
+		{
+			'fieldname': 'employee',
+			'label': _('Lab Technician'),
+			'fieldtype': 'Link',
+			'options': 'Employee',
+			'width': '120'
+		},
+		{
+			'fieldname': 'status',
+			'label': _('Status'),
+			'fieldtype': 'Data',
+			'width': '100'
+		},
+		{
+			'fieldname': 'invoiced',
+			'label': _('Invoiced'),
+			'fieldtype': 'Check',
+			'width': '100'
+		},
+		{
+			'fieldname': 'result_date',
+			'label': _('Result Date'),
+			'fieldtype': 'Date',
+			'width': '100'
+		},
+		{
+			'fieldname': 'practitioner',
+			'label': _('Requesting Practitioner'),
+			'fieldtype': 'Link',
+			'options': 'Healthcare Practitioner',
+			'width': '120'
+		},
+		{
+			'fieldname': 'department',
+			'label': _('Medical Department'),
+			'fieldtype': 'Link',
+			'options': 'Medical Department',
+			'width': '100'
+		}
 	]
 
-	return columns
+def get_lab_tests(filters):
+	conditions = get_conditions(filters)
+	data = frappe.get_all(
+		doctype='Lab Test',
+		fields=['name', 'template', 'company', 'patient', 'patient_name', 'practitioner', 'employee', 'status', 'invoiced', 'result_date', 'department'],
+		filters=conditions,
+		order_by='submitted_date desc'
+	)
+	return data
 
 def get_conditions(filters):
-	conditions = ""
+	conditions = {
+		'docstatus': ('=', 1)
+	}
 
-	if filters.get("patient"):
-		conditions += "and patient = %(patient)s"
-	if filters.get("from_date"):
-		conditions += "and result_date >= %(from_date)s"
-	if filters.get("to_date"):
-		conditions += " and result_date <= %(to_date)s"
-	if filters.get("department"):
-		conditions += " and department = %(department)s"
+	if filters.get('from_date') and filters.get('to_date'):
+		conditions['result_date'] = ('between', (filters.get('from_date'), filters.get('to_date')))
+		filters.pop('from_date')
+		filters.pop('to_date')
+
+	for key, value in filters.items():
+		if filters.get(key):
+			conditions[key] = value
 
 	return conditions
 
-def get_lab_test(filters):
-	conditions = get_conditions(filters)
-	return frappe.db.sql("""select name, patient, lab_test_name, patient_name, status, result_date, practitioner, invoiced, department
-		from `tabLab Test`
-		where docstatus<2 %s order by submitted_date desc, name desc""" %
-		conditions, filters, as_dict=1)
+def get_chart_data(data):
+	if not data:
+		return None
+
+	labels = ['Completed', 'Approved', 'Rejected']
+
+	status_wise_data = {
+		'Completed': 0,
+		'Approved': 0,
+		'Rejected': 0
+	}
+
+	datasets = []
+
+	for entry in data:
+		status_wise_data[entry.status] += 1
+
+	datasets.append({
+		'name': 'Lab Test Status',
+		'values': [status_wise_data.get('Completed'), status_wise_data.get('Approved'), status_wise_data.get('Rejected')]
+	})
+
+	chart = {
+		'data': {
+			'labels': labels,
+			'datasets': datasets
+		},
+		'type': 'donut',
+		'height': 300,
+	}
+
+	return chart
+
+
+def get_report_summary(data):
+	if not data:
+		return None
+
+	total_lab_tests = len(data)
+	invoiced_lab_tests, unbilled_lab_tests = 0, 0
+
+	for entry in data:
+		if entry.invoiced:
+			invoiced_lab_tests += 1
+		else:
+			unbilled_lab_tests += 1
+
+	return [
+		{
+			'value': total_lab_tests,
+			'indicator': 'Blue',
+			'label': 'Total Lab Tests',
+			'datatype': 'Int',
+		},
+		{
+			'value': invoiced_lab_tests,
+			'indicator': 'Green',
+			'label': 'Invoiced Lab Tests',
+			'datatype': 'Int',
+		},
+		{
+			'value': unbilled_lab_tests,
+			'indicator': 'Red',
+			'label': 'Unbilled Lab Tests',
+			'datatype': 'Int',
+		}
+	]
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index b71c465..cbd3adf 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -282,6 +282,11 @@
 ]
 
 scheduler_events = {
+	"cron": {
+		"0/30 * * * *": [
+			"erpnext.utilities.doctype.video.video.update_youtube_data",
+		]
+	},
 	"all": [
 		"erpnext.projects.doctype.project.project.project_status_update_reminder",
 		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder",
diff --git a/erpnext/hr/doctype/appraisal/appraisal.json b/erpnext/hr/doctype/appraisal/appraisal.json
index 4f6da97..91201d4 100644
--- a/erpnext/hr/doctype/appraisal/appraisal.json
+++ b/erpnext/hr/doctype/appraisal/appraisal.json
@@ -696,11 +696,11 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2019-01-30 11:28:08.401459", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Appraisal", 
- "owner": "ashwini@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 0, 
diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json
index f22969b..b8ec5a8 100644
--- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json
+++ b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json
@@ -207,11 +207,11 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2016-07-11 03:27:57.897071", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Appraisal Goal", 
- "owner": "ashwini@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [], 
  "quick_entry": 0, 
  "read_only": 0, 
diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.json b/erpnext/hr/doctype/appraisal_template/appraisal_template.json
index ac6e400..7329c35 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template.json
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.json
@@ -113,11 +113,11 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2016-12-13 12:37:56.937023", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Appraisal Template", 
- "owner": "ashwini@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 0, 
diff --git a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json
index 34ea5d8..2858419 100644
--- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json
+++ b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json
@@ -78,11 +78,11 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2016-07-11 03:27:57.979215", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Appraisal Template Goal", 
- "owner": "ashwini@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [], 
  "quick_entry": 0, 
  "read_only": 0, 
diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json
index a656a7e..134098f 100644
--- a/erpnext/hr/doctype/attendance/attendance.json
+++ b/erpnext/hr/doctype/attendance/attendance.json
@@ -205,11 +205,11 @@
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-29 13:51:37.177231",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Attendance",
- "owner": "ashwini@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "cancel": 1,
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index 45b7060..373b940 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -98,7 +98,8 @@
 		e = {
 			"name": d.name,
 			"doctype": "Attendance",
-			"date": d.attendance_date,
+			"start": d.attendance_date,
+			"end": d.attendance_date,
 			"title": cstr(d.status),
 			"docstatus": d.docstatus
 		}
diff --git a/erpnext/hr/doctype/attendance/attendance_calendar.js b/erpnext/hr/doctype/attendance/attendance_calendar.js
index 104f09d..4566489 100644
--- a/erpnext/hr/doctype/attendance/attendance_calendar.js
+++ b/erpnext/hr/doctype/attendance/attendance_calendar.js
@@ -1,12 +1,6 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 frappe.views.calendar["Attendance"] = {
-	field_map: {
-		"start": "attendance_date",
-		"end": "attendance_date",
-		"id": "name",
-		"docstatus": 1
-	},
 	options: {
 		header: {
 			left: 'prev,next today',
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index fa28470..e3e6e80 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -371,12 +371,12 @@
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-06-15 12:43:04.099803",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Claim",
  "name_case": "Title Case",
- "owner": "harshada@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "amend": 1,
diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
index 3cce50e..70a48f9 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
@@ -120,11 +120,11 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-05-11 18:54:35.601592",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Claim Detail",
- "owner": "harshada@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC"
diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json
index cf2cbb4..02ab4cb 100644
--- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json
+++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json
@@ -48,11 +48,11 @@
  "icon": "fa fa-flag",
  "idx": 1,
  "links": [],
- "modified": "2019-12-11 13:38:59.534034",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Claim Type",
- "owner": "harshada@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "create": 1,
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
index a5ac3f3..4abba5f 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
@@ -1,5 +1,4 @@
 {
- "actions": [],
  "creation": "2019-05-09 15:47:39.760406",
  "doctype": "DocType",
  "engine": "InnoDB",
@@ -54,6 +53,7 @@
   {
    "fieldname": "transaction_type",
    "fieldtype": "Link",
+   "in_standard_filter": 1,
    "label": "Transaction Type",
    "options": "DocType"
   },
@@ -109,9 +109,9 @@
   }
  ],
  "in_create": 1,
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
- "links": [],
- "modified": "2020-02-27 14:40:10.502605",
+ "modified": "2020-09-04 12:16:36.569066",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Ledger Entry",
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js
new file mode 100644
index 0000000..889325b
--- /dev/null
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry_list.js
@@ -0,0 +1,13 @@
+frappe.listview_settings['Leave Ledger Entry'] = {
+	onload: function(listview) {
+		if(listview.page.fields_dict.transaction_type) {
+			listview.page.fields_dict.transaction_type.get_query = function() {
+				return {
+					"filters": {
+						"name": ["in", ["Leave Allocation", "Leave Application", "Leave Encashment"]],
+					}
+				};
+			};
+		}
+	}
+};
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index f8b7334..2c385e8 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -103,7 +103,7 @@
 			"doctype": "Shift Assignment",
 			"start_date": d.start_date,
 			"end_date": d.end_date if d.end_date else nowdate(),
-			"title": cstr(d.employee_name) + \
+			"title": cstr(d.employee_name) + ": "+ \
 				cstr(d.shift_type),
 			"docstatus": d.docstatus
 		}
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.json b/erpnext/hr/doctype/upload_attendance/upload_attendance.json
index 16b7a83..a1451fd 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.json
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.json
@@ -229,11 +229,11 @@
  "issingle": 1, 
  "istable": 0, 
  "max_attachments": 1, 
- "modified": "2017-11-14 12:51:34.980103", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Upload Attendance", 
- "owner": "harshada@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 0, 
diff --git a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
index ae0d492..b1e421d 100644
--- a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
+++ b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
@@ -40,8 +40,8 @@
  "mapping_name": "Company to Hub Company",
  "mapping_type": "Push",
  "migration_id_field": "hub_sync_id",
- "modified": "2018-02-14 15:57:05.571142",
- "modified_by": "achilles@erpnext.com",
+ "modified": "2020-09-18 17:26:09.703215",
+ "modified_by": "Administrator",
  "name": "Company to Hub Company",
  "owner": "Administrator",
  "page_length": 10,
diff --git a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
index d2af755..d11abeb 100644
--- a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
+++ b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
@@ -21,10 +21,10 @@
  "mapping_name": "Hub Message to Lead",
  "mapping_type": "Pull",
  "migration_id_field": "hub_sync_id",
- "modified": "2018-02-14 15:57:05.606597",
- "modified_by": "achilles@erpnext.com",
+ "modified": "2020-09-18 17:26:09.703215",
+ "modified_by": "Administrator",
  "name": "Hub Message to Lead",
- "owner": "frappetest@gmail.com",
+ "owner": "Administrator",
  "page_length": 10,
  "remote_objectname": "Hub Message",
  "remote_primary_key": "name"
diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.json b/erpnext/hub_node/doctype/hub_user/hub_user.json
index a660022..f51ffb4 100644
--- a/erpnext/hub_node/doctype/hub_user/hub_user.json
+++ b/erpnext/hub_node/doctype/hub_user/hub_user.json
@@ -121,8 +121,8 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2018-09-01 13:56:07.816894", 
- "modified_by": "netchamp@rawcoderz.com", 
+ "modified": "2020-09-18 17:26:09.703215", 
+ "modified_by": "Administrator", 
  "module": "Hub Node", 
  "name": "Hub User", 
  "name_case": "", 
diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.json b/erpnext/hub_node/doctype/hub_users/hub_users.json
index 2027e72..d42f3fd 100644
--- a/erpnext/hub_node/doctype/hub_users/hub_users.json
+++ b/erpnext/hub_node/doctype/hub_users/hub_users.json
@@ -54,12 +54,12 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2018-03-06 04:41:17.916243", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Hub Node", 
  "name": "Hub Users", 
  "name_case": "", 
- "owner": "test1@example.com", 
+ "owner": "Administrator", 
  "permissions": [], 
  "quick_entry": 1, 
  "read_only": 0, 
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
index 4b49f19..e784f68 100644
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
+++ b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
@@ -352,12 +352,12 @@
  "issingle": 1, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2019-02-01 14:21:16.729848", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Hub Node", 
  "name": "Marketplace Settings", 
  "name_case": "", 
- "owner": "netchamp@rawcoderz.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 0, 
diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js
index ffef60b..9b4c217 100644
--- a/erpnext/loan_management/doctype/loan/loan.js
+++ b/erpnext/loan_management/doctype/loan/loan.js
@@ -73,8 +73,8 @@
 
 	loan_type: function(frm) {
 		frm.toggle_reqd("repayment_method", frm.doc.is_term_loan);
-		frm.toggle_display("repayment_method", 1 - frm.doc.is_term_loan);
-		frm.toggle_display("repayment_periods", s1 - frm.doc.is_term_loan);
+		frm.toggle_display("repayment_method", frm.doc.is_term_loan);
+		frm.toggle_display("repayment_periods", frm.doc.is_term_loan);
 	},
 
 
@@ -119,12 +119,10 @@
 
 	create_loan_security_unpledge: function(frm) {
 		frappe.call({
-			method: "erpnext.loan_management.doctype.loan.loan.create_loan_security_unpledge",
+			method: "erpnext.loan_management.doctype.loan.loan.unpledge_security",
 			args : {
 				"loan": frm.doc.name,
-				"applicant_type": frm.doc.applicant_type,
-				"applicant": frm.doc.applicant,
-				"company": frm.doc.company
+				"as_dict": 1
 			},
 			callback: function(r) {
 				if (r.message)
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index e20b484..d1b7589 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -7,7 +7,7 @@
 import erpnext
 from frappe import _
 from frappe.utils import flt, rounded, add_months, nowdate, getdate, now_datetime
-
+from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
 from erpnext.controllers.accounts_controller import AccountsController
 
 class Loan(AccountsController):
@@ -223,30 +223,56 @@
 		return repayment_entry
 
 @frappe.whitelist()
-def create_loan_security_unpledge(loan, applicant_type, applicant, company, as_dict=1):
-	loan_security_pledge_details = frappe.db.sql("""
-		SELECT p.loan_security, sum(p.qty) as qty
-		FROM `tabLoan Security Pledge` lsp , `tabPledge` p
-		WHERE p.parent = lsp.name AND lsp.loan = %s AND lsp.docstatus = 1
-		GROUP BY p.loan_security
-	""",(loan), as_dict=1)
+def unpledge_security(loan=None, loan_security_pledge=None, as_dict=0, save=0, submit=0, approve=0):
+	# if loan is passed it will be considered as full unpledge
+	if loan:
+		pledge_qty_map = get_pledged_security_qty(loan)
+		loan_doc = frappe.get_doc('Loan', loan)
+		unpledge_request = create_loan_security_unpledge(pledge_qty_map, loan_doc.name, loan_doc.company,
+			loan_doc.applicant_type, loan_doc.applicant)
+	# will unpledge qty based on loan security pledge
+	elif loan_security_pledge:
+		security_map = {}
+		pledge_doc = frappe.get_doc('Loan Security Pledge', loan_security_pledge)
+		for security in pledge_doc.securities:
+			security_map.setdefault(security.loan_security, security.qty)
 
+		unpledge_request = create_loan_security_unpledge(security_map, pledge_doc.loan,
+			pledge_doc.company, pledge_doc.applicant_type, pledge_doc.applicant)
+
+	if save:
+		unpledge_request.save()
+
+	if submit:
+		unpledge_request.submit()
+
+	if approve:
+		if unpledge_request.docstatus == 1:
+			unpledge_request.status = 'Approved'
+			unpledge_request.save()
+		else:
+			frappe.throw(_('Only submittted unpledge requests can be approved'))
+
+	if as_dict:
+		return unpledge_request
+	else:
+		return unpledge_request
+
+def create_loan_security_unpledge(unpledge_map, loan, company, applicant_type, applicant):
 	unpledge_request = frappe.new_doc("Loan Security Unpledge")
 	unpledge_request.applicant_type = applicant_type
 	unpledge_request.applicant = applicant
 	unpledge_request.loan = loan
 	unpledge_request.company = company
 
-	for loan_security in loan_security_pledge_details:
-		unpledge_request.append('securities', {
-			"loan_security": loan_security.loan_security,
-			"qty": loan_security.qty
-		})
+	for security, qty in unpledge_map.items():
+		if qty:
+			unpledge_request.append('securities', {
+				"loan_security": security,
+				"qty": qty
+			})
 
-	if as_dict:
-		return unpledge_request.as_dict()
-	else:
-		return unpledge_request
+	return unpledge_request
 
 
 
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 23815d598..5a4a19a 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -14,9 +14,11 @@
 	process_loan_interest_accrual_for_term_loans)
 from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year
 from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall
-from erpnext.loan_management.doctype.loan.loan import create_loan_security_unpledge
+from erpnext.loan_management.doctype.loan.loan import unpledge_security
 from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
 from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
+from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
 
 class TestLoan(unittest.TestCase):
 	def setUp(self):
@@ -193,18 +195,13 @@
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
 		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
 
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
 			"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
 		repayment_entry.submit()
 
-		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
-			'paid_principal_amount'])
+		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
 
-		unaccrued_interest_amount =  (loan.loan_amount * loan.rate_of_interest * 6) \
-			/ (days_in_year(get_datetime(first_date).year) * 100)
-
-		self.assertEquals(flt(amounts[0] + unaccrued_interest_amount, 3),
-			flt(accrued_interest_amount, 3))
+		self.assertEquals(flt(amount, 2),flt(accrued_interest_amount, 2))
 		self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
 
 		loan.load_from_db()
@@ -306,13 +303,10 @@
 			"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
 		repayment_entry.submit()
 
-		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
-			'paid_principal_amount'])
-
 		loan.load_from_db()
 		self.assertEquals(loan.status, "Loan Closure Requested")
 
-		unpledge_request = create_loan_security_unpledge(loan.name, loan.applicant_type, loan.applicant, loan.company, as_dict=0)
+		unpledge_request = unpledge_security(loan=loan.name, save=1)
 		unpledge_request.submit()
 		unpledge_request.status = 'Approved'
 		unpledge_request.save()
@@ -323,6 +317,102 @@
 		self.assertEqual(loan.status, 'Closed')
 		self.assertEquals(sum(pledged_qty.values()), 0)
 
+		amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
+		self.assertEqual(amounts['pending_principal_amount'], 0)
+		self.assertEqual(amounts['payable_principal_amount'], 0)
+		self.assertEqual(amounts['interest_amount'], 0)
+
+	def test_disbursal_check_with_shortfall(self):
+		pledges = [{
+			"loan_security": "Test Security 2",
+			"qty": 8000.00,
+			"haircut": 50,
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant2,
+			'Stock Loan', pledges, "Repay Over Number of Periods", 12)
+
+		create_pledge(loan_application)
+
+		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
+		loan.submit()
+
+		#Disbursing 7,00,000 from the allowed 10,00,000 according to security pledge
+		make_loan_disbursement_entry(loan.name, 700000)
+
+		frappe.db.sql("""UPDATE `tabLoan Security Price` SET loan_security_price = 100
+			where loan_security='Test Security 2'""")
+
+		create_process_loan_security_shortfall()
+		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
+		self.assertTrue(loan_security_shortfall)
+
+		self.assertEqual(get_disbursal_amount(loan.name), 0)
+
+		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
+			where loan_security='Test Security 2'""")
+
+	def test_disbursal_check_without_shortfall(self):
+		pledges = [{
+			"loan_security": "Test Security 2",
+			"qty": 8000.00,
+			"haircut": 50,
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant2,
+			'Stock Loan', pledges, "Repay Over Number of Periods", 12)
+
+		create_pledge(loan_application)
+
+		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
+		loan.submit()
+
+		#Disbursing 7,00,000 from the allowed 10,00,000 according to security pledge
+		make_loan_disbursement_entry(loan.name, 700000)
+
+		self.assertEqual(get_disbursal_amount(loan.name), 300000)
+
+	def test_pending_loan_amount_after_closure_request(self):
+		pledge = [{
+			"loan_security": "Test Security 1",
+			"qty": 4000.00
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		create_pledge(loan_application)
+
+		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+		loan.submit()
+
+		self.assertEquals(loan.loan_amount, 1000000)
+
+		first_date = '2019-10-01'
+		last_date = '2019-10-30'
+
+		no_of_days = date_diff(last_date, first_date) + 1
+
+		no_of_days += 6
+
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+			/ (days_in_year(get_datetime(first_date).year) * 100)
+
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+		amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
+
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
+			"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
+		repayment_entry.submit()
+
+		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
+			'paid_principal_amount'])
+
+		loan.load_from_db()
+		self.assertEquals(loan.status, "Loan Closure Requested")
+
+		amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
+		self.assertEquals(amounts['pending_principal_amount'], 0.0)
 
 def create_loan_accounts():
 	if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js
index b56fce1..1365274 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.js
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.js
@@ -33,18 +33,18 @@
 
 			if (frm.doc.is_secured_loan) {
 				frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => {
-					if (!r) {
+					if (Object.keys(r).length === 0) {
 						frm.add_custom_button(__('Loan Security Pledge'), function() {
-							frm.trigger('create_loan_security_pledge')
+							frm.trigger('create_loan_security_pledge');
 						},__('Create'))
 					}
 				});
 			}
 
 			frappe.db.get_value("Loan", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => {
-				if (!r) {
+				if (Object.keys(r).length === 0) {
 					frm.add_custom_button(__('Loan'), function() {
-						frm.trigger('create_loan')
+						frm.trigger('create_loan');
 					},__('Create'))
 				} else {
 					frm.set_df_property('status', 'read_only', 1);
@@ -54,7 +54,7 @@
 	},
 	create_loan: function(frm) {
 		if (frm.doc.status != "Approved") {
-			frappe.throw(__("Cannot create loan until application is approved"))
+			frappe.throw(__("Cannot create loan until application is approved"));
 		}
 
 		frappe.model.open_mapped_doc({
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 6c27e12..260fada 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -67,28 +67,10 @@
 			disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount
 			total_payment = loan_details.total_payment
 
-			if disbursed_amount > loan_details.loan_amount and loan_details.is_term_loan:
-				frappe.throw(_("Disbursed Amount cannot be greater than loan amount"))
+			possible_disbursal_amount = get_disbursal_amount(self.against_loan)
 
-			if loan_details.status == 'Disbursed':
-				pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
-					- flt(loan_details.total_principal_paid)
-			else:
-				pending_principal_amount = loan_details.disbursed_amount
-
-			security_value = 0.0
-			if loan_details.is_secured_loan:
-				security_value = get_total_pledged_security_value(self.against_loan)
-
-			if not security_value:
-				security_value = loan_details.loan_amount
-
-			if pending_principal_amount + self.disbursed_amount > flt(security_value):
-				allowed_amount = security_value - pending_principal_amount
-				if allowed_amount < 0:
-					allowed_amount = 0
-
-				frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(allowed_amount))
+			if self.disbursed_amount > possible_disbursal_amount:
+				frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(possible_disbursal_amount))
 
 			if loan_details.status == "Disbursed" and not loan_details.is_term_loan:
 				process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1),
@@ -176,3 +158,32 @@
 		security_value += (loan_security_price_map.get(security) * qty * hair_cut_map.get(security))/100
 
 	return security_value
+
+@frappe.whitelist()
+def get_disbursal_amount(loan):
+	loan_details = frappe.get_all("Loan", fields = ["loan_amount", "disbursed_amount", "total_payment",
+		"total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan"],
+		filters= { "name": loan })[0]
+
+	if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan,
+		'status': 'Pending'}):
+		return 0
+
+	if loan_details.status == 'Disbursed':
+		pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
+			- flt(loan_details.total_principal_paid)
+	else:
+		pending_principal_amount = flt(loan_details.disbursed_amount)
+
+	security_value = 0.0
+	if loan_details.is_secured_loan:
+		security_value = get_total_pledged_security_value(loan)
+
+	if not security_value and not loan_details.is_secured_loan:
+		security_value = flt(loan_details.loan_amount)
+
+	disbursal_amount = flt(security_value) - flt(pending_principal_amount)
+
+	return disbursal_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 c5111fd..2d959bf 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
@@ -85,8 +85,11 @@
 	if no_of_days <= 0:
 		return
 
-	pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
-		- flt(loan.total_principal_paid)
+	if loan.status == 'Disbursed':
+		pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
+			- flt(loan.total_principal_paid)
+	else:
+		pending_principal_amount = loan.disbursed_amount
 
 	interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)
 	payable_interest = interest_per_day * no_of_days
@@ -107,7 +110,7 @@
 
 def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None):
 	query_filters = {
-		"status": "Disbursed",
+		"status": ('in', ['Disbursed', 'Partially Disbursed']),
 		"docstatus": 1
 	}
 
@@ -118,8 +121,9 @@
 
 	if not open_loans:
 		open_loans = frappe.get_all("Loan",
-			fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account", "is_term_loan",
-				"disbursement_date", "applicant_type", "applicant", "rate_of_interest", "total_interest_payable", "repayment_start_date"],
+			fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account",
+				"is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant",
+				"rate_of_interest", "total_interest_payable", "total_principal_paid", "repayment_start_date"],
 			filters=query_filters)
 
 	for loan in open_loans:
@@ -209,7 +213,8 @@
 		WHERE loan = %s""", (loan.name))
 
 	if last_posting_date[0][0]:
-		return last_posting_date[0][0]
+		# interest for last interest accrual date is already booked, so add 1 day
+		return add_days(last_posting_date[0][0], 1)
 	else:
 		return loan.disbursement_date
 
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 9605045..97dbc44 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -13,6 +13,7 @@
 from erpnext.controllers.accounts_controller import AccountsController
 from erpnext.accounts.general_ledger import make_gl_entries
 from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import update_shortfall_status
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
 
 class LoanRepayment(AccountsController):
 
@@ -22,6 +23,9 @@
 		self.validate_amount()
 		self.allocate_amounts(amounts['pending_accrual_entries'])
 
+	def before_submit(self):
+		self.book_unaccrued_interest()
+
 	def on_submit(self):
 		self.update_paid_amount()
 		self.make_gl_entries()
@@ -72,6 +76,26 @@
 			msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount)
 			frappe.throw(msg)
 
+	def book_unaccrued_interest(self):
+		if self.payment_type == 'Loan Closure':
+			total_interest_paid = 0
+			for payment in self.repayment_details:
+				total_interest_paid += payment.paid_interest_amount
+
+			if total_interest_paid < self.interest_payable:
+				if not self.is_term_loan:
+					process = process_loan_interest_accrual_for_demand_loans(posting_date=self.posting_date,
+						loan=self.against_loan)
+
+					lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
+						process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
+
+					self.append('repayment_details', {
+						'loan_interest_accrual': lia.name,
+						'paid_interest_amount': lia.interest_amount,
+						'paid_principal_amount': lia.payable_principal_amount
+					})
+
 	def update_paid_amount(self):
 		precision = cint(frappe.db.get_default("currency_precision")) or 2
 
@@ -116,6 +140,7 @@
 	def allocate_amounts(self, paid_entries):
 		self.set('repayment_details', [])
 		self.principal_amount_paid = 0
+		total_interest_paid = 0
 		interest_paid = self.amount_paid - self.penalty_amount
 
 		if self.amount_paid - self.penalty_amount > 0 and paid_entries:
@@ -137,12 +162,17 @@
 						interest_paid = 0
 						paid_principal=0
 
+				total_interest_paid += interest_amount
 				self.append('repayment_details', {
 					'loan_interest_accrual': lia,
 					'paid_interest_amount': interest_amount,
 					'paid_principal_amount': paid_principal
 				})
 
+		if self.payment_type == 'Loan Closure' and total_interest_paid < self.interest_payable:
+			unaccrued_interest = self.interest_payable - total_interest_paid
+			interest_paid -= unaccrued_interest
+
 		if interest_paid:
 			self.principal_amount_paid += interest_paid
 
@@ -281,7 +311,7 @@
 
 		due_date = add_days(entry.posting_date, 1)
 		no_of_late_days = date_diff(posting_date,
-					add_days(due_date, loan_type_details.grace_period_in_days))
+			add_days(due_date, loan_type_details.grace_period_in_days))
 
 		if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary):
 			penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)/365
@@ -297,7 +327,10 @@
 		if not final_due_date:
 			final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
 
-	pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable
+	if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
+		pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable
+	else:
+		pending_principal_amount = against_loan_doc.disbursed_amount
 
 	if payment_type == "Loan Closure":
 		if due_date:
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
index 4572e99..7dd5725 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
@@ -21,6 +21,10 @@
   "total_security_value",
   "column_break_11",
   "maximum_loan_value",
+  "more_information_section",
+  "reference_no",
+  "column_break_18",
+  "description",
   "amended_from"
  ],
  "fields": [
@@ -129,11 +133,34 @@
    "label": "Applicant Type",
    "options": "Employee\nMember\nCustomer",
    "reqd": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "more_information_section",
+   "fieldtype": "Section Break",
+   "label": "More Information"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "reference_no",
+   "fieldtype": "Data",
+   "label": "Reference No"
+  },
+  {
+   "fieldname": "column_break_18",
+   "fieldtype": "Column Break"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description"
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-07-02 23:38:24.002382",
+ "modified": "2020-09-04 22:38:19.894488",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Security Pledge",
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 02efe24..0f42bde 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -4,7 +4,7 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import get_datetime
+from frappe.utils import get_datetime, flt
 from frappe.model.document import Document
 from six import iteritems
 from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
@@ -51,13 +51,19 @@
 			"valid_upto": (">=", update_time)
 		}, as_list=1))
 
-	loans = frappe.get_all('Loan', fields=['name', 'loan_amount', 'total_principal_paid'],
-		filters={'status': 'Disbursed', 'is_secured_loan': 1})
+	loans = frappe.get_all('Loan', fields=['name', 'loan_amount', 'total_principal_paid', 'total_payment',
+		'total_interest_payable', 'disbursed_amount', 'status'],
+		filters={'status': ('in',['Disbursed','Partially Disbursed']), 'is_secured_loan': 1})
 
 	loan_security_map = {}
 
 	for loan in loans:
-		outstanding_amount = loan.loan_amount - loan.total_principal_paid
+		if loan.status == 'Disbursed':
+			outstanding_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
+				- flt(loan.total_principal_paid)
+		else:
+			outstanding_amount = loan.disbursed_amount
+
 		pledged_securities = get_pledged_security_qty(loan.name)
 		ltv_ratio = ''
 		security_value = 0.0
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json
index aece46f..2e2b251 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json
@@ -16,6 +16,10 @@
   "status",
   "loan_security_details_section",
   "securities",
+  "more_information_section",
+  "reference_no",
+  "column_break_13",
+  "description",
   "amended_from"
  ],
  "fields": [
@@ -95,11 +99,34 @@
    "label": "Applicant Type",
    "options": "Employee\nMember\nCustomer",
    "reqd": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "more_information_section",
+   "fieldtype": "Section Break",
+   "label": "More Information"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "reference_no",
+   "fieldtype": "Data",
+   "label": "Reference No"
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description"
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-05 07:23:18.440058",
+ "modified": "2020-09-04 22:39:57.756146",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Security Unpledge",
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index 5e9d82a..b3eb600 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -17,7 +17,6 @@
 		self.validate_unpledge_qty()
 
 	def on_cancel(self):
-		self.update_loan_security_pledge(cancel=1)
 		self.update_loan_status(cancel=1)
 		self.db_set('status', 'Requested')
 
@@ -43,13 +42,14 @@
 				"valid_upto": (">=", get_datetime())
 			}, as_list=1))
 
-		loan_amount, principal_paid = frappe.get_value("Loan", self.loan, ['loan_amount', 'total_principal_paid'])
-		pending_principal_amount = loan_amount - principal_paid
+		total_payment, principal_paid, interest_payable = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+			'total_interest_payable'])
+
+		pending_principal_amount = flt(total_payment) - flt(interest_payable) - flt(principal_paid)
 		security_value = 0
 
 		for security in self.securities:
-			pledged_qty = pledge_qty_map.get(security.loan_security)
-
+			pledged_qty = pledge_qty_map.get(security.loan_security, 0)
 			if security.qty > pledged_qty:
 				frappe.throw(_("""Row {0}: {1} {2} of {3} is pledged against Loan {4}.
 					You are trying to unpledge more""").format(security.idx, pledged_qty, security.uom,
@@ -58,16 +58,23 @@
 			qty_after_unpledge = pledged_qty - security.qty
 			ltv_ratio = ltv_ratio_map.get(security.loan_security_type)
 
-			security_value += qty_after_unpledge * loan_security_price_map.get(security.loan_security)
+			current_price = loan_security_price_map.get(security.loan_security)
+			if not current_price:
+				frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(security.loan_security)))
 
-		if not security_value and pending_principal_amount > 0:
+			security_value += qty_after_unpledge * current_price
+
+		if not security_value and flt(pending_principal_amount, 2) > 0:
 			frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
 
-		if security_value and (pending_principal_amount/security_value) * 100 > ltv_ratio:
+		if security_value and flt(pending_principal_amount/security_value) * 100 > ltv_ratio:
 			frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
 
 	def on_update_after_submit(self):
-		if self.status == "Approved":
+		self.approve()
+
+	def approve(self):
+		if self.status == "Approved" and not self.unpledge_time:
 			self.update_loan_status()
 			self.db_set('unpledge_time', get_datetime())
 
diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
index cd3cf7e..0fa9686 100644
--- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py
@@ -36,6 +36,8 @@
 
 	loan_process.submit()
 
+	return loan_process.name
+
 def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None):
 
 	if not term_loan_accrual_pending(posting_date or nowdate()):
@@ -49,6 +51,8 @@
 
 	loan_process.submit()
 
+	return loan_process.name
+
 def term_loan_accrual_pending(date):
 	pending_accrual = frappe.db.get_value('Repayment Schedule', {
 		'payment_date': ('<=', date),
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
index 3788bc3..606d22f 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
@@ -813,7 +813,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-08-21 14:44:33.670332", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Maintenance", 
  "name": "Maintenance Schedule", 
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index add7bbf..cba6a2d 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -67,16 +67,16 @@
 
 			for key in scheduled_date:
 				description =frappe._("Reference: {0}, Item Code: {1} and Customer: {2}").format(self.name, d.item_code, self.customer)
-				frappe.get_doc({
+				event = frappe.get_doc({
 					"doctype": "Event",
 					"owner": email_map.get(d.sales_person, self.owner),
 					"subject": description,
 					"description": description,
 					"starts_on": cstr(key["scheduled_date"]) + " 10:00:00",
 					"event_type": "Private",
-					"ref_type": self.doctype,
-					"ref_name": self.name
-				}).insert(ignore_permissions=1)
+				})
+				event.add_participant(self.doctype, self.name)
+				event.insert(ignore_permissions=1)
 
 		frappe.db.set(self, 'status', 'Submitted')
 
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
index d8ae17b..3c307e9 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -2,6 +2,7 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 from __future__ import unicode_literals
+from frappe.utils.data import get_datetime, add_days
 
 import frappe
 import unittest
@@ -9,4 +10,39 @@
 # test_records = frappe.get_test_records('Maintenance Schedule')
 
 class TestMaintenanceSchedule(unittest.TestCase):
-	pass
+	def test_events_should_be_created_and_deleted(self):
+		ms = make_maintenance_schedule()
+		ms.generate_schedule()
+		ms.submit()
+
+		all_events = get_events(ms)
+		self.assertTrue(len(all_events) > 0)
+
+		ms.cancel()
+		events_after_cancel = get_events(ms)
+		self.assertTrue(len(events_after_cancel) == 0)
+
+def get_events(ms):
+	return frappe.get_all("Event Participants", filters={
+			"reference_doctype": ms.doctype,
+			"reference_docname": ms.name,
+			"parenttype": "Event"
+		})
+
+def make_maintenance_schedule():
+	ms = frappe.new_doc("Maintenance Schedule")
+	ms.company = "_Test Company"
+	ms.customer = "_Test Customer"
+	ms.transaction_date = get_datetime()
+
+	ms.append("items", {
+		"item_code": "_Test Item",
+		"start_date": get_datetime(),
+		"end_date": add_days(get_datetime(), 32),
+		"periodicity": "Weekly",
+		"no_of_visits": 4,
+		"sales_person": "Sales Team",
+	})
+	ms.insert(ignore_permissions=True)
+
+	return ms
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
index 1192568..32bfa0e 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
@@ -1001,11 +1001,11 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2020-07-15 14:44:44.911402", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Maintenance", 
  "name": "Maintenance Visit", 
- "owner": "ashwini@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 1, 
diff --git a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
index 84dc72c..467441d 100644
--- a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
+++ b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json
@@ -125,11 +125,11 @@
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-10-03 14:55:52.786805",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Maintenance",
  "name": "Maintenance Visit Purpose",
- "owner": "ashwini@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
index e6c10ad..742d18c 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -90,6 +90,7 @@
 		update_cost()
 
 def replace_bom(args):
+	frappe.db.auto_commit_on_many_writes = 1
 	args = frappe._dict(args)
 
 	doc = frappe.get_doc("BOM Update Tool")
@@ -97,6 +98,8 @@
 	doc.new_bom = args.new_bom
 	doc.replace_bom()
 
+	frappe.db.auto_commit_on_many_writes = 0
+
 def update_cost():
 	frappe.db.auto_commit_on_many_writes = 1
 	bom_list = get_boms_in_bottom_up_order()
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index bab0dfb..b051b32 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -2,6 +2,17 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Job Card', {
+	setup: function(frm) {
+		frm.set_query('operation', function() {
+			return {
+				query: 'erpnext.manufacturing.doctype.job_card.job_card.get_operations',
+				filters: {
+					'work_order': frm.doc.work_order
+				}
+			};
+		});
+	},
+
 	refresh: function(frm) {
 		frappe.flags.pause_job = 0;
 		frappe.flags.resume_job = 0;
@@ -20,12 +31,60 @@
 			}
 		}
 
+		frm.trigger("toggle_operation_number");
+
 		if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
-			&& (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
+			&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
 			frm.trigger("prepare_timer_buttons");
 		}
 	},
 
+	operation: function(frm) {
+		frm.trigger("toggle_operation_number");
+
+		if (frm.doc.operation && frm.doc.work_order) {
+			frappe.call({
+				method: "erpnext.manufacturing.doctype.job_card.job_card.get_operation_details",
+				args: {
+					"work_order":frm.doc.work_order,
+					"operation":frm.doc.operation
+				},
+				callback: function (r) {
+					if (r.message) {
+						if (r.message.length == 1) {
+							frm.set_value("operation_id", r.message[0].name);
+						} else {
+							let args = [];
+
+							r.message.forEach((row) => {
+								args.push({ "label": row.idx, "value": row.name });
+							});
+
+							let description = __("Operation {0} added multiple times in the work order {1}",
+								[frm.doc.operation, frm.doc.work_order]);
+
+							frm.set_df_property("operation_row_number", "options", args);
+							frm.set_df_property("operation_row_number", "description", description);
+						}
+
+						frm.trigger("toggle_operation_number");
+					}
+				}
+			})
+		}
+	},
+
+	operation_row_number(frm) {
+		if (frm.doc.operation_row_number) {
+			frm.set_value("operation_id", frm.doc.operation_row_number);
+		}
+	},
+
+	toggle_operation_number(frm) {
+		frm.toggle_display("operation_row_number", !frm.doc.operation_id && frm.doc.operation);
+		frm.toggle_reqd("operation_row_number", !frm.doc.operation_id && frm.doc.operation);
+	},
+
 	prepare_timer_buttons: function(frm) {
 		frm.trigger("make_dashboard");
 		if (!frm.doc.job_started) {
@@ -35,9 +94,9 @@
 						fieldname: 'employee'}, d => {
 						if (d.employee) {
 							frm.set_value("employee", d.employee);
+						} else {
+							frm.events.start_job(frm);
 						}
-
-						frm.events.start_job(frm);
 					}, __("Enter Value"), __("Start"));
 				} else {
 					frm.events.start_job(frm);
@@ -82,9 +141,7 @@
 			frm.set_value('current_time' , 0);
 		}
 
-		frm.save("Save", () => {}, "", () => {
-			frm.doc.time_logs.pop(-1);
-		});
+		frm.save();
 	},
 
 	complete_job: function(frm, completed_time, completed_qty) {
@@ -116,6 +173,8 @@
 	employee: function(frm) {
 		if (frm.doc.job_started && !frm.doc.current_time) {
 			frm.trigger("reset_timer");
+		} else {
+			frm.events.start_job(frm);
 		}
 	},
 
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index fba670c..087ab6b 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -11,6 +11,7 @@
   "bom_no",
   "workstation",
   "operation",
+  "operation_row_number",
   "column_break_4",
   "posting_date",
   "company",
@@ -291,11 +292,15 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "operation_row_number",
+   "fieldtype": "Select",
+   "label": "Operation Row Number"
   }
  ],
  "is_submittable": 1,
- "links": [],
- "modified": "2020-04-20 15:14:00.273441",
+ "modified": "2020-08-24 15:21:21.398267",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card",
@@ -347,7 +352,6 @@
    "write": 1
   }
  ],
- "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
  "title_field": "operation",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index c29d4ba..8855e0a 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -15,10 +15,13 @@
 
 class OverlapError(frappe.ValidationError): pass
 
+class OperationMismatchError(frappe.ValidationError): pass
+
 class JobCard(Document):
 	def validate(self):
 		self.validate_time_logs()
 		self.set_status()
+		self.validate_operation_id()
 
 	def validate_time_logs(self):
 		self.total_completed_qty = 0.0
@@ -209,11 +212,10 @@
 		for_quantity, time_in_mins = 0, 0
 		from_time_list, to_time_list = [], []
 
-		field = "operation_id" if self.operation_id else "operation"
+		field = "operation_id"
 		data = frappe.get_all('Job Card',
 			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
-			filters = {"docstatus": 1, "work_order": self.work_order,
-				"workstation": self.workstation, field: self.get(field)})
+			filters = {"docstatus": 1, "work_order": self.work_order, field: self.get(field)})
 
 		if data and len(data) > 0:
 			for_quantity = data[0].completed_qty
@@ -226,14 +228,13 @@
 				FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
 				WHERE
 					jctl.parent = jc.name and jc.work_order = %s
-					and jc.workstation = %s and jc.{0} = %s and jc.docstatus = 1
-			""".format(field), (self.work_order, self.workstation, self.get(field)), as_dict=1)
+					and jc.{0} = %s and jc.docstatus = 1
+			""".format(field), (self.work_order, self.get(field)), as_dict=1)
 
 			wo = frappe.get_doc('Work Order', self.work_order)
 
-			work_order_field = "name" if field == "operation_id" else field
 			for data in wo.operations:
-				if data.get(work_order_field) == self.get(field) and data.workstation == self.workstation:
+				if data.get("name") == self.get(field):
 					data.completed_qty = for_quantity
 					data.actual_operation_time = time_in_mins
 					data.actual_start_time = time_data[0].start_time if time_data else None
@@ -306,6 +307,37 @@
 		if update_status:
 			self.db_set('status', self.status)
 
+	def validate_operation_id(self):
+		if (self.get("operation_id") and self.get("operation_row_number") and self.operation and self.work_order and
+			frappe.get_cached_value("Work Order Operation", self.operation_row_number, "name") != self.operation_id):
+			work_order = frappe.bold(get_link_to_form("Work Order", self.work_order))
+			frappe.throw(_("Operation {0} does not belong to the work order {1}")
+				.format(frappe.bold(self.operation), work_order), OperationMismatchError)
+
+@frappe.whitelist()
+def get_operation_details(work_order, operation):
+	if work_order and operation:
+		return frappe.get_all("Work Order Operation", fields = ["name", "idx"],
+			filters = {
+				"parent": work_order,
+				"operation": operation
+			}
+		)
+
+@frappe.whitelist()
+def get_operations(doctype, txt, searchfield, start, page_len, filters):
+	if filters.get("work_order"):
+		args = {"parent": filters.get("work_order")}
+		if txt:
+			args["operation"] = ("like", "%{0}%".format(txt))
+
+		return frappe.get_all("Work Order Operation",
+			filters = args,
+			fields = ["distinct operation as operation"],
+			limit_start = start,
+			limit_page_length = page_len,
+			order_by="idx asc", as_list=1)
+
 @frappe.whitelist()
 def make_material_request(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index ca05fea..b6a6c33 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -4,6 +4,72 @@
 from __future__ import unicode_literals
 
 import unittest
+import frappe
+from frappe.utils import random_string
+from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
+from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
+from erpnext.manufacturing.doctype.job_card.job_card import OperationMismatchError
 
 class TestJobCard(unittest.TestCase):
-	pass
+	def test_job_card(self):
+		data = frappe.get_cached_value('BOM',
+			{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
+
+		if data:
+			bom, bom_item = data
+
+			work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
+
+			job_cards = frappe.get_all('Job Card',
+				filters = {'work_order': work_order.name}, fields = ["operation_id", "name"])
+
+			if job_cards:
+				job_card = job_cards[0]
+				frappe.db.set_value("Job Card", job_card.name, "operation_row_number", job_card.operation_id)
+
+				doc = frappe.get_doc("Job Card", job_card.name)
+				doc.operation_id = "Test Data"
+				self.assertRaises(OperationMismatchError, doc.save)
+
+			for d in job_cards:
+				frappe.delete_doc("Job Card", d.name)
+
+	def test_job_card_with_different_work_station(self):
+		data = frappe.get_cached_value('BOM',
+			{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
+
+		if data:
+			bom, bom_item = data
+
+			work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
+
+			job_cards = frappe.get_all('Job Card',
+				filters = {'work_order': work_order.name},
+				fields = ["operation_id", "workstation", "name", "for_quantity"])
+
+			job_card = job_cards[0]
+
+			if job_card:
+				workstation = frappe.db.get_value("Workstation",
+					{"name": ("not in", [job_card.workstation])}, "name")
+
+				if not workstation or job_card.workstation == workstation:
+					workstation = make_workstation(workstation_name=random_string(5)).name
+
+				doc = frappe.get_doc("Job Card", job_card.name)
+				doc.workstation = workstation
+				doc.append("time_logs", {
+					"from_time": "2009-01-01 12:06:25",
+					"to_time": "2009-01-01 12:37:25",
+					"time_in_mins": "31.00002",
+					"completed_qty": job_card.for_quantity
+				})
+				doc.submit()
+
+				completed_qty = frappe.db.get_value("Work Order Operation", job_card.operation_id, "completed_qty")
+				self.assertEqual(completed_qty, job_card.for_quantity)
+
+				doc.cancel()
+
+			for d in job_cards:
+				frappe.delete_doc("Job Card", d.name)
\ 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 2260bef..b7c7c32 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -7,7 +7,7 @@
 import frappe
 from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
-from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry, 
+from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
 	ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError)
 from erpnext.stock.doctype.stock_entry import test_stock_entry
 from erpnext.stock.utils import get_bin
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index 2169260..8266cf7 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -20,3 +20,18 @@
 			"_Test Workstation 1", "Operation 1", "2013-02-02 05:00:00", "2013-02-02 20:00:00")
 		self.assertRaises(WorkstationHolidayError, check_if_within_operating_hours,
 			"_Test Workstation 1", "Operation 1", "2013-02-01 10:00:00", "2013-02-02 20:00:00")
+
+def make_workstation(**args):
+	args = frappe._dict(args)
+
+	try:
+		doc = frappe.get_doc({
+			"doctype": "Workstation",
+			"workstation_name": args.workstation_name
+		})
+
+		doc.insert()
+
+		return doc
+	except frappe.DuplicateEntryError:
+		return frappe.get_doc("Workstation", args.workstation_name)
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donor/donor.json b/erpnext/non_profit/doctype/donor/donor.json
index 7e24dac..9639265 100644
--- a/erpnext/non_profit/doctype/donor/donor.json
+++ b/erpnext/non_profit/doctype/donor/donor.json
@@ -1,336 +1,105 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "autoname": "field:email", 
- "beta": 0, 
- "creation": "2017-09-19 16:20:27.510196", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:email",
+ "creation": "2017-09-19 16:20:27.510196",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "donor_name",
+  "column_break_5",
+  "donor_type",
+  "email",
+  "image",
+  "address_contacts",
+  "address_html",
+  "column_break_9",
+  "contact_html"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "donor_name", 
-   "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": "Donor Name", 
-   "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
-  }, 
+   "fieldname": "donor_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Donor Name",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_5", 
-   "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_5",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "donor_type", 
-   "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": "Donor Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Donor 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
-  }, 
+   "fieldname": "donor_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Donor Type",
+   "options": "Donor Type",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "email", 
-   "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": "Email", 
-   "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
-  }, 
+   "fieldname": "email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Email",
+   "reqd": 1,
+   "unique": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "image", 
-   "fieldtype": "Attach Image", 
-   "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": "Image", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "image",
+   "fieldtype": "Attach Image",
+   "hidden": 1,
+   "label": "Image",
+   "no_copy": 1,
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address_contacts", 
-   "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": "Address and Contact", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "fa fa-map-marker", 
-   "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
-  }, 
+   "depends_on": "eval:!doc.__islocal;",
+   "fieldname": "address_contacts",
+   "fieldtype": "Section Break",
+   "label": "Address and Contact",
+   "options": "fa fa-map-marker"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address_html", 
-   "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": "Address HTML", 
-   "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": "address_html",
+   "fieldtype": "HTML",
+   "label": "Address HTML"
+  },
   {
-   "allow_bulk_edit": 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, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "contact_html", 
-   "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": "Contact HTML", 
-   "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": "contact_html",
+   "fieldtype": "HTML",
+   "label": "Contact HTML"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_field": "image", 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-01-22 15:53:35.059946", 
- "modified_by": "Administrator", 
- "module": "Non Profit", 
- "name": "Donor", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "image_field": "image",
+ "links": [],
+ "modified": "2020-09-16 23:46:04.083274",
+ "modified_by": "Administrator",
+ "module": "Non Profit",
+ "name": "Donor",
+ "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": "Non Profit Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Non Profit Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Non Profit", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "donor_name", 
- "track_changes": 1, 
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "restrict_to_domain": "Non Profit",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "donor_name",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json
index bb73a84..992ef16 100644
--- a/erpnext/non_profit/doctype/member/member.json
+++ b/erpnext/non_profit/doctype/member/member.json
@@ -111,6 +111,7 @@
    "options": "Supplier"
   },
   {
+   "depends_on": "eval:!doc.__islocal;",
    "fieldname": "address_contacts",
    "fieldtype": "Section Break",
    "label": "Address and Contact",
@@ -133,7 +134,8 @@
   {
    "fieldname": "email_id",
    "fieldtype": "Data",
-   "label": "Email Address"
+   "label": "Email Address",
+   "options": "Email"
   },
   {
    "fieldname": "subscription_id",
@@ -176,7 +178,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-04-07 14:20:33.215700",
+ "modified": "2020-09-16 23:44:13.596948",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Member",
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index c52082c..44b975e 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -9,6 +9,7 @@
 from frappe.contacts.address_and_contact import load_address_and_contact
 from frappe.utils import cint
 from frappe.integrations.utils import get_payment_gateway_controller
+from erpnext.non_profit.doctype.membership_type.membership_type import get_membership_type
 
 class Member(Document):
 	def onload(self):
@@ -74,19 +75,23 @@
 		return create_member(user_details)
 
 def create_member(user_details):
+	user_details = frappe._dict(user_details)
 	member = frappe.new_doc("Member")
 	member.update({
 		"member_name": user_details.fullname,
 		"email_id": user_details.email,
-		"pan_number": user_details.pan,
+		"pan_number": user_details.pan or None,
 		"membership_type": user_details.plan_id,
-		"customer": create_customer(user_details)
+		"subscription_id": user_details.subscription_id or None
 	})
 
 	member.insert(ignore_permissions=True)
+	member.customer = create_customer(user_details, member.name)
+	member.save(ignore_permissions=True)
+
 	return member
 
-def create_customer(user_details):
+def create_customer(user_details, member=None):
 	customer = frappe.new_doc("Customer")
 	customer.customer_name = user_details.fullname
 	customer.customer_type = "Individual"
@@ -107,7 +112,13 @@
 			"link_name": customer.name
 		})
 
-		contact.save()
+		if member:
+			contact.append("links", {
+				"link_doctype": "Member",
+				"link_name": member
+			})
+
+		contact.save(ignore_permissions=True)
 
 	except frappe.DuplicateEntryError:
 		return customer.name
@@ -139,12 +150,31 @@
 
 	user_details = frappe._dict(user_details)
 	member = get_or_create_member(user_details)
-	if not member:
-		member = create_member(user_details)
 
 	subscription = member.setup_subscription()
 
 	member.subscription_id = subscription.get('subscription_id')
 	member.save(ignore_permissions=True)
 
-	return subscription
\ No newline at end of file
+	return subscription
+
+@frappe.whitelist()
+def register_member(fullname, email, rzpay_plan_id, subscription_id, pan=None, mobile=None):
+	plan = get_membership_type(rzpay_plan_id)
+	if not plan:
+		raise frappe.DoesNotExistError
+
+	member = frappe.db.exists("Member", {'email': email, 'subscription_id': subscription_id })
+	if member:
+		return member
+	else:
+		member = create_member(dict(
+			fullname=fullname,
+			email=email,
+			plan_id=plan,
+			subscription_id=subscription_id,
+			pan=pan,
+			mobile=mobile
+		))
+
+		return member.name
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership/membership.js b/erpnext/non_profit/doctype/membership/membership.js
index 554549a..ee8a8c0 100644
--- a/erpnext/non_profit/doctype/membership/membership.js
+++ b/erpnext/non_profit/doctype/membership/membership.js
@@ -8,6 +8,24 @@
 		})
 	},
 
+	refresh: function(frm) {
+		!frm.doc.invoice && frm.add_custom_button("Generate Invoice", () => {
+			frm.call("generate_invoice", {
+				save: true
+			}).then(() => {
+				frm.reload_doc();
+			});
+		});
+
+		frappe.db.get_single_value("Membership Settings", "send_email").then(val => {
+			if (val) frm.add_custom_button("Send Acknowledgement", () => {
+				frm.call("send_acknowlement").then(() => {
+					frm.reload_doc();
+				});
+			});
+		})
+	},
+
 	onload: function(frm) {
 		frm.add_fetch('membership_type', 'amount', 'amount');
 	}
diff --git a/erpnext/non_profit/doctype/membership/membership.json b/erpnext/non_profit/doctype/membership/membership.json
index 238f4c3..7f21896 100644
--- a/erpnext/non_profit/doctype/membership/membership.json
+++ b/erpnext/non_profit/doctype/membership/membership.json
@@ -19,10 +19,10 @@
   "paid",
   "currency",
   "amount",
+  "invoice",
   "razorpay_details_section",
   "subscription_id",
-  "payment_id",
-  "webhook_payload"
+  "payment_id"
  ],
  "fields": [
   {
@@ -90,9 +90,9 @@
   },
   {
    "fieldname": "currency",
-   "fieldtype": "Select",
+   "fieldtype": "Link",
    "label": "Currency",
-   "options": "USD\nINR"
+   "options": "Currency"
   },
   {
    "fieldname": "amount",
@@ -118,17 +118,15 @@
    "read_only": 1
   },
   {
-   "fieldname": "webhook_payload",
-   "fieldtype": "Code",
-   "hidden": 1,
-   "label": "Webhook Payload",
-   "options": "JSON",
-   "read_only": 1
+   "fieldname": "invoice",
+   "fieldtype": "Link",
+   "label": "Invoice",
+   "options": "Sales Invoice"
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-07-27 14:28:11.532696",
+ "modified": "2020-09-19 14:28:11.532696",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Membership",
@@ -163,4 +161,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index 729e111..f058004 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -10,6 +10,7 @@
 from frappe.model.document import Document
 from frappe.email import sendmail_to_system_managers
 from frappe.utils import add_days, add_years, nowdate, getdate, add_months, get_link_to_form
+from erpnext.non_profit.doctype.member.member import create_member
 from frappe import _
 import erpnext
 
@@ -57,11 +58,95 @@
 			self.load_from_db()
 			self.db_set('paid', 1)
 
+	def generate_invoice(self, save=True):
+		if not (self.paid or self.currency or self.amount):
+			frappe.throw(_("The payment for this membership is not paid. To generate invoice fill the payment details"))
+
+		if self.invoice:
+			frappe.throw(_("An invoice is already linked to this document"))
+
+		member = frappe.get_doc("Member", self.member)
+		plan = frappe.get_doc("Membership Type", self.membership_type)
+		settings = frappe.get_doc("Membership Settings")
+
+		if not member.customer:
+			frappe.throw(_("No customer linked to member {}", [member.name]))
+
+		if not settings.debit_account:
+			frappe.throw(_("You need to set <b>Debit Account</b> in Membership Settings"))
+
+		if not settings.company:
+			frappe.throw(_("You need to set <b>Default Company</b> for invoicing in Membership Settings"))
+
+		invoice = make_invoice(self, member, plan, settings)
+		self.invoice = invoice.name
+
+		if save:
+			self.save()
+
+		return invoice
+
+	def send_acknowlement(self):
+		settings = frappe.get_doc("Membership Settings")
+		if not settings.send_email:
+			frappe.throw(_("You need to enable <b>Send Acknowledge Email</b> in Membership Settings"))
+
+		member = frappe.get_doc("Member", self.member)
+		plan = frappe.get_doc("Membership Type", self.membership_type)
+		email = member.email_id if member.email_id else member.email
+		attachments = [frappe.attach_print("Membership", self.name, print_format=settings.membership_print_format)]
+
+		if self.invoice and settings.send_invoice:
+			attachments.append(frappe.attach_print("Sales Invoice", self.invoice, print_format=settings.inv_print_format))
+
+		email_template = frappe.get_doc("Email Template", settings.email_template)
+		context = { "doc": self, "member": member}
+
+		email_args = {
+			"recipients": [email],
+			"message": frappe.render_template(email_template.get("response"), context),
+			"subject": frappe.render_template(email_template.get("subject"), context),
+			"attachments": attachments,
+			"reference_doctype": self.doctype,
+			"reference_name": self.name
+		}
+
+		if not frappe.flags.in_test:
+			frappe.enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
+		else:
+			frappe.sendmail(**email_args)
+
+	def generate_and_send_invoice(self):
+		invoice = self.generate_invoice(False)
+		self.send_acknowlement()
+
+def make_invoice(membership, member, plan, settings):
+	invoice = frappe.get_doc({
+		'doctype': 'Sales Invoice',
+		'customer': member.customer,
+		'debit_to': settings.debit_account,
+		'currency': membership.currency,
+		'is_pos': 0,
+		'items': [
+			{
+				'item_code': plan.linked_item,
+				'rate': membership.amount,
+				'qty': 1
+			}
+		]
+	})
+
+	invoice.insert(ignore_permissions=True)
+	invoice.submit()
+
+	return invoice
+
 def get_member_based_on_subscription(subscription_id, email):
 	members = frappe.get_all("Member", filters={
 					'subscription_id': subscription_id,
 					'email_id': email
 				}, order_by="creation desc")
+
 	try:
 		return frappe.get_doc("Member", members[0]['name'])
 	except:
@@ -77,16 +162,15 @@
 
 	controller.verify_signature(data, signature, key)
 
-
 @frappe.whitelist(allow_guest=True)
 def trigger_razorpay_subscription(*args, **kwargs):
 	data = frappe.request.get_data(as_text=True)
 	try:
 		verify_signature(data)
 	except Exception as e:
-		signature = frappe.request.headers.get('X-Razorpay-Signature')
-		log = "{0} \n\n {1} \n\n {2} \n\n {3}".format(e, frappe.get_traceback(), signature, data)
-		frappe.log_error(e, "Webhook Verification Error")
+		log = frappe.log_error(e, "Webhook Verification Error")
+		notify_failure(log)
+		return { 'status': 'Failed', 'reason': e}
 
 	if isinstance(data, six.string_types):
 		data = json.loads(data)
@@ -99,35 +183,42 @@
 	payment = frappe._dict(payment)
 
 	try:
-		data_json = json.dumps(data, indent=4, sort_keys=True)
+		if not data.event == "subscription.charged":
+			return
+
 		member = get_member_based_on_subscription(subscription.id, payment.email)
-	except Exception as e:
-		error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed"))
-		notify_failure(error_log)
-		return { status: 'Failed' }
+		if not member:
+			member = create_member(frappe._dict({
+				'fullname': payment.email,
+				'email': payment.email,
+				'plan_id': get_plan_from_razorpay_id(subscription.plan_id)
+			}))
 
-	if not member:
-		return { status: 'Failed' }
-	try:
-		if data.event == "subscription.activated":
+			member.subscription_id = subscription.id
 			member.customer_id = payment.customer_id
-		elif data.event == "subscription.charged":
-			membership = frappe.new_doc("Membership")
-			membership.update({
-				"member": member.name,
-				"membership_status": "Current",
-				"membership_type": member.membership_type,
-				"currency": "INR",
-				"paid": 1,
-				"payment_id": payment.id,
-				"webhook_payload": data_json,
-				"from_date": datetime.fromtimestamp(subscription.current_start),
-				"to_date": datetime.fromtimestamp(subscription.current_end),
-				"amount": payment.amount / 100 # Convert to rupees from paise
-			})
-			membership.insert(ignore_permissions=True)
+			if subscription.notes and type(subscription.notes) == dict:
+				notes = '\n'.join("{}: {}".format(k, v) for k, v in subscription.notes.items())
+				member.add_comment("Comment", notes)
+			elif subscription.notes and type(subscription.notes) == str:
+				member.add_comment("Comment", subscription.notes)
 
-		# Update these values anyway
+
+		# Update Membership
+		membership = frappe.new_doc("Membership")
+		membership.update({
+			"member": member.name,
+			"membership_status": "Current",
+			"membership_type": member.membership_type,
+			"currency": "INR",
+			"paid": 1,
+			"payment_id": payment.id,
+			"from_date": datetime.fromtimestamp(subscription.current_start),
+			"to_date": datetime.fromtimestamp(subscription.current_end),
+			"amount": payment.amount / 100 # Convert to rupees from paise
+		})
+		membership.insert(ignore_permissions=True)
+
+		# Update membership values
 		member.subscription_start = datetime.fromtimestamp(subscription.start_at)
 		member.subscription_end = datetime.fromtimestamp(subscription.end_at)
 		member.subscription_activated = 1
@@ -135,9 +226,9 @@
 	except Exception as e:
 		log = frappe.log_error(e, "Error creating membership entry")
 		notify_failure(log)
-		return { status: 'Failed' }
+		return { 'status': 'Failed', 'reason': e}
 
-	return { status: 'Success' }
+	return { 'status': 'Success' }
 
 
 def notify_failure(log):
@@ -152,3 +243,11 @@
 		sendmail_to_system_managers("[Important] [ERPNext] Razorpay membership webhook failed , please check.", content)
 	except:
 		pass
+
+def get_plan_from_razorpay_id(plan_id):
+	plan = frappe.get_all("Membership Type", filters={'razorpay_plan_id': plan_id}, order_by="creation desc")
+
+	try:
+		return plan[0]['name']
+	except:
+		return None
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.js b/erpnext/non_profit/doctype/membership_settings/membership_settings.js
index 8c0e3a4..1d89402 100644
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.js
+++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.js
@@ -10,7 +10,39 @@
 				})
 			});
 		}
+
+		frm.set_query('inv_print_format', function(doc) {
+			return {
+				filters: {
+					"doc_type": "Sales Invoice"
+				}
+			};
+		});
+
+		frm.set_query('membership_print_format', function(doc) {
+			return {
+				filters: {
+					"doc_type": "Membership"
+				}
+			};
+		});
+
+		frm.set_query('debit_account', function(doc) {
+			return {
+				filters: {
+					'account_type': 'Receivable',
+					'is_group': 0,
+					'company': frm.doc.company
+				}
+			};
+		});
+
+		let docs_url = "https://docs.erpnext.com/docs/user/manual/en/non_profit/membership";
+
+		frm.set_intro(__("You can learn more about memberships in the manual. ") + `<a href='${docs_url}'>${__('ERPNext Docs')}</a>`, true);
+
 		frm.trigger("add_generate_button");
+		frm.trigger("add_copy_buttonn");
 	},
 
 	add_generate_button: function(frm) {
@@ -27,4 +59,12 @@
 			});
 		});
 	},
+
+	add_copy_buttonn: function(frm) {
+		if (frm.doc.webhook_secret) {
+			frm.add_custom_button(__("Copy Webhook URL"), () => {
+				frappe.utils.copy_to_clipboard(`https://${frappe.boot.sitename}/api/method/erpnext.non_profit.doctype.membership.membership.trigger_razorpay_subscription`);
+			});
+		}
+	}
 });
diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json
index 52b9d01..5b6bab5 100644
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json
+++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json
@@ -9,7 +9,17 @@
   "razorpay_settings_section",
   "billing_cycle",
   "billing_frequency",
-  "webhook_secret"
+  "webhook_secret",
+  "column_break_6",
+  "enable_auto_invoicing",
+  "company",
+  "debit_account",
+  "column_break_9",
+  "send_email",
+  "send_invoice",
+  "membership_print_format",
+  "inv_print_format",
+  "email_template"
  ],
  "fields": [
   {
@@ -41,11 +51,79 @@
    "fieldtype": "Password",
    "label": "Webhook Secret",
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Section Break",
+   "label": "Invoicing"
+  },
+  {
+   "default": "0",
+   "fieldname": "enable_auto_invoicing",
+   "fieldtype": "Check",
+   "label": "Enable Auto Invoicing",
+   "mandatory_depends_on": "eval:doc.send_invoice"
+  },
+  {
+   "depends_on": "eval:doc.enable_auto_invoicing",
+   "fieldname": "debit_account",
+   "fieldtype": "Link",
+   "label": "Debit Account",
+   "mandatory_depends_on": "eval:doc.enable_auto_invoicing",
+   "options": "Account"
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.enable_auto_invoicing",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "mandatory_depends_on": "eval:doc.enable_auto_invoicing",
+   "options": "Company"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.enable_auto_invoicing && doc.send_email",
+   "fieldname": "send_invoice",
+   "fieldtype": "Check",
+   "label": "Send Invoice with Email"
+  },
+  {
+   "default": "0",
+   "fieldname": "send_email",
+   "fieldtype": "Check",
+   "label": "Send Membership Acknowledgement"
+  },
+  {
+   "depends_on": "eval: doc.send_invoice",
+   "fieldname": "inv_print_format",
+   "fieldtype": "Link",
+   "label": "Invoice Print Format",
+   "mandatory_depends_on": "eval: doc.send_invoice",
+   "options": "Print Format"
+  },
+  {
+   "depends_on": "eval:doc.send_email",
+   "fieldname": "membership_print_format",
+   "fieldtype": "Link",
+   "label": "Membership Print Format",
+   "options": "Print Format"
+  },
+  {
+   "depends_on": "eval:doc.send_email",
+   "fieldname": "email_template",
+   "fieldtype": "Link",
+   "label": "Email Template",
+   "mandatory_depends_on": "eval:doc.send_email",
+   "options": "Email Template"
   }
  ],
  "issingle": 1,
  "links": [],
- "modified": "2020-05-22 12:38:27.103759",
+ "modified": "2020-08-05 17:26:37.287395",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Membership Settings",
@@ -60,6 +138,23 @@
    "role": "System Manager",
    "share": 1,
    "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Non Profit Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Non Profit Member",
+   "share": 1
   }
  ],
  "quick_entry": 1,
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.js b/erpnext/non_profit/doctype/membership_type/membership_type.js
index 226981d..43311a2 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.js
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.js
@@ -5,6 +5,10 @@
 	refresh: function(frm) {
 		frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => {
 			if (val) frm.set_df_property('razorpay_plan_id', 'hidden', false);
-		})
+		});
+
+		frappe.db.get_single_value("Membership Settings", "enable_auto_invoicing").then(val => {
+			if (val) frm.set_df_property('linked_item', 'hidden', false);
+		});
 	}
 });
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.json b/erpnext/non_profit/doctype/membership_type/membership_type.json
index 319078f..6ce1ecd 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.json
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.json
@@ -8,7 +8,8 @@
  "field_order": [
   "membership_type",
   "amount",
-  "razorpay_plan_id"
+  "razorpay_plan_id",
+  "linked_item"
  ],
  "fields": [
   {
@@ -33,10 +34,17 @@
    "hidden": 1,
    "label": "Razorpay Plan ID",
    "unique": 1
+  },
+  {
+   "fieldname": "linked_item",
+   "fieldtype": "Link",
+   "label": "Linked Item",
+   "options": "Item",
+   "unique": 1
   }
  ],
  "links": [],
- "modified": "2020-03-30 12:54:07.850857",
+ "modified": "2020-08-05 15:21:43.595745",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Membership Type",
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.py b/erpnext/non_profit/doctype/membership_type/membership_type.py
index ed6b549..b95b043 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.py
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.py
@@ -4,6 +4,10 @@
 
 from __future__ import unicode_literals
 from frappe.model.document import Document
+import frappe
 
 class MembershipType(Document):
 	pass
+
+def get_membership_type(razorpay_id):
+	return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id})
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/volunteer/volunteer.json b/erpnext/non_profit/doctype/volunteer/volunteer.json
index 052b288..08b7f87 100644
--- a/erpnext/non_profit/doctype/volunteer/volunteer.json
+++ b/erpnext/non_profit/doctype/volunteer/volunteer.json
@@ -1,580 +1,148 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "autoname": "field:email", 
- "beta": 0, 
- "creation": "2017-09-19 16:16:45.676019", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:email",
+ "creation": "2017-09-19 16:16:45.676019",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "volunteer_name",
+  "column_break_5",
+  "volunteer_type",
+  "email",
+  "image",
+  "address_contacts",
+  "address_html",
+  "column_break_9",
+  "contact_html",
+  "volunteer_availability_and_skills_details",
+  "availability",
+  "availability_timeslot",
+  "column_break_12",
+  "volunteer_skills",
+  "section_break_15",
+  "note"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "volunteer_name", 
-   "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": "Volunteer Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "volunteer_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Volunteer Name",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_5", 
-   "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_5",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "volunteer_type", 
-   "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": "Volunteer Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Volunteer 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
-  }, 
+   "fieldname": "volunteer_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Volunteer Type",
+   "options": "Volunteer Type",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "email", 
-   "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": "Email", 
-   "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, 
+   "fieldname": "email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Email",
+   "reqd": 1,
    "unique": 1
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "image", 
-   "fieldtype": "Attach Image", 
-   "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": "Image", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "image",
+   "fieldtype": "Attach Image",
+   "hidden": 1,
+   "label": "Image",
+   "no_copy": 1,
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address_contacts", 
-   "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": "Address and Contact", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "fa fa-map-marker", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:!doc.__islocal;",
+   "fieldname": "address_contacts",
+   "fieldtype": "Section Break",
+   "label": "Address and Contact",
+   "options": "fa fa-map-marker"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address_html", 
-   "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": "Address HTML", 
-   "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": "address_html",
+   "fieldtype": "HTML",
+   "label": "Address HTML"
+  },
   {
-   "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": "contact_html", 
-   "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": "Contact HTML", 
-   "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": "contact_html",
+   "fieldtype": "HTML",
+   "label": "Contact HTML"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "volunteer_availability_and_skills_details", 
-   "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": "Availability and Skills", 
-   "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": "volunteer_availability_and_skills_details",
+   "fieldtype": "Section Break",
+   "label": "Availability and Skills"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "availability", 
-   "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": "Availability", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nWeekly\nWeekdays\nWeekends", 
-   "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": "availability",
+   "fieldtype": "Select",
+   "label": "Availability",
+   "options": "\nWeekly\nWeekdays\nWeekends"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "availability_timeslot", 
-   "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": "Availability Timeslot", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nMorning\nAfternoon\nEvening\nAnytime", 
-   "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": "availability_timeslot",
+   "fieldtype": "Select",
+   "label": "Availability Timeslot",
+   "options": "\nMorning\nAfternoon\nEvening\nAnytime"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_12", 
-   "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_12",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "volunteer_skills", 
-   "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": "Volunteer Skills", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Volunteer Skill", 
-   "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": "volunteer_skills",
+   "fieldtype": "Table",
+   "label": "Volunteer Skills",
+   "options": "Volunteer Skill"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_15", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_15",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "note", 
-   "fieldtype": "Long 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": "Note", 
-   "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": "note",
+   "fieldtype": "Long Text",
+   "label": "Note"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_field": "image", 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-11-04 03:36:25.776211", 
- "modified_by": "Administrator", 
- "module": "Non Profit", 
- "name": "Volunteer", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "image_field": "image",
+ "links": [],
+ "modified": "2020-09-16 23:45:15.595952",
+ "modified_by": "Administrator",
+ "module": "Non Profit",
+ "name": "Volunteer",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Non Profit Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Non Profit Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "restrict_to_domain": "Non Profit", 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "title_field": "volunteer_name", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "restrict_to_domain": "Non Profit",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "volunteer_name",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e17e949..6087ce2 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -632,7 +632,7 @@
 execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
 execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field')
 erpnext.patches.v12_0.remove_bank_remittance_custom_fields
-erpnext.patches.v12_0.generate_leave_ledger_entries
+erpnext.patches.v12_0.generate_leave_ledger_entries #27-08-2020
 execute:frappe.delete_doc_if_exists("Report", "Loan Repayment")
 erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit
 erpnext.patches.v12_0.add_variant_of_in_item_attribute_table
@@ -690,6 +690,7 @@
 erpnext.patches.v13_0.update_old_loans
 erpnext.patches.v12_0.set_serial_no_status #2020-05-21
 erpnext.patches.v12_0.update_price_list_currency_in_bom
+execute:frappe.reload_doctype('Dashboard')
 execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
 erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
 erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25
@@ -697,7 +698,7 @@
 execute:frappe.delete_doc("Report", "Department Analytics")
 execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True)
 erpnext.patches.v12_0.update_uom_conversion_factor
-execute:frappe.delete_doc_if_exists("Page", "pos") #29-05-2020
+erpnext.patches.v13_0.replace_pos_page_with_point_of_sale_page
 erpnext.patches.v13_0.delete_old_purchase_reports
 erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
 erpnext.patches.v13_0.update_subscription
@@ -718,7 +719,13 @@
 erpnext.patches.v12_0.update_item_tax_template_company
 erpnext.patches.v13_0.move_branch_code_to_bank_account
 erpnext.patches.v13_0.healthcare_lab_module_rename_doctypes
+erpnext.patches.v13_0.add_standard_navbar_items #4
 erpnext.patches.v13_0.stock_entry_enhancements
 erpnext.patches.v12_0.update_state_code_for_daman_and_diu
 erpnext.patches.v12_0.rename_lost_reason_detail
+erpnext.patches.v13_0.drop_razorpay_payload_column
 erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment
+erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports
+erpnext.patches.v13_0.rename_issue_doctype_fields
+erpnext.patches.v13_0.change_default_pos_print_format
+erpnext.patches.v13_0.set_youtube_video_id
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index c5bec19..342c129 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -36,8 +36,7 @@
 
 	for allocation in allocation_list:
 		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}):
-			allocation.update(dict(doctype="Leave Allocation"))
-			allocation_obj = frappe.get_doc(allocation)
+			allocation_obj = frappe.get_doc("Leave Allocation", allocation)
 			allocation_obj.create_leave_ledger_entry()
 
 def generate_application_leave_ledger_entries():
@@ -46,8 +45,7 @@
 
 	for application in leave_applications:
 		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': application.name}):
-			application.update(dict(doctype="Leave Application"))
-			frappe.get_doc(application).create_leave_ledger_entry()
+			frappe.get_doc("Leave Application", application.name).create_leave_ledger_entry()
 
 def generate_encashment_leave_ledger_entries():
 	''' fix ledger entries for missing leave encashment transaction '''
@@ -55,8 +53,7 @@
 
 	for encashment in leave_encashments:
 		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}):
-			encashment.update(dict(doctype="Leave Encashment"))
-			frappe.get_doc(encashment).create_leave_ledger_entry()
+			frappe.get_doc("Leave Enchashment", encashment).create_leave_ledger_entry()
 
 def generate_expiry_allocation_ledger_entries():
 	''' fix ledger entries for missing leave allocation transaction '''
@@ -65,24 +62,16 @@
 
 	for allocation in allocation_list:
 		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name, 'is_expired': 1}):
-			allocation.update(dict(doctype="Leave Allocation"))
-			allocation_obj = frappe.get_doc(allocation)
+			allocation_obj = frappe.get_doc("Leave Allocation", allocation)
 			if allocation_obj.to_date <= getdate(today()):
 				expire_allocation(allocation_obj)
 
 def get_allocation_records():
-	return frappe.get_all("Leave Allocation", filters={
-		"docstatus": 1
-		}, fields=['name', 'employee', 'leave_type', 'new_leaves_allocated',
-			'unused_leaves', 'from_date', 'to_date', 'carry_forward'
-		], order_by='to_date ASC')
+	return frappe.get_all("Leave Allocation", filters={"docstatus": 1},
+		fields=['name'], order_by='to_date ASC')
 
 def get_leaves_application_records():
-	return frappe.get_all("Leave Application", filters={
-		"docstatus": 1
-		}, fields=['name', 'employee', 'leave_type', 'total_leave_days', 'from_date', 'to_date'])
+	return frappe.get_all("Leave Application", filters={"docstatus": 1}, fields=['name'])
 
 def get_leave_encashment_records():
-	return frappe.get_all("Leave Encashment", filters={
-		"docstatus": 1
-		}, fields=['name', 'employee', 'leave_type', 'encashable_days', 'encashment_date'])
+	return frappe.get_all("Leave Encashment", filters={"docstatus": 1}, fields=['name'])
diff --git a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
index 8e60d33..d2bcb12 100644
--- a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
+++ b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py
@@ -4,17 +4,16 @@
 from __future__ import unicode_literals
 import frappe
 
+
 def execute():
 	frappe.reload_doc("erpnext_integrations", "doctype", "plaid_settings")
 	plaid_settings = frappe.get_single("Plaid Settings")
 	if plaid_settings.enabled:
-		if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env \
-			and frappe.conf.plaid_public_key and frappe.conf.plaid_secret):
+		if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env and frappe.conf.plaid_secret):
 			plaid_settings.enabled = 0
 		else:
 			plaid_settings.update({
 				"plaid_client_id": frappe.conf.plaid_client_id,
-				"plaid_public_key": frappe.conf.plaid_public_key,
 				"plaid_env": frappe.conf.plaid_env,
 				"plaid_secret": frappe.conf.plaid_secret
 			})
diff --git a/erpnext/patches/v13_0/add_standard_navbar_items.py b/erpnext/patches/v13_0/add_standard_navbar_items.py
new file mode 100644
index 0000000..d05b258
--- /dev/null
+++ b/erpnext/patches/v13_0/add_standard_navbar_items.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+# import frappe
+from erpnext.setup.install import add_standard_navbar_items
+
+def execute():
+	# Add standard navbar items for ERPNext in Navbar Settings
+	add_standard_navbar_items()
diff --git a/erpnext/patches/v13_0/change_default_pos_print_format.py b/erpnext/patches/v13_0/change_default_pos_print_format.py
new file mode 100644
index 0000000..605a29e
--- /dev/null
+++ b/erpnext/patches/v13_0/change_default_pos_print_format.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.db.sql(
+		"""UPDATE `tabPOS Profile` profile
+		SET profile.`print_format` = 'POS Invoice'
+		WHERE profile.`print_format` = 'Point of Sale'""")
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/drop_razorpay_payload_column.py b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
new file mode 100644
index 0000000..8980fd0
--- /dev/null
+++ b/erpnext/patches/v13_0/drop_razorpay_payload_column.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	if frappe.db.exists("DocType", "Membership"):
+		if 'webhook_payload' in frappe.db.get_table_columns("Membership"):
+			frappe.db.sql("alter table `tabMembership` drop column webhook_payload")
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
new file mode 100644
index 0000000..5bd6596
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2020, 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():
+	if frappe.db.exists('DocType', 'Issue'):
+		issues = frappe.db.get_all('Issue', fields=['name', 'response_by_variance', 'resolution_by_variance', 'mins_to_first_response'],
+			 order_by='creation desc')
+		frappe.reload_doc('support', 'doctype', 'issue')
+
+		# rename fields
+		rename_map = {
+			'agreement_fulfilled': 'agreement_status',
+			'mins_to_first_response': 'first_response_time'
+		}
+		for old, new in rename_map.items():
+			rename_field('Issue', old, new)
+
+		# change fieldtype to duration
+		count = 0
+		for entry in issues:
+			response_by_variance = convert_to_seconds(entry.response_by_variance, 'Hours')
+			resolution_by_variance = convert_to_seconds(entry.resolution_by_variance, 'Hours')
+			mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, 'Minutes')
+			frappe.db.set_value('Issue', entry.name, {
+				'response_by_variance': response_by_variance,
+				'resolution_by_variance': resolution_by_variance,
+				'first_response_time': mins_to_first_response
+			})
+			# commit after every 100 updates
+			count += 1
+			if count%100 == 0:
+				frappe.db.commit()
+
+	if frappe.db.exists('DocType', 'Opportunity'):
+		opportunities = frappe.db.get_all('Opportunity', fields=['name', 'mins_to_first_response'], order_by='creation desc')
+		frappe.reload_doc('crm', 'doctype', 'opportunity')
+		rename_field('Opportunity', 'mins_to_first_response', 'first_response_time')
+
+		# change fieldtype to duration
+		count = 0
+		for entry in opportunities:
+			mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, 'Minutes')
+			frappe.db.set_value('Opportunity', entry.name, 'first_response_time', mins_to_first_response)
+			# commit after every 100 updates
+			count += 1
+			if count%100 == 0:
+				frappe.db.commit()
+
+	# renamed reports from "Minutes to First Response for Issues" to "First Response Time for Issues". Same for Opportunity
+	for report in ['Minutes to First Response for Issues', 'Minutes to First Response for Opportunity']:
+		if frappe.db.exists('Report', report):
+			frappe.delete_doc('Report', report)
+
+
+def convert_to_seconds(value, unit):
+	seconds = 0
+	if unit == 'Hours':
+		seconds = value * 3600
+	if unit == 'Minutes':
+		seconds = value * 60
+	return seconds
diff --git a/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py b/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py
new file mode 100644
index 0000000..390e217
--- /dev/null
+++ b/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py
@@ -0,0 +1,6 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	if frappe.db.exists("Page", "point-of-sale"):
+		frappe.rename_doc("Page", "pos", "point-of-sale", 1, 1)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/set_youtube_video_id.py b/erpnext/patches/v13_0/set_youtube_video_id.py
new file mode 100644
index 0000000..c3b49eb
--- /dev/null
+++ b/erpnext/patches/v13_0/set_youtube_video_id.py
@@ -0,0 +1,10 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.utilities.doctype.video.video import get_id_from_url
+
+def execute():
+	frappe.reload_doc("utilities", "doctype","video")
+
+	for video in frappe.get_all("Video", fields=["name", "url", "youtube_video_id"]):
+		if video.url and not video.youtube_video_id:
+			frappe.db.set_value("Video", video.name, "youtube_video_id", get_id_from_url(video.url))
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py
new file mode 100644
index 0000000..ecc7822
--- /dev/null
+++ b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py
@@ -0,0 +1,10 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.regional.india.setup import add_custom_roles_for_reports
+
+def execute():
+    company = frappe.get_all('Company', filters = {'country': 'India'})
+    if not company:
+        return
+
+    add_custom_roles_for_reports()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py
index dcc4f95..0bdcc9c 100644
--- a/erpnext/patches/v13_0/stock_entry_enhancements.py
+++ b/erpnext/patches/v13_0/stock_entry_enhancements.py
@@ -24,4 +24,8 @@
         if not frappe.db.exists('Warehouse Type', 'Transit'):
             doc = frappe.new_doc('Warehouse Type')
             doc.name = 'Transit'
-            doc.insert()
\ No newline at end of file
+            doc.insert()
+
+        frappe.reload_doc("stock", "doctype", "stock_entry_type")
+        frappe.delete_doc_if_exists("Stock Entry Type", "Send to Warehouse")
+        frappe.delete_doc_if_exists("Stock Entry Type", "Receive at Warehouse")
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index ef174bd..e3dc907 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -5,8 +5,8 @@
 from __future__ import unicode_literals
 import frappe
 from frappe.model.document import Document
-from frappe import _
-from frappe.utils import getdate, date_diff
+from frappe import _, bold
+from frappe.utils import getdate, date_diff, comma_and, formatdate
 
 class AdditionalSalary(Document):
 
@@ -22,9 +22,37 @@
 
 	def validate(self):
 		self.validate_dates()
+		self.validate_recurring_additional_salary_overlap()
 		if self.amount < 0:
 			frappe.throw(_("Amount should not be less than zero."))
 
+	def validate_recurring_additional_salary_overlap(self):
+		if self.is_recurring:
+			additional_salaries = frappe.db.sql("""
+				SELECT
+					name
+				FROM `tabAdditional Salary`
+				WHERE
+					employee=%s
+					AND name <> %s
+					AND docstatus=1
+					AND is_recurring=1
+					AND salary_component = %s
+					AND to_date >= %s
+					AND from_date <= %s""",
+				(self.employee, self.name, self.salary_component, self.from_date, self.to_date), as_dict = 1)
+
+			additional_salaries = [salary.name for salary in additional_salaries]
+
+			if additional_salaries and len(additional_salaries):
+				frappe.throw(_("Additional Salary: {0} already exist for Salary Component: {1} for period {2} and {3}").format(
+					bold(comma_and(additional_salaries)),
+					bold(self.salary_component),
+					bold(formatdate(self.from_date)),
+					bold(formatdate(self.to_date)
+				)))
+
+
 	def validate_dates(self):
 		date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index 9eef16b..9ba4cdc 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -13,13 +13,15 @@
 	for f in fields:
 		doctype = f.get_link_doctype()
 
-		# apply enable/disable filter
+		# apply enable/disable/show_in_website filter
 		meta = frappe.get_meta(doctype)
 		filters = {}
 		if meta.has_field('enabled'):
 			filters['enabled'] = 1
 		if meta.has_field('disabled'):
 			filters['disabled'] = 0
+		if meta.has_field('show_in_website'):
+			filters['show_in_website'] = 1
 
 		values = [d.name for d in frappe.get_all(doctype, filters)]
 		filter_data.append([f, values])
diff --git a/erpnext/projects/doctype/dependent_task/dependent_task.json b/erpnext/projects/doctype/dependent_task/dependent_task.json
index e00a2b2..858a554 100644
--- a/erpnext/projects/doctype/dependent_task/dependent_task.json
+++ b/erpnext/projects/doctype/dependent_task/dependent_task.json
@@ -48,8 +48,8 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2016-10-26 17:37:25.638190", 
- "modified_by": "rohitw1991@gmail.com", 
+ "modified": "2020-09-18 17:26:09.703215", 
+ "modified_by": "Administrator", 
  "module": "Projects", 
  "name": "Dependent Task", 
  "name_case": "", 
diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js
index 9870f81..eb709e5 100644
--- a/erpnext/public/js/conf.js
+++ b/erpnext/public/js/conf.js
@@ -3,32 +3,6 @@
 
 frappe.provide('erpnext');
 
-// add toolbar icon
-$(document).bind('toolbar_setup', function() {
-	frappe.app.name = "ERPNext";
-
-	frappe.help_feedback_link = '<p><a class="text-muted" \
-		href="https://discuss.erpnext.com">Feedback</a></p>'
-
-
-	$('[data-link="docs"]').attr("href", "https://erpnext.com/docs")
-	$('[data-link="issues"]').attr("href", "https://github.com/frappe/erpnext/issues")
-
-
-	// default documentation goes to erpnext
-	// $('[data-link-type="documentation"]').attr('data-path', '/erpnext/manual/index');
-
-	// additional help links for erpnext
-	var $help_menu = $('.dropdown-help ul .documentation-links');
-	$('<li><a data-link-type="forum" href="https://erpnext.com/docs/user/manual" \
-		target="_blank">'+__('Documentation')+'</a></li>').insertBefore($help_menu);
-	$('<li><a data-link-type="forum" href="https://discuss.erpnext.com" \
-		target="_blank">'+__('User Forum')+'</a></li>').insertBefore($help_menu);
-	$('<li><a href="https://github.com/frappe/erpnext/issues" \
-		target="_blank">'+__('Report an Issue')+'</a></li>').insertBefore($help_menu);
-
-});
-
 // preferred modules for breadcrumbs
 $.extend(frappe.breadcrumbs.preferred, {
 	"Item Group": "Stock",
@@ -37,7 +11,9 @@
 	"Territory": "Selling",
 	"Sales Person": "Selling",
 	"Sales Partner": "Selling",
-	"Brand": "Stock"
+	"Brand": "Stock",
+	"Maintenance Schedule": "Support",
+	"Maintenance Visit": "Support"
 });
 
 $.extend(frappe.breadcrumbs.module_map, {
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index f4eaad5..6e97d81 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -120,7 +120,7 @@
 
 var get_payment_mode_account = function(frm, mode_of_payment, callback) {
 	if(!frm.doc.company) {
-		frappe.throw(__("Please select the Company first"));
+		frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")});
 	}
 
 	if(!mode_of_payment) {
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index a4cc68b..cb76c87 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -503,11 +503,11 @@
 
 					if(!r.exc && r.message) {
 						remove_empty_first_row(frm);
-						for ( var i=0; i< r.message.length; i++ ) {
+						for (var i=0; i< r.message.length; i++) {
 							var d = frm.add_child("items");
 							var item = r.message[i];
-							for ( var key in  item) {
-								if ( !is_null(item[key]) ) {
+							for (var key in  item) {
+								if (!is_null(item[key]) && key !== "doctype") {
 									d[key] = item[key];
 								}
 							}
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 6951539..be30086 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -595,7 +595,7 @@
 			$.each(actual_taxes_dict, function(key, value) {
 				if (value) total_actual_tax += value;
 			});
-			
+
 			return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total"));
 		}
 	},
@@ -673,23 +673,14 @@
 			);
 		}
 
-		frappe.db.get_value('Sales Invoice Payment', {'parent': this.frm.doc.pos_profile, 'default': 1},
-			['mode_of_payment', 'account', 'type'], (value) => {
-				if (this.frm.is_dirty()) {
-					frappe.model.clear_table(this.frm.doc, 'payments');
-					if (value) {
-						let row = frappe.model.add_child(this.frm.doc, 'Sales Invoice Payment', 'payments');
-						row.mode_of_payment = value.mode_of_payment;
-						row.type = value.type;
-						row.account = value.account;
-						row.default = 1;
-						row.amount = total_amount_to_pay;
-					} else {
-						this.frm.set_value('is_pos', 1);
-					}
-					this.frm.refresh_fields();
-				}
-			}, 'Sales Invoice');
+		this.frm.doc.payments.find(pay => {
+			if (pay.default) {
+				pay.amount = total_amount_to_pay;
+			} else {
+				pay.amount = 0.0
+			}
+		});
+		this.frm.refresh_fields();
 
 		this.calculate_paid_amount();
 	},
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 436a232..3391179 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -417,7 +417,7 @@
 		var taxes_and_charges_field = frappe.meta.get_docfield(me.frm.doc.doctype, "taxes_and_charges",
 			me.frm.doc.name);
 
-		if (!this.frm.doc.taxes_and_charges && this.frm.doc.taxes) {
+		if (!this.frm.doc.taxes_and_charges && this.frm.doc.taxes && this.frm.doc.taxes.length > 0) {
 			return;
 		}
 
@@ -781,10 +781,23 @@
 		else var date = this.frm.doc.transaction_date;
 
 		if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
-			in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)){
+			in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
 			erpnext.utils.get_shipping_address(this.frm, function(){
 				set_party_account(set_pricing);
 			})
+
+			// Get default company billing address in Purchase Invoice, Order and Receipt
+			frappe.call({
+				'method': 'frappe.contacts.doctype.address.address.get_default_address',
+				'args': {
+					'doctype': 'Company',
+					'name': this.frm.doc.company
+				},
+				'callback': function(r) {
+					me.frm.set_value('billing_address', r.message);
+				}
+			});
+
 		} else {
 			set_party_account(set_pricing);
 		}
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index bcab0d8..87982f1 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -467,6 +467,33 @@
 		disabled: 0,
 		label: __('Item Code')
 	}, {
+		fieldtype:'Link',
+		fieldname:'uom',
+		options: 'UOM',
+		read_only: 0,
+		label: __('UOM'),
+		reqd: 1,
+		onchange: function () {
+			frappe.call({
+				method: "erpnext.stock.get_item_details.get_conversion_factor",
+				args: { item_code: this.doc.item_code, uom: this.value },
+				callback: r => {
+					if(!r.exc) {
+						if (this.doc.conversion_factor == r.message.conversion_factor) return;
+						
+						const docname = this.doc.docname;
+						dialog.fields_dict.trans_items.df.data.some(doc => {
+							if (doc.docname == docname) {
+								doc.conversion_factor = r.message.conversion_factor;
+								dialog.fields_dict.trans_items.grid.refresh();
+								return true;
+							}
+						})
+					}
+				}
+			});
+		}
+	}, {
 		fieldtype:'Float',
 		fieldname:"qty",
 		default: 0,
@@ -546,6 +573,7 @@
 			"conversion_factor": d.conversion_factor,
 			"qty": d.qty,
 			"rate": d.rate,
+			"uom": d.uom
 		});
 		this.data = dialog.fields_dict.trans_items.df.data;
 		dialog.fields_dict.trans_items.grid.refresh();
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 0653267..44e75ae 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -224,6 +224,10 @@
 		party = frm.doc.party_name;
 	}
 
+	if (!frm.doc.company) {
+		frappe.throw(__("Kindly select the company first"));
+	}
+
 	frappe.call({
 		method: "erpnext.accounts.party.set_taxes",
 		args: {
@@ -292,4 +296,4 @@
 	} else {
 		frappe.msgprint(__("Select company first"));
 	}
-}
\ No newline at end of file
+}
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/__init__.py b/erpnext/regional/germany/utils/__init__.py
similarity index 100%
copy from erpnext/support/report/minutes_to_first_response_for_issues/__init__.py
copy to erpnext/regional/germany/utils/__init__.py
diff --git a/erpnext/crm/report/minutes_to_first_response_for_opportunity/__init__.py b/erpnext/regional/germany/utils/datev/__init__.py
similarity index 100%
copy from erpnext/crm/report/minutes_to_first_response_for_opportunity/__init__.py
copy to erpnext/regional/germany/utils/datev/__init__.py
diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py
similarity index 89%
rename from erpnext/regional/report/datev/datev_constants.py
rename to erpnext/regional/germany/utils/datev/datev_constants.py
index e063703..63f9a77 100644
--- a/erpnext/regional/report/datev/datev_constants.py
+++ b/erpnext/regional/germany/utils/datev/datev_constants.py
@@ -460,80 +460,8 @@
 	"Sprach-ID"
 ]
 
-QUERY_REPORT_COLUMNS = [
-	{
-		"label": "Umsatz (ohne Soll/Haben-Kz)",
-		"fieldname": "Umsatz (ohne Soll/Haben-Kz)",
-		"fieldtype": "Currency",
-		"width": 100
-	},
-	{
-		"label": "Soll/Haben-Kennzeichen",
-		"fieldname": "Soll/Haben-Kennzeichen",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Konto",
-		"fieldname": "Konto",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Gegenkonto (ohne BU-Schlüssel)",
-		"fieldname": "Gegenkonto (ohne BU-Schlüssel)",
-		"fieldtype": "Data",
-		"width": 100
-	},
-	{
-		"label": "Belegdatum",
-		"fieldname": "Belegdatum",
-		"fieldtype": "Date",
-		"width": 100
-	},
-	{
-		"label": "Belegfeld 1",
-		"fieldname": "Belegfeld 1",
-		"fieldtype": "Data",
-		"width": 150
-	},
-	{
-		"label": "Buchungstext",
-		"fieldname": "Buchungstext",
-		"fieldtype": "Text",
-		"width": 300
-	},
-	{
-		"label": "Beleginfo - Art 1",
-		"fieldname": "Beleginfo - Art 1",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Inhalt 1",
-		"fieldname": "Beleginfo - Inhalt 1",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 1",
-		"width": 150
-	},
-	{
-		"label": "Beleginfo - Art 2",
-		"fieldname": "Beleginfo - Art 2",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100
-	},
-	{
-		"label": "Beleginfo - Inhalt 2",
-		"fieldname": "Beleginfo - Inhalt 2",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 2",
-		"width": 150
-	}
-]
-
 class DataCategory():
+
 	"""Field of the CSV Header."""
 
 	DEBTORS_CREDITORS = "16"
@@ -542,6 +470,7 @@
 	POSTING_TEXT_CONSTANTS = "67"
 
 class FormatName():
+
 	"""Field of the CSV Header, corresponds to DataCategory."""
 
 	DEBTORS_CREDITORS = "Debitoren/Kreditoren"
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
new file mode 100644
index 0000000..aae734f
--- /dev/null
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -0,0 +1,174 @@
+# coding: utf-8
+from __future__ import unicode_literals
+
+import datetime
+import zipfile
+from csv import QUOTE_NONNUMERIC
+from six import BytesIO
+
+import six
+import frappe
+import pandas as pd
+from frappe import _
+from .datev_constants import DataCategory
+
+
+def get_datev_csv(data, filters, csv_class):
+	"""
+	Fill in missing columns and return a CSV in DATEV Format.
+
+	For automatic processing, DATEV requires the first line of the CSV file to
+	hold meta data such as the length of account numbers oder the category of
+	the data.
+
+	Arguments:
+	data -- array of dictionaries
+	filters -- dict
+	csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS
+	"""
+	empty_df = pd.DataFrame(columns=csv_class.COLUMNS)
+	data_df = pd.DataFrame.from_records(data)
+	result = empty_df.append(data_df, sort=True)
+
+	if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
+		result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])
+
+	if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
+		result['Sprach-ID'] = 'de-DE'
+
+	data = result.to_csv(
+		# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
+		sep=str(';'),
+		# European decimal seperator
+		decimal=',',
+		# Windows "ANSI" encoding
+		encoding='latin_1',
+		# format date as DDMM
+		date_format='%d%m',
+		# Windows line terminator
+		line_terminator='\r\n',
+		# Do not number rows
+		index=False,
+		# Use all columns defined above
+		columns=csv_class.COLUMNS,
+		# Quote most fields, even currency values with "," separator
+		quoting=QUOTE_NONNUMERIC
+	)
+
+	if not six.PY2:
+		data = data.encode('latin_1')
+
+	header = get_header(filters, csv_class)
+	header = ';'.join(header).encode('latin_1')
+
+	# 1st Row: Header with meta data
+	# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.
+	# 3rd - nth Row: Data (Nutzdaten)
+	return header + b'\r\n' + data
+
+
+def get_header(filters, csv_class):
+	description = filters.get('voucher_type', csv_class.FORMAT_NAME)
+	company = filters.get('company')
+	datev_settings = frappe.get_doc('DATEV Settings', {'client': company})
+	default_currency = frappe.get_value('Company', company, 'default_currency')
+	coa = frappe.get_value('Company', company, 'chart_of_accounts')
+	coa_short_code = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
+
+	header = [
+		# DATEV format
+		#	"DTVF" = created by DATEV software,
+		#	"EXTF" = created by other software
+		'"EXTF"',
+		# version of the DATEV format
+		#	141 = 1.41, 
+		#	510 = 5.10,
+		#	720 = 7.20
+		'700',
+		csv_class.DATA_CATEGORY,
+		'"%s"' % csv_class.FORMAT_NAME,
+		# Format version (regarding format name)
+		csv_class.FORMAT_VERSION,
+		# Generated on
+		datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '000',
+		# Imported on -- stays empty
+		'',
+		# Origin. Any two symbols, will be replaced by "SV" on import.
+		'"EN"',
+		# I = Exported by
+		'"%s"' % frappe.session.user,
+		# J = Imported by -- stays empty
+		'',
+		# K = Tax consultant number (Beraternummer)
+		datev_settings.get('consultant_number', '0000000'),
+		# L = Tax client number (Mandantennummer)
+		datev_settings.get('client_number', '00000'),
+		# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
+		frappe.utils.formatdate(frappe.defaults.get_user_default('year_start_date'), 'yyyyMMdd'),
+		# N = Length of account numbers (Sachkontenlänge)
+		datev_settings.get('account_number_length', '4'),
+		# O = Transaction batch start date (YYYYMMDD)
+		frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
+		# P = Transaction batch end date (YYYYMMDD)
+		frappe.utils.formatdate(filters.get('to_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
+		# Q = Description (for example, "Sales Invoice") Max. 30 chars
+		'"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
+		# R = Diktatkürzel
+		'',
+		# S = Buchungstyp
+		#	1 = Transaction batch (Finanzbuchführung),
+		#	2 = Annual financial statement (Jahresabschluss)
+		'1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
+		# T = Rechnungslegungszweck
+		#	0 oder leer = vom Rechnungslegungszweck unabhängig
+		#	50 = Handelsrecht
+		#	30 = Steuerrecht
+		#	64 = IFRS
+		#	40 = Kalkulatorik
+		#	11 = Reserviert
+		#	12 = Reserviert
+		'0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
+		# U = Festschreibung
+		# TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1"
+		'0',
+		# V = Default currency, for example, "EUR"
+		'"%s"' % default_currency if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
+		# reserviert
+		'',
+		# Derivatskennzeichen
+		'',
+		# reserviert
+		'',
+		# reserviert
+		'',
+		# SKR
+		'"%s"' % coa_short_code,
+		# Branchen-Lösungs-ID
+		'',
+		# reserviert
+		'',
+		# reserviert
+		'',
+		# Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung)
+		''
+	]
+	return header
+
+
+def download_csv_files_as_zip(csv_data_list):
+	"""
+	Put CSV files in a zip archive and send that to the client.
+
+	Params:
+	csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}]
+	"""
+	zip_buffer = BytesIO()
+
+	datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
+	for csv_file in csv_data_list:
+		datev_zip.writestr(csv_file.get('file_name'), csv_file.get('csv_data'))
+	datev_zip.close()
+
+	frappe.response['filecontent'] = zip_buffer.getvalue()
+	frappe.response['filename'] = 'DATEV.zip'
+	frappe.response['type'] = 'binary'
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 290694a..cbcd6e3 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -73,6 +73,19 @@
 				]
 			)).insert()
 
+	for report_name in ('HSN-wise-summary of outward supplies', 'GSTR-1', 'GSTR-2'):
+
+		if not frappe.db.get_value('Custom Role', dict(report=report_name)):
+			frappe.get_doc(dict(
+				doctype='Custom Role',
+				report=report_name,
+				roles= [
+					dict(role='Accounts User'),
+					dict(role='Accounts Manager'),
+					dict(role='Auditor')
+				]
+			)).insert()
+
 def add_permissions():
 	for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'):
 		add_permission(doctype, 'All', 0)
diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js
index fbccc6b..3b6a28f 100644
--- a/erpnext/regional/india/taxes.js
+++ b/erpnext/regional/india/taxes.js
@@ -6,6 +6,9 @@
 		shipping_address: function(frm) {
 			frm.trigger('get_tax_template');
 		},
+		supplier_address: function(frm) {
+			frm.trigger('get_tax_template');
+		},
 		tax_category: function(frm) {
 			frm.trigger('get_tax_template');
 		},
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index 7fec94e..dd818e6 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -9,31 +9,91 @@
 """
 from __future__ import unicode_literals
 
-import datetime
 import json
-import zipfile
-import six
 import frappe
-import pandas as pd
-
 from frappe import _
-from csv import QUOTE_NONNUMERIC
-from six import BytesIO
 from six import string_types
-from .datev_constants import DataCategory
-from .datev_constants import Transactions
-from .datev_constants import DebtorsCreditors
-from .datev_constants import AccountNames
-from .datev_constants import QUERY_REPORT_COLUMNS
+from erpnext.regional.germany.utils.datev.datev_csv import download_csv_files_as_zip, get_datev_csv
+from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames
+
+COLUMNS = [
+	{
+		"label": "Umsatz (ohne Soll/Haben-Kz)",
+		"fieldname": "Umsatz (ohne Soll/Haben-Kz)",
+		"fieldtype": "Currency",
+		"width": 100
+	},
+	{
+		"label": "Soll/Haben-Kennzeichen",
+		"fieldname": "Soll/Haben-Kennzeichen",
+		"fieldtype": "Data",
+		"width": 100
+	},
+	{
+		"label": "Konto",
+		"fieldname": "Konto",
+		"fieldtype": "Data",
+		"width": 100
+	},
+	{
+		"label": "Gegenkonto (ohne BU-Schlüssel)",
+		"fieldname": "Gegenkonto (ohne BU-Schlüssel)",
+		"fieldtype": "Data",
+		"width": 100
+	},
+	{
+		"label": "Belegdatum",
+		"fieldname": "Belegdatum",
+		"fieldtype": "Date",
+		"width": 100
+	},
+	{
+		"label": "Belegfeld 1",
+		"fieldname": "Belegfeld 1",
+		"fieldtype": "Data",
+		"width": 150
+	},
+	{
+		"label": "Buchungstext",
+		"fieldname": "Buchungstext",
+		"fieldtype": "Text",
+		"width": 300
+	},
+	{
+		"label": "Beleginfo - Art 1",
+		"fieldname": "Beleginfo - Art 1",
+		"fieldtype": "Link",
+		"options": "DocType",
+		"width": 100
+	},
+	{
+		"label": "Beleginfo - Inhalt 1",
+		"fieldname": "Beleginfo - Inhalt 1",
+		"fieldtype": "Dynamic Link",
+		"options": "Beleginfo - Art 1",
+		"width": 150
+	},
+	{
+		"label": "Beleginfo - Art 2",
+		"fieldname": "Beleginfo - Art 2",
+		"fieldtype": "Link",
+		"options": "DocType",
+		"width": 100
+	},
+	{
+		"label": "Beleginfo - Inhalt 2",
+		"fieldname": "Beleginfo - Inhalt 2",
+		"fieldtype": "Dynamic Link",
+		"options": "Beleginfo - Art 2",
+		"width": 150
+	}
+]
 
 
 def execute(filters=None):
 	"""Entry point for frappe."""
 	validate(filters)
-	result = get_transactions(filters, as_dict=0)
-	columns = QUERY_REPORT_COLUMNS
-
-	return columns, result
+	return COLUMNS, get_transactions(filters, as_dict=0)
 
 
 def validate(filters):
@@ -240,146 +300,8 @@
 	""", filters, as_dict=1)
 
 
-def get_datev_csv(data, filters, csv_class):
-	"""
-	Fill in missing columns and return a CSV in DATEV Format.
-
-	For automatic processing, DATEV requires the first line of the CSV file to
-	hold meta data such as the length of account numbers oder the category of
-	the data.
-
-	Arguments:
-	data -- array of dictionaries
-	filters -- dict
-	csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS
-	"""
-	empty_df = pd.DataFrame(columns=csv_class.COLUMNS)
-	data_df = pd.DataFrame.from_records(data)
-
-	result = empty_df.append(data_df, sort=True)
-
-	if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
-		result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])
-
-	if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
-		result['Sprach-ID'] = 'de-DE'
-
-	data = result.to_csv(
-		# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
-		sep=str(';'),
-		# European decimal seperator
-		decimal=',',
-		# Windows "ANSI" encoding
-		encoding='latin_1',
-		# format date as DDMM
-		date_format='%d%m',
-		# Windows line terminator
-		line_terminator='\r\n',
-		# Do not number rows
-		index=False,
-		# Use all columns defined above
-		columns=csv_class.COLUMNS,
-		# Quote most fields, even currency values with "," separator
-		quoting=QUOTE_NONNUMERIC
-	)
-
-	if not six.PY2:
-		data = data.encode('latin_1')
-
-	header = get_header(filters, csv_class)
-	header = ';'.join(header).encode('latin_1')
-
-	# 1st Row: Header with meta data
-	# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.
-	# 3rd - nth Row: Data (Nutzdaten)
-	return header + b'\r\n' + data
-
-
-def get_header(filters, csv_class):
-	description = filters.get('voucher_type', csv_class.FORMAT_NAME)
-
-	header = [
-		# DATEV format
-		#	"DTVF" = created by DATEV software,
-		#	"EXTF" = created by other software
-		'"EXTF"',
-		# version of the DATEV format
-		#	141 = 1.41, 
-		#	510 = 5.10,
-		#	720 = 7.20
-		'700',
-		csv_class.DATA_CATEGORY,
-		'"%s"' % csv_class.FORMAT_NAME,
-		# Format version (regarding format name)
-		csv_class.FORMAT_VERSION,
-		# Generated on
-		datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000',
-		# Imported on -- stays empty
-		'',
-		# Origin. Any two symbols, will be replaced by "SV" on import.
-		'"EN"',
-		# I = Exported by
-		'"%s"' % frappe.session.user,
-		# J = Imported by -- stays empty
-		'',
-		# K = Tax consultant number (Beraternummer)
-		filters.get('consultant_number', '0000000'),
-		# L = Tax client number (Mandantennummer)
-		filters.get('client_number', '00000'),
-		# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
-		frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"),
-		# N = Length of account numbers (Sachkontenlänge)
-		'%d' % filters.get('acc_len', 4),
-		# O = Transaction batch start date (YYYYMMDD)
-		frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# P = Transaction batch end date (YYYYMMDD)
-		frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# Q = Description (for example, "Sales Invoice") Max. 30 chars
-		'"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# R = Diktatkürzel
-		'',
-		# S = Buchungstyp
-		#	1 = Transaction batch (Finanzbuchführung),
-		#	2 = Annual financial statement (Jahresabschluss)
-		'1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# T = Rechnungslegungszweck
-		#	0 oder leer = vom Rechnungslegungszweck unabhängig
-		#	50 = Handelsrecht
-		#	30 = Steuerrecht
-		#	64 = IFRS
-		#	40 = Kalkulatorik
-		#	11 = Reserviert
-		#	12 = Reserviert
-		'0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# U = Festschreibung
-		# TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1"
-		'0',
-		# V = Default currency, for example, "EUR"
-		'"%s"' % filters.get('default_currency', 'EUR') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
-		# reserviert
-		'',
-		# Derivatskennzeichen
-		'',
-		# reserviert
-		'',
-		# reserviert
-		'',
-		# SKR
-		'"%s"' % filters.get('skr', '04'),
-		# Branchen-Lösungs-ID
-		'',
-		# reserviert
-		'',
-		# reserviert
-		'',
-		# Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung)
-		''
-	]
-	return header
-
-
 @frappe.whitelist()
-def download_datev_csv(filters=None):
+def download_datev_csv(filters):
 	"""
 	Provide accounting entries for download in DATEV format.
 
@@ -400,38 +322,26 @@
 	coa = frappe.get_value('Company', filters.get('company'), 'chart_of_accounts')
 	filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
 
-	# set account number length
-	account_numbers = frappe.get_list('Account', fields=['account_number'], filters={'is_group': 0, 'account_number': ('!=', '')})
-	filters['acc_len'] = max([len(a.account_number) for a in account_numbers])
-
-	filters['consultant_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'consultant_number')
-	filters['client_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'client_number')
-	filters['default_currency'] = frappe.get_value('Company', filters.get('company'), 'default_currency')
-
-	# This is where my zip will be written
-	zip_buffer = BytesIO()
-	# This is my zip file
-	datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
-
 	transactions = get_transactions(filters)
-	transactions_csv = get_datev_csv(transactions, filters, csv_class=Transactions)
-	datev_zip.writestr('EXTF_Buchungsstapel.csv', transactions_csv)
-
 	account_names = get_account_names(filters)
-	account_names_csv = get_datev_csv(account_names, filters, csv_class=AccountNames)
-	datev_zip.writestr('EXTF_Kontenbeschriftungen.csv', account_names_csv)
-
 	customers = get_customers(filters)
-	customers_csv = get_datev_csv(customers, filters, csv_class=DebtorsCreditors)
-	datev_zip.writestr('EXTF_Kunden.csv', customers_csv)
-
 	suppliers = get_suppliers(filters)
-	suppliers_csv = get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors)
-	datev_zip.writestr('EXTF_Lieferanten.csv', suppliers_csv)
-	
-	# You must call close() before exiting your program or essential records will not be written.
-	datev_zip.close()
 
-	frappe.response['filecontent'] = zip_buffer.getvalue()
-	frappe.response['filename'] = 'DATEV.zip'
-	frappe.response['type'] = 'binary'
+	download_csv_files_as_zip([
+		{
+			'file_name': 'EXTF_Buchungsstapel.csv',
+			'csv_data': get_datev_csv(transactions, filters, csv_class=Transactions)
+		},
+		{
+			'file_name': 'EXTF_Kontenbeschriftungen.csv',
+			'csv_data': get_datev_csv(account_names, filters, csv_class=AccountNames)
+		},
+		{
+			'file_name': 'EXTF_Kunden.csv',
+			'csv_data': get_datev_csv(customers, filters, csv_class=DebtorsCreditors)
+		},
+		{
+			'file_name': 'EXTF_Lieferanten.csv',
+			'csv_data': get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors)
+		},
+	])
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
index eed62a8..9529923 100644
--- a/erpnext/regional/report/datev/test_datev.py
+++ b/erpnext/regional/report/datev/test_datev.py
@@ -1,32 +1,22 @@
 # coding=utf-8
 from __future__ import unicode_literals
 
-import os
-import json
 import zipfile
+import frappe
 from six import BytesIO
 from unittest import TestCase
-
-import frappe
-from frappe.utils import getdate, today, now_datetime, cstr
-from frappe.test_runner import make_test_objects
+from frappe.utils import today, now_datetime, cstr
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
 
 from erpnext.regional.report.datev.datev import validate
 from erpnext.regional.report.datev.datev import get_transactions
 from erpnext.regional.report.datev.datev import get_customers
 from erpnext.regional.report.datev.datev import get_suppliers
 from erpnext.regional.report.datev.datev import get_account_names
-from erpnext.regional.report.datev.datev import get_datev_csv
-from erpnext.regional.report.datev.datev import get_header
 from erpnext.regional.report.datev.datev import download_datev_csv
 
-from erpnext.regional.report.datev.datev_constants import DataCategory
-from erpnext.regional.report.datev.datev_constants import Transactions
-from erpnext.regional.report.datev.datev_constants import DebtorsCreditors
-from erpnext.regional.report.datev.datev_constants import AccountNames
-from erpnext.regional.report.datev.datev_constants import QUERY_REPORT_COLUMNS
+from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, get_header
+from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames
 
 def make_company(company_name, abbr):
 	if not frappe.db.exists("Company", company_name):
diff --git a/erpnext/regional/report/gstr_1/gstr_1.json b/erpnext/regional/report/gstr_1/gstr_1.json
index 2012bb8..75aed8c 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.json
+++ b/erpnext/regional/report/gstr_1/gstr_1.json
@@ -7,7 +7,7 @@
  "doctype": "Report",
  "idx": 0,
  "is_standard": "Yes",
- "modified": "2019-06-30 19:33:59.769385",
+ "modified": "2019-09-03 19:33:59.769385",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "GSTR-1",
@@ -16,15 +16,5 @@
  "ref_doctype": "GL Entry",
  "report_name": "GSTR-1",
  "report_type": "Script Report",
- "roles": [
-  {
-   "role": "Accounts User"
-  },
-  {
-   "role": "Accounts Manager"
-  },
-  {
-   "role": "Auditor"
-  }
- ]
+ "roles": []
 }
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_2/gstr_2.json b/erpnext/regional/report/gstr_2/gstr_2.json
index 929ed91..b70d0f9 100644
--- a/erpnext/regional/report/gstr_2/gstr_2.json
+++ b/erpnext/regional/report/gstr_2/gstr_2.json
@@ -1,29 +1,19 @@
 {
- "add_total_row": 0, 
- "apply_user_permissions": 1, 
- "creation": "2018-01-29 12:59:55.650445", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 0, 
- "is_standard": "Yes", 
- "modified": "2018-01-29 12:59:55.650445", 
- "modified_by": "Administrator", 
- "module": "Regional", 
- "name": "GSTR-2", 
- "owner": "Administrator", 
- "ref_doctype": "GL Entry", 
- "report_name": "GSTR-2", 
- "report_type": "Script Report", 
- "roles": [
-  {
-   "role": "Accounts User"
-  }, 
-  {
-   "role": "Accounts Manager"
-  }, 
-  {
-   "role": "Auditor"
-  }
- ]
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2018-01-29 12:59:55.650445",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2018-09-03 12:59:55.650445",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "GSTR-2",
+ "owner": "Administrator",
+ "ref_doctype": "GL Entry",
+ "report_name": "GSTR-2",
+ "report_type": "Script Report",
+ "roles": []
 }
\ No newline at end of file
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js
index dfdf9dc..b757d53 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js
@@ -46,5 +46,28 @@
 	],
 	onload: (report) => {
 		fetch_gstins(report);
+
+		report.page.add_inner_button(__("Download JSON"), function () {
+			var filters = report.get_values();
+
+			frappe.call({
+				method: 'erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies.get_json',
+				args: {
+					data: report.data,
+					report_name: report.report_name,
+					filters: filters
+				},
+				callback: function(r) {
+					if (r.message) {
+						const args = {
+							cmd: 'erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies.download_json_file',
+							data: r.message.data,
+							report_name: r.message.report_name
+						};
+						open_url_post(frappe.request.url, args);
+					}
+				}
+			});
+		});
 	}
 };
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json
index 124a720..cc6ad57 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json
@@ -1,28 +1,18 @@
 {
- "add_total_row": 0, 
- "creation": "2018-04-26 10:49:29.159400", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 0, 
- "is_standard": "Yes", 
- "modified": "2019-04-26 12:59:38.603649", 
- "modified_by": "Administrator", 
- "module": "Regional", 
- "name": "HSN-wise-summary of outward supplies", 
- "owner": "Administrator", 
- "ref_doctype": "Sales Invoice", 
- "report_name": "HSN-wise-summary of outward supplies", 
- "report_type": "Script Report", 
- "roles": [
-  {
-   "role": "Accounts User"
-  }, 
-  {
-   "role": "Accounts Manager"
-  }, 
-  {
-   "role": "Auditor"
-  }
- ]
+ "add_total_row": 0,
+ "creation": "2018-04-26 10:49:29.159400",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2019-09-03 12:59:38.603649",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "HSN-wise-summary of outward supplies",
+ "owner": "Administrator",
+ "ref_doctype": "Sales Invoice",
+ "report_name": "HSN-wise-summary of outward supplies",
+ "report_type": "Script Report",
+ "roles": []
 }
\ No newline at end of file
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
index a3ed4ce..59389ce 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
@@ -4,11 +4,13 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 from frappe import _
-from frappe.utils import flt
+from frappe.utils import flt, getdate, cstr
 from frappe.model.meta import get_field_precision
 from frappe.utils.xlsxutils import handle_html
 from six import iteritems
 import json
+from erpnext.regional.india.utils import get_gst_accounts
+from erpnext.regional.report.gstr_1.gstr_1 import get_company_gstin_number
 
 def execute(filters=None):
 	return _execute(filters)
@@ -141,7 +143,7 @@
 
 	tax_details = frappe.db.sql("""
 		select
-			parent, description, item_wise_tax_detail,
+			parent, account_head, item_wise_tax_detail,
 			base_tax_amount_after_discount_amount
 		from `tab%s`
 		where
@@ -153,11 +155,11 @@
 	""" % (tax_doctype, '%s', ', '.join(['%s']*len(invoice_item_row)), conditions),
 		tuple([doctype] + list(invoice_item_row)))
 
-	for parent, description, item_wise_tax_detail, tax_amount in tax_details:
-		description = handle_html(description)
-		if description not in tax_columns and tax_amount:
+	for parent, account_head, item_wise_tax_detail, tax_amount in tax_details:
+
+		if account_head not in tax_columns and tax_amount:
 			# as description is text editor earlier and markup can break the column convention in reports
-			tax_columns.append(description)
+			tax_columns.append(account_head)
 
 		if item_wise_tax_detail:
 			try:
@@ -175,17 +177,17 @@
 					for d in item_row_map.get(parent, {}).get(item_code, []):
 						item_tax_amount = tax_amount
 						if item_tax_amount:
-							itemised_tax.setdefault((parent, item_code), {})[description] = frappe._dict({
+							itemised_tax.setdefault((parent, item_code), {})[account_head] = frappe._dict({
 								"tax_amount": flt(item_tax_amount, tax_amount_precision)
 							})
 			except ValueError:
 				continue
 
 	tax_columns.sort()
-	for desc in tax_columns:
+	for account_head in tax_columns:
 		columns.append({
-			"label": desc,
-			"fieldname": frappe.scrub(desc),
+			"label": account_head,
+			"fieldname": frappe.scrub(account_head),
 			"fieldtype": "Float",
 			"width": 110
 		})
@@ -212,3 +214,76 @@
 
 	return result
 
+@frappe.whitelist()
+def get_json(filters, report_name, data):
+	filters = json.loads(filters)
+	report_data = json.loads(data)
+	gstin = filters.get('company_gstin') or get_company_gstin_number(filters["company"])
+
+	if not filters.get('from_date') or not filters.get('to_date'):
+		frappe.throw(_("Please enter From Date and To Date to generate JSON"))
+
+	fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
+
+	gst_json = {"version": "GST2.3.4",
+		"hash": "hash", "gstin": gstin, "fp": fp}
+
+	gst_json["hsn"] = {
+		"data": get_hsn_wise_json_data(filters, report_data)
+	}
+
+	return {
+		'report_name': report_name,
+		'data': gst_json
+	}
+
+@frappe.whitelist()
+def download_json_file():
+	'''download json content in a file'''
+	data = frappe._dict(frappe.local.form_dict)
+	frappe.response['filename'] = frappe.scrub("{0}".format(data['report_name'])) + '.json'
+	frappe.response['filecontent'] = data['data']
+	frappe.response['content_type'] = 'application/json'
+	frappe.response['type'] = 'download'
+
+def get_hsn_wise_json_data(filters, report_data):
+
+	filters = frappe._dict(filters)
+	gst_accounts = get_gst_accounts(filters.company)
+	data = []
+	count = 1
+
+	for hsn in report_data:
+		row = {
+			"num": count,
+			"hsn_sc": hsn.get("gst_hsn_code"),
+			"desc": hsn.get("description"),
+			"uqc": hsn.get("stock_uom").upper(),
+			"qty": hsn.get("stock_qty"),
+			"val": flt(hsn.get("total_amount"), 2),
+			"txval": flt(hsn.get("taxable_amount", 2)),
+			"iamt": 0.0,
+			"camt": 0.0,
+			"samt": 0.0,
+			"csamt": 0.0
+
+		}
+
+		for account in gst_accounts.get('igst_account'):
+			row['iamt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+		for account in gst_accounts.get('cgst_account'):
+			row['camt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+		for account in gst_accounts.get('sgst_account'):
+			row['samt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+		for account in gst_accounts.get('cess_account'):
+			row['csamt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+		data.append(row)
+		count +=1
+
+	return data
+
+
diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py
index 67834d1..d3509e5 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.py
+++ b/erpnext/regional/report/irs_1099/irs_1099.py
@@ -16,9 +16,15 @@
 
 def execute(filters=None):
 	filters = filters if isinstance(filters, _dict) else _dict(filters)
+
 	if not filters:
 		filters.setdefault('fiscal_year', get_fiscal_year(nowdate())[0])
 		filters.setdefault('company', frappe.db.get_default("company"))
+
+	region = frappe.db.get_value("Company", fieldname = ["country"], filters = { "name": filters.company })
+	if region != 'United States':
+		return [],[]
+
 	data = []
 	columns = get_columns()
 	data = frappe.db.sql("""
diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py
index 688f145..ad95010 100644
--- a/erpnext/regional/united_states/test_united_states.py
+++ b/erpnext/regional/united_states/test_united_states.py
@@ -24,7 +24,7 @@
 
     def test_irs_1099_report(self):
         make_payment_entry_to_irs_1099_supplier()
-        filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company"})
+        filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company 1"})
         columns, data = execute_1099_report(filters)
         print(columns, data)
         expected_row = {'supplier': '_US 1099 Test Supplier',
@@ -42,10 +42,10 @@
 
     pe = frappe.new_doc("Payment Entry")
     pe.payment_type = "Pay"
-    pe.company = "_Test Company"
+    pe.company = "_Test Company 1"
     pe.posting_date = "2016-01-10"
-    pe.paid_from = "_Test Bank USD - _TC"
-    pe.paid_to = "_Test Payable USD - _TC"
+    pe.paid_from = "_Test Bank USD - _TC1"
+    pe.paid_to = "_Test Payable USD - _TC1"
     pe.paid_amount = 100
     pe.received_amount = 100
     pe.reference_no = "For IRS 1099 testing"
diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json
index 526bd9a..cdafaea 100644
--- a/erpnext/selling/desk_page/retail/retail.json
+++ b/erpnext/selling/desk_page/retail/retail.json
@@ -2,8 +2,18 @@
  "cards": [
   {
    "hidden": 0,
-   "label": "Retail Operations",
-   "links": "[\n    {\n        \"description\": \"Setup default values for POS Invoices\",\n        \"label\": \"Point of Sale Profile\",\n        \"name\": \"POS Profile\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"POS Profile\"\n        ],\n        \"description\": \"Point of Sale\",\n        \"label\": \"Point of Sale\",\n        \"name\": \"point-of-sale\",\n        \"onboard\": 1,\n        \"type\": \"page\"\n    },\n    {\n        \"description\": \"Setup mode of POS (Online / Offline)\",\n        \"label\": \"POS Settings\",\n        \"name\": \"POS Settings\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Cashier Closing\",\n        \"label\": \"Cashier Closing\",\n        \"name\": \"Cashier Closing\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"To make Customer based incentive schemes.\",\n        \"label\": \"Loyalty Program\",\n        \"name\": \"Loyalty Program\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n        \"label\": \"Loyalty Point Entry\",\n        \"name\": \"Loyalty Point Entry\",\n        \"type\": \"doctype\"\n    }\n]"
+   "label": "Settings & Configurations",
+   "links": "[\n    {\n        \"description\": \"Setup default values for POS Invoices\",\n        \"label\": \"Point-of-Sale Profile\",\n        \"name\": \"POS Profile\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"POS Settings\",\n        \"name\": \"POS Settings\",\n        \"type\": \"doctype\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Loyalty Program",
+   "links": "[\n    {\n        \"description\": \"To make Customer based incentive schemes.\",\n        \"label\": \"Loyalty Program\",\n        \"name\": \"Loyalty Program\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n        \"label\": \"Loyalty Point Entry\",\n        \"name\": \"Loyalty Point Entry\",\n        \"type\": \"doctype\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Opening & Closing",
+   "links": "[\n    {\n        \"label\": \"POS Opening Entry\",\n        \"name\": \"POS Opening Entry\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"POS Closing Entry\",\n        \"name\": \"POS Closing Entry\",\n        \"type\": \"doctype\"\n    }\n]"
   }
  ],
  "category": "Domains",
@@ -19,7 +29,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Retail",
- "modified": "2020-08-20 18:00:07.515691",
+ "modified": "2020-09-09 11:46:28.297435",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Retail",
@@ -29,25 +39,10 @@
  "restrict_to_domain": "Retail",
  "shortcuts": [
   {
-   "color": "#9deca2",
    "doc_view": "",
-   "format": "{} Active",
-   "label": "Point of Sale Profile",
-   "link_to": "POS Profile",
-   "stats_filter": "{\n    \"disabled\": 0\n}",
-   "type": "DocType"
-  },
-  {
-   "doc_view": "",
-   "label": "Point of Sale",
+   "label": "Point Of Sale",
    "link_to": "point-of-sale",
    "type": "Page"
-  },
-  {
-   "doc_view": "",
-   "label": "POS Settings",
-   "link_to": "POS Settings",
-   "type": "DocType"
   }
  ]
 }
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 93d4832..1f955fc 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -184,10 +184,10 @@
 	def validate_credit_limit_on_change(self):
 		if self.get("__islocal") or not self.credit_limits:
 			return
-		
+
 		past_credit_limits = [d.credit_limit
 			for d in frappe.db.get_all("Customer Credit Limit", filters={'parent': self.name}, fields=["credit_limit"], order_by="company")]
-		
+
 		current_credit_limits = [d.credit_limit for d in sorted(self.credit_limits, key=lambda k: k.company)]
 
 		if past_credit_limits == current_credit_limits:
@@ -396,13 +396,12 @@
 			credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager")
 
 			# form a list of emails and names to show to the user
-			credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
-
-			if not credit_controller_users:
+			credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
+			if not credit_controller_users_formatted:
 				frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
 
 			message = """Please contact any of the following users to extend the credit limits for {0}:
-				<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users))
+				<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))
 
 			# if the current user does not have permissions to override credit limit,
 			# prompt them to send out an email to the controller users
@@ -427,7 +426,7 @@
 	subject = (_("Credit limit reached for customer {0}").format(args.get('customer')))
 	message = (_("Credit limit has been crossed for customer {0} ({1}/{2})")
 			.format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit')))
-	frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message)
+	frappe.sendmail(recipients=args.get('credit_controller_users_list'), subject=subject, message=message)
 
 def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None):
 	# Outstanding based on GL Entries
diff --git a/erpnext/selling/doctype/industry_type/industry_type.json b/erpnext/selling/doctype/industry_type/industry_type.json
index f4fcae4..6c49f0f 100644
--- a/erpnext/selling/doctype/industry_type/industry_type.json
+++ b/erpnext/selling/doctype/industry_type/industry_type.json
@@ -49,11 +49,11 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2016-07-25 05:24:25.881361", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Selling", 
  "name": "Industry Type", 
- "owner": "harshada@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [
   {
    "amend": 0, 
diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.json b/erpnext/selling/doctype/product_bundle/product_bundle.json
index b63fb4b..56155fb 100644
--- a/erpnext/selling/doctype/product_bundle/product_bundle.json
+++ b/erpnext/selling/doctype/product_bundle/product_bundle.json
@@ -238,8 +238,8 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2017-10-18 14:23:06.538568", 
- "modified_by": "tundebabzy@gmail.com", 
+ "modified": "2020-09-18 17:26:09.703215", 
+ "modified_by": "Administrator", 
  "module": "Selling", 
  "name": "Product Bundle", 
  "owner": "Administrator", 
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 4fb19f8..d5428e2 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -285,9 +285,17 @@
 						return customer
 					else:
 						raise
-				except frappe.MandatoryError:
+				except frappe.MandatoryError as e:
+					mandatory_fields = e.args[0].split(':')[1].split(',')
+					mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields]
+
 					frappe.local.message_log = []
-					frappe.throw(_("Please create Customer from Lead {0}").format(lead_name))
+					lead_link = frappe.utils.get_link_to_form("Lead", lead_name)
+					message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "<br>"
+					message += "<br><ul><li>" + "</li><li>".join(mandatory_fields) + "</li></ul>"
+					message += _("Please create Customer from Lead {0}.").format(lead_link)
+
+					frappe.throw(message, title=_("Mandatory Missing"))
 			else:
 				return customer_name
 		else:
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index accaa9c..735b071 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -318,7 +318,7 @@
 		self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
 			existing_reserved_qty_item2 + 20)
 
-	def test_add_new_item_in_update_child_qty_rate(self):
+	def test_update_child_adding_new_item(self):
 		so = make_sales_order(item_code= "_Test Item", qty=4)
 		create_dn_against_so(so.name, 4)
 		make_sales_invoice(so.name)
@@ -338,7 +338,7 @@
 		self.assertEqual(so.get("items")[-1].amount, 1400)
 		self.assertEqual(so.status, 'To Deliver and Bill')
 
-	def test_remove_item_in_update_child_qty_rate(self):
+	def test_update_child_removing_item(self):
 		so = make_sales_order(**{
 			"item_list": [{
 				"item_code": '_Test Item',
@@ -381,7 +381,7 @@
 		self.assertEqual(so.status, 'To Deliver and Bill')
 
 
-	def test_update_child_qty_rate(self):
+	def test_update_child(self):
 		so = make_sales_order(item_code= "_Test Item", qty=4)
 		create_dn_against_so(so.name, 4)
 		make_sales_invoice(so.name)
@@ -402,7 +402,7 @@
 		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}])
 		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
 
-	def test_update_child_qty_rate_perm(self):
+	def test_update_child_perm(self):
 		so = make_sales_order(item_code= "_Test Item", qty=4)
 
 		user = 'test@example.com'
@@ -417,9 +417,44 @@
 		# add new item
 		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
 		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
+		test_user.remove_roles("Accounts User")
 		frappe.set_user("Administrator")
+	
+	def test_update_child_qty_rate_with_workflow(self):
+		from frappe.model.workflow import apply_workflow
 
-	def test_update_child_qty_rate_product_bundle(self):
+		workflow = make_sales_order_workflow()
+		so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
+		apply_workflow(so, 'Approve')
+
+		frappe.set_user("Administrator")
+		user = 'test@example.com'
+		test_user = frappe.get_doc('User', user)
+		test_user.add_roles("Sales User", "Test Junior Approver")
+		frappe.set_user(user)
+
+		# user shouldn't be able to edit since grand_total will become > 200 if qty is doubled
+		trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 150, 'qty' : 2, 'docname': so.items[0].name}])
+		self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Sales Order', trans_item, so.name)
+
+		frappe.set_user("Administrator")
+		user2 = 'test2@example.com'
+		test_user2 = frappe.get_doc('User', user2)
+		test_user2.add_roles("Sales User", "Test Approver")
+		frappe.set_user(user2)
+
+		# Test Approver is allowed to edit with grand_total > 200
+		update_child_qty_rate("Sales Order", trans_item, so.name)
+		so.reload()
+		self.assertEqual(so.items[0].qty, 2)
+
+		frappe.set_user("Administrator")
+		test_user.remove_roles("Sales User", "Test Junior Approver", "Test Approver")
+		test_user2.remove_roles("Sales User", "Test Junior Approver", "Test Approver")
+		workflow.is_active = 0
+		workflow.save()
+
+	def test_update_child_product_bundle(self):
 		# test Update Items with product bundle
 		if not frappe.db.exists("Item", "_Product Bundle Item"):
 			bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0})
@@ -439,6 +474,20 @@
 		so.reload()
 		self.assertEqual(so.packed_items[0].qty, 4)
 
+		# test uom and conversion factor change
+		update_uom_conv_factor = json.dumps([{
+			'item_code': so.get("items")[0].item_code,
+			'rate': so.get("items")[0].rate,
+			'qty': so.get("items")[0].qty,
+			'uom': "_Test UOM 1",
+			'conversion_factor': 2,
+			'docname': so.get("items")[0].name
+		}])
+		update_child_qty_rate('Sales Order', update_uom_conv_factor, so.name)
+
+		so.reload()
+		self.assertEqual(so.packed_items[0].qty, 8)
+
 	def test_warehouse_user(self):
 		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
 		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
@@ -973,3 +1022,37 @@
 		"reserved_qty"))
 
 test_dependencies = ["Currency Exchange"]
+
+def make_sales_order_workflow():
+	if frappe.db.exists('Workflow', 'SO Test Workflow'):
+		doc = frappe.get_doc("Workflow", "SO Test Workflow")
+		doc.set("is_active", 1)
+		doc.save()
+		return doc
+
+	frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
+	frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
+	frappe.db.commit()
+	frappe.cache().hdel('roles', frappe.session.user)
+
+	workflow = frappe.get_doc({
+		"doctype": "Workflow",
+		"workflow_name": "SO Test Workflow",
+		"document_type": "Sales Order",
+		"workflow_state_field": "workflow_state",
+		"is_active": 1,
+		"send_email_alert": 0,
+	})
+	workflow.append('states', dict( state = 'Pending', allow_edit = 'All' ))
+	workflow.append('states', dict( state = 'Approved', allow_edit = 'Test Approver', doc_status = 1 ))
+	workflow.append('transitions', dict(
+		state = 'Pending', action = 'Approve', next_state = 'Approved', allowed = 'Test Junior Approver', allow_self_approval = 1,
+		condition = 'doc.grand_total < 200'
+	))
+	workflow.append('transitions', dict(
+		state = 'Pending', action = 'Approve', next_state = 'Approved', allowed = 'Test Approver', allow_self_approval = 1,
+		 condition = 'doc.grand_total > 200'
+	))
+	workflow.insert(ignore_permissions=True)
+
+	return workflow
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 2ce0b27..8d4ac78 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -9,7 +9,7 @@
 		title: __('Point of Sale'),
 		single_column: true
 	});
-	// online
+
 	wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
 	window.cur_pos = wrapper.pos;
 };
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index ae5471b..5018254 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -8,7 +8,7 @@
 {% include "erpnext/selling/page/point_of_sale/pos_past_order_summary.js" %}
 
 erpnext.PointOfSale.Controller = class {
-    constructor(wrapper) {
+	constructor(wrapper) {
 		this.wrapper = $(wrapper).find('.layout-main-section');
 		this.page = wrapper.page;
 
@@ -36,7 +36,7 @@
 		const table_fields = [
 			{ fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, label: "Mode of Payment", options: "Mode of Payment", reqd: 1 },
 			{ fieldname: "opening_amount", fieldtype: "Currency", default: 0, in_list_view: 1, label: "Opening Amount", 
-				options: "company:company_currency", reqd: 1 }
+				options: "company:company_currency" }
 		];
 
 		const dialog = new frappe.ui.Dialog({
@@ -51,29 +51,16 @@
 					options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
 					onchange: () => {
 						const pos_profile = dialog.fields_dict.pos_profile.get_value();
-						const company = dialog.fields_dict.company.get_value();
-						const user = frappe.session.user
 
-						if (!pos_profile || !company || !user) return;
+						if (!pos_profile) return;
 
-						// auto fetch last closing entry's balance details
-						frappe.db.get_list("POS Closing Entry", {
-							filters: { company, pos_profile, user },
-							limit: 1,
-							order_by: 'period_end_date desc'
-						}).then((res) => {
-							if (!res.length) return;
-							const pos_closing_entry = res[0];
-							frappe.db.get_doc("POS Closing Entry", pos_closing_entry.name).then(({ payment_reconciliation }) => {
-								dialog.fields_dict.balance_details.df.data = [];
-								payment_reconciliation.forEach(pay => {
-									const { mode_of_payment } = pay;
-									dialog.fields_dict.balance_details.df.data.push({
-										mode_of_payment: mode_of_payment
-									});
-								});
-								dialog.fields_dict.balance_details.grid.refresh();
+						frappe.db.get_doc("POS Profile", pos_profile).then(doc => {
+							dialog.fields_dict.balance_details.df.data = [];
+							doc.payments.forEach(pay => {
+								const { mode_of_payment } = pay;
+								dialog.fields_dict.balance_details.df.data.push({ mode_of_payment });
 							});
+							dialog.fields_dict.balance_details.grid.refresh();
 						});
 					}
 				},
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 c23a6ad..724b60b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -1,36 +1,36 @@
 erpnext.PointOfSale.ItemCart = class {
-    constructor({ wrapper, events }) {
+	constructor({ wrapper, events }) {
 		this.wrapper = wrapper;
 		this.events = events;
-        this.customer_info = undefined;
-        
-        this.init_component();
-    }
-    
-    init_component() {
-        this.prepare_dom();
-        this.init_child_components();
+		this.customer_info = undefined;
+		
+		this.init_component();
+	}
+	
+	init_component() {
+		this.prepare_dom();
+		this.init_child_components();
 		this.bind_events();
 		this.attach_shortcuts();
-    }
+	}
 
-    prepare_dom() {
+	prepare_dom() {
 		this.wrapper.append(
-            `<section class="col-span-4 flex flex-col shadow rounded item-cart bg-white mx-h-70 h-100"></section>`
-        )
+			`<section class="col-span-4 flex flex-col shadow rounded item-cart bg-white mx-h-70 h-100"></section>`
+		)
 
-        this.$component = this.wrapper.find('.item-cart');
-    }
+		this.$component = this.wrapper.find('.item-cart');
+	}
 
-    init_child_components() {
-        this.init_customer_selector();
-        this.init_cart_components();
-    }
+	init_child_components() {
+		this.init_customer_selector();
+		this.init_cart_components();
+	}
 
-    init_customer_selector() {
+	init_customer_selector() {
 		this.$component.append(
-            `<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
-        )
+			`<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
+		)
 		this.$customer_section = this.$component.find('.customer-section');
 	}
 	
@@ -41,9 +41,9 @@
 		this.make_customer_selector();
 		this.customer_field.set_focus();
 	}
-    
-    init_cart_components() {
-        this.$component.append(
+	
+	init_cart_components() {
+		this.$component.append(
 			`<div class="cart-container flex flex-col items-center rounded flex-1 relative">
 				<div class="absolute flex flex-col p-8 pt-0 w-full h-full">
 					<div class="flex text-grey cart-header pt-2 pb-2 p-4 mt-2 mb-2 w-full f-shrink-0">
@@ -55,23 +55,23 @@
 					<div class="cart-totals-section flex flex-col w-full mt-4 f-shrink-0"></div>
 					<div class="numpad-section flex flex-col mt-4 d-none w-full p-8 pt-0 pb-0 f-shrink-0"></div>
 				</div>		
-            </div>`
-        );
+			</div>`
+		);
 		this.$cart_container = this.$component.find('.cart-container');
 
 		this.make_cart_totals_section();
 		this.make_cart_items_section();
-        this.make_cart_numpad();
-    }
+		this.make_cart_numpad();
+	}
 
-    make_cart_items_section() {
-        this.$cart_header = this.$component.find('.cart-header');
-        this.$cart_items_wrapper = this.$component.find('.cart-items-section');
+	make_cart_items_section() {
+		this.$cart_header = this.$component.find('.cart-header');
+		this.$cart_items_wrapper = this.$component.find('.cart-items-section');
 
 		this.make_no_items_placeholder();
-    }
-    
-    make_no_items_placeholder() {
+	}
+	
+	make_no_items_placeholder() {
 		this.$cart_header.addClass('d-none');
 		this.$cart_items_wrapper.html(
 			`<div class="no-item-wrapper flex items-center h-18">
@@ -81,8 +81,8 @@
 		this.$cart_items_wrapper.addClass('mt-4 border-grey border-dashed');
 	}
 
-    make_cart_totals_section() {
-        this.$totals_section = this.$component.find('.cart-totals-section');
+	make_cart_totals_section() {
+		this.$totals_section = this.$component.find('.cart-totals-section');
 
 		this.$totals_section.append(
 			`<div class="add-discount flex items-center pt-4 pb-4 pr-4 pl-4 text-grey pointer no-select d-none">
@@ -116,9 +116,9 @@
 		)
 
 		this.$add_discount_elem = this.$component.find(".add-discount");
-    }
-    
-    make_cart_numpad() {
+	}
+	
+	make_cart_numpad() {
 		this.$numpad_section = this.$component.find('.numpad-section');
 
 		this.number_pad = new erpnext.PointOfSale.NumberPad({
@@ -155,9 +155,9 @@
 					Checkout
 			</div>`
 		)
-    }
-    
-    bind_events() {
+	}
+	
+	bind_events() {
 		const me = this;
 		this.$customer_section.on('click', '.add-remove-customer', function (e) {
 			const customer_info_is_visible = me.$cart_container.hasClass('d-none');
@@ -356,7 +356,7 @@
 				onchange: function() {
 					if (this.value || this.value == 0) {
 						const frm = me.events.get_frm();
-						frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', this.value);
+						frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', flt(this.value));
 						me.hide_discount_control(this.value);
 					}
 				},
@@ -381,8 +381,8 @@
 			`
 		);
 	}
-    
-    update_customer_section() {
+	
+	update_customer_section() {
 		const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
 
 		if (customer) {
@@ -403,7 +403,7 @@
 				</div>`
 			);
 		} else {
-            // reset customer selector
+			// reset customer selector
 			this.reset_customer_selector();
 		}
 
@@ -430,9 +430,9 @@
 						</div>`
 			}
 		}
-    }
-    
-    update_totals_section(frm) {
+	}
+	
+	update_totals_section(frm) {
 		if (!frm) frm = this.events.get_frm();
 
 		this.render_net_total(frm.doc.base_net_total);
@@ -440,9 +440,9 @@
 
 		const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
 		this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
-    }
-    
-    render_net_total(value) {
+	}
+	
+	render_net_total(value) {
 		const currency = this.events.get_frm().doc.currency;
 		this.$totals_section.find('.net-total').html(
 			`<div class="flex flex-col">
@@ -454,9 +454,9 @@
 		)
 
 		this.$numpad_section.find('.numpad-net-total').html(`Net Total: <span class="text-bold">${format_currency(value, currency)}</span>`)
-    }
-    
-    render_grand_total(value) {
+	}
+	
+	render_grand_total(value) {
 		const currency = this.events.get_frm().doc.currency;
 		this.$totals_section.find('.grand-total').html(
 			`<div class="flex flex-col">
@@ -495,20 +495,20 @@
 		} else {
 			this.$totals_section.find('.taxes').html('')
 		}
-    }
+	}
 
-    get_cart_item({ item_code, batch_no, uom }) {
+	get_cart_item({ item_code, batch_no, uom }) {
 		const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
 		const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
 		const uom_attr = `[data-uom=${escape(uom)}]`;
 
-        const item_selector = batch_no ? 
-            `.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
-            
-        return this.$cart_items_wrapper.find(item_selector);
-    }
-    
-    update_item_html(item, remove_item) {
+		const item_selector = batch_no ? 
+			`.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
+			
+		return this.$cart_items_wrapper.find(item_selector);
+	}
+	
+	update_item_html(item, remove_item) {
 		const $item = this.get_cart_item(item);
 
 		if (remove_item) {
@@ -524,33 +524,33 @@
 
 		const no_of_cart_items = this.$cart_items_wrapper.children().length;
 		no_of_cart_items > 0 && this.highlight_checkout_btn(no_of_cart_items > 0);
-        
+		
 		this.update_empty_cart_section(no_of_cart_items);
 	}
-    
-    render_cart_item(item_data, $item_to_update) {
+	
+	render_cart_item(item_data, $item_to_update) {
 		const currency = this.events.get_frm().doc.currency;
 		const me = this;
 		
-        if (!$item_to_update.length) {
-            this.$cart_items_wrapper.append(
-                `<div class="cart-item-wrapper flex items-center h-18 pr-4 pl-4 rounded border-grey pointer no-select" 
+		if (!$item_to_update.length) {
+			this.$cart_items_wrapper.append(
+				`<div class="cart-item-wrapper flex items-center h-18 pr-4 pl-4 rounded border-grey pointer no-select" 
 						data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
 						data-batch-no="${escape(item_data.batch_no || '')}">
-                </div>`
-            )
-            $item_to_update = this.get_cart_item(item_data);
-        }
+				</div>`
+			)
+			$item_to_update = this.get_cart_item(item_data);
+		}
 
 		$item_to_update.html(
 			`<div class="flex flex-col flex-1 f-shrink-1 overflow-hidden whitespace-nowrap">
-                <div class="text-md text-dark-grey text-bold">
-                    ${item_data.item_name}
-                </div>
-                ${get_description_html()}
-            </div>
-                ${get_rate_discount_html()}
-            </div>`
+				<div class="text-md text-dark-grey text-bold">
+					${item_data.item_name}
+				</div>
+				${get_description_html()}
+			</div>
+				${get_rate_discount_html()}
+			</div>`
 		)
 
 		set_dynamic_rate_header_width();
@@ -572,7 +572,7 @@
 			me.$cart_header.find(".rate-list-header").css("width", max_width);
 			me.$cart_items_wrapper.find(".rate-col").css("width", max_width);
 		}
-        
+		
 		function get_rate_discount_html() {
 			if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) {
 				return `
@@ -625,7 +625,7 @@
 		$item_to_update.attr(`data-${selector}`, value);
 	}
 
-    toggle_checkout_btn(show_checkout) {
+	toggle_checkout_btn(show_checkout) {
 		if (show_checkout) {
 			this.$totals_section.find('.checkout-btn').removeClass('d-none');
 			this.$totals_section.find('.edit-cart-btn').addClass('d-none');
@@ -635,7 +635,7 @@
 		}
 	}
 
-    highlight_checkout_btn(toggle) {
+	highlight_checkout_btn(toggle) {
 		const has_primary_class = this.$totals_section.find('.checkout-btn').hasClass('bg-primary');
 		if (toggle && !has_primary_class) {
 			this.$totals_section.find('.checkout-btn').addClass('bg-primary text-white text-lg');
@@ -643,8 +643,8 @@
 			this.$totals_section.find('.checkout-btn').removeClass('bg-primary text-white text-lg');
 		}
 	}
-    
-    update_empty_cart_section(no_of_cart_items) {
+	
+	update_empty_cart_section(no_of_cart_items) {
 		const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
 
 		// if cart has items and no item is present
@@ -652,27 +652,27 @@
 			&& this.$cart_items_wrapper.removeClass('mt-4 border-grey border-dashed') && this.$cart_header.removeClass('d-none');
 
 		no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
-    }
-    
-    on_numpad_event($btn) {
+	}
+	
+	on_numpad_event($btn) {
 		const current_action = $btn.attr('data-button-value');
 		const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
 
 		this.highlight_numpad_btn($btn, current_action);
 
-        const action_is_pressed_twice = this.prev_action === current_action;
-        const first_click_event = !this.prev_action;
-        const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
+		const action_is_pressed_twice = this.prev_action === current_action;
+		const first_click_event = !this.prev_action;
+		const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
 
 		if (action_is_field_edit) {
 
 			if (first_click_event || field_to_edit_changed) {
-                this.prev_action = current_action;
+				this.prev_action = current_action;
 			} else if (action_is_pressed_twice) {
 				this.prev_action = undefined;
 			}
-            this.numpad_value = '';
-            
+			this.numpad_value = '';
+			
 		} else if (current_action === 'checkout') {
 			this.prev_action = undefined;
 			this.toggle_item_highlight();
@@ -688,7 +688,7 @@
 			this.numpad_value = this.numpad_value || 0;
 		}
 
-        const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
+		const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
 
 		if (first_click_event_is_not_field_edit) {
 			frappe.show_alert({
@@ -708,34 +708,34 @@
 			this.numpad_value = current_action;
 		}
 
-        this.events.numpad_event(this.numpad_value, this.prev_action);
-    }
-    
-    highlight_numpad_btn($btn, curr_action) {
-        const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
-        const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
+		this.events.numpad_event(this.numpad_value, this.prev_action);
+	}
+	
+	highlight_numpad_btn($btn, curr_action) {
+		const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
+		const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
 
-        if (!curr_action_is_highlighted) {
-            $btn.addClass('shadow-inner bg-selected');
-        }
-        if (this.prev_action === curr_action && curr_action_is_highlighted) {
-            // if Qty is pressed twice
-            $btn.removeClass('shadow-inner bg-selected');
-        }
-        if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
-            // Order: Qty -> Rate then remove Qty highlight
-            const prev_btn = $(`[data-button-value='${this.prev_action}']`);
-            prev_btn.removeClass('shadow-inner bg-selected');
-        }
-        if (!curr_action_is_action || curr_action === 'done') {
-            // if numbers are clicked
-            setTimeout(() => {
-                $btn.removeClass('shadow-inner bg-selected');
-            }, 100);
-        }
-    }
+		if (!curr_action_is_highlighted) {
+			$btn.addClass('shadow-inner bg-selected');
+		}
+		if (this.prev_action === curr_action && curr_action_is_highlighted) {
+			// if Qty is pressed twice
+			$btn.removeClass('shadow-inner bg-selected');
+		}
+		if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
+			// Order: Qty -> Rate then remove Qty highlight
+			const prev_btn = $(`[data-button-value='${this.prev_action}']`);
+			prev_btn.removeClass('shadow-inner bg-selected');
+		}
+		if (!curr_action_is_action || curr_action === 'done') {
+			// if numbers are clicked
+			setTimeout(() => {
+				$btn.removeClass('shadow-inner bg-selected');
+			}, 100);
+		}
+	}
 
-    toggle_numpad(show) {
+	toggle_numpad(show) {
 		if (show) {
 			this.$totals_section.addClass('d-none');
 			this.$numpad_section.removeClass('d-none');
@@ -946,6 +946,6 @@
 
 	toggle_component(show) {
 		show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
-    }
-    
-}
\ No newline at end of file
+	}
+	
+}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index 86a1be9..3a5f89b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -1,28 +1,28 @@
 erpnext.PointOfSale.ItemDetails = class {
-    constructor({ wrapper, events }) {
+	constructor({ wrapper, events }) {
 		this.wrapper = wrapper;
-        this.events = events;
-        this.current_item = {};
+		this.events = events;
+		this.current_item = {};
 
-        this.init_component();
-    }
+		this.init_component();
+	}
 
-    init_component() {
-        this.prepare_dom();
-        this.init_child_components();
+	init_component() {
+		this.prepare_dom();
+		this.init_child_components();
 		this.bind_events();
 		this.attach_shortcuts();
-    }
+	}
 
-    prepare_dom() {
-        this.wrapper.append(
-            `<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>`
-        )
+	prepare_dom() {
+		this.wrapper.append(
+			`<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>`
+		)
 
-        this.$component = this.wrapper.find('.item-details');
-    }
+		this.$component = this.wrapper.find('.item-details');
+	}
 
-    init_child_components() {
+	init_child_components() {
 		this.$component.html(
 			`<div class="details-container flex flex-col p-8 rounded w-full">
 				<div class="flex justify-between mb-2">
@@ -49,28 +49,28 @@
 		this.$item_image = this.$component.find('.item-image');
 		this.$form_container = this.$component.find('.form-container');
 		this.$dicount_section = this.$component.find('.discount-section');
-    }
+	}
 
-    toggle_item_details_section(item) {
+	toggle_item_details_section(item) {
 		const { item_code, batch_no, uom } = this.current_item; 
 		const item_code_is_same = item && item_code === item.item_code;
 		const batch_is_same = item && batch_no == item.batch_no;
 		const uom_is_same = item && uom === item.uom;
 
-        this.item_has_changed = !item ? false : item_code_is_same && batch_is_same && uom_is_same ? false : true;
+		this.item_has_changed = !item ? false : item_code_is_same && batch_is_same && uom_is_same ? false : true;
 
-        this.events.toggle_item_selector(this.item_has_changed);
+		this.events.toggle_item_selector(this.item_has_changed);
 		this.toggle_component(this.item_has_changed);
-        
+		
 		if (this.item_has_changed) {
-            this.doctype = item.doctype;
+			this.doctype = item.doctype;
 			this.item_meta = frappe.get_meta(this.doctype);
 			this.name = item.name;
 			this.item_row = item;
-            this.currency = this.events.get_frm().doc.currency;
-            
-            this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom };
-            
+			this.currency = this.events.get_frm().doc.currency;
+			
+			this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom };
+			
 			this.render_dom(item);
 			this.render_discount_dom(item);
 			this.render_form(item);
@@ -102,9 +102,9 @@
 			this.events.remove_item_from_cart();
 		}
 	}
-    
-    render_dom(item) {
-        let { item_code ,item_name, description, image, price_list_rate } = item;
+	
+	render_dom(item) {
+		let { item_code ,item_name, description, image, price_list_rate } = item;
 
 		function get_description_html() {
 			if (description) {
@@ -112,8 +112,8 @@
 				return description;
 			}
 			return ``;
-        }
-        
+		}
+		
 		this.$item_name.html(item_name);
 		this.$item_description.html(get_description_html());
 		this.$item_price.html(format_currency(price_list_rate, this.currency));
@@ -125,9 +125,9 @@
 			this.$item_image.html(frappe.get_abbr(item_code));
 		}
 
-    }
-    
-    render_discount_dom(item) {
+	}
+	
+	render_discount_dom(item) {
 		if (item.discount_percentage) {
 			this.$dicount_section.html(
 				`<div class="text-grey line-through mr-4 text-md mb-2">
@@ -141,9 +141,9 @@
 		} else {
 			this.$dicount_section.html(``)
 		}
-    }
+	}
 
-    render_form(item) {
+	render_form(item) {
 		const fields_to_display = this.get_form_fields(item);
 		this.$form_container.html('');
 
@@ -157,7 +157,7 @@
 			const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname);
 			fieldname === 'discount_percentage' ? (field_meta.label = __('Discount (%)')) : '';
 			const me = this;
-            
+			
 			this[`${fieldname}_control`] = frappe.ui.form.make_control({
 				df: { 
 					...field_meta, 
@@ -174,16 +174,16 @@
 		this.make_auto_serial_selection_btn(item);
 
 		this.bind_custom_control_change_event();
-    }
+	}
 
-    get_form_fields(item) {
+	get_form_fields(item) {
 		const fields = ['qty', 'uom', 'rate', 'price_list_rate', 'discount_percentage', 'warehouse', 'actual_qty'];
 		if (item.has_serial_no) fields.push('serial_no');
 		if (item.has_batch_no) fields.push('batch_no');
 		return fields;
 	}
 
-    make_auto_serial_selection_btn(item) {
+	make_auto_serial_selection_btn(item) {
 		if (item.has_serial_no) {
 			this.$form_container.append(
 				`<div class="grid-filler no-select"></div>`
@@ -203,8 +203,8 @@
 			this.$form_container.find('.serial_no-control').parent().addClass('row-span-2');
 		}
 	}
-    
-    bind_custom_control_change_event() {
+	
+	bind_custom_control_change_event() {
 		const me = this;
 		if (this.rate_control) {
 			this.rate_control.df.onchange = function() {
@@ -276,8 +276,8 @@
 			};
 			this.batch_no_control.df.onchange = function() {
 				me.events.set_value_in_current_cart_item('batch-no', this.value);
-                me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
-                me.current_item.batch_no = this.value;
+				me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
+				me.current_item.batch_no = this.value;
 			}
 			this.batch_no_control.refresh();
 		}
@@ -289,9 +289,9 @@
 				me.current_item.uom = this.value;
 			}
 		}
-    }
-    
-    async auto_update_batch_no() {
+	}
+	
+	async auto_update_batch_no() {
 		if (this.serial_no_control && this.batch_no_control) {
 			const selected_serial_nos = this.serial_no_control.get_value().split(`\n`).filter(s => s);
 			if (!selected_serial_nos.length) return;
@@ -310,9 +310,9 @@
 			const batch_no = Object.keys(batch_serial_map)[0];
 			const batch_serial_nos = batch_serial_map[batch_no].join(`\n`);
 			// eg. 10 selected serial no. -> 5 belongs to first batch other 5 belongs to second batch
-            const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length;
-            
-            const current_batch_no = this.batch_no_control.get_value();
+			const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length;
+			
+			const current_batch_no = this.batch_no_control.get_value();
 			current_batch_no != batch_no && await this.batch_no_control.set_value(batch_no);
 
 			if (serial_nos_belongs_to_other_batch) {
@@ -326,8 +326,8 @@
 				this.events.clone_new_batch_item_in_frm(batch_serial_map, this.current_item);
 		}
 	}
-    
-    bind_events() {
+	
+	bind_events() {
 		this.bind_auto_serial_fetch_event();
 		this.bind_fields_to_numpad_fields();
 
@@ -345,7 +345,7 @@
 		});
 	}
 
-    bind_fields_to_numpad_fields() {
+	bind_fields_to_numpad_fields() {
 		const me = this;
 		this.$form_container.on('click', '.input-with-feedback', function() {
 			const fieldname = $(this).attr('data-fieldname');
@@ -355,8 +355,8 @@
 			}
 		});
 	}
-    
-    bind_auto_serial_fetch_event() {
+	
+	bind_auto_serial_fetch_event() {
 		this.$form_container.on('click', '.auto-fetch-btn', () => {
 			this.batch_no_control.set_value('');
 			let qty = this.qty_control.get_value();
@@ -382,7 +382,7 @@
 					frappe.msgprint(`Fetched only ${records_length} available serial numbers.`);
 					this.qty_control.set_value(records_length);
 				}
-                numbers = auto_fetched_serial_numbers.join(`\n`);
+				numbers = auto_fetched_serial_numbers.join(`\n`);
 				this.serial_no_control.set_value(numbers);
 			});
 		})
@@ -390,5 +390,5 @@
 
 	toggle_component(show) {
 		show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
-    }
+	}
 }
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index ee0c06d..c87b845 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -1,115 +1,115 @@
 erpnext.PointOfSale.ItemSelector = class {
-    constructor({ frm, wrapper, events, pos_profile }) {
+	constructor({ frm, wrapper, events, pos_profile }) {
 		this.wrapper = wrapper;
 		this.events = events;
-        this.pos_profile = pos_profile;
-        
-        this.inti_component();
-    }
-    
-    inti_component() {
-        this.prepare_dom();
-        this.make_search_bar();
-        this.load_items_data();
-        this.bind_events();
-        this.attach_shortcuts();
-    }
+		this.pos_profile = pos_profile;
+		
+		this.inti_component();
+	}
+	
+	inti_component() {
+		this.prepare_dom();
+		this.make_search_bar();
+		this.load_items_data();
+		this.bind_events();
+		this.attach_shortcuts();
+	}
 
-    prepare_dom() {
+	prepare_dom() {
 		this.wrapper.append(
-            `<section class="col-span-6 flex shadow rounded items-selector bg-white mx-h-70 h-100">
-                <div class="flex flex-col rounded w-full scroll-y">
-                    <div class="filter-section flex p-8 pb-2 bg-white sticky z-100">
-                        <div class="search-field flex f-grow-3 mr-8 items-center text-grey"></div>
-                        <div class="item-group-field flex f-grow-1 items-center text-grey text-bold"></div>
-                    </div>
-                    <div class="flex flex-1 flex-col p-8 pt-2">
-                        <div class="text-grey mb-6">ALL ITEMS</div>
-                        <div class="items-container grid grid-cols-4 gap-8">
-                        </div>					
-                    </div>
-                </div>
-            </section>`
-        );
-        
-        this.$component = this.wrapper.find('.items-selector');
-    }
+			`<section class="col-span-6 flex shadow rounded items-selector bg-white mx-h-70 h-100">
+				<div class="flex flex-col rounded w-full scroll-y">
+					<div class="filter-section flex p-8 pb-2 bg-white sticky z-100">
+						<div class="search-field flex f-grow-3 mr-8 items-center text-grey"></div>
+						<div class="item-group-field flex f-grow-1 items-center text-grey text-bold"></div>
+					</div>
+					<div class="flex flex-1 flex-col p-8 pt-2">
+						<div class="text-grey mb-6">ALL ITEMS</div>
+						<div class="items-container grid grid-cols-4 gap-8">
+						</div>					
+					</div>
+				</div>
+			</section>`
+		);
+		
+		this.$component = this.wrapper.find('.items-selector');
+	}
 
-    async load_items_data() {
-        if (!this.item_group) {
-            const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name");
-            this.parent_item_group = res.message.name;
-        };
-        if (!this.price_list) {
-            const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
-            this.price_list = res.message.selling_price_list;
-        }
+	async load_items_data() {
+		if (!this.item_group) {
+			const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name");
+			this.parent_item_group = res.message.name;
+		};
+		if (!this.price_list) {
+			const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
+			this.price_list = res.message.selling_price_list;
+		}
 
-        this.get_items({}).then(({message}) => {
-            this.render_item_list(message.items);
-        });
-    }
+		this.get_items({}).then(({message}) => {
+			this.render_item_list(message.items);
+		});
+	}
 
-    get_items({start = 0, page_length = 40, search_value=''}) {
-        const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
-        let { item_group, pos_profile } = this;
+	get_items({start = 0, page_length = 40, search_value=''}) {
+		const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
+		let { item_group, pos_profile } = this;
 
-        !item_group && (item_group = this.parent_item_group);
-        
+		!item_group && (item_group = this.parent_item_group);
+		
 		return frappe.call({
 			method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
 			freeze: true,
-            args: { start, page_length, price_list, item_group, search_value, pos_profile },
-        });
+			args: { start, page_length, price_list, item_group, search_value, pos_profile },
+		});
 	}
 
 
 	render_item_list(items) {
-        this.$items_container = this.$component.find('.items-container');
-        this.$items_container.html('');
+		this.$items_container = this.$component.find('.items-container');
+		this.$items_container.html('');
 
-        items.forEach(item => {
-            const item_html = this.get_item_html(item);
-            this.$items_container.append(item_html);
-        })
-    }
+		items.forEach(item => {
+			const item_html = this.get_item_html(item);
+			this.$items_container.append(item_html);
+		})
+	}
 
-    get_item_html(item) {
-        const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
-        const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
+	get_item_html(item) {
+		const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
+		const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
 
-        function get_item_image_html() {
-            if (item_image) {
-                return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
-                            <img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;">
-                        </div>`
-            } else {
-                return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
-                            ${frappe.get_abbr(item.item_name)}
-                        </div>`
-            }
-        }
+		function get_item_image_html() {
+			if (item_image) {
+				return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
+							<img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;">
+						</div>`
+			} else {
+				return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
+							${frappe.get_abbr(item.item_name)}
+						</div>`
+			}
+		}
 
 		return (
-            `<div class="item-wrapper rounded shadow pointer no-select" data-item-code="${escape(item.item_code)}"
-                data-serial-no="${escape(serial_no)}" data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
-                title="Avaiable Qty: ${actual_qty}">
-                ${get_item_image_html()}
-                <div class="flex items-center pr-4 pl-4 h-10 justify-between">
-                    <div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
-                        <span class="indicator ${indicator_color}"></span>
-                        ${frappe.ellipsis(item.item_name, 18)}
-                    </div>
-                    <div class="f-shrink-0 text-dark-grey text-bold ml-4">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
-                </div>
-            </div>`
-        )
-    }
+			`<div class="item-wrapper rounded shadow pointer no-select" data-item-code="${escape(item.item_code)}"
+				data-serial-no="${escape(serial_no)}" data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
+				title="Avaiable Qty: ${actual_qty}">
+				${get_item_image_html()}
+				<div class="flex items-center pr-4 pl-4 h-10 justify-between">
+					<div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
+						<span class="indicator ${indicator_color}"></span>
+						${frappe.ellipsis(item.item_name, 18)}
+					</div>
+					<div class="f-shrink-0 text-dark-grey text-bold ml-4">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
+				</div>
+			</div>`
+		)
+	}
 
-    make_search_bar() {
-        const me = this;
-        this.$component.find('.search-field').html('');
-        this.$component.find('.item-group-field').html('');
+	make_search_bar() {
+		const me = this;
+		this.$component.find('.search-field').html('');
+		this.$component.find('.item-group-field').html('');
 
 		this.search_field = frappe.ui.form.make_control({
 			df: {
@@ -119,104 +119,104 @@
 			},
 			parent: this.$component.find('.search-field'),
 			render_input: true,
-        });
+		});
 		this.item_group_field = frappe.ui.form.make_control({
 			df: {
 				label: __('Item Group'),
 				fieldtype: 'Link',
 				options: 'Item Group',
-                placeholder: __('Select item group'),
-                onchange: function() {
-                    me.item_group = this.value;
-                    !me.item_group && (me.item_group = me.parent_item_group);
-                    me.filter_items();
-                },
-                get_query: function () {
-                    return {
-                        query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
-                        filters: {
-                            pos_profile: me.events.get_frm().doc?.pos_profile
-                        }
-                    }
-                },
+				placeholder: __('Select item group'),
+				onchange: function() {
+					me.item_group = this.value;
+					!me.item_group && (me.item_group = me.parent_item_group);
+					me.filter_items();
+				},
+				get_query: function () {
+					return {
+						query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
+						filters: {
+							pos_profile: me.events.get_frm().doc?.pos_profile
+						}
+					}
+				},
 			},
-            parent: this.$component.find('.item-group-field'),
+			parent: this.$component.find('.item-group-field'),
 			render_input: true,
-        });
-        this.search_field.toggle_label(false);
+		});
+		this.search_field.toggle_label(false);
 		this.item_group_field.toggle_label(false);
 	}
 
-    bind_events() {
-        const me = this;
-        onScan.attachTo(document, {
-            onScan: (sScancode) => {
-                if (this.search_field && this.$component.is(':visible')) {
-                    this.search_field.set_focus();
-                    $(this.search_field.$input[0]).val(sScancode).trigger("input");
-                    this.barcode_scanned = true;
-                }
-            }
-        });
+	bind_events() {
+		const me = this;
+		onScan.attachTo(document, {
+			onScan: (sScancode) => {
+				if (this.search_field && this.$component.is(':visible')) {
+					this.search_field.set_focus();
+					$(this.search_field.$input[0]).val(sScancode).trigger("input");
+					this.barcode_scanned = true;
+				}
+			}
+		});
 
 		this.$component.on('click', '.item-wrapper', function() {
 			const $item = $(this);
 			const item_code = unescape($item.attr('data-item-code'));
-            let batch_no = unescape($item.attr('data-batch-no'));
-            let serial_no = unescape($item.attr('data-serial-no'));
-            let uom = unescape($item.attr('data-uom'));
-            
-            // escape(undefined) returns "undefined" then unescape returns "undefined"
-            batch_no = batch_no === "undefined" ? undefined : batch_no;
-            serial_no = serial_no === "undefined" ? undefined : serial_no;
-            uom = uom === "undefined" ? undefined : uom;
+			let batch_no = unescape($item.attr('data-batch-no'));
+			let serial_no = unescape($item.attr('data-serial-no'));
+			let uom = unescape($item.attr('data-uom'));
+			
+			// escape(undefined) returns "undefined" then unescape returns "undefined"
+			batch_no = batch_no === "undefined" ? undefined : batch_no;
+			serial_no = serial_no === "undefined" ? undefined : serial_no;
+			uom = uom === "undefined" ? undefined : uom;
 
-            me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
-        })
+			me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
+		})
 
-        this.search_field.$input.on('input', (e) => {
+		this.search_field.$input.on('input', (e) => {
 			clearTimeout(this.last_search);
 			this.last_search = setTimeout(() => {
 				const search_term = e.target.value;
 				this.filter_items({ search_term });
 			}, 300);
-        });
-    }
+		});
+	}
 
-    attach_shortcuts() {
-        frappe.ui.keys.on("ctrl+i", () => {
-            const selector_is_visible = this.$component.is(':visible');
-            if (!selector_is_visible) return;
-            this.search_field.set_focus();
-        });
-        frappe.ui.keys.on("ctrl+g", () => {
-            const selector_is_visible = this.$component.is(':visible');
-            if (!selector_is_visible) return;
-            this.item_group_field.set_focus();
-        });
-        // for selecting the last filtered item on search
-        frappe.ui.keys.on("enter", () => {
-            const selector_is_visible = this.$component.is(':visible');
-            if (!selector_is_visible || this.search_field.get_value() === "") return;
+	attach_shortcuts() {
+		frappe.ui.keys.on("ctrl+i", () => {
+			const selector_is_visible = this.$component.is(':visible');
+			if (!selector_is_visible) return;
+			this.search_field.set_focus();
+		});
+		frappe.ui.keys.on("ctrl+g", () => {
+			const selector_is_visible = this.$component.is(':visible');
+			if (!selector_is_visible) return;
+			this.item_group_field.set_focus();
+		});
+		// for selecting the last filtered item on search
+		frappe.ui.keys.on("enter", () => {
+			const selector_is_visible = this.$component.is(':visible');
+			if (!selector_is_visible || this.search_field.get_value() === "") return;
 
-            if (this.items.length == 1) {
-                this.$items_container.find(".item-wrapper").click();
-                frappe.utils.play_sound("submit");
-                $(this.search_field.$input[0]).val("").trigger("input");
-            } else if (this.items.length == 0 && this.barcode_scanned) {
-                // only show alert of barcode is scanned and enter is pressed
-                frappe.show_alert({
-                    message: __("No items found. Scan barcode again."),
-                    indicator: 'orange'
-                });
-                frappe.utils.play_sound("error");
-                this.barcode_scanned = false;
-                $(this.search_field.$input[0]).val("").trigger("input");
-            }
-        });
-    }
-    
-    filter_items({ search_term='' }={}) {
+			if (this.items.length == 1) {
+				this.$items_container.find(".item-wrapper").click();
+				frappe.utils.play_sound("submit");
+				$(this.search_field.$input[0]).val("").trigger("input");
+			} else if (this.items.length == 0 && this.barcode_scanned) {
+				// only show alert of barcode is scanned and enter is pressed
+				frappe.show_alert({
+					message: __("No items found. Scan barcode again."),
+					indicator: 'orange'
+				});
+				frappe.utils.play_sound("error");
+				this.barcode_scanned = false;
+				$(this.search_field.$input[0]).val("").trigger("input");
+			}
+		});
+	}
+	
+	filter_items({ search_term='' }={}) {
 		if (search_term) {
 			search_term = search_term.toLowerCase();
 
@@ -227,39 +227,39 @@
 				this.items = items;
 				this.render_item_list(items);
 				return;
-            }
+			}
 		}
 
 		this.get_items({ search_value: search_term })
-            .then(({ message }) => {
-                const { items, serial_no, batch_no, barcode } = message;
+			.then(({ message }) => {
+				const { items, serial_no, batch_no, barcode } = message;
 				if (search_term && !barcode) {
 					this.search_index[search_term] = items;
 				}
 				this.items = items;
-                this.render_item_list(items);
-            });
+				this.render_item_list(items);
+			});
 	}
-    
-    resize_selector(minimize) {
-        minimize ? 
-        this.$component.find('.search-field').removeClass('mr-8') : 
-        this.$component.find('.search-field').addClass('mr-8');
+	
+	resize_selector(minimize) {
+		minimize ? 
+		this.$component.find('.search-field').removeClass('mr-8') : 
+		this.$component.find('.search-field').addClass('mr-8');
 
-        minimize ? 
-        this.$component.find('.filter-section').addClass('flex-col') : 
-        this.$component.find('.filter-section').removeClass('flex-col');
+		minimize ? 
+		this.$component.find('.filter-section').addClass('flex-col') : 
+		this.$component.find('.filter-section').removeClass('flex-col');
 
-        minimize ?
-        this.$component.removeClass('col-span-6').addClass('col-span-2') :
-        this.$component.removeClass('col-span-2').addClass('col-span-6')
+		minimize ?
+		this.$component.removeClass('col-span-6').addClass('col-span-2') :
+		this.$component.removeClass('col-span-2').addClass('col-span-6')
 
-        minimize ?
-        this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') :
-        this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4')
-    }
+		minimize ?
+		this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') :
+		this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4')
+	}
 
-    toggle_component(show) {
+	toggle_component(show) {
 		show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
-    }
+	}
 }
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js
index 2ffc2c0..4b8e841 100644
--- a/erpnext/selling/page/point_of_sale/pos_number_pad.js
+++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js
@@ -1,49 +1,48 @@
 erpnext.PointOfSale.NumberPad = class {
-    constructor({ wrapper, events, cols, keys, css_classes, fieldnames_map }) {
-        this.wrapper = wrapper;
-        this.events = events;
-        this.cols = cols;
-        this.keys = keys;
-        this.css_classes = css_classes || [];
-        this.fieldnames = fieldnames_map || {};
+	constructor({ wrapper, events, cols, keys, css_classes, fieldnames_map }) {
+		this.wrapper = wrapper;
+		this.events = events;
+		this.cols = cols;
+		this.keys = keys;
+		this.css_classes = css_classes || [];
+		this.fieldnames = fieldnames_map || {};
 
-        this.init_component();
-    }
+		this.init_component();
+	}
 
-    init_component() {
-        this.prepare_dom();
-        this.bind_events();
-    }
+	init_component() {
+		this.prepare_dom();
+		this.bind_events();
+	}
 
-    prepare_dom() {
-        const { cols, keys, css_classes, fieldnames } = this;
+	prepare_dom() {
+		const { cols, keys, css_classes, fieldnames } = this;
 
-        function get_keys() {
-           return keys.reduce((a, row, i) => {
-               return a + row.reduce((a2, number, j) => {
-                   const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : '';
-                   const fieldname = fieldnames && fieldnames[number] ? 
-                       fieldnames[number] : 
-                       typeof number === 'string' ? frappe.scrub(number) : number;
-                   
-                   return a2 + `<div class="numpad-btn pointer no-select rounded ${class_to_append}
-                                       flex items-center justify-center h-16 text-md border-grey border" data-button-value="${fieldname}">${number}</div>`
-               }, '')
-           }, '');
-       }
+		function get_keys() {
+			return keys.reduce((a, row, i) => {
+				return a + row.reduce((a2, number, j) => {
+					const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : '';
+					const fieldname = fieldnames && fieldnames[number] ? 
+						fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number;
 
-        this.wrapper.html(
-            `<div class="grid grid-cols-${cols} gap-4">
-                ${get_keys()}
-           </div>`
-        )
-    }
+						return a2 + `<div class="numpad-btn pointer no-select rounded ${class_to_append}
+							flex items-center justify-center h-16 text-md border-grey border" data-button-value="${fieldname}">${number}</div>`
+				}, '')
+			}, '');
+		}
 
-    bind_events() {
-       const me = this;
-       this.wrapper.on('click', '.numpad-btn', function() {
-           const $btn = $(this);
-           me.events.numpad_event($btn);
-       })
-    }
+		this.wrapper.html(
+			`<div class="grid grid-cols-${cols} gap-4">
+				${get_keys()}
+			</div>`
+		)
+	}
+
+	bind_events() {
+		const me = this;
+		this.wrapper.on('click', '.numpad-btn', function() {
+			const $btn = $(this);
+			me.events.numpad_event($btn);
+		});
+	}
 }
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index e1c54f6..7f0cabe 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -70,13 +70,23 @@
 				this.$invoice_fields.append(
 					`<div class="invoice_detail_field ${df.fieldname}-field" data-fieldname="${df.fieldname}"></div>`
 				);
+				let df_events = {
+					onchange: function() { frm.set_value(this.df.fieldname, this.value); }
+				}
+				if (df.fieldtype == "Button") {
+					df_events = {
+						click: function() {
+							if (frm.script_manager.has_handlers(df.fieldname, frm.doc.doctype)) {
+								frm.script_manager.trigger(df.fieldname, frm.doc.doctype, frm.doc.docname);
+							}
+						}
+					}
+				}
 
 				this[`${df.fieldname}_field`] = frappe.ui.form.make_control({
 					df: { 
 						...df,
-						onchange: function() {
-							frm.set_value(this.df.fieldname, this.value);
-						}
+						...df_events
 					},
 					parent: this.$invoice_fields.find(`.${df.fieldname}-field`),
 					render_input: true,
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py
index dba24ef..b613718 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.py
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.py
@@ -20,29 +20,28 @@
 	validate_filters(from_date, to_date, company)
 
 	active_leads = frappe.db.sql("""select count(*) from `tabLead`
-		where (date(`modified`) between %s and %s)
-		and status != "Do Not Contact" and company=%s""", (from_date, to_date, company))[0][0]
-
-	active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact
-		left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer'
-		and (date(contact.modified) between %s and %s) and status != "Passive" """, (from_date, to_date))[0][0]
+		where (date(`creation`) between %s and %s)
+		and company=%s""", (from_date, to_date, company))[0][0]
 
 	opportunities = frappe.db.sql("""select count(*) from `tabOpportunity`
 		where (date(`creation`) between %s and %s)
-		and status != "Lost" and company=%s""", (from_date, to_date, company))[0][0]
+		and opportunity_from='Lead' and company=%s""", (from_date, to_date, company))[0][0]
 
 	quotations = frappe.db.sql("""select count(*) from `tabQuotation`
 		where docstatus = 1 and (date(`creation`) between %s and %s)
-		and status != "Lost" and company=%s""", (from_date, to_date, company))[0][0]
+		and (opportunity!="" or quotation_to="Lead") and company=%s""", (from_date, to_date, company))[0][0]
 
-	sales_orders = frappe.db.sql("""select count(*) from `tabSales Order`
-		where docstatus = 1 and (date(`creation`) between %s and %s) and company=%s""", (from_date, to_date, company))[0][0]
+	converted = frappe.db.sql("""select count(*) from `tabCustomer`
+		JOIN `tabLead` ON `tabLead`.name = `tabCustomer`.lead_name 
+		WHERE (date(`tabCustomer`.creation) between %s and %s)
+		and `tabLead`.company=%s""", (from_date, to_date, company))[0][0]
+
 
 	return [
-		{ "title": _("Active Leads / Customers"), "value": active_leads, "color": "#B03B46" },
+		{ "title": _("Active Leads"), "value": active_leads, "color": "#B03B46" },
 		{ "title": _("Opportunities"), "value": opportunities, "color": "#F09C00" },
 		{ "title": _("Quotations"), "value": quotations, "color": "#006685" },
-		{ "title": _("Sales Orders"), "value": sales_orders, "color": "#00AD65" }
+		{ "title": _("Converted"), "value": converted, "color": "#00AD65" }
 	]
 
 @frappe.whitelist()
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
index 0a70b97..c716aa9 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
@@ -188,7 +188,7 @@
 		conditions += "AND so.transaction_date <= '%s'" %filters.to_date
 
 	if filters.get("item_code"):
-		conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code)
+		conditions += "AND so_item.item_code = %s" %frappe.db.escape(filters.item_code)
 
 	if filters.get("customer"):
 		conditions += "AND so.customer = %s" %frappe.db.escape(filters.customer)
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
index 80874c1..0e565a3 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -8,7 +8,7 @@
 			fieldname: "tree_type",
 			label: __("Tree Type"),
 			fieldtype: "Select",
-			options: ["Customer Group","Customer","Item Group","Item","Territory","Order Type"],
+			options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Order Type", "Project"],
 			default: "Customer",
 			reqd: 1
 		},
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index 4d113c8..d036a1c 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -34,7 +34,7 @@
 
 	def get_columns(self):
 		self.columns = [{
-				"label": _(self.filters.tree_type + " ID"),
+				"label": _(self.filters.tree_type),
 				"options": self.filters.tree_type if self.filters.tree_type != "Order Type" else "",
 				"fieldname": "entity",
 				"fieldtype": "Link" if self.filters.tree_type != "Order Type" else "Data",
@@ -97,6 +97,10 @@
 			self.get_sales_transactions_based_on_order_type()
 			self.get_rows_by_group()
 
+		elif self.filters.tree_type == "Project":
+			self.get_sales_transactions_based_on_project()
+			self.get_rows()
+
 	def get_sales_transactions_based_on_order_type(self):
 		if self.filters["value_quantity"] == 'Value':
 			value_field = "base_net_total"
@@ -198,6 +202,24 @@
 
 		self.get_groups()
 
+	def get_sales_transactions_based_on_project(self):
+		if self.filters["value_quantity"] == 'Value':
+			value_field = "base_net_total as value_field"
+		else:
+			value_field = "total_qty as value_field"
+
+		entity = "project as entity"
+
+		self.entries = frappe.get_all(self.filters.doc_type,
+			fields=[entity, value_field, self.date_field],
+			filters={
+				"docstatus": 1,
+				"company": self.filters.company,
+				"project": ["!=", ""],
+				self.date_field: ('between', [self.filters.from_date, self.filters.to_date])
+			}
+		)
+
 	def get_rows(self):
 		self.data = []
 		self.get_periodic_data()
@@ -205,7 +227,7 @@
 		for entity, period_data in iteritems(self.entity_periodic_data):
 			row = {
 				"entity": entity,
-				"entity_name": self.entity_names.get(entity)
+				"entity_name": self.entity_names.get(entity) if hasattr(self, 'entity_names') else None
 			}
 			total = 0
 			for end_date in self.periodic_daterange:
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
index 857b982..ae216ca 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
@@ -63,13 +63,13 @@
 		"label": _(partner_doctype),
 		"fieldtype": "Link",
 		"options": partner_doctype,
-		"width": 100
+		"width": 150
 	}, {
 		"fieldname": "item_group",
 		"label": _("Item Group"),
 		"fieldtype": "Link",
 		"options": "Item Group",
-		"width": 100
+		"width": 150
 	}]
 
 	for period in period_list:
@@ -81,19 +81,19 @@
 			"label": _("Target ({})").format(period.label),
 			"fieldtype": fieldtype,
 			"options": options,
-			"width": 100
+			"width": 150
 		}, {
 			"fieldname": period.key,
 			"label": _("Achieved ({})").format(period.label),
 			"fieldtype": fieldtype,
 			"options": options,
-			"width": 100
+			"width": 150
 		}, {
 			"fieldname": variance_key,
 			"label": _("Variance ({})").format(period.label),
 			"fieldtype": fieldtype,
 			"options": options,
-			"width": 100
+			"width": 150
 		}])
 
 	columns.extend([{
@@ -101,19 +101,19 @@
 		"label": _("Total Target"),
 		"fieldtype": fieldtype,
 		"options": options,
-		"width": 100
+		"width": 150
 	}, {
 		"fieldname": "total_achieved",
 		"label": _("Total Achieved"),
 		"fieldtype": fieldtype,
 		"options": options,
-		"width": 100
+		"width": 150
 	}, {
 		"fieldname": "total_variance",
 		"label": _("Total Variance"),
 		"fieldtype": fieldtype,
 		"options": options,
-		"width": 100
+		"width": 150
 	}])
 
 	return columns
@@ -154,10 +154,10 @@
 				if (r.get(sales_field) == d.parent and r.item_group == d.item_group and
 					period.from_date <= r.get(date_field) and r.get(date_field) <= period.to_date):
 					details[p_key] += r.get(qty_or_amount_field, 0)
-					details[variance_key] = details.get(target_key) - details.get(p_key)
+					details[variance_key] = details.get(p_key) - details.get(target_key)
 
 			details["total_achieved"] += details.get(p_key)
-			details["total_variance"] = details.get("total_target") - details.get("total_achieved")
+			details["total_variance"] = details.get("total_achieved") - details.get("total_target")
 
 	return rows
 
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
index f99f68c..38bb127 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js
@@ -44,5 +44,20 @@
 			options: "Quantity\nAmount",
 			default: "Quantity"
 		},
-	]
+	],
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		
+		if (column.fieldname.includes('variance')) {
+			
+			if (data[column.fieldname] < 0) {
+				value = "<span style='color:red'>" + value + "</span>";
+			}
+			else if (data[column.fieldname] > 0) {
+				value = "<span style='color:green'>" + value + "</span>";
+			}
+		}
+
+		return value;
+	}
 }
diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
index 9f6bfc4..a8e2fad 100644
--- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js
@@ -44,5 +44,20 @@
 			options: "Quantity\nAmount",
 			default: "Quantity"
 		},
-	]
+	],
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		
+		if (column.fieldname.includes('variance')) {
+			
+			if (data[column.fieldname] < 0) {
+				value = "<span style='color:red'>" + value + "</span>";
+			}
+			else if (data[column.fieldname] > 0) {
+				value = "<span style='color:green'>" + value + "</span>";
+			}
+		}
+
+		return value;
+	}
 }
diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
index dd9607f..263391a 100644
--- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
+++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js
@@ -44,5 +44,20 @@
 			options: "Quantity\nAmount",
 			default: "Quantity"
 		},
-	]
+	],
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		
+		if (column.fieldname.includes('variance')) {
+			
+			if (data[column.fieldname] < 0) {
+				value = "<span style='color:red'>" + value + "</span>";
+			}
+			else if (data[column.fieldname] > 0) {
+				value = "<span style='color:green'>" + value + "</span>";
+			}
+		}
+
+		return value;
+	}
 }
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 8ecc13b..c94831e 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -26,7 +26,8 @@
 		tabDocField where fieldtype='Link' and options='Company'"""):
 		if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
 			"Party Account", "Employee", "Sales Taxes and Charges Template",
-			"Purchase Taxes and Charges Template", "POS Profile", 'BOM'):
+			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
+			"Company", "Bank Account"):
 				delete_for_doctype(doctype, company_name)
 
 	# reset company values
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 50f9d84..2225fe1 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -20,12 +20,14 @@
 	frappe.get_doc({'doctype': "Role", "role_name": "Analytics"}).insert()
 	set_single_defaults()
 	create_compact_item_print_custom_field()
+	create_print_uom_after_qty_custom_field()
 	create_print_zero_amount_taxes_custom_field()
 	add_all_roles_to("Administrator")
 	create_default_cash_flow_mapper_templates()
 	create_default_success_action()
 	create_default_energy_point_rules()
 	add_company_to_session_defaults()
+	add_standard_navbar_items()
 	frappe.db.commit()
 
 
@@ -65,6 +67,16 @@
 	})
 
 
+def create_print_uom_after_qty_custom_field():
+	create_custom_field('Print Settings', {
+		'label': _('Print UOM after Quantity'),
+		'fieldname': 'print_uom_after_quantity',
+		'fieldtype': 'Check',
+		'default': 0,
+		'insert_after': 'compact_item_print'
+	})
+
+
 def create_print_zero_amount_taxes_custom_field():
 	create_custom_field('Print Settings', {
 		'label': _('Print taxes with zero amount'),
@@ -104,3 +116,45 @@
 		"ref_doctype": "Company"
 	})
 	settings.save()
+
+def add_standard_navbar_items():
+	navbar_settings = frappe.get_single("Navbar Settings")
+
+	erpnext_navbar_items = [
+		{
+			'item_label': 'Documentation',
+			'item_type': 'Route',
+			'route': 'https://erpnext.com/docs/user/manual',
+			'is_standard': 1
+		},
+		{
+			'item_label': 'User Forum',
+			'item_type': 'Route',
+			'route': 'https://discuss.erpnext.com',
+			'is_standard': 1
+		},
+		{
+			'item_label': 'Report an Issue',
+			'item_type': 'Route',
+			'route': 'https://github.com/frappe/erpnext/issues',
+			'is_standard': 1
+		}
+	]
+
+	current_nabvar_items = navbar_settings.help_dropdown
+	navbar_settings.set('help_dropdown', [])
+
+	for item in erpnext_navbar_items:
+		navbar_settings.append('help_dropdown', item)
+
+	for item in current_nabvar_items:
+		navbar_settings.append('help_dropdown', {
+			'item_label': item.item_label,
+			'item_type': item.item_type,
+			'route': item.route,
+			'action': item.action,
+			'is_standard': item.is_standard,
+			'hidden': item.hidden
+		})
+
+	navbar_settings.save()
diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py
index e863f17..ef238f1 100644
--- a/erpnext/startup/leaderboard.py
+++ b/erpnext/startup/leaderboard.py
@@ -123,7 +123,8 @@
 	if field == "outstanding_amount":
 		filters = [['docstatus', '=', '1'], ['company', '=', company]]
 		if date_range:
-			filters.append(['posting_date', 'between', date_range])
+			date_range = frappe.parse_json(date_range)
+			filters.append(['posting_date', 'between', [date_range[0], date_range[1]]])
 		return frappe.db.get_all('Purchase Invoice',
 			fields = ['supplier as name', 'sum(outstanding_amount) as value'],
 			filters = filters,
diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json
index 89f40f0..81bbb51 100644
--- a/erpnext/stock/desk_page/stock/stock.json
+++ b/erpnext/stock/desk_page/stock/stock.json
@@ -33,7 +33,7 @@
   {
    "hidden": 0,
    "label": "Key Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Item Price\"\n        ],\n        \"doctype\": \"Item Price\",\n        \"is_query_report\": false,\n        \"label\": \"Item-wise Price List Rate\",\n        \"name\": \"Item-wise Price List Rate\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Stock Entry\"\n        ],\n        \"doctype\": \"Stock Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Analytics\",\n        \"name\": \"Stock Analytics\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Delivery Note\"\n        ],\n        \"doctype\": \"Delivery Note\",\n        \"is_query_report\": true,\n        \"label\": \"Delivery Note Trends\",\n        \"name\": \"Delivery Note Trends\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Purchase Receipt\"\n        ],\n        \"doctype\": \"Purchase Receipt\",\n        \"is_query_report\": true,\n        \"label\": \"Purchase Receipt Trends\",\n        \"name\": \"Purchase Receipt Trends\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Sales Order\"\n        ],\n        \"doctype\": \"Sales Order\",\n        \"is_query_report\": true,\n        \"label\": \"Sales Order Analysis\",\n        \"name\": \"Sales Order Analysis\",\n        \"type\": \"report\"\n    },\n   {\n         \"dependencies\": [\n            \"Purchase Order\"\n        ],\n        \"doctype\": \"Purchase Order\",\n        \"is_query_report\": true,\n        \"label\": \"Purchase Order Analysis\",\n        \"name\": \"Purchase Order Analysis\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Bin\"\n        ],\n        \"doctype\": \"Bin\",\n        \"is_query_report\": true,\n        \"label\": \"Item Shortage Report\",\n        \"name\": \"Item Shortage Report\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Batch\"\n        ],\n        \"doctype\": \"Batch\",\n        \"is_query_report\": true,\n        \"label\": \"Batch-Wise Balance History\",\n        \"name\": \"Batch-Wise Balance History\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Item Price\"\n        ],\n        \"doctype\": \"Item Price\",\n        \"is_query_report\": false,\n        \"label\": \"Item-wise Price List Rate\",\n        \"name\": \"Item-wise Price List Rate\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Stock Entry\"\n        ],\n        \"doctype\": \"Stock Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Analytics\",\n        \"name\": \"Stock Analytics\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Qty vs Serial No Count\",\n        \"name\": \"Stock Qty vs Serial No Count\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Delivery Note\"\n        ],\n        \"doctype\": \"Delivery Note\",\n        \"is_query_report\": true,\n        \"label\": \"Delivery Note Trends\",\n        \"name\": \"Delivery Note Trends\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Purchase Receipt\"\n        ],\n        \"doctype\": \"Purchase Receipt\",\n        \"is_query_report\": true,\n        \"label\": \"Purchase Receipt Trends\",\n        \"name\": \"Purchase Receipt Trends\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Sales Order\"\n        ],\n        \"doctype\": \"Sales Order\",\n        \"is_query_report\": true,\n        \"label\": \"Sales Order Analysis\",\n        \"name\": \"Sales Order Analysis\",\n        \"type\": \"report\"\n    },\n   {\n         \"dependencies\": [\n            \"Purchase Order\"\n        ],\n        \"doctype\": \"Purchase Order\",\n        \"is_query_report\": true,\n        \"label\": \"Purchase Order Analysis\",\n        \"name\": \"Purchase Order Analysis\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Bin\"\n        ],\n        \"doctype\": \"Bin\",\n        \"is_query_report\": true,\n        \"label\": \"Item Shortage Report\",\n        \"name\": \"Item Shortage Report\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Batch\"\n        ],\n        \"doctype\": \"Batch\",\n        \"is_query_report\": true,\n        \"label\": \"Batch-Wise Balance History\",\n        \"name\": \"Batch-Wise Balance History\",\n        \"type\": \"report\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -59,7 +59,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Stock",
- "modified": "2020-06-30 18:41:21.822817",
+ "modified": "2020-08-11 17:29:32.626067",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index 1eb4577..943cb34 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -166,11 +166,11 @@
  "idx": 1,
  "image_field": "image",
  "max_attachments": 5,
- "modified": "2019-10-03 22:38:45.104056",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Batch",
- "owner": "harshada@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "create": 1,
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 60f6a68..19d0bec 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -130,12 +130,18 @@
 			if (this.frm.doc.docstatus===0) {
 				this.frm.add_custom_button(__('Sales Order'),
 					function() {
+						if (!me.frm.doc.customer) {
+							frappe.throw({
+								title: __("Mandatory"),
+								message: __("Please Select a Customer")
+							});
+						}
 						erpnext.utils.map_current_doc({
 							method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
 							source_doctype: "Sales Order",
 							target: me.frm,
 							setters: {
-								customer: me.frm.doc.customer || undefined,
+								customer: me.frm.doc.customer,
 							},
 							get_query_filters: {
 								docstatus: 1,
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index e07e653..faf4acc 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -26,19 +26,19 @@
 
 	refresh: function(frm) {
 		if (frm.doc.is_stock_item) {
-			frm.add_custom_button(__("Balance"), function() {
+			frm.add_custom_button(__("Stock Balance"), function() {
 				frappe.route_options = {
 					"item_code": frm.doc.name
 				}
 				frappe.set_route("query-report", "Stock Balance");
 			}, __("View"));
-			frm.add_custom_button(__("Ledger"), function() {
+			frm.add_custom_button(__("Stock Ledger"), function() {
 				frappe.route_options = {
 					"item_code": frm.doc.name
 				}
 				frappe.set_route("query-report", "Stock Ledger");
 			}, __("View"));
-			frm.add_custom_button(__("Projected"), function() {
+			frm.add_custom_button(__("Stock Projected Qty"), function() {
 				frappe.route_options = {
 					"item_code": frm.doc.name
 				}
diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
index 90a392c..b24d621 100644
--- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
+++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
@@ -133,11 +133,11 @@
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-11-12 15:41:21.053462",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Landed Cost Item",
- "owner": "wasim@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC"
diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
index f49c147..9b2b5da 100644
--- a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
+++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
@@ -173,11 +173,11 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2016-07-20 10:49:34.228751", 
+ "modified": "2020-09-18 17:26:09.703215", 
  "modified_by": "Administrator", 
  "module": "Stock", 
  "name": "Landed Cost Purchase Receipt", 
- "owner": "wasim@webnotestech.com", 
+ "owner": "Administrator", 
  "permissions": [], 
  "quick_entry": 0, 
  "read_only": 0, 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 50c18f6..c504e23 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -116,12 +116,18 @@
 			if (this.frm.doc.docstatus == 0) {
 				this.frm.add_custom_button(__('Purchase Order'),
 					function () {
+						if (!me.frm.doc.supplier) {
+							frappe.throw({
+								title: __("Mandatory"),
+								message: __("Please Select a Supplier")
+							});
+						}
 						erpnext.utils.map_current_doc({
 							method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
 							source_doctype: "Purchase Order",
 							target: me.frm,
 							setters: {
-								supplier: me.frm.doc.supplier || undefined,
+								supplier: me.frm.doc.supplier,
 							},
 							get_query_filters: {
 								docstatus: 1,
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 4a8236d..1e7153e 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -3,6 +3,7 @@
 
 from __future__ import unicode_literals
 import unittest
+import json
 import frappe, erpnext
 import frappe.defaults
 from frappe.utils import cint, flt, cstr, today, random_string
@@ -152,13 +153,78 @@
 			qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
 		pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=0, is_subcontracted="Yes",
 			company="_Test Company with perpetual inventory", warehouse='Stores - TCP1', supplier_warehouse='Work In Progress - TCP1')
-		
+
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
 
 		self.assertFalse(gl_entries)
 
 		set_perpetual_inventory(0)
 
+	def test_subcontracting_over_receipt(self):
+		"""
+			Behaviour: Raise multiple PRs against one PO that in total
+				receive more than the required qty in the PO.
+			Expected Result: Error Raised for Over Receipt against PO.
+		"""
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+		from erpnext.buying.doctype.purchase_order.test_purchase_order import (update_backflush_based_on,
+			make_subcontracted_item, create_purchase_order)
+		from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt,
+			make_rm_stock_entry as make_subcontract_transfer_entry)
+
+		update_backflush_based_on("Material Transferred for Subcontract")
+		item_code = "_Test Subcontracted FG Item 1"
+		make_subcontracted_item(item_code)
+
+		po = create_purchase_order(item_code=item_code, qty=1,
+			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
+
+		#stock raw materials in a warehouse before transfer
+		make_stock_entry(target="_Test Warehouse - _TC",
+			item_code="_Test Item Home Desktop 100", qty=1, basic_rate=100)
+		make_stock_entry(target="_Test Warehouse - _TC",
+			item_code = "Test Extra Item 1", qty=1, basic_rate=100)
+		make_stock_entry(target="_Test Warehouse - _TC",
+			item_code = "_Test Item", qty=1, basic_rate=100)
+
+		rm_items = [
+			{
+				"item_code": item_code,
+				"rm_item_code": po.supplied_items[0].rm_item_code,
+				"item_name": "_Test Item",
+				"qty": po.supplied_items[0].required_qty,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos"
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": po.supplied_items[1].rm_item_code,
+				"item_name": "Test Extra Item 1",
+				"qty": po.supplied_items[1].required_qty,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos"
+			},
+			{
+				"item_code": item_code,
+				"rm_item_code": po.supplied_items[2].rm_item_code,
+				"item_name": "_Test Item Home Desktop 100",
+				"qty": po.supplied_items[2].required_qty,
+				"warehouse": "_Test Warehouse - _TC",
+				"stock_uom": "Nos"
+			}
+		]
+		rm_item_string = json.dumps(rm_items)
+		se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
+		se.to_warehouse = "_Test Warehouse 1 - _TC"
+		se.save()
+		se.submit()
+
+		pr1 = make_purchase_receipt(po.name)
+		pr2 = make_purchase_receipt(po.name)
+
+		pr1.submit()
+		self.assertRaises(frappe.ValidationError, pr2.submit)
+
 	def test_serial_no_supplier(self):
 		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
 		self.assertEqual(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"),
@@ -428,8 +494,7 @@
 			"expected_value_after_useful_life": 10,
 			"depreciation_method": "Straight Line",
 			"total_number_of_depreciations": 3,
-			"frequency_of_depreciation": 1,
-			"depreciation_start_date": frappe.utils.nowdate()
+			"frequency_of_depreciation": 1
 		})
 		asset.submit()
 
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index c951066..3643174 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -73,7 +73,8 @@
    "fieldname": "reference_type",
    "fieldtype": "Select",
    "label": "Reference Type",
-   "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry"
+   "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry",
+   "reqd": 1
   },
   {
    "fieldname": "reference_name",
@@ -84,7 +85,8 @@
    "label": "Reference Name",
    "oldfieldname": "purchase_receipt_no",
    "oldfieldtype": "Link",
-   "options": "reference_type"
+   "options": "reference_type",
+   "reqd": 1
   },
   {
    "fieldname": "section_break_7",
@@ -231,9 +233,10 @@
  ],
  "icon": "fa fa-search",
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-04-26 17:50:25.068222",
+ "modified": "2020-09-12 16:11:31.910508",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 9845bc2..1f95447 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -23,6 +23,24 @@
 			}
 		});
 
+		frm.set_query('source_warehouse_address', function() {
+			return {
+				filters: {
+					link_doctype: 'Warehouse',
+					link_name: frm.doc.from_warehouse
+				}
+			}
+		});
+
+		frm.set_query('target_warehouse_address', function() {
+			return {
+				filters: {
+					link_doctype: 'Warehouse',
+					link_name: frm.doc.to_warehouse
+				}
+			}
+		});
+
 		frappe.db.get_value('Stock Settings', {name: 'Stock Settings'}, 'sample_retention_warehouse', (r) => {
 			if (r.sample_retention_warehouse) {
 				var filters = [
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 30bcccd..a92d04f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -513,7 +513,7 @@
 						d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
 					elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually:
 						d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty)
-						d.basic_amount = d.basic_rate * d.qty
+						d.basic_amount = d.basic_rate * flt(d.qty)
 
 	def distribute_additional_costs(self):
 		if self.purpose == "Material Issue":
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 43fbc00..b81f8a0 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -258,6 +258,7 @@
 
 			sl_entries.append(args)
 
+		qty_after_transaction = 0
 		for serial_no in serial_nos:
 			args = self.get_sle_for_items(row, [serial_no])
 
@@ -271,11 +272,19 @@
 			if previous_sle and row.warehouse != previous_sle.get("warehouse"):
 				# If serial no exists in different warehouse
 
+				warehouse = previous_sle.get("warehouse", '') or row.warehouse
+
+				if not qty_after_transaction:
+					qty_after_transaction = get_stock_balance(row.item_code,
+						warehouse, self.posting_date, self.posting_time)
+
+				qty_after_transaction -= 1
+
 				new_args = args.copy()
 				new_args.update({
 					'actual_qty': -1,
-					'qty_after_transaction': cint(previous_sle.get('qty_after_transaction')) - 1,
-					'warehouse': previous_sle.get("warehouse", '') or row.warehouse,
+					'qty_after_transaction': qty_after_transaction,
+					'warehouse': warehouse,
 					'valuation_rate': previous_sle.get("valuation_rate")
 				})
 
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js
index 23700c9..2499c80 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js
@@ -9,13 +9,15 @@
 			"fieldtype": "Date",
 			"width": "80",
 			"default": frappe.sys_defaults.year_start_date,
+			"reqd": 1
 		},
 		{
 			"fieldname":"to_date",
 			"label": __("To Date"),
 			"fieldtype": "Date",
 			"width": "80",
-			"default": frappe.datetime.get_today()
+			"default": frappe.datetime.get_today(),
+			"reqd": 1
 		},
 		{
 			"fieldname": "item",
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 2c95084..8f3e246 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -11,6 +11,9 @@
 def execute(filters=None):
 	if not filters: filters = {}
 
+	if filters.from_date > filters.to_date:
+		frappe.throw(_("From Date must be before To Date"))
+
 	float_precision = cint(frappe.db.get_default("float_precision")) or 3
 
 	columns = get_columns(filters)
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/__init__.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/__init__.py
similarity index 100%
copy from erpnext/support/report/minutes_to_first_response_for_issues/__init__.py
copy to erpnext/stock/report/stock_qty_vs_serial_no_count/__init__.py
diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js
new file mode 100644
index 0000000..2a0fd40
--- /dev/null
+++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Stock Qty vs Serial No Count"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+			"reqd": 1
+		},
+		{
+			"fieldname":"warehouse",
+			"label": __("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse",
+			"get_query": function() {
+				const company = frappe.query_report.get_filter_value('company');
+				return {
+					filters: { 'company': company }
+				}
+			},
+			"reqd": 1
+		},
+	],
+
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		if (column.fieldname == "difference" && data) {
+			if (data.difference > 0) {
+				value = "<span style='color:red'>" + value + "</span>";
+			}
+			else if (data.difference < 0) {
+				value = "<span style='color:red'>" + value + "</span>";
+			}
+		}
+		return value;
+	}
+};
diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.json b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.json
new file mode 100644
index 0000000..c7108b5
--- /dev/null
+++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.json
@@ -0,0 +1,27 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-07-23 19:31:32.395011",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-07-23 19:32:02.168185",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Qty vs Serial No Count",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Item",
+ "report_name": "Stock Qty vs Serial No Count",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock Manager"
+  },
+  {
+   "role": "Stock User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
new file mode 100644
index 0000000..55f041c
--- /dev/null
+++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py
@@ -0,0 +1,80 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute(filters=None):
+	columns = get_columns()
+	data = get_data(filters.warehouse)
+	return columns, data
+
+def get_columns():
+	columns = [
+		{
+			"label": _("Item Code"),
+			"fieldname": "item_code",
+			"fieldtype": "Link",
+			"options": "Item",
+			"width": 200
+		},
+		{
+			"label": _("Item Name"),
+			"fieldname": "item_name",
+			"fieldtype": "Data",
+			"width": 200
+		},
+		{
+			"label": _("Serial No Count"),
+			"fieldname": "total",
+			"fieldtype": "Float",
+			"width": 150
+		},
+		{
+			"label": _("Stock Qty"),
+			"fieldname": "stock_qty",
+			"fieldtype": "Float",
+			"width": 150
+		},
+		{
+			"label": _("Difference"),
+			"fieldname": "difference",
+			"fieldtype": "Float",
+			"width": 150
+		},
+	]
+
+	return columns
+
+def get_data(warehouse):
+	serial_item_list = frappe.get_all("Item", filters={
+		'has_serial_no': True,
+	}, fields=['item_code', 'item_name'])
+	
+	status_list = ['Active', 'Expired']
+	data = []
+	for item in serial_item_list:
+		total_serial_no = frappe.db.count("Serial No", 
+			filters={"item_code": item.item_code, "status": ("in", status_list), "warehouse": warehouse})
+
+		actual_qty = frappe.db.get_value('Bin', fieldname=['actual_qty'], 
+			filters={"warehouse": warehouse, "item_code": item.item_code})
+
+		# frappe.db.get_value returns null if no record exist.
+		if not actual_qty:
+			actual_qty = 0
+
+		difference = total_serial_no - actual_qty
+
+		row = {
+			"item_code": item.item_code,
+			"item_name": item.item_name,
+			"total": total_serial_no,
+			"stock_qty": actual_qty,
+			"difference": difference,
+		}
+
+		data.append(row)
+
+	return data
\ No newline at end of file
diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json
index 1a73e05..f676ce5 100644
--- a/erpnext/support/desk_page/support/support.json
+++ b/erpnext/support/desk_page/support/support.json
@@ -28,7 +28,7 @@
   {
    "hidden": 0,
    "label": "Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Issue\"\n        ],\n        \"doctype\": \"Issue\",\n        \"is_query_report\": true,\n        \"label\": \"Minutes to First Response for Issues\",\n        \"name\": \"Minutes to First Response for Issues\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Issue\"\n        ],\n        \"doctype\": \"Issue\",\n        \"is_query_report\": true,\n        \"label\": \"First Response Time for Issues\",\n        \"name\": \"First Response Time for Issues\",\n        \"type\": \"report\"\n    }\n]"
   }
  ],
  "category": "Modules",
@@ -44,7 +44,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Support",
- "modified": "2020-06-30 18:41:35.683929",
+ "modified": "2020-08-11 15:49:34.307341",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Support",
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index a3e2fc7..940b940 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -2,10 +2,14 @@
 	onload: function(frm) {
 		frm.email_field = "raised_by";
 
-		frappe.db.get_value("Support Settings", {name: "Support Settings"}, "allow_resetting_service_level_agreement", (r) => {
-			if (!r.allow_resetting_service_level_agreement) {
-				frm.set_df_property("reset_service_level_agreement", "hidden", 1) ;
-			}
+		frappe.db.get_value("Support Settings", {name: "Support Settings"},
+			["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => {
+				if (r && r.track_service_level_agreement == "0") {
+					frm.set_df_property("service_level_section", "hidden", 1);
+				}
+				if (r && r.allow_resetting_service_level_agreement == "0") {
+					frm.set_df_property("reset_service_level_agreement", "hidden", 1);
+				}
 		});
 
 		if (frm.doc.service_level_agreement) {
@@ -38,7 +42,7 @@
 	},
 
 	refresh: function (frm) {
-		if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") {
+		if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") {
 			if (frm.doc.service_level_agreement) {
 				frappe.call({
 					'method': 'frappe.client.get',
@@ -85,14 +89,14 @@
 			if (frm.doc.service_level_agreement) {
 				frm.dashboard.clear_headline();
 
-				let agreement_fulfilled = (frm.doc.agreement_fulfilled == "Fulfilled") ?
+				let agreement_status = (frm.doc.agreement_status == "Fulfilled") ?
 					{"indicator": "green", "msg": "Service Level Agreement has been fulfilled"} :
 					{"indicator": "red", "msg": "Service Level Agreement Failed"};
 
 				frm.dashboard.set_headline_alert(
 					'<div class="row">' +
 						'<div class="col-xs-12">' +
-							'<span class="indicator whitespace-nowrap '+ agreement_fulfilled.indicator +'"><span class="hidden-xs">'+ agreement_fulfilled.msg +'</span></span> ' +
+							'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
 						'</div>' +
 					'</div>'
 				);
@@ -198,13 +202,13 @@
 	frm.dashboard.clear_headline();
 
 	var time_to_respond = get_status(frm.doc.response_by_variance);
-	if (!frm.doc.first_responded_on && frm.doc.agreement_fulfilled === "Ongoing") {
-		time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled);
+	if (!frm.doc.first_responded_on && frm.doc.agreement_status === "Ongoing") {
+		time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_status);
 	}
 
 	var time_to_resolve = get_status(frm.doc.resolution_by_variance);
-	if (!frm.doc.resolution_date && frm.doc.agreement_fulfilled === "Ongoing") {
-		time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled);
+	if (!frm.doc.resolution_date && frm.doc.agreement_status === "Ongoing") {
+		time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_status);
 	}
 
 	frm.dashboard.set_headline_alert(
@@ -219,10 +223,10 @@
 	);
 }
 
-function get_time_left(timestamp, agreement_fulfilled) {
+function get_time_left(timestamp, agreement_status) {
 	const diff = moment(timestamp).diff(moment());
 	const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed";
-	let indicator = (diff_display == 'Failed' && agreement_fulfilled != "Fulfilled") ? "red" : "green";
+	let indicator = (diff_display == 'Failed' && agreement_status != "Fulfilled") ? "red" : "green";
 	return {"diff_display": diff_display, "indicator": indicator};
 }
 
diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json
index 6525ab2..a43381c 100644
--- a/erpnext/support/doctype/issue/issue.json
+++ b/erpnext/support/doctype/issue/issue.json
@@ -27,17 +27,25 @@
   "response_by_variance",
   "reset_service_level_agreement",
   "cb",
-  "agreement_fulfilled",
+  "agreement_status",
   "resolution_by",
   "resolution_by_variance",
   "service_level_agreement_creation",
   "on_hold_since",
   "total_hold_time",
   "response",
-  "mins_to_first_response",
+  "first_response_time",
   "first_responded_on",
   "column_break_26",
   "avg_response_time",
+  "section_break_19",
+  "resolution_details",
+  "column_break1",
+  "opening_date",
+  "opening_time",
+  "resolution_date",
+  "resolution_time",
+  "user_resolution_time",
   "additional_info",
   "lead",
   "contact",
@@ -46,23 +54,14 @@
   "customer_name",
   "project",
   "company",
-  "section_break_19",
-  "resolution_details",
-  "column_break1",
-  "opening_date",
-  "opening_time",
-  "resolution_date",
-  "content_type",
-  "attachment",
   "via_customer_portal",
-  "resolution_time",
-  "user_resolution_time"
+  "attachment",
+  "content_type"
  ],
  "fields": [
   {
    "fieldname": "subject_section",
    "fieldtype": "Section Break",
-   "label": "Subject",
    "options": "fa fa-flag"
   },
   {
@@ -158,7 +157,7 @@
    "collapsible": 1,
    "fieldname": "service_level_section",
    "fieldtype": "Section Break",
-   "label": "Service Level"
+   "label": "Service Level Agreement Details"
   },
   {
    "fieldname": "service_level_agreement",
@@ -191,14 +190,7 @@
    "collapsible": 1,
    "fieldname": "response",
    "fieldtype": "Section Break",
-   "label": "Response"
-  },
-  {
-   "bold": 1,
-   "fieldname": "mins_to_first_response",
-   "fieldtype": "Float",
-   "label": "Mins to First Response",
-   "read_only": 1
+   "label": "Response Details"
   },
   {
    "fieldname": "first_responded_on",
@@ -261,7 +253,7 @@
    "collapsible": 1,
    "fieldname": "section_break_19",
    "fieldtype": "Section Break",
-   "label": "Resolution"
+   "label": "Resolution Details"
   },
   {
    "depends_on": "eval:!doc.__islocal",
@@ -327,27 +319,18 @@
    "label": "Via Customer Portal"
   },
   {
-   "default": "Ongoing",
-   "depends_on": "eval: doc.service_level_agreement",
-   "fieldname": "agreement_fulfilled",
-   "fieldtype": "Select",
-   "label": "Service Level Agreement Fulfilled",
-   "options": "Ongoing\nFulfilled\nFailed",
-   "read_only": 1
-  },
-  {
    "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';",
-   "description": "in hours",
    "fieldname": "response_by_variance",
-   "fieldtype": "Float",
+   "fieldtype": "Duration",
+   "hide_seconds": 1,
    "label": "Response By Variance",
    "read_only": 1
   },
   {
    "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';",
-   "description": "in hours",
    "fieldname": "resolution_by_variance",
-   "fieldtype": "Float",
+   "fieldtype": "Duration",
+   "hide_seconds": 1,
    "label": "Resolution By Variance",
    "read_only": 1
   },
@@ -406,12 +389,28 @@
    "fieldtype": "Duration",
    "label": "Total Hold Time",
    "read_only": 1
+  },
+  {
+   "default": "Ongoing",
+   "depends_on": "eval: doc.service_level_agreement",
+   "fieldname": "agreement_status",
+   "fieldtype": "Select",
+   "label": "Service Level Agreement Status",
+   "options": "Ongoing\nFulfilled\nFailed",
+   "read_only": 1
+  },
+  {
+   "bold": 1,
+   "fieldname": "first_response_time",
+   "fieldtype": "Duration",
+   "label": "First Response Time",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-ticket",
  "idx": 7,
  "links": [],
- "modified": "2020-06-10 12:47:37.146914",
+ "modified": "2020-08-11 18:49:07.574769",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Issue",
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 87168e1..920c13c 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -61,7 +61,7 @@
 
 		if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]:
 			self.resolution_date = frappe.flags.current_time or now_datetime()
-			if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing":
+			if frappe.db.get_value("Issue", self.name, "agreement_status") == "Ongoing":
 				set_service_level_agreement_variance(issue=self.name)
 				self.update_agreement_status()
 			set_resolution_time(issue=self)
@@ -72,7 +72,7 @@
 			self.resolution_date = None
 			self.reset_issue_metrics()
 			# enable SLA and variance on Reopen
-			self.agreement_fulfilled = "Ongoing"
+			self.agreement_status = "Ongoing"
 			set_service_level_agreement_variance(issue=self.name)
 
 		self.handle_hold_time(status)
@@ -113,39 +113,39 @@
 					if not self.first_responded_on:
 						response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
 						response_by = add_to_date(response_by, seconds=round(last_hold_time))
-						response_by_variance = round(time_diff_in_hours(response_by, now_time))
+						response_by_variance = round(time_diff_in_seconds(response_by, now_time))
 						update_values['response_by'] = response_by
-						update_values['response_by_variance'] = response_by_variance + (last_hold_time // 3600)
+						update_values['response_by_variance'] = response_by_variance + last_hold_time
 
 					resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
 					resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time))
-					resolution_by_variance = round(time_diff_in_hours(resolution_by, now_time))
+					resolution_by_variance = round(time_diff_in_seconds(resolution_by, now_time))
 					update_values['resolution_by'] = resolution_by
-					update_values['resolution_by_variance'] = resolution_by_variance + (last_hold_time // 3600)
+					update_values['resolution_by_variance'] = resolution_by_variance + last_hold_time
 					update_values['on_hold_since'] = None
 
 				self.db_set(update_values)
 
 	def update_agreement_status(self):
-		if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
+		if self.service_level_agreement and self.agreement_status == "Ongoing":
 			if frappe.db.get_value("Issue", self.name, "response_by_variance") < 0 or \
 				frappe.db.get_value("Issue", self.name, "resolution_by_variance") < 0:
 
-				self.agreement_fulfilled = "Failed"
+				self.agreement_status = "Failed"
 			else:
-				self.agreement_fulfilled = "Fulfilled"
+				self.agreement_status = "Fulfilled"
 
-	def update_agreement_fulfilled_on_custom_status(self):
+	def update_agreement_status_on_custom_status(self):
 		"""
 			Update Agreement Fulfilled status using Custom Scripts for Custom Issue Status
 		"""
 		if not self.first_responded_on: # first_responded_on set when first reply is sent to customer
-			self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime()), 2)
+			self.response_by_variance = round(time_diff_in_seconds(self.response_by, now_datetime()), 2)
 
 		if not self.resolution_date: # resolution_date set when issue has been closed
-			self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()), 2)
+			self.resolution_by_variance = round(time_diff_in_seconds(self.resolution_by, now_datetime()), 2)
 
-		self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
+		self.agreement_status = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
 
 	def create_communication(self):
 		communication = frappe.new_doc("Communication")
@@ -172,7 +172,7 @@
 		replicated_issue = deepcopy(self)
 		replicated_issue.subject = subject
 		replicated_issue.issue_split_from = self.name
-		replicated_issue.mins_to_first_response = 0
+		replicated_issue.first_response_time = 0
 		replicated_issue.first_responded_on = None
 		replicated_issue.creation = now_datetime()
 
@@ -180,7 +180,7 @@
 		if replicated_issue.service_level_agreement:
 			replicated_issue.service_level_agreement_creation = now_datetime()
 			replicated_issue.service_level_agreement = None
-			replicated_issue.agreement_fulfilled = "Ongoing"
+			replicated_issue.agreement_status = "Ongoing"
 			replicated_issue.response_by = None
 			replicated_issue.response_by_variance = None
 			replicated_issue.resolution_by = None
@@ -241,8 +241,8 @@
 		self.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
 		self.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
 
-		self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime()))
-		self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()))
+		self.response_by_variance = round(time_diff_in_seconds(self.response_by, now_datetime()))
+		self.resolution_by_variance = round(time_diff_in_seconds(self.resolution_by, now_datetime()))
 
 	def change_service_level_agreement_and_priority(self):
 		if self.service_level_agreement and frappe.db.exists("Issue", self.name) and \
@@ -271,7 +271,7 @@
 
 		self.service_level_agreement_creation = now_datetime()
 		self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
-		self.agreement_fulfilled = "Ongoing"
+		self.agreement_status = "Ongoing"
 		self.save()
 
 	def reset_issue_metrics(self):
@@ -347,7 +347,7 @@
 def set_service_level_agreement_variance(issue=None):
 	current_time = frappe.flags.current_time or now_datetime()
 
-	filters = {"status": "Open", "agreement_fulfilled": "Ongoing"}
+	filters = {"status": "Open", "agreement_status": "Ongoing"}
 	if issue:
 		filters = {"name": issue}
 
@@ -358,13 +358,13 @@
 			variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
 			frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
 			if variance < 0:
-				frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
+				frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
 
 		if not doc.resolution_date: # resolution_date set when issue has been closed
 			variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
 			frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
 			if variance < 0:
-				frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
+				frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
 
 
 def set_resolution_time(issue):
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index fb8ceb5..c962dc6 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -73,7 +73,7 @@
 		issue.status = 'Closed'
 		issue.save()
 
-		self.assertEqual(issue.agreement_fulfilled, 'Fulfilled')
+		self.assertEqual(issue.agreement_status, 'Fulfilled')
 
 	def test_issue_metrics(self):
 		creation = datetime.datetime(2020, 3, 4, 4, 0)
diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json
index ae1a7a5..88ee4a3 100644
--- a/erpnext/support/doctype/warranty_claim/warranty_claim.json
+++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json
@@ -361,11 +361,11 @@
  ],
  "icon": "fa fa-bug",
  "idx": 1,
- "modified": "2019-05-24 10:56:30.626200",
+ "modified": "2020-09-18 17:26:09.703215",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Warranty Claim",
- "owner": "harshada@webnotestech.com",
+ "owner": "Administrator",
  "permissions": [
   {
    "create": 1,
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/__init__.py b/erpnext/support/report/first_response_time_for_issues/__init__.py
similarity index 100%
rename from erpnext/support/report/minutes_to_first_response_for_issues/__init__.py
rename to erpnext/support/report/first_response_time_for_issues/__init__.py
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
new file mode 100644
index 0000000..576e0b7
--- /dev/null
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["First Response Time for Issues"] = {
+	"filters": [
+		{
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
+		},
+		{
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default":frappe.datetime.nowdate()
+		}
+	],
+	get_chart_data: function(_columns, result) {
+		return {
+			data: {
+				labels: result.map(d => d[0]),
+				datasets: [{
+					name: 'First Response Time',
+					values: result.map(d => d[1])
+				}]
+			},
+			type: "line",
+			tooltipOptions: {
+				formatTooltipY: d => {
+					let duration_options = {
+						hide_days: 0,
+						hide_seconds: 0
+					};
+					value = frappe.utils.get_formatted_duration(d, duration_options);
+					return value;
+				}
+			}
+		}
+	}
+};
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.json b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.json
new file mode 100644
index 0000000..c4fe6f5
--- /dev/null
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-08-10 18:12:42.391224",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "Test 2",
+ "modified": "2020-08-10 18:12:42.391224",
+ "modified_by": "Administrator",
+ "module": "Support",
+ "name": "First Response Time for Issues",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "query": "select date(creation) as creation_date, avg(mins_to_first_response) from tabIssue where creation > '2016-05-01' group by date(creation) order by creation_date;",
+ "ref_doctype": "Issue",
+ "report_name": "First Response Time for Issues",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Support Team"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
new file mode 100644
index 0000000..922da2b
--- /dev/null
+++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute(filters=None):
+	columns = [
+		{
+			'fieldname': 'creation_date',
+			'label': 'Date',
+			'fieldtype': 'Date',
+			'width': 300
+		},
+		{
+			'fieldname': 'first_response_time',
+			'fieldtype': 'Duration',
+			'label': 'First Response Time',
+			'width': 300
+		},
+	]
+
+	data = frappe.db.sql('''
+		SELECT
+			date(creation) as creation_date,
+			avg(first_response_time) as avg_response_time
+		FROM tabIssue
+		WHERE
+			date(creation) between %s and %s
+			and first_response_time > 0
+		GROUP BY creation_date
+		ORDER BY creation_date desc
+	''', (filters.from_date, filters.to_date))
+
+	return columns, data
\ No newline at end of file
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.js b/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.js
deleted file mode 100644
index 034e777..0000000
--- a/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.js
+++ /dev/null
@@ -1,30 +0,0 @@
-frappe.query_reports["Minutes to First Response for Issues"] = {
-	"filters": [
-		{
-			"fieldname":"from_date",
-			"label": __("From Date"),
-			"fieldtype": "Date",
-			'reqd': 1,
-			"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
-		},
-		{
-			"fieldname":"to_date",
-			"label": __("To Date"),
-			"fieldtype": "Date",
-			'reqd': 1,
-			"default":frappe.datetime.nowdate()
-		},
-	],
-	get_chart_data: function(columns, result) {
-		return {
-			data: {
-				labels: result.map(d => d[0]),
-				datasets: [{
-					name: 'Mins to first response',
-					values: result.map(d => d[1])
-				}]
-			},
-			type: 'line',
-		}
-	}
-}
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.json b/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.json
deleted file mode 100644
index 539d3d9..0000000
--- a/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "add_total_row": 0, 
- "apply_user_permissions": 1, 
- "creation": "2016-06-14 17:44:26.034112", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 2, 
- "is_standard": "Yes", 
- "modified": "2017-02-24 20:06:18.391100", 
- "modified_by": "Administrator", 
- "module": "Support", 
- "name": "Minutes to First Response for Issues", 
- "owner": "Administrator", 
- "query": "select date(creation) as creation_date, avg(mins_to_first_response) from tabIssue where creation > '2016-05-01' group by date(creation) order by creation_date;", 
- "ref_doctype": "Issue", 
- "report_name": "Minutes to First Response for Issues", 
- "report_type": "Script Report", 
- "roles": [
-  {
-   "role": "Support Team"
-  }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.py b/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.py
deleted file mode 100644
index 57c2d44..0000000
--- a/erpnext/support/report/minutes_to_first_response_for_issues/minutes_to_first_response_for_issues.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute(filters=None):
-	columns = [
-		{
-			'fieldname': 'creation_date',
-			'label': 'Date',
-			'fieldtype': 'Date'
-		},
-		{
-			'fieldname': 'mins',
-			'fieldtype': 'Float',
-			'label': 'Mins to First Response'
-		},
-	]
-
-	data = frappe.db.sql('''select date(creation) as creation_date,
-		avg(mins_to_first_response) as mins
-		from tabIssue
-			where date(creation) between %s and %s
-			and mins_to_first_response > 0
-		group by creation_date order by creation_date desc''', (filters.from_date, filters.to_date))
-
-	return columns, data
diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html
index 2a70d8d..40bc0c7 100644
--- a/erpnext/templates/generators/item/item_add_to_cart.html
+++ b/erpnext/templates/generators/item/item_add_to_cart.html
@@ -27,22 +27,25 @@
 			{% endif %}
 		</div>
 		{% endif %}
-		{% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %}
 		<div class="mt-3">
-			<a href="/cart"
-				class="btn btn-light btn-view-in-cart {% if not product_info.qty %}hidden{% endif %}"
-				role="button"
-			>
-				{{ _("View in Cart") }}
-			</a>
-			<button
-				data-item-code="{{item_code}}"
-				class="btn btn-outline-primary btn-add-to-cart {% if product_info.qty %}hidden{% endif %}"
-			>
-				{{ _("Add to Cart") }}
-			</button>
+			{% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %}
+				<a href="/cart"
+					class="btn btn-light btn-view-in-cart {% if not product_info.qty %}hidden{% endif %}"
+					role="button"
+				>
+					{{ _("View in Cart") }}
+				</a>
+				<button
+					data-item-code="{{item_code}}"
+					class="btn btn-outline-primary btn-add-to-cart {% if product_info.qty %}hidden{% endif %}"
+				>
+					{{ _("Add to Cart") }}
+				</button>
+			{% endif %}
+			{% if cart_settings.show_contact_us_button %}
+				{% include "templates/generators/item/item_inquiry.html" %}
+			{% endif %}
 		</div>
-		{% endif %}
 	</div>
 </div>
 
diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html
index b8b0d98..73f9ec9 100644
--- a/erpnext/templates/generators/item/item_configure.html
+++ b/erpnext/templates/generators/item/item_configure.html
@@ -10,14 +10,11 @@
 		{{ _('Configure') }}
 	</button>
 	{% endif %}
-	{% if cart_settings.show_contact_us_button | int %}
-	<button class="btn btn-link btn-inquiry" data-item-code="{{ doc.name }}">
-		{{ _('Contact Us') }}
-	</button>
+	{% if cart_settings.show_contact_us_button %}
+		{% include "templates/generators/item/item_inquiry.html" %}
 	{% endif %}
 </div>
 <script>
 {% include "templates/generators/item/item_configure.js" %}
-{% include "templates/generators/item/item_inquiry.js" %}
 </script>
 {% endif %}
diff --git a/erpnext/templates/generators/item/item_inquiry.html b/erpnext/templates/generators/item/item_inquiry.html
new file mode 100644
index 0000000..83653b6
--- /dev/null
+++ b/erpnext/templates/generators/item/item_inquiry.html
@@ -0,0 +1,11 @@
+{% if shopping_cart and shopping_cart.cart_settings.enabled %}
+{% set cart_settings = shopping_cart.cart_settings %}
+    {% if cart_settings.show_contact_us_button | int %}
+        <button class="btn btn-inquiry btn-primary-light" data-item-code="{{ doc.name }}">
+            {{ _('Contact Us') }}
+        </button>
+	{% endif %}
+<script>
+{% include "templates/generators/item/item_inquiry.js" %}
+</script>
+{% endif %}
diff --git a/erpnext/templates/generators/item/item_inquiry.js b/erpnext/templates/generators/item/item_inquiry.js
index 52ddae2..e7db3a3 100644
--- a/erpnext/templates/generators/item/item_inquiry.js
+++ b/erpnext/templates/generators/item/item_inquiry.js
@@ -22,6 +22,13 @@
 			},
 			{
 				fieldtype: 'Data',
+				label: __('Phone Number'),
+				fieldname: 'phone',
+				options: 'Phone',
+				reqd: 1
+			},
+			{
+				fieldtype: 'Data',
 				label: __('Subject'),
 				fieldname: 'subject',
 				reqd: 1
diff --git a/erpnext/templates/print_formats/includes/item_table_qty.html b/erpnext/templates/print_formats/includes/item_table_qty.html
index 239859e..ecaaef4 100644
--- a/erpnext/templates/print_formats/includes/item_table_qty.html
+++ b/erpnext/templates/print_formats/includes/item_table_qty.html
@@ -1,6 +1,15 @@
-{% if (doc.uom and not doc.is_print_hide("uom")) %}
-	<small class="pull-left">{{ _(doc.uom) }}</small>
-{% elif (doc.stock_uom and not doc.is_print_hide("stock_uom")) %}
-	<small class="pull-left">{{ _(doc.stock_uom) }}</small>
+{% set qty_first=frappe.db.get_single_value("Print Settings", "print_uom_after_quantity") %}
+{% if qty_first %}
+	{{ doc.get_formatted("qty", doc) }}
+	{% if (doc.uom and not doc.is_print_hide("uom")) %} {{ _(doc.uom) }}
+	{% elif (doc.stock_uom and not doc.is_print_hide("stock_uom")) %} {{ _(doc.stock_uom) }}
+	{%- endif %}
+{% else %}
+	{% if (doc.uom and not doc.is_print_hide("uom")) %}
+		<small class="pull-left">{{ _(doc.uom) }}</small>
+	{% elif (doc.stock_uom and not doc.is_print_hide("stock_uom")) %}
+		<small class="pull-left">{{ _(doc.stock_uom) }}</small>
+	{%- endif %}
+	{{ doc.get_formatted("qty", doc) }}
 {%- endif %}
-{{ doc.get_formatted("qty", doc) }}
+
diff --git a/erpnext/utilities/activation.py b/erpnext/utilities/activation.py
index 63c36b3..7b17c8c 100644
--- a/erpnext/utilities/activation.py
+++ b/erpnext/utilities/activation.py
@@ -11,8 +11,39 @@
 	activation_level = 0
 	sales_data = []
 	min_count = 0
-	doctypes = {"Item": 5, "Customer": 5, "Sales Order": 2, "Sales Invoice": 2, "Purchase Order": 2, "Employee": 3, "Lead": 3, "Quotation": 3,
-					"Payment Entry": 2, "User": 5, "Student": 5, "Instructor": 5, "BOM": 3, "Journal Entry": 3, "Stock Entry": 3}
+	doctypes = {
+		"Asset": 5,
+		"BOM": 3, 
+		"Customer": 5, 
+		"Delivery Note": 5,
+		"Employee": 3, 
+		"Instructor": 5, 
+		"Instructor": 5, 
+		"Issue": 5,
+		"Item": 5, 
+		"Journal Entry": 3, 
+		"Lead": 3,
+		"Leave Application": 5,
+		"Material Request": 5,
+		"Opportunity": 5, 
+		"Payment Entry": 2, 
+		"Project": 5,
+		"Purchase Order": 2, 
+		"Purchase Invoice": 5,
+		"Purchase Receipt": 5,
+		"Quotation": 3,
+		"Salary Slip": 5,
+		"Salary Structure": 5,
+		"Sales Order": 2, 
+		"Sales Invoice": 2, 
+		"Stock Entry": 3,
+		"Student": 5, 
+		"Supplier": 5,
+		"Task": 5,
+		"User": 5, 
+		"Work Order": 5
+	}
+
 	for doctype, min_count in iteritems(doctypes):
 		count = frappe.db.count(doctype)
 		if count > min_count:
diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/desk_page/utilities/utilities.json
new file mode 100644
index 0000000..591eab5
--- /dev/null
+++ b/erpnext/utilities/desk_page/utilities/utilities.json
@@ -0,0 +1,29 @@
+{
+ "cards": [
+  {
+   "hidden": 0,
+   "label": "Video",
+   "links": "[\n    {\n        \"description\": \"Video\",\n        \"label\": \"Video\",\n        \"name\": \"Video\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"description\": \"Video settings\",\n        \"label\": \"Video Settings\",\n        \"name\": \"Video Settings\",\n        \"type\": \"doctype\"\n    }\n]"
+  }
+ ],
+ "category": "Modules",
+ "charts": [],
+ "creation": "2020-09-10 12:21:22.335307",
+ "developer_mode_only": 0,
+ "disable_user_customization": 0,
+ "docstatus": 0,
+ "doctype": "Desk Page",
+ "extends_another_page": 0,
+ "hide_custom": 0,
+ "idx": 0,
+ "is_standard": 1,
+ "label": "Utilities",
+ "modified": "2020-09-10 12:33:30.089853",
+ "modified_by": "user@erpnext.com",
+ "module": "Utilities",
+ "name": "Utilities",
+ "owner": "user@erpnext.com",
+ "pin_to_bottom": 1,
+ "pin_to_top": 0,
+ "shortcuts": []
+}
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/video/video.js b/erpnext/utilities/doctype/video/video.js
index 056bd3c..9cb5a15 100644
--- a/erpnext/utilities/doctype/video/video.js
+++ b/erpnext/utilities/doctype/video/video.js
@@ -2,7 +2,16 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Video', {
-	// refresh: function(frm) {
+	refresh: function (frm) {
+		frm.events.toggle_youtube_statistics_section(frm);
+		frm.add_custom_button("Watch Video", () => frappe.help.show_video(frm.doc.url, frm.doc.title));
+	},
 
-	// }
+	toggle_youtube_statistics_section: (frm) => {
+		if (frm.doc.provider === "YouTube") {
+			frappe.db.get_single_value("Video Settings", "enable_youtube_tracking").then( val => {
+				frm.toggle_display("youtube_tracking_section", val);
+			});
+		}
+	}
 });
diff --git a/erpnext/utilities/doctype/video/video.json b/erpnext/utilities/doctype/video/video.json
index 5d2cc13..2a82db2 100644
--- a/erpnext/utilities/doctype/video/video.json
+++ b/erpnext/utilities/doctype/video/video.json
@@ -11,11 +11,19 @@
   "title",
   "provider",
   "url",
+  "youtube_video_id",
   "column_break_4",
   "publish_date",
   "duration",
+  "youtube_tracking_section",
+  "like_count",
+  "view_count",
+  "col_break",
+  "dislike_count",
+  "comment_count",
   "section_break_7",
-  "description"
+  "description",
+  "image"
  ],
  "fields": [
   {
@@ -37,7 +45,6 @@
   {
    "fieldname": "url",
    "fieldtype": "Data",
-   "in_list_view": 1,
    "label": "URL",
    "reqd": 1
   },
@@ -48,11 +55,12 @@
   {
    "fieldname": "publish_date",
    "fieldtype": "Date",
+   "in_list_view": 1,
    "label": "Publish Date"
   },
   {
    "fieldname": "duration",
-   "fieldtype": "Data",
+   "fieldtype": "Duration",
    "label": "Duration"
   },
   {
@@ -62,13 +70,67 @@
   {
    "fieldname": "description",
    "fieldtype": "Text Editor",
-   "in_list_view": 1,
    "label": "Description",
    "reqd": 1
+  },
+  {
+   "fieldname": "like_count",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Likes",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "view_count",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Views",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "col_break",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "dislike_count",
+   "fieldtype": "Float",
+   "label": "Dislikes",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "comment_count",
+   "fieldtype": "Float",
+   "label": "Comments",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "image",
+   "fieldtype": "Attach Image",
+   "hidden": 1,
+   "label": "Image",
+   "no_copy": 1
+  },
+  {
+   "depends_on": "eval:doc.provider==\"YouTube\"",
+   "fieldname": "youtube_tracking_section",
+   "fieldtype": "Section Break",
+   "label": "Youtube Statistics"
+  },
+  {
+   "fieldname": "youtube_video_id",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Youtube ID",
+   "read_only": 1
   }
  ],
+ "image_field": "image",
  "links": [],
- "modified": "2020-07-21 19:29:46.603734",
+ "modified": "2020-09-07 17:02:20.185794",
  "modified_by": "Administrator",
  "module": "Utilities",
  "name": "Video",
diff --git a/erpnext/utilities/doctype/video/video.py b/erpnext/utilities/doctype/video/video.py
index 3c17b56..c2e414e 100644
--- a/erpnext/utilities/doctype/video/video.py
+++ b/erpnext/utilities/doctype/video/video.py
@@ -3,8 +3,144 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-# import frappe
+import frappe
+import re
+import pytz
 from frappe.model.document import Document
+from frappe import _
+from datetime import datetime
+from six import string_types
+from pyyoutube import Api
 
 class Video(Document):
-	pass
+	def validate(self):
+		if self.provider == "YouTube" and is_tracking_enabled():
+			self.set_video_id()
+			self.set_youtube_statistics()
+
+	def set_video_id(self):
+		if self.url and not self.get("youtube_video_id"):
+			self.youtube_video_id = get_id_from_url(self.url)
+
+	def set_youtube_statistics(self):
+		api_key = frappe.db.get_single_value("Video Settings", "api_key")
+		api = Api(api_key=api_key)
+
+		try:
+			video = api.get_video_by_id(video_id=self.youtube_video_id)
+			video_stats = video.items[0].to_dict().get('statistics')
+
+			self.like_count = video_stats.get('likeCount')
+			self.view_count = video_stats.get('viewCount')
+			self.dislike_count = video_stats.get('dislikeCount')
+			self.comment_count = video_stats.get('commentCount')
+
+		except Exception:
+			title = "Failed to Update YouTube Statistics for Video: {0}".format(self.name)
+			frappe.log_error(title + "\n\n" +  frappe.get_traceback(), title=title)
+
+def is_tracking_enabled():
+	return frappe.db.get_single_value("Video Settings", "enable_youtube_tracking")
+
+
+def get_frequency(value):
+	# Return numeric value from frequency field, return 1 as fallback default value: 1 hour
+	if value != "Daily":
+		return frappe.utils.cint(value[:2].strip())
+	elif value:
+		return 24
+	return 1
+
+
+def update_youtube_data():
+	# Called every 30 minutes via hooks
+	enable_youtube_tracking, frequency = frappe.db.get_value("Video Settings", "Video Settings", ["enable_youtube_tracking", "frequency"])
+
+	if not enable_youtube_tracking:
+		return
+
+	frequency = get_frequency(frequency)
+	time = datetime.now()
+	timezone = pytz.timezone(frappe.utils.get_time_zone())
+	site_time = time.astimezone(timezone)
+
+	if frequency == 30:
+		batch_update_youtube_data()
+	elif site_time.hour % frequency == 0 and site_time.minute < 15:
+		# make sure it runs within the first 15 mins of the hour
+		batch_update_youtube_data()
+
+
+def get_formatted_ids(video_list):
+	# format ids to comma separated string for bulk request
+	ids = []
+	for video in video_list:
+		ids.append(video.youtube_video_id)
+
+	return ','.join(ids)
+
+
+@frappe.whitelist()
+def get_id_from_url(url):
+	"""
+		Returns video id from url
+		:param youtube url: String URL
+	"""
+	if not isinstance(url, string_types):
+		frappe.throw(_("URL can only be a string"), title=_("Invalid URL"))
+
+	pattern = re.compile(r'[a-z\:\//\.]+(youtube|youtu)\.(com|be)/(watch\?v=|embed/|.+\?v=)?([^"&?\s]{11})?')
+	id = pattern.match(url)
+	return id.groups()[-1]
+
+
+@frappe.whitelist()
+def batch_update_youtube_data():
+	def get_youtube_statistics(video_ids):
+		api_key = frappe.db.get_single_value("Video Settings", "api_key")
+		api = Api(api_key=api_key)
+		try:
+			video = api.get_video_by_id(video_id=video_ids)
+			video_stats = video.items
+			return video_stats
+		except Exception:
+			title = "Failed to Update YouTube Statistics"
+			frappe.log_error(title + "\n\n" +  frappe.get_traceback(), title=title)
+
+	def prepare_and_set_data(video_list):
+		video_ids = get_formatted_ids(video_list)
+		stats = get_youtube_statistics(video_ids)
+		set_youtube_data(stats)
+
+	def set_youtube_data(entries):
+		for entry in entries:
+			video_stats = entry.to_dict().get('statistics')
+			video_id = entry.to_dict().get('id')
+			stats = {
+				'like_count' : video_stats.get('likeCount'),
+				'view_count' : video_stats.get('viewCount'),
+				'dislike_count' : video_stats.get('dislikeCount'),
+				'comment_count' : video_stats.get('commentCount'),
+				'video_id': video_id
+			}
+
+			frappe.db.sql("""
+				UPDATE `tabVideo`
+				SET
+					like_count  = %(like_count)s,
+					view_count = %(view_count)s,
+					dislike_count = %(dislike_count)s,
+					comment_count = %(comment_count)s
+				WHERE youtube_video_id = %(video_id)s""", stats)
+
+	video_list = frappe.get_all("Video", fields=["youtube_video_id"])
+	if len(video_list) > 50:
+		# Update in batches of 50
+		start, end = 0, 50
+		while start < len(video_list):
+			batch = video_list[start:end]
+			prepare_and_set_data(batch)
+			start += 50
+			end += 50
+	else:
+		prepare_and_set_data(video_list)
diff --git a/erpnext/utilities/doctype/video/video_list.js b/erpnext/utilities/doctype/video/video_list.js
new file mode 100644
index 0000000..8273a4a
--- /dev/null
+++ b/erpnext/utilities/doctype/video/video_list.js
@@ -0,0 +1,7 @@
+frappe.listview_settings["Video"] = {
+	onload: (listview) => {
+		listview.page.add_menu_item(__("Video Settings"), function() {
+			frappe.set_route("Form","Video Settings", "Video Settings");
+		});
+	}
+}
\ No newline at end of file
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/__init__.py b/erpnext/utilities/doctype/video_settings/__init__.py
similarity index 100%
copy from erpnext/support/report/minutes_to_first_response_for_issues/__init__.py
copy to erpnext/utilities/doctype/video_settings/__init__.py
diff --git a/erpnext/utilities/doctype/video_settings/test_video_settings.py b/erpnext/utilities/doctype/video_settings/test_video_settings.py
new file mode 100644
index 0000000..b217afe
--- /dev/null
+++ b/erpnext/utilities/doctype/video_settings/test_video_settings.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 TestVideoSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/utilities/doctype/video_settings/video_settings.js b/erpnext/utilities/doctype/video_settings/video_settings.js
new file mode 100644
index 0000000..9ac8b9e
--- /dev/null
+++ b/erpnext/utilities/doctype/video_settings/video_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Video Settings', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/utilities/doctype/video_settings/video_settings.json b/erpnext/utilities/doctype/video_settings/video_settings.json
new file mode 100644
index 0000000..fb3274d
--- /dev/null
+++ b/erpnext/utilities/doctype/video_settings/video_settings.json
@@ -0,0 +1,60 @@
+{
+ "actions": [],
+ "creation": "2020-08-02 03:50:21.339609",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "enable_youtube_tracking",
+  "api_key",
+  "frequency"
+ ],
+ "fields": [
+  {
+   "default": "0",
+   "fieldname": "enable_youtube_tracking",
+   "fieldtype": "Check",
+   "label": "Enable YouTube Tracking"
+  },
+  {
+   "depends_on": "eval:doc.enable_youtube_tracking",
+   "fieldname": "api_key",
+   "fieldtype": "Data",
+   "label": "API Key",
+   "mandatory_depends_on": "eval:doc.enable_youtube_tracking"
+  },
+  {
+   "default": "1 hr",
+   "depends_on": "eval:doc.enable_youtube_tracking",
+   "fieldname": "frequency",
+   "fieldtype": "Select",
+   "label": "Frequency",
+   "mandatory_depends_on": "eval:doc.enable_youtube_tracking",
+   "options": "30 mins\n1 hr\n6 hrs\nDaily"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2020-09-07 16:09:00.360668",
+ "modified_by": "Administrator",
+ "module": "Utilities",
+ "name": "Video Settings",
+ "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",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/utilities/doctype/video_settings/video_settings.py b/erpnext/utilities/doctype/video_settings/video_settings.py
new file mode 100644
index 0000000..36fb54f
--- /dev/null
+++ b/erpnext/utilities/doctype/video_settings/video_settings.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from apiclient.discovery import build
+
+class VideoSettings(Document):
+	def validate(self):
+		self.validate_youtube_api_key()
+
+	def validate_youtube_api_key(self):
+		if self.enable_youtube_tracking and self.api_key:
+			try:
+				build("youtube", "v3", developerKey=self.api_key)
+			except Exception:
+				title = _("Failed to Authenticate the API key.")
+				frappe.log_error(title + "\n\n" +  frappe.get_traceback(), title=title)
+				frappe.throw(title + " Please check the error logs.", title=_("Invalid Credentials"))
\ No newline at end of file
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index c23c1f7..66d6cd3 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -82,6 +82,7 @@
 			pricing_rule = get_pricing_rule_for_item(frappe._dict({
 				"item_code": item_code,
 				"qty": qty,
+				"stock_qty": qty,
 				"transaction_type": "selling",
 				"price_list": price_list,
 				"customer_group": customer_group,
diff --git a/erpnext/crm/report/minutes_to_first_response_for_opportunity/__init__.py b/erpnext/utilities/report/__init__.py
similarity index 100%
copy from erpnext/crm/report/minutes_to_first_response_for_opportunity/__init__.py
copy to erpnext/utilities/report/__init__.py
diff --git a/erpnext/support/report/minutes_to_first_response_for_issues/__init__.py b/erpnext/utilities/report/youtube_interactions/__init__.py
similarity index 100%
copy from erpnext/support/report/minutes_to_first_response_for_issues/__init__.py
copy to erpnext/utilities/report/youtube_interactions/__init__.py
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.js b/erpnext/utilities/report/youtube_interactions/youtube_interactions.js
new file mode 100644
index 0000000..6e3e4e6
--- /dev/null
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.js
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["YouTube Interactions"] = {
+	"filters": [
+		{
+			fieldname: "from_date",
+			label: __("From Date"),
+			fieldtype: "Date",
+			default: frappe.datetime.add_months(frappe.datetime.now_date(), -12),
+		},
+		{
+			fieldname:"to_date",
+			label: __("To Date"),
+			fieldtype: "Date",
+			default: frappe.datetime.now_date(),
+		}
+	]
+};
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.json b/erpnext/utilities/report/youtube_interactions/youtube_interactions.json
new file mode 100644
index 0000000..a40247b
--- /dev/null
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.json
@@ -0,0 +1,27 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-08-02 05:05:00.457093",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-08-02 05:05:00.457093",
+ "modified_by": "Administrator",
+ "module": "Utilities",
+ "name": "YouTube Interactions",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Video",
+ "report_name": "YouTube Interactions",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "All"
+  },
+  {
+   "role": "System Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
new file mode 100644
index 0000000..3516a35
--- /dev/null
+++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py
@@ -0,0 +1,113 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import flt
+
+def execute(filters=None):
+	if not frappe.db.get_single_value("Video Settings", "enable_youtube_tracking") or not filters:
+		return [], []
+
+	columns = get_columns()
+	data = get_data(filters)
+	chart_data, summary = get_chart_summary_data(data)
+	return columns, data, None, chart_data, summary
+
+def get_columns():
+	return [
+		{
+			"label": _("Published Date"),
+			"fieldname": "publish_date",
+			"fieldtype": "Date",
+			"width": 100
+		},
+		{
+			"label": _("Title"),
+			"fieldname": "title",
+			"fieldtype": "Data",
+			"width": 200
+		},
+		{
+			"label": _("Duration"),
+			"fieldname": "duration",
+			"fieldtype": "Duration",
+			"width": 100
+		},
+		{
+			"label": _("Views"),
+			"fieldname": "view_count",
+			"fieldtype": "Float",
+			"width": 200
+		},
+		{
+			"label": _("Likes"),
+			"fieldname": "like_count",
+			"fieldtype": "Float",
+			"width": 200
+		},
+		{
+			"label": _("Dislikes"),
+			"fieldname": "dislike_count",
+			"fieldtype": "Float",
+			"width": 100
+		},
+		{
+			"label": _("Comments"),
+			"fieldname": "comment_count",
+			"fieldtype": "Float",
+			"width": 100
+		}
+	]
+
+def get_data(filters):
+	return frappe.db.sql("""
+		SELECT
+			publish_date, title, provider, duration,
+			view_count, like_count, dislike_count, comment_count
+		FROM `tabVideo`
+		WHERE view_count is not null
+			and publish_date between %(from_date)s and %(to_date)s
+		ORDER BY view_count desc""", filters, as_dict=1)
+
+def get_chart_summary_data(data):
+	labels, likes, views = [], [], []
+	total_views = 0
+
+	for row in data:
+		labels.append(row.get('title'))
+		likes.append(row.get('like_count'))
+		views.append(row.get('view_count'))
+		total_views += flt(row.get('view_count'))
+
+
+	chart_data = {
+		"data" : {
+			"labels" : labels,
+			"datasets" : [
+				{
+					"name" : "Likes",
+					"values" : likes
+				},
+				{
+					"name" : "Views",
+					"values" : views
+				}
+			]
+		},
+		"type": "bar",
+		"barOptions": {
+			"stacked": 1
+		},
+	}
+
+	summary = [
+		{
+			"value": total_views,
+			"indicator": "Blue",
+			"label": "Total Views",
+			"datatype": "Float",
+		}
+	]
+	return chart_data, summary
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 912d61f..c4f9171 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,10 +3,11 @@
 gocardless-pro==1.11.0
 googlemaps==3.1.1
 pandas==1.0.5
-plaid-python==3.4.0
+plaid-python==6.0.0
 pycountry==19.8.18
 PyGithub==1.44.1
 python-stdnum==1.12
+python-youtube==0.6.0
 taxjar==1.9.0
 tweepy==3.8.0
 Unidecode==1.1.1
diff --git a/yarn.lock b/yarn.lock
index b19f566..97a0635 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -282,9 +282,9 @@
   integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
 
 bl@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
-  integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f"
+  integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==
   dependencies:
     readable-stream "^3.0.1"
 
@@ -866,12 +866,12 @@
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@^2.0.3, inherits@~2.0.3:
+inherits@2, inherits@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
   integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
 
-inherits@2.0.4, inherits@~2.0.1:
+inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -1447,9 +1447,9 @@
     util-deprecate "~1.0.1"
 
 readable-stream@^3.0.1, readable-stream@^3.1.1:
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606"
-  integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
   dependencies:
     inherits "^2.0.3"
     string_decoder "^1.1.1"
@@ -1505,9 +1505,9 @@
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
 safe-buffer@~5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
-  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
 "safer-buffer@>= 2.1.2 < 3":
   version "2.1.2"