Merge pull request #25660 from deepeshgarg007/promotional_item_cost_center

fix: Breaking cost center validation
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 0606823..1be2fbf 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -13,7 +13,7 @@
 class Account(NestedSet):
 	nsm_parent_field = 'parent_account'
 	def on_update(self):
-		if frappe.local.flags.ignore_on_update:
+		if frappe.local.flags.ignore_update_nsm:
 			return
 		else:
 			super(Account, self).on_update()
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index 0e3b24c..927adc7 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -57,10 +57,10 @@
 
 		# Rebuild NestedSet HSM tree for Account Doctype
 		# after all accounts are already inserted.
-		frappe.local.flags.ignore_on_update = True
+		frappe.local.flags.ignore_update_nsm = True
 		_import_accounts(chart, None, None, root_account=True)
 		rebuild_tree("Account", "parent_account")
-		frappe.local.flags.ignore_on_update = False
+		frappe.local.flags.ignore_update_nsm = False
 
 def add_suffix_if_duplicate(account_name, account_number, accounts):
 	if account_number:
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index e1276e7..781f94e 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -7,26 +7,30 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "auto_accounting_for_stock",
-  "acc_frozen_upto",
-  "frozen_accounts_modifier",
-  "determine_address_tax_category_from",
+  "accounts_transactions_settings_section",
   "over_billing_allowance",
   "role_allowed_to_over_bill",
-  "column_break_4",
-  "credit_controller",
-  "check_supplier_invoice_uniqueness",
   "make_payment_via_journal_entry",
+  "column_break_11",
+  "check_supplier_invoice_uniqueness",
   "unlink_payment_on_cancellation_of_invoice",
-  "unlink_advance_payment_on_cancelation_of_order",
-  "book_asset_depreciation_entry_automatically",
-  "add_taxes_from_item_tax_template",
   "automatically_fetch_payment_terms",
   "delete_linked_ledger_entries",
+  "book_asset_depreciation_entry_automatically",
+  "unlink_advance_payment_on_cancelation_of_order",
+  "tax_settings_section",
+  "determine_address_tax_category_from",
+  "column_break_19",
+  "add_taxes_from_item_tax_template",
+  "period_closing_settings_section",
+  "acc_frozen_upto",
+  "frozen_accounts_modifier",
+  "column_break_4",
+  "credit_controller",
   "deferred_accounting_settings_section",
-  "automatically_process_deferred_accounting_entry",
   "book_deferred_entries_based_on",
   "column_break_18",
+  "automatically_process_deferred_accounting_entry",
   "book_deferred_entries_via_journal_entry",
   "submit_journal_entries",
   "print_settings",
@@ -41,15 +45,6 @@
  ],
  "fields": [
   {
-   "default": "1",
-   "description": "If enabled, the system will post accounting entries for inventory automatically",
-   "fieldname": "auto_accounting_for_stock",
-   "fieldtype": "Check",
-   "hidden": 1,
-   "in_list_view": 1,
-   "label": "Make Accounting Entry For Every Stock Movement"
-  },
-  {
    "description": "Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below",
    "fieldname": "acc_frozen_upto",
    "fieldtype": "Date",
@@ -94,6 +89,7 @@
    "default": "0",
    "fieldname": "make_payment_via_journal_entry",
    "fieldtype": "Check",
+   "hidden": 1,
    "label": "Make Payment via Journal Entry"
   },
   {
@@ -234,6 +230,29 @@
    "fieldtype": "Link",
    "label": "Role Allowed to Over Bill ",
    "options": "Role"
+  },
+  {
+   "fieldname": "period_closing_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Period Closing Settings"
+  },
+  {
+   "fieldname": "accounts_transactions_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Transactions Settings"
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "tax_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Tax Settings"
+  },
+  {
+   "fieldname": "column_break_19",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "icon-cog",
@@ -241,7 +260,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-03-11 18:52:05.601996",
+ "modified": "2021-04-30 15:25:10.381008",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
index 5593466..4d33880 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -30,5 +30,5 @@
 	def enable_payment_schedule_in_print(self):
 		show_in_print = cint(self.show_payment_schedule_in_print)
 		for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
-			make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check")
-			make_property_setter(doctype, "payment_schedule", "print_hide",  0 if show_in_print else 1, "Check")
+			make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, "payment_schedule", "print_hide",  0 if show_in_print else 1, "Check", validate_fields_for_doctype=False)
diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py
index cb18309..c5ce514 100644
--- a/erpnext/accounts/doctype/dunning/test_dunning.py
+++ b/erpnext/accounts/doctype/dunning/test_dunning.py
@@ -42,9 +42,9 @@
 			['Sales - _TC',  0.0, 20.44]
 		])
 		for gle in gl_entries:
-			self.assertEquals(expected_values[gle.account][0], gle.account)
-			self.assertEquals(expected_values[gle.account][1], gle.debit)
-			self.assertEquals(expected_values[gle.account][2], gle.credit)
+			self.assertEqual(expected_values[gle.account][0], gle.account)
+			self.assertEqual(expected_values[gle.account][1], gle.debit)
+			self.assertEqual(expected_values[gle.account][2], gle.credit)
 
 	def test_payment_entry(self):
 		dunning = create_dunning()
diff --git a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
index b4a547b..4167ca7 100644
--- a/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/test_gl_entry.py
@@ -54,4 +54,4 @@
 		self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries)))
 
 		new_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
-		self.assertEquals(old_naming_series_current_value + 2, new_naming_series_current_value)
+		self.assertEqual(old_naming_series_current_value + 2, new_naming_series_current_value)
diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py
index 1c23e2a..5fdde07 100644
--- a/erpnext/accounts/doctype/payment_order/test_payment_order.py
+++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py
@@ -31,10 +31,10 @@
 
 		doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
 		reference_doc = doc.get("references")[0]
-		self.assertEquals(reference_doc.reference_name, payment_entry.name)
-		self.assertEquals(reference_doc.reference_doctype, "Payment Entry")
-		self.assertEquals(reference_doc.supplier, "_Test Supplier")
-		self.assertEquals(reference_doc.amount, 250)
+		self.assertEqual(reference_doc.reference_name, payment_entry.name)
+		self.assertEqual(reference_doc.reference_doctype, "Payment Entry")
+		self.assertEqual(reference_doc.supplier, "_Test Supplier")
+		self.assertEqual(reference_doc.amount, 250)
 
 def create_payment_order_against_payment_entry(ref_doc, order_type):
 	payment_order = frappe.get_doc(dict(
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index cf6ec18..6635128 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -114,7 +114,7 @@
 				'party_type': self.party_type,
 				'voucher_type': voucher_type,
 				'account': self.receivable_payable_account
-			}, as_dict=1, debug=1)
+			}, as_dict=1)
 
 	def add_payment_entries(self, entries):
 		self.set('payments', [])
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 9ea616f..aa0c53e 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -22,7 +22,43 @@
 		});
 		
 		if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime());
-		if (frm.doc.docstatus === 1) set_html_data(frm);
+		
+		frappe.realtime.on('closing_process_complete', async function(data) {
+			await frm.reload_doc();
+			if (frm.doc.status == 'Failed' && frm.doc.error_message && data.user == frappe.session.user) {
+				frappe.msgprint({
+					title: __('POS Closing Failed'),
+					message: frm.doc.error_message,
+					indicator: 'orange',
+					clear: true
+				});
+			}
+		});
+
+		set_html_data(frm);
+	},
+
+	refresh: function(frm) {
+		if (frm.doc.docstatus == 1 && frm.doc.status == 'Failed') {
+			const issue = '<a id="jump_to_error" style="text-decoration: underline;">issue</a>';
+			frm.dashboard.set_headline(
+				__('POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.', [issue]));
+			
+			$('#jump_to_error').on('click', (e) => {
+				e.preventDefault();
+				frappe.utils.scroll_to(
+					cur_frm.get_field("error_message").$wrapper,
+					true,
+					30
+				);
+			});
+
+			frm.add_custom_button(__('Retry'), function () {
+				frm.call('retry', {}, () => {
+					frm.reload_doc();
+				});
+			});
+		}
 	},
 
 	pos_opening_entry(frm) {
@@ -61,44 +97,24 @@
 				refresh_fields(frm);
 				set_html_data(frm);
 			}
-		})
+		});
+	},
+
+	before_save: function(frm) {
+		for (let row of frm.doc.pos_transactions) {
+			frappe.db.get_doc("POS Invoice", row.pos_invoice).then(doc => {
+				cur_frm.doc.grand_total -= flt(doc.grand_total);
+				cur_frm.doc.net_total -= flt(doc.net_total);
+				cur_frm.doc.total_quantity -= flt(doc.total_qty);
+				refresh_payments(doc, cur_frm, 1);
+				refresh_taxes(doc, cur_frm, 1);
+				refresh_fields(cur_frm);
+				set_html_data(cur_frm);
+			});
+		}
 	}
 });
 
-cur_frm.cscript.before_pos_transactions_remove = function(doc, cdt, cdn) {
-	const removed_row = locals[cdt][cdn];
-
-	if (!removed_row.pos_invoice) return;
-
-	frappe.db.get_doc("POS Invoice", removed_row.pos_invoice).then(doc => {
-		cur_frm.doc.grand_total -= flt(doc.grand_total);
-		cur_frm.doc.net_total -= flt(doc.net_total);
-		cur_frm.doc.total_quantity -= flt(doc.total_qty);
-		refresh_payments(doc, cur_frm, 1);
-		refresh_taxes(doc, cur_frm, 1);
-		refresh_fields(cur_frm);
-		set_html_data(cur_frm);
-	});
-}
-
-frappe.ui.form.on('POS Invoice Reference', {
-	pos_invoice(frm, cdt, cdn) {
-		const added_row = locals[cdt][cdn];
-
-		if (!added_row.pos_invoice) return;
-
-		frappe.db.get_doc("POS Invoice", added_row.pos_invoice).then(doc => {
-			frm.doc.grand_total += flt(doc.grand_total);
-			frm.doc.net_total += flt(doc.net_total);
-			frm.doc.total_quantity += flt(doc.total_qty);
-			refresh_payments(doc, frm);
-			refresh_taxes(doc, frm);
-			refresh_fields(frm);
-			set_html_data(frm);
-		});
-	}
-})
-
 frappe.ui.form.on('POS Closing Entry Detail', {
 	closing_amount: (frm, cdt, cdn) => {
 		const row = locals[cdt][cdn];
@@ -177,11 +193,13 @@
 }
 
 function set_html_data(frm) {
-	frappe.call({
-		method: "get_payment_reconciliation_details",
-		doc: frm.doc,
-		callback: (r) => {
-			frm.get_field("payment_reconciliation_details").$wrapper.html(r.message);
-		}
-	})
+	if (frm.doc.docstatus === 1 && frm.doc.status == 'Submitted') {
+		frappe.call({
+			method: "get_payment_reconciliation_details",
+			doc: frm.doc,
+			callback: (r) => {
+				frm.get_field("payment_reconciliation_details").$wrapper.html(r.message);
+			}
+		});
+	}
 }
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
index a9b91e0..4d6e4a2 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
@@ -30,6 +30,8 @@
   "total_quantity",
   "column_break_16",
   "taxes",
+  "failure_description_section",
+  "error_message",
   "section_break_14",
   "amended_from"
  ],
@@ -195,7 +197,7 @@
    "fieldtype": "Select",
    "hidden": 1,
    "label": "Status",
-   "options": "Draft\nSubmitted\nQueued\nCancelled",
+   "options": "Draft\nSubmitted\nQueued\nFailed\nCancelled",
    "print_hide": 1,
    "read_only": 1
   },
@@ -203,6 +205,21 @@
    "fieldname": "period_details_section",
    "fieldtype": "Section Break",
    "label": "Period Details"
+  },
+  {
+   "collapsible": 1,
+   "collapsible_depends_on": "error_message",
+   "depends_on": "error_message",
+   "fieldname": "failure_description_section",
+   "fieldtype": "Section Break",
+   "label": "Failure Description"
+  },
+  {
+   "depends_on": "error_message",
+   "fieldname": "error_message",
+   "fieldtype": "Small Text",
+   "label": "Error",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
@@ -212,7 +229,7 @@
    "link_fieldname": "pos_closing_entry"
   }
  ],
- "modified": "2021-02-01 13:47:20.722104",
+ "modified": "2021-05-05 16:59:49.723261",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Closing Entry",
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 1065168..8252872 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -60,6 +60,10 @@
 	def on_cancel(self):
 		unconsolidate_pos_invoices(closing_entry=self)
 
+	@frappe.whitelist()
+	def retry(self):
+		consolidate_pos_invoices(closing_entry=self)
+
 	def update_opening_entry(self, for_cancel=False):
 		opening_entry = frappe.get_doc("POS Opening Entry", self.pos_opening_entry)
 		opening_entry.pos_closing_entry = self.name if not for_cancel else None
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js
index 20fd610..cffeb4d 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js
@@ -8,6 +8,7 @@
 			"Draft": "red",
 			"Submitted": "blue",
 			"Queued": "orange",
+			"Failed": "red",
 			"Cancelled": "red"
 
 		};
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 1e6a3d1..473db56 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -461,7 +461,17 @@
 		order by posting_date desc, posting_time desc
 		limit 1""", (item_code, warehouse), as_dict=1)
 
-	pos_sales_qty = frappe.db.sql("""select sum(p_item.qty) as qty
+	pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
+
+	sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
+
+	if sle_qty and pos_sales_qty:
+		return sle_qty - pos_sales_qty
+	else:
+		return sle_qty
+
+def get_pos_reserved_qty(item_code, warehouse):
+	reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
 		from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
 		where p.name = p_item.parent
 		and p.consolidated_invoice is NULL
