Merge branch 'develop' into purchase-invoice-to-purchase-receipt-develop
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 199a183..d2e5870 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
 from erpnext.hooks import regional_overrides
 from frappe.utils import getdate
 
-__version__ = '13.0.0-dev'
+__version__ = '13.0.1'
 
 def get_default_company(user=None):
 	'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js
index 49b2b18..059e1d3 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -42,10 +42,9 @@
 		});
 	});
 
-	frappe.meta.get_docfield("Bank Transaction Mapping", "bank_transaction_field",
-		frm.doc.name).options = options;
-
-	frm.fields_dict.bank_transaction_mapping.grid.refresh();
+	frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
+		'bank_transaction_field', 'options', options
+	);
 };
 
 erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 37b03f3..d76641d 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -327,18 +327,16 @@
 	},
 
 	setup_balance_formatter: function() {
-		var me = this;
-		$.each(["balance", "party_balance"], function(i, field) {
-			var df = frappe.meta.get_docfield("Journal Entry Account", field, me.frm.doc.name);
-			df.formatter = function(value, df, options, doc) {
-				var currency = frappe.meta.get_field_currency(df, doc);
-				var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
-				return "<div style='text-align: right'>"
-					+ ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
-					+ " " + dr_or_cr
-					+ "</div>";
-			}
-		})
+		const formatter = function(value, df, options, doc) {
+			var currency = frappe.meta.get_field_currency(df, doc);
+			var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
+			return "<div style='text-align: right'>"
+				+ ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
+				+ " " + dr_or_cr
+				+ "</div>";
+		};
+		this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
+		this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
 	},
 
 	reference_name: function(doc, cdt, cdn) {
@@ -431,15 +429,6 @@
 	cur_frm.cscript.update_totals(doc);
 }
 
-cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
-	if(doc.select_print_heading){
-		// print heading
-		cur_frm.pformat.print_heading = doc.select_print_heading;
-	}
-	else
-		cur_frm.pformat.print_heading = __("Journal Entry");
-}
-
 frappe.ui.form.on("Journal Entry Account", {
 	party: function(frm, cdt, cdn) {
 		var d = frappe.get_doc(cdt, cdn);
@@ -511,8 +500,11 @@
 		};
 
 		$.each(field_label_map, function (fieldname, label) {
-			var df = frappe.meta.get_docfield("Journal Entry Account", fieldname, frm.doc.name);
-			df.label = frm.doc.multi_currency ? (label + " in Account Currency") : label;
+			frm.fields_dict.accounts.grid.update_docfield_property(
+				fieldname,
+				'label',
+				frm.doc.multi_currency ? (label + " in Account Currency") : label
+			);
 		})
 	},
 
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 774159d..a89fefd 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -280,7 +280,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-06-24 14:06:54.833738",
+ "modified": "2020-06-26 14:06:54.833738",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 6b07197..08103184 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -234,8 +234,9 @@
 		});
 
 		if (invoices) {
-			frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
-				me.frm.doc.name).options = "\n" + invoices.join("\n");
+			this.frm.fields_dict.payment.grid.update_docfield_property(
+				'invoice_number', 'options', "\n" + invoices.join("\n")
+			);
 
 			$.each(me.frm.doc.payments || [], function(i, p) {
 				if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 832fb80..e614459 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -108,7 +108,6 @@
 				filters = { "item_code": d.item_code, "warehouse": d.warehouse }
 				if d.batch_no:
 					filters["batch_no"] = d.batch_no
-
 				reserved_serial_nos = get_pos_reserved_serial_nos(filters)
 				serial_nos = get_serial_nos(d.serial_no)
 				invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos]
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 40f77b4..6d2cffc 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -12,6 +12,7 @@
 from frappe.model.mapper import map_doc, map_child_doc
 from frappe.utils.scheduler import is_scheduler_inactive
 from frappe.core.page.background_jobs.background_jobs import get_info
+import json
 
 from six import iteritems
 
@@ -78,8 +79,11 @@
 		sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
 
 		sales_invoice.is_consolidated = 1
+		sales_invoice.set_posting_time = 1
+		sales_invoice.posting_date = getdate(self.posting_date)
 		sales_invoice.save()
 		sales_invoice.submit()
+
 		self.consolidated_invoice = sales_invoice.name
 
 		return sales_invoice.name
@@ -91,10 +95,13 @@
 		credit_note = self.merge_pos_invoice_into(credit_note, data)
 
 		credit_note.is_consolidated = 1
+		credit_note.set_posting_time = 1
+		credit_note.posting_date = getdate(self.posting_date)
 		# TODO: return could be against multiple sales invoice which could also have been consolidated?
 		# credit_note.return_against = self.consolidated_invoice
 		credit_note.save()
 		credit_note.submit()
+
 		self.consolidated_credit_note = credit_note.name
 
 		return credit_note.name
@@ -131,12 +138,14 @@
 					if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
 						t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount)
 						t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount)
+						update_item_wise_tax_detail(t, tax)
 						found = True
 				if not found:
 					tax.charge_type = 'Actual'
 					tax.included_in_print_rate = 0
 					tax.tax_amount = tax.tax_amount_after_discount_amount
 					tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
+					tax.item_wise_tax_detail = tax.item_wise_tax_detail
 					taxes.append(tax)
 
 			for payment in doc.get('payments'):
@@ -168,11 +177,9 @@
 		sales_invoice = frappe.new_doc('Sales Invoice')
 		sales_invoice.customer = self.customer
 		sales_invoice.is_pos = 1
-		# date can be pos closing date?
-		sales_invoice.posting_date = getdate(nowdate())
 
 		return sales_invoice
-	
+
 	def update_pos_invoices(self, invoice_docs, sales_invoice='', credit_note=''):
 		for doc in invoice_docs:
 			doc.load_from_db()
@@ -187,6 +194,26 @@
 			si.flags.ignore_validate = True
 			si.cancel()
 
+def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
+	consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail)
+	tax_row_detail = json.loads(tax_row.item_wise_tax_detail)
+
+	if not consolidated_tax_detail:
+		consolidated_tax_detail = {}
+
+	for item_code, tax_data in tax_row_detail.items():
+		if consolidated_tax_detail.get(item_code):
+			consolidated_tax_data = consolidated_tax_detail.get(item_code)
+			consolidated_tax_detail.update({
+				item_code: [consolidated_tax_data[0], consolidated_tax_data[1] + tax_data[1]]
+			})
+		else:
+			consolidated_tax_detail.update({
+				item_code: [tax_data[0], tax_data[1]]
+			})
+
+	consolidate_tax_row.item_wise_tax_detail = json.dumps(consolidated_tax_detail, separators=(',', ':'))
+
 def get_all_unconsolidated_invoices():
 	filters = {
 		'consolidated_invoice': [ 'in', [ '', None ]],
@@ -214,7 +241,7 @@
 
 	if len(invoices) >= 5 and closing_entry:
 		closing_entry.set_status(update=True, status='Queued')
-		enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
+		enqueue_job(create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry)
 	else:
 		create_merge_logs(invoice_by_customer, closing_entry)
 
@@ -227,21 +254,21 @@
 
 	if len(merge_logs) >= 5:
 		closing_entry.set_status(update=True, status='Queued')
-		enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
+		enqueue_job(cancel_merge_logs, merge_logs=merge_logs, closing_entry=closing_entry)
 	else:
 		cancel_merge_logs(merge_logs, closing_entry)
 
 def create_merge_logs(invoice_by_customer, closing_entry={}):
 	for customer, invoices in iteritems(invoice_by_customer):
 		merge_log = frappe.new_doc('POS Invoice Merge Log')
-		merge_log.posting_date = getdate(nowdate())
+		merge_log.posting_date = getdate(closing_entry.get('posting_date'))
 		merge_log.customer = customer
 		merge_log.pos_closing_entry = closing_entry.get('name', None)
 
 		merge_log.set('pos_invoices', invoices)
 		merge_log.save(ignore_permissions=True)
 		merge_log.submit()
-	
+
 	if closing_entry:
 		closing_entry.set_status(update=True, status='Submitted')
 		closing_entry.update_opening_entry()
@@ -256,7 +283,7 @@
 		closing_entry.set_status(update=True, status='Cancelled')
 		closing_entry.update_opening_entry(for_cancel=True)
 
-def enqueue_job(job, invoice_by_customer, closing_entry):
+def enqueue_job(job, merge_logs=None, invoice_by_customer=None, closing_entry=None):
 	check_scheduler_status()
 
 	job_name = closing_entry.get("name")
@@ -269,6 +296,7 @@
 			job_name=job_name,
 			closing_entry=closing_entry,
 			invoice_by_customer=invoice_by_customer,
+			merge_logs=merge_logs,
 			now=frappe.conf.developer_mode or frappe.flags.in_test
 		)
 
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
index d880caa..040a815 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
@@ -5,6 +5,7 @@
 
 import frappe
 import unittest
+import json
 from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
 from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
@@ -99,4 +100,51 @@
 			frappe.db.sql("delete from `tabPOS Profile`")
 			frappe.db.sql("delete from `tabPOS Invoice`")
 
+	def test_consolidated_invoice_item_taxes(self):
+		frappe.db.sql("delete from `tabPOS Invoice`")
+
+		try:
+			inv = create_pos_invoice(qty=1, rate=100, do_not_save=True)
+
+			inv.append("taxes", {
+				"account_head": "_Test Account VAT - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "VAT",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 9
+			})
+			inv.insert()
+			inv.submit()
+
+			inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True)
+			inv2.get('items')[0].item_code = '_Test Item 2'
+			inv2.append("taxes", {
+				"account_head": "_Test Account VAT - _TC",
+				"charge_type": "On Net Total",
+				"cost_center": "_Test Cost Center - _TC",
+				"description": "VAT",
+				"doctype": "Sales Taxes and Charges",
+				"rate": 5
+			})
+			inv2.insert()
+			inv2.submit()
+
+			consolidate_pos_invoices()
+			inv.load_from_db()
+
+			consolidated_invoice = frappe.get_doc('Sales Invoice', inv.consolidated_invoice)
+			item_wise_tax_detail = json.loads(consolidated_invoice.get('taxes')[0].item_wise_tax_detail)
+
+			tax_rate, amount = item_wise_tax_detail.get('_Test Item')
+			self.assertEqual(tax_rate, 9)
+			self.assertEqual(amount, 9)
+
+			tax_rate2, amount2 = item_wise_tax_detail.get('_Test Item 2')
+			self.assertEqual(tax_rate2, 5)
+			self.assertEqual(amount2, 5)
+		finally:
+			frappe.set_user("Administrator")
+			frappe.db.sql("delete from `tabPOS Profile`")
+			frappe.db.sql("delete from `tabPOS Invoice`")
 
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index ee76bba..cf7ed26 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -62,14 +62,15 @@
 
 		if len(default_mode) > 1:
 			frappe.throw(_("You can only select one mode of payment as default"))
-		
+
 		invalid_modes = []
 		for d in self.payments:
 			account = frappe.db.get_value(
-				"Mode of Payment Account", 
+				"Mode of Payment Account",
 				{"parent": d.mode_of_payment, "company": self.company},
 				"default_account"
 			)
+
 			if not account:
 				invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
 
diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
index 62dc1fc..0033965 100644
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
@@ -92,11 +92,21 @@
 		"write_off_cost_center":  args.write_off_cost_center or "_Test Write Off Cost Center - _TC"
 	})
 
-	payments = [{
+	mode_of_payment = frappe.get_doc("Mode of Payment", "Cash")
+	company = args.company or "_Test Company"
+	default_account = args.income_account or "Sales - _TC"
+
+	if not frappe.db.get_value("Mode of Payment Account", {"company": company, "parent": "Cash"}):
+		mode_of_payment.append("accounts", {
+			"company": company,
+			"default_account": default_account
+		})
+		mode_of_payment.save()
+
+	pos_profile.append("payments", {
 		'mode_of_payment': 'Cash',
 		'default': 1
-	}]
-	pos_profile.set("payments", payments)
+	})
 
 	if not frappe.db.exists("POS Profile", args.name or "_Test POS Profile"):
 		pos_profile.insert()
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js
index 8890d59..3625393 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.js
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js
@@ -16,8 +16,11 @@
 				}
 			});
 
-			frappe.meta.get_docfield("POS Field", "fieldname", frm.doc.name).options = [""].concat(fields);
+			frm.fields_dict.invoice_fields.grid.update_docfield_property(
+				'fieldname', 'options', [""].concat(fields)
+			);
 		});
+
 	}
 });
 
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index f28cee7..ef9aad5 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -328,6 +328,21 @@
 		self.assertEquals(item.discount_amount, 110)
 		self.assertEquals(item.rate, 990)
 
+	def test_pricing_rule_with_margin_and_discount_amount(self):
+		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
+		make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10,
+			rate_or_discount="Discount Amount", discount_amount=110)
+		si = create_sales_invoice(do_not_save=True)
+		si.items[0].price_list_rate = 1000
+		si.payment_schedule = []
+		si.insert(ignore_permissions=True)
+
+		item = si.items[0]
+		self.assertEquals(item.margin_rate_or_amount, 10)
+		self.assertEquals(item.rate_with_margin, 1100)
+		self.assertEquals(item.discount_amount, 110)
+		self.assertEquals(item.rate, 990)
+
 	def test_pricing_rule_for_product_discount_on_same_item(self):
 		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
 		test_record = {
@@ -560,6 +575,7 @@
 		"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
 		"condition": args.condition or '',
 		"priority": 1,
+		"discount_amount": args.discount_amount or 0.0,
 		"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
 	})
 
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index c676abd..b91a7a5 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -471,7 +471,7 @@
 
 					if not d.get(pr_field): continue
 
-					if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
+					if d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field):
 						frappe.msgprint(_("User has not applied rule on the invoice {0}")
 							.format(doc.name))
 					else:
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
index 7425132..6dc4643 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js
@@ -92,7 +92,7 @@
 							frm.refresh_field('customers');
 						}
 						else{
-							frappe.msgprint('No Customers found with selected options.');
+							frappe.throw('No Customers found with selected options.');
 						}
 					}
 				}
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index d50e4a8..43fbb06 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -126,9 +126,11 @@
 	sales_person_records = frappe._dict()
 	for d in records:
 		sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
-	customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
+	if sales_person_records.get('Customer'):
+		return frappe.get_list('Customer', fields=['name', 'email_id'], \
 			filters=[['name', 'in', list(sales_person_records['Customer'])]])
-	return customers
+	else:
+		return []
 
 def get_recipients_and_cc(customer, doc):
 	recipients = []
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 73475a1..f58c8f4 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -496,15 +496,6 @@
 	}
 }
 
-cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
-	if(doc.select_print_heading){
-		// print heading
-		cur_frm.pformat.print_heading = doc.select_print_heading;
-	}
-	else
-		cur_frm.pformat.print_heading = __("Purchase Invoice");
-}
-
 frappe.ui.form.on("Purchase Invoice", {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 5c37eda..24e67fe 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1380,7 +1380,7 @@
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-30 21:45:58.334107",
+ "modified": "2021-03-30 22:45:58.334107",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
index 3e1c522..ada665a 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
@@ -1,14 +1,14 @@
 var globalOnload = frappe.listview_settings['Sales Invoice'].onload;
-frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
+frappe.listview_settings['Sales Invoice'].onload = function (list_view) {
 
 	// Provision in case onload event is added to sales_invoice.js in future
 	if (globalOnload) {
-		globalOnload(doclist);
+		globalOnload(list_view);
 	}
 
 	const action = () => {
-		const selected_docs = doclist.get_checked_items();
-		const docnames = doclist.get_checked_items(true);
+		const selected_docs = list_view.get_checked_items();
+		const docnames = list_view.get_checked_items(true);
 
 		for (let doc of selected_docs) {
 			if (doc.docstatus !== 1) {
@@ -19,7 +19,7 @@
 		frappe.call({
 			method: 'erpnext.regional.india.utils.generate_ewb_json',
 			args: {
-				'dt': doclist.doctype,
+				'dt': list_view.doctype,
 				'dn': docnames
 			},
 			callback: function(r) {
@@ -35,5 +35,140 @@
 		});
 	};
 
-	doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
+	list_view.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
+
+	const generate_irns = () => {
+		const docnames = list_view.get_checked_items(true);
+		if (docnames && docnames.length) {
+			frappe.call({
+				method: 'erpnext.regional.india.e_invoice.utils.generate_einvoices',
+				args: { docnames },
+				freeze: true,
+				freeze_message: __('Generating E-Invoices...')
+			});
+		} else {
+			frappe.msgprint({
+				message: __('Please select at least one sales invoice to generate IRN'),
+				title: __('No Invoice Selected'),
+				indicator: 'red'
+			});
+		}
+	};
+
+	const cancel_irns = () => {
+		const docnames = list_view.get_checked_items(true);
+
+		const fields = [
+			{
+				"label": "Reason",
+				"fieldname": "reason",
+				"fieldtype": "Select",
+				"reqd": 1,
+				"default": "1-Duplicate",
+				"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
+			},
+			{ 
+				"label": "Remark",
+				"fieldname": "remark",
+				"fieldtype": "Data",
+				"reqd": 1
+			}
+		];
+
+		const d = new frappe.ui.Dialog({
+			title: __("Cancel IRN"),
+			fields: fields,
+			primary_action: function() {
+				const data = d.get_values();
+				frappe.call({
+					method: 'erpnext.regional.india.e_invoice.utils.cancel_irns',
+					args: { 
+						doctype: list_view.doctype,
+						docnames,
+						reason: data.reason.split('-')[0],
+						remark: data.remark
+					},
+					freeze: true,
+					freeze_message: __('Cancelling E-Invoices...'),
+				});
+				d.hide();
+			},
+			primary_action_label: __('Submit')
+		});
+		d.show();
+	};
+
+	let einvoicing_enabled = false;
+	frappe.db.get_single_value("E Invoice Settings", "enable").then(enabled => {
+		einvoicing_enabled = enabled;
+	});
+
+	list_view.$result.on("change", "input[type=checkbox]", () => {
+		if (einvoicing_enabled) {
+			const docnames = list_view.get_checked_items(true);
+			// show/hide e-invoicing actions when no sales invoices are checked
+			if (docnames && docnames.length) {
+				// prevent adding actions twice if e-invoicing action group already exists
+				if (list_view.page.get_inner_group_button(__('E-Invoicing')).length == 0) {
+					list_view.page.add_inner_button(__('Generate IRNs'), generate_irns, __('E-Invoicing'));
+					list_view.page.add_inner_button(__('Cancel IRNs'), cancel_irns, __('E-Invoicing'));
+				}
+			} else {
+				list_view.page.remove_inner_button(__('Generate IRNs'), __('E-Invoicing'));
+				list_view.page.remove_inner_button(__('Cancel IRNs'), __('E-Invoicing'));
+			}
+		}
+	});
+
+	frappe.realtime.on("bulk_einvoice_generation_complete", (data) => {
+		const { failures, user, invoices } = data;
+		
+		if (invoices.length != failures.length) {
+			frappe.msgprint({
+				message: __('{0} e-invoices generated successfully', [invoices.length]),
+				title: __('Bulk E-Invoice Generation Complete'),
+				indicator: 'orange'
+			});
+		}
+
+		if (failures && failures.length && user == frappe.session.user) {
+			let message = `
+				Failed to generate IRNs for following ${failures.length} sales invoices:
+				<ul style="padding-left: 20px; padding-top: 5px;">
+					${failures.map(d => `<li>${d.docname}</li>`).join('')}
+				</ul>
+			`;
+			frappe.msgprint({
+				message: message,
+				title: __('Bulk E-Invoice Generation Complete'),
+				indicator: 'orange'
+			});
+		}
+	});
+
+	frappe.realtime.on("bulk_einvoice_cancellation_complete", (data) => {
+		const { failures, user, invoices } = data;
+
+		if (invoices.length != failures.length) {
+			frappe.msgprint({
+				message: __('{0} e-invoices cancelled successfully', [invoices.length]),
+				title: __('Bulk E-Invoice Cancellation Complete'),
+				indicator: 'orange'
+			});
+		}
+
+		if (failures && failures.length && user == frappe.session.user) {
+			let message = `
+				Failed to cancel IRNs for following ${failures.length} sales invoices:
+				<ul style="padding-left: 20px; padding-top: 5px;">
+					${failures.map(d => `<li>${d.docname}</li>`).join('')}
+				</ul>
+			`;
+			frappe.msgprint({
+				message: message,
+				title: __('Bulk E-Invoice Cancellation Complete'),
+				indicator: 'orange'
+			});
+		}
+	});
 };
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index b361c0c..8a42d9e 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-// print heading
-cur_frm.pformat.print_heading = 'Invoice';
-
 {% include 'erpnext/selling/sales_common.js' %};
 frappe.provide("erpnext.accounts");
 
@@ -916,7 +913,7 @@
 				},
 				callback: function(r, rt) {
 					if(r.message){
-						data = r.message;
+						let data = r.message;
 						frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
 						frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
 						frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 21d550a..3c91dcc 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -24,6 +24,7 @@
 from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 from frappe.model.utils import get_fetch_values
 from frappe.contacts.doctype.address.address import get_address_display
+from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
 
 from erpnext.healthcare.utils import manage_invoice_submit_cancel
 
@@ -214,6 +215,9 @@
 		if self.update_stock == 1:
 			self.repost_future_sle_and_gle()
 
+		if self.update_stock == 1:
+			self.repost_future_sle_and_gle()
+
 		if not self.is_return:
 			self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
 			self.update_billing_status_for_zero_amount_refdoc("Sales Order")
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index f09cc5a..4a6f9d1 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1166,10 +1166,12 @@
 
 	def test_create_so_with_margin(self):
 		si = create_sales_invoice(item_code="_Test Item", qty=1, do_not_submit=True)
-		price_list_rate = 100
+		price_list_rate = flt(100) * flt(si.plc_conversion_rate)
 		si.items[0].price_list_rate = price_list_rate
 		si.items[0].margin_type = 'Percentage'
 		si.items[0].margin_rate_or_amount = 25
+		si.items[0].discount_amount = 0.0
+		si.items[0].discount_percentage = 0.0
 		si.save()
 		self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
 
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index fadb665..9ffa481 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -444,6 +444,16 @@
    "type": "Link"
   },
   {
+    "dependencies": "GL Entry",
+    "hidden": 0,
+    "is_query_report": 1,
+    "label": "UAE VAT 201",
+    "link_to": "UAE VAT 201",
+    "link_type": "Report",
+    "onboard": 0,
+    "type": "Link"
+   },
+  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Financial Statements",
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index 248cb9a..630a1dc 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -13,6 +13,8 @@
   "po_required",
   "pr_required",
   "maintain_same_rate",
+  "maintain_same_rate_action",
+  "role_to_override_stop_action",
   "allow_multiple_items",
   "subcontract",
   "backflush_raw_materials_of_subcontract_based_on",
@@ -89,6 +91,23 @@
   {
    "fieldname": "column_break_11",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "Stop",
+   "depends_on": "maintain_same_rate",
+   "description": "Configure the action to stop the transaction or just warn if the same rate is not maintained.",
+   "fieldname": "maintain_same_rate_action",
+   "fieldtype": "Select",
+   "label": "Action If Same Rate is Not Maintained",
+   "mandatory_depends_on": "maintain_same_rate",
+   "options": "Stop\nWarn"
+  },
+  {
+   "depends_on": "eval:doc.maintain_same_rate_action == 'Stop'",
+   "fieldname": "role_to_override_stop_action",
+   "fieldtype": "Link",
+   "label": "Role Allowed to Override Stop Action",
+   "options": "Role"
   }
  ],
  "icon": "fa fa-cog",
@@ -96,7 +115,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-03-02 17:34:04.190677",
+ "modified": "2021-04-04 20:01:44.087066",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying Settings",
diff --git a/erpnext/change_log/v13/v13_0_0.md b/erpnext/change_log/v13/v13_0_0.md
new file mode 100644
index 0000000..a6cebab
--- /dev/null
+++ b/erpnext/change_log/v13/v13_0_0.md
@@ -0,0 +1,471 @@
+# Version 13.0.0 Release Notes
+
+### Accounting
+- [New and refreshed POS](https://github.com/frappe/erpnext/pull/20789)
+- [GST E-invoicing for India](https://docs.erpnext.com/docs/user/manual/en/regional/india/setup-e-invoicing)
+- [Distributed Cost Center](https://docs.erpnext.com/docs/user/manual/en/accounts/distributed-cost-center)
+- [Process Bulk Statement Of Accounts](https://docs.erpnext.com/docs/user/manual/en/accounts/process-statement-of-accounts)
+- [More controlled deferred revenue booking](https://docs.erpnext.com/docs/user/manual/en/accounts/process-deferred-accounting)
+- [Dunning](https://docs.erpnext.com/docs/user/manual/en/accounts/dunning)
+- [Journal Entry Template](https://docs.erpnext.com/docs/user/manual/en/accounts/journal-entry-template)
+- [POS Register report](https://github.com/frappe/erpnext/pull/23313)
+- [UAE VAT 201 Report](https://github.com/frappe/erpnext/pull/23447)
+
+
+### Loan Management
+- [Loan Application](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-application)
+- [Loan](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan)
+- [Loan Security Pledge](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-security-pledge)
+- [Loan Disbursement](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-disbursement)
+- [Loan Repayment](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-repayment)
+- [Loan Interest Accrual](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-interest-accrual)
+- [Loan Write Off](https://docs.erpnext.com/docs/user/manual/en/loan-management/loan-write-off)
+
+### Healthcare
+- [Refactored Healthcare Module](https://docs.erpnext.com/docs/user/manual/en/healthcare)
+- [Rehabilitation Module](https://docs.erpnext.com/docs/user/manual/en/healthcare/exercise_type)
+- [Laboratory Module](https://docs.erpnext.com/docs/user/manual/en/healthcare/setup_laboratory)
+- [Patient Progress Page](https://github.com/frappe/erpnext/pull/22474)
+- [Inpatient Medication Order and Entry](https://docs.erpnext.com/docs/user/manual/en/healthcare/inpatient_medication_entry)
+- [Therapy Plan Template](https://docs.erpnext.com/docs/user/manual/en/healthcare/therapy_plan)
+- [Multi company support in Healthcare](https://github.com/frappe/erpnext/pull/21290)
+- [Inpatient Medication Orders Script Report](https://github.com/frappe/erpnext/pull/23984)
+- [Patient History Enhancements](https://github.com/frappe/erpnext/pull/24033)
+
+
+### Stock
+- [Putaway](https://docs.erpnext.com/docs/user/manual/en/stock/putaway-rule)
+- [More accurate stock valuation in case of back-dated stock transactions](https://github.com/frappe/erpnext/pull/24183)
+- [Repost item costing via background job](https://github.com/frappe/erpnext/pull/24183)
+- [Item valuation for internal stock transfers](https://github.com/frappe/erpnext/pull/24200)
+- [Multi currency in Landed Cost Voucher](https://github.com/frappe/erpnext/pull/24127)
+- [Formula based Quality Inspection](https://docs.erpnext.com/docs/user/manual/en/stock/quality-inspection)
+- [Value Based and Numeric Quality Inspection](https://github.com/frappe/erpnext/pull/24181)
+- [Shipment](https://github.com/frappe/erpnext/pull/22914)
+- [Return tracking in PR/DN](https://github.com/frappe/erpnext/pull/22859)
+
+### Manufacturing
+- [Production forecasting using Exponential Smoothing method](https://docs.erpnext.com/docs/user/manual/en/manufacturing/reports/demand-driven-forecasting)
+- [BOM Template](https://docs.erpnext.com/docs/user/manual/en/manufacturing/bill-of-materials#34-bom-template)
+- [Downtime Entry](https://docs.erpnext.com/docs/user/manual/en/manufacturing/downtime-entry)
+- [Quality Inspection on Job Card](https://github.com/frappe/erpnext/pull/23964)
+- New Reports
+  - Production Planning Report ([#21763](https://github.com/frappe/erpnext/pull/21763))
+  - BOM Operations Time ([#21763](https://github.com/frappe/erpnext/pull/21763))
+  - Work Order Summary ([#21430](https://github.com/frappe/erpnext/pull/21430))
+  - Job card Summary ([#21430](https://github.com/frappe/erpnext/pull/21430))
+  - Downtime Analysis ([#21430](https://github.com/frappe/erpnext/pull/21430))
+  - Quality Inspection ([#21430](https://github.com/frappe/erpnext/pull/21430))
+
+### HR
+- [Leave policy assignment](https://github.com/frappe/erpnext/pull/23112)
+- [In and Out time in attendance](https://github.com/frappe/erpnext/pull/21547)
+- [Shift management](https://docs.erpnext.com/docs/user/manual/en/human-resources/shift-management)
+- [Recruitment analytics](https://github.com/frappe/erpnext/pull/21732)
+- [Bulk Mark Attendance](https://github.com/frappe/erpnext/pull/20062)
+- [Leave type with partial payment](https://github.com/frappe/erpnext/pull/23173)
+- New and enhanced reports
+    - Employee Analytics ([#21705](https://github.com/frappe/erpnext/pull/21705))
+    - Employee Leave Balance ([#20754](https://github.com/frappe/erpnext/pull/20754))
+    - Employee Leave Balance Summary ([#20754](https://github.com/frappe/erpnext/pull/20754))
+
+### Payroll
+- [Multi-currency payroll](https://github.com/frappe/erpnext/pull/23519)
+- [Payroll based on attendance](https://github.com/frappe/erpnext/pull/21258)
+- [Payroll based on employee cost center](https://github.com/frappe/erpnext/pull/21609)
+- [Recurring Additional Salary](https://github.com/frappe/erpnext/pull/20936)
+- [Compute Year to Date for Salary Slip components](https://github.com/frappe/erpnext/pull/24362)
+- New Reports
+  - Income Tax Deductions
+  - Professional Tax Deductions
+  - Provident Fund Deductions
+  - Total Salary Payments Based on Payment Mode
+  - Salary Payments via ECS
+
+### CRM
+- [Social Media Post](https://docs.erpnext.com/docs/user/manual/en/CRM/social-media-post)
+- [Make Quotation against Blanket Order](https://docs.erpnext.com/docs/user/manual/en/selling/blanket-order)
+- [Calendar View for Opportunity](https://github.com/frappe/erpnext/pull/21280)
+
+### Selling
+- [Batch wise item pricing](https://github.com/frappe/erpnext/pull/24470)
+- [Refreshed shopping cart](https://github.com/frappe/erpnext/pull/22617)
+- [Territory-wise Sales Report](https://github.com/frappe/erpnext/pull/20428)
+
+#### Buying
+- [Multi UOM support in Request for Quotation](https://github.com/frappe/erpnext/pull/22249)
+- [Provision to make RFQ against Opportunity](https://github.com/frappe/erpnext/pull/22765)
+- [Item Rate in Stock UOM in purchase cycle](https://github.com/frappe/erpnext/pull/24315)
+- New Reports
+  - Requested Items To Order ([#21611](https://github.com/frappe/erpnext/pull/21611))
+  - Purchase Order Analysis ([#21611](https://github.com/frappe/erpnext/pull/21611))
+  - Supplier Quotation Comparison report ([#23323](https://github.com/frappe/erpnext/pull/23323))
+
+### Project
+- [Project template with dependent tasks](https://github.com/frappe/erpnext/pull/24092)
+- [Project Summary Report](https://github.com/frappe/erpnext/pull/21587)
+
+### Support
+- [Help Articles on support portal](https://github.com/frappe/erpnext/pull/22194)
+- [Issue Metrics and SLA Enhancements](https://github.com/frappe/erpnext/pull/21617)
+- [Issue Summary Script Report](https://docs.erpnext.com/docs/user/manual/en/support/support_reports)
+- [Issue Analytics Script Report](https://docs.erpnext.com/docs/user/manual/en/support/support_reports)
+
+### Non-Profits
+- [80G Certificates and Donations](https://docs.erpnext.com/docs/user/manual/en/non_profit/tax_exemption_80g_certificate)
+
+#### Integrations
+- [Woocommerce Integration](https://docs.erpnext.com/docs/user/manual/en/erpnext_integration/woocommerce_integration)
+- [Taxjar Integration](https://github.com/frappe/erpnext/pull/21047)
+- [M-pesa Integration](https://docs.erpnext.com/docs/user/manual/en/erpnext_integration/mpesa-integration)
+- [Telephony feature using Twillio](https://github.com/frappe/erpnext/pull/24032)
+- [Voice Call Settings](https://github.com/frappe/erpnext/pull/24126)
+
+
+#### Other Enhancements and Fixes
+- Accounting Dimensions in Budget Variance Report ([#19973](https://github.com/frappe/erpnext/pull/19973))
+- "Sync Now" option in Plaid Settings ([#23602](https://github.com/frappe/erpnext/pull/23602))
+- Custom Fields in POS ([#19876](https://github.com/frappe/erpnext/pull/19876))
+- [Inter Warehouse Stock Transfer in Purchase Receipt](https://docs.erpnext.com/docs/user/manual/en/stock/articles/material-transfer-from-delivery-note)
+- [Accounts Payable Report based on Payment Terms](https://docs.erpnext.com/docs/user/manual/en/accounts/accounting-reports)
+- Configurable accounting dimension filters and validations ([#23912](https://github.com/frappe/erpnext/pull/23912))
+- Territory tree in Customer Acquisition and Loyalty report ([#21668](https://github.com/frappe/erpnext/pull/21668))
+- Allow Purchase Invoice Creation Without Purchase Order Checkbox in Supplier ([#20864](https://github.com/frappe/erpnext/pull/20864))
+- Gross Profit In Quotation ([#21795](https://github.com/frappe/erpnext/pull/21795))
+- Notify credit controller users for credit limit extension via Email ([#22213](https://github.com/frappe/erpnext/pull/22213))
+- Run MRP at parent level in the production plan and make material transfer based upon materials availability ([#21545](https://github.com/frappe/erpnext/pull/21545))
+- Balance Serial Nos in Stock Ledger report ([#23675](https://github.com/frappe/erpnext/pull/23675))
+- Youtube interactions via Video  ([#22867](https://github.com/frappe/erpnext/pull/22867))
+- Consider Holiday List in Student Leave Application and Attendance ([#23388](https://github.com/frappe/erpnext/pull/23388))
+- Patient appointment status changes ([#24201](https://github.com/frappe/erpnext/pull/24201))
+- Sales order status filter added for production plan ([#23805](https://github.com/frappe/erpnext/pull/23805))
+- Monthly attendance sheet report group by Department, Designation, Employee Grade and Branch ([#21331](https://github.com/frappe/erpnext/pull/21331))
+- Upload Attendance template now have pre-filled holiday status ([#20947](https://github.com/frappe/erpnext/pull/20947))
+- Provision to disable serial no and batch selector ([#24398](https://github.com/frappe/erpnext/pull/24398))
+
+<details>
+<summary>More</summary>
+
+- Fetch Items from BOM in Stock Entry([#19498](https://github.com/frappe/erpnext/pull/19498))
+- Supplier Sourced Items in BOM ([#23557](https://github.com/frappe/erpnext/pull/23557))
+- Close Production Plan ([#23728](https://github.com/frappe/erpnext/pull/23728))
+- Button to create Stock Entry for Drug Shortage ([#24012](https://github.com/frappe/erpnext/pull/24012))
+- Added column cost center in Accounts Receivable report ([#23835](https://github.com/frappe/erpnext/pull/23835))
+- Added jinja templating in Contract Template ([#24046](https://github.com/frappe/erpnext/pull/24046))
+- Make account number length configurable ([#23845](https://github.com/frappe/erpnext/pull/23845))
+- Add company and correct filter in bank reconciliation statement ([#23614](https://github.com/frappe/erpnext/pull/23614))
+- Added Condition field in Pricing Rule ([#23014](https://github.com/frappe/erpnext/pull/23014))
+- Open lead status on next contact date ([#23445](https://github.com/frappe/erpnext/pull/23445))
+- [Tax Category in POS Profile](https://docs.erpnext.com/docs/user/manual/en/accounts/pos-profile)
+- Added phone field in product Inquiry ([#23170](https://github.com/frappe/erpnext/pull/23170))
+- Allow Discharge despite Unbilled Healthcare Services ([#24281](https://github.com/frappe/erpnext/pull/24281))
+- Do Not Bill Patient Encounters for Inpatients ([#24355](https://github.com/frappe/erpnext/pull/24355))
+- Autofill Supplier pop-up when only 1 Supplier in RFQ ([#22512](https://github.com/frappe/erpnext/pull/22512))
+- Accounting entries for service item in Purchase receipt ([#22223](https://github.com/frappe/erpnext/pull/22223))
+- Added Project in Sales Analytics report ([#23309](https://github.com/frappe/erpnext/pull/23309))
+- Added all companies option in employee tree to view employee across all companies ([#22573](https://github.com/frappe/erpnext/pull/22573))
+- Email Group Option In Email Campaign ([#22731](https://github.com/frappe/erpnext/pull/22731))
+- Stock Report Enhancements ([#21727](https://github.com/frappe/erpnext/pull/21727))
+- Added range for age in stock ageing ([#22622](https://github.com/frappe/erpnext/pull/22622))
+- Report Summary in Financial Statement([#20876](https://github.com/frappe/erpnext/pull/20876))
+- Added sequence id in routing for the completion of operations sequentially ([#23641](https://github.com/frappe/erpnext/pull/23641))
+- Nested Set filtering for Accounting Dimension
+- Add/Remove Items from submitted Sales/Purchase Order
+- Provision to edit Item Details from Marketplace
+- Scan Barcode in Purchase Receipt
+- Disable Rounded Totals Checkbox for Salary Slips in HR Settings
+
+- Renamed Loan Management to Loan on Desk Page ([#21877](https://github.com/frappe/erpnext/pull/21877))
+- Added Expense Approver field in Employee master ([#22244](https://github.com/frappe/erpnext/pull/22244))
+- Bill all hours by default on Timesheet ([#22155](https://github.com/frappe/erpnext/pull/22155))
+- Unable to cancel employee advance ([#22374](https://github.com/frappe/erpnext/pull/22374))
+- Status error in purchase invoice ([#22351](https://github.com/frappe/erpnext/pull/22351))
+- Item-wise sales and purchase register export ([#22184](https://github.com/frappe/erpnext/pull/22184))
+- Billing address in for Purchase documents ([#22233](https://github.com/frappe/erpnext/pull/22233))
+- Handle canceled entries in financial statements ([#22231](https://github.com/frappe/erpnext/pull/22231))
+- Default period start date and period end date for financial statements ([#22011](https://github.com/frappe/erpnext/pull/22011))
+- Update Packed Items via Update Items in Sales Order ([#22392](https://github.com/frappe/erpnext/pull/22392))
+- Hide delete company transactions button if not system manager ([#21839](https://github.com/frappe/erpnext/pull/21839))
+- Skipping total row for tree-view reports ([#22350](https://github.com/frappe/erpnext/pull/22350))
+- Cancelled entries in tds payable monthly report ([#22131](https://github.com/frappe/erpnext/pull/22131))
+- Inter-company Invoice currency for multicurrency transactions ([#21984](https://github.com/frappe/erpnext/pull/21984))
+- Filter batches based on item and warehouse in Pick List (develop) ([#21780](https://github.com/frappe/erpnext/pull/21780))
+- Set cost center in Expense Claim child based on parent (if missing) ([#22175](https://github.com/frappe/erpnext/pull/22175))
+- Item wise backdated stock entry posting for immutable ledger ([#22366](https://github.com/frappe/erpnext/pull/22366))
+- Shopping cart UI fixes ([#22137](https://github.com/frappe/erpnext/pull/22137))
+- Filter Leave Type based on allocation for a particular employee ([#22050](https://github.com/frappe/erpnext/pull/22050))
+- Party validation for inter-warehouse transaction ([#22186](https://github.com/frappe/erpnext/pull/22186))
+- Manufacturing dashboard and work order summary chart ([#21946](https://github.com/frappe/erpnext/pull/21946))
+- IP Admission and Discharge, Minor fixes ([#21817](https://github.com/frappe/erpnext/pull/21817))
+- Validation of Purchase Order against Material Request missing ([#22192](https://github.com/frappe/erpnext/pull/22192))
+- Staffing Plan validation ([#22379](https://github.com/frappe/erpnext/pull/22379))
+- Do not allow backdated stock transactions in previous fiscal year ([#21967](https://github.com/frappe/erpnext/pull/21967))
+- Employee Advance Return not working ([#21812](https://github.com/frappe/erpnext/pull/21812))
+- Added card for reports on education desk ([#21853](https://github.com/frappe/erpnext/pull/21853))
+- Refactored project summary report  ([#21943](https://github.com/frappe/erpnext/pull/21943))
+- Revenue and Customer Count only in date range in Customer Acquitition Report ([#22210](https://github.com/frappe/erpnext/pull/22210))
+- Alternative item not working for subcontract ([#22386](https://github.com/frappe/erpnext/pull/22386))
+- Unable to create batched Item ([#22393](https://github.com/frappe/erpnext/pull/22393))
+- Filters for the manufacturing reports ([#21960](https://github.com/frappe/erpnext/pull/21960))
+- Raw material warehouse in Production Planning Report ([#21982](https://github.com/frappe/erpnext/pull/21982))
+- Allowed LWP leave types to select in Leave Application even if there is no allocation against them ([#22197](https://github.com/frappe/erpnext/pull/22197))
+- Report not working on parameter Grade ([#21951](https://github.com/frappe/erpnext/pull/21951))
+- Allow to enter Relieving date if employee status is Left ([#22242](https://github.com/frappe/erpnext/pull/22242))
+- Resetting lost reason in opportunity and quotation ([#22378](https://github.com/frappe/erpnext/pull/22378))
+- Filtering issues in opening invoice creation tool ([#21969](https://github.com/frappe/erpnext/pull/21969))
+- Set default reference Id for "On Previous Row Amount" and "On Previous Row Total" ([#22346](https://github.com/frappe/erpnext/pull/22346))
+- UX date range field separated in from and to date fields. ([#21765](https://github.com/frappe/erpnext/pull/21765))
+- Enable show_configure_button when shopping cart is enabled ([#22468](https://github.com/frappe/erpnext/pull/22468))
+- Setup status indicators for Job Offer and Job Applicant (develop) ([#22445](https://github.com/frappe/erpnext/pull/22445))
+- Item-wise sales history report ([#22783](https://github.com/frappe/erpnext/pull/22783))
+- Setting filter for project in kanban board ([#22717](https://github.com/frappe/erpnext/pull/22717))
+- Dashboard For Timesheet ([#22750](https://github.com/frappe/erpnext/pull/22750))
+- Handle custom statuses for the pause SLA configuration ([#22349](https://github.com/frappe/erpnext/pull/22349))
+- Quality Feedback and Template ([#22571](https://github.com/frappe/erpnext/pull/22571))
+- Unable to change link from new lead to existing customer ([#22787](https://github.com/frappe/erpnext/pull/22787))
+- Move Issue List actions under 'Actions' dropdown (ux) ([#22710](https://github.com/frappe/erpnext/pull/22710))
+- Cost center should only show option of selected company ([#22598](https://github.com/frappe/erpnext/pull/22598))
+- Serial No Rename does not affect  Stock Ledger Entry ([#22746](https://github.com/frappe/erpnext/pull/22746))
+- Descriptions not copied while creating Fees from Fee Structure ([#22792](https://github.com/frappe/erpnext/pull/22792))
+- Company filter for cost_center and expense_account in all sales and purchase transactions ([#22478](https://github.com/frappe/erpnext/pull/22478))
+- Arrangements of filters for reports accounts payable & receivable  ([#22636](https://github.com/frappe/erpnext/pull/22636))
+- Update the project after task deletion so that the % completed shows correct value ([#22591](https://github.com/frappe/erpnext/pull/22591))
+- Block Invalid Serial No updates in Maintenance Schedule ([#22665](https://github.com/frappe/erpnext/pull/22665))
+- Fetch item price in sales invoice based on it's validity ([#22563](https://github.com/frappe/erpnext/pull/22563))
+- Add view ledger button for cancelled docs ([#22432](https://github.com/frappe/erpnext/pull/22432))
+- Allow creating SLA documents even if SLA tracking is not enabled ([#22608](https://github.com/frappe/erpnext/pull/22608))
+- Quotation list view blank if quotation_to field not set as a standard filter ([#22672](https://github.com/frappe/erpnext/pull/22672))
+- Salary deductions report fixes ([#22397](https://github.com/frappe/erpnext/pull/22397))
+22727))
+- Incorrect delivered qty in Supplier-Wise Sales Analytics ([#22631](https://github.com/frappe/erpnext/pull/22631))
+- Moved parent warehouse to top section also added a section break ([#22708](https://github.com/frappe/erpnext/pull/22708))
+- Skip Progress and Completed by fields on Task Duplication ([#22565](https://github.com/frappe/erpnext/pull/22565))
+- Incorrect stock after merging the items ([#22526](https://github.com/frappe/erpnext/pull/22526))
+- Letter head not found in opening invoice creation tool ([#22488](https://github.com/frappe/erpnext/pull/22488))
+- Cannot cancel asset and asset movement ([#22441](https://github.com/frappe/erpnext/pull/22441))
+- Fetch project-related info in Timesheet ([#22423](https://github.com/frappe/erpnext/pull/22423))
+- Currency symbol not showing as per company currency in stock balance report ([#22724](https://github.com/frappe/erpnext/pull/22724))
+- Add default cost center in payment reconciliation JV ([#22614](https://github.com/frappe/erpnext/pull/22614))
+- Stock Reconciliation Invalid Quantity for Batched Item ([#22726](https://github.com/frappe/erpnext/pull/22726))
+- Project link not set in accounts other than profit and loss accounts ([#22051](https://github.com/frappe/erpnext/pull/22051))
+- Buying price for non stock item in gross profit report ([#22616](https://github.com/frappe/erpnext/pull/22616))
+- Multi currency payment reconciliation ([#22738](https://github.com/frappe/erpnext/pull/22738))
+- Cannot cancel assets with repair pending ([#22440](https://github.com/frappe/erpnext/pull/22440))
+- Reset homepage to home after unchecking products page ([#22736](https://github.com/frappe/erpnext/pull/22736))
+- Generic Message in previous doc validation for buying and selling ([#22546](https://github.com/frappe/erpnext/pull/22546))
+- Expense claim outstanding while making payment entry ([#22735](https://github.com/frappe/erpnext/pull/22735))
+- Take parent cost center for child if no cost center at child in expense claim ([#22496](https://github.com/frappe/erpnext/pull/22496))
+- Consider company fiscal year for getting balance ([#22577](https://github.com/frappe/erpnext/pull/22577))
+- Pick List empty table and Serial-Batch items handling ([#22426](https://github.com/frappe/erpnext/pull/22426))
+- Show total row in print format of financial statement ([#22693](https://github.com/frappe/erpnext/pull/22693))
+- Set Root as Parent if no parent in new tree view node ([#22497](https://github.com/frappe/erpnext/pull/22497))
+- Multiple pos issues ([#23725](https://github.com/frappe/erpnext/pull/23725))
+- Calculate taxes if tax is based on item quantity and inclusive on item price ([#23001](https://github.com/frappe/erpnext/pull/23001))
+- Contact us button not visible in the website for the non variant items ([#23217](https://github.com/frappe/erpnext/pull/23217))
+- Not able to make Material Request from Sales Order ([#23669](https://github.com/frappe/erpnext/pull/23669))
+- Capture advance payments in payment order ([#23256](https://github.com/frappe/erpnext/pull/23256))
+- Program and Course Enrollment fixes ([#23333](https://github.com/frappe/erpnext/pull/23333))
+- Cannot create asset if cwip disabled and account not set ([#23580](https://github.com/frappe/erpnext/pull/23580))
+- Cannot merge pos invoices with inclusive tax ([#23541](https://github.com/frappe/erpnext/pull/23541))
+- Do not allow Company as accounting dimension ([#23755](https://github.com/frappe/erpnext/pull/23755))
+- Set value of wrong Bank Account field in Payment Entry ([#22302](https://github.com/frappe/erpnext/pull/22302))
+- Reverse journal entry for multi-currency ([#23165](https://github.com/frappe/erpnext/pull/23165))
+- Updated integrations desk page ([#23772](https://github.com/frappe/erpnext/pull/23772))
+- Assessment Result child table not visible when accessed via Assessment Plan dashboard ([#22880](https://github.com/frappe/erpnext/pull/22880))
+- Conversion factor fixes in Stock Entry ([#23407](https://github.com/frappe/erpnext/pull/23407))
+- Total calculations for multi-currency RCM invoices ([#23072](https://github.com/frappe/erpnext/pull/23072))
+- Show accounts in financial statements upto level 20 ([#23718](https://github.com/frappe/erpnext/pull/23718))
+- Consolidated financial statement sums values into wrong parent ([#23288](https://github.com/frappe/erpnext/pull/23288))
+- Set SLA variance in seconds for Duration fieldtype ([#23765](https://github.com/frappe/erpnext/pull/23765))
+- Added missing reports on selling desk ([#23548](https://github.com/frappe/erpnext/pull/23548))
+- Fixed heading in the mobile view ([#23145](https://github.com/frappe/erpnext/pull/23145))
+- Misleading filters on Item tax Template Link field ([#22918](https://github.com/frappe/erpnext/pull/22918))
+- Do not consider opening entries for TDS calculation ([#23597](https://github.com/frappe/erpnext/pull/23597))
+- Attendance calendar map fix ([#23245](https://github.com/frappe/erpnext/pull/23245))
+- Post cancellation accounting entry on posting date instead of current ([#23361](https://github.com/frappe/erpnext/pull/23361))
+- Set Customer only if Contact is present ([#23704](https://github.com/frappe/erpnext/pull/23704))
+- Add Delivery Note Count in Sales Invoice Dashboard ([#23161](https://github.com/frappe/erpnext/pull/23161))
+- Breadcrumbs for Maintenance Visit and Schedule ([#23369](https://github.com/frappe/erpnext/pull/23369))
+- Raise Error on over receipt/consumption for sub-contracted PR ([#23195](https://github.com/frappe/erpnext/pull/23195))
+- Validate if company not set in the Payment Entry ([#23419](https://github.com/frappe/erpnext/pull/23419))
+- Ignore company and bank account doctype while deleting company transactions ([#22953](https://github.com/frappe/erpnext/pull/22953))
+- Sales funnel data is inconsistent ([#23110](https://github.com/frappe/erpnext/pull/23110))
+- Credit Limit Email not working ([#23059](https://github.com/frappe/erpnext/pull/23059))
+- Add Company in list fields to fetch for Expense Claim ([#23007](https://github.com/frappe/erpnext/pull/23007))
+- Issue form cleaned up and renamed Minutes to First Response field ([#23066](https://github.com/frappe/erpnext/pull/23066))
+- Quotation lost reason options fix ([#22814](https://github.com/frappe/erpnext/pull/22814))
+- Tax amounts in HSN Wise Outward summary ([#23076](https://github.com/frappe/erpnext/pull/23076))
+- Patient Appointment not able to save ([#23434](https://github.com/frappe/erpnext/pull/23434))
+- Removed Working Hours field from Company ([#23009](https://github.com/frappe/erpnext/pull/23009))
+- Added check-in time validation in the Inpatient Record - Transfer ([#22958](https://github.com/frappe/erpnext/pull/22958))
+- Handle Blank from/to range in Numeric Item Attribute ([#23483](https://github.com/frappe/erpnext/pull/23483))
+- Sequence Matcher error in Bank Reconciliation ([#23539](https://github.com/frappe/erpnext/pull/23539))
+- Fixed Conversion Factor rate for the BOM Exploded Item ([#23151](https://github.com/frappe/erpnext/pull/23151))
+- Payment Schedule not fetching ([#23476](https://github.com/frappe/erpnext/pull/23476))
+- Validate if removed Item Attributes exist in variant items ([#22911](https://github.com/frappe/erpnext/pull/22911))
+- Set default billing address for purchase documents ([#22950](https://github.com/frappe/erpnext/pull/22950))
+- Added help link in navbar settings ([#22943](https://github.com/frappe/erpnext/pull/22943))
+- Apply TDS on Purchase Invoice creation from Purchase Order and Purchase Receipt ([#23282](https://github.com/frappe/erpnext/pull/23282))
+- Education Module fixes ([#23714](https://github.com/frappe/erpnext/pull/23714))
+- Filter out cancelled entries in customer ledger summary ([#23205](https://github.com/frappe/erpnext/pull/23205))
+- Fiscal Year and Tax Rates for Italy ([#23623](https://github.com/frappe/erpnext/pull/23623))
+- Production Plan incorrect Work Order qty ([#23264](https://github.com/frappe/erpnext/pull/23264))
+- Added new filters in the Batch-wise Balance History report ([#23676](https://github.com/frappe/erpnext/pull/23676))
+- Update state code and union territory for Daman and Diu ([#22988](https://github.com/frappe/erpnext/pull/22988))
+- Set Stock UOM in item while creating Material Request from Stock Entry ([#23436](https://github.com/frappe/erpnext/pull/23436))
+- Sales Order to Purchase Order flow improvement ([#23357](https://github.com/frappe/erpnext/pull/23357))
+- Student Admission and Student Applicant fixes ([#23515](https://github.com/frappe/erpnext/pull/23515))
+- Loan disbursement amount validation ([#24000](https://github.com/frappe/erpnext/pull/24000))
+- Making company address read-only in delivery note ([#23890](https://github.com/frappe/erpnext/pull/23890))
+- BOM stock report color showing always red ([#23994](https://github.com/frappe/erpnext/pull/23994))
+- Added filter for customer field in Issue ([#24051](https://github.com/frappe/erpnext/pull/24051))
+- Added project link in timesheet form ([#23764](https://github.com/frappe/erpnext/pull/23764))
+- Update integrations desk page ([#23767](https://github.com/frappe/erpnext/pull/23767))
+- Place of supply change on address change ([#23941](https://github.com/frappe/erpnext/pull/23941))
+- TDS calculation, skip invoices with "Apply Tax Withholding Amount" has disabled ([#23672](https://github.com/frappe/erpnext/pull/23672))
+- Auto fetch serial nos with modified conversion factor ([#23854](https://github.com/frappe/erpnext/pull/23854))
+- Default cost center in item master not set in stock entry ([#23877](https://github.com/frappe/erpnext/pull/23877))
+- Incorrect de-link serial no and batch ([#23947](https://github.com/frappe/erpnext/pull/23947))
+- Accounting for internal transfer invoices within same company ([#24021](https://github.com/frappe/erpnext/pull/24021))
+- Multiple pricing rule with margin type as Percentage is not working ([#24205](https://github.com/frappe/erpnext/pull/24205))
+- Added Purchase Order to Global Search ([#24055](https://github.com/frappe/erpnext/pull/24055))
+- Cannot expand row in update items dialog ([#23839](https://github.com/frappe/erpnext/pull/23839))
+- Maintain stock can't be changed it there is product bundle ([#23989](https://github.com/frappe/erpnext/pull/23989))
+- SO to PO Mapping Issue ([#23820](https://github.com/frappe/erpnext/pull/23820))
+- Asset with value zero doesn't show up in fixed asset register ([#24091](https://github.com/frappe/erpnext/pull/24091))
+- Cannot save customer email & phone ([#23797](https://github.com/frappe/erpnext/pull/23797))
+- Incorrect balance value in stock balance report ([#24048](https://github.com/frappe/erpnext/pull/24048))
+- Payment Terms not fetched in Purchase Invoice from Purchase Receipt ([#23735](https://github.com/frappe/erpnext/pull/23735))
+- Fix for LMS Sign Up link ([#23743](https://github.com/frappe/erpnext/pull/23743))
+- Incorrect stock quantity if 'Allow Multiple Material Consumption… ([#24116](https://github.com/frappe/erpnext/pull/24116))
+- Added wrong absent days calculation in salary slip ([#23897](https://github.com/frappe/erpnext/pull/23897))
+- Purchase receipt to purchase invoice bill date mapping ([#23967](https://github.com/frappe/erpnext/pull/23967))
+- Overriding po ([#24022](https://github.com/frappe/erpnext/pull/24022))
+- Do not cancel reference document on Quality Inspection cancellation ([#24198](https://github.com/frappe/erpnext/pull/24198))
+- Get formatted value in 'taxes' print template ([#24035](https://github.com/frappe/erpnext/pull/24035))
+- Don't overrule Item Price via Pricing Rule Rate if 0 ([#23636](https://github.com/frappe/erpnext/pull/23636))
+- Job card error handling for operations field ([#23991](https://github.com/frappe/erpnext/pull/23991))
+- Validation for journal entry with 0 debit and credit values ([#23975](https://github.com/frappe/erpnext/pull/23975))
+- Check if customer exists in product listing ([#24030](https://github.com/frappe/erpnext/pull/24030))
+- Asset finance book posting date fix ([#23778](https://github.com/frappe/erpnext/pull/23778))
+- Same source and target tables in Status Updater's update query ([#24110](https://github.com/frappe/erpnext/pull/24110))
+- Asset finance book depreciation posting date fix ([#23833](https://github.com/frappe/erpnext/pull/23833))
+- Ignore exception during leave ledger creation from patch ([#24005](https://github.com/frappe/erpnext/pull/24005))
+- Added link of bank reconciliation and clearance in accounting desk page ([#23850](https://github.com/frappe/erpnext/pull/23850))
+- Sales invoice add button from sales order dashboard ([#24077](https://github.com/frappe/erpnext/pull/24077))
+- Incorrect calculation for consumed qty for subcontract item ([#23257](https://github.com/frappe/erpnext/pull/23257))
+- Incorrect required_qty in Production Planning Report ([#24074](https://github.com/frappe/erpnext/pull/24074))
+- Email digest user not found ([#23949](https://github.com/frappe/erpnext/pull/23949))
+- Delete Receive at Warehouse entry on cancellation of Send to War… ([#24115](https://github.com/frappe/erpnext/pull/24115))
+- Added TDS Payable account number and an error message ([#24065](https://github.com/frappe/erpnext/pull/24065))
+- Override field_map for job card gantt ([#24155](https://github.com/frappe/erpnext/pull/24155))
+- Old shopify order syncing date ([#23990](https://github.com/frappe/erpnext/pull/23990))
+- Shipping chanrges not sync in erpnext from shopify ([#24114](https://github.com/frappe/erpnext/pull/24114))
+- GSTR B2C report ([#24039](https://github.com/frappe/erpnext/pull/24039))
+- Ignore cancelled entries in stock balance report ([#23757](https://github.com/frappe/erpnext/pull/23757))
+- Stock ageing report not working ([#23923](https://github.com/frappe/erpnext/pull/23923))
+- Incorrect assign to in Maintenance Schedule  ([#23831](https://github.com/frappe/erpnext/pull/23831))
+- Improve UX of DATEV report ([#23892](https://github.com/frappe/erpnext/pull/23892))
+- Set SLA variance in seconds for Duration fieldtype ([#23765](https://github.com/frappe/erpnext/pull/23765))
+- dDouble exception in payroll ([#24078](https://github.com/frappe/erpnext/pull/24078))
+- Make asset dashboard charts public ([#23751](https://github.com/frappe/erpnext/pull/23751))
+- Don't copy terms and discount from SO to PO ([#23903](https://github.com/frappe/erpnext/pull/23903))
+- Ignore doctypes on company transaction delete ([#23864](https://github.com/frappe/erpnext/pull/23864))
+- Error handling in Upload Attendance  ([#23907](https://github.com/frappe/erpnext/pull/23907))
+- Tax template update on customer address change ([#24160](https://github.com/frappe/erpnext/pull/24160))
+- Not able to save bom ([#23910](https://github.com/frappe/erpnext/pull/23910))
+- Enable Allow Auto Repeat for standard doctypes having auto_repeat field ([#23776](https://github.com/frappe/erpnext/pull/23776))
+- Place of Supply fix in Sales Invoices ([#23785](https://github.com/frappe/erpnext/pull/23785))
+- Opening invoices in GSTR-1 report ([#24117](https://github.com/frappe/erpnext/pull/24117))
+- Partial serial no return issue ([#24208](https://github.com/frappe/erpnext/pull/24208))
+- Import taxjar globally in the taxjar_integration module ([#24027](https://github.com/frappe/erpnext/pull/24027))
+- Payroll attendance error ([#23887](https://github.com/frappe/erpnext/pull/23887))
+- Loan application link on creating loan ([#23937](https://github.com/frappe/erpnext/pull/23937))
+- POS item search includes non stock items ([#23914](https://github.com/frappe/erpnext/pull/23914))
+- Paid amount in Sales Invoice POS return resets to 0 ([#24057](https://github.com/frappe/erpnext/pull/24057))
+- Fiscal year can be shorter than 12 months ([#23838](https://github.com/frappe/erpnext/pull/23838))
+- Loan repayment type option remove ([#23582](https://github.com/frappe/erpnext/pull/23582))
+- Item wise tax calculation ([#23744](https://github.com/frappe/erpnext/pull/23744))
+- Enabling track changes for stock settings ([#23982](https://github.com/frappe/erpnext/pull/23982))
+- Added link of bank reconciliation and clearance in accounting desk page ([#23809](https://github.com/frappe/erpnext/pull/23809))
+- Location data on Asset to use command(make_demo) ([#23825](https://github.com/frappe/erpnext/pull/23825))
+- Handle Account and Item None not found in Opening Invoice Creation Tool ([#23559](https://github.com/frappe/erpnext/pull/23559))
+- Multiple subcontracting issues ([#23662](https://github.com/frappe/erpnext/pull/23662))
+- Sequence id override with workstation column ([#23810](https://github.com/frappe/erpnext/pull/23810))
+- Leave policy dashboard fix and roles ([#24170](https://github.com/frappe/erpnext/pull/24170))
+- Scan barcode does not update barcode item field in sales order ([#24090](https://github.com/frappe/erpnext/pull/24090))
+- Item price duplicate checking ([#23408](https://github.com/frappe/erpnext/pull/23408))
+- Tax template update on supplier change for India ([#24060](https://github.com/frappe/erpnext/pull/24060))
+- Consumed qty logic for subcontracted raw materials ([#23314](https://github.com/frappe/erpnext/pull/23314))
+- Finance book not getting added in journal Entry of asset value adjustment ([#24100](https://github.com/frappe/erpnext/pull/24100))
+- Set proper state code in ewaybill JSON when GST category is SEZ ([#23953](https://github.com/frappe/erpnext/pull/23953))
+- Copying po no when mapping doc ([#23729](https://github.com/frappe/erpnext/pull/23729))
+- Duplicate items validation for POS Invoice when allow multiple items is disabled ([#23896](https://github.com/frappe/erpnext/pull/23896))
+- Do not allow Company as accounting dimension ([#23749](https://github.com/frappe/erpnext/pull/23749))
+- Validation for duplicate Tax Category ([#23978](https://github.com/frappe/erpnext/pull/23978))
+- Therapy plan and session fixes ([#23817](https://github.com/frappe/erpnext/pull/23817))
+- Pricing rule with transaction not working for additional product ([#24053](https://github.com/frappe/erpnext/pull/24053))
+- Inpatient Medication Order and Entry fixes ([#23799](https://github.com/frappe/erpnext/pull/23799))
+- Avoid using SQL query to get fiscal year dates ([#24050](https://github.com/frappe/erpnext/pull/24050))
+- Auto Statewise gst tax template ([#23832](https://github.com/frappe/erpnext/pull/23832))
+- On save sequence id column override with workstation ([#23812](https://github.com/frappe/erpnext/pull/23812))
+- Multiple pricing rules are not working on selling side ([#22711](https://github.com/frappe/erpnext/pull/22711))
+- Salary slip popup error ([#24192](https://github.com/frappe/erpnext/pull/24192))
+- Multiple pricing rule with margin type as Percentage is not working ([#24204](https://github.com/frappe/erpnext/pull/24204))
+- Allow statistical component in salary structure. ([#24424](https://github.com/frappe/erpnext/pull/24424))
+- Set current asset value before calculating difference amount ([#24119](https://github.com/frappe/erpnext/pull/24119))
+- To use Stock UoM in BOM Stock Report ([#24339](https://github.com/frappe/erpnext/pull/24339))
+- Accounting entries of asset when submitting purchase receipt ([#24191](https://github.com/frappe/erpnext/pull/24191))
+- Batch/Serial Selector for Scanned Batched Item ([#24338](https://github.com/frappe/erpnext/pull/24338))
+- Link timesheets with corresponding projects ([#24346](https://github.com/frappe/erpnext/pull/24346))
+- Material request wrong status issue ([#24019](https://github.com/frappe/erpnext/pull/24019))
+- UX issues in e-invoicing ([#24358](https://github.com/frappe/erpnext/pull/24358))
+- Company Wise Valuation Rate for RM in BOM ([#24324](https://github.com/frappe/erpnext/pull/24324))
+- Stock ageing should not take cancelled stock entries. ([#24437](https://github.com/frappe/erpnext/pull/24437))
+- Partial loan security unpledging ([#24252](https://github.com/frappe/erpnext/pull/24252))
+- Asset depreciation ledger ([#24226](https://github.com/frappe/erpnext/pull/24226))
+- Back Update from QC based on Batch No ([#24329](https://github.com/frappe/erpnext/pull/24329))
+- Fix for not having fiscal year while creating new company ([#24130](https://github.com/frappe/erpnext/pull/24130))
+- E-invoice print format not showing other charges ([#24474](https://github.com/frappe/erpnext/pull/24474))
+- Tax template update on customer address change ([#24146](https://github.com/frappe/erpnext/pull/24146))
+- Do not manufacture same serial no multiple times ([#24164](https://github.com/frappe/erpnext/pull/24164))
+- Ignore group cost center validation for period closing voucher ([#24375](https://github.com/frappe/erpnext/pull/24375))
+- Partial serial no return issue ([#24207](https://github.com/frappe/erpnext/pull/24207))
+- GSTR-1 double entry issue ([#24376](https://github.com/frappe/erpnext/pull/24376))
+- Not able to create dunning from sales invoice ([#24349](https://github.com/frappe/erpnext/pull/24349))
+- Set company in leave allocation and leave ledger entry ([#24296](https://github.com/frappe/erpnext/pull/24296))
+- Allow leave policy assignment to be canceled. ([#24265](https://github.com/frappe/erpnext/pull/24265))
+- Removed all day event from shift assignment calendar ([#24397](https://github.com/frappe/erpnext/pull/24397))
+- Tax calculation on salary slip for the first month ([#24272](https://github.com/frappe/erpnext/pull/24272))
+- Validate tax template for tax category ([#24402](https://github.com/frappe/erpnext/pull/24402))
+- Numeric/Non-numeric QI UX ([#24517](https://github.com/frappe/erpnext/pull/24517))
+- Finished good produced qty validation ([#24220](https://github.com/frappe/erpnext/pull/24220))
+- Incorrect serial no in the subcontracted purchase receipt ([#24354](https://github.com/frappe/erpnext/pull/24354))
+- Don't validate warehouse values between Material Request and Stock Entry ([#24294](https://github.com/frappe/erpnext/pull/24294))
+- Don't cancel job card if manufacturing entry has made ([#24063](https://github.com/frappe/erpnext/pull/24063))
+- Subscription prepaid date validation ([#24356](https://github.com/frappe/erpnext/pull/24356))
+- Payment Period based on invoice date report fix/refactor ([#24378](https://github.com/frappe/erpnext/pull/24378))
+- Drop ship partial order fixed ([#24072](https://github.com/frappe/erpnext/pull/24072))
+- Payment entry multi-currency issue ([#24332](https://github.com/frappe/erpnext/pull/24332))
+- Multiple pricing rule issue ([#24515](https://github.com/frappe/erpnext/pull/24515))
+- Last purchase rate not updating when voucher cancelled if only one voucher is present ([#24322](https://github.com/frappe/erpnext/pull/24322))
+- Do not cancel reference document on Quality Inspection cancellation ([#24197](https://github.com/frappe/erpnext/pull/24197))
+- Refactored fetching & validating address from erpnext rather than gst portal ([#24297](https://github.com/frappe/erpnext/pull/24297))
+- Opportunity Status fix ([#22944](https://github.com/frappe/erpnext/pull/22944))
+- Fixed stock and account balance syncing ([#24644](https://github.com/frappe/erpnext/pull/24644))
+- Fixed incorrect stock ledger qty in the stock ledger report and bin ([#24649](https://github.com/frappe/erpnext/pull/24649))
+- Fixed Consolidated Financial Statement report ([#24580](https://github.com/frappe/erpnext/pull/24580))
+- Repost incompleted backdated transactions ([#24991](https://github.com/frappe/erpnext/pull/24991))
+- Unequal debit and credit issue on RCM Invoice ([#24838](https://github.com/frappe/erpnext/pull/24838))
+- Period list for exponential smoothing forecasting report ([#24983](https://github.com/frappe/erpnext/pull/24983))
+- POS Opening Entry with empty balance detail rows ([#24891](https://github.com/frappe/erpnext/pull/24891))
+- Use account_name only in consolidated report ([#24840](https://github.com/frappe/erpnext/pull/24840))
+- Validation of job card in stock entry ([#24882](https://github.com/frappe/erpnext/pull/24882))
+- Incorrect Nil Exempt and Non GST amount in GSTR3B report ([#24918](https://github.com/frappe/erpnext/pull/24918))
+- TDS check getting checked after reload ([#24973](https://github.com/frappe/erpnext/pull/24973))
+- Membership and Donation API fixes ([#24900](https://github.com/frappe/erpnext/pull/24900))
+- Allow zero valuation in stock reconciliation ([#24985](https://github.com/frappe/erpnext/pull/24985))
+- Simplified logic for additional salary ([#24907](https://github.com/frappe/erpnext/pull/24907))
+- Allow to select item code in batch naming ([#24825](https://github.com/frappe/erpnext/pull/24825))
+- Membership renewal validation (#24963) ([#24964](https://github.com/frappe/erpnext/pull/24964))
+</details>
\ No newline at end of file
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 81f0ad3..c0c1315 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -325,7 +325,7 @@
 			and status not in ("Stopped", "Closed") %(fcond)s
 			and (
 				(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
-				or `tabDelivery Note`.grand_total = 0
+				or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100)
 				or (
 					`tabDelivery Note`.is_return = 1
 					and return_against in (select name from `tabDelivery Note` where per_billed < 100)
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index f352bae..2049957 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -406,8 +406,7 @@
 	def set_rate_of_stock_uom(self):
 		if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
 			for d in self.get("items"):
-				if d.conversion_factor:
-					d.stock_uom_rate = d.rate / d.conversion_factor
+				d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
 
 	def validate_internal_transfer(self):
 		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 5f73c55..9fae494 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -113,10 +113,12 @@
 					item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
 					if flt(item.rate_with_margin) > 0:
 						item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
+
 						if item.discount_amount and not item.discount_percentage:
-							item.rate -= item.discount_amount
+							item.rate = item.rate_with_margin - item.discount_amount
 						else:
 							item.discount_amount = item.rate_with_margin - item.rate
+
 					elif flt(item.price_list_rate) > 0:
 						item.discount_amount = item.price_list_rate - item.rate
 				elif flt(item.price_list_rate) > 0 and not item.discount_amount:
@@ -147,7 +149,9 @@
 				validate_taxes_and_charges(tax)
 				validate_inclusive_tax(tax, self.doc)
 
-			tax.item_wise_tax_detail = {}
+			if not self.doc.get('is_consolidated'):
+				tax.item_wise_tax_detail = {}
+
 			tax_fields = ["total", "tax_amount_after_discount_amount",
 				"tax_amount_for_current_item", "grand_total_for_current_item",
 				"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
@@ -287,10 +291,13 @@
 				# set precision in the last item iteration
 				if n == len(self.doc.get("items")) - 1:
 					self.round_off_totals(tax)
+					self._set_in_company_currency(tax,
+						["tax_amount", "tax_amount_after_discount_amount"])
+
+					self.round_off_base_values(tax)
 					self.set_cumulative_total(i, tax)
 
-					self._set_in_company_currency(tax,
-						["total", "tax_amount", "tax_amount_after_discount_amount"])
+					self._set_in_company_currency(tax, ["total"])
 
 					# adjust Discount Amount loss in last tax iteration
 					if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
@@ -337,18 +344,11 @@
 		elif tax.charge_type == "On Item Quantity":
 			current_tax_amount = tax_rate * item.qty
 
-		current_tax_amount = self.get_final_current_tax_amount(tax, current_tax_amount)
-		self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
+		if not self.doc.get("is_consolidated"):
+			self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
 
 		return current_tax_amount
 
-	def get_final_current_tax_amount(self, tax, current_tax_amount):
-		# Some countries need individual tax components to be rounded
-		# Handeled via regional doctypess
-		if tax.account_head in frappe.flags.round_off_applicable_accounts:
-			current_tax_amount = round(current_tax_amount, 0)
-		return current_tax_amount
-
 	def set_item_wise_tax(self, item, tax, tax_rate, current_tax_amount):
 		# store tax breakup for each item
 		key = item.item_code or item.item_name
@@ -359,10 +359,20 @@
 		tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
 
 	def round_off_totals(self, tax):
+		if tax.account_head in frappe.flags.round_off_applicable_accounts:
+			tax.tax_amount = round(tax.tax_amount, 0)
+			tax.tax_amount_after_discount_amount = round(tax.tax_amount_after_discount_amount, 0)
+
 		tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
 		tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
 			tax.precision("tax_amount"))
 
+	def round_off_base_values(self, tax):
+		# Round off to nearest integer based on regional settings
+		if tax.account_head in frappe.flags.round_off_applicable_accounts:
+			tax.base_tax_amount = round(tax.base_tax_amount, 0)
+			tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
+
 	def manipulate_grand_total_for_inclusive_tax(self):
 		# if fully inclusive taxes and diff
 		if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]):
@@ -440,8 +450,9 @@
 			self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"])
 
 	def _cleanup(self):
-		for tax in self.doc.get("taxes"):
-			tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
+		if not self.doc.get('is_consolidated'):
+			for tax in self.doc.get("taxes"):
+				tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
 
 	def set_discount_amount(self):
 		if self.doc.additional_discount_percentage:
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index 801c405..ecf041e 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -25,7 +25,7 @@
 
 	if not filters: filters = []
 
-	if doctype in ['Supplier Quotation', 'Purchase Invoice', 'Quotation']:
+	if doctype in ['Supplier Quotation', 'Purchase Invoice']:
 		filters.append((doctype, 'docstatus', '<', 2))
 	else:
 		filters.append((doctype, 'docstatus', '=', 1))
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
index 5f471ab..6bec301 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
@@ -22,7 +22,7 @@
 			frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
 
 		# use the fixture data
-		import_doc(frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
+		import_doc(path=frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
 
 		frappe.reload_doctype("Customer")
 		frappe.reload_doctype("Sales Order")
diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js
index c7074e8..f28d32c 100644
--- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js
+++ b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.js
@@ -39,11 +39,13 @@
 	},
 
 	set_score_range: function(frm) {
-		let options = [];
+		let options = [''];
 		for(let i = frm.doc.scale_min; i <= frm.doc.scale_max; i++) {
 			options.push(i);
 		}
-		frappe.meta.get_docfield('Patient Assessment Sheet', 'score', frm.doc.name).options = [''].concat(options);
+		frm.fields_dict.assessment_sheet.grid.update_docfield_property(
+			'score', 'options', options
+		);
 	},
 
 	calculate_total_score: function(frm, cdt, cdn) {
@@ -83,4 +85,4 @@
 	score: function(frm, cdt, cdn) {
 		frm.events.calculate_total_score(frm, cdt, cdn);
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js
index d1f72d6..42e231d 100644
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js
+++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.js
@@ -58,8 +58,12 @@
 		}
 
 		if (frm.doc.therapy_plan_template) {
-			frappe.meta.get_docfield('Therapy Plan Detail', 'therapy_type', frm.doc.name).read_only = 1;
-			frappe.meta.get_docfield('Therapy Plan Detail', 'no_of_sessions', frm.doc.name).read_only = 1;
+			frm.fields_dict.therapy_plan_details.grid.update_docfield_property(
+				'therapy_type', 'read_only', 1
+			);
+			frm.fields_dict.therapy_plan_details.grid.update_docfield_property(
+				'no_of_sessions', 'read_only', 1
+			);
 		}
 	},
 
@@ -126,4 +130,4 @@
 		frm.set_value('total_sessions', total);
 		refresh_field('total_sessions');
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 2e26fd2..98d5966 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -262,7 +262,8 @@
 		],
 		"on_trash": "erpnext.regional.check_deletion_permission",
 		"validate": [
-			"erpnext.regional.india.utils.validate_document_name"
+			"erpnext.regional.india.utils.validate_document_name",
+			"erpnext.regional.india.utils.update_taxable_values"
 		]
 	},
 	"Purchase Invoice": {
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index 18a4fe5..f3b8a79 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -35,7 +35,8 @@
 				and docstatus != 2
 		""", (self.employee, getdate(self.attendance_date), self.name))
 		if res:
-			frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee))
+			frappe.throw(_("Attendance for employee {0} is already marked for the date {1}").format(
+				frappe.bold(self.employee), frappe.bold(self.attendance_date)))
 
 	def check_leave_record(self):
 		leave_record = frappe.db.sql("""
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json
index 04f98d1..ea25aa7 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.json
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.json
@@ -200,7 +200,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 14:42:47.321368",
+ "modified": "2021-03-31 22:31:53.746659",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee Advance",
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.json b/erpnext/hr/doctype/leave_encashment/leave_encashment.json
index dcb5874..1f6c03f 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.json
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.json
@@ -154,7 +154,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 14:45:27.948207",
+ "modified": "2021-03-31 22:32:55.492327",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Encashment",
diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
index f5fece8..303c829 100644
--- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
+++ b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.py
@@ -187,4 +187,4 @@
 		else:
 			ja_joff_map[offer.job_applicant].append(offer)
 
-	return ja_joff_map
\ No newline at end of file
+	return ja_joff_map
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index acf09f5..4f8ceb0 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -23,6 +23,7 @@
   "rate_of_interest",
   "is_secured_loan",
   "disbursement_date",
+  "closure_date",
   "disbursed_amount",
   "column_break_11",
   "maximum_loan_amount",
@@ -348,12 +349,18 @@
    "no_copy": 1,
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "fieldname": "closure_date",
+   "fieldtype": "Date",
+   "label": "Closure Date",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-24 12:27:23.208240",
+ "modified": "2021-04-10 09:28:21.946972",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 4b9a894..6f8da31 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -523,33 +523,7 @@
 		self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
 
 	def test_penalty(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='2019-10-01')
-		loan.submit()
-
-		self.assertEquals(loan.loan_amount, 1000000)
-
-		first_date = '2019-10-01'
-		last_date = '2019-10-30'
-
-		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, 1))
-		paid_amount = amounts['interest_amount']/2
-
-		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
-			paid_amount)
-
-		repayment_entry.submit()
-
+		loan, amounts = create_loan_scenario_for_penalty(self)
 		# 30 days - grace period
 		penalty_days = 30 - 4
 		penalty_applicable_amount = flt(amounts['interest_amount']/2)
@@ -559,8 +533,28 @@
 		calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
 			{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
 
+		self.assertEquals(loan.loan_amount, 1000000)
 		self.assertEquals(calculated_penalty_amount, penalty_amount)
 
+	def test_penalty_repayment(self):
+		loan, dummy = create_loan_scenario_for_penalty(self)
+		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:00')
+
+		first_penalty = 10000
+		second_penalty = amounts['penalty_amount'] - 10000
+
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:00', 10000)
+		repayment_entry.submit()
+
+		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01')
+		self.assertEquals(amounts['penalty_amount'], second_penalty)
+
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty)
+		repayment_entry.submit()
+
+		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02')
+		self.assertEquals(amounts['penalty_amount'], 0)
+
 	def test_loan_write_off_limit(self):
 		pledge = [{
 			"loan_security": "Test Security 1",
@@ -651,6 +645,32 @@
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
 		self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
 
+def create_loan_scenario_for_penalty(doc):
+	pledge = [{
+		"loan_security": "Test Security 1",
+		"qty": 4000.00
+	}]
+
+	loan_application = create_loan_application('_Test Company', doc.applicant2, 'Demand Loan', pledge)
+	create_pledge(loan_application)
+	loan = create_demand_loan(doc.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+	loan.submit()
+
+	first_date = '2019-10-01'
+	last_date = '2019-10-30'
+
+	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, 1))
+	paid_amount = amounts['interest_amount']/2
+
+	repayment_entry = create_repayment_entry(loan.name, doc.applicant2, add_days(last_date, 5),
+		paid_amount)
+
+	repayment_entry.submit()
+
+	return loan, amounts
 
 def create_loan_accounts():
 	if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
index cd5df4d..662c626 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
@@ -20,6 +20,10 @@
   "cost_center",
   "customer_details_section",
   "bank_account",
+  "disbursement_references_section",
+  "reference_date",
+  "column_break_17",
+  "reference_number",
   "amended_from"
  ],
  "fields": [
@@ -126,12 +130,31 @@
   {
    "fieldname": "column_break_8",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "disbursement_references_section",
+   "fieldtype": "Section Break",
+   "label": "Disbursement References"
+  },
+  {
+   "fieldname": "reference_date",
+   "fieldtype": "Date",
+   "label": "Reference Date"
+  },
+  {
+   "fieldname": "column_break_17",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "reference_number",
+   "fieldtype": "Data",
+   "label": "Reference Number"
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-06 10:04:30.882322",
+ "modified": "2021-04-10 10:03:41.502210",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Disbursement",
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
index 86ea59d..8fbf233 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
@@ -239,14 +239,16 @@
   {
    "fieldname": "total_penalty_paid",
    "fieldtype": "Currency",
+   "hidden": 1,
    "label": "Total Penalty Paid",
-   "options": "Company:company:default_currency"
+   "options": "Company:company:default_currency",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-05 13:45:19.137896",
+ "modified": "2021-04-10 10:00:31.859076",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Repayment",
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 5d57ced..728eadf 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -75,7 +75,7 @@
 			"docstatus": 1, "against_loan": self.against_loan}, 'posting_date')
 
 		if future_repayment_date:
-			frappe.throw("Repayment already made till date {0}".format(getdate(future_repayment_date)))
+			frappe.throw("Repayment already made till date {0}".format(get_datetime(future_repayment_date)))
 
 	def validate_amount(self):
 		precision = cint(frappe.db.get_default("currency_precision")) or 2
@@ -83,10 +83,6 @@
 		if not self.amount_paid:
 			frappe.throw(_("Amount paid cannot be zero"))
 
-		if not self.shortfall_amount and self.amount_paid < self.penalty_amount:
-			msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
-			frappe.throw(msg)
-
 	def book_unaccrued_interest(self):
 		precision = cint(frappe.db.get_default("currency_precision")) or 2
 		if self.total_interest_paid > self.interest_payable:
@@ -231,6 +227,14 @@
 		gle_map = []
 		loan_details = frappe.get_doc("Loan", self.against_loan)
 
+		if self.shortfall_amount and self.amount_paid > self.shortfall_amount:
+			remarks = _("Shortfall Repayment of {0}.\nRepayment against Loan: {1}").format(self.shortfall_amount,
+				self.against_loan)
+		elif self.shortfall_amount:
+			remarks = _("Shortfall Repayment of {0}").format(self.shortfall_amount)
+		else:
+			remarks = _("Repayment against Loan: ") + self.against_loan
+
 		if self.total_penalty_paid:
 			gle_map.append(
 				self.get_gl_dict({
@@ -271,7 +275,7 @@
 				"debit_in_account_currency": self.amount_paid,
 				"against_voucher_type": "Loan",
 				"against_voucher": self.against_loan,
-				"remarks": _("Repayment against Loan: ") + self.against_loan,
+				"remarks": remarks,
 				"cost_center": self.cost_center,
 				"posting_date": getdate(self.posting_date)
 			})
@@ -287,7 +291,7 @@
 				"credit_in_account_currency": self.amount_paid,
 				"against_voucher_type": "Loan",
 				"against_voucher": self.against_loan,
-				"remarks": _("Repayment against Loan: ") + self.against_loan,
+				"remarks": remarks,
 				"cost_center": self.cost_center,
 				"posting_date": getdate(self.posting_date)
 			})
@@ -338,6 +342,18 @@
 
 	return unpaid_accrued_entries
 
+def get_penalty_details(against_loan):
+	penalty_details = frappe.db.sql("""
+		SELECT posting_date, (penalty_amount - total_penalty_paid) as pending_penalty_amount
+		FROM `tabLoan Repayment` where posting_date >= (SELECT MAX(posting_date) from `tabLoan Repayment`
+		where against_loan = %s) and docstatus = 1 and against_loan = %s
+	""", (against_loan, against_loan))
+
+	if penalty_details:
+		return penalty_details[0][0], flt(penalty_details[0][1])
+	else:
+		return None, 0
+
 # This function returns the amounts that are payable at the time of loan repayment based on posting date
 # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
 
@@ -348,6 +364,7 @@
 	loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
 	accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name, posting_date)
 
+	computed_penalty_date, pending_penalty_amount = get_penalty_details(against_loan)
 	pending_accrual_entries = {}
 
 	total_pending_interest = 0
@@ -362,8 +379,13 @@
 		# and if no_of_late days are positive then penalty is levied
 
 		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)) + 1
+		due_date_after_grace_period = add_days(due_date, loan_type_details.grace_period_in_days)
+
+		# Consider one day after already calculated penalty
+		if computed_penalty_date and getdate(computed_penalty_date) >= due_date_after_grace_period:
+			due_date_after_grace_period = add_days(computed_penalty_date, 1)
+
+		no_of_late_days = date_diff(posting_date, due_date_after_grace_period) + 1
 
 		if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
 			penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
@@ -401,7 +423,7 @@
 	amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
 	amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
 	amounts["interest_amount"] = flt(total_pending_interest, precision)
-	amounts["penalty_amount"] = flt(penalty_amount, precision)
+	amounts["penalty_amount"] = flt(penalty_amount + pending_penalty_amount, precision)
 	amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
 	amounts["pending_accrual_entries"] = pending_accrual_entries
 	amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
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 c4c2d68..b24dc2f 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
@@ -6,7 +6,7 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import get_datetime, flt
+from frappe.utils import get_datetime, flt, getdate
 import json
 from six import iteritems
 from erpnext.loan_management.doctype.loan_security_price.loan_security_price import get_loan_security_price
@@ -113,7 +113,11 @@
 				pledged_qty += qty
 
 			if not pledged_qty:
-				frappe.db.set_value('Loan', self.loan, 'status', 'Closed')
+				frappe.db.set_value('Loan', self.loan,
+					{
+						'status': 'Closed',
+						'closure_date': getdate()
+					})
 
 @frappe.whitelist()
 def get_pledged_security_qty(loan):
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index cd61d2a..7108338 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -93,15 +93,15 @@
 		base_raw_material_cost = raw_material_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
 		base_op_cost = op_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
 
-		# test amounts in selected currency
-		self.assertEqual(bom.operating_cost, op_cost)
-		self.assertEqual(bom.raw_material_cost, raw_material_cost)
-		self.assertEqual(bom.total_cost, raw_material_cost + op_cost)
+		# test amounts in selected currency, almostEqual checks for 7 digits by default
+		self.assertAlmostEqual(bom.operating_cost, op_cost)
+		self.assertAlmostEqual(bom.raw_material_cost, raw_material_cost)
+		self.assertAlmostEqual(bom.total_cost, raw_material_cost + op_cost)
 
 		# test amounts in selected currency
-		self.assertEqual(bom.base_operating_cost, base_op_cost)
-		self.assertEqual(bom.base_raw_material_cost, base_raw_material_cost)
-		self.assertEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
+		self.assertAlmostEqual(bom.base_operating_cost, base_op_cost)
+		self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
+		self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
 
 	def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
 		frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 92074c6..fb26062 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -433,6 +433,7 @@
 def make_stock_entry(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
 		target.t_warehouse = source_parent.wip_warehouse
+		target.conversion_factor = 1
 
 	def set_missing_values(source, target):
 		target.purpose = "Material Transfer for Manufacture"
diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js
index 9b1a8ca..032c9cd 100644
--- a/erpnext/manufacturing/doctype/routing/routing.js
+++ b/erpnext/manufacturing/doctype/routing/routing.js
@@ -11,10 +11,9 @@
 	},
 
 	display_sequence_id_column: function(frm) {
-		frappe.meta.get_docfield("BOM Operation", "sequence_id",
-			frm.doc.name).in_list_view = true;
-
-		frm.fields_dict.operations.grid.refresh();
+		frm.fields_dict.operations.grid.update_docfield_property(
+			'sequence_id', 	'in_list_view', 1
+		);
 	},
 
 	calculate_operating_cost: function(frm, child) {
@@ -69,4 +68,4 @@
 		const d = locals[cdt][cdn];
 		frm.events.calculate_operating_cost(frm, d);
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
index 7b5747e..7317152 100644
--- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json
@@ -19,7 +19,7 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-07-08 14:05:56.197563",
+ "modified": "2020-06-29 20:25:36.899106",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Manufacturing",
@@ -53,4 +53,4 @@
  "subtitle": "Products, Raw Materials, BOM, Work Order, and more.",
  "success_message": "Manufacturing module is all set up!",
  "title": "Let's Set Up the Manufacturing Module."
-}
\ No newline at end of file
+}
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 1686314..14f1ab8 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -756,11 +756,18 @@
 erpnext.patches.v12_0.add_state_code_for_ladakh
 erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
 erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
-erpnext.patches.v13_0.update_vehicle_no_reqd_condition
+erpnext.patches.v12_0.update_vehicle_no_reqd_condition
+erpnext.patches.v12_0.add_einvoice_status_field #2021-03-17
+erpnext.patches.v12_0.add_einvoice_summary_report_permissions
 erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
 erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
 erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
 erpnext.patches.v13_0.setup_uae_vat_fields
 execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
+erpnext.patches.v12_0.add_company_link_to_einvoice_settings
 erpnext.patches.v13_0.rename_discharge_date_in_ip_record
+erpnext.patches.v12_0.create_taxable_value_field
+erpnext.patches.v12_0.add_gst_category_in_delivery_note
 erpnext.patches.v12_0.purchase_receipt_status
+erpnext.patches.v13_0.fix_non_unique_represents_company
+erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
diff --git a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py
new file mode 100644
index 0000000..3b560fd
--- /dev/null
+++ b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company or not frappe.db.count('E Invoice User'):
+		return
+
+	frappe.reload_doc("regional", "doctype", "e_invoice_user")
+	for creds in frappe.db.get_all('E Invoice User', fields=['name', 'gstin']):
+		company_name = frappe.db.sql("""
+			select dl.link_name from `tabAddress` a, `tabDynamic Link` dl
+			where a.gstin = %s and dl.parent = a.name and dl.link_doctype = 'Company'
+		""", (creds.get('gstin')))
+		if company_name and len(company_name) == 1:
+			frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0])
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py b/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py
new file mode 100644
index 0000000..4d649dd
--- /dev/null
+++ b/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py
@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+import frappe
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'Italy'})
+	if not company:
+		return
+
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='type_of_document', label='Type of Document',
+				fieldtype='Select', insert_after='customer_fiscal_code',
+				options='\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27'),
+		]
+	}
+
+	create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_einvoice_status_field.py b/erpnext/patches/v12_0/add_einvoice_status_field.py
new file mode 100644
index 0000000..387e885
--- /dev/null
+++ b/erpnext/patches/v12_0/add_einvoice_status_field.py
@@ -0,0 +1,69 @@
+from __future__ import unicode_literals
+import json
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	# move hidden einvoice fields to a different section
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
+			print_hide=1, hidden=1),
+		
+			dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
+				no_copy=1, print_hide=1),
+			
+			dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
+
+			dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date', 
+				no_copy=1, print_hide=1),
+
+			dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
+				no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
+				no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
+				no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
+				options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
+				hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
+
+	if frappe.db.exists('E Invoice Settings') and frappe.db.get_single_value('E Invoice Settings', 'enable'):
+		frappe.db.sql('''
+			UPDATE `tabSales Invoice` SET einvoice_status = 'Pending'
+			WHERE
+				posting_date >= '2021-04-01'
+				AND ifnull(irn, '') = ''
+				AND ifnull(`billing_address_gstin`, '') != ifnull(`company_gstin`, '')
+				AND ifnull(gst_category, '') in ('Registered Regular', 'SEZ', 'Overseas', 'Deemed Export')
+		''')
+
+		# set appropriate statuses
+		frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Generated'
+			WHERE ifnull(irn, '') != '' AND ifnull(irn_cancelled, 0) = 0''')
+
+		frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Cancelled'
+			WHERE ifnull(irn_cancelled, 0) = 1''')
+
+	# set correct acknowledgement in e-invoices
+	einvoices = frappe.get_all('Sales Invoice', {'irn': ['is', 'set']}, ['name', 'signed_einvoice'])
+
+	if einvoices:
+		for inv in einvoices:
+			signed_einvoice = inv.get('signed_einvoice')
+			if signed_einvoice:
+				signed_einvoice = json.loads(signed_einvoice)
+				frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_no', signed_einvoice.get('AckNo'), update_modified=False)
+				frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py
new file mode 100644
index 0000000..bf8f566
--- /dev/null
+++ b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py
@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	if frappe.db.exists('Report', 'E-Invoice Summary') and \
+		not frappe.db.get_value('Custom Role', dict(report='E-Invoice Summary')):
+		frappe.get_doc(dict(
+			doctype='Custom Role',
+			report='E-Invoice Summary',
+			roles= [
+				dict(role='Accounts User'),
+				dict(role='Accounts Manager')
+			]
+		)).insert()	
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py b/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
new file mode 100644
index 0000000..1208222
--- /dev/null
+++ b/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py
@@ -0,0 +1,19 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = {
+		'Delivery Note': [
+			dict(fieldname='gst_category', label='GST Category',
+				fieldtype='Select', insert_after='gst_vehicle_type', print_hide=1,
+				options='\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
+				fetch_from='customer.gst_category', fetch_if_empty=1),
+		]
+	}
+
+	create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/create_taxable_value_field.py b/erpnext/patches/v12_0/create_taxable_value_field.py
new file mode 100644
index 0000000..a0c9fcf
--- /dev/null
+++ b/erpnext/patches/v12_0/create_taxable_value_field.py
@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = {
+		'Sales Invoice Item': [
+			dict(fieldname='taxable_value', label='Taxable Value',
+				fieldtype='Currency', insert_after='base_net_amount', hidden=1, options="Company:company:default_currency",
+				print_hide=1)
+		]
+	}
+
+	create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
similarity index 83%
rename from erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
rename to erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
index c26cddb..01a4ae0 100644
--- a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
+++ b/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
@@ -1,6 +1,7 @@
 import frappe
 
 def execute():
+	frappe.reload_doc('custom', 'doctype', 'custom_field')
 	company = frappe.get_all('Company', filters = {'country': 'India'})
 	if not company:
 		return
diff --git a/erpnext/patches/v13_0/check_is_income_tax_component.py b/erpnext/patches/v13_0/check_is_income_tax_component.py
index 9ad48e2..c92d52d 100644
--- a/erpnext/patches/v13_0/check_is_income_tax_component.py
+++ b/erpnext/patches/v13_0/check_is_income_tax_component.py
@@ -8,36 +8,39 @@
 
 def execute():
 
-    doctypes = ['salary_component',
-        'Employee Tax Exemption Declaration',
-        'Employee Tax Exemption Proof Submission',
-        'Employee Tax Exemption Declaration Category',
-        'Employee Tax Exemption Proof Submission Detail'
-    ]
+	doctypes = ['salary_component',
+		'Employee Tax Exemption Declaration',
+		'Employee Tax Exemption Proof Submission',
+		'Employee Tax Exemption Declaration Category',
+		'Employee Tax Exemption Proof Submission Detail',
+		'gratuity_rule',
+		'gratuity_rule_slab',
+		'gratuity_applicable_component'
+	]
 
-    for doctype in doctypes:
-        frappe.reload_doc('Payroll', 'doctype', doctype)
+	for doctype in doctypes:
+		frappe.reload_doc('Payroll', 'doctype', doctype)
 
 
-    reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
-    for report in reports:
-        frappe.reload_doc('Regional', 'Report', report)
-        frappe.reload_doc('Regional', 'Report', report)
+	reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
+	for report in reports:
+		frappe.reload_doc('Regional', 'Report', report)
+		frappe.reload_doc('Regional', 'Report', report)
 
-    if erpnext.get_region() == "India":
-        setup(patch=True)
+	if erpnext.get_region() == "India":
+		setup(patch=True)
 
-    if frappe.db.exists("Salary Component", "Income Tax"):
-        frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
-    if frappe.db.exists("Salary Component", "TDS"):
-        frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1)
+	if frappe.db.exists("Salary Component", "Income Tax"):
+		frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
+	if frappe.db.exists("Salary Component", "TDS"):
+		frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1)
 
-    components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1)
-    for component in components:
-        frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1)
+	components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1)
+	for component in components:
+		frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1)
 
-    if erpnext.get_region() == "India":
-        if frappe.db.exists("Salary Component", "Provident Fund"):
-            frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund")
-        if frappe.db.exists("Salary Component", "Professional Tax"):
-            frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax")
\ No newline at end of file
+	if erpnext.get_region() == "India":
+		if frappe.db.exists("Salary Component", "Provident Fund"):
+			frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund")
+		if frappe.db.exists("Salary Component", "Professional Tax"):
+			frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax")
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
index 48d5cb4..59b2e49 100644
--- a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
+++ b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
@@ -11,4 +11,8 @@
 	if not company:
 		return
 
+
+	frappe.reload_doc('accounts', 'doctype', 'pos_invoice')
+	frappe.reload_doc('accounts', 'doctype', 'pos_invoice_item')
+
 	make_custom_fields()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/fix_non_unique_represents_company.py b/erpnext/patches/v13_0/fix_non_unique_represents_company.py
new file mode 100644
index 0000000..61dc824
--- /dev/null
+++ b/erpnext/patches/v13_0/fix_non_unique_represents_company.py
@@ -0,0 +1,8 @@
+import frappe
+
+def execute():
+	frappe.db.sql("""
+		update tabCustomer
+		set represents_company = NULL
+		where represents_company = ''
+	""")
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
index 5920bf1..a78f802 100644
--- a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
+++ b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
@@ -18,6 +18,7 @@
 
 		for old_dt, new_dt in doctypes.items():
 			if not frappe.db.table_exists(new_dt) and frappe.db.table_exists(old_dt):
+				frappe.reload_doc('healthcare', 'doctype', frappe.scrub(old_dt))
 				frappe.rename_doc('DocType', old_dt, new_dt, force=True)
 				frappe.reload_doc('healthcare', 'doctype', frappe.scrub(new_dt))
 				frappe.delete_doc_if_exists('DocType', old_dt)
@@ -36,6 +37,18 @@
 				SET parentfield = %(parentfield)s
 			""".format(doctype), {'parentfield': parentfield})
 
+		# copy renamed child table fields (fields were already renamed in old doctype json, hence sql)
+		frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_name = test_name""")
+		frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_event = test_event""")
+		frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_uom = test_uom""")
+		frappe.db.sql("""UPDATE `tabNormal Test Result` SET lab_test_comment = test_comment""")
+		frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_event = test_event""")
+		frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_uom = test_uom""")
+		frappe.db.sql("""UPDATE `tabDescriptive Test Result` SET lab_test_particulars = test_particulars""")
+		frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_template = test_template""")
+		frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_description = test_description""")
+		frappe.db.sql("""UPDATE `tabLab Test Group Template` SET lab_test_rate = test_rate""")
+
 		# rename field
 		frappe.reload_doc('healthcare', 'doctype', 'lab_test')
 		if frappe.db.has_column('Lab Test', 'special_toggle'):
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
index d968e1f..021bb72 100644
--- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -20,9 +20,11 @@
 	frappe.clear_cache()
 	frappe.flags.warehouse_account_map = {}
 
+	company_list = []
+
 	data = frappe.db.sql('''
 		SELECT
-			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
+			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company
 		FROM
 			`tabStock Ledger Entry`
 		WHERE
@@ -36,6 +38,9 @@
 	total_sle = len(data)
 	i = 0
 	for d in data:
+		if d.company not in company_list:
+			company_list.append(d.company)
+
 		update_entries_after({
 			"item_code": d.item_code,
 			"warehouse": d.warehouse,
@@ -53,8 +58,10 @@
 
 	print("Reposting General Ledger Entries...")
 
-	for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
-		update_gl_entries_after(posting_date, posting_time, company=row.name)
+	if data:
+		for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+			if row.name in company_list:
+				update_gl_entries_after(posting_date, posting_time, company=row.name)
 
 	frappe.db.auto_commit_on_many_writes = 0
 
diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
index 2d3b096..d927524 100644
--- a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
+++ b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
@@ -6,8 +6,9 @@
 	if "Healthcare" not in frappe.get_active_domains():
 		return
 
-	frappe.reload_doc("healthcare", "doctype", "Therapy Session")
 	frappe.reload_doc("healthcare", "doctype", "Inpatient Medication Order")
+	frappe.reload_doc("healthcare", "doctype", "Therapy Session")
+	frappe.reload_doc("healthcare", "doctype", "Clinical Procedure")
 	frappe.reload_doc("healthcare", "doctype", "Patient History Settings")
 	frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type")
 	frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type")
diff --git a/erpnext/patches/v13_0/setup_uae_vat_fields.py b/erpnext/patches/v13_0/setup_uae_vat_fields.py
index d7a5c68..1830bab 100644
--- a/erpnext/patches/v13_0/setup_uae_vat_fields.py
+++ b/erpnext/patches/v13_0/setup_uae_vat_fields.py
@@ -2,11 +2,15 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
-from erpnext.regional.united_arab_emirates.setup import  setup
+from erpnext.regional.united_arab_emirates.setup import setup
 
 def execute():
 	company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
 	if not company:
 		return
 
-	setup()
\ No newline at end of file
+	frappe.reload_doc('regional', 'report', 'uae_vat_201')
+	frappe.reload_doc('regional', 'doctype', 'uae_vat_settings')
+	frappe.reload_doc('regional', 'doctype', 'uae_vat_account')
+
+	setup()
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json
index 61ae7e4..5e17a5c 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.json
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json
@@ -175,7 +175,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 14:45:48.566756",
+ "modified": "2021-03-31 22:33:59.098532",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Additional Salary",
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
index c6f764c..8332697 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
@@ -147,7 +147,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 14:46:22.465521",
+ "modified": "2021-03-31 22:35:08.940087",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Employee Benefit Application",
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json
index e331b7a..b3bac01 100644
--- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json
@@ -144,7 +144,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 15:51:51.489269",
+ "modified": "2021-03-31 22:37:21.024625",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Employee Benefit Claim",
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json
index 51346c6..0d10b2c 100644
--- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json
@@ -94,7 +94,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 14:48:00.919839",
+ "modified": "2021-03-31 22:38:20.332316",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Employee Incentive",
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json
index 873bf88..b247d26 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json
@@ -119,7 +119,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 20:41:57.387749",
+ "modified": "2021-03-31 22:39:59.237361",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Employee Tax Exemption Declaration",
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json
index f32202a..77b107e 100644
--- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json
@@ -142,7 +142,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 20:48:32.639885",
+ "modified": "2021-03-31 22:41:13.723339",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Employee Tax Exemption Proof Submission",
diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json
index c343a44..5a7de37 100644
--- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json
@@ -104,7 +104,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 20:53:33.323712",
+ "modified": "2021-03-31 22:42:08.139520",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Income Tax Slab",
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index fde2e07..4c9469e 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -567,6 +567,7 @@
 			if publish_progress:
 				frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
 					title = _("Creating Salary Slips..."))
+
 		else:
 			salary_slips_not_created.append(emp)
 
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json
index cd563bc..7ea6210 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json
@@ -105,7 +105,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 14:50:29.401020",
+ "modified": "2021-03-31 22:43:28.363644",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Retention Bonus",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
index e3993fa..a0ddd39 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -119,6 +119,7 @@
 					frm.set_df_property('exchange_rate', 'hidden', 1);
 					frm.set_df_property("exchange_rate", "description", "" );
 				}
+			}
 		}
 	},
 
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
index ec56076..42a0f29 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.json
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -631,7 +631,7 @@
  "idx": 9,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 15:39:28.817166",
+ "modified": "2021-03-31 22:44:09.772331",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Slip",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index f6d4c7b..afdf081 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -598,10 +598,10 @@
 				continue
 
 			if (
-				not d.additional_salary
-				and (not additional_salary or additional_salary.overwrite)
-				or additional_salary
-				and additional_salary.name == d.additional_salary
+				(not d.additional_salary
+				and (not additional_salary or additional_salary.overwrite))
+				or (additional_salary
+				and additional_salary.name == d.additional_salary)
 			):
 				component_row = d
 				break
@@ -611,7 +611,7 @@
 			self.set(component_type, [
 				d for d in self.get(component_type)
 				if d.salary_component != component_data.salary_component
-				or d.additional_salary and additional_salary.name != d.additional_salary
+				or (d.additional_salary and additional_salary.name != d.additional_salary)
 				or d == component_row
 			])
 
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 7672695..01e4170 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -312,7 +312,7 @@
 		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
 
 		create_salary_slips_for_payroll_period(applicant, salary_structure.name,
-			payroll_period, deduct_random=False)
+			payroll_period, deduct_random=False, num=6)
 
 		salary_slips = frappe.get_all('Salary Slip', fields=['year_to_date', 'net_pay'], filters={'employee_name':
 			'test_ytd@salary.com'}, order_by = 'posting_date')
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js
index 6aa1387..b539b1b 100755
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.js
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js
@@ -111,12 +111,19 @@
 				frappe.set_route('Form', 'Salary Structure Assignment', doc.name);
 			});
 			frm.add_custom_button(__("Assign to Employees"),function () {
-			frm.trigger('assign_to_employees')
-		})
+				frm.trigger('assign_to_employees')
+			})
 		}
+
+		// set columns read-only
 		let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"];
 		fields_read_only.forEach(function(field) {
-			frappe.meta.get_docfield("Salary Detail", field, frm.doc.name).read_only = 1;
+			frm.fields_dict.earnings.grid.update_docfield_property(
+				field, 'read_only', 1
+			);
+			frm.fields_dict.deductions.grid.update_docfield_property(
+				field, 'read_only', 1
+			);
 		});
 		frm.trigger('set_earning_deduction_component');
 	},
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index f2fb558..36387f2 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -164,7 +164,13 @@
 	salary_structure_assignment.employee = employee
 	salary_structure_assignment.base = 50000
 	salary_structure_assignment.variable = 5000
-	salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1)
+
+	if getdate(nowdate()).day == 1:
+		date = from_date or nowdate()
+	else:
+		date = from_date or add_days(nowdate(), -1)
+
+	salary_structure_assignment.from_date = date
 	salary_structure_assignment.salary_structure = salary_structure
 	salary_structure_assignment.currency = currency
 	salary_structure_assignment.payroll_payable_account = get_payable_account(company)
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
index 50fabed..c8b98e5 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
@@ -145,7 +145,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-03-31 15:49:36.361253",
+ "modified": "2021-03-31 22:44:46.267974",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Structure Assignment",
diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json
index 4bae675..b1a7cc2 100644
--- a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json
+++ b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json
@@ -8,7 +8,7 @@
  "is_mandatory": 1,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-06-01 11:53:54.553947",
+ "modified": "2020-06-29 11:53:54.553947",
  "modified_by": "Administrator",
  "name": "Create Payroll Period",
  "owner": "Administrator",
diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json
index 946b8c8..a7cf7bf 100644
--- a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json
+++ b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json
@@ -1,19 +1,19 @@
 {
- "action": "Go to Page",
+ "action": "Update Settings",
  "creation": "2020-06-04 16:34:29.664917",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
  "is_mandatory": 0,
- "is_single": 0,
+ "is_single": 1,
  "is_skipped": 0,
- "modified": "2020-06-04 16:34:29.664917",
+ "modified": "2020-06-29 16:34:29.664917",
  "modified_by": "Administrator",
  "name": "Payroll Settings",
  "owner": "Administrator",
- "path": "#Form/Payroll Settings",
+ "reference_document": "Payroll Settings",
  "show_full_form": 0,
  "title": "Payroll Settings",
- "validate_action": 1
+ "validate_action": 0
 }
\ No newline at end of file
diff --git a/erpnext/portal/doctype/products_settings/products_settings.js b/erpnext/portal/doctype/products_settings/products_settings.js
index b68b5d7..2f8b037 100644
--- a/erpnext/portal/doctype/products_settings/products_settings.js
+++ b/erpnext/portal/doctype/products_settings/products_settings.js
@@ -10,10 +10,12 @@
 				df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
 			).map(df => ({ label: df.label, value: df.fieldname }));
 
-			const field = frappe.meta.get_docfield("Website Filter Field", "fieldname", frm.docname);
-			field.fieldtype = 'Select';
-			field.options = valid_fields;
-			frm.fields_dict.filter_fields.grid.refresh();
+			frm.fields_dict.filter_fields.grid.update_docfield_property(
+				'fieldname', 'fieldtype', 'Select'
+			);
+			frm.fields_dict.filter_fields.grid.update_docfield_property(
+				'fieldname', 'options', valid_fields
+			);
 		});
 	}
 });
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 649eb45..ceeecb2 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -276,74 +276,3 @@
 		}
 	}
 }
-
-
-// For customizing print
-cur_frm.pformat.total = function(doc) { return ''; }
-cur_frm.pformat.discount_amount = function(doc) { return ''; }
-cur_frm.pformat.grand_total = function(doc) { return ''; }
-cur_frm.pformat.rounded_total = function(doc) { return ''; }
-cur_frm.pformat.in_words = function(doc) { return ''; }
-
-cur_frm.pformat.taxes= function(doc){
-	//function to make row of table
-	var make_row = function(title, val, bold, is_negative) {
-		var bstart = '<b>'; var bend = '</b>';
-		return '<tr><td style="width:50%;">' + (bold?bstart:'') + title + (bold?bend:'') + '</td>'
-			+ '<td style="width:50%;text-align:right;">' + (is_negative ? '- ' : '')
-		+ format_currency(val, doc.currency) + '</td></tr>';
-	}
-
-	function print_hide(fieldname) {
-		var doc_field = frappe.meta.get_docfield(doc.doctype, fieldname, doc.name);
-		return doc_field.print_hide;
-	}
-
-	out ='';
-	if (!doc.print_without_amount) {
-		var cl = doc.taxes || [];
-
-		// outer table
-		var out='<div><table class="noborder" style="width:100%"><tr><td style="width: 60%"></td><td>';
-
-		// main table
-
-		out +='<table class="noborder" style="width:100%">';
-
-		if(!print_hide('total')) {
-			out += make_row('Total', doc.total, 1);
-		}
-
-		// Discount Amount on net total
-		if(!print_hide('discount_amount') && doc.apply_discount_on == "Net Total" && doc.discount_amount)
-			out += make_row('Discount Amount', doc.discount_amount, 0, 1);
-
-		// add rows
-		if(cl.length){
-			for(var i=0;i<cl.length;i++) {
-				if(cl[i].tax_amount!=0 && !cl[i].included_in_print_rate)
-					out += make_row(cl[i].description, cl[i].tax_amount, 0);
-			}
-		}
-
-		// Discount Amount on grand total
-		if(!print_hide('discount_amount') && doc.apply_discount_on == "Grand Total" && doc.discount_amount)
-			out += make_row('Discount Amount', doc.discount_amount, 0, 1);
-
-		// grand total
-		if(!print_hide('grand_total'))
-			out += make_row('Grand Total', doc.grand_total, 1);
-
-		if(!print_hide('rounded_total'))
-			out += make_row('Rounded Total', doc.rounded_total, 1);
-
-		if(doc.in_words && !print_hide('in_words')) {
-			out +='</table></td></tr>';
-			out += '<tr><td colspan = "2">';
-			out += '<table><tr><td style="width:25%;"><b>In Words</b></td>';
-			out += '<td style="width:50%;">' + doc.in_words + '</td></tr>';
-		}
-		out += '</table></td></tr></table></div>';
-	}
-	return out;
-}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 3a3ee38..2e133be 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -323,12 +323,15 @@
 				// set precision in the last item iteration
 				if (n == me.frm.doc["items"].length - 1) {
 					me.round_off_totals(tax);
+					me.set_in_company_currency(tax,
+						["tax_amount", "tax_amount_after_discount_amount"]);
+
+					me.round_off_base_values(tax);
 
 					// in tax.total, accumulate grand total for each item
 					me.set_cumulative_total(i, tax);
 
-					me.set_in_company_currency(tax,
-						["total", "tax_amount", "tax_amount_after_discount_amount"]);
+					me.set_in_company_currency(tax, ["total"]);
 
 					// adjust Discount Amount loss in last tax iteration
 					if ((i == me.frm.doc["taxes"].length - 1) && me.discount_amount_applied
@@ -393,20 +396,11 @@
 			current_tax_amount = tax_rate * item.qty;
 		}
 
-		current_tax_amount = this.get_final_tax_amount(tax, current_tax_amount);
 		this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
 
 		return current_tax_amount;
 	},
 
-	get_final_tax_amount: function(tax, current_tax_amount) {
-		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
-			current_tax_amount = Math.round(current_tax_amount);
-		}
-
-		return current_tax_amount;
-	},
-
 	set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
 		// store tax breakup for each item
 		let tax_detail = tax.item_wise_tax_detail;
@@ -420,10 +414,22 @@
 	},
 
 	round_off_totals: function(tax) {
+		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
+			tax.tax_amount= Math.round(tax.tax_amount);
+			tax.tax_amount_after_discount_amount = Math.round(tax.tax_amount_after_discount_amount);
+		}
+
 		tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax));
 		tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax));
 	},
 
+	round_off_base_values: function(tax) {
+		if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
+			tax.base_tax_amount= Math.round(tax.base_tax_amount);
+			tax.base_tax_amount_after_discount_amount = Math.round(tax.base_tax_amount_after_discount_amount);
+		}
+	},
+
 	manipulate_grand_total_for_inclusive_tax: function() {
 		var me = this;
 		// if fully inclusive taxes and diff
diff --git a/erpnext/public/js/website_theme.js b/erpnext/public/js/website_theme.js
new file mode 100644
index 0000000..0009cac
--- /dev/null
+++ b/erpnext/public/js/website_theme.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+// MIT License. See license.txt
+
+frappe.ui.form.on('Website Theme', {
+	validate(frm) {
+		let theme_scss = frm.doc.theme_scss;
+		if (theme_scss && theme_scss.includes('frappe/public/scss/website')
+			&& !theme_scss.includes('erpnext/public/scss/website')
+		) {
+			frm.set_value('theme_scss',
+				`${frm.doc.theme_scss}\n@import "erpnext/public/scss/website";`);
+		}
+	}
+});
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
index db8bda7..68ed339 100644
--- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
+++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
@@ -8,6 +8,7 @@
   "enable",
   "section_break_2",
   "sandbox_mode",
+  "applicable_from",
   "credentials",
   "auth_token",
   "token_expiry"
@@ -48,12 +49,19 @@
    "fieldname": "sandbox_mode",
    "fieldtype": "Check",
    "label": "Sandbox Mode"
+  },
+  {
+   "fieldname": "applicable_from",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Applicable From",
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-01-13 12:04:49.449199",
+ "modified": "2021-03-30 12:26:25.538294",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "E Invoice Settings",
diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
index dd9d997..a65b1ca 100644
--- a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
+++ b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
@@ -5,6 +5,7 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
+  "company",
   "gstin",
   "username",
   "password"
@@ -30,12 +31,20 @@
    "in_list_view": 1,
    "label": "Password",
    "reqd": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-22 15:10:53.466205",
+ "modified": "2021-03-22 12:16:56.365616",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "E Invoice User",
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
index d6047e8..ac1f543 100644
--- a/erpnext/regional/germany/setup.py
+++ b/erpnext/regional/germany/setup.py
@@ -3,4 +3,17 @@
 
 
 def setup(company=None, patch=True):
-	pass
+	add_custom_roles_for_reports()
+
+
+def add_custom_roles_for_reports():
+	"""Add Access Control to UAE VAT 201."""
+	if not frappe.db.get_value('Custom Role', dict(report='DATEV')):
+		frappe.get_doc(dict(
+			doctype='Custom Role',
+			report='DATEV',
+			roles= [
+				dict(role='Accounts User'),
+				dict(role='Accounts Manager')
+			]
+		)).insert()
\ No newline at end of file
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index 378b735..faeb36f 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -69,7 +69,7 @@
  "Mizoram": "15",
  "Nagaland": "13",
  "Odisha": "21",
- "Other Territory": "98",
+ "Other Territory": "97",
  "Pondicherry": "34",
  "Punjab": "03",
  "Rajasthan": "08",
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 7cd64f2..8d682be 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -1,12 +1,13 @@
 erpnext.setup_einvoice_actions = (doctype) => {
 	frappe.ui.form.on(doctype, {
 		async refresh(frm) {
-			const einvoicing_enabled = await frappe.db.get_single_value("E Invoice Settings", "enable");
-			const supply_type = frm.doc.gst_category;
-			const valid_supply_type = ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'].includes(supply_type);
-			const company_transaction = frm.doc.billing_address_gstin == frm.doc.company_gstin;
+			const res = await frappe.call({
+				method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility',
+				args: { doc: frm.doc }
+			});
+			const invoice_eligible = res.message;
 
-			if (cint(einvoicing_enabled) == 0 || !valid_supply_type || company_transaction) return;
+			if (!invoice_eligible) return;
 
 			const { doctype, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, __unsaved } = frm.doc;
 
@@ -45,7 +46,7 @@
 						"default": "1-Duplicate",
 						"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
 					},
-					{ 
+					{
 						"label": "Remark",
 						"fieldname": "remark",
 						"fieldtype": "Data",
@@ -60,7 +61,7 @@
 							const data = d.get_values();
 							frappe.call({
 								method: 'erpnext.regional.india.e_invoice.utils.cancel_irn',
-								args: { 
+								args: {
 									doctype,
 									docname: name,
 									irn: irn,
@@ -109,45 +110,25 @@
 			}
 
 			if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
-				const fields = [
-					{
-						"label": "Reason",
-						"fieldname": "reason",
-						"fieldtype": "Select",
-						"reqd": 1,
-						"default": "1-Duplicate",
-						"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
-					},
-					{
-						"label": "Remark",
-						"fieldname": "remark",
-						"fieldtype": "Data",
-						"reqd": 1
-					}
-				];
 				const action = () => {
-					const d = new frappe.ui.Dialog({
-						title: __('Cancel E-Way Bill'),
-						fields: fields,
+					let message = __('Cancellation of e-way bill is currently not supported. ');
+					message += '<br><br>';
+					message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.');
+
+					frappe.msgprint({
+						title: __('Update E-Way Bill Cancelled Status?'),
+						message: message,
+						indicator: 'orange',
 						primary_action: function() {
-							const data = d.get_values();
 							frappe.call({
 								method: 'erpnext.regional.india.e_invoice.utils.cancel_eway_bill',
-								args: {
-									doctype,
-									docname: name,
-									eway_bill: ewaybill,
-									reason: data.reason.split('-')[0],
-									remark: data.remark
-								},
+								args: { doctype, docname: name },
 								freeze: true,
-								callback: () => frm.reload_doc() || d.hide(),
-								error: () => d.hide()
+								callback: () => frm.reload_doc()
 							});
 						},
-						primary_action_label: __('Submit')
+						primary_action_label: __('Yes')
 					});
-					d.show();
 				};
 				add_custom_button(__("Cancel E-Way Bill"), action);
 			}
@@ -254,7 +235,7 @@
 		title: __("Preview"),
 		size: "large",
 		fields: [
-			{ 
+			{
 				"label": "Preview",
 				"fieldname": "preview_html",
 				"fieldtype": "HTML"
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 3dd1b36..605f4e1 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -15,18 +15,43 @@
 import io
 from frappe import _, bold
 from pyqrcode import create as qrcreate
+from frappe.utils.background_jobs import enqueue
+from frappe.utils.scheduler import is_scheduler_inactive
+from frappe.core.page.background_jobs.background_jobs import get_info
 from frappe.integrations.utils import make_post_request, make_get_request
 from erpnext.regional.india.utils import get_gst_accounts, get_place_of_supply
-from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date, get_link_to_form
+from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date, get_link_to_form, getdate, time_diff_in_hours
 
-def validate_einvoice_fields(doc):
-	einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable'))
-	invalid_doctype = doc.doctype != 'Sales Invoice'
+@frappe.whitelist()
+def validate_eligibility(doc):
+	if isinstance(doc, six.string_types):
+		doc = json.loads(doc)
+
+	invalid_doctype = doc.get('doctype') != 'Sales Invoice'
+	if invalid_doctype:
+		return False
+
+	einvoicing_enabled = cint(frappe.db.get_single_value('E Invoice Settings', 'enable'))
+	if not einvoicing_enabled:
+		return False
+
+	einvoicing_eligible_from = frappe.db.get_single_value('E Invoice Settings', 'applicable_from') or '2021-04-01'
+	if getdate(doc.get('posting_date')) < getdate(einvoicing_eligible_from):
+		return False
+
 	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
 	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
 	no_taxes_applied = not doc.get('taxes')
 
-	if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
+	if invalid_supply_type or company_transaction or no_taxes_applied:
+		return False
+
+	return True
+
+def validate_einvoice_fields(doc):
+	invoice_eligible = validate_eligibility(doc)
+
+	if not invoice_eligible:
 		return
 
 	if doc.docstatus == 0 and doc._action == 'save':
@@ -35,6 +60,8 @@
 		if len(doc.name) > 16:
 			raise_document_name_too_long_error()
 
+		doc.einvoice_status = 'Pending'
+
 	elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
 		frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
 
@@ -76,6 +103,9 @@
 	))
 
 def get_doc_details(invoice):
+	if getdate(invoice.posting_date) < getdate('2021-01-01'):
+		frappe.throw(_('IRN generation is not allowed for invoices dated before 1st Jan 2021'), title=_('Not Allowed'))
+
 	invoice_type = 'CRN' if invoice.is_return else 'INV'
 
 	invoice_name = invoice.name
@@ -87,56 +117,39 @@
 		invoice_date=invoice_date
 	))
 
-def get_party_details(address_name, company_address=None, billing_address=None, shipping_address=None):
-	d = frappe.get_all('Address', filters={'name': address_name}, fields=['*'])[0]
-
-	if ((not d.gstin and not shipping_address)
-		or not d.city
-		or not d.pincode
-		or not d.address_title
-		or not d.address_line1
-		or not d.gst_state_number):
+def validate_address_fields(address, is_shipping_address):
+	if ((not address.gstin and not is_shipping_address)
+		or not address.city
+		or not address.pincode
+		or not address.address_title
+		or not address.address_line1
+		or not address.gst_state_number):
 
 		frappe.throw(
-			msg=_('Address lines, city, pincode, gstin is mandatory for address {}. Please set them and try again.').format(
-				get_link_to_form('Address', address_name)
-			),
+			msg=_('Address Lines, City, Pincode, GSTIN are mandatory for address {}. Please set them and try again.').format(address.name),
 			title=_('Missing Address Fields')
 		)
 
-	if d.gst_state_number == 97:
+def get_party_details(address_name, is_shipping_address=False):
+	addr = frappe.get_doc('Address', address_name)
+	
+	validate_address_fields(addr, is_shipping_address)
+
+	if addr.gst_state_number == 97:
 		# according to einvoice standard
-		pincode = 999999
+		addr.pincode = 999999
 
 	party_address_details = frappe._dict(dict(
-		legal_name=sanitize_for_json(d.address_title),
-		location=sanitize_for_json(d.city),
-		pincode=d.pincode,
-		state_code=d.gst_state_number,
-		address_line1=sanitize_for_json(d.address_line1),
-		address_line2=sanitize_for_json(d.address_line2)
+		legal_name=sanitize_for_json(addr.address_title),
+		location=sanitize_for_json(addr.city),
+		pincode=addr.pincode, gstin=addr.gstin,
+		state_code=addr.gst_state_number,
+		address_line1=sanitize_for_json(addr.address_line1),
+		address_line2=sanitize_for_json(addr.address_line2)
 	))
-	if d.gstin:
-		party_address_details.gstin = d.gstin
+
 	return party_address_details
 
-def get_gstin_details(gstin):
-	if not hasattr(frappe.local, 'gstin_cache'):
-		frappe.local.gstin_cache = {}
-
-	key = gstin
-	details = frappe.local.gstin_cache.get(key)
-	if details:
-		return details
-
-	details = frappe.cache().hget('gstin_cache', key)
-	if details:
-		frappe.local.gstin_cache[key] = details
-		return details
-
-	if not details:
-		return GSPConnector.get_gstin_details(gstin)
-
 def get_overseas_address_details(address_name):
 	address_title, address_line1, address_line2, city = frappe.db.get_value(
 		'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city']
@@ -171,10 +184,15 @@
 		item.description = sanitize_for_json(d.item_name)
 
 		item.qty = abs(item.qty)
-		item.discount_amount = 0
-		item.unit_rate = abs(item.base_net_amount / item.qty)
-		item.gross_amount = abs(item.base_net_amount)
-		item.taxable_value = abs(item.base_net_amount)
+
+		if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
+			item.discount_amount = abs(item.base_amount - item.base_net_amount)
+		else:
+			item.discount_amount = 0
+
+		item.unit_rate = abs((abs(item.taxable_value) - item.discount_amount)/ item.qty)
+		item.gross_amount = abs(item.taxable_value) + item.discount_amount
+		item.taxable_value = abs(item.taxable_value)
 
 		item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
 		item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
@@ -207,11 +225,11 @@
 		is_applicable = t.tax_amount and t.account_head in gst_accounts_list
 		if is_applicable:
 			# this contains item wise tax rate & tax amount (incl. discount)
-			item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code)
+			item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code or item.item_name)
 
 			item_tax_rate = item_tax_detail[0]
 			# item tax amount excluding discount amount
-			item_tax_amount = (item_tax_rate / 100) * item.base_net_amount
+			item_tax_amount = (item_tax_rate / 100) * item.taxable_value
 
 			if t.account_head in gst_accounts.cess_account:
 				item_tax_amount_after_discount = item_tax_detail[1]
@@ -225,6 +243,9 @@
 				if t.account_head in gst_accounts[f'{tax_type}_account']:
 					item.tax_rate += item_tax_rate
 					item[f'{tax_type}_amount'] += abs(item_tax_amount)
+		else:
+			# TODO: other charges per item
+			pass
 
 	return item
 
@@ -232,10 +253,14 @@
 	invoice_value_details = frappe._dict(dict())
 
 	if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
-		invoice_value_details.base_total = abs(invoice.base_total)
-		invoice_value_details.invoice_discount_amt = abs(invoice.base_discount_amount)
+		# Discount already applied on net total which means on items
+		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
+		invoice_value_details.invoice_discount_amt = 0
+	elif invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount:
+		invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
+		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
 	else:
-		invoice_value_details.base_total = abs(invoice.base_net_total)
+		invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
 		# since tax already considers discount amount
 		invoice_value_details.invoice_discount_amt = 0
 
@@ -256,7 +281,11 @@
 	invoice_value_details.total_igst_amt = 0
 	invoice_value_details.total_cess_amt = 0
 	invoice_value_details.total_other_charges = 0
+	considered_rows = []
+
 	for t in invoice.taxes:
+		tax_amount = t.base_tax_amount if (invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount) \
+						else t.base_tax_amount_after_discount_amount
 		if t.account_head in gst_accounts_list:
 			if t.account_head in gst_accounts.cess_account:
 				# using after discount amt since item also uses after discount amt for cess calc
@@ -264,12 +293,26 @@
 
 			for tax_type in ['igst', 'cgst', 'sgst']:
 				if t.account_head in gst_accounts[f'{tax_type}_account']:
-					invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount)
+
+					invoice_value_details[f'total_{tax_type}_amt'] += abs(tax_amount)
+				update_other_charges(t, invoice_value_details, gst_accounts_list, invoice, considered_rows)
 		else:
-			invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount)
+			invoice_value_details.total_other_charges += abs(tax_amount)
 
 	return invoice_value_details
 
+def update_other_charges(tax_row, invoice_value_details, gst_accounts_list, invoice, considered_rows):
+	prev_row_id = cint(tax_row.row_id) - 1
+	if tax_row.account_head in gst_accounts_list and prev_row_id not in considered_rows:
+		if tax_row.charge_type == 'On Previous Row Amount':
+			amount = invoice.get('taxes')[prev_row_id].tax_amount_after_discount_amount
+			invoice_value_details.total_other_charges -= abs(amount)
+			considered_rows.append(prev_row_id)
+		if tax_row.charge_type == 'On Previous Row Total':
+			amount = invoice.get('taxes')[prev_row_id].base_total - invoice.base_net_total
+			invoice_value_details.total_other_charges -= abs(amount)
+			considered_rows.append(prev_row_id)
+
 def get_payment_details(invoice):
 	payee_name = invoice.company
 	mode_of_payment = ', '.join([d.mode_of_payment for d in invoice.payments])
@@ -282,6 +325,10 @@
 	))
 
 def get_return_doc_reference(invoice):
+	if not invoice.return_against:
+		frappe.throw(_('For generating IRN, reference to the original invoice is mandatory for a credit note. Please set {} field to generate e-invoice.')
+			.format(frappe.bold('Return Against')), title=_('Missing Field'))
+
 	invoice_date = frappe.db.get_value('Sales Invoice', invoice.return_against, 'posting_date')
 	return frappe._dict(dict(
 		invoice_name=invoice.return_against, invoice_date=format_date(invoice_date, 'dd/mm/yyyy')
@@ -289,7 +336,11 @@
 
 def get_eway_bill_details(invoice):
 	if invoice.is_return:
-		frappe.throw(_('E-Way Bill cannot be generated for Credit Notes & Debit Notes'), title=_('E Invoice Validation Failed'))
+		frappe.throw(_('E-Way Bill cannot be generated for Credit Notes & Debit Notes. Please clear fields in the Transporter Section of the invoice.'),
+			title=_('Invalid Fields'))
+	
+	if not invoice.distance:
+		frappe.throw(_('Distance is mandatory for generating e-way bill for an e-invoice.'), title=_('Missing Field'))
 
 	mode_of_transport = { '': '', 'Road': '1', 'Air': '2', 'Rail': '3', 'Ship': '4' }
 	vehicle_type = { 'Regular': 'R', 'Over Dimensional Cargo (ODC)': 'O' }
@@ -307,9 +358,15 @@
 
 def validate_mandatory_fields(invoice):
 	if not invoice.company_address:
-		frappe.throw(_('Company Address is mandatory to fetch company GSTIN details.'), title=_('Missing Fields'))
+		frappe.throw(
+			_('Company Address is mandatory to fetch company GSTIN details. Please set Company Address and try again.'),
+			title=_('Missing Fields')
+		)
 	if not invoice.customer_address:
-		frappe.throw(_('Customer Address is mandatory to fetch customer GSTIN details.'), title=_('Missing Fields'))
+		frappe.throw(
+			_('Customer Address is mandatory to fetch customer GSTIN details. Please set Company Address and try again.'),
+			title=_('Missing Fields')
+		)
 	if not frappe.db.get_value('Address', invoice.company_address, 'gstin'):
 		frappe.throw(
 			_('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
@@ -321,6 +378,39 @@
 			title=_('Missing Fields')
 		)
 
+def validate_totals(einvoice):
+	item_list = einvoice['ItemList']
+	value_details = einvoice['ValDtls']
+
+	total_item_ass_value = 0
+	total_item_cgst_value = 0
+	total_item_sgst_value = 0
+	total_item_igst_value = 0
+	total_item_value = 0
+	for item in item_list:
+		total_item_ass_value += flt(item['AssAmt'])
+		total_item_cgst_value += flt(item['CgstAmt'])
+		total_item_sgst_value += flt(item['SgstAmt'])
+		total_item_igst_value += flt(item['IgstAmt'])
+		total_item_value += flt(item['TotItemVal'])
+
+		if abs(flt(item['AssAmt']) * flt(item['GstRt']) / 100) - (flt(item['CgstAmt']) + flt(item['SgstAmt']) + flt(item['IgstAmt'])) > 1:
+			frappe.throw(_('Row #{}: GST rate is invalid. Please remove tax rows with zero tax amount from taxes table.').format(item.idx))
+
+	if abs(flt(value_details['AssVal']) - total_item_ass_value) > 1:
+		frappe.throw(_('Total Taxable Value of the items is not equal to the Invoice Net Total. Please check item taxes / discounts for any correction.'))
+
+	if abs(flt(value_details['TotInvVal']) + flt(value_details['Discount']) - total_item_value) > 1:
+		frappe.throw(_('Total Value of the items is not equal to the Invoice Grand Total. Please check item taxes / discounts for any correction.'))
+
+	calculated_invoice_value = \
+			flt(value_details['AssVal']) + flt(value_details['CgstVal']) \
+			+ flt(value_details['SgstVal']) + flt(value_details['IgstVal']) \
+			+ flt(value_details['OthChrg']) - flt(value_details['Discount'])
+
+	if abs(flt(value_details['TotInvVal']) - calculated_invoice_value) > 1:
+		frappe.throw(_('Total Item Value + Taxes - Discount is not equal to the Invoice Grand Total. Please check taxes / discounts for any correction.'))
+
 def make_einvoice(invoice):
 	validate_mandatory_fields(invoice)
 
@@ -330,12 +420,12 @@
 	item_list = get_item_list(invoice)
 	doc_details = get_doc_details(invoice)
 	invoice_value_details = get_invoice_value_details(invoice)
-	seller_details = get_party_details(invoice.company_address, company_address=1)
+	seller_details = get_party_details(invoice.company_address)
 
 	if invoice.gst_category == 'Overseas':
 		buyer_details = get_overseas_address_details(invoice.customer_address)
 	else:
-		buyer_details = get_party_details(invoice.customer_address, billing_address=1)
+		buyer_details = get_party_details(invoice.customer_address)
 		place_of_supply = get_place_of_supply(invoice, invoice.doctype)
 		if place_of_supply:
 			place_of_supply = place_of_supply.split('-')[0]
@@ -343,20 +433,23 @@
 			place_of_supply = sanitize_for_json(invoice.billing_address_gstin)[:2]
 		buyer_details.update(dict(place_of_supply=place_of_supply))
 
+	seller_details.update(dict(legal_name=invoice.company))
+	buyer_details.update(dict(legal_name=invoice.customer_name or invoice.customer))
+
 	shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
 	if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
 		if invoice.gst_category == 'Overseas':
 			shipping_details = get_overseas_address_details(invoice.shipping_address_name)
 		else:
-			shipping_details = get_party_details(invoice.shipping_address_name, shipping_address=1)
+			shipping_details = get_party_details(invoice.shipping_address_name, is_shipping_address=True)
 
 	if invoice.is_pos and invoice.base_paid_amount:
 		payment_details = get_payment_details(invoice)
 
-	if invoice.is_return and invoice.return_against:
+	if invoice.is_return:
 		prev_doc_details = get_return_doc_reference(invoice)
 
-	if invoice.transporter:
+	if invoice.transporter and flt(invoice.distance) and not invoice.is_return:
 		eway_bill_details = get_eway_bill_details(invoice)
 
 	# not yet implemented
@@ -369,18 +462,70 @@
 		period_details=period_details, prev_doc_details=prev_doc_details,
 		export_details=export_details, eway_bill_details=eway_bill_details
 	)
-	einvoice = safe_json_load(einvoice)
 
-	validations = json.loads(read_json('einv_validation'))
-	errors = validate_einvoice(validations, einvoice)
-	if errors:
-		message = "\n".join([
-			"E Invoice: ", json.dumps(einvoice, indent=4),
-			"-" * 50,
-			"Errors: ", json.dumps(errors, indent=4)
-		])
-		frappe.log_error(title="E Invoice Validation Failed", message=message)
-		frappe.throw(errors, title=_('E Invoice Validation Failed'), as_list=1)
+	try:
+		einvoice = safe_json_load(einvoice)
+		einvoice = santize_einvoice_fields(einvoice)
+		validate_totals(einvoice)
+
+	except Exception:
+		log_error(einvoice)
+		link_to_error_list = '<a href="List/Error Log/List?method=E Invoice Request Failed">Error Log</a>'
+		frappe.throw(
+			_('An error occurred while creating e-invoice for {}. Please check {} for more information.').format(
+				invoice.name, link_to_error_list),
+			title=_('E Invoice Creation Failed')
+		)
+
+	return einvoice
+
+def log_error(data=None):
+	if not isinstance(data, dict):
+		data = json.loads(data)
+
+	seperator = "--" * 50
+	err_tb = traceback.format_exc()
+	err_msg = str(sys.exc_info()[1])
+	data = json.dumps(data, indent=4)
+
+	message = "\n".join([
+		"Error", err_msg, seperator,
+		"Data:", data, seperator,
+		"Exception:", err_tb
+	])
+	frappe.log_error(title=_('E Invoice Request Failed'), message=message)
+
+def santize_einvoice_fields(einvoice):
+	int_fields = ["Pin","Distance","CrDay"]
+	float_fields = ["Qty","FreeQty","UnitPrice","TotAmt","Discount","PreTaxVal","AssAmt","GstRt","IgstAmt","CgstAmt","SgstAmt","CesRt","CesAmt","CesNonAdvlAmt","StateCesRt","StateCesAmt","StateCesNonAdvlAmt","OthChrg","TotItemVal","AssVal","CgstVal","SgstVal","IgstVal","CesVal","StCesVal","Discount","OthChrg","RndOffAmt","TotInvVal","TotInvValFc","PaidAmt","PaymtDue","ExpDuty",]
+	copy = einvoice.copy()
+	for key, value in copy.items():
+		if isinstance(value, list):
+			for idx, d in enumerate(value):
+				santized_dict = santize_einvoice_fields(d)
+				if santized_dict:
+					einvoice[key][idx] = santized_dict
+				else:
+					einvoice[key].pop(idx)
+
+			if not einvoice[key]:
+				einvoice.pop(key, None)
+
+		elif isinstance(value, dict):
+			santized_dict = santize_einvoice_fields(value)
+			if santized_dict:
+				einvoice[key] = santized_dict
+			else:
+				einvoice.pop(key, None)
+
+		elif not value or value == "None":
+			einvoice.pop(key, None)
+
+		elif key in float_fields:
+			einvoice[key] = flt(value, 2)
+
+		elif key in int_fields:
+			einvoice[key] = cint(value)
 
 	return einvoice
 
@@ -396,72 +541,22 @@
 		snippet = json_string[start:end]
 		frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
 
-def validate_einvoice(validations, einvoice, errors=None):
-	if errors is None:
-		errors = []
-	for fieldname, field_validation in validations.items():
-		value = einvoice.get(fieldname, None)
-		if not value or value == "None":
-			# remove keys with empty values
-			einvoice.pop(fieldname, None)
-			continue
-
-		value_type = field_validation.get("type").lower()
-		if value_type in ['object', 'array']:
-			child_validations = field_validation.get('properties')
-
-			if isinstance(value, list):
-				for d in value:
-					validate_einvoice(child_validations, d, errors)
-					if not d:
-						# remove empty dicts
-						einvoice.pop(fieldname, None)
-			else:
-				validate_einvoice(child_validations, value, errors)
-				if not value:
-					# remove empty dicts
-					einvoice.pop(fieldname, None)
-			continue
-
-		# convert to int or str
-		if value_type == 'string':
-			einvoice[fieldname] = str(value)
-		elif value_type == 'number':
-			is_integer = '.' not in str(field_validation.get('maximum'))
-			precision = 3 if '.999' in str(field_validation.get('maximum')) else 2
-			einvoice[fieldname] = flt(value, precision) if not is_integer else cint(value)
-			value = einvoice[fieldname]
-
-		max_length = field_validation.get('maxLength')
-		minimum = flt(field_validation.get('minimum'))
-		maximum = flt(field_validation.get('maximum'))
-		pattern_str = field_validation.get('pattern')
-		pattern = re.compile(pattern_str or '')
-
-		label = field_validation.get('description') or fieldname
-
-		if value_type == 'string' and len(value) > max_length:
-			errors.append(_('{} should not exceed {} characters').format(label, max_length))
-		if value_type == 'number' and (value > maximum or value < minimum):
-			errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum))
-		if pattern_str and not pattern.match(value):
-			errors.append(field_validation.get('validationMsg'))
-
-	return errors
-
-class RequestFailed(Exception): pass
+class RequestFailed(Exception):
+	pass
+class CancellationNotAllowed(Exception):
+	pass
 
 class GSPConnector():
 	def __init__(self, doctype=None, docname=None):
-		self.e_invoice_settings = frappe.get_cached_doc('E Invoice Settings')
-		sandbox_mode = self.e_invoice_settings.sandbox_mode
+		self.doctype = doctype
+		self.docname = docname
 
-		self.invoice = frappe.get_cached_doc(doctype, docname) if doctype and docname else None
-		self.credentials = self.get_credentials()
+		self.set_invoice()
+		self.set_credentials()
 
 		# authenticate url is same for sandbox & live
 		self.authenticate_url = 'https://gsp.adaequare.com/gsp/authenticate?grant_type=token'
-		self.base_url = 'https://gsp.adaequare.com' if not sandbox_mode else 'https://gsp.adaequare.com/test'
+		self.base_url = 'https://gsp.adaequare.com' if not self.e_invoice_settings.sandbox_mode else 'https://gsp.adaequare.com/test'
 
 		self.cancel_irn_url = self.base_url + '/enriched/ei/api/invoice/cancel'
 		self.irn_details_url = self.base_url + '/enriched/ei/api/invoice/irn'
@@ -470,15 +565,26 @@
 		self.cancel_ewaybill_url = self.base_url + '/enriched/ewb/ewayapi?action=CANEWB'
 		self.generate_ewaybill_url = self.base_url + '/enriched/ei/api/ewaybill'
 
-	def get_credentials(self):
+	def set_invoice(self):
+		self.invoice = None
+		if self.doctype and self.docname:
+			self.invoice = frappe.get_cached_doc(self.doctype, self.docname)
+
+	def set_credentials(self):
+		self.e_invoice_settings = frappe.get_cached_doc('E Invoice Settings')
+
+		if not self.e_invoice_settings.enable:
+			frappe.throw(_("E-Invoicing is disabled. Please enable it from {} to generate e-invoices.").format(get_link_to_form("E Invoice Settings", "E Invoice Settings")))
+
 		if self.invoice:
 			gstin = self.get_seller_gstin()
-			if not self.e_invoice_settings.enable:
-				frappe.throw(_("E-Invoicing is disabled. Please enable it from {} to generate e-invoices.").format(get_link_to_form("E Invoice Settings", "E Invoice Settings")))
-			credentials = next(d for d in self.e_invoice_settings.credentials if d.gstin == gstin)
+			credentials_for_gstin = [d for d in self.e_invoice_settings.credentials if d.gstin == gstin]
+			if credentials_for_gstin:
+				self.credentials = credentials_for_gstin[0]
+			else:
+				frappe.throw(_('Cannot find e-invoicing credentials for selected Company GSTIN. Please check E-Invoice Settings'))
 		else:
-			credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
-		return credentials
+			self.credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
 
 	def get_seller_gstin(self):
 		gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
@@ -529,7 +635,7 @@
 			self.e_invoice_settings.reload()
 
 		except Exception:
-			self.log_error(res)
+			log_error(res)
 			self.raise_error(True)
 
 	def get_headers(self):
@@ -551,16 +657,15 @@
 			if res.get('success'):
 				return res.get('result')
 			else:
-				self.log_error(res)
+				log_error(res)
 				raise RequestFailed
 
 		except RequestFailed:
 			self.raise_error()
 
 		except Exception:
-			self.log_error()
+			log_error()
 			self.raise_error(True)
-
 	@staticmethod
 	def get_gstin_details(gstin):
 		'''fetch and cache GSTIN details'''
@@ -576,12 +681,13 @@
 		return details
 
 	def generate_irn(self):
-		headers = self.get_headers()
-		einvoice = make_einvoice(self.invoice)
-		data = json.dumps(einvoice, indent=4)
-
+		data = {}
 		try:
+			headers = self.get_headers()
+			einvoice = make_einvoice(self.invoice)
+			data = json.dumps(einvoice, indent=4)
 			res = self.make_request('post', self.generate_irn_url, headers, data)
+
 			if res.get('success'):
 				self.set_einvoice_data(res.get('result'))
 
@@ -601,12 +707,36 @@
 
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
+			self.set_failed_status(errors=errors)
 			self.raise_error(errors=errors)
 
-		except Exception:
-			self.log_error(data)
+		except Exception as e:
+			self.set_failed_status(errors=str(e))
+			log_error(data)
 			self.raise_error(True)
 
+	@staticmethod
+	def bulk_generate_irn(invoices):
+		gsp_connector = GSPConnector()
+		gsp_connector.doctype = 'Sales Invoice'
+
+		failed = []
+
+		for invoice in invoices:
+			try:
+				gsp_connector.docname = invoice
+				gsp_connector.set_invoice()
+				gsp_connector.set_credentials()
+				gsp_connector.generate_irn()
+
+			except Exception as e:
+				failed.append({
+					'docname': invoice,
+					'message': str(e)
+				})
+
+		return failed
+
 	def get_irn_details(self, irn):
 		headers = self.get_headers()
 
@@ -623,21 +753,30 @@
 			self.raise_error(errors=errors)
 
 		except Exception:
-			self.log_error()
+			log_error()
 			self.raise_error(True)
 
 	def cancel_irn(self, irn, reason, remark):
-		headers = self.get_headers()
-		data = json.dumps({
-			'Irn': irn,
-			'Cnlrsn': reason,
-			'Cnlrem': remark
-		}, indent=4)
-
+		data, res = {}, {}
 		try:
+			# validate cancellation
+			if time_diff_in_hours(now_datetime(), self.invoice.ack_date) > 24:
+				frappe.throw(_('E-Invoice cannot be cancelled after 24 hours of IRN generation.'), title=_('Not Allowed'), exc=CancellationNotAllowed)
+			if not irn:
+				frappe.throw(_('IRN not found. You must generate IRN before cancelling.'), title=_('Not Allowed'), exc=CancellationNotAllowed)
+
+			headers = self.get_headers()
+			data = json.dumps({
+				'Irn': irn,
+				'Cnlrsn': reason,
+				'Cnlrem': remark
+			}, indent=4)
+
 			res = self.make_request('post', self.cancel_irn_url, headers, data)
-			if res.get('success'):
+			if res.get('success') or '9999' in res.get('message'):
 				self.invoice.irn_cancelled = 1
+				self.invoice.irn_cancel_date = res.get('result')['CancelDate'] if res.get('result') else ""
+				self.invoice.einvoice_status = 'Cancelled'
 				self.invoice.flags.updater_reference = {
 					'doctype': self.invoice.doctype,
 					'docname': self.invoice.name,
@@ -650,12 +789,41 @@
 
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
+			self.set_failed_status(errors=errors)
 			self.raise_error(errors=errors)
 
-		except Exception:
-			self.log_error(data)
+		except CancellationNotAllowed as e:
+			self.set_failed_status(errors=str(e))
+			self.raise_error(errors=str(e))
+
+		except Exception as e:
+			self.set_failed_status(errors=str(e))
+			log_error(data)
 			self.raise_error(True)
 
+	@staticmethod
+	def bulk_cancel_irn(invoices, reason, remark):
+		gsp_connector = GSPConnector()
+		gsp_connector.doctype = 'Sales Invoice'
+
+		failed = []
+
+		for invoice in invoices:
+			try:
+				gsp_connector.docname = invoice
+				gsp_connector.set_invoice()
+				gsp_connector.set_credentials()
+				irn = gsp_connector.invoice.irn
+				gsp_connector.cancel_irn(irn, reason, remark)
+
+			except Exception as e:
+				failed.append({
+					'docname': invoice,
+					'message': str(e)
+				})
+
+		return failed
+
 	def generate_eway_bill(self, **kwargs):
 		args = frappe._dict(kwargs)
 
@@ -694,7 +862,7 @@
 			self.raise_error(errors=errors)
 
 		except Exception:
-			self.log_error(data)
+			log_error(data)
 			self.raise_error(True)
 
 	def cancel_eway_bill(self, eway_bill, reason, remark):
@@ -726,7 +894,7 @@
 			self.raise_error(errors=errors)
 
 		except Exception:
-			self.log_error(data)
+			log_error(data)
 			self.raise_error(True)
 
 	def sanitize_error_message(self, message):
@@ -741,6 +909,9 @@
 			]
 			then we trim down the message by looping over errors
 		'''
+		if not message:
+			return []
+
 		errors = re.findall(': [^:]+', message)
 		for idx, e in enumerate(errors):
 			# remove colons
@@ -752,22 +923,6 @@
 
 		return errors
 
-	def log_error(self, data={}):
-		if not isinstance(data, dict):
-			data = json.loads(data)
-
-		seperator = "--" * 50
-		err_tb = traceback.format_exc()
-		err_msg = str(sys.exc_info()[1])
-		data = json.dumps(data, indent=4)
-
-		message = "\n".join([
-			"Error", err_msg, seperator,
-			"Data:", data, seperator,
-			"Exception:", err_tb
-		])
-		frappe.log_error(title=_('E Invoice Request Failed'), message=message)
-
 	def raise_error(self, raise_exception=False, errors=[]):
 		title = _('E Invoice Request Failed')
 		if errors:
@@ -790,7 +945,10 @@
 		self.invoice.ack_no = res.get('AckNo')
 		self.invoice.ack_date = res.get('AckDt')
 		self.invoice.signed_einvoice = dec_signed_invoice
+		self.invoice.ack_no = res.get('AckNo')
+		self.invoice.ack_date = res.get('AckDt')
 		self.invoice.signed_qr_code = res.get('SignedQRCode')
+		self.invoice.einvoice_status = 'Generated'
 
 		self.attach_qrcode_image()
 
@@ -800,7 +958,6 @@
 			'label': _('IRN Generated')
 		}
 		self.update_invoice()
-
 	def attach_qrcode_image(self):
 		qrcode = self.invoice.signed_qr_code
 		doctype = self.invoice.doctype
@@ -827,6 +984,17 @@
 		self.invoice.flags.ignore_validate = True
 		self.invoice.save()
 
+	def set_failed_status(self, errors=None):
+		frappe.db.rollback()
+		self.invoice.einvoice_status = 'Failed'
+		self.invoice.failure_description = self.get_failure_message(errors) if errors else ""
+		self.update_invoice()
+		frappe.db.commit()
+	
+	def get_failure_message(self, errors):
+		if isinstance(errors, list):
+			errors = ', '.join(errors)
+		return errors
 
 def sanitize_for_json(string):
 	"""Escape JSON specific characters from a string."""
@@ -856,5 +1024,114 @@
 
 @frappe.whitelist()
 def cancel_eway_bill(doctype, docname, eway_bill, reason, remark):
-	gsp_connector = GSPConnector(doctype, docname)
-	gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
+	# TODO: uncomment when eway_bill api from Adequare is enabled
+	# gsp_connector = GSPConnector(doctype, docname)
+	# gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
+
+	# update cancelled status only, to be able to cancel irn next
+	frappe.db.set_value(doctype, docname, 'eway_bill_cancelled', 1)
+
+@frappe.whitelist()
+def generate_einvoices(docnames):
+	docnames = json.loads(docnames) or []
+
+	if len(docnames) < 10:
+		failures = GSPConnector.bulk_generate_irn(docnames)
+		frappe.local.message_log = []
+
+		if failures:
+			show_bulk_action_failure_message(failures)
+
+		success = len(docnames) - len(failures)
+		frappe.msgprint(
+			_('{} e-invoices generated successfully').format(success),
+			title=_('Bulk E-Invoice Generation Complete')
+		)
+			
+	else:
+		enqueue_bulk_action(schedule_bulk_generate_irn, docnames=docnames)
+
+def schedule_bulk_generate_irn(docnames):
+	failures = GSPConnector.bulk_generate_irn(docnames)
+	frappe.local.message_log = []
+
+	frappe.publish_realtime("bulk_einvoice_generation_complete", {
+		"user": frappe.session.user,
+		"failures": failures,
+		"invoices": docnames
+	})
+
+def show_bulk_action_failure_message(failures):
+	for doc in failures:
+		docname = '<a href="sales-invoice/{0}">{0}</a>'.format(doc.get('docname'))
+		message = doc.get('message').replace("'", '"')
+		if message[0] == '[':
+			errors = json.loads(message)
+			error_list = ''.join(['<li>{}</li>'.format(err) for err in errors])
+			message = '''{} has following errors:<br>
+				<ul style="padding-left: 20px; padding-top: 5px">{}</ul>'''.format(docname, error_list)
+		else:
+			message = '{} - {}'.format(docname, message)
+
+		frappe.msgprint(
+			message,
+			title=_('Bulk E-Invoice Generation Complete'),
+			indicator='red'
+		)
+
+@frappe.whitelist()
+def cancel_irns(docnames, reason, remark):
+	docnames = json.loads(docnames) or []
+
+	if len(docnames) < 10:
+		failures = GSPConnector.bulk_cancel_irn(docnames, reason, remark)
+		frappe.local.message_log = []
+
+		if failures:
+			show_bulk_action_failure_message(failures)
+
+		success = len(docnames) - len(failures)
+		frappe.msgprint(
+			_('{} e-invoices cancelled successfully').format(success),
+			title=_('Bulk E-Invoice Cancellation Complete')
+		)
+	else:
+		enqueue_bulk_action(schedule_bulk_cancel_irn, docnames=docnames, reason=reason, remark=remark)
+
+def schedule_bulk_cancel_irn(docnames, reason, remark):
+	failures = GSPConnector.bulk_cancel_irn(docnames, reason, remark)
+	frappe.local.message_log = []
+
+	frappe.publish_realtime("bulk_einvoice_cancellation_complete", {
+		"user": frappe.session.user,
+		"failures": failures,
+		"invoices": docnames
+	})
+
+def enqueue_bulk_action(job, **kwargs):
+	check_scheduler_status()
+
+	enqueue(
+		job,
+		**kwargs,
+		queue="long",
+		timeout=10000,
+		event="processing_bulk_einvoice_action",
+		now=frappe.conf.developer_mode or frappe.flags.in_test,
+	)
+
+	if job == schedule_bulk_generate_irn:
+		msg = _('E-Invoices will be generated in a background process.')
+	else:
+		msg = _('E-Invoices will be cancelled in a background process.')
+
+	frappe.msgprint(msg, alert=1)
+
+def check_scheduler_status():
+	if is_scheduler_inactive() and not frappe.flags.in_test:
+		frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
+
+def job_already_enqueued(job_name):
+	enqueued_jobs = [d.get("job_name") for d in get_info()]
+	if job_name in enqueued_jobs:
+		return True
\ No newline at end of file
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index f7689cf..9ded8da 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -12,14 +12,14 @@
 from frappe.utils import today
 
 def setup(company=None, patch=True):
-	setup_company_independent_fixtures()
+	setup_company_independent_fixtures(patch=patch)
 	if not patch:
 		make_fixtures(company)
 
 # TODO: for all countries
-def setup_company_independent_fixtures():
+def setup_company_independent_fixtures(patch=False):
 	make_custom_fields()
-	make_property_setters()
+	make_property_setters(patch=patch)
 	add_permissions()
 	add_custom_roles_for_reports()
 	frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
@@ -51,7 +51,7 @@
 
 def add_custom_roles_for_reports():
 	for report_name in ('GST Sales Register', 'GST Purchase Register',
-		'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill'):
+		'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill', 'E-Invoice Summary'):
 
 		if not frappe.db.get_value('Custom Role', dict(report=report_name)):
 			frappe.get_doc(dict(
@@ -112,10 +112,11 @@
 	frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0)
 	frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0)
 
-def make_property_setters():
+def make_property_setters(patch=False):
 	# GST rules do not allow for an invoice no. bigger than 16 characters
-	make_property_setter('Sales Invoice', 'naming_series', 'options', 'SINV-.YY.-\nSRET-.YY.-', '')
-	make_property_setter('Purchase Invoice', 'naming_series', 'options', 'PINV-.YY.-\nPRET-.YY.-', '')
+	if not patch:
+		make_property_setter('Sales Invoice', 'naming_series', 'options', 'SINV-.YY.-\nSRET-.YY.-', '')
+		make_property_setter('Purchase Invoice', 'naming_series', 'options', 'PINV-.YY.-\nPRET-.YY.-', '')
 
 def make_custom_fields(update=True):
 	hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
@@ -127,6 +128,9 @@
 	is_non_gst = dict(fieldname='is_non_gst', label='Is Non GST',
 		fieldtype='Check', fetch_from='item_code.is_non_gst', insert_after='is_nil_exempt',
 		print_hide=1)
+	taxable_value = dict(fieldname='taxable_value', label='Taxable Value',
+		fieldtype='Currency', insert_after='base_net_amount', hidden=1, options="Company:company:default_currency",
+		print_hide=1)
 
 	purchase_invoice_gst_category = [
 		dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break',
@@ -156,6 +160,13 @@
 			fetch_if_empty=1),
 	]
 
+	delivery_note_gst_category = [
+		dict(fieldname='gst_category', label='GST Category',
+			fieldtype='Select', insert_after='gst_vehicle_type', print_hide=1,
+			options='\nRegistered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
+			fetch_from='customer.gst_category', fetch_if_empty=1),
+	]
+
 	invoice_gst_fields = [
 		dict(fieldname='invoice_copy', label='Invoice Copy',
 			fieldtype='Select', insert_after='export_type', print_hide=1, allow_on_submit=1,
@@ -280,7 +291,7 @@
 			'allow_on_submit': 1,
 			'insert_after': 'customer_name_in_arabic',
 			'translatable': 0,
-    	}
+		}
 	]
 
 	si_ewaybill_fields = [
@@ -408,21 +419,37 @@
 		dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1,
 			depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
 
-		dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='irn', no_copy=1, print_hide=1),
-
-		dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
-
 		dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
 			depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
 
 		dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
 			depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
 
-		dict(fieldname='signed_einvoice', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1),
+		dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
+			print_hide=1, hidden=1),
+		
+		dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
+			no_copy=1, print_hide=1),
+		
+		dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
 
-		dict(fieldname='signed_qr_code', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1),
+		dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date', 
+			no_copy=1, print_hide=1),
 
-		dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, no_copy=1, print_hide=1, read_only=1)
+		dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
+			no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
+			no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
+			no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
+			options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
+			hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
 	]
 
 	custom_fields = {
@@ -438,7 +465,7 @@
 		'Purchase Order': purchase_invoice_gst_fields,
 		'Purchase Receipt': purchase_invoice_gst_fields,
 		'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields,
-		'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields,
+		'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category,
 		'Sales Order': sales_invoice_gst_fields,
 		'Tax Category': inter_state_gst_field,
 		'Item': [
@@ -453,7 +480,7 @@
 		'Supplier Quotation Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
 		'Sales Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
 		'Delivery Note Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
-		'Sales Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
+		'Sales Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
 		'Purchase Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
 		'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
 		'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
diff --git a/erpnext/regional/india/test_utils.py b/erpnext/regional/india/test_utils.py
index 7ce27f6..a16f56c 100644
--- a/erpnext/regional/india/test_utils.py
+++ b/erpnext/regional/india/test_utils.py
@@ -12,14 +12,14 @@
 		mock_get_cached.return_value = "India"  # mock country
 		posting_date = "2021-05-01"
 
-		invalid_names = [ "SI$1231", "012345678901234567", "SI 2020 05", 
-				"SI.2020.0001", "PI2021 - 001" ]
+		invalid_names = ["SI$1231", "012345678901234567", "SI 2020 05",
+				"SI.2020.0001", "PI2021 - 001"]
 		for name in invalid_names:
 			doc = frappe._dict(name=name, posting_date=posting_date)
 			self.assertRaises(frappe.ValidationError, validate_document_name, doc)
 
-		valid_names = [ "012345678901236", "SI/2020/0001", "SI/2020-0001",
-			"2020-PI-0001", "PI2020-0001" ]
+		valid_names = ["012345678901236", "SI/2020/0001", "SI/2020-0001",
+			"2020-PI-0001", "PI2020-0001"]
 		for name in valid_names:
 			doc = frappe._dict(name=name, posting_date=posting_date)
 			try:
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 3637de4..6338056 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -2,7 +2,7 @@
 import frappe, re, json
 from frappe import _
 import erpnext
-from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate
+from frappe.utils import cstr, flt, cint, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate
 from erpnext.regional.india import states, state_numbers
 from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
 from erpnext.controllers.accounts_controller import get_taxes_and_charges
@@ -41,24 +41,25 @@
 		return
 
 	if len(doc.gstin) != 15:
-		frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters."))
+		frappe.throw(_("A GSTIN must have 15 characters."), title=_("Invalid GSTIN"))
 
 	if gst_category and gst_category == 'UIN Holders':
 		if not GSTIN_UIN_FORMAT.match(doc.gstin):
-			frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"))
+			frappe.throw(_("The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"),
+				title=_("Invalid GSTIN"))
 	else:
 		if not GSTIN_FORMAT.match(doc.gstin):
-			frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))
+			frappe.throw(_("The input you've entered doesn't match the format of GSTIN."), title=_("Invalid GSTIN"))
 
 		validate_gstin_check_digit(doc.gstin)
 		set_gst_state_and_state_number(doc)
 
 		if not doc.gst_state:
-			frappe.throw(_("Please Enter GST state"))
+			frappe.throw(_("Please enter GST state"), title=_("Invalid State"))
 
 		if doc.gst_state_number != doc.gstin[:2]:
-			frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
-				.format(doc.gst_state_number))
+			frappe.throw(_("First 2 digits of GSTIN should match with State number {0}.")
+				.format(doc.gst_state_number), title=_("Invalid GSTIN"))
 
 def validate_pan_for_india(doc, method):
 	if doc.get('country') != 'India' or not doc.pan:
@@ -154,6 +155,7 @@
 
 def validate_document_name(doc, method=None):
 	"""Validate GST invoice number requirements."""
+
 	country = frappe.get_cached_value("Company", doc.company, "country")
 
 	# Date was chosen as start of next FY to avoid irritating current users.
@@ -832,3 +834,48 @@
 	account_list.extend(gst_account_list)
 
 	return account_list
+
+def update_taxable_values(doc, method):
+	country = frappe.get_cached_value('Company', doc.company, 'country')
+
+	if country != 'India':
+		return
+
+	gst_accounts = get_gst_accounts(doc.company)
+
+	# Only considering sgst account to avoid inflating taxable value
+	gst_account_list = gst_accounts.get('sgst_account', []) + gst_accounts.get('sgst_account', []) \
+		+ gst_accounts.get('igst_account', [])
+
+	additional_taxes = 0
+	total_charges = 0
+	item_count = 0
+	considered_rows = []
+
+	for tax in doc.get('taxes'):
+		prev_row_id = cint(tax.row_id) - 1
+		if tax.account_head in gst_account_list and prev_row_id not in considered_rows:
+			if tax.charge_type == 'On Previous Row Amount':
+				additional_taxes += doc.get('taxes')[prev_row_id].tax_amount_after_discount_amount
+				considered_rows.append(prev_row_id)
+			if tax.charge_type == 'On Previous Row Total':
+				additional_taxes += doc.get('taxes')[prev_row_id].base_total - doc.base_net_total
+				considered_rows.append(prev_row_id)
+
+	for item in doc.get('items'):
+		if doc.apply_discount_on == 'Grand Total' and doc.discount_amount:
+			proportionate_value = item.base_amount if doc.base_total else item.qty
+			total_value = doc.base_total if doc.base_total else doc.total_qty
+		else:
+			proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
+			total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
+
+		applicable_charges = flt(flt(proportionate_value * (flt(additional_taxes) / flt(total_value)),
+			item.precision('taxable_value')))
+		item.taxable_value = applicable_charges + proportionate_value
+		total_charges += applicable_charges
+		item_count += 1
+
+	if total_charges != additional_taxes:
+		diff = additional_taxes - total_charges
+		doc.get('items')[item_count - 1].taxable_value += diff
diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py
index a1f5bb9..7db2f6b 100644
--- a/erpnext/regional/italy/setup.py
+++ b/erpnext/regional/italy/setup.py
@@ -139,6 +139,9 @@
 			dict(fieldname='customer_fiscal_code', label='Customer Fiscal Code',
 				fieldtype='Data', insert_after='cb_e_invoicing_reference', read_only=1,
 				fetch_from="customer.fiscal_code"),
+			dict(fieldname='type_of_document', label='Type of Document',
+				fieldtype='Select', insert_after='customer_fiscal_code',
+				options='\nTD01\nTD02\nTD03\nTD04\nTD05\nTD06\nTD16\nTD17\nTD18\nTD19\nTD20\nTD21\nTD22\nTD23\nTD24\nTD25\nTD26\nTD27'),
 		],
 		'Purchase Invoice Item': invoice_item_fields,
 		'Sales Order Item': invoice_item_fields,
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
index 08573cd..ba1aeaf 100644
--- a/erpnext/regional/italy/utils.py
+++ b/erpnext/regional/italy/utils.py
@@ -57,11 +57,12 @@
 	invoice.company_address_data = company_address
 
 	#Set invoice type
-	if invoice.is_return and invoice.return_against:
-		invoice.type_of_document = "TD04" #Credit Note (Nota di Credito)
-		invoice.return_against_unamended =  get_unamended_name(frappe.get_doc("Sales Invoice", invoice.return_against))
-	else:
-		invoice.type_of_document = "TD01" #Sales Invoice (Fattura)
+	if not invoice.type_of_document:
+		if invoice.is_return and invoice.return_against:
+			invoice.type_of_document = "TD04" #Credit Note (Nota di Credito)
+			invoice.return_against_unamended =  get_unamended_name(frappe.get_doc("Sales Invoice", invoice.return_against))
+		else:
+			invoice.type_of_document = "TD01" #Sales Invoice (Fattura)
 
 	#set customer information
 	invoice.customer_data = frappe.get_doc("Customer", invoice.customer)
diff --git a/erpnext/regional/report/datev/datev.json b/erpnext/regional/report/datev/datev.json
index 80a866c..94e3960 100644
--- a/erpnext/regional/report/datev/datev.json
+++ b/erpnext/regional/report/datev/datev.json
@@ -1,29 +1,22 @@
 {
-	"add_total_row": 0,
-	"apply_user_permissions": 0,
-	"creation": "2019-04-24 08:45:16.650129",
-	"disabled": 0,
-	"icon": "octicon octicon-repo-pull",
-	"color": "#4CB944",
-	"docstatus": 0,
-	"doctype": "Report",
-	"idx": 0,
-	"is_standard": "Yes",
-	"module": "Regional",
-	"name": "DATEV",
-	"owner": "Administrator",
-	"ref_doctype": "GL Entry",
-	"report_name": "DATEV",
-	"report_type": "Script Report",
-	"roles": [
-		{
-			"role": "Accounts User"
-		},
-		{
-			"role": "Accounts Manager"
-		},
-		{
-			"role": "Auditor"
-		}
-	]
-}
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2019-04-24 08:45:16.650129",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-04-06 12:23:00.379517",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "DATEV",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "GL Entry",
+ "report_name": "DATEV",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/e_invoice_summary/__init__.py b/erpnext/regional/report/e_invoice_summary/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/__init__.py
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js
new file mode 100644
index 0000000..4713217
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["E-Invoice Summary"] = {
+	"filters": [
+		{
+			"fieldtype": "Link",
+			"options": "Company",
+			"reqd": 1,
+			"fieldname": "company",
+			"label": __("Company"),
+			"default": frappe.defaults.get_user_default("Company"),
+		},
+		{
+			"fieldtype": "Link",
+			"options": "Customer",
+			"fieldname": "customer",
+			"label": __("Customer")
+		},
+		{
+			"fieldtype": "Date",
+			"reqd": 1,
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldtype": "Date",
+			"reqd": 1,
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"default": frappe.datetime.get_today(),
+		},
+		{
+			"fieldtype": "Select",
+			"fieldname": "status",
+			"label": __("Status"),
+			"options": "\nPending\nGenerated\nCancelled\nFailed"
+		}
+	],
+
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+
+		if (column.fieldname == "einvoice_status" && value) {
+			if (value == 'Pending') value = `<span class="bold" style="color: var(--text-on-orange)">${value}</span>`;
+			else if (value == 'Generated') value = `<span class="bold" style="color: var(--text-on-green)">${value}</span>`;
+			else if (value == 'Cancelled') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`;
+			else if (value == 'Failed') value = `<span class="bold"  style="color: var(--text-on-red)">${value}</span>`;
+		}
+
+		return value;
+	}
+};
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
new file mode 100644
index 0000000..4deb073
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
@@ -0,0 +1,28 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-03-12 11:23:37.312294",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "letter_head": "Logo",
+ "modified": "2021-03-12 12:36:48.689413",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "E-Invoice Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Invoice",
+ "report_name": "E-Invoice Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Administrator"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py
new file mode 100644
index 0000000..47acf29
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py
@@ -0,0 +1,106 @@
+# 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):
+	validate_filters(filters)
+
+	columns = get_columns()
+	data = get_data(filters)
+
+	return columns, data
+
+def validate_filters(filters={}):
+	filters = frappe._dict(filters)
+
+	if not filters.company:
+		frappe.throw(_('{} is mandatory for generating E-Invoice Summary Report').format(_('Company')), title=_('Invalid Filter'))
+	if filters.company:
+		# validate if company has e-invoicing enabled
+		pass
+	if not filters.from_date or not filters.to_date:
+		frappe.throw(_('From Date & To Date is mandatory for generating E-Invoice Summary Report'), title=_('Invalid Filter'))
+	if filters.from_date > filters.to_date:
+		frappe.throw(_('From Date must be before To Date'), title=_('Invalid Filter'))
+
+def get_data(filters={}):
+	query_filters = {
+		'posting_date': ['between', [filters.from_date, filters.to_date]],
+		'einvoice_status': ['is', 'set'],
+		'company': filters.company
+	}
+	if filters.customer:
+		query_filters['customer'] = filters.customer
+	if filters.status:
+		query_filters['einvoice_status'] = filters.status
+
+	data = frappe.get_all(
+		'Sales Invoice',
+		filters=query_filters,
+		fields=[d.get('fieldname') for d in get_columns()]
+	)
+
+	return data
+
+def get_columns():
+	return [
+		{
+			"fieldtype": "Date",
+			"fieldname": "posting_date",
+			"label": _("Posting Date"),
+			"width": 0
+		},
+		{
+			"fieldtype": "Link", 
+			"fieldname": "name", 
+			"label": _("Sales Invoice"),
+			"options": "Sales Invoice",
+			"width": 140
+		},
+		{ 
+			"fieldtype": "Data", 
+			"fieldname": "einvoice_status", 
+			"label": _("Status"), 
+			"width": 100
+		},
+		{ 
+			"fieldtype": "Link",
+			"fieldname": "customer",
+			"options": "Customer",
+			"label": _("Customer")
+		},
+		{ 
+			"fieldtype": "Check",
+			"fieldname": "is_return",
+			"label": _("Is Return"),
+			"width": 85
+		},
+		{
+			"fieldtype": "Data", 
+			"fieldname": "ack_no", 
+			"label": "Ack. No.", 
+			"width": 145
+		},
+		{ 
+			"fieldtype": "Data", 
+			"fieldname": "ack_date", 
+			"label": "Ack. Date", 
+			"width": 165
+		},
+		{
+			"fieldtype": "Data", 
+			"fieldname": "irn", 
+			"label": _("IRN No."),
+			"width": 250
+		},
+		{
+			"fieldtype": "Currency",
+			"options": "Company:company:default_currency", 
+			"fieldname": "base_grand_total", 
+			"label": _("Grand Total"),
+			"width": 120
+		}
+	]
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 62faa30..7507623 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -199,7 +199,7 @@
 		self.item_tax_rate = frappe._dict()
 
 		items = frappe.db.sql("""
-			select item_code, parent, base_net_amount, item_tax_rate
+			select item_code, parent, taxable_value, item_tax_rate
 			from `tab%s Item`
 			where parent in (%s)
 		""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
@@ -207,7 +207,7 @@
 		for d in items:
 			if d.item_code not in self.invoice_items.get(d.parent, {}):
 				self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
-					sum(i.get('base_net_amount', 0) for i in items
+					sum(i.get('taxable_value', 0) for i in items
 						if i.item_code == d.item_code and i.parent == d.parent))
 
 				item_tax_rate = {}
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 7d5e84d..cd94ee1 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -212,7 +212,8 @@
    "fieldtype": "Link",
    "ignore_user_permissions": 1,
    "label": "Represents Company",
-   "options": "Company"
+   "options": "Company",
+   "unique": 1
   },
   {
    "depends_on": "represents_company",
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 2104c01..f01934b 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -18,6 +18,8 @@
   "dn_required",
   "sales_update_frequency",
   "maintain_same_sales_rate",
+  "maintain_same_rate_action",
+  "role_to_override_stop_action",
   "editable_price_list_rate",
   "allow_multiple_items",
   "allow_against_multiple_purchase_orders",
@@ -133,6 +135,23 @@
    "fieldname": "hide_tax_id",
    "fieldtype": "Check",
    "label": "Hide Customer's Tax ID from Sales Transactions"
+  },
+  {
+   "default": "Stop",
+   "depends_on": "maintain_same_sales_rate",
+   "description": "Configure the action to stop the transaction or just warn if the same rate is not maintained.",
+   "fieldname": "maintain_same_rate_action",
+   "fieldtype": "Select",
+   "label": "Action If Same Rate is Not Maintained",
+   "mandatory_depends_on": "maintain_same_sales_rate",
+   "options": "Stop\nWarn"
+  },
+  {
+   "depends_on": "eval: doc.maintain_same_rate_action == 'Stop'",
+   "fieldname": "role_to_override_stop_action",
+   "fieldtype": "Link",
+   "label": "Role Allowed to Override Stop Action",
+   "options": "Role"
   }
  ],
  "icon": "fa fa-cog",
@@ -140,7 +159,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-03-02 17:35:53.603607",
+ "modified": "2021-04-04 20:18:12.814624",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling Settings",
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 9e3c9a5..8adf5bf 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -279,11 +279,6 @@
 					const item_row = frappe.model.get_doc(cdt, cdn);
 					if (item_row && item_row[fieldname] != value) {
 
-						if (fieldname === 'qty' && flt(value) == 0) {
-							this.remove_item_from_cart();
-							return;
-						}
-
 						const { item_code, batch_no, uom } = this.item_details.current_item;
 						const event = {
 							field: fieldname,
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 9ab9eef..11a63b3 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -7,7 +7,6 @@
 		this.allowed_customer_groups = settings.customer_groups;
 		this.allow_rate_change = settings.allow_rate_change;
 		this.allow_discount_change = settings.allow_discount_change;
-
 		this.init_component();
 	}
 
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index cb0a010..32a4556 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -201,7 +201,6 @@
 						me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
 							const item_row = frappe.get_doc(me.doctype, me.name);
 							const doc = me.events.get_frm().doc;
-
 							me.$item_price.html(format_currency(item_row.rate, doc.currency));
 							me.render_discount_dom(item_row);
 						});
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
index b10a9e3..a5a739c 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
@@ -176,6 +176,14 @@
 			this.show_summary_placeholder();
 		});
 
+		this.$summary_container.on('click', '.delete-btn', () => {
+			this.events.delete_order(this.doc.name);
+			this.show_summary_placeholder();
+			// this.toggle_component(false);
+			// this.$component.find('.no-summary-placeholder').removeClass('d-none');
+			// this.$summary_wrapper.addClass('d-none');
+		});
+
 		this.$summary_container.on('click', '.new-btn', () => {
 			this.events.new_order();
 			this.toggle_component(false);
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 22a279d..600f160 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -252,6 +252,41 @@
 		}
 	}
 
+	setup_listener_for_payments() {
+		frappe.realtime.on("process_phone_payment", (data) => {
+			const doc = this.events.get_frm().doc;
+			const { response, amount, success, failure_message } = data;
+			let message, title;
+
+			if (success) {
+				title = __("Payment Received");
+				if (amount >= doc.grand_total) {
+					frappe.dom.unfreeze();
+					message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]);
+					this.events.submit_invoice();
+					cur_frm.reload_doc();
+
+				} else {
+					message = __("Payment of {0} received successfully. Waiting for other requests to complete...", [format_currency(amount, doc.currency, 0)]);
+				}
+			} else if (failure_message) {
+				message = failure_message;
+				title = __("Payment Failed");
+			}
+
+			frappe.msgprint({ "message": message, "title": title });
+		});
+	}
+
+	auto_set_remaining_amount() {
+		const doc = this.events.get_frm().doc;
+		const remaining_amount = doc.grand_total - doc.paid_amount;
+		const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined;
+		if (!current_value && remaining_amount > 0 && this.selected_mode) {
+			this.selected_mode.set_value(remaining_amount);
+		}
+	}
+
 	attach_shortcuts() {
 		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
 		this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`);
diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
index f396705..6fb7666 100644
--- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
+++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py
@@ -57,18 +57,18 @@
 	return columns
 
 def get_details(filters):
-	conditions = ""
 
+	sql_query = """SELECT
+						c.name, c.customer_name,
+						ccl.bypass_credit_limit_check,
+						c.is_frozen, c.disabled
+					FROM `tabCustomer` c, `tabCustomer Credit Limit` ccl
+					WHERE
+						c.name = ccl.parent
+						AND ccl.company = %(company)s"""
+
+	# customer filter is optional.
 	if filters.get("customer"):
-		conditions += " AND c.name = '" + filters.get("customer") + "'"
+		sql_query += " AND c.name = %(customer)s"
 
-	return frappe.db.sql("""SELECT
-			c.name, c.customer_name,
-			ccl.bypass_credit_limit_check,
-			c.is_frozen, c.disabled
-		FROM `tabCustomer` c, `tabCustomer Credit Limit` ccl
-		WHERE
-			c.name = ccl.parent
-			AND ccl.company = '{0}'
-			{1}
-	""".format( filters.get("company"),conditions), as_dict=1) #nosec
+	return frappe.db.sql(sql_query, filters, as_dict=1)
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 0df4c87..933ed3c 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -27,7 +27,7 @@
 		if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
 			"Party Account", "Employee", "Sales Taxes and Charges Template",
 			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
-			"Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
+			"Company", "Bank Account", "Item Tax Template", "Mode Of Payment", "Mode of Payment Account",
 			"Item Default", "Customer", "Supplier", "GST Account"):
 				delete_for_doctype(doctype, company_name)
 
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 1413cb2..885d874 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -61,7 +61,7 @@
 				frappe.set_route("List", "Item", {"item_group": frm.doc.name});
 			});
 		}
-		
+
 		frappe.model.with_doctype('Item', () => {
 			const item_meta = frappe.get_meta('Item');
 
@@ -69,10 +69,12 @@
 				df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
 			).map(df => ({ label: df.label, value: df.fieldname }));
 
-			const field = frappe.meta.get_docfield("Website Filter Field", "fieldname", frm.docname);
-			field.fieldtype = 'Select';
-			field.options = valid_fields;
-			frm.fields_dict.filter_fields.grid.refresh();
+			frm.fields_dict.filter_fields.grid.update_docfield_property(
+				'fieldname', 'fieldtype', 'Select'
+			);
+			frm.fields_dict.filter_fields.grid.update_docfield_property(
+				'fieldname', 'options', valid_fields
+			);
 		});
 	},
 
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index 681d161..8515db3 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -112,9 +112,7 @@
 def request_for_quotation():
 	quotation = _get_cart_quotation()
 	quotation.flags.ignore_permissions = True
-	quotation.save()
-	if not get_shopping_cart_settings().save_quotations_as_draft:
-		quotation.submit()
+	quotation.submit()
 	return quotation.name
 
 @frappe.whitelist()
diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json
index c1f20a4..6cec852 100644
--- a/erpnext/stock/doctype/item/test_records.json
+++ b/erpnext/stock/doctype/item/test_records.json
@@ -59,6 +59,8 @@
   "show_in_website": 1,
   "website_warehouse": "_Test Warehouse - _TC",
   "gst_hsn_code": "999800",
+  "opening_stock": 10,
+  "valuation_rate": 100,
   "item_defaults": [{
     "company": "_Test Company",
     "default_warehouse": "_Test Warehouse - _TC",
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
index 24f7e31..e8fb347 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
@@ -15,8 +15,9 @@
 				}
 			});
 
-			const child = frappe.meta.get_docfield("Variant Field", "field_name", frm.doc.name);
-			child.options = allow_fields;
+			frm.fields_dict.fields.grid.update_docfield_property(
+				'field_name', 'options', allow_fields
+			);
 		});
 	}
 });
diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.js b/erpnext/stock/doctype/packing_slip/packing_slip.js
index bd14e5f..40d4685 100644
--- a/erpnext/stock/doctype/packing_slip/packing_slip.js
+++ b/erpnext/stock/doctype/packing_slip/packing_slip.js
@@ -110,19 +110,4 @@
 	refresh_many(['net_weight_pkg', 'net_weight_uom', 'gross_weight_uom', 'gross_weight_pkg']);
 }
 
-var make_row = function(title,val,bold){
-	var bstart = '<b>'; var bend = '</b>';
-	return '<tr><td class="datalabelcell">'+(bold?bstart:'')+title+(bold?bend:'')+'</td>'
-	+'<td class="datainputcell" style="text-align:left;">'+ val +'</td>'
-	+'</tr>'
-}
-
-cur_frm.pformat.net_weight_pkg= function(doc){
-	return '<table style="width:100%">' + make_row('Net Weight', doc.net_weight_pkg) + '</table>'
-}
-
-cur_frm.pformat.gross_weight_pkg= function(doc){
-	return '<table style="width:100%">' + make_row('Gross Weight', doc.gross_weight_pkg) + '</table>'
-}
-
 // TODO: validate gross weight field
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 755fa61..6ab68e2 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -379,7 +379,6 @@
 	else:
 		stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry)
 
-	stock_entry.set_incoming_rate()
 	stock_entry.set_actual_qty()
 	stock_entry.calculate_rate_and_amount()
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index f9abea6..befdad9 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -276,13 +276,6 @@
 	}
 }
 
-cur_frm.cscript.select_print_heading = function(doc, cdt, cdn) {
-	if(doc.select_print_heading)
-		cur_frm.pformat.print_heading = doc.select_print_heading;
-	else
-		cur_frm.pformat.print_heading = "Purchase Receipt";
-}
-
 cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) {
 	return {
 		filters: [
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index f8cfdf8..3f83780 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -124,10 +124,10 @@
 		return
 
 	for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
-		check_if_stock_and_account_balance_synced(today(), d.company)
+		check_if_stock_and_account_balance_synced(today(), d.name)
 
 def get_repost_item_valuation_entries():
-	date = add_to_date(today(), hours=-12)
+	date = add_to_date(today(), hours=-3)
 
 	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
 		WHERE status != 'Completed' and creation <= %s and docstatus = 1
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 98246fb..ef7d54a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -558,7 +558,6 @@
 				})
 			);
 		}
-
 		for (let i in frm.doc.items) {
 			let item = frm.doc.items[i];
 
@@ -856,7 +855,6 @@
 		}
 		erpnext.hide_company();
 		erpnext.utils.add_item(this.frm);
-		this.frm.trigger('add_to_transit');
 	},
 
 	scan_barcode: function() {
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 349d8ae..3296f5b 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -34,7 +34,7 @@
 			qty=50,
 			rate=100,
 			company=company,
-			expense_account = "Stock Adjustment - _TC",
+			expense_account = "Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
 			posting_date='2020-04-10',
 			posting_time='14:00'
 		)
@@ -46,7 +46,7 @@
 			qty=10,
 			rate=200,
 			company=company,
-			expense_account = "Stock Adjustment - _TC",
+			expense_account="Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
 			posting_date='2020-04-20',
 			posting_time='14:00'
 		)
@@ -58,7 +58,7 @@
 			target="Finished Goods - _TC",
 			company=company,
 			qty=10,
-			expense_account="Stock Adjustment - _TC",
+			expense_account="Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
 			posting_date='2020-04-30',
 			posting_time='14:00'
 		)
@@ -90,7 +90,7 @@
 			qty=50,
 			rate=150,
 			company=company,
-			expense_account = "Stock Adjustment - _TC",
+			expense_account ="Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC",
 			posting_date='2020-04-12',
 			posting_time='14:00'
 		)
diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json
index bddb114..9b90932 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.json
+++ b/erpnext/stock/doctype/warehouse/warehouse.json
@@ -70,6 +70,7 @@
    "oldfieldname": "company",
    "oldfieldtype": "Link",
    "options": "Company",
+   "read_only_depends_on": "eval: !doc.__islocal",
    "remember_last_selected_value": 1,
    "reqd": 1,
    "search_index": 1
@@ -244,7 +245,7 @@
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2021-02-16 17:21:52.380098",
+ "modified": "2021-04-09 19:54:56.263965",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Warehouse",
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index ff603fc..623dc2f 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -49,7 +49,7 @@
 	for batch in fifo_queue:
 		batch_age = date_diff(to_date, batch[1])
 
-		if type(batch[0]) in ['int', 'float']:
+		if isinstance(batch[0], (int, float)):
 			age_qty += batch_age * batch[0]
 			total_qty += batch[0]
 		else:
@@ -302,4 +302,4 @@
 		fieldname=fieldname,
 		fieldtype=fieldtype,
 		width=width
-	))
\ No newline at end of file
+	))
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 9fe12f9..ecc9fcf 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -48,44 +48,62 @@
 		}
 	},
 
-	refresh: function (frm) {
-		if (frm.doc.status !== "Closed") {
-			if (frm.doc.service_level_agreement && frm.doc.agreement_status === "Ongoing") {
-				frappe.call({
-					"method": "frappe.client.get",
-					args: {
-						doctype: "Service Level Agreement",
-						name: frm.doc.service_level_agreement
-					},
-					callback: function(data) {
-						let statuses = data.message.pause_sla_on;
-						const hold_statuses = [];
-						$.each(statuses, (_i, entry) => {
-							hold_statuses.push(entry.status);
-						});
-						if (hold_statuses.includes(frm.doc.status)) {
-							frm.dashboard.clear_headline();
-							let message = {"indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)])};
-							frm.dashboard.set_headline_alert(
-								'<div class="row">' +
-									'<div class="col-xs-12">' +
-										'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
-									'</div>' +
-								'</div>'
-							);
-						} else {
-							set_time_to_resolve_and_response(frm);
-						}
-					}
-				});
-			}
+	refresh: function(frm) {
 
-			frm.add_custom_button(__("Close"), function () {
+		// alert messages
+		if (frm.doc.status !== "Closed" && frm.doc.service_level_agreement
+			&& frm.doc.agreement_status === "Ongoing") {
+			frappe.call({
+				"method": "frappe.client.get",
+				args: {
+					doctype: "Service Level Agreement",
+					name: frm.doc.service_level_agreement
+				},
+				callback: function(data) {
+					let statuses = data.message.pause_sla_on;
+					const hold_statuses = [];
+					$.each(statuses, (_i, entry) => {
+						hold_statuses.push(entry.status);
+					});
+					if (hold_statuses.includes(frm.doc.status)) {
+						frm.dashboard.clear_headline();
+						let message = { "indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)]) };
+						frm.dashboard.set_headline_alert(
+							'<div class="row">' +
+							'<div class="col-xs-12">' +
+							'<span class="indicator whitespace-nowrap ' + message.indicator + '"><span>' + message.msg + '</span></span> ' +
+							'</div>' +
+							'</div>'
+						);
+					} else {
+						set_time_to_resolve_and_response(frm);
+					}
+				}
+			});
+		} else if (frm.doc.service_level_agreement) {
+			frm.dashboard.clear_headline();
+
+			let agreement_status = (frm.doc.agreement_status == "Fulfilled") ?
+				{ "indicator": "green", "msg": "Service Level Agreement has been fulfilled" } :
+				{ "indicator": "red", "msg": "Service Level Agreement Failed" };
+
+			frm.dashboard.set_headline_alert(
+				'<div class="row">' +
+				'<div class="col-xs-12">' +
+				'<span class="indicator whitespace-nowrap ' + agreement_status.indicator + '"><span class="hidden-xs">' + agreement_status.msg + '</span></span> ' +
+				'</div>' +
+				'</div>'
+			);
+		}
+
+		// buttons
+		if (frm.doc.status !== "Closed") {
+			frm.add_custom_button(__("Close"), function() {
 				frm.set_value("status", "Closed");
 				frm.save();
 			});
 
-			frm.add_custom_button(__("Task"), function () {
+			frm.add_custom_button(__("Task"), function() {
 				frappe.model.open_mapped_doc({
 					method: "erpnext.support.doctype.issue.issue.make_task",
 					frm: frm
@@ -93,23 +111,7 @@
 			}, __("Create"));
 
 		} else {
-			if (frm.doc.service_level_agreement) {
-				frm.dashboard.clear_headline();
-
-				let agreement_status = (frm.doc.agreement_status == "Fulfilled") ?
-					{"indicator": "green", "msg": "Service Level Agreement has been fulfilled"} :
-					{"indicator": "red", "msg": "Service Level Agreement Failed"};
-
-				frm.dashboard.set_headline_alert(
-					'<div class="row">' +
-						'<div class="col-xs-12">' +
-							'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
-						'</div>' +
-					'</div>'
-				);
-			}
-
-			frm.add_custom_button(__("Reopen"), function () {
+			frm.add_custom_button(__("Reopen"), function() {
 				frm.set_value("status", "Open");
 				frm.save();
 			});
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
index 5346195..00060b9 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
@@ -10,7 +10,9 @@
 			let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options;
 			statuses = statuses.split('\n');
 			allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status));
-			frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses);
+			frm.fields_dict.pause_sla_on.grid.update_docfield_property(
+				'status', 'options', [''].concat(allow_statuses)
+			);
 		});
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.js b/erpnext/support/report/issue_analytics/issue_analytics.js
index f87b2c2..746eee0 100644
--- a/erpnext/support/report/issue_analytics/issue_analytics.js
+++ b/erpnext/support/report/issue_analytics/issue_analytics.js
@@ -52,6 +52,7 @@
 			label: __("Status"),
 			fieldtype: "Select",
 			options:[
+				"",
 				{label: __('Open'), value: 'Open'},
 				{label: __('Replied'), value: 'Replied'},
 				{label: __('Resolved'), value: 'Resolved'},
@@ -138,4 +139,4 @@
 			}
 		});
 	}
-};
\ No newline at end of file
+};
diff --git a/erpnext/support/report/issue_summary/issue_summary.js b/erpnext/support/report/issue_summary/issue_summary.js
index 684482a..eb0e06c 100644
--- a/erpnext/support/report/issue_summary/issue_summary.js
+++ b/erpnext/support/report/issue_summary/issue_summary.js
@@ -39,6 +39,7 @@
 			label: __("Status"),
 			fieldtype: "Select",
 			options:[
+				"",
 				{label: __('Open'), value: 'Open'},
 				{label: __('Replied'), value: 'Replied'},
 				{label: __('Resolved'), value: 'Resolved'},
@@ -70,4 +71,4 @@
 			options: "User"
 		}
 	]
-};
\ 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 f5adbf0..167c848 100644
--- a/erpnext/templates/generators/item/item_add_to_cart.html
+++ b/erpnext/templates/generators/item/item_add_to_cart.html
@@ -11,7 +11,7 @@
 			<small class="formatted-price">({{ product_info.price.formatted_price }} / {{ product_info.uom }})</small>
 		</div>
 		{% else %}
-			{{ _("Unit of Measurement") }} : {{ product_info.uom }}
+			{{ _("UOM") }} : {{ product_info.uom }}
 		{% endif %}
 
 		{% if cart_settings.show_stock_availability %}
diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html
index 930d0c2..3834131 100644
--- a/erpnext/templates/includes/transaction_row.html
+++ b/erpnext/templates/includes/transaction_row.html
@@ -14,11 +14,7 @@
 			</div>
 		</div>
 		<div class="col-sm-3 text-right bold">
-			{% if doc.doctype == "Quotation" and not doc.docstatus %}
-				{{ _("Pending") }}
-			{% else %}
-				{{ doc.get_formatted("grand_total") }}
-			{% endif %}
+			{{ doc.get_formatted("grand_total") }}
 		</div>
 	</div>
 	<a class="transaction-item-link" href="/{{ pathname }}/{{ doc.name }}">Link</a>
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html
index 07dd676..28faea8 100644
--- a/erpnext/templates/pages/order.html
+++ b/erpnext/templates/pages/order.html
@@ -12,21 +12,22 @@
 {% endblock %}
 
 {% block header_actions %}
-	<div class="dropdown">
-		<button class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
-			<span>{{ _('Actions') }}</span>
-			<b class="caret"></b>
-		</button>
-		<ul class="dropdown-menu dropdown-menu-right" role="menu">
-			{% if doc.doctype == 'Purchase Order' %}
-			<a class="dropdown-item" href="/api/method/erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice_from_portal?purchase_order_name={{ doc.name }}" data-action="make_purchase_invoice">{{ _("Make Purchase Invoice") }}</a>
-			{% endif %}
-			<a class="dropdown-item" href='/printview?doctype={{ doc.doctype}}&name={{ doc.name }}&format={{ print_format }}'
-				target="_blank" rel="noopener noreferrer">
-				{{ _("Print") }}
-			</a>
-		</ul>
-	</div>
+<div class="dropdown">
+	<button class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
+		<span>{{ _('Actions') }}</span>
+		<b class="caret"></b>
+	</button>
+	<ul class="dropdown-menu dropdown-menu-right" role="menu">
+		{% if doc.doctype == 'Purchase Order' %}
+		<a class="dropdown-item" href="/api/method/erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice_from_portal?purchase_order_name={{ doc.name }}" data-action="make_purchase_invoice">{{ _("Make Purchase Invoice") }}</a>
+		{% endif %}
+		<a class="dropdown-item" href='/printview?doctype={{ doc.doctype}}&name={{ doc.name }}&format={{ print_format }}'
+			target="_blank" rel="noopener noreferrer">
+			{{ _("Print") }}
+		</a>
+	</ul>
+</div>
+
 {% endblock %}
 
 {% block page_content %}
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index c8ae733..f99da58 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -7,7 +7,6 @@
 from frappe import _
 from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form, date_diff, nowdate
 from erpnext.controllers.status_updater import StatusUpdater
-from erpnext.accounts.utils import get_fiscal_year
 
 from six import string_types
 
@@ -121,11 +120,11 @@
 		buying_doctypes = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]
 
 		if self.doctype in buying_doctypes:
-			to_disable = "Maintain same rate throughout Purchase cycle"
-			settings_page = "Buying Settings"
+			action = frappe.db.get_single_value("Buying Settings", "maintain_same_rate_action")
+			settings_doc = "Buying Settings"
 		else:
-			to_disable = "Maintain same rate throughout Sales cycle"
-			settings_page = "Selling Settings"
+			action = frappe.db.get_single_value("Selling Settings", "maintain_same_rate_action")
+			settings_doc = "Selling Settings"
 
 		for ref_dt, ref_dn_field, ref_link_field in ref_details:
 			for d in self.get("items"):
@@ -133,11 +132,16 @@
 					ref_rate = frappe.db.get_value(ref_dt + " Item", d.get(ref_link_field), "rate")
 
 					if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= .01:
-						frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ")
-							.format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate))
-						frappe.throw(_("To allow different rates, disable the {0} checkbox in {1}.")
-							.format(frappe.bold(_(to_disable)),
-							get_link_to_form(settings_page, settings_page, frappe.bold(settings_page))))
+						if action == "Stop":
+							role_allowed_to_override = frappe.db.get_single_value(settings_doc, 'role_to_override_stop_action')
+
+							if role_allowed_to_override not in frappe.get_roles():
+								frappe.throw(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
+									d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate))
+						else:
+							frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format(
+								d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate), title=_("Warning"), indicator="orange")
+
 
 	def get_link_filters(self, for_doctype):
 		if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype):