Merge pull request #29460 from deepeshgarg007/loan_refund_jv

feat: Refund entry against loans
diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js
index f9c201a..940a1bb 100644
--- a/erpnext/loan_management/doctype/loan/loan.js
+++ b/erpnext/loan_management/doctype/loan/loan.js
@@ -46,7 +46,7 @@
 			});
 		});
 
-		$.each(["payment_account", "loan_account"], function (i, field) {
+		$.each(["payment_account", "loan_account", "disbursement_account"], function (i, field) {
 			frm.set_query(field, function () {
 				return {
 					"filters": {
@@ -88,6 +88,10 @@
 				frm.add_custom_button(__('Loan Write Off'), function() {
 					frm.trigger("make_loan_write_off_entry");
 				},__('Create'));
+
+				frm.add_custom_button(__('Loan Refund'), function() {
+					frm.trigger("make_loan_refund");
+				},__('Create'));
 			}
 		}
 		frm.trigger("toggle_fields");
@@ -155,6 +159,21 @@
 		})
 	},
 
+	make_loan_refund: function(frm) {
+		frappe.call({
+			args: {
+				"loan": frm.doc.name
+			},
+			method: "erpnext.loan_management.doctype.loan.loan.make_refund_jv",
+			callback: function (r) {
+				if (r.message) {
+					let doc = frappe.model.sync(r.message)[0];
+					frappe.set_route("Form", doc.doctype, doc.name);
+				}
+			}
+		})
+	},
+
 	request_loan_closure: function(frm) {
 		frappe.confirm(__("Do you really want to close this loan"),
 			function() {
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index af26f7b..196f36f 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -2,7 +2,7 @@
  "actions": [],
  "allow_import": 1,
  "autoname": "ACC-LOAN-.YYYY.-.#####",
- "creation": "2019-08-29 17:29:18.176786",
+ "creation": "2022-01-25 10:30:02.294967",
  "doctype": "DocType",
  "document_type": "Document",
  "editable_grid": 1,
@@ -34,6 +34,7 @@
   "is_term_loan",
   "account_info",
   "mode_of_payment",
+  "disbursement_account",
   "payment_account",
   "column_break_9",
   "loan_account",
@@ -356,12 +357,21 @@
    "fieldtype": "Date",
    "label": "Closure Date",
    "read_only": 1
+  },
+  {
+   "fetch_from": "loan_type.disbursement_account",
+   "fieldname": "disbursement_account",
+   "fieldtype": "Link",
+   "label": "Disbursement Account",
+   "options": "Account",
+   "read_only": 1,
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-10-12 18:10:32.360818",
+ "modified": "2022-01-25 16:29:16.325501",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
@@ -391,5 +401,6 @@
  "search_fields": "posting_date",
  "sort_field": "creation",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index f660a24..b798e08 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -10,6 +10,7 @@
 from frappe.utils import add_months, flt, get_last_day, getdate, now_datetime, nowdate
 
 import erpnext
+from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry
 from erpnext.controllers.accounts_controller import AccountsController
 from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
 from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import (
@@ -233,17 +234,15 @@
 	loan_type = frappe.get_value('Loan', loan, 'loan_type')
 	write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount')
 
-	# checking greater than 0 as there may be some minor precision error
-	if not pending_amount:
-		frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
-	elif pending_amount < write_off_limit:
+	if pending_amount and abs(pending_amount) < write_off_limit:
 		# Auto create loan write off and update status as loan closure requested
 		write_off = make_loan_write_off(loan)
 		write_off.submit()
-		frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
-	else:
+	elif pending_amount > 0:
 		frappe.throw(_("Cannot close loan as there is an outstanding of {0}").format(pending_amount))
 
+	frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
+
 @frappe.whitelist()
 def get_loan_application(loan_application):
 	loan = frappe.get_doc("Loan Application", loan_application)
@@ -400,4 +399,39 @@
 	if getdate(date) == get_last_day(date):
 		return get_last_day(add_months(date, 1))
 	else:
-		return add_months(date, 1)
\ No newline at end of file
+		return add_months(date, 1)
+
+@frappe.whitelist()
+def make_refund_jv(loan, amount=0, reference_number=None, reference_date=None, submit=0):
+	loan_details = frappe.db.get_value('Loan', loan, ['applicant_type', 'applicant',
+		'loan_account', 'payment_account', 'posting_date', 'company', 'name',
+		'total_payment', 'total_principal_paid'], as_dict=1)
+
+	loan_details.doctype = 'Loan'
+	loan_details[loan_details.applicant_type.lower()] = loan_details.applicant
+
+	if not amount:
+		amount = flt(loan_details.total_principal_paid - loan_details.total_payment)
+
+		if amount < 0:
+			frappe.throw(_('No excess amount pending for refund'))
+
+	refund_jv = get_payment_entry(loan_details, {
+		"party_type": loan_details.applicant_type,
+		"party_account": loan_details.loan_account,
+		"amount_field_party": 'debit_in_account_currency',
+		"amount_field_bank": 'credit_in_account_currency',
+		"amount": amount,
+		"bank_account": loan_details.payment_account
+	})
+
+	if reference_number:
+		refund_jv.cheque_no = reference_number
+
+	if reference_date:
+		refund_jv.cheque_date = reference_date
+
+	if submit:
+		refund_jv.submit()
+
+	return refund_jv
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 1676c21..6415689 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -42,16 +42,17 @@
 		create_loan_type("Personal Loan", 500000, 8.4,
 			is_term_loan=1,
 			mode_of_payment='Cash',
+			disbursement_account='Disbursement Account - _TC',
 			payment_account='Payment Account - _TC',
 			loan_account='Loan Account - _TC',
 			interest_income_account='Interest Income Account - _TC',
 			penalty_income_account='Penalty Income Account - _TC')
 
-		create_loan_type("Stock Loan", 2000000, 13.5, 25, 1, 5, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
-			'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type("Stock Loan", 2000000, 13.5, 25, 1, 5, 'Cash', 'Disbursement Account - _TC',
+			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
 
-		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
-			'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Disbursement Account - _TC',
+			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
 
 		create_loan_security_type()
 		create_loan_security()
@@ -790,6 +791,18 @@
 			"account_type": "Bank",
 		}).insert(ignore_permissions=True)
 
+	if not frappe.db.exists("Account", "Disbursement Account - _TC"):
+		frappe.get_doc({
+			"doctype": "Account",
+			"company": "_Test Company",
+			"account_name": "Disbursement Account",
+			"root_type": "Asset",
+			"report_type": "Balance Sheet",
+			"currency": "INR",
+			"parent_account": "Bank Accounts - _TC",
+			"account_type": "Bank",
+		}).insert(ignore_permissions=True)
+
 	if not frappe.db.exists("Account", "Interest Income Account - _TC"):
 		frappe.get_doc({
 			"doctype": "Account",
@@ -815,7 +828,7 @@
 		}).insert(ignore_permissions=True)
 
 def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest, penalty_interest_rate=None, is_term_loan=None, grace_period_in_days=None,
-	mode_of_payment=None, payment_account=None, loan_account=None, interest_income_account=None, penalty_income_account=None,
+	mode_of_payment=None, disbursement_account=None, payment_account=None, loan_account=None, interest_income_account=None, penalty_income_account=None,
 	repayment_method=None, repayment_periods=None):
 
 	if not frappe.db.exists("Loan Type", loan_name):
@@ -829,6 +842,7 @@
 			"penalty_interest_rate": penalty_interest_rate,
 			"grace_period_in_days": grace_period_in_days,
 			"mode_of_payment": mode_of_payment,
+			"disbursement_account": disbursement_account,
 			"payment_account": payment_account,
 			"loan_account": loan_account,
 			"interest_income_account": interest_income_account,
diff --git a/erpnext/loan_management/doctype/loan_application/test_loan_application.py b/erpnext/loan_management/doctype/loan_application/test_loan_application.py
index d367e92..640709c 100644
--- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py
@@ -15,7 +15,7 @@
 class TestLoanApplication(unittest.TestCase):
 	def setUp(self):
 		create_loan_accounts()
-		create_loan_type("Home Loan", 500000, 9.2, 0, 1, 0, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
+		create_loan_type("Home Loan", 500000, 9.2, 0, 1, 0, 'Cash', 'Disbursement Account - _TC', 'Payment Account - _TC', 'Loan Account - _TC',
 			'Interest Income Account - _TC', 'Penalty Income Account - _TC', 'Repay Over Number of Periods', 18)
 		self.applicant = make_employee("kate_loan@loan.com", "_Test Company")
 		make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant, currency='INR')
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index e2d758b..df3aadf 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -122,7 +122,7 @@
 		gle_map.append(
 			self.get_gl_dict({
 				"account": loan_details.loan_account,
-				"against": loan_details.payment_account,
+				"against": loan_details.disbursement_account,
 				"debit": self.disbursed_amount,
 				"debit_in_account_currency": self.disbursed_amount,
 				"against_voucher_type": "Loan",
@@ -137,7 +137,7 @@
 
 		gle_map.append(
 			self.get_gl_dict({
-				"account": loan_details.payment_account,
+				"account": loan_details.disbursement_account,
 				"against": loan_details.loan_account,
 				"credit": self.disbursed_amount,
 				"credit_in_account_currency": self.disbursed_amount,
diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
index 94ec84e..10be750 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -44,8 +44,8 @@
 	def setUp(self):
 		create_loan_accounts()
 
-		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
-			'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Disbursement Account - _TC',
+			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
 
 		create_loan_security_type()
 		create_loan_security()
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
index 46aaaad..e8c7750 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
@@ -30,8 +30,8 @@
 	def setUp(self):
 		create_loan_accounts()
 
-		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
-			'Interest Income Account - _TC', 'Penalty Income Account - _TC')
+		create_loan_type("Demand Loan", 2000000, 13.5, 25, 0, 5, 'Cash', 'Disbursement Account - _TC',
+			'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC')
 
 		create_loan_security_type()
 		create_loan_security()
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.js b/erpnext/loan_management/doctype/loan_type/loan_type.js
index 04c89c4..9f9137c 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.js
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.js
@@ -15,7 +15,7 @@
 			});
 		});
 
-		$.each(["payment_account", "loan_account"], function (i, field) {
+		$.each(["payment_account", "loan_account", "disbursement_account"], function (i, field) {
 			frm.set_query(field, function () {
 				return {
 					"filters": {
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json
index c0a5d2c..00337e4 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.json
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.json
@@ -19,9 +19,10 @@
   "description",
   "account_details_section",
   "mode_of_payment",
+  "disbursement_account",
   "payment_account",
-  "loan_account",
   "column_break_12",
+  "loan_account",
   "interest_income_account",
   "penalty_income_account",
   "amended_from"
@@ -79,7 +80,7 @@
   {
    "fieldname": "payment_account",
    "fieldtype": "Link",
-   "label": "Payment Account",
+   "label": "Repayment Account",
    "options": "Account",
    "reqd": 1
   },
@@ -149,15 +150,23 @@
    "fieldtype": "Currency",
    "label": "Auto Write Off Amount ",
    "options": "Company:company:default_currency"
+  },
+  {
+   "fieldname": "disbursement_account",
+   "fieldtype": "Link",
+   "label": "Disbursement Account",
+   "options": "Account",
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-19 18:10:57.368490",
+ "modified": "2022-01-25 16:23:57.009349",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Type",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -181,5 +190,6 @@
   }
  ],
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index eace7ca..feafecb 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -349,3 +349,4 @@
 erpnext.patches.v14_0.migrate_cost_center_allocations
 erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template
 erpnext.patches.v13_0.shopping_cart_to_ecommerce
+erpnext.patches.v13_0.update_disbursement_account
diff --git a/erpnext/patches/v13_0/update_disbursement_account.py b/erpnext/patches/v13_0/update_disbursement_account.py
new file mode 100644
index 0000000..c56fa8f
--- /dev/null
+++ b/erpnext/patches/v13_0/update_disbursement_account.py
@@ -0,0 +1,22 @@
+import frappe
+
+
+def execute():
+
+	frappe.reload_doc("loan_management", "doctype", "loan_type")
+	frappe.reload_doc("loan_management", "doctype", "loan")
+
+	loan_type = frappe.qb.DocType("Loan Type")
+	loan = frappe.qb.DocType("Loan")
+
+	frappe.qb.update(
+		loan_type
+	).set(
+		loan_type.disbursement_account, loan_type.payment_account
+	).run()
+
+	frappe.qb.update(
+		loan
+	).set(
+		loan.disbursement_account, loan.payment_account
+	).run()
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index 4f097fa..5f836db 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -214,6 +214,7 @@
 			create_loan_type("Car Loan", 500000, 8.4,
 				is_term_loan=1,
 				mode_of_payment='Cash',
+				disbursement_account='Disbursement Account - _TC',
 				payment_account='Payment Account - _TC',
 				loan_account='Loan Account - _TC',
 				interest_income_account='Interest Income Account - _TC',
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 597fd5a..30b604b 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -370,6 +370,7 @@
 		create_loan_type("Car Loan", 500000, 8.4,
 			is_term_loan=1,
 			mode_of_payment='Cash',
+			disbursement_account='Disbursement Account - _TC',
 			payment_account='Payment Account - _TC',
 			loan_account='Loan Account - _TC',
 			interest_income_account='Interest Income Account - _TC',