@@ -470,14 +480,8 @@
 		and p_item.item_code = %s
 		and p_item.warehouse = %s
 		""", (item_code, warehouse), as_dict=1)
-
-	sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
-	pos_sales_qty = pos_sales_qty[0].qty or 0 if pos_sales_qty else 0
-
-	if sle_qty and pos_sales_qty:
-		return sle_qty - pos_sales_qty
-	else:
-		return sle_qty
+	
+	return reserved_qty[0].qty or 0 if reserved_qty else 0
 
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
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 4d5472d..bc78743 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
@@ -13,8 +13,7 @@
 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
+import six
 
 class POSInvoiceMergeLog(Document):
 	def validate(self):
@@ -239,7 +238,7 @@
 	invoices = pos_invoices or (closing_entry and closing_entry.get('pos_transactions')) or get_all_unconsolidated_invoices()
 	invoice_by_customer = get_invoice_customer_map(invoices)
 
-	if len(invoices) >= 1 and closing_entry:
+	if len(invoices) >= 10 and closing_entry:
 		closing_entry.set_status(update=True, status='Queued')
 		enqueue_job(create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry)
 	else:
@@ -252,36 +251,68 @@
 		pluck='name'
 	)
 
-	if len(merge_logs) >= 1:
+	if len(merge_logs) >= 10:
 		closing_entry.set_status(update=True, status='Queued')
 		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=None):
-	for customer, invoices in iteritems(invoice_by_customer):
-		merge_log = frappe.new_doc('POS Invoice Merge Log')
-		merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate()
-		merge_log.customer = customer
-		merge_log.pos_closing_entry = closing_entry.get('name') if closing_entry else None
+	try:
+		for customer, invoices in six.iteritems(invoice_by_customer):
+			merge_log = frappe.new_doc('POS Invoice Merge Log')
+			merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate()
+			merge_log.customer = customer
+			merge_log.pos_closing_entry = closing_entry.get('name') if closing_entry else None
 
-		merge_log.set('pos_invoices', invoices)
-		merge_log.save(ignore_permissions=True)
-		merge_log.submit()
+			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()
+		if closing_entry:
+			closing_entry.set_status(update=True, status='Submitted')
+			closing_entry.db_set('error_message', '')
+			closing_entry.update_opening_entry()
+
+	except Exception:
+		frappe.db.rollback()
+		message_log = frappe.message_log.pop()
+		error_message = safe_load_json(message_log)
+
+		if closing_entry:
+			closing_entry.set_status(update=True, status='Failed')
+			closing_entry.db_set('error_message', error_message)
+		raise
+
+	finally:
+		frappe.db.commit()
+		frappe.publish_realtime('closing_process_complete', {'user': frappe.session.user})
 
 def cancel_merge_logs(merge_logs, closing_entry=None):
-	for log in merge_logs:
-		merge_log = frappe.get_doc('POS Invoice Merge Log', log)
-		merge_log.flags.ignore_permissions = True
-		merge_log.cancel()
+	try:
+		for log in merge_logs:
+			merge_log = frappe.get_doc('POS Invoice Merge Log', log)
+			merge_log.flags.ignore_permissions = True
+			merge_log.cancel()
 
-	if closing_entry:
-		closing_entry.set_status(update=True, status='Cancelled')
-		closing_entry.update_opening_entry(for_cancel=True)
+		if closing_entry:
+			closing_entry.set_status(update=True, status='Cancelled')
+			closing_entry.db_set('error_message', '')
+			closing_entry.update_opening_entry(for_cancel=True)
+
+	except Exception:
+		frappe.db.rollback()
+		message_log = frappe.message_log.pop()
+		error_message = safe_load_json(message_log)
+
+		if closing_entry:
+			closing_entry.set_status(update=True, status='Submitted')
+			closing_entry.db_set('error_message', error_message)
+		raise
+
+	finally:
+		frappe.db.commit()
+		frappe.publish_realtime('closing_process_complete', {'user': frappe.session.user})
 
 def enqueue_job(job, **kwargs):
 	check_scheduler_status()
@@ -314,4 +345,14 @@
 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
+		return True
+
+def safe_load_json(message):
+	JSONDecodeError = ValueError if six.PY2 else json.JSONDecodeError
+
+	try:
+		json_message = json.loads(message).get('message')
+	except JSONDecodeError:
+		json_message = message
+
+	return json_message
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index ef9aad5..ffe8be1 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -99,7 +99,7 @@
 
 		args.item_code = "_Test Item 2"
 		details = get_item_details(args)
-		self.assertEquals(details.get("discount_percentage"), 15)
+		self.assertEqual(details.get("discount_percentage"), 15)
 
 	def test_pricing_rule_for_margin(self):
 		from erpnext.stock.get_item_details import get_item_details
@@ -145,8 +145,8 @@
 			"name": None
 		})
 		details = get_item_details(args)
-		self.assertEquals(details.get("margin_type"), "Percentage")
-		self.assertEquals(details.get("margin_rate_or_amount"), 10)
+		self.assertEqual(details.get("margin_type"), "Percentage")
+		self.assertEqual(details.get("margin_rate_or_amount"), 10)
 
 	def test_mixed_conditions_for_item_group(self):
 		for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]:
@@ -192,7 +192,7 @@
 			"name": None
 		})
 		details = get_item_details(args)
-		self.assertEquals(details.get("discount_percentage"), 10)
+		self.assertEqual(details.get("discount_percentage"), 10)
 
 	def test_pricing_rule_for_variants(self):
 		from erpnext.stock.get_item_details import get_item_details
@@ -322,11 +322,11 @@
 		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.assertEqual(item.margin_rate_or_amount, 10)
+		self.assertEqual(item.rate_with_margin, 1100)
 		self.assertEqual(item.discount_percentage, 10)
-		self.assertEquals(item.discount_amount, 110)
-		self.assertEquals(item.rate, 990)
+		self.assertEqual(item.discount_amount, 110)
+		self.assertEqual(item.rate, 990)
 
 	def test_pricing_rule_with_margin_and_discount_amount(self):
 		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
@@ -338,10 +338,10 @@
 		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)
+		self.assertEqual(item.margin_rate_or_amount, 10)
+		self.assertEqual(item.rate_with_margin, 1100)
+		self.assertEqual(item.discount_amount, 110)
+		self.assertEqual(item.rate, 990)
 
 	def test_pricing_rule_for_product_discount_on_same_item(self):
 		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
@@ -458,21 +458,21 @@
 		si.items[0].price_list_rate = 1000
 		si.submit()
 		item = si.items[0]
-		self.assertEquals(item.rate, 100)
+		self.assertEqual(item.rate, 100)
 
 		# Correct Customer and Incorrect is_return value
 		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1)
 		si.items[0].price_list_rate = 1000
 		si.submit()
 		item = si.items[0]
-		self.assertEquals(item.rate, 100)
+		self.assertEqual(item.rate, 100)
 
 		# Correct Customer and correct is_return value
 		si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0)
 		si.items[0].price_list_rate = 1000
 		si.submit()
 		item = si.items[0]
-		self.assertEquals(item.rate, 900)
+		self.assertEqual(item.rate, 900)
 
 	def test_multiple_pricing_rules(self):
 		make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
@@ -545,11 +545,11 @@
 			apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10)
 
 		si = create_sales_invoice(qty=5, do_not_submit=True)
-		self.assertEquals(len(si.items), 2)
-		self.assertEquals(si.items[1].rate, 10)
+		self.assertEqual(len(si.items), 2)
+		self.assertEqual(si.items[1].rate, 10)
 
 		si1 = create_sales_invoice(qty=2, do_not_submit=True)
-		self.assertEquals(len(si1.items), 1)
+		self.assertEqual(len(si1.items), 1)
 
 		for doc in [si, si1]:
 			doc.delete()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8a42d9e..7c73ad6 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -356,11 +356,11 @@
 	},
 
 	items_on_form_rendered: function() {
-		erpnext.setup_serial_no();
+		erpnext.setup_serial_or_batch_no();
 	},
 
 	packed_items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
+		erpnext.setup_serial_or_batch_no();
 	},
 
 	make_sales_return: function() {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4461f29..bb74a02 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1111,7 +1111,7 @@
 			if not item.serial_no:
 				continue
 
-			for serial_no in item.serial_no.split("\n"):
+			for serial_no in get_serial_nos(item.serial_no):
 				if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
 					frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
 
@@ -1755,15 +1755,10 @@
 		item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
 
 def get_delivery_note_details(internal_reference):
-	so_item_map = {}
-
 	si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
 		filters={'parent': internal_reference})
 
-	for d in si_item_details:
-		so_item_map.setdefault(d.name, d.so_detail)
-
-	return so_item_map
+	return {d.name: d.so_detail for d in si_item_details if d.so_detail}
 
 def get_sales_invoice_details(internal_reference):
 	dn_item_map = {}
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 09db7fe..5c1cbaa 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -21,7 +21,10 @@
 	else:
 		party_type = 'Supplier'
 		party = inv.supplier
-	
+
+	if not party:
+		frappe.throw(_("Please select {0} first").format(party_type))
+
 	return party_type, party
 
 def get_party_tax_withholding_details(inv, tax_withholding_category=None):
@@ -324,7 +327,7 @@
 		net_total, ldc.certificate_limit
 	):
 		tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
-	
+
 	return tds_amount
 
 def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
index bd7a126..4c7faf4 100644
--- a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
+++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json
@@ -1,5 +1,6 @@
 {
  "attach_print": 0,
+ "channel": "Email",
  "condition": "doc.auto_created",
  "creation": "2018-04-25 14:19:05.440361",
  "days_in_advance": 0,
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py
index 1729abc..287b8a7 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.py
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py
@@ -5,7 +5,8 @@
 import frappe
 from frappe import _
 from frappe.utils import flt, cint
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
+	get_filtered_list_for_consolidated_report)
 
 def execute(filters=None):
 	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@@ -132,6 +133,10 @@
 	if filters.get('accumulated_values'):
 		period_list = [period_list[-1]]
 
+	# from consolidated financial statement
+	if filters.get('accumulated_in_group_company'):
+		period_list = get_filtered_list_for_consolidated_report(period_list)
+
 	for period in period_list:
 		key = period if consolidated else period.key
 		if asset:
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index cf0946b..3577457 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _
 from frappe.utils import cint, cstr
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, get_filtered_list_for_consolidated_report)
 from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
 from erpnext.accounts.utils import get_fiscal_year
 from six import iteritems
@@ -67,9 +67,9 @@
 			section_data.append(account_data)
 
 		add_total_row_account(data, section_data, cash_flow_account['section_footer'],
-			period_list, company_currency, summary_data)
+			period_list, company_currency, summary_data, filters)
 
-	add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data)
+	add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters)
 	columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
 
 	chart = get_chart_data(columns, data)
@@ -162,18 +162,26 @@
 
 	return start_date
 
-def add_total_row_account(out, data, label, period_list, currency, summary_data, consolidated = False):
+def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False):
 	total_row = {
 		"account_name": "'" + _("{0}").format(label) + "'",
 		"account": "'" + _("{0}").format(label) + "'",
 		"currency": currency
 	}
+
+	summary_data[label] = 0
+
+	# from consolidated financial statement
+	if filters.get('accumulated_in_group_company'):
+		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
+
 	for row in data:
 		if row.get("parent_account"):
 			for period in period_list:
 				key = period if consolidated else period['key']
 				total_row.setdefault(key, 0.0)
 				total_row[key] += row.get(key, 0.0)
+				summary_data[label] += row.get(key)
 
 			total_row.setdefault("total", 0.0)
 			total_row["total"] += row["total"]
@@ -181,7 +189,6 @@
 	out.append(total_row)
 	out.append({})
 
-	summary_data[label] = total_row["total"]
 
 def get_report_summary(summary_data, currency):
 	report_summary = []
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index 094f5db..7793af7 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -94,7 +94,7 @@
 
 	chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
 
-	report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True)
+	report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, filters, True)
 
 	return data, None, chart, report_summary
 
@@ -149,9 +149,9 @@
 			section_data.append(account_data)
 
 		add_total_row_account(data, section_data, cash_flow_account['section_footer'],
-			companies, company_currency, summary_data, True)
+			companies, company_currency, summary_data, filters, True)
 
-	add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, True)
+	add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, filters, True)
 
 	report_summary = get_cash_flow_summary(summary_data, company_currency)
 
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 14efa1f..d20ddbd 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -119,10 +119,10 @@
 
 def validate_dates(from_date, to_date):
 	if not from_date or not to_date:
-		frappe.throw("From Date and To Date are mandatory")
+		frappe.throw(_("From Date and To Date are mandatory"))
 
 	if to_date < from_date:
-		frappe.throw("To Date cannot be less than From Date")
+		frappe.throw(_("To Date cannot be less than From Date"))
 
 def get_months(start_date, end_date):
 	diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month)
@@ -522,4 +522,12 @@
 				"width": 150
 			})
 
-	return columns
\ No newline at end of file
+	return columns
+
+def get_filtered_list_for_consolidated_report(filters, period_list):
+	filtered_summary_list = []
+	for period in period_list:
+		if period == filters.get('company'):
+			filtered_summary_list.append(period)
+
+	return filtered_summary_list
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index 52f7fe2..cfbd7fd 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -116,22 +116,19 @@
 		frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
 
 def get_conditions(filters):
-	conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format(
-		company=filters.get("company"),
-		from_date=filters.get("from_date"),
-		to_date=filters.get("to_date"))
+	conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s"
 
 	if filters.get("pos_profile"):
-		conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile"))
+		conditions += " AND pos_profile = %(pos_profile)s"
 	
 	if filters.get("owner"):
-		conditions += " AND owner = %(owner)s".format(owner=filters.get("owner"))
+		conditions += " AND owner = %(owner)s"
 	
 	if filters.get("customer"):
-		conditions += " AND customer = %(customer)s".format(customer=filters.get("customer"))
+		conditions += " AND customer = %(customer)s"
 	
 	if filters.get("is_return"):
-		conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return"))
+		conditions += " AND is_return = %(is_return)s"
 	
 	if filters.get("mode_of_payment"):
 		conditions += """
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
index fe261b3..5d04824 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
@@ -5,7 +5,8 @@
 import frappe
 from frappe import _
 from frappe.utils import flt
-from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
+	get_filtered_list_for_consolidated_report)
 
 def execute(filters=None):
 	period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
@@ -33,13 +34,17 @@
 	chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
 
 	currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency")
-	report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency)
+	report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters)
 
 	return columns, data, None, chart, report_summary
 
-def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, consolidated=False):
+def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, filters, consolidated=False):
 	net_income, net_expense, net_profit = 0.0, 0.0, 0.0
 
+	# from consolidated financial statement
+	if filters.get('accumulated_in_group_company'):
+		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
+
 	for period in period_list:
 		key = period if consolidated else period.key
 		if income:
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 9aff144..8799275 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -195,8 +195,7 @@
 				# If depreciation is already completed (for double declining balance)
 				if skip_row: continue
 
-				depreciation_amount = self.get_depreciation_amount(value_after_depreciation,
-					d.total_number_of_depreciations, d)
+				depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
 
 				if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
 					schedule_date = add_months(d.depreciation_start_date,
@@ -208,7 +207,7 @@
 
 				# For first row
 				if has_pro_rata and n==0:
-					depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
+					depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
 						self.available_for_use_date, d.depreciation_start_date)
 
 					# For first depr schedule date will be the start date
@@ -220,7 +219,7 @@
 					to_date = add_months(self.available_for_use_date,
 						n * cint(d.frequency_of_depreciation))
 
-					depreciation_amount, days, months = get_pro_rata_amt(d,
+					depreciation_amount, days, months = self.get_pro_rata_amt(d,
 						depreciation_amount, schedule_date, to_date)
 
 					monthly_schedule_date = add_months(schedule_date, 1)
@@ -365,24 +364,6 @@
 	def get_value_after_depreciation(self, idx):
 		return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
 
-	def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row):
-		precision = self.precision("gross_purchase_amount")
-
-		if row.depreciation_method in ("Straight Line", "Manual"):
-			depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked))
-
-			if not depreciation_left:
-				frappe.msgprint(_("All the depreciations has been booked"))
-				depreciation_amount = flt(row.expected_value_after_useful_life)
-				return depreciation_amount
-
-			depreciation_amount = (flt(row.value_after_depreciation) -
-				flt(row.expected_value_after_useful_life)) / depreciation_left
-		else:
-			depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
-
-		return depreciation_amount
-
 	def validate_expected_value_after_useful_life(self):
 		for row in self.get('finance_books'):
 			accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
@@ -575,6 +556,13 @@
 
 			return 100 * (1 - flt(depreciation_rate, float_precision))
 
+	def get_pro_rata_amt(self, row, depreciation_amount, from_date, to_date):
+		days = date_diff(to_date, from_date)
+		months = month_diff(to_date, from_date)
+		total_days = get_total_days(to_date, row.frequency_of_depreciation)
+
+		return (depreciation_amount * flt(days)) / flt(total_days), days, months
+
 def update_maintenance_status():
 	assets = frappe.get_all(
 		"Asset", filters={"docstatus": 1, "maintenance_required": 1}
@@ -758,15 +746,20 @@
 def is_cwip_accounting_enabled(asset_category):
 	return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
 
-def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
-	days = date_diff(to_date, from_date)
-	months = month_diff(to_date, from_date)
-	total_days = get_total_days(to_date, row.frequency_of_depreciation)
-
-	return (depreciation_amount * flt(days)) / flt(total_days), days, months
-
 def get_total_days(date, frequency):
 	period_start_date = add_months(date,
 		cint(frequency) * -1)
 
 	return date_diff(date, period_start_date)
+
+@erpnext.allow_regional
+def get_depreciation_amount(asset, depreciable_value, row):
+	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
+
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		depreciation_amount = (flt(row.value_after_depreciation) -
+			flt(row.expected_value_after_useful_life)) / depreciation_left
+	else:
+		depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
+
+	return depreciation_amount
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index a0d7603..30a270c 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -78,7 +78,7 @@
 		})
 
 		doc.set_missing_values()
-		self.assertEquals(doc.items[0].is_fixed_asset, 1)
+		self.assertEqual(doc.items[0].is_fixed_asset, 1)
 
 	def test_schedule_for_straight_line_method(self):
 		pr = make_purchase_receipt(item_code="Macbook Pro",
@@ -565,7 +565,7 @@
 
 		doc = make_invoice(pr.name)
 
-		self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
+		self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
 	
 	def test_asset_cwip_toggling_cases(self):
 		cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
@@ -635,6 +635,45 @@
 		frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
 		frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
 
+	def test_discounted_wdv_depreciation_rate_for_indian_region(self):
+		# set indian company
+		company_flag = frappe.flags.company
+		frappe.flags.company = "_Test Company"
+
+		pr = make_purchase_receipt(item_code="Macbook Pro",
+			qty=1, rate=8000.0, location="Test Location")
+
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+		asset = frappe.get_doc('Asset', asset_name)
+		asset.calculate_depreciation = 1
+		asset.available_for_use_date = '2030-06-12'
+		asset.purchase_date = '2030-01-01'
+		asset.append("finance_books", {
+			"expected_value_after_useful_life": 1000,
+			"depreciation_method": "Written Down Value",
+			"total_number_of_depreciations": 3,
+			"frequency_of_depreciation": 12,
+			"depreciation_start_date": "2030-12-31"
+		})
+		asset.save(ignore_permissions=True)
+
+		self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+		expected_schedules = [
+			["2030-12-31", 1106.85, 1106.85],
+			["2031-12-31", 3446.58, 4553.43],
+			["2032-12-31", 1723.29, 6276.72],
+			["2033-06-12", 723.28, 7000.00]
+		]
+
+		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+			for d in asset.get("schedules")]
+
+		self.assertEqual(schedules, expected_schedules)
+
+		# reset indian company
+		frappe.flags.company = company_flag
+
 def create_asset_data():
 	if not frappe.db.exists("Asset Category", "Computers"):
 		create_asset_category()
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 42f4472..aaa98f2 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -187,7 +187,7 @@
 		update_child_qty_rate('Purchase Order', trans_item, po.name)
 
 		po.reload()
-		self.assertEquals(len(po.get('items')), 2)
+		self.assertEqual(len(po.get('items')), 2)
 		self.assertEqual(po.status, 'To Receive and Bill')
 		# ordered qty should increase on row addition
 		self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
@@ -234,7 +234,7 @@
 		update_child_qty_rate('Purchase Order', trans_item, po.name)
 
 		po.reload()
-		self.assertEquals(len(po.get('items')), 1)
+		self.assertEqual(len(po.get('items')), 1)
 		self.assertEqual(po.status, 'To Receive and Bill')
 
 		# ordered qty should decrease (back to initial) on row deletion
@@ -448,13 +448,13 @@
 
 		pi.load_from_db()
 
-		self.assertEquals(pi.per_received, 100.00)
-		self.assertEquals(pi.items[0].qty, pi.items[0].received_qty)
+		self.assertEqual(pi.per_received, 100.00)
+		self.assertEqual(pi.items[0].qty, pi.items[0].received_qty)
 
 		po.load_from_db()
 
-		self.assertEquals(po.per_received, 100.00)
-		self.assertEquals(po.per_billed, 100.00)
+		self.assertEqual(po.per_received, 100.00)
+		self.assertEqual(po.per_billed, 100.00)
 
 		pr.cancel()
 
@@ -674,8 +674,8 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1)
 
-		self.assertEquals(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
-		self.assertEquals(bin2.projected_qty, bin1.projected_qty - 10)
+		self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
+		self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10)
 
 		# Create stock transfer
 		rm_item = [{"item_code":"_Test FG Item","rm_item_code":"_Test Item","item_name":"_Test Item",
@@ -690,7 +690,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+		self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
 		# close PO
 		po.update_status("Closed")
@@ -698,7 +698,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
+		self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 		# Re-open PO
 		po.update_status("Submitted")
@@ -706,7 +706,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+		self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
 		make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
 			qty=40, basic_rate=100)
@@ -723,7 +723,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
+		self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 		# Cancel PR
 		pr.cancel()
@@ -731,7 +731,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+		self.assertEqual(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
 		# Make Purchase Invoice
 		pi = make_pi_from_po(po.name)
@@ -743,7 +743,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
+		self.assertEqual(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 		# Cancel PR
 		pi.cancel()
@@ -751,7 +751,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+		self.assertEqual(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
 
 		# Cancel Stock Entry
 		se.cancel()
@@ -759,7 +759,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
+		self.assertEqual(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
 
 		# Cancel PO
 		po.reload()
@@ -768,7 +768,7 @@
 			filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
 			fieldname="reserved_qty_for_sub_contract", as_dict=1)
 
-		self.assertEquals(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
+		self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
 
 	def test_exploded_items_in_subcontracted(self):
 		item_code = "_Test Subcontracted FG Item 1"
@@ -782,7 +782,7 @@
 
 		exploded_items = sorted([d.item_code for d in bom.exploded_items if not d.get('sourced_by_supplier')])
 		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
-		self.assertEquals(exploded_items, supplied_items)
+		self.assertEqual(exploded_items, supplied_items)
 
 		po1 = create_purchase_order(item_code=item_code, qty=1,
 			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=0)
@@ -790,7 +790,7 @@
 		supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items])
 		bom_items = sorted([d.item_code for d in bom.items if not d.get('sourced_by_supplier')])
 
-		self.assertEquals(supplied_items1, bom_items)
+		self.assertEqual(supplied_items1, bom_items)
 
 	def test_backflush_based_on_stock_entry(self):
 		item_code = "_Test Subcontracted FG Item 1"
@@ -840,8 +840,8 @@
 		transferred_items = sorted([d.item_code for d in se.get('items') if se.purchase_order == po.name])
 		issued_items = sorted([d.rm_item_code for d in pr.get('supplied_items')])
 
-		self.assertEquals(transferred_items, issued_items)
-		self.assertEquals(pr.get('items')[0].rm_supp_cost, 2000)
+		self.assertEqual(transferred_items, issued_items)
+		self.assertEqual(pr.get('items')[0].rm_supp_cost, 2000)
 
 
 		transferred_rm_map = frappe._dict()
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index b686dc0..3f2d339 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -838,9 +838,10 @@
 		if not self.get("items"):
 			return
 
-		earliest_schedule_date = min([d.schedule_date for d in self.get("items")])
-		if earliest_schedule_date:
-			self.schedule_date = earliest_schedule_date
+		if any(d.schedule_date for d in self.get("items")):
+			# Select earliest schedule_date.
+			self.schedule_date = min(d.schedule_date for d in self.get("items")
+							if d.schedule_date is not None)
 
 		if self.schedule_date:
 			for d in self.get('items'):
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index bc1ac5e..b31724f 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -292,11 +292,14 @@
 		cond = """(`tabProject`.customer = %s or
 			ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
 
-	fields = get_fields("Project", ["name"])
+	fields = get_fields("Project", ["name", "project_name"])
+	searchfields = frappe.get_meta("Project").get_search_fields()
+	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
 
 	return frappe.db.sql("""select {fields} from `tabProject`
-		where `tabProject`.status not in ("Completed", "Cancelled")
-			and {cond} `tabProject`.name like %(txt)s {match_cond}
+		where
+			`tabProject`.status not in ("Completed", "Cancelled")
+			and {cond} {match_cond} {scond}
 		order by
 			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
 			idx desc,
@@ -304,6 +307,7 @@
 		limit {start}, {page_len}""".format(
 			fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]),
 			cond=cond,
+			scond=searchfields,
 			match_cond=get_match_cond(doctype),
 			start=start,
 			page_len=page_len), {
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 5276da9..ed3aee5 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -98,7 +98,12 @@
 		["Draft", None],
 		["Submitted", "eval:self.docstatus == 1"],
 		["Queued", "eval:self.status == 'Queued'"],
+		["Failed", "eval:self.status == 'Failed'"],
 		["Cancelled", "eval:self.docstatus == 2"],
+	],
+	"Transaction Deletion Record": [
+		["Draft", None],
+		["Completed", "eval:self.docstatus == 1"],
 	]
 }
 
diff --git a/erpnext/education/doctype/education_settings/education_settings.py b/erpnext/education/doctype/education_settings/education_settings.py
index a85d3e7..658380e 100644
--- a/erpnext/education/doctype/education_settings/education_settings.py
+++ b/erpnext/education/doctype/education_settings/education_settings.py
@@ -31,9 +31,9 @@
 	def validate(self):
 		from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 		if self.get('instructor_created_by')=='Naming Series':
-			make_property_setter('Instructor', "naming_series", "hidden", 0, "Check")
+			make_property_setter('Instructor', "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False)
 		else:
-			make_property_setter('Instructor', "naming_series", "hidden", 1, "Check")
+			make_property_setter('Instructor', "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False)
 
 def update_website_context(context):
 	context["lms_enabled"] = frappe.get_doc("Education Settings").enable_lms
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index f0a05ed..5d5b2e1 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -335,13 +335,13 @@
 
 	if not last_order_id:
 		if shopify_settings.sync_based_on == 'Date':
-			url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format(
+			url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format(
 				get_datetime(shopify_settings.from_date)), shopify_settings)
 		else:
-			url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(
+			url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(
 				shopify_settings.from_order_id), shopify_settings)
 	else:
-		url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
+		url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
 
 	return url
 
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
index 2948796..d370fbc 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -19,7 +19,7 @@
 		mode_of_payment = frappe.get_doc("Mode of Payment", "Mpesa-_Test")
 		self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"}))
 		self.assertTrue(mode_of_payment.name)
-		self.assertEquals(mode_of_payment.type, "Phone")
+		self.assertEqual(mode_of_payment.type, "Phone")
 
 	def test_processing_of_account_balance(self):
 		mpesa_doc = create_mpesa_settings(payment_gateway_name="_Account Balance")
@@ -31,11 +31,11 @@
 
 		# test integration request creation and successful update of the status on receiving callback response
 		self.assertTrue(integration_request)
-		self.assertEquals(integration_request.status, "Completed")
+		self.assertEqual(integration_request.status, "Completed")
 
 		# test formatting of account balance received as string to json with appropriate currency symbol
 		mpesa_doc.reload()
-		self.assertEquals(mpesa_doc.account_balance, dumps({
+		self.assertEqual(mpesa_doc.account_balance, dumps({
 			"Working Account": {
 				"current_balance": "Sh 481,000.00",
 				"available_balance": "Sh 481,000.00",
@@ -60,7 +60,7 @@
 
 		pr = pos_invoice.create_payment_request()
 		# test payment request creation
-		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
 
 		# submitting payment request creates integration requests with random id
 		integration_req_ids = frappe.get_all("Integration Request", filters={
@@ -75,12 +75,12 @@
 
 		# test integration request creation and successful update of the status on receiving callback response
 		self.assertTrue(integration_request)
-		self.assertEquals(integration_request.status, "Completed")
+		self.assertEqual(integration_request.status, "Completed")
 
 		pos_invoice.reload()
 		integration_request.reload()
-		self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
-		self.assertEquals(integration_request.status, "Completed")
+		self.assertEqual(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
+		self.assertEqual(integration_request.status, "Completed")
 		
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
 		integration_request.delete()
@@ -104,7 +104,7 @@
 
 		pr = pos_invoice.create_payment_request()
 		# test payment request creation
-		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
 
 		# submitting payment request creates integration requests with random id
 		integration_req_ids = frappe.get_all("Integration Request", filters={
@@ -126,12 +126,12 @@
 			verify_transaction(**callback_response)
 			# test completion of integration request
 			integration_request = frappe.get_doc("Integration Request", integration_req_ids[i])
-			self.assertEquals(integration_request.status, "Completed")
+			self.assertEqual(integration_request.status, "Completed")
 			integration_requests.append(integration_request)
 
 		# check receipt number once all the integration requests are completed
 		pos_invoice.reload()
-		self.assertEquals(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers))
+		self.assertEqual(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers))
 
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
 		[d.delete() for d in integration_requests]
@@ -155,7 +155,7 @@
 
 		pr = pos_invoice.create_payment_request()
 		# test payment request creation
-		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
 
 		# submitting payment request creates integration requests with random id
 		integration_req_ids = frappe.get_all("Integration Request", filters={
@@ -175,7 +175,7 @@
 		verify_transaction(**callback_response)
 		# test completion of integration request
 		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
-		self.assertEquals(integration_request.status, "Completed")
+		self.assertEqual(integration_request.status, "Completed")
 
 		# now one request is completed
 		# second integration request fails
@@ -187,7 +187,7 @@
 			'name': ['not in', integration_req_ids]
 		}, pluck="name")
 
-		self.assertEquals(len(new_integration_req_ids), 1)
+		self.assertEqual(len(new_integration_req_ids), 1)
 
 		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
 		frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 16c6573..21f1db6 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -90,9 +90,9 @@
 					"bank": bank["bank_name"],
 					"account": default_gl_account.account,
 					"account_name": account["name"],
-					"account_type": account["type"] or "",
-					"account_subtype": account["subtype"] or "",
-					"mask": account["mask"] or "",
+					"account_type": account.get("type", ""),
+					"account_subtype": account.get("subtype", ""),
+					"mask": account.get("mask", ""),
 					"integration_id": account["id"],
 					"is_company_account": 1,
 					"company": company
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index cbdf906..381c5e5 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -30,14 +30,14 @@
 		webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
 		# url = get_shopify_url('admin/webhooks.json', self)
 		created_webhooks = [d.method for d in self.webhooks]
-		url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
+		url = get_shopify_url('admin/api/2021-04/webhooks.json', self)
 		for method in webhooks:
 			session = get_request_session()
 			try:
 				res = session.post(url, data=json.dumps({
 					"webhook": {
 						"topic": method,
-						"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
+						"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True),
 						"format": "json"
 						}
 					}), headers=get_header(self))
@@ -56,7 +56,7 @@
 		deleted_webhooks = []
 
 		for d in self.webhooks:
-			url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
+			url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self)
 			try:
 				res = session.delete(url, headers=get_header(self))
 				res.raise_for_status()
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
index 7866fde..2af57f4 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py
@@ -32,10 +32,12 @@
 		raise e
 
 def create_customer_address(customer, shopify_customer):
-	if not shopify_customer.get("addresses"):
-		return
+	addresses = shopify_customer.get("addresses", [])
 
-	for i, address in enumerate(shopify_customer.get("addresses")):
+	if not addresses and "default_address" in shopify_customer:
+		addresses.append(shopify_customer["default_address"])
+
+	for i, address in enumerate(addresses):
 		address_title, address_type = get_address_title_and_type(customer.customer_name, i)
 		try :
 			frappe.get_doc({
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
index f9f0bb3..16efb6c 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
@@ -8,7 +8,7 @@
 shopify_variants_attr_list = ["option1", "option2", "option3"]
 
 def sync_item_from_shopify(shopify_settings, item):
-	url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
+	url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
 	session = get_request_session()
 
 	try:
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 362f6cf..3840e78 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -28,7 +28,7 @@
 
 	return innerfn
 
-def get_webhook_address(connector_name, method, exclude_uri=False):
+def get_webhook_address(connector_name, method, exclude_uri=False, force_https=False):
 	endpoint = "erpnext.erpnext_integrations.connectors.{0}.{1}".format(connector_name, method)
 
 	if exclude_uri:
@@ -39,7 +39,11 @@
 	except RuntimeError:
 		url = "http://localhost:8000"
 
-	server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint)
+	url_data = urlparse(url)
+	scheme = "https" if force_https else url_data.scheme
+	netloc = url_data.netloc
+
+	server_url = f"{scheme}://{netloc}/api/method/{endpoint}"
 
 	return server_url
 
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
index fb72073..03e96a4 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
@@ -17,7 +17,7 @@
 
 		procedure_template.disabled = 1
 		procedure_template.save()
-		self.assertEquals(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
+		self.assertEqual(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1)
 
 	def test_consumables(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
index 79ab8a4..c9f0029 100644
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
@@ -18,7 +18,7 @@
 
 		lab_template.disabled = 1
 		lab_template.save()
-		self.assertEquals(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1)
+		self.assertEqual(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1)
 
 		lab_template.reload()
 
@@ -57,7 +57,7 @@
 
 		# sample collection should not be created
 		lab_test.reload()
-		self.assertEquals(lab_test.sample, None)
+		self.assertEqual(lab_test.sample, None)
 
 	def test_create_lab_tests_from_sales_invoice(self):
 		sales_invoice = create_sales_invoice()
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index 2bb8a53..5f2dc48 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -20,13 +20,13 @@
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
 		appointment = create_appointment(patient, practitioner, nowdate())
-		self.assertEquals(appointment.status, 'Open')
+		self.assertEqual(appointment.status, 'Open')
 		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
-		self.assertEquals(appointment.status, 'Scheduled')
+		self.assertEqual(appointment.status, 'Scheduled')
 		encounter = create_encounter(appointment)
-		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
+		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
 		encounter.cancel()
-		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
+		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
 
 	def test_start_encounter(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
diff --git a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
index 7fb159d..d079bed 100644
--- a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
+++ b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
@@ -18,24 +18,24 @@
 
 	def test_status(self):
 		plan = create_therapy_plan()
-		self.assertEquals(plan.status, 'Not Started')
+		self.assertEqual(plan.status, 'Not Started')
 
 		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
 		frappe.get_doc(session).submit()
-		self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'In Progress')
+		self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'In Progress')
 
 		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
 		frappe.get_doc(session).submit()
-		self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
+		self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
 
 		patient, medical_department, practitioner = create_healthcare_docs()
 		appointment = create_appointment(patient, practitioner, nowdate())		
 		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
 		session = frappe.get_doc(session)
 		session.submit()
-		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
+		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
 		session.cancel()
-		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
+		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
 
 	def test_therapy_plan_from_template(self):
 		patient = create_patient()
@@ -49,7 +49,7 @@
 		si.save()
 
 		therapy_plan_template_amt = frappe.db.get_value('Therapy Plan Template', template, 'total_amount')
-		self.assertEquals(si.items[0].amount, therapy_plan_template_amt)
+		self.assertEqual(si.items[0].amount, therapy_plan_template_amt)
 
 
 def create_therapy_plan(template=None):
diff --git a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py
index 03a1be8..21f6369 100644
--- a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py
+++ b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py
@@ -13,7 +13,7 @@
 
 		therapy_type.disabled = 1
 		therapy_type.save()
-		self.assertEquals(frappe.db.get_value('Item', therapy_type.item, 'disabled'), 1)
+		self.assertEqual(frappe.db.get_value('Item', therapy_type.item, 'disabled'), 1)
 
 def create_therapy_type():
 	exercise = create_exercise_type()
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index bb6cd8b..9d1ce9b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -426,7 +426,8 @@
 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
 		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
-		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields'
+		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
+		'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount'
 	},
 	'United Arab Emirates': {
 		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
index 74ce301..3b99c57 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
@@ -68,19 +68,19 @@
 		filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
 		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters)
 
-		self.assertEquals(len(leave_ledger_entry), 1)
-		self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
-		self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
-		self.assertEquals(leave_ledger_entry[0].leaves, 1)
+		self.assertEqual(len(leave_ledger_entry), 1)
+		self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
+		self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
+		self.assertEqual(leave_ledger_entry[0].leaves, 1)
 
 		# check reverse leave ledger entry on cancellation
 		compensatory_leave_request.cancel()
 		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc')
 
-		self.assertEquals(len(leave_ledger_entry), 2)
-		self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
-		self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
-		self.assertEquals(leave_ledger_entry[0].leaves, -1)
+		self.assertEqual(len(leave_ledger_entry), 2)
+		self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
+		self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
+		self.assertEqual(leave_ledger_entry[0].leaves, -1)
 
 def get_compensatory_leave_request(employee, leave_date=today()):
 	prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request',
diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py
index 2cef509..539a360 100644
--- a/erpnext/hr/doctype/department/department.py
+++ b/erpnext/hr/doctype/department/department.py
@@ -31,7 +31,8 @@
 		return new
 
 	def on_update(self):
-		NestedSet.on_update(self)
+		if not frappe.local.flags.ignore_update_nsm:
+			super(Department, self).on_update()
 
 	def on_trash(self):
 		super(Department, self).on_trash()
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index e3e6e80..a268c15 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -14,6 +14,7 @@
   "column_break_5",
   "expense_approver",
   "approval_status",
+  "delivery_trip",
   "is_paid",
   "expense_details",
   "expenses",
@@ -365,13 +366,20 @@
    "label": "Total Taxes and Charges",
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.delivery_trip",
+   "fieldname": "delivery_trip",
+   "fieldtype": "Link",
+   "label": "Delivery Trip",
+   "options": "Delivery Trip"
   }
  ],
  "icon": "fa fa-money",
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-18 17:26:09.703215",
+ "modified": "2021-05-04 05:35:12.040199",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Claim",
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 3f22ca2..578eccf 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -88,9 +88,9 @@
 		])
 
 		for gle in gl_entries:
-			self.assertEquals(expected_values[gle.account][0], gle.account)
-			self.assertEquals(expected_values[gle.account][1], gle.debit)
-			self.assertEquals(expected_values[gle.account][2], gle.credit)
+			self.assertEqual(expected_values[gle.account][0], gle.account)
+			self.assertEqual(expected_values[gle.account][1], gle.debit)
+			self.assertEqual(expected_values[gle.account][2], gle.credit)
 
 	def test_rejected_expense_claim(self):
 		payable_account = get_payable_account(company_name)
@@ -104,11 +104,11 @@
 		})
 		expense_claim.submit()
 
-		self.assertEquals(expense_claim.status, 'Rejected')
-		self.assertEquals(expense_claim.total_sanctioned_amount, 0.0)
+		self.assertEqual(expense_claim.status, 'Rejected')
+		self.assertEqual(expense_claim.total_sanctioned_amount, 0.0)
 
 		gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': expense_claim.name})
-		self.assertEquals(len(gl_entry), 0)
+		self.assertEqual(len(gl_entry), 0)
 
 	def test_expense_approver_perms(self):
 		user = "test_approver_perm_emp@example.com"
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index 690a692..b3e1dc8 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -35,13 +35,13 @@
 		job_offer = create_job_offer(job_applicant=job_applicant.name)
 		job_offer.submit()
 		job_applicant.reload()
-		self.assertEquals(job_applicant.status, "Accepted")
+		self.assertEqual(job_applicant.status, "Accepted")
 
 		# status update after rejection
 		job_offer.status = "Rejected"
 		job_offer.submit()
 		job_applicant.reload()
-		self.assertEquals(job_applicant.status, "Rejected")
+		self.assertEqual(job_applicant.status, "Rejected")
 
 def create_job_offer(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
index 0b71036..6e7ae87 100644
--- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py
@@ -96,7 +96,7 @@
 			carry_forward=1)
 		leave_allocation_1.submit()
 
-		self.assertEquals(leave_allocation_1.unused_leaves, 10)
+		self.assertEqual(leave_allocation_1.unused_leaves, 10)
 
 		leave_allocation_1.cancel()
 
@@ -108,7 +108,7 @@
 			new_leaves_allocated=25)
 		leave_allocation_2.submit()
 
-		self.assertEquals(leave_allocation_2.unused_leaves, 5)
+		self.assertEqual(leave_allocation_2.unused_leaves, 5)
 
 	def test_carry_forward_leaves_expiry(self):
 		frappe.db.sql("delete from `tabLeave Allocation`")
@@ -145,7 +145,7 @@
 			to_date=add_months(nowdate(), 12))
 		leave_allocation_1.submit()
 
-		self.assertEquals(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated)
+		self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated)
 
 	def test_creation_of_leave_ledger_entry_on_submit(self):
 		frappe.db.sql("delete from `tabLeave Allocation`")
@@ -155,10 +155,10 @@
 
 		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_allocation.name))
 
-		self.assertEquals(len(leave_ledger_entry), 1)
-		self.assertEquals(leave_ledger_entry[0].employee, leave_allocation.employee)
-		self.assertEquals(leave_ledger_entry[0].leave_type, leave_allocation.leave_type)
-		self.assertEquals(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated)
+		self.assertEqual(len(leave_ledger_entry), 1)
+		self.assertEqual(leave_ledger_entry[0].employee, leave_allocation.employee)
+		self.assertEqual(leave_ledger_entry[0].leave_type, leave_allocation.leave_type)
+		self.assertEqual(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated)
 
 		# check if leave ledger entry is deleted on cancellation
 		leave_allocation.cancel()
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index b54c971..a4a96b8 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -16,36 +16,36 @@
 test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
 
 _test_records = [
- {
-  "company": "_Test Company",
-  "doctype": "Leave Application",
-  "employee": "_T-Employee-00001",
-  "from_date": "2013-05-01",
-  "description": "_Test Reason",
-  "leave_type": "_Test Leave Type",
-  "posting_date": "2013-01-02",
-  "to_date": "2013-05-05"
- },
- {
-  "company": "_Test Company",
-  "doctype": "Leave Application",
-  "employee": "_T-Employee-00002",
-  "from_date": "2013-05-01",
-  "description": "_Test Reason",
-  "leave_type": "_Test Leave Type",
-  "posting_date": "2013-01-02",
-  "to_date": "2013-05-05"
- },
- {
-  "company": "_Test Company",
-  "doctype": "Leave Application",
-  "employee": "_T-Employee-00001",
-  "from_date": "2013-01-15",
-  "description": "_Test Reason",
-  "leave_type": "_Test Leave Type LWP",
-  "posting_date": "2013-01-02",
-  "to_date": "2013-01-15"
- }
+	{
+		"company": "_Test Company",
+		"doctype": "Leave Application",
+		"employee": "_T-Employee-00001",
+		"from_date": "2013-05-01",
+		"description": "_Test Reason",
+		"leave_type": "_Test Leave Type",
+		"posting_date": "2013-01-02",
+		"to_date": "2013-05-05"
+	},
+	{
+		"company": "_Test Company",
+		"doctype": "Leave Application",
+		"employee": "_T-Employee-00002",
+		"from_date": "2013-05-01",
+		"description": "_Test Reason",
+		"leave_type": "_Test Leave Type",
+		"posting_date": "2013-01-02",
+		"to_date": "2013-05-05"
+	},
+	{
+		"company": "_Test Company",
+		"doctype": "Leave Application",
+		"employee": "_T-Employee-00001",
+		"from_date": "2013-01-15",
+		"description": "_Test Reason",
+		"leave_type": "_Test Leave Type LWP",
+		"posting_date": "2013-01-02",
+		"to_date": "2013-01-15"
+	}
 ]
 
 
@@ -516,9 +516,9 @@
 		leave_application.submit()
 		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_application.name))
 
-		self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee)
-		self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type)
-		self.assertEquals(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1)
+		self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee)
+		self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type)
+		self.assertEqual(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1)
 
 		# check if leave ledger entry is deleted on cancellation
 		leave_application.cancel()
@@ -549,11 +549,11 @@
 
 		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', '*', filters=dict(transaction_name=leave_application.name))
 
-		self.assertEquals(len(leave_ledger_entry), 2)
-		self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee)
-		self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type)
-		self.assertEquals(leave_ledger_entry[0].leaves, -9)
-		self.assertEquals(leave_ledger_entry[1].leaves, -2)
+		self.assertEqual(len(leave_ledger_entry), 2)
+		self.assertEqual(leave_ledger_entry[0].employee, leave_application.employee)
+		self.assertEqual(leave_ledger_entry[0].leave_type, leave_application.leave_type)
+		self.assertEqual(leave_ledger_entry[0].leaves, -9)
+		self.assertEqual(leave_ledger_entry[1].leaves, -2)
 
 	def test_leave_application_creation_after_expiry(self):
 		# test leave balance for carry forwarded allocation
@@ -566,7 +566,7 @@
 
 		create_carry_forwarded_allocation(employee, leave_type)
 
-		self.assertEquals(get_leave_balance_on(employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)), 0)
+		self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)), 0)
 
 	def test_leave_approver_perms(self):
 		employee = get_employee()
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
index aafc964..e0ffa5d 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -88,10 +88,10 @@
 
 		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_encashment.name))
 
-		self.assertEquals(len(leave_ledger_entry), 1)
-		self.assertEquals(leave_ledger_entry[0].employee, leave_encashment.employee)
-		self.assertEquals(leave_ledger_entry[0].leave_type, leave_encashment.leave_type)
-		self.assertEquals(leave_ledger_entry[0].leaves, leave_encashment.encashable_days *  -1)
+		self.assertEqual(len(leave_ledger_entry), 1)
+		self.assertEqual(leave_ledger_entry[0].employee, leave_encashment.employee)
+		self.assertEqual(leave_ledger_entry[0].leave_type, leave_encashment.leave_type)
+		self.assertEqual(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1)
 
 		# check if leave ledger entry is deleted on cancellation
 
diff --git a/erpnext/hr/notification/training_feedback/training_feedback.json b/erpnext/hr/notification/training_feedback/training_feedback.json
index 2cc064f..92b68a9 100644
--- a/erpnext/hr/notification/training_feedback/training_feedback.json
+++ b/erpnext/hr/notification/training_feedback/training_feedback.json
@@ -1,5 +1,6 @@
 {
  "attach_print": 0,
+ "channel": "Email",
  "creation": "2017-08-11 03:17:11.769210",
  "days_in_advance": 0,
  "docstatus": 0,
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index fae6f86..fa4707c 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -55,9 +55,9 @@
 
 	def test_loan(self):
 		loan = frappe.get_doc("Loan", {"applicant":self.applicant1})
-		self.assertEquals(loan.monthly_repayment_amount, 15052)
-		self.assertEquals(flt(loan.total_interest_payable, 0), 21034)
-		self.assertEquals(flt(loan.total_payment, 0), 301034)
+		self.assertEqual(loan.monthly_repayment_amount, 15052)
+		self.assertEqual(flt(loan.total_interest_payable, 0), 21034)
+		self.assertEqual(flt(loan.total_payment, 0), 301034)
 
 		schedule = loan.repayment_schedule
 
@@ -72,9 +72,9 @@
 		loan.monthly_repayment_amount = 14000
 		loan.save()
 
-		self.assertEquals(len(loan.repayment_schedule), 22)
-		self.assertEquals(flt(loan.total_interest_payable, 0), 22712)
-		self.assertEquals(flt(loan.total_payment, 0), 302712)
+		self.assertEqual(len(loan.repayment_schedule), 22)
+		self.assertEqual(flt(loan.total_interest_payable, 0), 22712)
+		self.assertEqual(flt(loan.total_payment, 0), 302712)
 
 	def test_loan_with_security(self):
 
@@ -89,7 +89,7 @@
 
 		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods",
 			12, loan_application)
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 	def test_loan_disbursement(self):
 		pledge = [{
@@ -102,7 +102,7 @@
 		create_pledge(loan_application)
 
 		loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application)
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		loan.submit()
 
@@ -120,8 +120,8 @@
 			filters = {'voucher_type': 'Loan Disbursement', 'voucher_no': loan_disbursement_entry2.name}
 		)
 
-		self.assertEquals(loan.status, "Disbursed")
-		self.assertEquals(loan.disbursed_amount, 1000000)
+		self.assertEqual(loan.status, "Disbursed")
+		self.assertEqual(loan.disbursed_amount, 1000000)
 		self.assertTrue(gl_entries1)
 		self.assertTrue(gl_entries2)
 
@@ -137,7 +137,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -156,15 +156,15 @@
 		repayment_entry.submit()
 
 		penalty_amount = (accrued_interest_amount * 5 * 25) / 100
-		self.assertEquals(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0))
+		self.assertEqual(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0))
 
 		amounts = frappe.db.get_all('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount'])
 
 		loan.load_from_db()
 
 		total_interest_paid = amounts[0]['paid_interest_amount'] + amounts[1]['paid_interest_amount']
-		self.assertEquals(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable)
-		self.assertEquals(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid -
+		self.assertEqual(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable)
+		self.assertEqual(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid -
 			 penalty_amount - total_interest_paid, 0))
 
 	def test_loan_closure(self):
@@ -179,7 +179,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -204,12 +204,12 @@
 
 		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
 
-		self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0))
-		self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
+		self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
 
 		request_loan_closure(loan.name)
 		loan.load_from_db()
-		self.assertEquals(loan.status, "Loan Closure Requested")
+		self.assertEqual(loan.status, "Loan Closure Requested")
 
 	def test_loan_repayment_for_term_loan(self):
 		pledges = [{
@@ -241,8 +241,8 @@
 		amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
 			'paid_principal_amount'])
 
-		self.assertEquals(amounts[0], 11250.00)
-		self.assertEquals(amounts[1], 78303.00)
+		self.assertEqual(amounts[0], 11250.00)
+		self.assertEqual(amounts[1], 78303.00)
 
 	def test_security_shortfall(self):
 		pledges = [{
@@ -268,17 +268,17 @@
 		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
 		self.assertTrue(loan_security_shortfall)
 
-		self.assertEquals(loan_security_shortfall.loan_amount, 1000000.00)
-		self.assertEquals(loan_security_shortfall.security_value, 800000.00)
-		self.assertEquals(loan_security_shortfall.shortfall_amount, 600000.00)
+		self.assertEqual(loan_security_shortfall.loan_amount, 1000000.00)
+		self.assertEqual(loan_security_shortfall.security_value, 800000.00)
+		self.assertEqual(loan_security_shortfall.shortfall_amount, 600000.00)
 
 		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
 			where loan_security='Test Security 2'""")
 
 		create_process_loan_security_shortfall()
 		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
-		self.assertEquals(loan_security_shortfall.status, "Completed")
-		self.assertEquals(loan_security_shortfall.shortfall_amount, 0)
+		self.assertEqual(loan_security_shortfall.status, "Completed")
+		self.assertEqual(loan_security_shortfall.shortfall_amount, 0)
 
 	def test_loan_security_unpledge(self):
 		pledge = [{
@@ -292,7 +292,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -312,7 +312,7 @@
 
 		request_loan_closure(loan.name)
 		loan.load_from_db()
-		self.assertEquals(loan.status, "Loan Closure Requested")
+		self.assertEqual(loan.status, "Loan Closure Requested")
 
 		unpledge_request = unpledge_security(loan=loan.name, save=1)
 		unpledge_request.submit()
@@ -323,11 +323,11 @@
 		pledged_qty = get_pledged_security_qty(loan.name)
 
 		self.assertEqual(loan.status, 'Closed')
-		self.assertEquals(sum(pledged_qty.values()), 0)
+		self.assertEqual(sum(pledged_qty.values()), 0)
 
 		amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5))
 		self.assertEqual(amounts['pending_principal_amount'], 0)
-		self.assertEquals(amounts['payable_principal_amount'], 0.0)
+		self.assertEqual(amounts['payable_principal_amount'], 0.0)
 		self.assertEqual(amounts['interest_amount'], 0)
 
 	def test_partial_loan_security_unpledge(self):
@@ -346,7 +346,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -379,7 +379,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		unpledge_map = {'Test Security 1': 4000}
 		unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
@@ -450,7 +450,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -475,7 +475,7 @@
 
 		request_loan_closure(loan.name)
 		loan.load_from_db()
-		self.assertEquals(loan.status, "Loan Closure Requested")
+		self.assertEqual(loan.status, "Loan Closure Requested")
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
 		self.assertEqual(amounts['pending_principal_amount'], 0.0)
@@ -492,7 +492,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -533,8 +533,8 @@
 		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)
+		self.assertEqual(loan.loan_amount, 1000000)
+		self.assertEqual(calculated_penalty_amount, penalty_amount)
 
 	def test_penalty_repayment(self):
 		loan, dummy = create_loan_scenario_for_penalty(self)
@@ -547,13 +547,13 @@
 		repayment_entry.submit()
 
 		amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01')
-		self.assertEquals(amounts['penalty_amount'], second_penalty)
+		self.assertEqual(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)
+		self.assertEqual(amounts['penalty_amount'], 0)
 
 	def test_loan_write_off_limit(self):
 		pledge = [{
@@ -567,7 +567,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -589,15 +589,15 @@
 
 		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
 
-		self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0))
-		self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
+		self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEquals(flt(amounts['pending_principal_amount'], 0), 50)
+		self.assertEqual(flt(amounts['pending_principal_amount'], 0), 50)
 
 		request_loan_closure(loan.name)
 		loan.load_from_db()
-		self.assertEquals(loan.status, "Loan Closure Requested")
+		self.assertEqual(loan.status, "Loan Closure Requested")
 
 	def test_loan_amount_write_off(self):
 		pledge = [{
@@ -611,7 +611,7 @@
 		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -633,17 +633,17 @@
 
 		amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)'])
 
-		self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0))
-		self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
+		self.assertEqual(flt(amount, 0),flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0)
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEquals(flt(amounts['pending_principal_amount'], 0), 100)
+		self.assertEqual(flt(amounts['pending_principal_amount'], 0), 100)
 
 		we = make_loan_write_off(loan.name, amount=amounts['pending_principal_amount'])
 		we.submit()
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
+		self.assertEqual(flt(amounts['pending_principal_amount'], 0), 0)
 
 def create_loan_scenario_for_penalty(doc):
 	pledge = [{
diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
index a875387..da56710 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -87,7 +87,7 @@
 		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date='2019-10-01')
 		loan.submit()
 
-		self.assertEquals(loan.loan_amount, 1000000)
+		self.assertEqual(loan.loan_amount, 1000000)
 
 		first_date = '2019-10-01'
 		last_date = '2019-10-30'
@@ -114,5 +114,5 @@
 		per_day_interest = get_per_day_interest(1500000, 13.5, '2019-10-30')
 		interest = per_day_interest * 15
 
-		self.assertEquals(amounts['pending_principal_amount'], 1500000)
-		self.assertEquals(amounts['interest_amount'], flt(interest + previous_interest, 2))
+		self.assertEqual(amounts['pending_principal_amount'], 1500000)
+		self.assertEqual(amounts['interest_amount'], flt(interest + previous_interest, 2))
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
index 85e008a..eb626f3 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
@@ -52,7 +52,7 @@
 		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 		loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
 
-		self.assertEquals(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0))
 
 	def test_accumulated_amounts(self):
 		pledge = [{
@@ -76,7 +76,7 @@
 		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
 		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
 
-		self.assertEquals(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0))
+		self.assertEqual(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0))
 
 		next_start_date = '2019-10-31'
 		next_end_date = '2019-11-29'
@@ -90,4 +90,4 @@
 
 		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name,
 			'process_loan_interest_accrual': process})
-		self.assertEquals(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount)
+		self.assertEqual(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount)
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index fbfd801..a09a5e3 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -29,7 +29,10 @@
 
 		frm.set_query("item", function() {
 			return {
-				query: "erpnext.manufacturing.doctype.bom.bom.item_query"
+				query: "erpnext.manufacturing.doctype.bom.bom.item_query",
+				filters: {
+					"is_stock_item": 1
+				}
 			};
 		});
 
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 979f7ca..d1f6385 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -973,6 +973,9 @@
 		if not has_variants:
 			query_filters["has_variants"] = 0
 
+	if filters and filters.get("is_stock_item"):
+		query_filters["is_stock_item"] = 1
+		
 	return frappe.get_all("Item",
 		fields = fields, filters=query_filters,
 		or_filters = or_cond_filters, order_by=order_by,
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 7108338..e1cca9e 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -223,7 +223,7 @@
 			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
 		bom_items = sorted([d.item_code for d in bom.items if d.sourced_by_supplier != 1])
 		supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
-		self.assertEquals(bom_items, supplied_items)
+		self.assertEqual(bom_items, supplied_items)
 
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
index ac9a409..80d1cdf 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
@@ -45,16 +45,16 @@
 		else:
 			doc = frappe.get_doc("BOM", bom_no)
 
-		self.assertEquals(doc.total_cost, 200)
+		self.assertEqual(doc.total_cost, 200)
 
 		frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 200)
 		update_cost()
 
 		doc.load_from_db()
-		self.assertEquals(doc.total_cost, 300)
+		self.assertEqual(doc.total_cost, 300)
 
 		frappe.db.set_value("Item", "BOM Cost Test Item 2", "valuation_rate", 100)
 		update_cost()
 
 		doc.load_from_db()
-		self.assertEquals(doc.total_cost, 200)
+		self.assertEqual(doc.total_cost, 200)
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 6b1fafe..cb1ee92 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -473,7 +473,7 @@
 	def test_cost_center_for_manufacture(self):
 		wo_order = make_wo_order_test_record()
 		ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty)
-		self.assertEquals(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC")
+		self.assertEqual(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC")
 
 	def test_operation_time_with_batch_size(self):
 		fg_item = "Test Batch Size Item For BOM"
@@ -539,11 +539,11 @@
 		ste_cancel_list.append(ste1)
 
 		ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2))
-		self.assertEquals(ste3.fg_completed_qty, 2)
+		self.assertEqual(ste3.fg_completed_qty, 2)
 
 		expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4}
 		for row in ste3.items:
-			self.assertEquals(row.qty, expected_qty.get(row.item_code))
+			self.assertEqual(row.qty, expected_qty.get(row.item_code))
 		ste_cancel_list.reverse()
 		for ste_doc in ste_cancel_list:
 			ste_doc.cancel()
@@ -577,7 +577,7 @@
 		ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
 		for ste_row in ste3.items:
 			if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse:
-				self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2)
+				self.assertEqual(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2)
 
 		ste3.submit()
 		ste_cancel_list.append(ste3)
@@ -585,7 +585,7 @@
 		ste2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
 		for ste_row in ste2.items:
 			if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse:
-				self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2)
+				self.assertEqual(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2)
 		ste_cancel_list.reverse()
 		for ste_doc in ste_cancel_list:
 			ste_doc.cancel()
diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py
index c6a534d..bbe9bf5 100644
--- a/erpnext/non_profit/doctype/donation/test_donation.py
+++ b/erpnext/non_profit/doctype/donation/test_donation.py
@@ -39,7 +39,7 @@
 		donation.on_payment_authorized()
 		donation.reload()
 
-		self.assertEquals(donation.paid, 1)
+		self.assertEqual(donation.paid, 1)
 		self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name}))
 
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 23f9fd8..82d223c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -774,5 +774,7 @@
 erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
 erpnext.patches.v13_0.update_shipment_status
 erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
+erpnext.patches.v12_0.add_ewaybill_validity_field
 erpnext.patches.v13_0.germany_make_custom_fields
 erpnext.patches.v13_0.germany_fill_debtor_creditor_number
+erpnext.patches.v13_0.set_pos_closing_as_failed
diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
new file mode 100644
index 0000000..87d98f1
--- /dev/null
+++ b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
@@ -0,0 +1,16 @@
+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': [
+			dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
+				depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill')
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index 06331d7..a6471eb 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -44,9 +44,11 @@
 		# make current item's tax map
 		item_tax_map = {}
 		for d in old_item_taxes[item_code]:
-			item_tax_map[d.tax_type] = d.tax_rate
+			if d.tax_type not in item_tax_map:
+				item_tax_map[d.tax_type] = d.tax_rate
 
-		item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
+		tax_types = []
+		item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code, tax_types=tax_types)
 
 		# update the item tax table
 		frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code)
@@ -68,7 +70,7 @@
 								and item_tax_template is NULL""".format(dt), as_dict=1):
 			item_tax_map = json.loads(d.item_tax_rate)
 			item_tax_template_name = get_item_tax_template(item_tax_templates,
-				item_tax_map, d.item_code, d.parenttype, d.parent)
+				item_tax_map, d.item_code, d.parenttype, d.parent, tax_types=tax_types)
 			frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
 
 	frappe.db.auto_commit_on_many_writes = False
@@ -78,7 +80,7 @@
 	settings.determine_address_tax_category_from = "Billing Address"
 	settings.save()
 
-def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None):
+def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None):
 	# search for previously created item tax template by comparing tax maps
 	for template, item_tax_template_map in iteritems(item_tax_templates):
 		if item_tax_map == item_tax_template_map:
@@ -126,7 +128,9 @@
 		account_type = frappe.get_cached_value("Account", tax_type, "account_type")
 
 		if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
-			item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
+			if tax_type not in tax_types:
+				item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
+				tax_types.append(tax_type)
 			item_tax_templates.setdefault(item_tax_template.title, {})
 			item_tax_templates[item_tax_template.title][tax_type] = tax_rate
 	if item_tax_template.get("taxes"):
diff --git a/erpnext/patches/v13_0/set_pos_closing_as_failed.py b/erpnext/patches/v13_0/set_pos_closing_as_failed.py
new file mode 100644
index 0000000..1c576db
--- /dev/null
+++ b/erpnext/patches/v13_0/set_pos_closing_as_failed.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+    frappe.reload_doc('accounts', 'doctype', 'pos_closing_entry')
+
+    frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'")
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
index 5efa41d..459b7ea 100644
--- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
@@ -28,5 +28,5 @@
 
 	def toggle_rounded_total(self):
 		self.disable_rounded_total = cint(self.disable_rounded_total)
-		make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check")
-		make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check")
+		make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
+		make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json
index 50db033..37381fa 100644
--- a/erpnext/payroll/notification/retention_bonus/retention_bonus.json
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.json
@@ -1,5 +1,6 @@
 {
  "attach_print": 0,
+ "channel": "Email",
  "condition": "doc.docstatus==1",
  "creation": "2018-05-15 18:52:36.362838",
  "date_changed": "bonus_payment_date",
diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py
index bf5c402..b717491 100644
--- a/erpnext/portal/doctype/homepage/test_homepage.py
+++ b/erpnext/portal/doctype/homepage/test_homepage.py
@@ -13,7 +13,7 @@
 		set_request(method='GET', path='home')
 		response = render()
 
-		self.assertEquals(response.status_code, 200)
+		self.assertEqual(response.status_code, 200)
 
 		html = frappe.safe_decode(response.get_data())
 		self.assertTrue('<section class="hero-section' in html)
diff --git a/erpnext/portal/doctype/homepage_section/test_homepage_section.py b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
index 5b3196d..f0aa554 100644
--- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py
+++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py
@@ -28,7 +28,7 @@
 		set_request(method='GET', path='home')
 		response = render()
 
-		self.assertEquals(response.status_code, 200)
+		self.assertEqual(response.status_code, 200)
 
 		html = frappe.safe_decode(response.get_data())
 
@@ -61,7 +61,7 @@
 		set_request(method='GET', path='home')
 		response = render()
 
-		self.assertEquals(response.status_code, 200)
+		self.assertEqual(response.status_code, 200)
 
 		html = frappe.safe_decode(response.get_data())
 
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 3cdfcb2..2570df7 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -458,7 +458,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 4,
- "modified": "2020-09-02 11:54:01.223620",
+ "modified": "2021-04-28 16:36:11.654632",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project",
@@ -495,11 +495,11 @@
   }
  ],
  "quick_entry": 1,
- "search_fields": "customer, status, priority, is_active",
+ "search_fields": "project_name,customer, status, priority, is_active",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
  "timeline_field": "customer",
  "title_field": "project_name",
  "track_seen": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index ed02f79..8d99b48 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -209,7 +209,7 @@
 	if parent:
 		condition = "AND parent = %(parent)s"
 	if from_time and to_time:
-		condition += "AND from_time BETWEEN %(from_time)s AND %(to_time)s"
+		condition += "AND CAST(from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
 
 	return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
 			from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1
diff --git a/erpnext/projects/report/project_profitability/project_profitability.py b/erpnext/projects/report/project_profitability/project_profitability.py
index 5ad2d85..9139d84 100644
--- a/erpnext/projects/report/project_profitability/project_profitability.py
+++ b/erpnext/projects/report/project_profitability/project_profitability.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
+from frappe.utils import flt
 
 def execute(filters=None):
 	columns, data = [], []
@@ -52,8 +53,8 @@
 
 def calculate_cost_and_profit(data):
 	for row in data:
-		row.fractional_cost = row.base_gross_pay * row.utilization
-		row.profit = row.base_grand_total - row.base_gross_pay * row.utilization
+		row.fractional_cost = flt(row.base_gross_pay) * flt(row.utilization)
+		row.profit = flt(row.base_grand_total) - flt(row.base_gross_pay) * flt(row.utilization)
 	return data
 
 def get_conditions(filters):
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index f91b432..e153e6c 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1329,7 +1329,7 @@
 
 		this.toggle_item_grid_columns(company_currency);
 
-		if(this.frm.fields_dict["operations"]) {
+		if (this.frm.doc.operations && this.frm.doc.operations.length > 0) {
 			this.frm.set_currency_labels(["operating_cost", "hour_rate"], this.frm.doc.currency, "operations");
 			this.frm.set_currency_labels(["base_operating_cost", "base_hour_rate"], company_currency, "operations");
 
@@ -1340,7 +1340,7 @@
 			});
 		}
 
-		if(this.frm.fields_dict["scrap_items"]) {
+		if (this.frm.doc.scrap_items && this.frm.doc.scrap_items.length > 0) {
 			this.frm.set_currency_labels(["rate", "amount"], this.frm.doc.currency, "scrap_items");
 			this.frm.set_currency_labels(["base_rate", "base_amount"], company_currency, "scrap_items");
 
@@ -1351,13 +1351,13 @@
 			});
 		}
 
-		if(this.frm.fields_dict["taxes"]) {
+		if (this.frm.doc.taxes && this.frm.doc.taxes.length > 0) {
 			this.frm.set_currency_labels(["tax_amount", "total", "tax_amount_after_discount"], this.frm.doc.currency, "taxes");
 
 			this.frm.set_currency_labels(["base_tax_amount", "base_total", "base_tax_amount_after_discount"], company_currency, "taxes");
 		}
 
-		if(this.frm.fields_dict["advances"]) {
+		if (this.frm.doc.advances && this.frm.doc.advances.length > 0) {
 			this.frm.set_currency_labels(["advance_amount", "allocated_amount"],
 				this.frm.doc.party_account_currency, "advances");
 		}
@@ -1379,12 +1379,12 @@
 
 	update_payment_schedule_grid_labels: function(company_currency) {
 		const me = this;
-		if (this.frm.fields_dict["payment_schedule"]) {
+		if (this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length > 0) {
 			this.frm.set_currency_labels(["base_payment_amount", "base_outstanding", "base_paid_amount"],
 				company_currency, "payment_schedule");
 			this.frm.set_currency_labels(["payment_amount", "outstanding", "paid_amount"],
 				this.frm.doc.currency, "payment_schedule");
-			
+
 			var schedule_grid = this.frm.fields_dict["payment_schedule"].grid;
 			$.each(["base_payment_amount", "base_outstanding", "base_paid_amount"], function(i, fname) {
 				if (frappe.meta.get_docfield(schedule_grid.doctype, fname))
@@ -2034,7 +2034,7 @@
 					if(r.message && !r.exc) {
 						me.frm.set_value("payment_schedule", r.message);
 						const company_currency = me.get_company_currency();
-						this.update_payment_schedule_grid_labels(company_currency);
+						me.update_payment_schedule_grid_labels(company_currency);
 					}
 				}
 			})
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 19c9073..472746a 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -48,31 +48,24 @@
 		return cint(frappe.boot.sysdefaults.allow_stale);
 	},
 
-	setup_serial_no: function() {
-		var grid_row = cur_frm.open_grid_row();
-		if(!grid_row || !grid_row.grid_form.fields_dict.serial_no ||
-			grid_row.grid_form.fields_dict.serial_no.get_status()!=="Write") return;
+	setup_serial_or_batch_no: function() {
+		let grid_row = cur_frm.open_grid_row();
+		if (!grid_row || !grid_row.grid_form.fields_dict.serial_no ||
+			grid_row.grid_form.fields_dict.serial_no.get_status() !== "Write") return;
 
-		var $btn = $('<button class="btn btn-sm btn-default">'+__("Add Serial No")+'</button>')
-			.appendTo($("<div>")
-				.css({"margin-bottom": "10px", "margin-top": "10px"})
-				.appendTo(grid_row.grid_form.fields_dict.serial_no.$wrapper));
+		frappe.model.get_value('Item', {'name': grid_row.doc.item_code},
+			['has_serial_no', 'has_batch_no'], ({has_serial_no, has_batch_no}) => {
+				Object.assign(grid_row.doc, {has_serial_no, has_batch_no});
 
-		var me = this;
-		$btn.on("click", function() {
-			let callback = '';
-			let on_close = '';
-
-			frappe.model.get_value('Item', {'name':grid_row.doc.item_code}, 'has_serial_no',
-				(data) => {
-					if(data) {
-						grid_row.doc.has_serial_no = data.has_serial_no;
-						me.show_serial_batch_selector(grid_row.frm, grid_row.doc,
-							callback, on_close, true);
-					}
+				if (has_serial_no) {
+					attach_selector_button(__("Add Serial No"),
+						grid_row.grid_form.fields_dict.serial_no.$wrapper, this, grid_row);
+				} else if (has_batch_no) {
+					attach_selector_button(__("Pick Batch No"),
+						grid_row.grid_form.fields_dict.batch_no.$wrapper, this, grid_row);
 				}
-			);
-		});
+			}
+		);
 	},
 
 	route_to_adjustment_jv: (args) => {
@@ -743,3 +736,14 @@
 		});
 	}
 });
+
+function attach_selector_button(inner_text, append_loction, context, grid_row) {
+	let $btn_div = $("<div>").css({"margin-bottom": "10px", "margin-top": "10px"})
+		.appendTo(append_loction);
+	let $btn = $(`<button class="btn btn-sm btn-default">${inner_text}</button>`)
+		.appendTo($btn_div);
+
+	$btn.on("click", function() {
+		context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
+	});
+}
diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss
index 0bb8e68..9bdaa8d 100644
--- a/erpnext/public/scss/point-of-sale.scss
+++ b/erpnext/public/scss/point-of-sale.scss
@@ -129,11 +129,20 @@
 				@extend .pointer-no-select;
 				border-radius: var(--border-radius-md);
 				box-shadow: var(--shadow-base);
+				position: relative;
 
 				&:hover {
 					transform: scale(1.02, 1.02);
 				}
 
+				.item-qty-pill {
+					position: absolute;
+					display: flex;
+					margin: var(--margin-sm);
+					justify-content: flex-end;
+					right: 0px;
+				}
+
 				.item-display {
 					display: flex;
 					align-items: center;
@@ -766,9 +775,10 @@
 		> .payment-modes {
 			display: flex;
 			padding-bottom: var(--padding-sm);
-			margin-bottom: var(--margin-xs);
+			margin-bottom: var(--margin-sm);
 			overflow-x: scroll;
 			overflow-y: hidden;
+			flex-shrink: 0;
 
 			> .payment-mode-wrapper {
 				min-width: 40%;
@@ -825,9 +835,24 @@
 		> .fields-numpad-container {
 			display: flex;
 			flex: 1;
+			height: 100%;
+    		position: relative;
+			justify-content: flex-end;
 
 			> .fields-section {
 				flex: 1;
+				position: absolute;
+				display: flex;
+				flex-direction: column;
+				width: 50%;
+				height: 100%;
+				top: 0;
+				left: 0;
+				padding-bottom: var(--margin-md);
+
+				.invoice-fields {
+					overflow-y: scroll;
+				}
 			}
 
 			> .number-pad {
@@ -835,6 +860,7 @@
 				display: flex;
 				justify-content: flex-end;
 				align-items: flex-end;
+				max-width: 50%;
 
 				.numpad-container {
 					display: grid;
@@ -861,6 +887,7 @@
 			margin-bottom: var(--margin-sm);
 			justify-content: center;
 			flex-direction: column;
+			flex-shrink: 0;
 
 			> .totals {
 				display: flex;
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
index 346ebbf..c478b0f 100644
--- a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
@@ -49,11 +49,11 @@
 		certificate.insert()
 
 		# check company details
-		self.assertEquals(certificate.company_pan_number, 'BBBTI3374C')
-		self.assertEquals(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087')
+		self.assertEqual(certificate.company_pan_number, 'BBBTI3374C')
+		self.assertEqual(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087')
 
 		# check donation details
-		self.assertEquals(certificate.amount, donation.amount)
+		self.assertEqual(certificate.amount, donation.amount)
 
 		duplicate_certificate = create_80g_certificate(args)
 		# duplicate validation
@@ -83,9 +83,9 @@
 		certificate.get_payments()
 		certificate.insert()
 
-		self.assertEquals(len(certificate.payments), 1)
-		self.assertEquals(certificate.payments[0].amount, membership.amount)
-		self.assertEquals(certificate.payments[0].invoice_id, invoice.name)
+		self.assertEqual(len(certificate.payments), 1)
+		self.assertEqual(certificate.payments[0].amount, membership.amount)
+		self.assertEqual(certificate.payments[0].invoice_id, invoice.name)
 
 
 def create_80g_certificate(args):
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 699441b..b4e7a88 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -71,13 +71,14 @@
 
 def raise_document_name_too_long_error():
 	title = _('Document ID Too Long')
-	msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice, ')
-	msg += _('document id {} exceed 16 letters. ').format(bold(_('should not')))
+	msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice')
+	msg += ', '
+	msg += _('document id {} exceed 16 letters.').format(bold(_('should not')))
 	msg += '<br><br>'
-	msg += _('You must {} your {} in order to have document id of {} length 16. ').format(
+	msg += _('You must {} your {} in order to have document id of {} length 16.').format(
 		bold(_('modify')), bold(_('naming series')), bold(_('maximum'))
 	)
-	msg += _('Please account for ammended documents too. ')
+	msg += _('Please account for ammended documents too.')
 	frappe.throw(msg, title=title)
 
 def read_json(name):
@@ -847,6 +848,7 @@
 			res = self.make_request('post', self.generate_ewaybill_url, headers, data)
 			if res.get('success'):
 				self.invoice.ewaybill = res.get('result').get('EwbNo')
+				self.invoice.eway_bill_validity = res.get('result').get('EwbValidTill')
 				self.invoice.eway_bill_cancelled = 0
 				self.invoice.update(args)
 				self.invoice.flags.updater_reference = {
@@ -944,6 +946,7 @@
 
 		self.invoice.irn = res.get('Irn')
 		self.invoice.ewaybill = res.get('EwbNo')
+		self.invoice.eway_bill_validity = res.get('EwbValidTill')
 		self.invoice.ack_no = res.get('AckNo')
 		self.invoice.ack_date = res.get('AckDt')
 		self.invoice.signed_einvoice = dec_signed_invoice
@@ -960,6 +963,7 @@
 			'label': _('IRN Generated')
 		}
 		self.update_invoice()
+
 	def attach_qrcode_image(self):
 		qrcode = self.invoice.signed_qr_code
 		doctype = self.invoice.doctype
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 9ded8da..b12e152 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -422,6 +422,9 @@
 		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_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
+			depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'),
+
 		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'),
 
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 6338056..052d7bd 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -879,3 +879,24 @@
 	if total_charges != additional_taxes:
 		diff = additional_taxes - total_charges
 		doc.get('items')[item_count - 1].taxable_value += diff
+
+def get_depreciation_amount(asset, depreciable_value, row):
+	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
+
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		depreciation_amount = (flt(row.value_after_depreciation) -
+			flt(row.expected_value_after_useful_life)) / depreciation_left
+	else:
+		rate_of_depreciation = row.rate_of_depreciation
+		# if its the first depreciation
+		if depreciable_value == asset.gross_purchase_amount:
+			# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
+			diff = date_diff(asset.available_for_use_date, row.depreciation_start_date)
+			if diff <= 180:
+				rate_of_depreciation = rate_of_depreciation / 2
+				frappe.msgprint(
+					_('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.'))
+
+		depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))
+
+	return depreciation_amount
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 49ca942..51d86ff 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -490,7 +490,7 @@
 	outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0
 
 	# Outstanding based on Sales Order
-	outstanding_based_on_so = 0.0
+	outstanding_based_on_so = 0
 
 	# if credit limit check is bypassed at sales order level,
 	# we should not consider outstanding Sales Orders, when customer credit balance report is run
@@ -501,9 +501,11 @@
 			where customer=%s and docstatus = 1 and company=%s
 			and per_billed < 100 and status != 'Closed'""", (customer, company))
 
-		outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0
+		outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0
 
 	# Outstanding based on Delivery Note, which are not created against Sales Order
+	outstanding_based_on_dn = 0
+
 	unmarked_delivery_note_items = frappe.db.sql("""select
 			dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total
 		from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
@@ -515,15 +517,29 @@
 			and ifnull(dn_item.against_sales_invoice, '') = ''
 		""", (customer, company), as_dict=True)
 
-	outstanding_based_on_dn = 0.0
+	if not unmarked_delivery_note_items:
+		return outstanding_based_on_gle + outstanding_based_on_so
+
+	si_amounts = frappe.db.sql("""
+		SELECT
+			dn_detail, sum(amount) from `tabSales Invoice Item`
+		WHERE
+			docstatus = 1
+			and dn_detail in ({})
+		GROUP BY dn_detail""".format(", ".join(
+			frappe.db.escape(dn_item.name)
+			for dn_item in unmarked_delivery_note_items
+		))
+	)
+
+	si_amounts = {si_item[0]: si_item[1] for si_item in si_amounts}
 
 	for dn_item in unmarked_delivery_note_items:
-		si_amount = frappe.db.sql("""select sum(amount)
-			from `tabSales Invoice Item`
-			where dn_detail = %s and docstatus = 1""", dn_item.name)[0][0]
+		dn_amount = flt(dn_item.amount)
+		si_amount = flt(si_amounts.get(dn_item.name))
 
-		if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total:
-			outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \
+		if dn_amount > si_amount and dn_item.base_net_total:
+			outstanding_based_on_dn += ((dn_amount - si_amount)
 				/ dn_item.base_net_total) * dn_item.base_grand_total
 
 	return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index f0143f3..527a999 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -48,7 +48,7 @@
 		sales_order.transaction_date = nowdate()
 		sales_order.insert()
 
-		self.assertEquals(sales_order.currency, "USD")
+		self.assertEqual(sales_order.currency, "USD")
 		self.assertNotEqual(sales_order.currency, quotation.currency)
 
 	def test_make_sales_order(self):
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 3137621..9873710 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -85,7 +85,7 @@
 		si1.update_billed_amount_in_sales_order = 1
 		si1.submit()
 		so.load_from_db()
-		self.assertEquals(so.per_billed, 0)
+		self.assertEqual(so.per_billed, 0)
 
 	def test_make_sales_invoice_with_terms(self):
 		so = make_sales_order(do_not_submit=True)
@@ -996,7 +996,7 @@
 		# Check if Work Orders were raised
 		for item in so_item_name:
 			wo_qty = frappe.db.sql("select sum(qty) from `tabWork Order` where sales_order=%s and sales_order_item=%s", (so.name, item))
-			self.assertEquals(wo_qty[0][0], so_item_name.get(item))
+			self.assertEqual(wo_qty[0][0], so_item_name.get(item))
 
 	def test_serial_no_based_delivery(self):
 		frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py
index d297883..b219e7e 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.py
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.py
@@ -30,8 +30,8 @@
 
 		# Make property setters to hide tax_id fields
 		for doctype in ("Sales Order", "Sales Invoice", "Delivery Note"):
