Merge pull request #17306 from surajshetty3416/fix-employee-permission

fix: Do not create employee user permission if already exists
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 64aa4e4..c8756af 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -145,23 +145,25 @@
 
 		if getdate(self.loan_end_date) > getdate(nowdate()):
 			for d in self.invoices:
-				je.append("accounts", {
-					"account": self.accounts_receivable_discounted,
-					"credit_in_account_currency": flt(d.outstanding_amount),
-					"reference_type": "Invoice Discounting",
-					"reference_name": self.name,
-					"party_type": "Customer",
-					"party": d.customer
-				})
+				outstanding_amount = frappe.db.get_value("Sales Invoice", d.sales_invoice, "outstanding_amount")
+				if flt(outstanding_amount) > 0:
+					je.append("accounts", {
+						"account": self.accounts_receivable_discounted,
+						"credit_in_account_currency": flt(outstanding_amount),
+						"reference_type": "Invoice Discounting",
+						"reference_name": self.name,
+						"party_type": "Customer",
+						"party": d.customer
+					})
 
-				je.append("accounts", {
-					"account": self.accounts_receivable_unpaid,
-					"debit_in_account_currency": flt(d.outstanding_amount),
-					"reference_type": "Invoice Discounting",
-					"reference_name": self.name,
-					"party_type": "Customer",
-					"party": d.customer
-				})
+					je.append("accounts", {
+						"account": self.accounts_receivable_unpaid,
+						"debit_in_account_currency": flt(outstanding_amount),
+						"reference_type": "Invoice Discounting",
+						"reference_name": self.name,
+						"party_type": "Customer",
+						"party": d.customer
+					})
 
 		return je
 
@@ -190,9 +192,28 @@
 			customer,
 			posting_date,
 			outstanding_amount
-		from `tabSales Invoice`
+		from `tabSales Invoice` si
 		where
 			docstatus = 1
 			and outstanding_amount > 0
 			%s
-	""" % where_condition, filters, as_dict=1)
\ No newline at end of file
+			and not exists(select di.name from `tabDiscounted Invoice` di
+				where di.docstatus=1 and di.sales_invoice=si.name)
+	""" % where_condition, filters, as_dict=1)
+
+def get_party_account_based_on_invoice_discounting(sales_invoice):
+	party_account = None
+	invoice_discounting = frappe.db.sql("""
+		select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
+		from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
+		where par.name=ch.parent
+			and par.docstatus=1
+			and ch.sales_invoice = %s
+	""", (sales_invoice), as_dict=1)
+	if invoice_discounting:
+		if invoice_discounting[0].status == "Disbursed":
+			party_account = invoice_discounting[0].accounts_receivable_discounted
+		elif invoice_discounting[0].status == "Settled":
+			party_account = invoice_discounting[0].accounts_receivable_unpaid
+
+	return party_account
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
new file mode 100644
index 0000000..6523cd3
--- /dev/null
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py
@@ -0,0 +1,20 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'reference_name',
+		'internal_links': {
+			'Sales Invoice': ['invoices', 'sales_invoice']
+		},
+		'transactions': [
+			{
+				'label': _('Reference'),
+				'items': ['Sales Invoice']
+			},
+			{
+				'label': _('Payment'),
+				'items': ['Payment Entry', 'Journal Entry']
+			}
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
index 4f93e11..3d74d9a 100644
--- a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py
@@ -122,27 +122,62 @@
 			period=60
 			)
 
-		inv_disc.create_disbursement_entry()
-		je = inv_disc.close_loan()
+		je1 = inv_disc.create_disbursement_entry()
+		je1.posting_date = nowdate()
+		je1.submit()
 
-		self.assertEqual(je.accounts[0].account, self.short_term_loan)
-		self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
+		je2 = inv_disc.close_loan()
 
-		self.assertEqual(je.accounts[1].account, self.bank_account)
-		self.assertEqual(je.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
+		self.assertEqual(je2.accounts[0].account, self.short_term_loan)
+		self.assertEqual(je2.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
 
-		self.assertEqual(je.accounts[2].account, self.ar_discounted)
-		self.assertEqual(je.accounts[2].credit_in_account_currency, flt(inv.outstanding_amount))
+		self.assertEqual(je2.accounts[1].account, self.bank_account)
+		self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
 
-		self.assertEqual(je.accounts[3].account, self.ar_unpaid)
-		self.assertEqual(je.accounts[3].debit_in_account_currency, flt(inv.outstanding_amount))
+		self.assertEqual(je2.accounts[2].account, self.ar_discounted)
+		self.assertEqual(je2.accounts[2].credit_in_account_currency, flt(inv.outstanding_amount))
 
-		je.posting_date = nowdate()
-		je.submit()
+		self.assertEqual(je2.accounts[3].account, self.ar_unpaid)
+		self.assertEqual(je2.accounts[3].debit_in_account_currency, flt(inv.outstanding_amount))
+
+		je2.posting_date = nowdate()
+		je2.submit()
 		inv_disc.reload()
 
 		self.assertEqual(inv_disc.status, "Settled")
 
+	def test_on_close_after_loan_period_after_inv_payment(self):
+		inv = create_sales_invoice(rate=600)
+		inv_disc = create_invoice_discounting([inv.name],
+			accounts_receivable_credit=self.ar_credit,
+			accounts_receivable_discounted=self.ar_discounted,
+			accounts_receivable_unpaid=self.ar_unpaid,
+			short_term_loan=self.short_term_loan,
+			bank_charges_account=self.bank_charges_account,
+			bank_account=self.bank_account,
+			start=nowdate(),
+			period=60
+			)
+
+		je1 = inv_disc.create_disbursement_entry()
+		je1.posting_date = nowdate()
+		je1.submit()
+
+		je_on_payment = frappe.get_doc(get_payment_entry_against_invoice("Sales Invoice", inv.name))
+		je_on_payment.posting_date = nowdate()
+		je_on_payment.cheque_no = "126981"
+		je_on_payment.cheque_date = nowdate()
+		je_on_payment.save()
+		je_on_payment.submit()
+
+		je2 = inv_disc.close_loan()
+
+		self.assertEqual(je2.accounts[0].account, self.short_term_loan)
+		self.assertEqual(je2.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
+
+		self.assertEqual(je2.accounts[1].account, self.bank_account)
+		self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
+
 	def test_on_close_before_loan_period(self):
 		inv = create_sales_invoice(rate=700)
 		inv_disc = create_invoice_discounting([inv.name],
@@ -154,23 +189,21 @@
 			bank_account=self.bank_account,
 			start=add_days(nowdate(), -80),
 			period=60
-			)
+		)
 
-		inv_disc.create_disbursement_entry()
-		je = inv_disc.close_loan()
+		je1 = inv_disc.create_disbursement_entry()
+		je1.posting_date = nowdate()
+		je1.submit()
 
-		je.posting_date = nowdate()
-		je.submit()
+		je2 = inv_disc.close_loan()
+		je2.posting_date = nowdate()
+		je2.submit()
 
-		self.assertEqual(je.accounts[0].account, self.short_term_loan)
-		self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
+		self.assertEqual(je2.accounts[0].account, self.short_term_loan)
+		self.assertEqual(je2.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
 
-		self.assertEqual(je.accounts[1].account, self.bank_account)
-		self.assertEqual(je.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
-
-		inv_disc.reload()
-
-		self.assertEqual(inv_disc.status, "Settled")
+		self.assertEqual(je2.accounts[1].account, self.bank_account)
+		self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
 
 	def test_make_payment_before_loan_period(self):
 		#it has problem
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 28869d8..63f180d 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -3,13 +3,14 @@
 
 from __future__ import unicode_literals
 import frappe, erpnext, json
-from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, nowdate, cint
+from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, nowdate, cint, get_link_to_form
 from frappe import msgprint, _, scrub
 from erpnext.controllers.accounts_controller import AccountsController
 from erpnext.accounts.utils import get_balance_on, get_account_currency
 from erpnext.accounts.party import get_party_account
 from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
 from erpnext.hr.doctype.loan.loan import update_disbursement_status, update_total_amount_paid
+from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
 
 from six import string_types, iteritems
 
@@ -53,6 +54,20 @@
 		self.update_inter_company_jv()
 		self.update_invoice_discounting()
 
+	def on_cancel(self):
+		from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
+		from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
+		unlink_ref_doc_from_payment_entries(self)
+		unlink_ref_doc_from_salary_slip(self.name)
+		self.make_gl_entries(1)
+		self.update_advance_paid()
+		self.update_expense_claim()
+		self.update_loan()
+		self.unlink_advance_entry_reference()
+		self.unlink_asset_reference()
+		self.unlink_inter_company_jv()
+		self.unlink_asset_adjustment_entry()
+		self.update_invoice_discounting()
 
 	def get_title(self):
 		return self.pay_to_recd_from or self.accounts[0].account
@@ -83,31 +98,32 @@
 				"inter_company_journal_entry_reference", self.name)
 
 	def update_invoice_discounting(self):
-		invoice_discounting_list = [d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]
+		def _validate_invoice_discounting_status(inv_disc, id_status, expected_status, row_id):
+			id_link = get_link_to_form("Invoice Discounting", inv_disc)
+			if id_status != expected_status:
+				frappe.throw(_("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(d.idx, expected_status, id_link))
+
+		invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]))
 		for inv_disc in invoice_discounting_list:
-			short_term_loan_account = frappe.db.get_value("Invoice Discounting", inv_disc, "short_term_loan")
+			short_term_loan_account, id_status = frappe.db.get_value("Invoice Discounting", inv_disc, ["short_term_loan", "status"])
 			for d in self.accounts:
 				if d.account == short_term_loan_account and d.reference_name == inv_disc:
-					if d.credit > 0:
-						status = "Disbursed"
-					elif d.debit > 0:
-						status = "Settled"
+					if self.docstatus == 1:
+						if d.credit > 0:
+							_validate_invoice_discounting_status(inv_disc, id_status, "Sanctioned", d.idx)
+							status = "Disbursed"
+						elif d.debit > 0:
+							_validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx)
+							status = "Settled"
+					else:
+						if d.credit > 0:
+							_validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx)
+							status = "Sanctioned"
+						elif d.debit > 0:
+							_validate_invoice_discounting_status(inv_disc, id_status, "Settled", d.idx)
+							status = "Disbursed"
 					frappe.db.set_value("Invoice Discounting", inv_disc, "status", status)
 
-	def on_cancel(self):
-		from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
-		from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
-		unlink_ref_doc_from_payment_entries(self)
-		unlink_ref_doc_from_salary_slip(self.name)
-		self.make_gl_entries(1)
-		self.update_advance_paid()
-		self.update_expense_claim()
-		self.update_loan()
-		self.unlink_advance_entry_reference()
-		self.unlink_asset_reference()
-		self.unlink_inter_company_jv()
-		self.unlink_asset_adjustment_entry()
-
 	def unlink_advance_entry_reference(self):
 		for d in self.get("accounts"):
 			if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
@@ -732,23 +748,6 @@
 		"journal_entry": journal_entry
 	})
 
-def get_party_account_based_on_invoice_discounting(sales_invoice):
-	party_account = None
-	invoice_discounting = frappe.db.sql("""
-		select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
-		from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
-		where par.name=ch.parent
-			and par.docstatus=1
-			and ch.sales_invoice = %s
-	""", (sales_invoice), as_dict=1)
-	if invoice_discounting:
-		if invoice_discounting[0].status == "Disbursed":
-			party_account = invoice_discounting[0].accounts_receivable_discounted
-		elif invoice_discounting[0].status == "Settled":
-			party_account = invoice_discounting[0].accounts_receivable_unpaid
-
-	return party_account
-
 def get_payment_entry(ref_doc, args):
 	cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company',  ref_doc.company,  "cost_center")
 	exchange_rate = 1
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index eb672e0..b7ab4fe 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -14,6 +14,7 @@
 from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
 from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details
 from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
+from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
 
 from six import string_types, iteritems
 
@@ -237,7 +238,7 @@
 
 					if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
 						if self.party_type == "Customer":
-							ref_party_account = ref_doc.debit_to
+							ref_party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
 						elif self.party_type == "Student":
 							ref_party_account = ref_doc.receivable_account
 						elif self.party_type=="Supplier":
@@ -826,7 +827,7 @@
 
 	# party account
 	if dt == "Sales Invoice":
