Merge branch 'develop' of https://github.com/P-Froggy/erpnext into develop
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
index 52bbe33..8900767 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
@@ -13,13 +13,15 @@
 
 class BankReconciliation(Document):
 	def get_payment_entries(self):
-		if not (self.bank_account and self.from_date and self.to_date):
-			msgprint(_("Bank Account, From Date and To Date are Mandatory"))
-			return
+		if not (self.from_date and self.to_date):
+			frappe.throw(_("From Date and To Date are Mandatory"))
+
+		if not self.account:
+			frappe.throw(_("Account is mandatory to get payment entries"))
 
 		condition = ""
 		if not self.include_reconciled_entries:
-			condition = " and (clearance_date is null or clearance_date='0000-00-00')"
+			condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
 
 		journal_entries = frappe.db.sql("""
 			select
@@ -32,10 +34,14 @@
 			where
 				t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1
 				and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s
-				and ifnull(t1.is_opening, 'No') = 'No' %(condition)s
+				and ifnull(t1.is_opening, 'No') = 'No' {condition}
 			group by t2.account, t1.name
 			order by t1.posting_date ASC, t1.name DESC
-		""", {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
+		""".format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
+		condition = ''
+
+		if self.bank_account:
+			condition += 'and bank_account = %(bank_account)s'
 
 		payment_entries = frappe.db.sql("""
 			select
@@ -49,10 +55,10 @@
 			where
 				(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
 				and posting_date >= %(from)s and posting_date <= %(to)s
-				and bank_account = %(bank_account)s
+				{condition}
 			order by
 				posting_date ASC, name DESC
-		""", {"account": self.account, "from":self.from_date,
+		""".format(condition=condition), {"account": self.account, "from":self.from_date,
 				"to": self.to_date, "bank_account": self.bank_account}, as_dict=1)
 
 		pos_entries = []
diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
index 5c3519a..02b0c4d 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
@@ -18,7 +18,8 @@
    "in_list_view": 1,
    "label": "Invoice",
    "options": "Sales Invoice",
-   "reqd": 1
+   "reqd": 1,
+   "search_index": 1
   },
   {
    "fetch_from": "sales_invoice.customer",
@@ -60,7 +61,7 @@
   }
  ],
  "istable": 1,
- "modified": "2019-09-26 11:05:36.016772",
+ "modified": "2020-02-20 16:16:20.724620",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Discounted Invoice",
diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
index 93dfcc1..109737f 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
@@ -7,4 +7,4 @@
 from frappe.model.document import Document
 
 class DiscountedInvoice(Document):
-	pass
+	pass
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 041e419..f9e4fd7 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -232,11 +232,36 @@
 		if bal < 0 and not on_cancel:
 			frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
 
-	# Update outstanding amt on against voucher
 	if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
+		update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal)
+
+def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal):
+	data = []
+	# Update outstanding amt on against voucher
+	if against_voucher_type == "Fees":
 		ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
 		ref_doc.db_set('outstanding_amount', bal)
 		ref_doc.set_status(update=True)
+		return
+	elif against_voucher_type == "Purchase Invoice":
+		from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status
+		data = frappe.db.get_value(against_voucher_type, against_voucher, 
+			["name as purchase_invoice", "outstanding_amount", 
+			"is_return", "due_date", "docstatus"])
+	elif against_voucher_type == "Sales Invoice":
+		from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status
+		data = frappe.db.get_value(against_voucher_type, against_voucher, 
+			["name as sales_invoice", "outstanding_amount", "is_discounted", 
+			"is_return", "due_date", "docstatus"])
+
+	precision = frappe.get_precision(against_voucher_type, "outstanding_amount")
+	data = list(data)
+	data.append(precision)
+	status = get_status(data)
+	frappe.db.set_value(against_voucher_type, against_voucher, {
+		'outstanding_amount': bal,
+		'status': status
+	})
 
 def validate_frozen_account(account, adv_adj=None):
 	frozen_account = frappe.db.get_value("Account", account, "freeze_account")
@@ -274,6 +299,9 @@
 		if d.against != new_against:
 			frappe.db.set_value("GL Entry", d.name, "against", new_against)
 
+def on_doctype_update():
+	frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
+	frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
 
 def rename_gle_sle_docs():
 	for doctype in ["GL Entry", "Stock Ledger Entry"]:
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index a68c368..847fbed 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -125,6 +125,27 @@
 			else:
 				self.remarks = _("No Remarks")
 
+	def set_status(self, update=False, status=None, update_modified=True):
+		if self.is_new():
+			if self.get('amended_from'):
+				self.status = 'Draft'
+			return
+
+		if not status:
+			precision = self.precision("outstanding_amount")
+			args = [
+				self.name,
+				self.outstanding_amount,
+				self.is_return, 
+				self.due_date, 
+				self.docstatus,
+				precision
+			]
+			status = get_status(args)
+
+		if update:
+			self.db_set('status', status, update_modified = update_modified)
+
 	def set_missing_values(self, for_validate=False):
 		if not self.credit_to:
 			self.credit_to = get_party_account("Supplier", self.supplier, self.company)
