fix: UX fixes in loan (#18220)

* fix: UX fixes in loan

* Update loan.py
diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py
deleted file mode 100644
index b6c6502..0000000
--- a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe, math
-from frappe import _
-from frappe.utils import flt, rounded
-from frappe.model.mapper import get_mapped_doc
-from frappe.model.document import Document
-
-from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method
-
-class EmployeeLoanApplication(Document):
-	def validate(self):
-		check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
-		self.validate_loan_amount()
-		self.get_repayment_details()
-
-	def validate_loan_amount(self):
-		maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount')
-		if maximum_loan_limit and self.loan_amount > maximum_loan_limit:
-			frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit))
-
-	def get_repayment_details(self):
-		if self.repayment_method == "Repay Over Number of Periods":
-			self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
-
-		if self.repayment_method == "Repay Fixed Amount per Period":
-			monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
-			if monthly_interest_rate:
-				monthly_interest_amount = self.loan_amount * monthly_interest_rate
-				if monthly_interest_amount >= self.repayment_amount:
-					frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}").
-						format(self.repayment_amount, monthly_interest_amount))
-
-				self.repayment_periods = math.ceil((math.log(self.repayment_amount) - 
-					math.log(self.repayment_amount - (monthly_interest_amount))) /
-					(math.log(1 + monthly_interest_rate)))
-			else:
-				self.repayment_periods = self.loan_amount / self.repayment_amount
-
-		self.calculate_payable_amount()
-		
-	def calculate_payable_amount(self):
-		balance_amount = self.loan_amount
-		self.total_payable_amount = 0
-		self.total_payable_interest = 0
-
-		while(balance_amount > 0):
-			interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
-			balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
-
-			self.total_payable_interest += interest_amount
-			
-		self.total_payable_amount = self.loan_amount + self.total_payable_interest
-		
-@frappe.whitelist()
-def make_employee_loan(source_name, target_doc = None):
-	doclist = get_mapped_doc("Employee Loan Application", source_name, {
-		"Employee Loan Application": {
-			"doctype": "Employee Loan",
-			"validation": {
-				"docstatus": ["=", 1]
-			}
-		}
-	}, target_doc)
-
-	return doclist
\ No newline at end of file
diff --git a/erpnext/hr/doctype/loan/loan.js b/erpnext/hr/doctype/loan/loan.js
index e1b4178..3f5c30c 100644
--- a/erpnext/hr/doctype/loan/loan.js
+++ b/erpnext/hr/doctype/loan/loan.js
@@ -39,31 +39,19 @@
 	},
 
 	refresh: function (frm) {
-		if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") {
-			frm.add_custom_button(__('Create Disbursement Entry'), function() {
-				frm.trigger("make_jv");
-			})
-		}
-		if (frm.doc.repayment_schedule) {
-			let total_amount_paid = 0;
-			$.each(frm.doc.repayment_schedule || [], function(i, row) {
-				if (row.paid) {
-					total_amount_paid += row.total_payment;
-				}
-			});
-			frm.set_value("total_amount_paid", total_amount_paid);
-;		}
-		if (frm.doc.docstatus == 1 && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
-			frm.add_custom_button(__('Create Repayment Entry'), function() {
-				frm.trigger("make_repayment_entry");
-			})
+		if (frm.doc.docstatus == 1) {
+			if (frm.doc.status == "Sanctioned") {
+				frm.add_custom_button(__('Create Disbursement Entry'), function() {
+					frm.trigger("make_jv");
+				}).addClass("btn-primary");
+			} else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
+				frm.add_custom_button(__('Create Repayment Entry'), function() {
+					frm.trigger("make_repayment_entry");
+				}).addClass("btn-primary");
+			}
 		}
 		frm.trigger("toggle_fields");
 	},