-		party_account = doc.debit_to
+		party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
 	elif dt == "Purchase Invoice":
 		party_account = doc.credit_to
 	elif dt == "Fees":
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index 8a39744..8500aea 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -112,13 +112,15 @@
 
 			if entry.get('debit'):
 				entry['debit'] = converted_value
-			else:
+
+			if entry.get('credit'):
 				entry['credit'] = converted_value
 
 		elif account_currency == presentation_currency:
 			if entry.get('debit'):
 				entry['debit'] = debit_in_account_currency
-			else:
+
+			if entry.get('credit'):
 				entry['credit'] = credit_in_account_currency
 
 		converted_gl_list.append(entry)
diff --git a/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json b/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json
index f351f3b..9e6e80a 100644
--- a/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json
+++ b/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json
@@ -1,28 +1,29 @@
 {
- "add_total_row": 1, 
- "apply_user_permissions": 1, 
- "creation": "2013-05-13 16:10:02", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 3, 
- "is_standard": "Yes", 
- "modified": "2017-02-24 20:10:53.005589", 
- "modified_by": "Administrator", 
- "module": "Buying", 
- "name": "Requested Items To Be Ordered", 
- "owner": "Administrator", 
- "query": "select \n    mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.qty, 0)) as \"Qty:Float:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.qty, 0))\norder by mr.transaction_date asc", 
- "ref_doctype": "Purchase Order", 
- "report_name": "Requested Items To Be Ordered", 
- "report_type": "Query Report", 
+ "add_total_row": 1,
+ "creation": "2013-05-13 16:10:02",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2019-04-18 19:02:03.099422",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Requested Items To Be Ordered",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "query": "select \n    mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc",
+ "ref_doctype": "Purchase Order",
+ "report_name": "Requested Items To Be Ordered",
+ "report_type": "Query Report",
  "roles": [
   {
    "role": "Stock User"
-  }, 
+  },
   {
    "role": "Purchase Manager"
-  }, 
+  },
   {
    "role": "Purchase User"
   }
diff --git a/erpnext/config/accounting.py b/erpnext/config/accounting.py
index afe35f8..1b8bf27 100644
--- a/erpnext/config/accounting.py
+++ b/erpnext/config/accounting.py
@@ -234,6 +234,11 @@
 				},
 				{
 					"type": "doctype",
+					"label": _("Invoice Discounting"),
+					"name": "Invoice Discounting",
+				},
+				{
+					"type": "doctype",
 					"label": _("Bank Statement Transaction Entry List"),
 					"name": "Bank Statement Transaction Entry",
 					"route": "#List/Bank Statement Transaction Entry",
diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json
index 2602aef..707db08 100644
--- a/erpnext/projects/doctype/task/task.json
+++ b/erpnext/projects/doctype/task/task.json
@@ -1392,7 +1392,7 @@
  "istable": 0,
  "max_attachments": 5,
  "menu_index": 0,
- "modified": "2019-02-19 12:22:02.147606",
+ "modified": "2019-04-18 12:22:02.147606",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Task",
@@ -1422,11 +1422,11 @@
  "read_only": 0,
  "read_only_onload": 0,
  "search_fields": "subject",
- "show_name_in_global_search": 0,
+ "show_name_in_global_search": 1,
  "sort_order": "DESC",
  "timeline_field": "project",
  "title_field": "subject",
  "track_changes": 0,
  "track_seen": 1,
  "track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index aa51f47..aeab9ed 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -1062,3 +1062,7 @@
 		count+=1
 		if publish_progress:
 				frappe.publish_progress(count*100/len(variants), title = _("Updating Variants..."))
+
+def on_doctype_update():
+	# since route is a Text column, it needs a length for indexing
+	frappe.db.add_index("Item", ["route(500)"])
diff --git a/erpnext/templates/pages/demo.html b/erpnext/templates/pages/demo.html
index 8eec800..a4b5e01 100644
--- a/erpnext/templates/pages/demo.html
+++ b/erpnext/templates/pages/demo.html
@@ -60,7 +60,7 @@
 </div>
 
 
-<p class='text-muted text-center small' style='margin-top: -20px;'><a href="https://erpnext.com/pricing">Start a free 30-day trial </a>
+<p class='text-muted text-center small' style='margin-top: -20px;'><a href="https://erpnext.com/pricing">Start a free 14-day trial </a>
 </p>
 <style>
 html, body {