@@ -1007,6 +1028,34 @@
 		# calculate totals again after applying TDS
 		self.calculate_taxes_and_totals()
 
+def get_status(*args):
+	purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0]
+
+	outstanding_amount = flt(outstanding_amount, precision)
+	due_date = getdate(due_date)
+	now_date = getdate()
+
+	if docstatus == 2:
+		status = "Cancelled"
+	elif docstatus == 1:
+		if outstanding_amount > 0 and due_date < now_date:
+			status = "Overdue"
+		elif outstanding_amount > 0 and due_date >= now_date:
+			status = "Unpaid"
+		#Check if outstanding amount is 0 due to debit note issued against invoice
+		elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}):
+			status = "Debit Note Issued"
+		elif is_return == 1:
+			status = "Return"
+		elif outstanding_amount <=0:
+			status = "Paid"
+		else:
+			status = "Submitted"
+	else:
+		status = "Draft"
+	
+	return status
+
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
 	list_context = get_list_context(context)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 658e703..bcaa394 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1217,62 +1217,83 @@
 
 		self.set_missing_values(for_validate = True)
 
-	def get_discounting_status(self):
-		status = None
-		if self.is_discounted:
-			invoice_discounting_list = frappe.db.sql("""
-				select status
-				from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
-				where
-					id.name = d.parent
-					and d.sales_invoice=%s
-					and id.docstatus=1
-					and status in ('Disbursed', 'Settled')
-			""", self.name)
-			for d in invoice_discounting_list:
-				status = d[0]
-				if status == "Disbursed":
-					break
-		return status
-
 	def set_status(self, update=False, status=None, update_modified=True):
 		if self.is_new():
 			if self.get('amended_from'):
 				self.status = 'Draft'
 			return
 
-		precision = self.precision("outstanding_amount")
-		outstanding_amount = flt(self.outstanding_amount, precision)
-		due_date = getdate(self.due_date)
-		nowdate = getdate()
-		discountng_status = self.get_discounting_status()
-
 		if not status:
-			if self.docstatus == 2:
-				status = "Cancelled"
-			elif self.docstatus == 1:
-				if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
-					self.status = "Overdue and Discounted"
-				elif outstanding_amount > 0 and due_date < nowdate:
-					self.status = "Overdue"
-				elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed':
-					self.status = "Unpaid and Discounted"
-				elif outstanding_amount > 0 and due_date >= nowdate:
-					self.status = "Unpaid"
-				#Check if outstanding amount is 0 due to credit note issued against invoice
-				elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
-					self.status = "Credit Note Issued"
-				elif self.is_return == 1:
-					self.status = "Return"
-				elif outstanding_amount<=0:
-					self.status = "Paid"
-				else:
-					self.status = "Submitted"
-			else:
-				self.status = "Draft"
+			precision = self.precision("outstanding_amount")
+			args = [
+				self.name,
+				self.outstanding_amount,
+				self.is_discounted, 
+				self.is_return, 
+				self.due_date, 
+				self.docstatus,
+				precision,
+			]
+			status = get_status(args)
 
 		if update:
-			self.db_set('status', self.status, update_modified = update_modified)
+			self.db_set('status', status, update_modified = update_modified)
+
+def get_discounting_status(sales_invoice):
+	status = None
+
+	invoice_discounting_list = frappe.db.sql("""
+		select status
+		from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
+		where
+			id.name = d.parent
+			and d.sales_invoice=%s
+			and id.docstatus=1
+			and status in ('Disbursed', 'Settled')
+	""", sales_invoice)
+
+	for d in invoice_discounting_list:
+		status = d[0]
+		if status == "Disbursed":
+			break
+
+	return status
+
+def get_status(*args):
+	sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0]
+	
+	discounting_status = None
+	if is_discounted:
+		discounting_status = get_discounting_status(sales_invoice)
+
+	outstanding_amount = flt(outstanding_amount, precision)
+	due_date = getdate(due_date)
+	now_date = getdate()
+
+	if docstatus == 2:
+		status = "Cancelled"
+	elif docstatus == 1:
+		if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed':
+			status = "Overdue and Discounted"
+		elif outstanding_amount > 0 and due_date < now_date:
+			status = "Overdue"
+		elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed':
+			status = "Unpaid and Discounted"
+		elif outstanding_amount > 0 and due_date >= now_date:
+			status = "Unpaid"
+		#Check if outstanding amount is 0 due to credit note issued against invoice
+		elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}):
+			status = "Credit Note Issued"
+		elif is_return == 1:
+			status = "Return"
+		elif outstanding_amount <=0:
+			status = "Paid"
+		else:
+			status = "Submitted"
+	else:
+		status = "Draft"
+	
+	return status
 
 def validate_inter_company_party(doctype, party, company, inter_company_reference):
 	if not party:
@@ -1444,7 +1465,7 @@
 		"party": party,
 		"company": company
 	}