-			make_property_setter(doctype, "tax_id", "hidden", self.hide_tax_id, "Check")
-			make_property_setter(doctype, "tax_id", "print_hide", self.hide_tax_id, "Check")
+			make_property_setter(doctype, "tax_id", "hidden", self.hide_tax_id, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, "tax_id", "print_hide", self.hide_tax_id, "Check", validate_fields_for_doctype=False)
 
 	def set_default_customer_group_and_territory(self):
 		if not self.customer_group:
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 8adf5bf..8e0a1e1 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -58,7 +58,7 @@
 		}
 		const pos_profile_query = {
 			query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
-			filters: { company: frappe.defaults.get_default('company') }
+			filters: { company: dialog.fields_dict.company.get_value() }
 		}
 		const dialog = new frappe.ui.Dialog({
 			title: __('Create POS Opening Entry'),
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 9384ae5..b8a82a9 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -90,14 +90,16 @@
 
 		function get_item_image_html() {
 			if (!me.hide_images && item_image) {
-				return `<div class="flex" style="margin: 8px; justify-content: flex-end">
-						<span class="indicator-pill whitespace-nowrap ${indicator_color}" id="text">${qty_to_display}</span></div>
+				return `<div class="item-qty-pill">
+							<span class="indicator-pill whitespace-nowrap ${indicator_color}">${qty_to_display}</span>
+						</div>
 						<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
 							<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
 						</div>`;
 			} else {
-				return `<div class="flex" style="margin: 8px; justify-content: flex-end">
-						<span class="indicator-pill whitespace-nowrap ${indicator_color}">${qty_to_display}</span></div>
+				return `<div class="item-qty-pill">
+							<span class="indicator-pill whitespace-nowrap ${indicator_color}">${qty_to_display}</span>
+						</div>
 						<div class="item-display abbr">${frappe.get_abbr(item.item_name)}</div>`;
 			}
 		}
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index c2b5e4f..9957aad 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -169,9 +169,9 @@
 					return;
 				}
 				frappe.call({
-					method: "erpnext.setup.doctype.company.delete_company_transactions.delete_company_transactions",
+					method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request",
 					args: {
-						company_name: data.company_name
+						company: data.company_name
 					},
 					freeze: true,
 					callback: function(r, rt) {
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 64e027d..077538d 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -613,4 +613,13 @@
 	if out:
 		return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0]
 	else:
-		return None
\ No newline at end of file
+		return None
+
+@frappe.whitelist()
+def create_transaction_deletion_request(company):
+	tdr = frappe.get_doc({
+		'doctype': 'Transaction Deletion Record',
+		'company': company
+	})
+	tdr.insert()
+	tdr.submit()
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
deleted file mode 100644
index 8367a25..0000000
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-from frappe.utils import cint
-from frappe import _
-from frappe.desk.notifications import clear_notifications
-
-import functools
-
-@frappe.whitelist()
-def delete_company_transactions(company_name):
-	frappe.only_for("System Manager")
-	doc = frappe.get_doc("Company", company_name)
-
-	if frappe.session.user != doc.owner and frappe.session.user != 'Administrator':
-		frappe.throw(_("Transactions can only be deleted by the creator of the Company"),
-			frappe.PermissionError)
-
-	delete_bins(company_name)
-	delete_lead_addresses(company_name)
-
-	for doctype in frappe.db.sql_list("""select parent from
-		tabDocField where fieldtype='Link' and options='Company'"""):
-		if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
-			"Party Account", "Employee", "Sales Taxes and Charges Template",
-			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
-			"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)
-
-	# reset company values
-	doc.total_monthly_sales = 0
-	doc.sales_monthly_history = None
-	doc.save()
-	# Clear notification counts
-	clear_notifications()
-
-def delete_for_doctype(doctype, company_name):
-	meta = frappe.get_meta(doctype)
-	company_fieldname = meta.get("fields", {"fieldtype": "Link",
-		"options": "Company"})[0].fieldname
-
-	if not meta.issingle:
-		if not meta.istable:
-			# delete communication
-			delete_communications(doctype, company_name, company_fieldname)
-
-			# delete children
-			for df in meta.get_table_fields():
-				frappe.db.sql("""delete from `tab{0}` where parent in
-					(select name from `tab{1}` where `{2}`=%s)""".format(df.options,
-						doctype, company_fieldname), company_name)
-
-		#delete version log
-		frappe.db.sql("""delete from `tabVersion` where ref_doctype=%s and docname in
-			(select name from `tab{0}` where `{1}`=%s)""".format(doctype,
-				company_fieldname), (doctype, company_name))
-
-		# delete parent
-		frappe.db.sql("""delete from `tab{0}`
-			where {1}= %s """.format(doctype, company_fieldname), company_name)
-
-		# reset series
-		naming_series = meta.get_field("naming_series")
-		if naming_series and naming_series.options:
-			prefixes = sorted(naming_series.options.split("\n"),
-				key=functools.cmp_to_key(lambda a, b: len(b) - len(a)))
-
-			for prefix in prefixes:
-				if prefix:
-					last = frappe.db.sql("""select max(name) from `tab{0}`
-						where name like %s""".format(doctype), prefix + "%")
-					if last and last[0][0]:
-						last = cint(last[0][0].replace(prefix, ""))
-					else:
-						last = 0
-
-					frappe.db.sql("""update tabSeries set current = %s
-						where name=%s""", (last, prefix))
-
-def delete_bins(company_name):
-	frappe.db.sql("""delete from tabBin where warehouse in
-			(select name from tabWarehouse where company=%s)""", company_name)
-
-def delete_lead_addresses(company_name):
-	"""Delete addresses to which leads are linked"""
-	leads = frappe.get_all("Lead", filters={"company": company_name})
-	leads = [ "'%s'"%row.get("name") for row in leads ]
-	addresses = []
-	if leads:
-		addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
-			in ({leads})""".format(leads=",".join(leads)))
-
-		if addresses:
-			addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
-
-			frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
-				name not in (select distinct dl1.parent from `tabDynamic Link` dl1
-				inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
-				and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
-
-			frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
-				and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
-
-		frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
-
-def delete_communications(doctype, company_name, company_fieldname):
-		reference_docs = frappe.get_all(doctype, filters={company_fieldname:company_name})
-		reference_doc_names = [r.name for r in reference_docs]
-
-		communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in", reference_doc_names]})
-		communication_names = [c.name for c in communications]
-
-		frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index 29f6c37..e1c803a 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -86,15 +86,6 @@
 					self.delete_mode_of_payment(template)
 					frappe.delete_doc("Company", template)
 
-	def test_delete_communication(self):
-		from erpnext.setup.doctype.company.delete_company_transactions import delete_communications
-		company = create_child_company()
-		lead = create_test_lead_in_company(company)
-		communication = create_company_communication("Lead", lead)
-		delete_communications("Lead", "Test Company", "company")
-		self.assertFalse(frappe.db.exists("Communcation", communication))
-		self.assertFalse(frappe.db.exists({"doctype":"Comunication Link", "link_name": communication}))
-
 	def delete_mode_of_payment(self, company):
 		frappe.db.sql(""" delete from `tabMode of Payment Account`
 			where company =%s """, (company))
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index 76a8450..e587217 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -60,11 +60,11 @@
 		# Make property setters to hide rounded total fields
 		for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
 			"Supplier Quotation", "Purchase Order", "Purchase Invoice"):
-			make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check")
-			make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check")
+			make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check", validate_fields_for_doctype=False)
 
-			make_property_setter(doctype, "rounded_total", "hidden", self.disable_rounded_total, "Check")
-			make_property_setter(doctype, "rounded_total", "print_hide", self.disable_rounded_total, "Check")
+			make_property_setter(doctype, "rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, "rounded_total", "print_hide", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
 
 	def toggle_in_words(self):
 		self.disable_in_words = cint(self.disable_in_words)
@@ -72,5 +72,5 @@
 		# Make property setters to hide in words fields
 		for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
 				"Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"):
-			make_property_setter(doctype, "in_words", "hidden", self.disable_in_words, "Check")
-			make_property_setter(doctype, "in_words", "print_hide", self.disable_in_words, "Check")
+			make_property_setter(doctype, "in_words", "hidden", self.disable_in_words, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, "in_words", "print_hide", self.disable_in_words, "Check", validate_fields_for_doctype=False)
diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py
index 373b0a5..c1f9433 100644
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ b/erpnext/setup/doctype/naming_series/naming_series.py
@@ -183,8 +183,8 @@
 def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True):
 	from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 	if naming_series:
-		make_property_setter(doctype, "naming_series", "hidden", 0, "Check")
-		make_property_setter(doctype, "naming_series", "reqd", 1, "Check")
+		make_property_setter(doctype, "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False)
+		make_property_setter(doctype, "naming_series", "reqd", 1, "Check", validate_fields_for_doctype=False)
 
 		# set values for mandatory
 		try:
@@ -195,15 +195,15 @@
 			pass
 
 		if hide_name_field:
-			make_property_setter(doctype, fieldname, "reqd", 0, "Check")
-			make_property_setter(doctype, fieldname, "hidden", 1, "Check")
+			make_property_setter(doctype, fieldname, "reqd", 0, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, fieldname, "hidden", 1, "Check", validate_fields_for_doctype=False)
 	else:
-		make_property_setter(doctype, "naming_series", "reqd", 0, "Check")
-		make_property_setter(doctype, "naming_series", "hidden", 1, "Check")
+		make_property_setter(doctype, "naming_series", "reqd", 0, "Check", validate_fields_for_doctype=False)
+		make_property_setter(doctype, "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False)
 
 		if hide_name_field:
-			make_property_setter(doctype, fieldname, "hidden", 0, "Check")
-			make_property_setter(doctype, fieldname, "reqd", 1, "Check")
+			make_property_setter(doctype, fieldname, "hidden", 0, "Check", validate_fields_for_doctype=False)
+			make_property_setter(doctype, fieldname, "reqd", 1, "Check", validate_fields_for_doctype=False)
 
 			# set values for mandatory
 			frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=`name` where
diff --git a/erpnext/setup/doctype/transaction_deletion_record/__init__.py b/erpnext/setup/doctype/transaction_deletion_record/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/__init__.py
diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
new file mode 100644
index 0000000..bbe6836
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestTransactionDeletionRecord(unittest.TestCase):
+	def setUp(self):
+		create_company('Dunder Mifflin Paper Co')
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def test_doctypes_contain_company_field(self):
+		tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		for doctype in tdr.doctypes:
+			contains_company = False
+			doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()['fields']
+			for doctype_field in doctype_fields:
+				if doctype_field['fieldtype'] == 'Link' and doctype_field['options'] == 'Company':
+					contains_company = True
+					break
+			self.assertTrue(contains_company)
+	
+	def test_no_of_docs_is_correct(self):
+		for i in range(5):
+			create_task('Dunder Mifflin Paper Co')
+		tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		for doctype in tdr.doctypes:
+			if doctype.doctype_name == 'Task':
+				self.assertEqual(doctype.no_of_docs, 5)
+
+	def test_deletion_is_successful(self):
+		create_task('Dunder Mifflin Paper Co')
+		create_transaction_deletion_request('Dunder Mifflin Paper Co')
+		tasks_containing_company = frappe.get_all('Task',
+		filters = {
+			'company' : 'Dunder Mifflin Paper Co'
+		})
+		self.assertEqual(tasks_containing_company, [])
+		
+def create_company(company_name):
+	company = frappe.get_doc({
+		'doctype': 'Company',
+		'company_name': company_name,
+		'default_currency': 'INR'
+	})		
+	company.insert(ignore_if_duplicate = True)
+
+def create_transaction_deletion_request(company):
+	tdr = frappe.get_doc({
+		'doctype': 'Transaction Deletion Record',
+		'company': company
+	})
+	tdr.insert()
+	tdr.submit()
+	return tdr
+
+
+def create_task(company):
+	task = frappe.get_doc({
+		'doctype': 'Task',
+		'company': company,
+		'subject': 'Delete'
+	})
+	task.insert()
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
new file mode 100644
index 0000000..20caa15
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Transaction Deletion Record', {
+	onload: function(frm) {
+		if (frm.doc.docstatus == 0) {
+			let doctypes_to_be_ignored_array;	
+			frappe.call({
+				method: 'erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.get_doctypes_to_be_ignored',
+				callback: function(r) {
+					doctypes_to_be_ignored_array = r.message;
+					populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm);
+					frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
+					frm.refresh_field('doctypes_to_be_ignored');
+				}
+			});
+		}
+
+		frm.get_field('doctypes_to_be_ignored').grid.cannot_add_rows = true;
+		frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
+		frm.refresh_field('doctypes_to_be_ignored');
+	},
+
+	refresh: function(frm) {
+		frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false);
+		frm.refresh_field('doctypes_to_be_ignored');
+	}
+	
+});
+
+function populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm) {
+	if (!(frm.doc.doctypes_to_be_ignored)) {
+		var i;
+		for (i = 0; i < doctypes_to_be_ignored_array.length; i++) {     
+			frm.add_child('doctypes_to_be_ignored', {
+				doctype_name: doctypes_to_be_ignored_array[i]					
+			});
+		}
+	}
+}
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
new file mode 100644
index 0000000..9313f95
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
@@ -0,0 +1,79 @@
+{
+ "actions": [],
+ "autoname": "TDL.####",
+ "creation": "2021-04-06 20:17:18.404716",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "doctypes",
+  "doctypes_to_be_ignored",
+  "amended_from",
+  "status"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "doctypes",
+   "fieldtype": "Table",
+   "label": "Summary",
+   "options": "Transaction Deletion Record Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "doctypes_to_be_ignored",
+   "fieldtype": "Table",
+   "label": "Excluded DocTypes",
+   "options": "Transaction Deletion Record Item"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Transaction Deletion Record",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Status",
+   "options": "Draft\nCompleted"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-05-08 23:13:48.049879",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Transaction Deletion Record",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
new file mode 100644
index 0000000..38f8de7
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.utils import cint
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.desk.notifications import clear_notifications
+
+class TransactionDeletionRecord(Document):
+	def validate(self):
+		frappe.only_for('System Manager')
+		company_obj = frappe.get_doc('Company', self.company)
+		if frappe.session.user != company_obj.owner and frappe.session.user != 'Administrator':
+			frappe.throw(_('Transactions can only be deleted by the creator of the Company or the Administrator.'), 
+				frappe.PermissionError)
+		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
+		for doctype in self.doctypes_to_be_ignored:
+			if doctype.doctype_name not in doctypes_to_be_ignored_list:
+				frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it. "), title=_("Not Allowed"))
+
+	def before_submit(self):
+		if not self.doctypes_to_be_ignored:
+			self.populate_doctypes_to_be_ignored_table()
+
+		self.delete_bins()
+		self.delete_lead_addresses()
+		
+		company_obj = frappe.get_doc('Company', self.company)
+		# reset company values
+		company_obj.total_monthly_sales = 0
+		company_obj.sales_monthly_history = None
+		company_obj.save()
+		# Clear notification counts
+		clear_notifications()
+
+		singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
+		tables = frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name')
+		doctypes_to_be_ignored_list = singles
+		for doctype in self.doctypes_to_be_ignored:
+			doctypes_to_be_ignored_list.append(doctype.doctype_name)
+
+		docfields = frappe.get_all('DocField', 
+			filters = {
+				'fieldtype': 'Link', 
+				'options': 'Company',
+				'parent': ['not in', doctypes_to_be_ignored_list]},
+			fields=['parent', 'fieldname'])
+	
+		for docfield in docfields:
+			if docfield['parent'] != self.doctype:
+				no_of_docs = frappe.db.count(docfield['parent'], {
+							docfield['fieldname'] : self.company
+						})
+
+				if no_of_docs > 0:
+					self.delete_version_log(docfield['parent'], docfield['fieldname'])
+					self.delete_communications(docfield['parent'], docfield['fieldname'])
+
+					# populate DocTypes table
+					if docfield['parent'] not in tables:
+						self.append('doctypes', {
+							'doctype_name' : docfield['parent'],
+							'no_of_docs' : no_of_docs
+						})
+
+					# delete the docs linked with the specified company
+					frappe.db.delete(docfield['parent'], {
+						docfield['fieldname'] : self.company
+					})
+
+					naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
+					if naming_series:
+						if '#' in naming_series:
+							self.update_naming_series(naming_series, docfield['parent'])	
+
+	def populate_doctypes_to_be_ignored_table(self):		
+		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
+		for doctype in doctypes_to_be_ignored_list:
+			self.append('doctypes_to_be_ignored', {
+						'doctype_name' : doctype
+					})
+
+	def update_naming_series(self, naming_series, doctype_name):
+		if '.' in naming_series:
+			prefix, hashes = naming_series.rsplit('.', 1)
+		else:
+			prefix, hashes = naming_series.rsplit('{', 1)
+		last = frappe.db.sql("""select max(name) from `tab{0}`
+						where name like %s""".format(doctype_name), prefix + '%')
+		if last and last[0][0]:
+			last = cint(last[0][0].replace(prefix, ''))
+		else:
+			last = 0
+
+		frappe.db.sql("""update tabSeries set current = %s where name=%s""", (last, prefix))
+
+	def delete_version_log(self, doctype, company_fieldname):
+		frappe.db.sql("""delete from `tabVersion` where ref_doctype=%s and docname in
+			(select name from `tab{0}` where `{1}`=%s)""".format(doctype,
+				company_fieldname), (doctype, self.company))
+
+	def delete_communications(self, doctype, company_fieldname):
+		reference_docs = frappe.get_all(doctype, filters={company_fieldname:self.company})
+		reference_doc_names = [r.name for r in reference_docs]
+
+		communications = frappe.get_all('Communication', filters={'reference_doctype':doctype,'reference_name':['in', reference_doc_names]})
+		communication_names = [c.name for c in communications]
+
+		frappe.delete_doc('Communication', communication_names, ignore_permissions=True)
+
+	def delete_bins(self):
+		frappe.db.sql("""delete from tabBin where warehouse in
+				(select name from tabWarehouse where company=%s)""", self.company)
+
+	def delete_lead_addresses(self):
+		"""Delete addresses to which leads are linked"""
+		leads = frappe.get_all('Lead', filters={'company': self.company})
+		leads = ["'%s'" % row.get("name") for row in leads]
+		addresses = []
+		if leads:
+			addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
+				in ({leads})""".format(leads=",".join(leads)))
+
+			if addresses:
+				addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
+
+				frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
+					name not in (select distinct dl1.parent from `tabDynamic Link` dl1
+					inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
+					and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
+
+				frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
+					and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
+
+			frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
+
+@frappe.whitelist()
+def get_doctypes_to_be_ignored():
+	doctypes_to_be_ignored_list = ['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',
+		'Item Default', 'Customer', 'Supplier', 'GST Account']
+	return doctypes_to_be_ignored_list
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
new file mode 100644
index 0000000..d7175dd
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.listview_settings['Transaction Deletion Record'] = {
+	get_indicator: function(doc) {
+		if (doc.docstatus == 0) {
+			return [__("Draft"), "red"];
+		} else {
+			return [__("Completed"), "green"];
+		}
+	}
+};
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/__init__.py b/erpnext/setup/doctype/transaction_deletion_record_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/__init__.py
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
new file mode 100644
index 0000000..be0be94
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
@@ -0,0 +1,39 @@
+{
+ "actions": [],
+ "creation": "2021-04-07 07:34:00.124124",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "doctype_name",
+  "no_of_docs"
+ ],
+ "fields": [
+  {
+   "fieldname": "doctype_name",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "DocType",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "no_of_docs",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Number of Docs"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-08 23:10:46.166744",
+ "modified_by": "Administrator",
+ "module": "Setup",
+ "name": "Transaction Deletion Record Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
new file mode 100644
index 0000000..2176cb1
--- /dev/null
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class TransactionDeletionRecordItem(Document):
+	pass
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 5053c6a..5c725d3 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -12,6 +12,7 @@
 
 from erpnext.accounts.doctype.account.account import RootNotEditable
 from erpnext.regional.address_template.setup import set_up_address_templates
+from frappe.utils.nestedset import rebuild_tree
 
 default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
 	"Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing",
@@ -280,13 +281,15 @@
 	set_more_defaults()
 	update_global_search_doctypes()
 
-	# path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
-	# if os.path.exists(path.encode("utf-8")):
-	# 	frappe.get_attr("erpnext.regional.{0}.setup.setup_company_independent_fixtures".format(frappe.scrub(country)))()
-
-
 def set_more_defaults():
 	# Do more setup stuff that can be done here with no dependencies
+	update_selling_defaults()
+	update_buying_defaults()
+	update_hr_defaults()
+	add_uom_data()
+	update_item_variant_settings()
+
+def update_selling_defaults():
 	selling_settings = frappe.get_doc("Selling Settings")
 	selling_settings.set_default_customer_group_and_territory()
 	selling_settings.cust_master_name = "Customer Name"
@@ -296,13 +299,7 @@
 	selling_settings.sales_update_frequency = "Each Transaction"
 	selling_settings.save()
 
-	add_uom_data()
-
-	# set no copy fields of an item doctype to item variant settings
-	doc = frappe.get_doc('Item Variant Settings')
-	doc.set_default_fields()
-	doc.save()
-
+def update_buying_defaults():
 	buying_settings = frappe.get_doc("Buying Settings")
 	buying_settings.supp_master_name = "Supplier Name"
 	buying_settings.po_required = "No"
@@ -311,12 +308,19 @@
 	buying_settings.allow_multiple_items = 1
 	buying_settings.save()
 
+def update_hr_defaults():
 	hr_settings = frappe.get_doc("HR Settings")
 	hr_settings.emp_created_by = "Naming Series"
 	hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
 	hr_settings.leave_status_notification_template = _("Leave Status Notification")
 	hr_settings.save()
 
+def update_item_variant_settings():
+	# set no copy fields of an item doctype to item variant settings
+	doc = frappe.get_doc('Item Variant Settings')
+	doc.set_default_fields()
+	doc.save()
+
 def add_uom_data():
 	# add UOMs
 	uoms = json.loads(open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_data.json")).read())
@@ -327,7 +331,7 @@
 				"uom_name": _(d.get("uom_name")),
 				"name": _(d.get("uom_name")),
 				"must_be_whole_number": d.get("must_be_whole_number")
-			}).insert(ignore_permissions=True)
+			}).db_insert()
 
 	# bootstrap uom conversion factors
 	uom_conversions = json.loads(open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_conversion_data.json")).read())
@@ -336,7 +340,7 @@
 			frappe.get_doc({
 				"doctype": "UOM Category",
 				"category_name": _(d.get("category"))
-			}).insert(ignore_permissions=True)
+			}).db_insert()
 
 		if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}):
 			uom_conversion = frappe.get_doc({
@@ -369,8 +373,8 @@
 		{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
 		{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
 	]
-
-	make_records(records)
+	for sales_stage in records:
+		frappe.get_doc(sales_stage).db_insert()
 
 def install_company(args):
 	records = [
@@ -418,7 +422,14 @@
 		{'doctype': 'Department', 'department_name': _('Legal'), 'parent_department': _('All Departments'), 'company': args.company_name},
 	]
 
-	make_records(records)
+	# Make root department with NSM updation
+	make_records(records[:1])
+
+	frappe.local.flags.ignore_update_nsm = True
+	make_records(records[1:])
+	frappe.local.flags.ignore_update_nsm = False
+
+	rebuild_tree("Department", "parent_department")
 
 
 def install_defaults(args=None):
@@ -432,7 +443,15 @@
 
 	# enable default currency
 	frappe.db.set_value("Currency", args.get("currency"), "enabled", 1)
+	frappe.db.set_value("Stock Settings", None, "email_footer_address", args.get("company_name"))
 
+	set_global_defaults(args)
+	set_active_domains(args)
+	update_stock_settings()
+	update_shopping_cart_settings(args)
+	create_bank_account(args)
+
+def set_global_defaults(args):
 	global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
 	current_fiscal_year = frappe.get_all("Fiscal Year")[0]
 
@@ -445,13 +464,10 @@
 
 	global_defaults.save()
 
-	system_settings = frappe.get_doc("System Settings")
-	system_settings.email_footer_address = args.get("company_name")
-	system_settings.save()
+def set_active_domains(args):
+	frappe.get_single('Domain Settings').set_active_domains(args.get('domains'))
 
-	domain_settings = frappe.get_single('Domain Settings')
-	domain_settings.set_active_domains(args.get('domains'))
-
+def update_stock_settings():
 	stock_settings = frappe.get_doc("Stock Settings")
 	stock_settings.item_naming_by = "Item Code"
 	stock_settings.valuation_method = "FIFO"
@@ -463,48 +479,44 @@
 	stock_settings.set_qty_in_transactions_based_on_serial_no_input = 1
 	stock_settings.save()
 
-	if args.bank_account:
-		company_name = args.company_name
-		bank_account_group =  frappe.db.get_value("Account",
-			{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
-				"company": company_name})
-		if bank_account_group:
-			bank_account = frappe.get_doc({
-				"doctype": "Account",
-				'account_name': args.bank_account,
-				'parent_account': bank_account_group,
-				'is_group':0,
-				'company': company_name,
-				"account_type": "Bank",
-			})
-			try:
-				doc = bank_account.insert()
+def create_bank_account(args):
+	if not args.bank_account:
+		return
 
-				frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False)
+	company_name = args.company_name
+	bank_account_group =  frappe.db.get_value("Account",
+		{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
+			"company": company_name})
+	if bank_account_group:
+		bank_account = frappe.get_doc({
+			"doctype": "Account",
+			'account_name': args.bank_account,
+			'parent_account': bank_account_group,
+			'is_group':0,
+			'company': company_name,
+			"account_type": "Bank",
+		})
+		try:
+			doc = bank_account.insert()
 
-			except RootNotEditable:
-				frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account))
-			except frappe.DuplicateEntryError:
-				# bank account same as a CoA entry
-				pass
+			frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False)
 