-	status: function (frm) {
-		frm.toggle_reqd("disbursement_date", frm.doc.status == 'Disbursed')
-		frm.toggle_reqd("repayment_start_date", frm.doc.status == 'Disbursed')
-	},
 
 	make_jv: function (frm) {
 		frappe.call({
diff --git a/erpnext/hr/doctype/loan/loan.json b/erpnext/hr/doctype/loan/loan.json
index 587b301..505b601 100644
--- a/erpnext/hr/doctype/loan/loan.json
+++ b/erpnext/hr/doctype/loan/loan.json
@@ -1,5 +1,6 @@
 {
  "allow_copy": 0, 
+ "allow_events_in_timeline": 0, 
  "allow_guest_to_view": 0, 
  "allow_import": 1, 
  "allow_rename": 0, 
@@ -20,6 +21,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "applicant_type", 
    "fieldtype": "Select", 
    "hidden": 0, 
@@ -53,6 +55,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "applicant", 
    "fieldtype": "Dynamic Link", 
    "hidden": 0, 
@@ -86,6 +89,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "applicant_name", 
    "fieldtype": "Data", 
    "hidden": 0, 
@@ -118,6 +122,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "loan_application", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -151,6 +156,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "loan_type", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -184,6 +190,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "column_break_3", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -215,7 +222,8 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "default": "", 
+   "default": "Today", 
+   "fetch_if_empty": 0, 
    "fieldname": "posting_date", 
    "fieldtype": "Date", 
    "hidden": 0, 
@@ -248,6 +256,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "company", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -282,6 +291,7 @@
    "collapsible": 0, 
    "columns": 0, 
    "default": "Sanctioned", 
+   "fetch_if_empty": 0, 
    "fieldname": "status", 
    "fieldtype": "Select", 
    "hidden": 0, 
@@ -299,7 +309,7 @@
    "precision": "", 
    "print_hide": 0, 
    "print_hide_if_no_value": 0, 
-   "read_only": 0, 
+   "read_only": 1, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
    "reqd": 0, 
@@ -316,6 +326,7 @@
    "collapsible": 0, 
    "columns": 0, 
    "depends_on": "eval:doc.applicant_type==\"Employee\"", 
+   "fetch_if_empty": 0, 
    "fieldname": "repay_from_salary", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -348,6 +359,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "section_break_8", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -380,6 +392,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "loan_amount", 
    "fieldtype": "Currency", 
    "hidden": 0, 
@@ -415,6 +428,7 @@
    "columns": 0, 
    "default": "", 
    "fetch_from": "loan_type.rate_of_interest", 
+   "fetch_if_empty": 0, 
    "fieldname": "rate_of_interest", 
    "fieldtype": "Percent", 
    "hidden": 0, 
@@ -448,6 +462,8 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "depends_on": "eval:doc.status==\"Disbursed\"", 
+   "fetch_if_empty": 0, 
    "fieldname": "disbursement_date", 
    "fieldtype": "Date", 
    "hidden": 0, 
@@ -480,6 +496,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "repayment_start_date", 
    "fieldtype": "Date", 
    "hidden": 0, 
@@ -499,7 +516,7 @@
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 0, 
+   "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
    "translatable": 0, 
@@ -512,6 +529,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "column_break_11", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -544,6 +562,7 @@
    "collapsible": 0, 
    "columns": 0, 
    "default": "Repay Over Number of Periods", 
+   "fetch_if_empty": 0, 
    "fieldname": "repayment_method", 
    "fieldtype": "Select", 
    "hidden": 0, 
@@ -579,6 +598,7 @@
    "columns": 0, 
    "default": "", 
    "depends_on": "", 
+   "fetch_if_empty": 0, 
    "fieldname": "repayment_periods", 
    "fieldtype": "Int", 
    "hidden": 0, 
@@ -613,6 +633,7 @@
    "columns": 0, 
    "default": "", 
    "depends_on": "", 
+   "fetch_if_empty": 0, 
    "fieldname": "monthly_repayment_amount", 
    "fieldtype": "Currency", 
    "hidden": 0, 
@@ -646,6 +667,7 @@
    "bold": 0, 
    "collapsible": 1, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "account_info", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -678,6 +700,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "mode_of_payment", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -711,6 +734,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "payment_account", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -744,6 +768,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "column_break_9", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -775,6 +800,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "loan_account", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -808,6 +834,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "interest_income_account", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -841,6 +868,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "section_break_15", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -873,6 +901,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "repayment_schedule", 
    "fieldtype": "Table", 
    "hidden": 0, 
@@ -906,6 +935,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "section_break_17", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -939,6 +969,7 @@
    "collapsible": 0, 
    "columns": 0, 
    "default": "0", 
+   "fetch_if_empty": 0, 
    "fieldname": "total_payment", 
    "fieldtype": "Currency", 
    "hidden": 0, 
@@ -972,6 +1003,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "column_break_19", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -1004,6 +1036,7 @@
    "collapsible": 0, 
    "columns": 0, 
    "default": "0", 
+   "fetch_if_empty": 0, 
    "fieldname": "total_interest_payable", 
    "fieldtype": "Currency", 
    "hidden": 0, 
@@ -1037,6 +1070,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "total_amount_paid", 
    "fieldtype": "Currency", 
    "hidden": 0, 
@@ -1070,6 +1104,7 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "amended_from", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -1106,7 +1141,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-08-21 16:15:53.267145", 
+ "modified": "2019-07-10 13:04:20.953694", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Loan", 
@@ -1149,7 +1184,6 @@
    "set_user_permissions": 0, 
    "share": 0, 
    "submit": 0, 
-   "user_permission_doctypes": "[\"Employee\"]", 
    "write": 0
   }
  ], 
diff --git a/erpnext/hr/doctype/loan/loan.py b/erpnext/hr/doctype/loan/loan.py
index 58c9b8f..a803863 100644
--- a/erpnext/hr/doctype/loan/loan.py
+++ b/erpnext/hr/doctype/loan/loan.py
@@ -6,29 +6,33 @@
 import frappe, math, json
 import erpnext
 from frappe import _
-from frappe.utils import flt, rounded, add_months, nowdate
+from frappe.utils import flt, rounded, add_months, nowdate, getdate
 from erpnext.controllers.accounts_controller import AccountsController
 
 class Loan(AccountsController):
 	def validate(self):
-		check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
+		validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
+		self.set_missing_fields()
+		self.make_repayment_schedule()
+		self.set_repayment_period()
+		self.calculate_totals()
+
+	def set_missing_fields(self):
 		if not self.company:
 			self.company = erpnext.get_default_company()
+
 		if not self.posting_date:
 			self.posting_date = nowdate()
+
 		if self.loan_type and not self.rate_of_interest:
 			self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
+
 		if self.repayment_method == "Repay Over Number of Periods":
 			self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
+
 		if self.status == "Repaid/Closed":
 			self.total_amount_paid = self.total_payment
-		if self.status == 'Disbursed' and self.repayment_start_date < self.disbursement_date:
-			frappe.throw(_("Repayment Start Date cannot be before Disbursement Date."))
 
-		if self.status == "Disbursed":
-			self.make_repayment_schedule()
-			self.set_repayment_period()
-			self.calculate_totals()
 
 	def make_jv_entry(self):
 		self.check_permission('write')
@@ -105,20 +109,31 @@
 	frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
 
 def update_disbursement_status(doc):
-	disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
-		from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""",
-		(doc.payment_account, doc.name), as_dict=1)[0]
-	if disbursement.disbursed_amount == doc.loan_amount:
-		frappe.db.set_value("Loan", doc.name , "status", "Disbursed")
-	if disbursement.disbursed_amount == 0:
-		frappe.db.set_value("Loan", doc.name , "status", "Sanctioned")
-	if disbursement.disbursed_amount > doc.loan_amount:
-		frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
-	if disbursement.disbursed_amount > 0:
-		frappe.db.set_value("Loan", doc.name , "disbursement_date", disbursement.posting_date)
-		frappe.db.set_value("Loan", doc.name , "repayment_start_date", disbursement.posting_date)
+	disbursement = frappe.db.sql("""
+		select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
+		from `tabGL Entry`
+		where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
+	""", (doc.payment_account, doc.name), as_dict=1)[0]
 
-def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
+	disbursement_date = None
+	if not disbursement or disbursement.disbursed_amount == 0:
+		status = "Sanctioned"
+	elif disbursement.disbursed_amount == doc.loan_amount:
+		disbursement_date = disbursement.posting_date
+		status = "Disbursed"
+	elif disbursement.disbursed_amount > doc.loan_amount:
+		frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
+
+	if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")):
+			frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date"))
+
+	frappe.db.sql("""
+		update `tabLoan`
+		set status = %s, disbursement_date = %s
+		where name = %s
+	""", (status, disbursement_date, doc.name))
+
+def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
 	if repayment_method == "Repay Over Number of Periods" and not repayment_periods:
 		frappe.throw(_("Please enter Repayment Periods"))
 
@@ -222,4 +237,4 @@
 		"reference_name": loan,
 		})
 	journal_entry.set("accounts", account_amt_list)
-	return journal_entry.as_dict()
\ No newline at end of file
+	return journal_entry.as_dict()
diff --git a/erpnext/hr/doctype/loan_application/loan_application.js b/erpnext/hr/doctype/loan_application/loan_application.js
index febcbd8..a73b62a 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.js
+++ b/erpnext/hr/doctype/loan_application/loan_application.js
@@ -23,9 +23,8 @@
 	},
 	add_toolbar_buttons: function(frm) {
 		if (frm.doc.status == "Approved") {
-			frm.add_custom_button(__('Loan'), function() {
+			frm.add_custom_button(__('Create Loan'), function() {
 				frappe.call({
-					type: "GET",
 					method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
 					args: {
 						"source_name": frm.doc.name
@@ -37,7 +36,7 @@
 						}
 					}
 				});
-			})
+			}).addClass("btn-primary");
 		}
 	}
 });
diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py
index 706c964..5dbcf15 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.py
+++ b/erpnext/hr/doctype/loan_application/loan_application.py
@@ -9,11 +9,11 @@
 from frappe.model.mapper import get_mapped_doc
 from frappe.model.document import Document
 
-from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, check_repayment_method
+from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method
 
 class LoanApplication(Document):
 	def validate(self):
-		check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
+		validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
 		self.validate_loan_amount()
 		self.get_repayment_details()
 
@@ -29,14 +29,14 @@
 		if self.repayment_method == "Repay Fixed Amount per Period":
 			monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
 			if monthly_interest_rate:
-				self.repayment_periods = math.ceil((math.log(self.repayment_amount) - 
+				self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
 					math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) /
 					(math.log(1 + monthly_interest_rate)))
 			else:
 				self.repayment_periods = self.loan_amount / self.repayment_amount
 
 		self.calculate_payable_amount()
-		
+
 	def calculate_payable_amount(self):
 		balance_amount = self.loan_amount
 		self.total_payable_amount = 0
@@ -47,9 +47,9 @@
 			balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
 
 			self.total_payable_interest += interest_amount
-			
+
 		self.total_payable_amount = self.loan_amount + self.total_payable_interest
-		
+
 @frappe.whitelist()
 def make_loan(source_name, target_doc = None):
 	doclist = get_mapped_doc("Loan Application", source_name, {