-
+  
 def get_internal_party(parties, link_doctype, doc):
 	if len(parties) == 1:
 			party = parties[0].name
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index bb1b7e3..6d53530 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -140,8 +140,11 @@
 	gle = frappe.get_doc(args)
 	gle.flags.ignore_permissions = 1
 	gle.flags.from_repost = from_repost
-	gle.insert()
+	gle.validate()
+	gle.flags.ignore_permissions = True
+	gle.db_insert()
 	gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
+	gle.flags.ignore_validate = True
 	gle.submit()
 
 def validate_account_for_perpetual_inventory(gl_map):
diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
index 7854660..d7efbad 100644
--- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
+++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py
@@ -29,7 +29,7 @@
 
 		row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
 		row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
-				flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
+				flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated_during_the_period))
 
 		row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
 				flt(row.accumulated_depreciation_as_on_from_date))
@@ -86,7 +86,6 @@
 		group by asset_category
 	""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
 
-
 def get_assets(filters):
 	return frappe.db.sql("""
 		SELECT results.asset_category,
@@ -94,9 +93,7 @@
 			   sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
 			   sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
 		from (SELECT a.asset_category,
-				   ifnull(sum(a.opening_accumulated_depreciation +
-							  case when ds.schedule_date < %(from_date)s and
-										(ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
+				   ifnull(sum(case when ds.schedule_date < %(from_date)s then
 								   ds.depreciation_amount
 							  else
 								   0
@@ -107,7 +104,6 @@
 							  else
 								   0
 							  end), 0) as depreciation_eliminated_during_the_period,
-
 				   ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
 										and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
 								   ds.depreciation_amount
@@ -120,7 +116,8 @@
 			union
 			SELECT a.asset_category,
 				   ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
-										and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
+										and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) 
+										then
 									0
 							   else
 									a.opening_accumulated_depreciation
@@ -133,7 +130,6 @@
 				   0 as depreciation_amount_during_the_period
 			from `tabAsset` a
 			where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
-			and not exists(select * from `tabDepreciation Schedule` ds where a.name = ds.parent)
 			group by a.asset_category) as results
 		group by results.asset_category
 		""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index 8b6359c..4523f66 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -306,10 +306,6 @@
 
 def get_items(filters, additional_query_columns):
 	conditions = get_conditions(filters)
-	match_conditions = frappe.build_match_conditions("Purchase Invoice")
-
-	if match_conditions:
-		match_conditions = " and {0} ".format(match_conditions)
 
 	if additional_query_columns:
 		additional_query_columns = ', ' + ', '.join(additional_query_columns)
@@ -327,8 +323,8 @@
 			`tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0}
 		from `tabPurchase Invoice`, `tabPurchase Invoice Item`
 		where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
-		`tabPurchase Invoice`.docstatus = 1 %s %s
-	""".format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1)
+		`tabPurchase Invoice`.docstatus = 1 %s
+	""".format(additional_query_columns) % (conditions), filters, as_dict=1)
 
 def get_aii_accounts():
 	return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 2cc2db6..786e04d 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -119,7 +119,7 @@
 		add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
 		data.append(total_row_map.get('total_row'))
 		skip_total_row = 1
-	
+
 	return columns, data, None, None, None, skip_total_row
 
 def get_columns(additional_table_columns, filters):
@@ -370,10 +370,6 @@
 
 def get_items(filters, additional_query_columns):
 	conditions = get_conditions(filters)
-	match_conditions = frappe.build_match_conditions("Sales Invoice")
-
-	if match_conditions:
-		match_conditions = " and {0} ".format(match_conditions)
 
 	if additional_query_columns:
 		additional_query_columns = ', ' + ', '.join(additional_query_columns)
@@ -394,8 +390,8 @@
 			`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
 		from `tabSales Invoice`, `tabSales Invoice Item`
 		where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
-			and `tabSales Invoice`.docstatus = 1 {1} {2}
-		""".format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1) #nosec
+			and `tabSales Invoice`.docstatus = 1 {1}
+		""".format(additional_query_columns or '', conditions), filters, as_dict=1) #nosec
 
 def get_delivery_notes_against_sales_order(item_list):
 	so_dn_map = frappe._dict()
diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py
index b5598f8..47b4866 100644
--- a/erpnext/buying/utils.py
+++ b/erpnext/buying/utils.py
@@ -12,7 +12,6 @@
 
 def update_last_purchase_rate(doc, is_submit):
 	"""updates last_purchase_rate in item table for each item"""
-
 	import frappe.utils
 	this_purchase_date = frappe.utils.getdate(doc.get('posting_date') or doc.get('transaction_date'))
 
@@ -23,7 +22,7 @@
 		# compare last purchase date and this transaction's date
 		last_purchase_rate = None
 		if last_purchase_details and \
-				(last_purchase_details.purchase_date > this_purchase_date):
+				(doc.get('docstatus') == 2 or last_purchase_details.purchase_date > this_purchase_date):
 			last_purchase_rate = last_purchase_details['base_net_rate']
 		elif is_submit == 1:
 			# even if this transaction is the latest one, it should be submitted
diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py
index 6f5ab32..1d40547 100644
--- a/erpnext/config/buying.py
+++ b/erpnext/config/buying.py
@@ -9,6 +9,13 @@
 			"items": [
 				{
 					"type": "doctype",
+					"name": "Material Request",
+					"onboard": 1,
+					"dependencies": ["Item"],
+					"description": _("Request for purchase."),
+				},
+				{
+					"type": "doctype",
 					"name": "Purchase Order",
 					"onboard": 1,
 					"dependencies": ["Item", "Supplier"],
@@ -22,13 +29,6 @@
 				},
 				{
 					"type": "doctype",
-					"name": "Material Request",
-					"onboard": 1,
-					"dependencies": ["Item"],
-					"description": _("Request for purchase."),
-				},
-				{
-					"type": "doctype",
 					"name": "Request for Quotation",
 					"onboard": 1,
 					"dependencies": ["Item", "Supplier"],
@@ -65,6 +65,11 @@
 				},
 				{
 					"type": "doctype",
+					"name": "Pricing Rule",
+					"description": _("Rules for applying pricing and discount.")
+				},
+				{
+					"type": "doctype",
 					"name": "Product Bundle",
 					"description": _("Bundle items at time of sale."),
 				},
@@ -80,11 +85,6 @@
 					"type": "doctype",
 					"name": "Promotional Scheme",
 					"description": _("Rules for applying different promotional schemes.")
-				},
-				{
-					"type": "doctype",
-					"name": "Pricing Rule",
-					"description": _("Rules for applying pricing and discount.")
 				}
 			]
 		},
@@ -152,13 +152,6 @@
 				{
 					"type": "report",
 					"is_query_report": True,
-					"name": "Supplier-Wise Sales Analytics",
-					"reference_doctype": "Stock Ledger Entry",
-					"onboard": 1
-				},
-				{
-					"type": "report",
-					"is_query_report": True,
 					"name": "Purchase Order Trends",
 					"reference_doctype": "Purchase Order",
 					"onboard": 1,
@@ -177,6 +170,16 @@
 					"reference_doctype": "Material Request",
 					"onboard": 1,
 				},
+				{
+					"type": "report",
+					"is_query_report": True,
+					"name": "Address And Contacts",
+					"label": _("Supplier Addresses And Contacts"),
+					"reference_doctype": "Address",
+					"route_options": {
+						"party_type": "Supplier"
+					}
+				}
 			]
 		},
 		{
@@ -226,18 +229,15 @@
 				{
 					"type": "report",
 					"is_query_report": True,
-					"name": "Material Requests for which Supplier Quotations are not created",
-					"reference_doctype": "Material Request"
+					"name": "Supplier-Wise Sales Analytics",
+					"reference_doctype": "Stock Ledger Entry",
+					"onboard": 1
 				},
 				{
 					"type": "report",
 					"is_query_report": True,
-					"name": "Address And Contacts",
-					"label": _("Supplier Addresses And Contacts"),
-					"reference_doctype": "Address",
-					"route_options": {
-						"party_type": "Supplier"
-					}
+					"name": "Material Requests for which Supplier Quotations are not created",
+					"reference_doctype": "Material Request"
 				}
 			]
 		},
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index ca09f76..d661bcb 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1200,6 +1200,8 @@
 				child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code"))
 		else:
 			child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
+			if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
+				continue
 
 		if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
 			frappe.throw(_("Cannot set quantity less than delivered quantity"))
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 8b275a6..b465a10 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -44,17 +44,6 @@
 		["Closed", "eval:self.status=='Closed'"],
 		["On Hold", "eval:self.status=='On Hold'"],
 	],
-	"Purchase Invoice": [
-		["Draft", None],
-		["Submitted", "eval:self.docstatus==1"],
-		["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
-		["Return", "eval:self.is_return==1 and self.docstatus==1"],
-		["Debit Note Issued",
-		 "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
-		["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
-		["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
-		["Cancelled", "eval:self.docstatus==2"],
-	],
 	"Purchase Order": [
 		["Draft", None],
 		["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"],
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 57b4ddd..b10d8be 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -429,7 +429,7 @@
 	for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
 		from `tabStock Ledger Entry` sle
 		where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
-		order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc""".format(condition=condition),
+		order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
 		tuple([posting_date, posting_time] + values), as_dict=True):
 			future_stock_vouchers.append([d.voucher_type, d.voucher_no])
 