-	# Now, with fixtures out of the way, onto concrete stuff
-	records = [
+		except RootNotEditable:
+			frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account))
+		except frappe.DuplicateEntryError:
+			# bank account same as a CoA entry
+			pass
 
-		# Shopping cart: needs price lists
-		{
-			"doctype": "Shopping Cart Settings",
-			"enabled": 1,
-			'company': args.company_name,
-			# uh oh
-			'price_list': frappe.db.get_value("Price List", {"selling": 1}),
-			'default_customer_group': _("Individual"),
-			'quotation_series': "QTN-",
-		},
-	]
-
-	make_records(records)
-
+def update_shopping_cart_settings(args):
+	shopping_cart = frappe.get_doc("Shopping Cart Settings")
+	shopping_cart.update({
+		"enabled": 1,
+		'company': args.company_name,
+		'price_list': frappe.db.get_value("Price List", {"selling": 1}),
+		'default_customer_group': _("Individual"),
+		'quotation_series': "QTN-",
+	})
+	shopping_cart.update_single(shopping_cart.get_valid_dict())
 
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index e74d837..f63d269 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -52,11 +52,6 @@
 				'fail_msg': 'Failed to set defaults',
 				'tasks': [
 					{
-						'fn': setup_post_company_fixtures,
-						'args': args,
-						'fail_msg': _("Failed to setup post company fixtures")
-					},
-					{
 						'fn': setup_defaults,
 						'args': args,
 						'fail_msg': _("Failed to setup defaults")
@@ -94,9 +89,6 @@
 def setup_company(args):
 	fixtures.install_company(args)
 
-def setup_post_company_fixtures(args):
-	fixtures.install_post_company_fixtures(args)
-
 def setup_defaults(args):
 	fixtures.install_defaults(frappe._dict(args))
 
@@ -129,7 +121,6 @@
 def setup_complete(args=None):
 	stage_fixtures(args)
 	setup_company(args)
-	setup_post_company_fixtures(args)
 	setup_defaults(args)
 	stage_four(args)
 	fin(args)
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 334bdea..7875b9c 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -273,11 +273,11 @@
 	},
 
 	items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
+		erpnext.setup_serial_or_batch_no();
 	},
 
 	packed_items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
+		erpnext.setup_serial_or_batch_no();
 	},
 
 	close_delivery_note: function(doc){
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index d39b229..0c63df0 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -710,7 +710,7 @@
 		dn1.submit()
 
 		si = make_sales_invoice(dn.name)
-		self.assertEquals(si.items[0].qty, 1)
+		self.assertEqual(si.items[0].qty, 1)
 
 	def test_make_sales_invoice_from_dn_with_returned_qty_duplicate_items(self):
 		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
@@ -738,8 +738,8 @@
 		dn1.submit()
 
 		si2 = make_sales_invoice(dn.name)
-		self.assertEquals(si2.items[0].qty, 2)
-		self.assertEquals(si2.items[1].qty, 1)
+		self.assertEqual(si2.items[0].qty, 2)
+		self.assertEqual(si2.items[1].qty, 1)
 
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
index a6fbb66..68cba29 100755
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
@@ -41,6 +41,15 @@
 	},
 
 	refresh: function (frm) {
+		if (frm.doc.docstatus == 1 && frm.doc.employee) {
+			frm.add_custom_button(__('Expense Claim'), function() {
+				frappe.model.open_mapped_doc({
+					method: 'erpnext.stock.doctype.delivery_trip.delivery_trip.make_expense_claim',
+					frm: cur_frm,
+				});
+			}, __("Create"));
+		}
+
 		if (frm.doc.docstatus == 1 && frm.doc.delivery_stops.length > 0) {
 			frm.add_custom_button(__("Notify Customers via Email"), function () {
 				frm.trigger('notify_customers');
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
index 879901f..11b71c2 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
@@ -21,6 +21,7 @@
   "column_break_4",
   "vehicle",
   "departure_time",
+  "employee",
   "delivery_service_stops",
   "delivery_stops",
   "calculate_arrival_time",
@@ -176,11 +177,19 @@
    "fieldtype": "Data",
    "label": "Driver Email",
    "read_only": 1
+  },
+  {
+   "fetch_from": "driver.employee",
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "label": "Employee",
+   "options": "Employee",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-01-26 22:37:14.824021",
+ "modified": "2021-04-30 21:21:36.610142",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Trip",
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index de85bc3..81e7301 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -11,6 +11,7 @@
 from frappe.contacts.doctype.address.address import get_address_display
 from frappe.model.document import Document
 from frappe.utils import cint, get_datetime, get_link_to_form
+from frappe.model.mapper import get_mapped_doc
 
 
 class DeliveryTrip(Document):
@@ -394,3 +395,15 @@
 	employee = frappe.db.get_value("Driver", driver, "employee")
 	email = frappe.db.get_value("Employee", employee, "prefered_email")
 	return {"email": email}
+
+@frappe.whitelist()
+def make_expense_claim(source_name, target_doc=None):
+	doc = get_mapped_doc("Delivery Trip", source_name,
+		{"Delivery Trip": {
+			"doctype": "Expense Claim",
+			"field_map": {
+				"name" : "delivery_trip"
+			}
+		}}, target_doc)
+
+	return doc
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index eeea6da..1e71603 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -7,7 +7,7 @@
 
 import erpnext
 import frappe
-from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers
+from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers, make_expense_claim
 from erpnext.tests.utils import create_test_contact_and_address
 from frappe.utils import add_days, flt, now_datetime, nowdate
 
@@ -28,6 +28,10 @@
 		frappe.db.sql("delete from `tabEmail Template`")
 		frappe.db.sql("delete from `tabDelivery Trip`")
 
+	def test_expense_claim_fields_are_fetched_properly(self):
+		expense_claim = make_expense_claim(self.delivery_trip.name)
+		self.assertEqual(self.delivery_trip.name, expense_claim.delivery_trip)
+
 	def test_delivery_trip_notify_customers(self):
 		notify_customers(delivery_trip=self.delivery_trip.name)
 		self.delivery_trip.load_from_db()
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index 8d7b238..4e2d9e6 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -181,7 +181,7 @@
    "no_copy": 1,
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nOrdered\nIssued\nTransferred\nReceived",
+   "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nPartially Received\nOrdered\nIssued\nTransferred\nReceived",
    "print_hide": 1,
    "print_width": "100px",
    "read_only": 1,
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 16eea24..e5ef978 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -13,8 +13,9 @@
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import make_item
 from six import iteritems
+from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 class TestPurchaseReceipt(unittest.TestCase):
 	def setUp(self):
@@ -144,6 +145,62 @@
 		self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
 		self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
 
+	def test_duplicate_serial_nos(self):
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		item = frappe.db.exists("Item", {'item_name': 'Test Serialized Item 123'})
+		if not item:
+			item = create_item("Test Serialized Item 123")
+			item.has_serial_no = 1
+			item.serial_no_series = "TSI123-.####"
+			item.save()
+		else:
+			item = frappe.get_doc("Item", {'item_name': 'Test Serialized Item 123'})
+
+		# First make purchase receipt
+		pr = make_purchase_receipt(item_code=item.name, qty=2, rate=500)
+		pr.load_from_db()
+
+		serial_nos = frappe.db.get_value('Stock Ledger Entry',
+			{'voucher_type': 'Purchase Receipt', 'voucher_no': pr.name, 'item_code': item.name}, 'serial_no')
+
+		serial_nos = get_serial_nos(serial_nos)
+
+		self.assertEquals(get_serial_nos(pr.items[0].serial_no), serial_nos)
+
+		# Then tried to receive same serial nos in difference company
+		pr_different_company = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+			serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
+			warehouse = 'Stores - _TC1')
+
+		self.assertRaises(SerialNoDuplicateError, pr_different_company.submit)
+
+		# Then made delivery note to remove the serial nos from stock
+		dn = create_delivery_note(item_code=item.name, qty=2, rate = 1500, serial_no='\n'.join(serial_nos))
+		dn.load_from_db()
+		self.assertEquals(get_serial_nos(dn.items[0].serial_no), serial_nos)
+
+		posting_date = add_days(today(), -3)
+
+		# Try to receive same serial nos again in the same company with backdated.
+		pr1 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+			posting_date=posting_date, serial_no='\n'.join(serial_nos), do_not_submit=True)
+
+		self.assertRaises(SerialNoExistsInFutureTransaction, pr1.submit)
+
+		# Try to receive same serial nos with different company with backdated.
+		pr2 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+			posting_date=posting_date, serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
+			warehouse = 'Stores - _TC1')
+
+		self.assertRaises(SerialNoExistsInFutureTransaction, pr2.submit)
+
+		# Receive the same serial nos after the delivery note posting date and time
+		make_purchase_receipt(item_code=item.name, qty=2, rate=500, serial_no='\n'.join(serial_nos))
+
+		# Raise the error for backdated deliver note entry cancel
+		self.assertRaises(SerialNoExistsInFutureTransaction, dn.cancel)
+
 	def test_purchase_receipt_gl_entry(self):
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
 			warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
@@ -562,30 +619,6 @@
 
 		new_pr_doc.cancel()
 
-	def test_not_accept_duplicate_serial_no(self):
-		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
-
-		item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0, "has_batch_no": 0})
-		if not item_code:
-			item = make_item("Test Serial Item 1", dict(has_serial_no=1, has_batch_no=0))
-			item_code = item.name
-
-		serial_no = random_string(5)
-		pr1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no)
-		dn = create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no)
-
-		pr2 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True)
-		self.assertRaises(SerialNoDuplicateError, pr2.submit)
-
-		se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1,
-			serial_no=serial_no, basic_rate=100, do_not_submit=True)
-		se.submit()
-
-		se.cancel()
-		dn.cancel()
-		pr1.cancel()
-
 	def test_auto_asset_creation(self):
 		asset_item = "Test Asset Item"
 
@@ -620,10 +653,10 @@
 		pr = make_purchase_receipt(item_code=asset_item, qty=3)
 		assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
 
-		self.assertEquals(len(assets), 3)
+		self.assertEqual(len(assets), 3)
 
 		location = frappe.db.get_value('Asset', assets[0].name, 'location')
-		self.assertEquals(location, "Test Location")
+		self.assertEqual(location, "Test Location")
 
 		pr.cancel()
 
@@ -728,7 +761,7 @@
 		pr1.submit()
 
 		pi = make_purchase_invoice(pr.name)
-		self.assertEquals(pi.items[0].qty, 3)
+		self.assertEqual(pi.items[0].qty, 3)
 
 		pr1.cancel()
 		pr.reload()
@@ -759,8 +792,8 @@
 		pr2.submit()
 
 		pi2 = make_purchase_invoice(pr1.name)
-		self.assertEquals(pi2.items[0].qty, 2)
-		self.assertEquals(pi2.items[1].qty, 1)
+		self.assertEqual(pi2.items[0].qty, 2)
+		self.assertEqual(pi2.items[1].qty, 1)
 
 		pr2.cancel()
 		pi1.cancel()
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index c02dd2e..5ecc9f8 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -243,7 +243,7 @@
 				if frappe.db.exists("Serial No", serial_no):
 					sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
 						"delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type",
-						"purchase_document_no", "company"], as_dict=1)
+						"purchase_document_no", "company", "status"], as_dict=1)
 
 					if sr.item_code!=sle.item_code:
 						if not allow_serial_nos_with_different_item(serial_no, sle):
@@ -266,6 +266,9 @@
 							frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
 								sle.warehouse), SerialNoWarehouseError)
 
+						if not sr.purchase_document_no:
+							frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError)
+
 						if sle.voucher_type in ("Delivery Note", "Sales Invoice"):
 
 							if sr.batch_no and sr.batch_no != sle.batch_no:
@@ -382,19 +385,6 @@
 	if sn.company != sle.company:
 		return False
 
