Merge branch 'develop' into refactor-lab-module
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index a249783..2c52314 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -43,7 +43,7 @@
   {
    "hidden": 0,
    "label": "Bank Statement",
-   "links": "[\n    {\n        \"label\": \"Bank\",\n        \"name\": \"Bank\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Account\",\n        \"name\": \"Bank Account\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Statement Transaction Entry\",\n        \"name\": \"Bank Statement Transaction Entry\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Statement Settings\",\n        \"name\": \"Bank Statement Settings\",\n        \"type\": \"doctype\"\n    }\n]"
+   "links": "[\n    {\n        \"label\": \"Bank\",\n        \"name\": \"Bank\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Account\",\n        \"name\": \"Bank Account\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Reconciliation\",\n        \"name\": \"bank-reconciliation\",\n        \"type\": \"page\"\n    },\n    {\n        \"label\": \"Bank Clearance\",\n        \"name\": \"Bank Clearance\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Statement Transaction Entry\",\n        \"name\": \"Bank Statement Transaction Entry\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Bank Statement Settings\",\n        \"name\": \"Bank Statement Settings\",\n        \"type\": \"doctype\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -98,7 +98,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Accounting",
- "modified": "2020-06-19 12:42:44.054598",
+ "modified": "2020-09-03 10:37:07.865801",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
@@ -158,4 +158,4 @@
    "type": "Dashboard"
   }
  ]
-}
+}
\ No newline at end of file
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/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/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 842c64f..bb312bf 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -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/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..2bfa4a5 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.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..b4ee7c9 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):
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..01d3903 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
@@ -342,7 +343,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/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/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/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 0bd03a8..9d08d92 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -146,7 +146,7 @@
 			'assets': assets,
 			'purpose': 'Receipt',
 			'company': self.company,
-			'transaction_date': getdate(nowdate()),
+			'transaction_date': getdate(self.purchase_date),
 			'reference_doctype': reference_doctype,
 			'reference_name': reference_docname
 		}).insert()
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/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/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/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/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/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/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/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.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/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/member/member.json b/erpnext/non_profit/doctype/member/member.json
index bb73a84..77cdb94 100644
--- a/erpnext/non_profit/doctype/member/member.json
+++ b/erpnext/non_profit/doctype/member/member.json
@@ -133,7 +133,8 @@
   {
    "fieldname": "email_id",
    "fieldtype": "Data",
-   "label": "Email Address"
+   "label": "Email Address",
+   "options": "Email"
   },
   {
    "fieldname": "subscription_id",
@@ -176,7 +177,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-04-07 14:20:33.215700",
+ "modified": "2020-08-06 10:06:01.153564",
  "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..95bb3a5 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": [
   {
@@ -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-07-31 13:57:02.328995",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Membership",
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/patches.txt b/erpnext/patches.txt
index a8648f5..6c58f2f 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
@@ -697,7 +697,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
@@ -722,4 +722,6 @@
 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
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/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/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/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/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/transaction.js b/erpnext/public/js/controllers/transaction.js
index 436a232..792235f 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -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/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/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/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/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/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/install.py b/erpnext/setup/install.py
index 4f0f572..2225fe1 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -20,6 +20,7 @@
 	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()
@@ -66,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'),
diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py
index 5545f13..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[0], date_range[1]]])
+			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 1bf81f7..2fba5fa 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,
@@ -58,7 +58,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Stock",
- "modified": "2020-05-30 17:32:11.062681",
+ "modified": "2020-08-11 17:29:32.626067",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
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/stock_qty_vs_serial_no_count/__init__.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/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/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/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/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/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"