diff --git a/erpnext/education/doctype/student_admission/student_admission.js b/erpnext/education/doctype/student_admission/student_admission.js
index d7f7454..2b62967 100644
--- a/erpnext/education/doctype/student_admission/student_admission.js
+++ b/erpnext/education/doctype/student_admission/student_admission.js
@@ -11,5 +11,12 @@
 
 	academic_year: function(frm) {
 		frm.trigger("program");
+	},
+
+	admission_end_date: function(frm) {
+		if(frm.doc.admission_end_date && frm.doc.admission_end_date <= frm.doc.admission_start_date){
+			frm.set_value("admission_end_date", "");
+			frappe.throw(__("Admission End Date should be greater than Admission Start Date."));
+		}
 	}
 });
diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py
index 8498b3d..bc7dcee 100644
--- a/erpnext/hr/doctype/additional_salary/additional_salary.py
+++ b/erpnext/hr/doctype/additional_salary/additional_salary.py
@@ -39,19 +39,21 @@
 		return amount_per_day * no_of_days
 
 @frappe.whitelist()
-def get_additional_salary_component(employee, start_date, end_date):
+def get_additional_salary_component(employee, start_date, end_date, component_type):
 	additional_components = frappe.db.sql("""
 		select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
 		from `tabAdditional Salary`
 		where employee=%(employee)s
 			and docstatus = 1
 			and payroll_date between %(from_date)s and %(to_date)s
+			and type = %(component_type)s
 		group by salary_component, overwrite_salary_structure_amount
 		order by salary_component, overwrite_salary_structure_amount
 	""", {
 		'employee': employee,
 		'from_date': start_date,
-		'to_date': end_date
+		'to_date': end_date,
+		'component_type': "Earning" if component_type == "earnings" else "Deduction"
 	}, as_dict=1)
 
 	additional_components_list = []
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 42f0179..ad2cc02 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -64,6 +64,9 @@
 
 		allocation = self.get_leave_allocation()
 
+		if not allocation:
+			frappe.throw(_("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format(self.employee, self.leave_type))
+
 		self.leave_balance = allocation.total_leaves_allocated - allocation.carry_forwarded_leaves_count\
 			- get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date)
 
@@ -116,4 +119,4 @@
 			leave_type=allocation.leave_type,
 			encashment_date=allocation.to_date
 		))
-		leave_encashment.insert(ignore_permissions=True)
\ No newline at end of file
+		leave_encashment.insert(ignore_permissions=True)
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index eee7974..d03a3dd 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -299,9 +299,11 @@
 
 	def calculate_net_pay(self):
 		if self.salary_structure:
-			self.calculate_component_amounts()
-
+			self.calculate_component_amounts("earnings")
 		self.gross_pay = self.get_component_totals("earnings")
+
+		if self.salary_structure:
+			self.calculate_component_amounts("deductions")
 		self.total_deduction = self.get_component_totals("deductions")
 
 		self.set_loan_repayment()
@@ -309,25 +311,27 @@
 		self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
 		self.rounded_total = rounded(self.net_pay)
 
-	def calculate_component_amounts(self):
+	def calculate_component_amounts(self, component_type):
 		if not getattr(self, '_salary_structure_doc', None):
 			self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
 
 		payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
 
-		self.add_structure_components()
-		self.add_employee_benefits(payroll_period)
-		self.add_additional_salary_components()
-		self.add_tax_components(payroll_period)
-		self.set_component_amounts_based_on_payment_days()
+		self.add_structure_components(component_type)
+		self.add_additional_salary_components(component_type)
+		if component_type == "earnings":
+			self.add_employee_benefits(payroll_period)
+		else:
+			self.add_tax_components(payroll_period)
 
-	def add_structure_components(self):
+		self.set_component_amounts_based_on_payment_days(component_type)
+
+	def add_structure_components(self, component_type):
 		data = self.get_data_for_eval()
-		for key in ('earnings', 'deductions'):
-			for struct_row in self._salary_structure_doc.get(key):
-				amount = self.eval_condition_and_formula(struct_row, data)
-				if amount and struct_row.statistical_component == 0:
-					self.update_component_row(struct_row, amount, key)
+		for struct_row in self._salary_structure_doc.get(component_type):
+			amount = self.eval_condition_and_formula(struct_row, data)
+			if amount and struct_row.statistical_component == 0:
+				self.update_component_row(struct_row, amount, component_type)
 
 	def get_data_for_eval(self):
 		'''Returns data for evaluating formula'''
@@ -400,14 +404,15 @@
 						amount = last_benefit.amount
 						self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
 
-	def add_additional_salary_components(self):
-		additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date)
+	def add_additional_salary_components(self, component_type):
+		additional_components = get_additional_salary_component(self.employee,
+			self.start_date, self.end_date, component_type)
 		if additional_components:
 			for additional_component in additional_components:
 				amount = additional_component.amount
 				overwrite = additional_component.overwrite
-				key = "earnings" if additional_component.type == "Earning" else "deductions"
-				self.update_component_row(frappe._dict(additional_component.struct_row), amount, key, overwrite=overwrite)
+				self.update_component_row(frappe._dict(additional_component.struct_row), amount,
+					component_type, overwrite=overwrite)
 
 	def add_tax_components(self, payroll_period):
 		# Calculate variable_based_on_taxable_salary after all components updated in salary slip
@@ -736,7 +741,7 @@
 				total += d.amount
 		return total
 
-	def set_component_amounts_based_on_payment_days(self):
+	def set_component_amounts_based_on_payment_days(self, component_type):
 		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
 
@@ -746,9 +751,8 @@
 		if not joining_date:
 			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
 
-		for component_type in ("earnings", "deductions"):
-			for d in self.get(component_type):
-				d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
+		for d in self.get(component_type):
+			d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
 
 	def set_loan_repayment(self):
 		self.set('loans', [])
diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
index 7815094..6ca6dfd 100644
--- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
@@ -25,7 +25,6 @@
 		make_employee("test_employee@salary.com")
 		make_employee("test_employee_2@salary.com")
 
-
 	def make_holiday_list(self):
 		if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
 			holiday_list = frappe.get_doc({
@@ -38,6 +37,29 @@
 			holiday_list.get_weekly_off_dates()
 			holiday_list.save()
 
+	def test_salary_structure_deduction_based_on_gross_pay(self):
+
+		emp = make_employee("test_employee_3@salary.com")
+
+		sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True)
+
+		sal_struct.earnings = [sal_struct.earnings[0]]
+		sal_struct.earnings[0].amount_based_on_formula = 1
+		sal_struct.earnings[0].formula =  "base"
+
+		sal_struct.deductions = [sal_struct.deductions[0]]
+
+		sal_struct.deductions[0].amount_based_on_formula = 1
+		sal_struct.deductions[0].condition = "gross_pay > 100"
+		sal_struct.deductions[0].formula =  "gross_pay * 0.2"
+
+		sal_struct.submit()
+
+		assignment = create_salary_structure_assignment(emp, "Salary Structure 2")
+		ss = make_salary_slip(sal_struct.name, employee = emp)
+
+		self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount)
+
 	def test_amount_totals(self):
 		frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
 		sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index 4988410..d56080e 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -75,7 +75,7 @@
 		for date in dates:
 			shift_details = get_employee_shift(employee, date, True)
 			if shift_details and shift_details.shift_type.name == self.name:
-				mark_attendance(employee, date, self.name, 'Absent')
+				mark_attendance(employee, date, 'Absent', self.name)
 
 	def get_assigned_employee(self, from_date=None, consider_default_shift=False):
 		filters = {'date':('>=', from_date), 'shift_type': self.name, 'docstatus': '1'}
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.json b/erpnext/loan_management/doctype/loan_application/loan_application.json
index 4c43302..a353a77 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.json
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "ACC-LOAP-.YYYY.-.#####",
  "creation": "2019-08-29 17:46:49.201740",
  "doctype": "DocType",
@@ -122,7 +123,6 @@
   },
   {
    "depends_on": "eval: doc.is_term_loan == 1",
-   "fetch_from": "loan_type.repayment_method",
    "fetch_if_empty": 1,
    "fieldname": "repayment_method",
    "fieldtype": "Select",
@@ -213,7 +213,8 @@
   }
  ],
  "is_submittable": 1,
- "modified": "2019-10-24 10:32:03.740558",
+ "links": [],
+ "modified": "2020-03-01 10:21:44.413353",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Application",
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
index 92e9817..4b930c5 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
@@ -229,13 +229,14 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-02-24 07:35:47.168123",
+ "modified": "2020-02-26 06:18:54.934538",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Repayment",
  "owner": "Administrator",
  "permissions": [
   {
+   "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
@@ -245,9 +246,11 @@
    "report": 1,
    "role": "System Manager",
    "share": 1,
+   "submit": 1,
    "write": 1
   },
   {
+   "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
@@ -257,6 +260,7 @@
    "report": 1,
    "role": "Loan Manager",
    "share": 1,
+   "submit": 1,
    "write": 1
   }
  ],
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 599f6da..b7be84f 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -55,7 +55,10 @@
 			"valid_upto": (">=", update_time)
 		}, as_list=1))
 
-	loans = frappe.db.sql(""" SELECT l.name, l.loan_amount, l.total_principal_paid, lp.loan_security, lp.haircut, lp.qty
+	ltv_ratio_map = frappe._dict(frappe.get_all("Loan Security Type",
+		fields=["name", "loan_to_value_ratio"], as_list=1))
+
+	loans = frappe.db.sql(""" SELECT l.name, l.loan_amount, l.total_principal_paid, lp.loan_security, lp.haircut, lp.qty, lp.loan_security_type
 		FROM `tabLoan` l, `tabPledge` lp , `tabLoan Security Pledge`p WHERE lp.parent = p.name and p.loan = l.name and l.docstatus = 1
 		and l.is_secured_loan and l.status = 'Disbursed' and p.status in ('Pledged', 'Partially Unpledged')""", as_dict=1)
 
@@ -68,11 +71,12 @@
 		})
 
 		current_loan_security_amount = loan_security_price_map.get(loan.loan_security, 0) * loan.qty
+		ltv_ratio = ltv_ratio_map.get(loan.loan_security_type)
 
 		loan_security_map[loan.name]['security_value'] += current_loan_security_amount - (current_loan_security_amount * loan.haircut/100)
 
 	for loan, value in iteritems(loan_security_map):
-		if value["security_value"] < value["loan_amount"]:
+		if (value["security_value"]/value["loan_amount"]) < ltv_ratio:
 			create_loan_security_shortfall(loan, value, process_loan_security_shortfall)
 
 def create_loan_security_shortfall(loan, value, process_loan_security_shortfall):
diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json
index a5ab057..5f29609 100644
--- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json
+++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "field:loan_security_type",
  "creation": "2019-08-29 18:46:07.322056",
  "doctype": "DocType",
@@ -8,7 +9,9 @@
   "loan_security_type",
   "unit_of_measure",
   "haircut",
-  "disabled"
+  "disabled",
+  "column_break_5",
+  "loan_to_value_ratio"
  ],
  "fields": [
   {
@@ -33,9 +36,19 @@
    "fieldtype": "Link",
    "label": "Unit Of Measure",
    "options": "UOM"
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "loan_to_value_ratio",
+   "fieldtype": "Percent",
+   "label": "Loan To Value Ratio"
   }
  ],
- "modified": "2019-10-10 03:05:37.912866",
+ "links": [],
+ "modified": "2020-02-28 12:43:20.364447",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Security Type",
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 1503adb..8ecc13b 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -108,8 +108,8 @@
 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)
+		frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index a2a913a..74ae627 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -981,6 +981,7 @@
 def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
 	"""returns last purchase details in stock uom"""
 	# get last purchase order item details
+
 	last_purchase_order = frappe.db.sql("""\
 		select po.name, po.transaction_date, po.conversion_rate,
 			po_item.conversion_factor, po_item.base_price_list_rate,
@@ -991,6 +992,7 @@
 		order by po.transaction_date desc, po.name desc
 		limit 1""", (item_code, cstr(doc_name)), as_dict=1)
 
+
 	# get last purchase receipt item details
 	last_purchase_receipt = frappe.db.sql("""\
 		select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate,
@@ -1002,19 +1004,20 @@
 		order by pr.posting_date desc, pr.posting_time desc, pr.name desc
 		limit 1""", (item_code, cstr(doc_name)), as_dict=1)
 
+	
+	
 	purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
 							   or "1900-01-01")
 	purchase_receipt_date = getdate(last_purchase_receipt and
 								 last_purchase_receipt[0].posting_date or "1900-01-01")
 
-	if (purchase_order_date > purchase_receipt_date) or \
-				(last_purchase_order and not last_purchase_receipt):
+	if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt):
 		# use purchase order
+		
 		last_purchase = last_purchase_order[0]
 		purchase_date = purchase_order_date
 
-	elif (purchase_receipt_date > purchase_order_date) or \
-				(last_purchase_receipt and not last_purchase_order):
+	elif last_purchase_receipt and (purchase_receipt_date > purchase_order_date or not last_purchase_order):
 		# use purchase receipt
 		last_purchase = last_purchase_receipt[0]
 		purchase_date = purchase_receipt_date
@@ -1026,10 +1029,11 @@
 	out = frappe._dict({
 		"base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor,
 		"base_rate": flt(last_purchase.base_rate) / conversion_factor,
-		"base_net_rate": flt(last_purchase.net_rate) / conversion_factor,
+		"base_net_rate": flt(last_purchase.base_net_rate) / conversion_factor,
 		"discount_percentage": flt(last_purchase.discount_percentage),
 		"purchase_date": purchase_date
 	})
+	
 
 	conversion_rate = flt(conversion_rate) or 1.0
 	out.update({
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index d34f420..64d4c6c 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -205,6 +205,7 @@
 
 def validate_serial_no(sle, item_det):
 	serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else []
+	validate_material_transfer_entry(sle)
 
 	if item_det.has_serial_no==0:
 		if serial_nos:
@@ -224,7 +225,9 @@
 
 			for serial_no in serial_nos:
 				if frappe.db.exists("Serial No", serial_no):
-					sr = frappe.get_doc("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_no", "company"], as_dict=1)
 
 					if sr.item_code!=sle.item_code:
 						if not allow_serial_nos_with_different_item(serial_no, sle):
@@ -305,6 +308,19 @@
 				frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
 					.format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
 
+def validate_material_transfer_entry(sle_doc):
+	sle_doc.update({
+		"skip_update_serial_no": False,
+		"skip_serial_no_validaiton": False
+	})
+
+	if (sle_doc.voucher_type == "Stock Entry" and sle_doc.is_cancelled == "No" and
+		frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"):
+		if sle_doc.actual_qty < 0:
+			sle_doc.skip_update_serial_no = True
+		else:
+			sle_doc.skip_serial_no_validaiton = True
+
 def validate_so_serial_no(sr, sales_order,):
 	if not sr.sales_order or sr.sales_order!= sales_order:
 		frappe.throw(_("""Sales Order {0} has reservation for item {1}, you can
@@ -312,7 +328,8 @@
 		be delivered""").format(sales_order, sr.item_code, sr.name))
 
 def has_duplicate_serial_no(sn, sle):
-	if sn.warehouse and sle.voucher_type != 'Stock Reconciliation':
+	if (sn.warehouse and not sle.skip_serial_no_validaiton
+		and sle.voucher_type != 'Stock Reconciliation'):
 		return True
 
 	if sn.company != sle.company:
@@ -337,7 +354,7 @@
 	"""
 	allow_serial_nos = False
 	if sle.voucher_type=="Stock Entry" and cint(sle.actual_qty) > 0:
-		stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no)
+		stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no)
 		if stock_entry.purpose in ("Repack", "Manufacture"):
 			for d in stock_entry.get("items"):
 				if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse):