-	status = False
-	if sn.purchase_document_no:
-		if (sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and
-			sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]):
-			status = True
-
-		# If status is receipt then system will allow to in-ward the delivered serial no
-		if (status and sle.voucher_type == "Stock Entry" and frappe.db.get_value("Stock Entry",
-			sle.voucher_no, "purpose") in ("Material Receipt", "Material Transfer")):
-			status = False
-
-	return status
-
 def allow_serial_nos_with_different_item(sle_serial_no, sle):
 	"""
 		Allows same serial nos for raw materials and finished goods
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index ef7d54a..772c8df 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -996,7 +996,7 @@
 	},
 
 	items_on_form_rendered: function(doc, grid_row) {
-		erpnext.setup_serial_no();
+		erpnext.setup_serial_or_batch_no();
 	},
 
 	toggle_related_fields: function(doc) {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 48cfa51..2f76bc7 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -76,6 +76,7 @@
 		self.validate_difference_account()
 		self.set_job_card_data()
 		self.set_purpose_for_stock_entry()
+		self.validate_duplicate_serial_no()
 
 		if not self.from_bom:
 			self.fg_completed_qty = 0.0
@@ -587,6 +588,22 @@
 			self.purpose = frappe.get_cached_value('Stock Entry Type',
 				self.stock_entry_type, 'purpose')
 
+	def validate_duplicate_serial_no(self):
+		warehouse_wise_serial_nos = {}
+
+		# In case of repack the source and target serial nos could be same
+		for warehouse in ['s_warehouse', 't_warehouse']:
+			serial_nos = []
+			for row in self.items:
+				if not (row.serial_no and row.get(warehouse)): continue
+
+				for sn in get_serial_nos(row.serial_no):
+					if sn in serial_nos:
+						frappe.throw(_('The serial no {0} has added multiple times in the stock entry {1}')
+							.format(frappe.bold(sn), self.name))
+
+					serial_nos.append(sn)
+
 	def validate_purchase_order(self):
 		"""Throw exception if more raw material is transferred against Purchase Order than in
 		the raw materials supplied table"""
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 0ee6dc7..7e216d6 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -72,7 +72,7 @@
 
 				if item_dict.get("serial_nos"):
 					item.current_serial_no = item_dict.get("serial_nos")
-					if self.purpose == "Stock Reconciliation":
+					if self.purpose == "Stock Reconciliation" and not item.serial_no:
 						item.serial_no = item.current_serial_no
 
 				item.current_qty = item_dict.get("qty")
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index f18eabc..cf5d98d 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -5,40 +5,44 @@
  "doctype": "DocType",
  "engine": "InnoDB",
  "field_order": [
+  "item_defaults_section",
   "item_naming_by",
   "item_group",
   "stock_uom",
   "default_warehouse",
-  "sample_retention_warehouse",
   "column_break_4",
   "valuation_method",
+  "sample_retention_warehouse",
+  "use_naming_series",
+  "naming_series_prefix",
+  "section_break_9",
   "over_delivery_receipt_allowance",
   "role_allowed_to_over_deliver_receive",
-  "action_if_quality_inspection_is_not_submitted",
-  "show_barcode_field",
-  "clean_description_html",
-  "disable_serial_no_and_batch_selector",
-  "section_break_7",
+  "column_break_12",
   "auto_insert_price_list_rate_if_missing",
   "allow_negative_stock",
-  "column_break_10",
+  "show_barcode_field",
+  "clean_description_html",
+  "action_if_quality_inspection_is_not_submitted",
+  "section_break_7",
   "automatically_set_serial_nos_based_on_fifo",
   "set_qty_in_transactions_based_on_serial_no_input",
+  "column_break_10",
+  "disable_serial_no_and_batch_selector",
   "auto_material_request",
   "auto_indent",
+  "column_break_27",
   "reorder_email_notify",
   "inter_warehouse_transfer_settings_section",
   "allow_from_dn",
+  "column_break_31",
   "allow_from_pr",
   "control_historical_stock_transactions_section",
-  "role_allowed_to_create_edit_back_dated_transactions",
-  "column_break_26",
   "stock_frozen_upto",
   "stock_frozen_upto_days",
-  "stock_auth_role",
-  "batch_id_sb",
-  "use_naming_series",
-  "naming_series_prefix"
+  "column_break_26",
+  "role_allowed_to_create_edit_back_dated_transactions",
+  "stock_auth_role"
  ],
  "fields": [
   {
@@ -102,23 +106,24 @@
    "default": "1",
    "fieldname": "show_barcode_field",
    "fieldtype": "Check",
-   "label": "Show Barcode Field"
+   "label": "Show Barcode Field in Stock Transactions"
   },
   {
    "default": "1",
    "fieldname": "clean_description_html",
    "fieldtype": "Check",
-   "label": "Convert Item Description to Clean HTML"
+   "label": "Convert Item Description to Clean HTML in Transactions"
   },
   {
    "fieldname": "section_break_7",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Serialised and Batch Setting"
   },
   {
    "default": "0",
    "fieldname": "auto_insert_price_list_rate_if_missing",
    "fieldtype": "Check",
-   "label": "Auto Insert Price List Rate If Missing"
+   "label": "Auto Insert Item Price If Missing"
   },
   {
    "default": "0",
@@ -180,15 +185,10 @@
    "options": "Role"
   },
   {
-   "fieldname": "batch_id_sb",
-   "fieldtype": "Section Break",
-   "label": "Batch Identification"
-  },
-  {
    "default": "0",
    "fieldname": "use_naming_series",
    "fieldtype": "Check",
-   "label": "Use Naming Series"
+   "label": "Have Default Naming Series for Batch ID?"
   },
   {
    "default": "BATCH-",
@@ -242,6 +242,28 @@
    "fieldtype": "Link",
    "label": "Role Allowed to Over Deliver/Receive",
    "options": "Role"
+  },
+  {
+   "fieldname": "item_defaults_section",
+   "fieldtype": "Section Break",
+   "label": "Item Defaults"
+  },
+  {
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Stock Transactions Settings"
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_27",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_31",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "icon-cog",
@@ -249,7 +271,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-03-11 18:48:14.513055",
+ "modified": "2021-04-30 17:27:42.709231",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 3b9608b..2dd7c6f 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -30,7 +30,7 @@
 		# show/hide barcode field
 		for name in ["barcode", "barcodes", "scan_barcode"]:
 			frappe.make_property_setter({'fieldname': name, 'property': 'hidden',
-				'value': 0 if self.show_barcode_field else 1})
+				'value': 0 if self.show_barcode_field else 1}, validate_fields_for_doctype=False)
 
 		self.validate_warehouses()
 		self.cant_change_valuation_method()
@@ -67,10 +67,10 @@
 		self.toggle_warehouse_field_for_inter_warehouse_transfer()
 
 	def toggle_warehouse_field_for_inter_warehouse_transfer(self):
-		make_property_setter("Sales Invoice Item", "target_warehouse", "hidden", 1 - cint(self.allow_from_dn), "Check")
-		make_property_setter("Delivery Note Item", "target_warehouse", "hidden", 1 - cint(self.allow_from_dn), "Check")
-		make_property_setter("Purchase Invoice Item", "from_warehouse", "hidden", 1 - cint(self.allow_from_pr), "Check")
-		make_property_setter("Purchase Receipt Item", "from_warehouse", "hidden", 1 - cint(self.allow_from_pr), "Check")
+		make_property_setter("Sales Invoice Item", "target_warehouse", "hidden", 1 - cint(self.allow_from_dn), "Check", validate_fields_for_doctype=False)
+		make_property_setter("Delivery Note Item", "target_warehouse", "hidden", 1 - cint(self.allow_from_dn), "Check", validate_fields_for_doctype=False)
+		make_property_setter("Purchase Invoice Item", "from_warehouse", "hidden", 1 - cint(self.allow_from_pr), "Check", validate_fields_for_doctype=False)
+		make_property_setter("Purchase Receipt Item", "from_warehouse", "hidden", 1 - cint(self.allow_from_pr), "Check", validate_fields_for_doctype=False)
 
 
 def clean_all_descriptions():
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 3832415..d1dcdc2 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -79,7 +79,7 @@
 		get_price_list_rate(args, item, out)
 
 	if args.customer and cint(args.is_pos):
-		out.update(get_pos_profile_item_details(args.company, args))
+		out.update(get_pos_profile_item_details(args.company, args, update_data=True))
 
 	if (args.get("doctype") == "Material Request" and
 		args.get("material_request_type") == "Material Transfer"):
@@ -935,8 +935,8 @@
 	return bin_details
 
 def get_company_total_stock(item_code, company):
-	return frappe.db.sql("""SELECT sum(actual_qty) from 
-		(`tabBin` INNER JOIN `tabWarehouse` ON `tabBin`.warehouse = `tabWarehouse`.name) 
+	return frappe.db.sql("""SELECT sum(actual_qty) from
+		(`tabBin` INNER JOIN `tabWarehouse` ON `tabBin`.warehouse = `tabWarehouse`.name)
 		WHERE `tabWarehouse`.company = %s and `tabBin`.item_code = %s""",
 		(company, item_code))[0][0]
 
diff --git a/erpnext/stock/report/serial_no_ledger/__init__.py b/erpnext/stock/report/serial_no_ledger/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/__init__.py
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
new file mode 100644
index 0000000..616312e
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Serial No Ledger"] = {
+	"filters": [
+		{
+			'label': __('Item Code'),
+			'fieldtype': 'Link',
+			'fieldname': 'item_code',
+			'reqd': 1,
+			'options': 'Item',
+			get_query: function() {
+				return {
+					filters: {
+						'has_serial_no': 1
+					}
+				}
+			}
+		},
+		{
+			'label': __('Serial No'),
+			'fieldtype': 'Link',
+			'fieldname': 'serial_no',
+			'options': 'Serial No',
+			'reqd': 1
+		},
+		{
+			'label': __('Warehouse'),
+			'fieldtype': 'Link',
+			'fieldname': 'warehouse',
+			'options': 'Warehouse',
+			get_query: function() {
+				let company = frappe.query_report.get_filter_value('company');
+
+				if (company) {
+					return {
+						filters: {
+							'company': company
+						}
+					}
+				}
+			}
+		},
+		{
+			'label': __('As On Date'),
+			'fieldtype': 'Date',
+			'fieldname': 'posting_date',
+			'default': frappe.datetime.get_today()
+		},
+	]
+};
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
new file mode 100644
index 0000000..e20e74c
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
@@ -0,0 +1,33 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-04-20 13:32:41.523219",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2021-04-20 13:33:19.015829",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Serial No Ledger",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Serial No Ledger",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Purchase User"
+  },
+  {
+   "role": "Sales User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
new file mode 100644
index 0000000..c3339fd
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe import _
+from erpnext.stock.stock_ledger import get_stock_ledger_entries
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+	return columns, data
+
+def get_columns(filters):
+	columns = [{
+		'label': _('Posting Date'),
+		'fieldtype': 'Date',
+		'fieldname': 'posting_date'
+	}, {
+		'label': _('Posting Time'),
+		'fieldtype': 'Time',
+		'fieldname': 'posting_time'
+	}, {
+		'label': _('Voucher Type'),
+		'fieldtype': 'Link',
+		'fieldname': 'voucher_type',
+		'options': 'DocType',
+		'width': 220
+	}, {
+		'label': _('Voucher No'),
+		'fieldtype': 'Dynamic Link',
+		'fieldname': 'voucher_no',
+		'options': 'voucher_type',
+		'width': 220
+	}, {
+		'label': _('Company'),
+		'fieldtype': 'Link',
+		'fieldname': 'company',
+		'options': 'Company',
+		'width': 220
+	}, {
+		'label': _('Warehouse'),
+		'fieldtype': 'Link',
+		'fieldname': 'warehouse',
+		'options': 'Warehouse',
+		'width': 220
+	}]
+
+	return columns
+
+def get_data(filters):
+	return get_stock_ledger_entries(filters, '<=', order="asc") or []
+
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
index babc6dc..cb109f8 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
@@ -14,7 +14,14 @@
 			"fieldname":"warehouse",
 			"label": __("Warehouse"),
 			"fieldtype": "Link",
-			"options": "Warehouse"
+			"options": "Warehouse",
+			"get_query": () => {
+				return {
+					filters: {
+						company: frappe.query_report.get_filter_value('company')
+					}
+				}
+			}
 		},
 		{
 			"fieldname":"item_code",
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index 1183e41..808d279 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -6,6 +6,7 @@
 from frappe import _
 from frappe.utils import flt, today
 from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress
+from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_pos_reserved_qty
 
 def execute(filters=None):
 	is_reposting_item_valuation_in_progress()
@@ -49,9 +50,13 @@
 		if (re_order_level or re_order_qty) and re_order_level > bin.projected_qty:
 			shortage_qty = re_order_level - flt(bin.projected_qty)
 
+		reserved_qty_for_pos = get_pos_reserved_qty(bin.item_code, bin.warehouse)
+		if reserved_qty_for_pos:
+			bin.projected_qty -= reserved_qty_for_pos
+
 		data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
 			item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty,
-			bin.reserved_qty, bin.reserved_qty_for_production, bin.reserved_qty_for_sub_contract,
+			bin.reserved_qty, bin.reserved_qty_for_production, bin.reserved_qty_for_sub_contract, reserved_qty_for_pos,
 			bin.projected_qty, re_order_level, re_order_qty, shortage_qty])
 
 		if include_uom:
@@ -74,9 +79,11 @@
 		{"label": _("Requested Qty"), "fieldname": "indented_qty", "fieldtype": "Float", "width": 110, "convertible": "qty"},
 		{"label": _("Ordered Qty"), "fieldname": "ordered_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
 		{"label": _("Reserved Qty"), "fieldname": "reserved_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
-		{"label": _("Reserved Qty for Production"), "fieldname": "reserved_qty_for_production", "fieldtype": "Float",
+		{"label": _("Reserved for Production"), "fieldname": "reserved_qty_for_production", "fieldtype": "Float",
 			"width": 100, "convertible": "qty"},
-		{"label": _("Reserved for sub contracting"), "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float",
+		{"label": _("Reserved for Sub Contracting"), "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float",
+			"width": 100, "convertible": "qty"},
+		{"label": _("Reserved for POS Transactions"), "fieldname": "reserved_qty_for_pos", "fieldtype": "Float",
 			"width": 100, "convertible": "qty"},
 		{"label": _("Projected Qty"), "fieldname": "projected_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
 		{"label": _("Reorder Level"), "fieldname": "re_order_level", "fieldtype": "Float", "width": 100, "convertible": "qty"},
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
index ed52393..59c253c 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -51,7 +51,7 @@
 			INNER JOIN `tabWarehouse` warehouse
 				ON warehouse.name = ledger.warehouse
 			WHERE
-				actual_qty != 0 %s""" % (columns, conditions))
+				ledger.actual_qty != 0 %s""" % (columns, conditions))
 
 def validate_filters(filters):
 	if filters.get("group_by") == 'Company' and \
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index bbfcb7a..9729987 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -2,9 +2,11 @@
 # License: GNU General Public License v3. See license.txt
 from __future__ import unicode_literals
 
-import frappe, erpnext
+import frappe
+import erpnext
+import copy
 from frappe import _
-from frappe.utils import cint, flt, cstr, now, now_datetime
+from frappe.utils import cint, flt, cstr, now, get_link_to_form
 from frappe.model.meta import get_field_precision
 from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
 from erpnext.stock.utils import get_bin
@@ -13,6 +15,8 @@
 
 # future reposting
 class NegativeStockError(frappe.ValidationError): pass
+class SerialNoExistsInFutureTransaction(frappe.ValidationError):
+	pass
 
 _exceptions = frappe.local('stockledger_exceptions')
 # _exceptions = []
@@ -27,6 +31,9 @@
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 		for sle in sl_entries:
+			if sle.serial_no:
+				validate_serial_no(sle)
+
 			if cancel:
 				sle['actual_qty'] = -flt(sle.get('actual_qty'))
 
@@ -46,6 +53,30 @@
 			args = sle_doc.as_dict()
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
+def validate_serial_no(sle):
+	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+	for sn in get_serial_nos(sle.serial_no):
+		args = copy.deepcopy(sle)
+		args.serial_no = sn
+		args.warehouse = ''
+
+		vouchers = []
+		for row in get_stock_ledger_entries(args, '>'):
+			voucher_type = frappe.bold(row.voucher_type)
+			voucher_no = frappe.bold(get_link_to_form(row.voucher_type, row.voucher_no))
+			vouchers.append(f'{voucher_type} {voucher_no}')
+
+		if vouchers:
+			serial_no = frappe.bold(sn)
+			msg = (f'''The serial no {serial_no} has been used in the future transactions so you need to cancel them first.
+				The list of the transactions are as below.''' + '<br><br><ul><li>')
+
+			msg += '</li><li>'.join(vouchers)
+			msg += '</li></ul>'
+
+			title = 'Cannot Submit' if not sle.get('is_cancelled') else 'Cannot Cancel'
+			frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction)
+
 def validate_cancellation(args):
 	if args[0].get("is_cancelled"):
 		repost_entry = frappe.db.get_value("Repost Item Valuation", {
@@ -718,7 +749,17 @@
 		conditions += " and " + previous_sle.get("warehouse_condition")
 
 	if check_serial_no and previous_sle.get("serial_no"):
-		conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+		# conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+		serial_no = previous_sle.get("serial_no")
+		conditions += (""" and
+			(
+				serial_no = {0}
+				or serial_no like {1}
+				or serial_no like {2}
+				or serial_no like {3}
+			)
+		""").format(frappe.db.escape(serial_no), frappe.db.escape('{}\n%'.format(serial_no)),
+			frappe.db.escape('%\n{}'.format(serial_no)), frappe.db.escape('%\n{}\n%'.format(serial_no)))
 
 	if not previous_sle.get("posting_date"):
 		previous_sle["posting_date"] = "1900-01-01"
@@ -793,12 +834,12 @@
 	if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
 			and cint(erpnext.is_perpetual_inventory_enabled(company)):
 		frappe.local.message_log = []
-		form_link = frappe.utils.get_link_to_form("Item", item_code)
+		form_link = get_link_to_form("Item", item_code)
 
 		message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
-		message += "<br><br>" + _(" Here are the options to proceed:")
+		message += "<br><br>" + _("Here are the options to proceed:")
 		solutions = "<li>" + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "</li>"
-		solutions += "<li>" + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "</li>"
+		solutions += "<li>" + _("If not, you can Cancel / Submit this entry") + " {0} ".format(frappe.bold("after")) + _("performing either one below:") + "</li>"
 		sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
 		sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
 		msg = message + solutions + sub_solutions + "</li>"
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index 46d02d8..7da5d7f 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -22,50 +22,50 @@
 		customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory")
 		issue = make_issue(creation, "_Test Customer", 1)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
 
 		# make issue with customer_group specific SLA
 		customer = create_customer("__Test Customer", "_Test SLA Customer Group", "__Test SLA Territory")
 		issue = make_issue(creation, "__Test Customer", 2)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
 
 
 		# make issue with territory specific SLA
 		customer = create_customer("___Test Customer", "__Test SLA Customer Group", "_Test SLA Territory")
 		issue = make_issue(creation, "___Test Customer", 3)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 15, 0))
 
 		# make issue with default SLA
 		issue = make_issue(creation=creation, index=4)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 4, 16, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 4, 18, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 16, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 18, 0))
 
 		# make issue with default SLA before working hours
 		creation = datetime.datetime(2019, 3, 4, 7, 0)
 		issue = make_issue(creation=creation, index=5)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 4, 16, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 14, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 4, 16, 0))
 
 		# make issue with default SLA after working hours
 		creation = datetime.datetime(2019, 3, 4, 20, 0)
 		issue = make_issue(creation, index=6)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 6, 14, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 6, 16, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 6, 14, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 6, 16, 0))
 
 		# make issue with default SLA next day
 		creation = datetime.datetime(2019, 3, 4, 14, 0)
 		issue = make_issue(creation=creation, index=7)
 
-		self.assertEquals(issue.response_by, datetime.datetime(2019, 3, 4, 18, 0))
-		self.assertEquals(issue.resolution_by, datetime.datetime(2019, 3, 6, 12, 0))
+		self.assertEqual(issue.response_by, datetime.datetime(2019, 3, 4, 18, 0))
+		self.assertEqual(issue.resolution_by, datetime.datetime(2019, 3, 6, 12, 0))
 
 		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 15, 0)