Merge pull request #21729 from nabinhait/basic-hra-comp

fix: Submit perm for other income and removed caching while getting hra and basic from company
diff --git a/.travis.yml b/.travis.yml
index 213445b..77d427e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
-dist: trusty
-
 language: python
+dist: trusty
 
 git:
   depth: 1
@@ -14,21 +13,10 @@
 
 jobs:
   include:
-  - name: "Python 2.7 Server Side Test"
-    python: 2.7
-    script: bench --site test_site run-tests --app erpnext --coverage
-
   - name: "Python 3.6 Server Side Test"
     python: 3.6
     script: bench --site test_site run-tests --app erpnext --coverage
 
-  - name: "Python 2.7 Patch Test"
-    python: 2.7
-    before_script:
-      - wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
-      - bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
-    script: bench --site test_site migrate
-
   - name: "Python 3.6 Patch Test"
     python: 3.6
     before_script:
@@ -40,8 +28,7 @@
   - cd ~
   - nvm install 10
 
-  - git clone https://github.com/frappe/bench --depth 1
-  - pip install -e ./bench
+  - pip install frappe-bench
 
   - git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
   - bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index 5decccb..39bf4b0 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -14,7 +14,7 @@
 @frappe.whitelist()
 @cache_source
 def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
-	to_date = None, timespan = None, time_interval = None):
+	to_date = None, timespan = None, time_interval = None, heatmap_year = None):
 	if chart_name:
 		chart = frappe.get_doc('Dashboard Chart', chart_name)
 	else:
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index b0210e5..b57e678 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -185,7 +185,7 @@
 			total_days, total_booking_days, account_currency)
 
 		make_gl_entries(doc, credit_account, debit_account, against,
-			amount, base_amount, end_date, project, account_currency, item.cost_center, item.name, deferred_process)
+			amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
 
 		# Returned in case of any errors because it tries to submit the same record again and again in case of errors
 		if frappe.flags.deferred_accounting_error:
@@ -222,7 +222,7 @@
 		doc.submit()
 
 def make_gl_entries(doc, credit_account, debit_account, against,
-	amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no, deferred_process=None):
+	amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
 	# GL Entry for crediting the amount in the deferred expense
 	from erpnext.accounts.general_ledger import make_gl_entries
 
@@ -236,12 +236,12 @@
 			"credit": base_amount,
 			"credit_in_account_currency": amount,
 			"cost_center": cost_center,
-			"voucher_detail_no": voucher_detail_no,
+			"voucher_detail_no": item.name,
 			'posting_date': posting_date,
 			'project': project,
 			'against_voucher_type': 'Process Deferred Accounting',
 			'against_voucher': deferred_process
-		}, account_currency)
+		}, account_currency, item=item)
 	)
 	# GL Entry to debit the amount from the expense
 	gl_entries.append(
@@ -251,12 +251,12 @@
 			"debit": base_amount,
 			"debit_in_account_currency": amount,
 			"cost_center": cost_center,
-			"voucher_detail_no": voucher_detail_no,
+			"voucher_detail_no": item.name,
 			'posting_date': posting_date,
 			'project': project,
 			'against_voucher_type': 'Process Deferred Accounting',
 			'against_voucher': deferred_process
-		}, account_currency)
+		}, account_currency, item=item)
 	)
 
 	if gl_entries:
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 7a85bfb..894ec5b 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -162,9 +162,9 @@
 
 def get_doctypes_with_dimensions():
 	doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
-		"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item",
-		"Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item",
-		"Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
+		"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
+		"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
+		"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
 		"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
 		"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
 		"Subscription Plan"]
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 39fc203..594b4d4 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -8,6 +8,7 @@
 from frappe.utils import flt, getdate, nowdate, add_days
 from erpnext.controllers.accounts_controller import AccountsController
 from erpnext.accounts.general_ledger import make_gl_entries
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
 
 class InvoiceDiscounting(AccountsController):
 	def validate(self):
@@ -81,10 +82,15 @@
 	def make_gl_entries(self):
 		company_currency = frappe.get_cached_value('Company',  self.company, "default_currency")
 
+
 		gl_entries = []
+		invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
+		accounting_dimensions = get_accounting_dimensions()
+
+		invoice_fields.extend(accounting_dimensions)
+
 		for d in self.invoices:
-			inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
-				["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
+			inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
 
 			if d.outstanding_amount:
 				outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
@@ -102,7 +108,7 @@
 					"cost_center": inv.cost_center,
 					"against_voucher": d.sales_invoice,
 					"against_voucher_type": "Sales Invoice"
-				}, inv.party_account_currency))
+				}, inv.party_account_currency, item=inv))
 
 				gl_entries.append(self.get_gl_dict({
 					"account": self.accounts_receivable_credit,
@@ -115,7 +121,7 @@
 					"cost_center": inv.cost_center,
 					"against_voucher": d.sales_invoice,
 					"against_voucher_type": "Sales Invoice"
-				}, ar_credit_account_currency))
+				}, ar_credit_account_currency, item=inv))
 
 		make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
 
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 83c670e..22df5be 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -86,7 +86,7 @@
 		self.update_payment_schedule(cancel=1)
 		self.set_payment_req_status()
 		self.set_status()
-	
+
 	def set_payment_req_status(self):
 		from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
 		update_payment_req_status(self, None)
@@ -280,7 +280,7 @@
 				outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
 				if outstanding_amount <= 0 and not is_return:
 					no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
-		
+
 		for k, v in no_oustanding_refs.items():
 			frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
 					If this is undesirable please cancel the corresponding Payment Entry.")
@@ -506,7 +506,7 @@
 				"against": against_account,
 				"account_currency": self.party_account_currency,
 				"cost_center": self.cost_center
-			})
+			}, item=self)
 
 			dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
 
@@ -550,7 +550,7 @@
 					"credit_in_account_currency": self.paid_amount,
 					"credit": self.base_paid_amount,
 					"cost_center": self.cost_center
-				})
+				}, item=self)
 			)
 		if self.payment_type in ("Receive", "Internal Transfer"):
 			gl_entries.append(
@@ -561,7 +561,7 @@
 					"debit_in_account_currency": self.received_amount,
 					"debit": self.base_received_amount,
 					"cost_center": self.cost_center
-				})
+				}, item=self)
 			)
 
 	def add_deductions_gl_entries(self, gl_entries):
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index b358f56..cb05481 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -4,13 +4,19 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-import frappe, copy, json
-from frappe import throw, _
+
+import copy
+import json
+
 from six import string_types
-from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
+
+import frappe
 from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
 from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
 from erpnext.stock.get_item_details import get_conversion_factor
+from frappe import _, throw
+from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
+
 
 class MultiplePricingRuleConflict(frappe.ValidationError): pass
 
@@ -502,18 +508,16 @@
 	return list(set(apply_on_data))
 
 def validate_coupon_code(coupon_name):
-	from frappe.utils import today,getdate
-	coupon=frappe.get_doc("Coupon Code",coupon_name)
+	coupon = frappe.get_doc("Coupon Code", coupon_name)
+
 	if coupon.valid_from:
-		if coupon.valid_from > getdate(today()) :
-			frappe.throw(_("Sorry,coupon code validity has not started"))
+		if coupon.valid_from > getdate(today()):
+			frappe.throw(_("Sorry, this coupon code's validity has not started"))
 	elif coupon.valid_upto:
-		if coupon.valid_upto < getdate(today()) :
-			frappe.throw(_("Sorry,coupon code validity has expired"))
-	elif coupon.used>=coupon.maximum_use:
-		frappe.throw(_("Sorry,coupon code are exhausted"))
-	else:
-		return
+		if coupon.valid_upto < getdate(today()):
+			frappe.throw(_("Sorry, this coupon code's validity has expired"))
+	elif coupon.used >= coupon.maximum_use:
+		frappe.throw(_("Sorry, this coupon code is no longer valid"))
 
 def update_coupon_code_count(coupon_name,transaction_type):
 	coupon=frappe.get_doc("Coupon Code",coupon_name)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 3aa24df..cf4e158 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -460,7 +460,7 @@
 					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
 					"against_voucher_type": self.doctype,
 					"cost_center": self.cost_center
-				}, self.party_account_currency)
+				}, self.party_account_currency, item=self)
 			)
 
 	def make_item_gl_entries(self, gl_entries):
@@ -841,7 +841,7 @@
 					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
 					"against_voucher_type": self.doctype,
 					"cost_center": self.cost_center
-				}, self.party_account_currency)
+				}, self.party_account_currency, item=self)
 			)
 
 			gl_entries.append(
@@ -852,7 +852,7 @@
 					"credit_in_account_currency": self.base_paid_amount \
 						if bank_account_currency==self.company_currency else self.paid_amount,
 					"cost_center": self.cost_center
-				}, bank_account_currency)
+				}, bank_account_currency, item=self)
 			)
 
 	def make_write_off_gl_entry(self, gl_entries):
@@ -873,7 +873,7 @@
 					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
 					"against_voucher_type": self.doctype,
 					"cost_center": self.cost_center
-				}, self.party_account_currency)
+				}, self.party_account_currency, item=self)
 			)
 			gl_entries.append(
 				self.get_gl_dict({
@@ -883,7 +883,7 @@
 					"credit_in_account_currency": self.base_write_off_amount \
 						if write_off_account_currency==self.company_currency else self.write_off_amount,
 					"cost_center": self.cost_center or self.write_off_cost_center
-				})
+				}, item=self)
 			)
 
 	def make_gle_for_rounding_adjustment(self, gl_entries):
@@ -902,8 +902,7 @@
 					"debit_in_account_currency": self.rounding_adjustment,
 					"debit": self.base_rounding_adjustment,
 					"cost_center": self.cost_center or round_off_cost_center,
-				}
-			))
+				}, item=self))
 
 	def on_cancel(self):
 		super(PurchaseInvoice, self).on_cancel()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3b0fade..05b85da 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -791,7 +791,7 @@
 					"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
 					"against_voucher_type": self.doctype,
 					"cost_center": self.cost_center
-				}, self.party_account_currency)
+				}, self.party_account_currency, item=self)
 			)
 
 	def make_tax_gl_entries(self, gl_entries):
@@ -808,7 +808,7 @@
 							tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
 							flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
 						"cost_center": tax.cost_center
-					}, account_currency)
+					}, account_currency, item=tax)
 				)
 
 	def make_item_gl_entries(self, gl_entries):
@@ -828,7 +828,7 @@
 
 					for gle in fixed_asset_gl_entries:
 						gle["against"] = self.customer
-						gl_entries.append(self.get_gl_dict(gle))
+						gl_entries.append(self.get_gl_dict(gle, item=item))
 
 					asset.db_set("disposal_date", self.posting_date)
 					asset.set_status("Sold" if self.docstatus==1 else None)
@@ -866,7 +866,7 @@
 					"against_voucher": self.return_against if cint(self.is_return) else self.name,
 					"against_voucher_type": self.doctype,
 					"cost_center": self.cost_center
-				})
+				}, item=self)
 			)
 			gl_entries.append(
 				self.get_gl_dict({
@@ -875,7 +875,7 @@
 					"against": self.customer,
 					"debit": self.loyalty_amount,
 					"remark": "Loyalty Points redeemed by the customer"
-				})
+				}, item=self)
 			)
 
 	def make_pos_gl_entries(self, gl_entries):
@@ -896,7 +896,7 @@
 							"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
 							"against_voucher_type": self.doctype,
 							"cost_center": self.cost_center
-						}, self.party_account_currency)
+						}, self.party_account_currency, item=self)
 					)
 
 					payment_mode_account_currency = get_account_currency(payment_mode.account)
@@ -909,7 +909,7 @@
 								if payment_mode_account_currency==self.company_currency \
 								else payment_mode.amount,
 							"cost_center": self.cost_center
-						}, payment_mode_account_currency)
+						}, payment_mode_account_currency, item=self)
 					)
 
 	def make_gle_for_change_amount(self, gl_entries):
@@ -927,7 +927,7 @@
 						"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
 						"against_voucher_type": self.doctype,
 						"cost_center": self.cost_center
-					}, self.party_account_currency)
+					}, self.party_account_currency, item=self)
 				)
 
 				gl_entries.append(
@@ -936,7 +936,7 @@
 						"against": self.customer,
 						"credit": self.base_change_amount,
 						"cost_center": self.cost_center
-					})
+					}, item=self)
 				)
 			else:
 				frappe.throw(_("Select change amount account"), title="Mandatory Field")
@@ -960,7 +960,7 @@
 					"against_voucher": self.return_against if cint(self.is_return) else self.name,
 					"against_voucher_type": self.doctype,
 					"cost_center": self.cost_center
-				}, self.party_account_currency)
+				}, self.party_account_currency, item=self)
 			)
 			gl_entries.append(
 				self.get_gl_dict({
@@ -971,7 +971,7 @@
 						self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
 						else flt(self.write_off_amount, self.precision("write_off_amount"))),
 					"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
-				}, write_off_account_currency)
+				}, write_off_account_currency, item=self)
 			)
 
 	def make_gle_for_rounding_adjustment(self, gl_entries):
@@ -988,8 +988,7 @@
 					"credit": flt(self.base_rounding_adjustment,
 						self.precision("base_rounding_adjustment")),
 					"cost_center": self.cost_center or round_off_cost_center,
-				}
-			))
+				}, item=self))
 
 	def update_billing_status_in_dn(self, update_modified=True):
 		updated_delivery_notes = []
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 1188bea..2aecd6b 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -53,7 +53,7 @@
 			"label": __("Voucher No"),
 			"fieldtype": "Data",
 			on_change: function() {
-				frappe.query_report.set_filter_value('group_by', "");
+				frappe.query_report.set_filter_value('group_by', "Group by Voucher (Consolidated)");
 			}
 		},
 		{
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 622bab6..07752e1 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -46,7 +46,7 @@
 				"default": frappe.defaults.get_user_default("year_end_date"),
 			},
 			{
-				"fieldname":"cost_center",
+				"fieldname": "cost_center",
 				"label": __("Cost Center"),
 				"fieldtype": "Link",
 				"options": "Cost Center",
@@ -61,7 +61,13 @@
 				}
 			},
 			{
-				"fieldname":"finance_book",
+				"fieldname": "project",
+				"label": __("Project"),
+				"fieldtype": "Link",
+				"options": "Project"
+			},
+			{
+				"fieldname": "finance_book",
 				"label": __("Finance Book"),
 				"fieldtype": "Link",
 				"options": "Finance Book",
@@ -97,7 +103,7 @@
 	}
 
 	erpnext.dimension_filters.forEach((dimension) => {
-		frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
+		frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
 			"fieldname": dimension["fieldname"],
 			"label": __(dimension["label"]),
 			"fieldtype": "Link",
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index d783241..8bd4399 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -69,6 +69,10 @@
 	gl_entries_by_account = {}
 
 	opening_balances = get_opening_balances(filters)
+
+	#add filter inside list so that the query in financial_statements.py doesn't break
+	filters.project = [filters.project]
+
 	set_gl_entries_by_account(filters.company, filters.from_date,
 		filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
 
@@ -102,6 +106,9 @@
 		additional_conditions += """ and cost_center in (select name from `tabCost Center`
 			where lft >= %s and rgt <= %s)""" % (lft, rgt)
 
+	if filters.project:
+		additional_conditions += " and project = %(project)s"
+
 	if filters.finance_book:
 		fb_conditions = " AND finance_book = %(finance_book)s"
 		if filters.include_default_book_entries:
@@ -116,6 +123,7 @@
 		"from_date": filters.from_date,
 		"report_type": report_type,
 		"year_start_date": filters.year_start_date,
+		"project": filters.project,
 		"finance_book": filters.finance_book,
 		"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
 	}
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index a3200d5..505ba4c 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -125,7 +125,7 @@
 
 		if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
 			frappe.throw(_("Available-for-use Date should be after purchase date"))
-	
+
 	def validate_gross_and_purchase_amount(self):
 		if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount:
 			frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\
@@ -455,7 +455,7 @@
 			for d in self.get('finance_books'):
 				if d.finance_book == self.default_finance_book:
 					return cint(d.idx) - 1
-	
+
 	def validate_make_gl_entry(self):
 		purchase_document = self.get_purchase_document()
 		asset_bought_with_invoice = purchase_document == self.purchase_invoice
@@ -487,14 +487,14 @@
 		purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
 
 		return purchase_document
-	
+
 	def get_asset_accounts(self):
 		fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
 					asset_category = self.asset_category, company = self.company)
 
 		cwip_account = get_asset_account("capital_work_in_progress_account",
 			self.name, self.asset_category, self.company)
-		
+
 		return fixed_asset_account, cwip_account
 
 	def make_gl_entries(self):
@@ -513,7 +513,7 @@
 				"credit": self.purchase_receipt_amount,
 				"credit_in_account_currency": self.purchase_receipt_amount,
 				"cost_center": self.cost_center
-			}))
+			}, item=self))
 
 			gl_entries.append(self.get_gl_dict({
 				"account": fixed_asset_account,
@@ -523,7 +523,7 @@
 				"debit": self.purchase_receipt_amount,
 				"debit_in_account_currency": self.purchase_receipt_amount,
 				"cost_center": self.cost_center
-			}))
+			}, item=self))
 
 		if gl_entries:
 			from erpnext.accounts.general_ledger import make_gl_entries
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index 16061c6..1b8b404 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -18,6 +18,10 @@
 	refresh: function() {
 		var me = this;
 		this._super();
+
+		if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
+			this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
+		}
 		if (this.frm.doc.docstatus === 1) {
 			cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order,
 				__('Create'));
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 82fc628..3bc441a 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -13,9 +13,10 @@
   "supplier",
   "supplier_name",
   "column_break1",
-  "transaction_date",
-  "amended_from",
   "company",
+  "transaction_date",
+  "valid_till",
+  "amended_from",
   "address_section",
   "supplier_address",
   "contact_person",
@@ -791,13 +792,18 @@
    "options": "Opportunity",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "valid_till",
+   "fieldtype": "Date",
+   "label": "Valid Till"
   }
  ],
  "icon": "fa fa-shopping-cart",
  "idx": 29,
  "is_submittable": 1,
  "links": [],
- "modified": "2019-12-30 19:17:28.208693",
+ "modified": "2020-04-15 11:44:52.958022",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 5b4356a..baf2457 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import flt, nowdate, add_days
+from frappe.utils import flt, nowdate, add_days, getdate
 from frappe.model.mapper import get_mapped_doc
 
 from erpnext.controllers.buying_controller import BuyingController
@@ -28,6 +28,7 @@
 		validate_for_items(self)
 		self.validate_with_previous_doc()
 		self.validate_uom_is_integer("uom", "qty")
+		self.validate_valid_till()
 
 	def on_submit(self):
 		frappe.db.set(self, "status", "Submitted")
@@ -52,6 +53,11 @@
 				"is_child_table": True
 			}
 		})
+
+	def validate_valid_till(self):
+		if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
+			frappe.throw(_("Valid till Date cannot be before Transaction Date"))
+
 	def update_rfq_supplier_status(self, include_me):
 		rfq_list = set([])
 		for item in self.items:
@@ -158,3 +164,11 @@
 	}, target_doc)
 
 	return doclist
+
+def set_expired_status():
+	frappe.db.sql("""
+		UPDATE
+			`tabSupplier Quotation` SET `status` = 'Expired'
+		WHERE
+			`status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s
+		""", (nowdate()))
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js
index 9555439..9f4fece 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js
@@ -5,6 +5,8 @@
 			return [__("Ordered"), "green", "status,=,Ordered"];
 		} else if(doc.status==="Rejected") {
 			return [__("Lost"), "darkgrey", "status,=,Lost"];
+		} else if(doc.status==="Expired") {
+			return [__("Expired"), "darkgrey", "status,=,Expired"];
 		}
 	}
 };
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
index 3d05612..a76ffee 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
@@ -5,20 +5,18 @@
 	filters: [
 		{
 			fieldtype: "Link",
-			label: __("Supplier Quotation"),
-			options: "Supplier Quotation",
-			fieldname: "supplier_quotation",
-			default: "",
-			get_query: () => {
-				return { filters: { "docstatus": ["<", 2] } }
-			}
+			label: __("Company"),
+			options: "Company",
+			fieldname: "company",
+			default: frappe.defaults.get_user_default("Company"),
+			"reqd": 1
 		},
 		{
 			reqd: 1,
 			default: "",
 			options: "Item",
 			label: __("Item"),
-			fieldname: "item",
+			fieldname: "item_code",
 			fieldtype: "Link",
 			get_query: () => {
 				let quote = frappe.query_report.get_filter_value('supplier_quotation');
@@ -37,8 +35,37 @@
 					}
 				}
 			}
+		},
+		{
+			fieldname: "supplier",
+			label: __("Supplier"),
+			fieldtype: "MultiSelectList",
+			get_data: function(txt) {
+				return frappe.db.get_link_options('Supplier', txt);
+			}
+		},
+		{
+			fieldtype: "Link",
+			label: __("Supplier Quotation"),
+			options: "Supplier Quotation",
+			fieldname: "supplier_quotation",
+			default: "",
+			get_query: () => {
+				return { filters: { "docstatus": ["<", 2] } }
+			}
+		},
+		{
+			fieldtype: "Link",
+			label: __("Request for Quotation"),
+			options: "Request for Quotation",
+			fieldname: "request_for_quotation",
+			default: "",
+			get_query: () => {
+				return { filters: { "docstatus": ["<", 2] } }
+			}
 		}
 	],
+
 	onload: (report) => {
 		// Create a button for setting the default supplier
 		report.page.add_inner_button(__("Select Default Supplier"), () => {
@@ -102,6 +129,4 @@
 		});
 		dialog.show();
 	}
-}
-
-
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
index 5aff6ba..a33867a 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
@@ -2,103 +2,180 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-from erpnext.setup.utils import get_exchange_rate
-from frappe.utils import flt, cint
 import frappe
+from frappe.utils import flt, cint
+from frappe import _
+from collections import defaultdict
+from erpnext.setup.utils import get_exchange_rate
 
 def execute(filters=None):
-	qty_list = get_quantity_list(filters.item)
-	data = get_quote_list(filters.item, qty_list)
-	columns = get_columns(qty_list)
-	return columns, data
-	
-def get_quote_list(item, qty_list):
-	out = []
-	if not item:
+	if not filters:
+		return [], []
+
+	conditions = get_conditions(filters)
+	supplier_quotation_data = get_data(filters, conditions)
+	columns = get_columns()
+
+	data, chart_data = prepare_data(supplier_quotation_data)
+
+	return columns, data, None, chart_data
+
+def get_conditions(filters):
+	conditions = ""
+	if filters.get("supplier_quotation"):
+		conditions += " AND sqi.parent = %(supplier_quotation)s"
+
+	if filters.get("request_for_quotation"):
+		conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
+
+	if filters.get("supplier"):
+		conditions += " AND sq.supplier in %(supplier)s"
+	return conditions
+
+def get_data(filters, conditions):
+	if not filters.get("item_code"):
 		return []
 
-	suppliers = []
-	price_data = []
-	company_currency = frappe.db.get_default("currency")
-	float_precision = cint(frappe.db.get_default("float_precision")) or 2 
-	# Get the list of suppliers
-	for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
-		where item_code=%s and docstatus < 2""", item, as_dict=1):
-		for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
-			where name =%s and docstatus < 2""", root.parent, as_dict=1):
-			ip = frappe._dict({
-				"supplier": splr.supplier,
-				"qty": root.qty,
-				"parent": root.parent,
-				"rate": root.rate
-			})
-			price_data.append(ip)
-			suppliers.append(splr.supplier)
+	supplier_quotation_data = frappe.db.sql("""SELECT
+		sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
+		sq.supplier
+		FROM
+			`tabSupplier Quotation Item` sqi,
+			`tabSupplier Quotation` sq
+		WHERE
+			sqi.item_code = %(item_code)s
+			AND sqi.parent = sq.name
+			AND sqi.docstatus < 2
+			AND sq.company = %(company)s
+			AND sq.status != 'Expired'
+			{0}""".format(conditions), filters, as_dict=1)
 
-	#Add a row for each supplier
-	for root in set(suppliers):
-		supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
+	return supplier_quotation_data
+
+def prepare_data(supplier_quotation_data):
+	out, suppliers, qty_list = [], [], []
+	supplier_wise_map = defaultdict(list)
+	supplier_qty_price_map = {}
+
+	company_currency = frappe.db.get_default("currency")
+	float_precision = cint(frappe.db.get_default("float_precision")) or 2
+
+	for data in supplier_quotation_data:
+		supplier = data.get("supplier")
+		supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency")
+
 		if supplier_currency:
 			exchange_rate = get_exchange_rate(supplier_currency, company_currency)
 		else:
 			exchange_rate = 1
 
-		row = frappe._dict({
-			"supplier_name": root
-		})
-		for col in qty_list:
-			# Get the quantity for this row
-			for item_price in price_data:
-				if str(item_price.qty) == col.key and item_price.supplier == root:
-					row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
-					row[col.key + "QUOTE"] = item_price.parent
-					break
-				else:
-					row[col.key] = ""
-					row[col.key + "QUOTE"] = ""
-		out.append(row)
-			
-	return out
-	
-def get_quantity_list(item):
-	out = []
-	
-	if item:
-		qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
-			where ifnull(item_code,'')=%s and docstatus < 2 order by qty""", item, as_dict=1)
+		row = {
+			"quotation": data.get("parent"),
+			"qty": data.get("qty"),
+			"price": flt(data.get("rate") * exchange_rate, float_precision),
+			"uom": data.get("uom"),
+			"request_for_quotation": data.get("request_for_quotation"),
+		}
 
-		for qt in qty_list:
-			col = frappe._dict({
-				"key": str(qt.qty),
-				"label": "Qty: " + str(int(qt.qty))
-			})
-			out.append(col)
+		# map for report view of form {'supplier1':[{},{},...]}
+		supplier_wise_map[supplier].append(row)
 
-	return out
-	
-def get_columns(qty_list):
+		# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
+		if not supplier in supplier_qty_price_map:
+			supplier_qty_price_map[supplier] = {}
+		supplier_qty_price_map[supplier][row["qty"]] = row["price"]
+
+		suppliers.append(supplier)
+		qty_list.append(data.get("qty"))
+
+	suppliers = list(set(suppliers))
+	qty_list = list(set(qty_list))
+
+	# final data format for report view
+	for supplier in suppliers:
+		supplier_wise_map[supplier][0].update({"supplier_name": supplier})
+		for entry in supplier_wise_map[supplier]:
+			out.append(entry)
+
+	chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
+
+	return out, chart_data
+
+def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
+	data_points_map = {}
+	qty_list.sort()
+
+	# create qty wise values map of the form {'qty1':[value1, value2]}
+	for supplier in suppliers:
+		entry = supplier_qty_price_map[supplier]
+		for qty in qty_list:
+			if not qty in data_points_map:
+				data_points_map[qty] = []
+			if qty in entry:
+				data_points_map[qty].append(entry[qty])
+			else:
+				data_points_map[qty].append(None)
+
+	dataset = []
+	for qty in qty_list:
+		datapoints = {
+			"name": _("Price for Qty ") + str(qty),
+			"values": data_points_map[qty]
+		}
+		dataset.append(datapoints)
+
+	chart_data = {
+		"data": {
+			"labels": suppliers,
+			"datasets": dataset
+		},
+		"type": "bar"
+	}
+
+	return chart_data
+
+def get_columns():
 	columns = [{
 		"fieldname": "supplier_name",
-		"label": "Supplier",
+		"label": _("Supplier"),
 		"fieldtype": "Link",
 		"options": "Supplier",
 		"width": 200
-	}]
-
-	for qty in qty_list:
-		columns.append({
-			"fieldname": qty.key,
-			"label": qty.label,
-			"fieldtype": "Currency",
-			"options": "currency",
-			"width": 80
-		})
-		columns.append({
-			"fieldname": qty.key + "QUOTE",
-			"label": "Quotation",
-			"fieldtype": "Link",
-			"options": "Supplier Quotation",
-			"width": 90
-		})
+	},
+	{
+		"fieldname": "quotation",
+		"label": _("Supplier Quotation"),
+		"fieldtype": "Link",
+		"options": "Supplier Quotation",
+		"width": 200
+	},
+	{
+		"fieldname": "qty",
+		"label": _("Quantity"),
+		"fieldtype": "Float",
+		"width": 80
+	},
+	{
+		"fieldname": "price",
+		"label": _("Price"),
+		"fieldtype": "Currency",
+		"options": "Company:company:default_currency",
+		"width": 110
+	},
+	{
+		"fieldname": "uom",
+		"label": _("UOM"),
+		"fieldtype": "Link",
+		"options": "UOM",
+		"width": 90
+	},
+	{
+		"fieldname": "request_for_quotation",
+		"label": _("Request for Quotation"),
+		"fieldtype": "Link",
+		"options": "Request for Quotation",
+		"width": 200
+	}
+	]
 
 	return columns
\ No newline at end of file
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
index bdde9ee..377e061 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
@@ -15,7 +15,7 @@
 		params = urlencode({
 			"response_type":"code",
 			"client_id": self.consumer_key,
-			"redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?",
+			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
 			"scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social"
 		})
 
@@ -30,7 +30,7 @@
 			"code": code,
 			"client_id": self.consumer_key,
 			"client_secret": self.get_password(fieldname="consumer_secret"),
-			"redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?",
+			"redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
 		}
 		headers = {
 			"Content-Type": "application/x-www-form-urlencoded"
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.json b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
index f92e7f0..36776e5 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.json
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
@@ -11,8 +11,8 @@
   "consumer_key",
   "column_break_5",
   "consumer_secret",
-  "oauth_token",
-  "oauth_secret",
+  "access_token",
+  "access_token_secret",
   "session_status"
  ],
  "fields": [
@@ -42,20 +42,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "oauth_token",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "OAuth Token",
-   "read_only": 1
-  },
-  {
-   "fieldname": "oauth_secret",
-   "fieldtype": "Password",
-   "hidden": 1,
-   "label": "OAuth Token Secret",
-   "read_only": 1
-  },
-  {
    "fieldname": "column_break_5",
    "fieldtype": "Column Break"
   },
@@ -72,12 +58,26 @@
    "label": "Session Status",
    "options": "Expired\nActive",
    "read_only": 1
+  },
+  {
+   "fieldname": "access_token",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Access Token",
+   "read_only": 1
+  },
+  {
+   "fieldname": "access_token_secret",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Access Token Secret",
+   "read_only": 1
   }
  ],
  "image_field": "profile_pic",
  "issingle": 1,
  "links": [],
- "modified": "2020-04-21 22:06:43.726798",
+ "modified": "2020-05-13 17:50:47.934776",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Twitter Settings",
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
index 7616b4c..976a23d 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
@@ -31,13 +31,13 @@
 
 		try:
 			auth.get_access_token(oauth_verifier)
-			api = self.get_api()
+			api = self.get_api(auth.access_token, auth.access_token_secret)
 			user = api.me()
 			profile_pic = (user._json["profile_image_url"]).replace("_normal","")
 
 			frappe.db.set_value(self.doctype, self.name, {
-				"oauth_token" : auth.access_token,
-				"oauth_secret" : auth.access_token_secret,
+				"access_token" : auth.access_token,
+				"access_token_secret" : auth.access_token_secret,
 				"account_name" : user._json["screen_name"],
 				"profile_pic" : profile_pic,
 				"session_status" : "Active"
@@ -49,11 +49,11 @@
 			frappe.msgprint(_("Error! Failed to get access token."))
 			frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
 
-	def get_api(self):
+	def get_api(self, access_token, access_token_secret):
 		# authentication of consumer key and secret 
 		auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret")) 
 		# authentication of access token and secret 
-		auth.set_access_token(self.oauth_token, self.get_password(fieldname="oauth_secret")) 
+		auth.set_access_token(access_token, access_token_secret) 
 
 		return tweepy.API(auth)
 
@@ -67,13 +67,13 @@
 	
 	def upload_image(self, media):
 		media = get_file_path(media)
-		api = self.get_api()
+		api = self.get_api(self.access_token, self.access_token_secret)
 		media = api.media_upload(media)
 
 		return media.media_id
 
 	def send_tweet(self, text, media_id=None):
-		api = self.get_api()
+		api = self.get_api(self.access_token, self.access_token_secret)
 		try:
 			if media_id:
 				response = api.update_status(status = text, media_ids = [media_id])
diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py
index f0d60fa..25d67d2 100644
--- a/erpnext/education/doctype/fees/fees.py
+++ b/erpnext/education/doctype/fees/fees.py
@@ -98,14 +98,16 @@
 			"debit_in_account_currency": self.grand_total,
 			"against_voucher": self.name,
 			"against_voucher_type": self.doctype
-		})
+		}, item=self)
+
 		fee_gl_entry = self.get_gl_dict({
 			"account": self.income_account,
 			"against": self.student,
 			"credit": self.grand_total,
 			"credit_in_account_currency": self.grand_total,
 			"cost_center": self.cost_center
-		})
+		}, item=self)
+
 		from erpnext.accounts.general_ledger import make_gl_entries
 		make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2),
 			update_outstanding="Yes", merge_entries=False)
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index b4a5bd1..a706223 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -209,7 +209,7 @@
 			result.append(new_transaction.name)
 
 		except Exception:
-			frappe.throw(frappe.get_traceback())
+			frappe.throw(title=_('Bank transaction creation error'))
 
 	return result
 
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 6b198e7..ab161aa 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -308,7 +308,8 @@
 		"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
 		"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
 		"erpnext.selling.doctype.quotation.quotation.set_expired_status",
-		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status"
+		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
+		"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status"
 	],
 	"daily_long": [
 		"erpnext.setup.doctype.email_digest.email_digest.send",
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index ac1bfa1..ea469b8 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -116,8 +116,9 @@
 					"party_type": "Employee",
 					"party": self.employee,
 					"against_voucher_type": self.doctype,
-					"against_voucher": self.name
-				})
+					"against_voucher": self.name,
+					"cost_center": self.cost_center
+				}, item=self)
 			)
 
 		# expense entries
@@ -129,7 +130,7 @@
 					"debit_in_account_currency": data.sanctioned_amount,
 					"against": self.employee,
 					"cost_center": data.cost_center
-				})
+				}, item=data)
 			)
 
 		for data in self.advances:
@@ -157,7 +158,7 @@
 					"credit": self.grand_total,
 					"credit_in_account_currency": self.grand_total,
 					"against": self.employee
-				})
+				}, item=self)
 			)
 
 			gl_entry.append(
@@ -170,7 +171,7 @@
 					"debit_in_account_currency": self.grand_total,
 					"against_voucher": self.name,
 					"against_voucher_type": self.doctype,
-				})
+				}, item=self)
 			)
 
 		return gl_entry
@@ -187,7 +188,7 @@
 					"cost_center": self.cost_center,
 					"against_voucher_type": self.doctype,
 					"against_voucher": self.name
-				})
+				}, item=tax)
 			)
 
 	def validate_account_details(self):
diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
index 16e9eef..3cce50e 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
@@ -13,9 +13,11 @@
   "description",
   "section_break_6",
   "amount",
-  "cost_center",
   "column_break_8",
-  "sanctioned_amount"
+  "sanctioned_amount",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break"
  ],
  "fields": [
   {
@@ -104,12 +106,21 @@
    "fieldtype": "Link",
    "label": "Cost Center",
    "options": "Cost Center"
+  },
+  {
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2019-12-11 13:42:33.233432",
+ "modified": "2020-05-11 18:54:35.601592",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Claim Detail",
diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
index d68caf1..885e3ee 100644
--- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
+++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
@@ -8,14 +8,16 @@
  "engine": "InnoDB",
  "field_order": [
   "account_head",
-  "cost_center",
   "rate",
   "col_break1",
   "description",
   "section_break_6",
   "tax_amount",
   "column_break_8",
-  "total"
+  "total",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break"
  ],
  "fields": [
   {
@@ -91,11 +93,20 @@
   {
    "fieldname": "column_break_8",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-03-11 13:25:06.721917",
+ "modified": "2020-05-11 19:01:26.611758",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Taxes and Charges",
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 47b1bb7..d2620be 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -549,7 +549,7 @@
 
 	return _get_remaining_leaves(total_leaves, allocation.to_date)
 
-def get_leaves_for_period(employee, leave_type, from_date, to_date):
+def get_leaves_for_period(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False):
 	leave_entries = get_leave_entries(employee, leave_type, from_date, to_date)
 	leave_days = 0
 
@@ -559,8 +559,8 @@
 		if  inclusive_period and leave_entry.transaction_type == 'Leave Encashment':
 			leave_days += leave_entry.leaves
 
-		elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
-			and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date):
+		elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' and leave_entry.is_expired \
+			and (do_not_skip_expired_leaves or not skip_expiry_leaves(leave_entry, to_date)):
 			leave_days += leave_entry.leaves
 
 		elif leave_entry.transaction_type == 'Leave Application':
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
index 9ed58c9..63559c4 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
@@ -88,32 +88,40 @@
 	}, fieldname=['name'])
 
 def process_expired_allocation():
-	''' Check if a carry forwarded allocation has expired and create a expiry ledger entry '''
+	''' Check if a carry forwarded allocation has expired and create a expiry ledger entry
+		Case 1: carry forwarded expiry period is set for the leave type,
+			create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves
+		Case 2: leave type has no specific expiry period for carry forwarded leaves
+			and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves.
+	'''
 
 	# fetch leave type records that has carry forwarded leaves expiry
 	leave_type_records = frappe.db.get_values("Leave Type", filters={
 			'expire_carry_forwarded_leaves_after_days': (">", 0)
 		}, fieldname=['name'])
 
-	leave_type = [record[0] for record in leave_type_records]
+	leave_type = [record[0] for record in leave_type_records] or ['']
 
-	expired_allocation = frappe.db.sql_list("""SELECT name
-		FROM `tabLeave Ledger Entry`
-		WHERE
-			`transaction_type`='Leave Allocation'
-			AND `is_expired`=1""")
-
-	expire_allocation = frappe.get_all("Leave Ledger Entry",
-		fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'],
-		filters={
-			'to_date': ("<", today()),
-			'transaction_type': 'Leave Allocation',
-			'transaction_name': ('not in', expired_allocation)
-		},
-		or_filters={
-			'is_carry_forward': 0,
-			'leave_type': ('in', leave_type)
-		})
+	# fetch non expired leave ledger entry of transaction_type allocation
+	expire_allocation = frappe.db.sql("""
+		SELECT
+			leaves, to_date, employee, leave_type,
+			is_carry_forward, transaction_name as name, transaction_type
+		FROM `tabLeave Ledger Entry` l
+		WHERE (NOT EXISTS
+			(SELECT name
+				FROM `tabLeave Ledger Entry`
+				WHERE
+					transaction_name = l.transaction_name
+					AND transaction_type = 'Leave Allocation'
+					AND name<>l.name
+					AND docstatus = 1
+					AND (
+						is_carry_forward=l.is_carry_forward
+						OR (is_carry_forward = 0 AND leave_type not in %s)
+			)))
+			AND transaction_type = 'Leave Allocation'
+			AND to_date < %s""", (leave_type, today()), as_dict=1)
 
 	if expire_allocation:
 		create_expiry_ledger_entry(expire_allocation)
@@ -133,6 +141,7 @@
 			'employee': allocation.employee,
 			'leave_type': allocation.leave_type,
 			'to_date': ('<=', allocation.to_date),
+			'docstatus': 1
 		}, fieldname=['SUM(leaves)'])
 
 @frappe.whitelist()
@@ -159,7 +168,8 @@
 def expire_carried_forward_allocation(allocation):
 	''' Expires remaining leaves in the on carried forward allocation '''
 	from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
-	leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date)
+	leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type,
+		allocation.from_date, allocation.to_date, do_not_skip_expired_leaves=True)
 	leaves = flt(allocation.leaves) + flt(leaves_taken)
 
 	# allow expired leaves entry to be created
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index f721724..ebbcccc 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -623,7 +623,7 @@
 erpnext.patches.v12_0.update_due_date_in_gle
 erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
 erpnext.patches.v12_0.update_ewaybill_field_position
-erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
+erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes #2020-05-11
 erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
 erpnext.patches.v12_0.move_plaid_settings_to_doctype
 execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link')
@@ -678,6 +678,9 @@
 erpnext.patches.v12_0.fix_quotation_expired_status
 erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
 erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
+erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries
 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
 execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
 execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
+erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
+erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation
diff --git a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
index b71ea66..657decf 100644
--- a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
+++ b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
@@ -20,7 +20,8 @@
 		else:
 			insert_after_field = 'accounting_dimensions_section'
 
-		for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item"]:
+		for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item",
+			"Expense Claim Detail", "Expense Taxes and Charges"]:
 
 			field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
 
diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
new file mode 100644
index 0000000..98a2fcf
--- /dev/null
+++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
@@ -0,0 +1,44 @@
+# Copyright (c) 2018, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	"""Delete duplicate leave ledger entries of type allocation created."""
+	if not frappe.db.a_row_exists("Leave Ledger Entry"):
+		return
+
+	duplicate_records_list = get_duplicate_records()
+	delete_duplicate_ledger_entries(duplicate_records_list)
+
+def get_duplicate_records():
+	"""Fetch all but one duplicate records from the list of expired leave allocation."""
+	return frappe.db.sql_list("""
+		WITH duplicate_records AS
+		(SELECT
+			name, transaction_name, is_carry_forward,
+			ROW_NUMBER() over(partition by transaction_name order by creation)as row
+		FROM `tabLeave Ledger Entry` l
+		WHERE (EXISTS
+			(SELECT name
+				FROM `tabLeave Ledger Entry`
+				WHERE
+					transaction_name = l.transaction_name
+					AND transaction_type = 'Leave Allocation'
+					AND name <> l.name
+					AND employee = l.employee
+					AND docstatus = 1
+					AND leave_type = l.leave_type
+					AND is_carry_forward=l.is_carry_forward
+					AND to_date = l.to_date
+					AND from_date = l.from_date
+					AND is_expired = 1
+		)))
+		SELECT name FROM duplicate_records WHERE row > 1
+	""")
+
+def delete_duplicate_ledger_entries(duplicate_records_list):
+	"""Delete duplicate leave ledger entries."""
+	if duplicate_records_list:
+		frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
new file mode 100644
index 0000000..4a6e228
--- /dev/null
+++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc("buying", "doctype", "supplier_quotation")
+	frappe.db.sql("""UPDATE `tabSupplier Quotation`
+		SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH)
+		WHERE docstatus < 2""")
diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
new file mode 100644
index 0000000..60aec05
--- /dev/null
+++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+    invalid_selling_item_price = frappe.db.sql(
+        """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')"""
+    )
+    invalid_buying_item_price = frappe.db.sql(
+        """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')"""
+    )
+    docs_to_modify = invalid_buying_item_price + invalid_selling_item_price
+    for d in docs_to_modify:
+        # saving the doc will auto reset invalid customer/supplier field
+        doc = frappe.get_doc("Item Price", d[0])
+        doc.save()
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 50e719f..3d172ac 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -165,6 +165,10 @@
 				contact.mobile_no = lead.mobile_no
 				contact.is_primary_contact = 1
 				contact.append('links', dict(link_doctype='Customer', link_name=self.name))
+				if lead.email_id:
+					contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1))
+				if lead.mobile_no:
+					contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1))
 				contact.flags.ignore_permissions = self.flags.ignore_permissions
 				contact.autoname()
 				if not frappe.db.exists("Contact", contact.name):
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index b37cc17..29f6c37 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -47,9 +47,7 @@
 		frappe.delete_doc("Company", "COA from Existing Company")
 
 	def test_coa_based_on_country_template(self):
-		countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France",
-			"Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore",
-			"Brazil", "Argentina", "Hungary", "Taiwan"]
+		countries = ["Canada", "Germany", "France"]
 
 		for country in countries:
 			templates = get_charts_for_country(country)
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index e11e1bb..4ac546e 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -541,27 +541,31 @@
 	return doc.tc_name
 
 @frappe.whitelist(allow_guest=True)
-def apply_coupon_code(applied_code,applied_referral_sales_partner):
+def apply_coupon_code(applied_code, applied_referral_sales_partner):
 	quotation = True
-	if applied_code:
-		coupon_list=frappe.get_all('Coupon Code', filters={"docstatus": ("<", "2"), 'coupon_code':applied_code }, fields=['name'])
-		if coupon_list:
-			coupon_name=coupon_list[0].name
-			from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
-			validate_coupon_code(coupon_name)
-			quotation = _get_cart_quotation()
-			quotation.coupon_code=coupon_name
+
+	if not applied_code:
+		frappe.throw(_("Please enter a coupon code"))
+
+	coupon_list = frappe.get_all('Coupon Code', filters={'coupon_code': applied_code})
+	if not coupon_list:
+		frappe.throw(_("Please enter a valid coupon code"))
+
+	coupon_name = coupon_list[0].name
+
+	from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
+	validate_coupon_code(coupon_name)
+	quotation = _get_cart_quotation()
+	quotation.coupon_code = coupon_name
+	quotation.flags.ignore_permissions = True
+	quotation.save()
+
+	if applied_referral_sales_partner:
+		sales_partner_list = frappe.get_all('Sales Partner', filters={'referral_code': applied_referral_sales_partner})
+		if sales_partner_list:
+			sales_partner_name = sales_partner_list[0].name
+			quotation.referral_sales_partner = sales_partner_name
 			quotation.flags.ignore_permissions = True
 			quotation.save()
-			if applied_referral_sales_partner:
-				sales_partner_list=frappe.get_all('Sales Partner', filters={'docstatus': 0, 'referral_code':applied_referral_sales_partner }, fields=['name'])
-				if sales_partner_list:
-					sales_partner_name=sales_partner_list[0].name
-					quotation.referral_sales_partner=sales_partner_name
-					quotation.flags.ignore_permissions = True
-					quotation.save()
-		else:
-			frappe.throw(_("Please enter valid coupon code !!"))
-	else:
-		frappe.throw(_("Please enter coupon code !!"))
+
 	return quotation
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 957c415..8e39eb5 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -69,3 +69,10 @@
 			self.reference = self.customer
 		if self.buying:
 			self.reference = self.supplier
+		
+		if self.selling and not self.buying:
+			# if only selling then remove supplier
+			self.supplier = None
+		if self.buying and not self.selling:
+			# if only buying then remove customer
+			self.customer = None
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index d50712a..11b6403 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -630,7 +630,7 @@
 		elif args.get("supplier"):
 			conditions += " and supplier=%(supplier)s"
 		else:
-			conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')"
+			conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')"
 
 	if args.get('transaction_date'):
 		conditions += """ and %(transaction_date)s between