@@ -348,6 +365,7 @@
 	return allow_serial_nos
 
 def update_serial_nos(sle, item_det):
+	if sle.skip_update_serial_no: return
 	if sle.is_cancelled == "No" and not sle.serial_no and cint(sle.actual_qty) > 0 \
 			and item_det.has_serial_no == 1 and item_det.serial_no_series:
 		serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
@@ -369,22 +387,16 @@
 	voucher_type = args.get('voucher_type')
 	item_code = args.get('item_code')
 	for serial_no in serial_nos:
+		is_new = False
 		if frappe.db.exists("Serial No", serial_no):
-			sr = frappe.get_doc("Serial No", serial_no)
-			sr.via_stock_ledger = True
-			sr.item_code = item_code
-			sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None
-			sr.batch_no = args.get('batch_no')
-			sr.location = args.get('location')
-			sr.company = args.get('company')
-			sr.supplier = args.get('supplier')
-			if sr.sales_order and voucher_type == "Stock Entry" \
-				and not args.get('actual_qty', 0) > 0:
-				sr.sales_order = None
-			sr.update_serial_no_reference()
-			sr.save(ignore_permissions=True)
+			sr = frappe.get_cached_doc("Serial No", serial_no)
 		elif args.get('actual_qty', 0) > 0:
-			created_numbers.append(make_serial_no(serial_no, args))
+			sr = frappe.new_doc("Serial No")
+			is_new = True
+
+		sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new)
+		if is_new:
+			created_numbers.append(sr.name)
 
 	form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers))
 
@@ -419,20 +431,34 @@
 	return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
 		if s.strip()]
 
-def make_serial_no(serial_no, args):
-	sr = frappe.new_doc("Serial No")
-	sr.serial_no = serial_no
-	sr.item_code = args.get('item_code')
-	sr.company = args.get('company')
-	sr.batch_no = args.get('batch_no')
-	sr.via_stock_ledger = args.get('via_stock_ledger') or True
-	sr.warehouse = args.get('warehouse')
+def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False):
+	serial_no_doc.update({
+		"item_code": args.get("item_code"),
+		"company": args.get("company"),
+		"batch_no": args.get("batch_no"),
+		"via_stock_ledger": args.get("via_stock_ledger") or True,
+		"supplier": args.get("supplier"),
+		"location": args.get("location"),
+		"warehouse": (args.get("warehouse")
+			if args.get("actual_qty", 0) > 0 else None)
+	})
 
-	sr.validate_item()
-	sr.update_serial_no_reference(serial_no)
-	sr.db_insert()
+	if is_new:
+		serial_no_doc.serial_no = serial_no
 
-	return sr.name
+	if (serial_no_doc.sales_order and args.get("voucher_type") == "Stock Entry"
+		and not args.get("actual_qty", 0) > 0):
+		serial_no_doc.sales_order = None
+
+	serial_no_doc.validate_item()
+	serial_no_doc.update_serial_no_reference(serial_no)
+
+	if is_new:
+		serial_no_doc.db_insert()
+	else:
+		serial_no_doc.db_update()
+
+	return serial_no_doc
 
 def update_serial_nos_after_submit(controller, parentfield):
 	stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index c9eba71..c03eb79 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -240,6 +240,7 @@
    "options": "Company",
    "print_width": "150px",
    "read_only": 1,
+   "search_index": 1,
    "width": "150px"
   },
   {
@@ -274,7 +275,7 @@
  "icon": "fa fa-list",
  "idx": 1,
  "in_create": 1,
- "modified": "2019-11-27 12:17:31.522675",
+ "modified": "2020-02-25 22:53:33.504681",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Ledger Entry",
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 2c6c953..f3381c7 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -136,7 +136,7 @@
 		bin_obj.flags.ignore_permissions = 1
 		bin_obj.insert()
 	else:
-		bin_obj = frappe.get_doc('Bin', bin)
+		bin_obj = frappe.get_cached_doc('Bin', bin)
 	bin_obj.flags.ignore_permissions = True
 	return bin_obj
 
diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json
index 53af80c..6641f56 100644
--- a/erpnext/support/doctype/issue/issue.json
+++ b/erpnext/support/doctype/issue/issue.json
@@ -366,7 +366,7 @@
  "icon": "fa fa-ticket",
  "idx": 7,
  "links": [],
- "modified": "2020-02-18 21:26:35.636013",
+ "modified": "2020-02-26 02:19:49.477928",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Issue",
@@ -387,9 +387,9 @@
  "quick_entry": 1,
  "search_fields": "status,customer,subject,raised_by",
  "sort_field": "modified",
- "sort_order": "ASC",
+ "sort_order": "DESC",
  "timeline_field": "customer",
  "title_field": "subject",
  "track_changes": 1,
  "track_seen": 1
-}
\ No newline at end of file
+}