Merge branch 'develop' of https://github.com/frappe/erpnext into term_loan_enhancement
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index 5979992..af26f7b 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -240,12 +240,14 @@
    "label": "Repayment Schedule"
   },
   {
+   "allow_on_submit": 1,
    "depends_on": "eval:doc.is_term_loan == 1",
    "fieldname": "repayment_schedule",
    "fieldtype": "Table",
    "label": "Repayment Schedule",
    "no_copy": 1,
-   "options": "Repayment Schedule"
+   "options": "Repayment Schedule",
+   "read_only": 1
   },
   {
    "fieldname": "section_break_17",
@@ -363,6 +365,7 @@
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
+ "naming_rule": "Expression (old style)",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index 0f2c3cf..b9f74df 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -9,7 +9,7 @@
 
 import frappe
 from frappe import _
-from frappe.utils import add_months, flt, getdate, now_datetime, nowdate
+from frappe.utils import add_months, flt, get_last_day, getdate, now_datetime, nowdate
 from six import string_types
 
 import erpnext
@@ -65,7 +65,7 @@
 			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)
+			self.monthly_repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods)
 
 	def check_sanctioned_amount_limit(self):
 		sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
@@ -102,7 +102,7 @@
 				"total_payment": total_payment,
 				"balance_loan_amount": balance_amount
 			})
-			next_payment_date = add_months(payment_date, 1)
+			next_payment_date = add_single_month(payment_date)
 			payment_date = next_payment_date
 
 	def set_repayment_period(self):
@@ -214,7 +214,7 @@
 		if monthly_repayment_amount > loan_amount:
 			frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount"))
 
-def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest, repayment_periods):
+def get_monthly_repayment_amount(loan_amount, rate_of_interest, repayment_periods):
 	if rate_of_interest:
 		monthly_interest_rate = flt(rate_of_interest) / (12 *100)
 		monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate *
@@ -398,3 +398,9 @@
 		"value": len(applicants),
 		"fieldtype": "Int"
 	}
+
+def add_single_month(date):
+	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
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index ec0aebb..c10cf36 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -297,6 +297,27 @@
 		self.assertEqual(amounts[0], 11250.00)
 		self.assertEqual(amounts[1], 78303.00)
 
+	def test_repayment_schedule_update(self):
+		loan = create_loan(self.applicant2, "Personal Loan", 200000, "Repay Over Number of Periods", 4,
+			applicant_type='Customer', repayment_start_date='2021-04-30', posting_date='2021-04-01')
+
+		loan.submit()
+
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date='2021-04-01')
+
+		process_loan_interest_accrual_for_term_loans(posting_date='2021-05-01')
+		process_loan_interest_accrual_for_term_loans(posting_date='2021-06-01')
+
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2021-06-05', 120000)
+		repayment_entry.submit()
+
+		loan.load_from_db()
+
+		self.assertEqual(flt(loan.get('repayment_schedule')[3].principal_amount, 2), 32151.83)
+		self.assertEqual(flt(loan.get('repayment_schedule')[3].interest_amount, 2), 225.06)
+		self.assertEqual(flt(loan.get('repayment_schedule')[3].total_payment, 2), 32376.89)
+		self.assertEqual(flt(loan.get('repayment_schedule')[3].balance_loan_amount, 2), 0)
+
 	def test_security_shortfall(self):
 		pledges = [{
 			"loan_security": "Test Security 2",
@@ -940,18 +961,18 @@
 
 
 def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods,
-	repayment_start_date=None, posting_date=None):
+	applicant_type=None, repayment_start_date=None, posting_date=None):
 
 	loan = frappe.get_doc({
 		"doctype": "Loan",
-		"applicant_type": "Employee",
+		"applicant_type": applicant_type or "Employee",
 		"company": "_Test Company",
 		"applicant": applicant,
 		"loan_type": loan_type,
 		"loan_amount": loan_amount,
 		"repayment_method": repayment_method,
 		"repayment_periods": repayment_periods,
-		"repayment_start_date": nowdate(),
+		"repayment_start_date": repayment_start_date or nowdate(),
 		"is_term_loan": 1,
 		"posting_date": posting_date or nowdate()
 	})
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index ede0467..a28d167 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -83,7 +83,7 @@
 
 		if self.is_term_loan:
 			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)
+				self.repayment_amount = get_monthly_repayment_amount(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)
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 99f0d25..5c398ec 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -178,20 +178,19 @@
 
 @frappe.whitelist()
 def get_disbursal_amount(loan, on_current_security_price=0):
+	from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+		get_pending_principal_amount,
+	)
+
 	loan_details = frappe.get_value("Loan", loan, ["loan_amount", "disbursed_amount", "total_payment",
 		"total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan",
-		"maximum_loan_amount"], as_dict=1)
+		"maximum_loan_amount", "written_off_amount"], as_dict=1)
 
 	if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan,
 		'status': 'Pending'}):
 		return 0
 
-	if loan_details.status == 'Disbursed':
-		pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
-			- flt(loan_details.total_principal_paid)
-	else:
-		pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
-			- flt(loan_details.total_principal_paid)
+	pending_principal_amount = get_pending_principal_amount(loan_details)
 
 	security_value = 0.0
 	if loan_details.is_secured_loan and on_current_security_price:
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index 93513a8..3dd1328 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -76,6 +76,39 @@
 				})
 			)
 
+		if self.payable_principal_amount:
+			gle_map.append(
+				self.get_gl_dict({
+					"account": self.loan_account,
+					"party_type": self.applicant_type,
+					"party": self.applicant,
+					"against": self.interest_income_account,
+					"debit": self.payable_principal_amount,
+					"debit_in_account_currency": self.interest_amount,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.loan,
+					"remarks": _("Interest accrued from {0} to {1} against loan: {2}").format(
+						self.last_accrual_date, self.posting_date, self.loan),
+					"cost_center": erpnext.get_default_cost_center(self.company),
+					"posting_date": self.posting_date
+				})
+			)
+
+			gle_map.append(
+				self.get_gl_dict({
+					"account": self.interest_income_account,
+					"against": self.loan_account,
+					"credit": self.payable_principal_amount,
+					"credit_in_account_currency":  self.interest_amount,
+					"against_voucher_type": "Loan",
+					"against_voucher": self.loan,
+					"remarks": ("Interest accrued from {0} to {1} against loan: {2}").format(
+						self.last_accrual_date, self.posting_date, self.loan),
+					"cost_center": erpnext.get_default_cost_center(self.company),
+					"posting_date": self.posting_date
+				})
+			)
+
 		if gle_map:
 			make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)
 
@@ -84,7 +117,10 @@
 # rate of interest is 13.5 then first loan interest accural will be on '01-10-2019'
 # which means interest will be accrued for 30 days which should be equal to 11095.89
 def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type):
-	from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
+	from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+		calculate_amounts,
+		get_pending_principal_amount,
+	)
 
 	no_of_days = get_no_of_days_for_interest_accural(loan, posting_date)
 	precision = cint(frappe.db.get_default("currency_precision")) or 2
@@ -92,12 +128,7 @@
 	if no_of_days <= 0:
 		return
 
-	if loan.status == 'Disbursed':
-		pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
-			- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
-	else:
-		pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
-			- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
+	pending_principal_amount = get_pending_principal_amount(loan)
 
 	interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date)
 	payable_interest = interest_per_day * no_of_days
@@ -135,7 +166,7 @@
 
 	if not open_loans:
 		open_loans = frappe.get_all("Loan",
-			fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account",
+			fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account", "loan_amount",
 				"is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant",
 				"rate_of_interest", "total_interest_payable", "written_off_amount", "total_principal_paid", "repayment_start_date"],
 			filters=query_filters)
@@ -192,7 +223,8 @@
 			AND l.is_term_loan =1
 			AND rs.payment_date <= %s
 			AND rs.is_accrued=0 {0}
-			AND l.status = 'Disbursed'""".format(condition), (getdate(date)), as_dict=1)
+			AND l.status = 'Disbursed'
+			ORDER BY rs.payment_date""".format(condition), (getdate(date)), as_dict=1)
 
 	return term_loans
 
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 40bb581..962ae5e 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -38,10 +38,12 @@
 
 	def on_submit(self):
 		self.update_paid_amount()
+		self.update_repayment_schedule()
 		self.make_gl_entries()
 
 	def on_cancel(self):
 		self.mark_as_unpaid()
+		self.update_repayment_schedule()
 		self.ignore_linked_doctypes = ['GL Entry']
 		self.make_gl_entries(cancel=1)
 
@@ -124,7 +126,18 @@
 					})
 
 	def update_paid_amount(self):
-		loan = frappe.get_doc("Loan", self.against_loan)
+		loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid',
+			'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'total_interest_payable',
+			'written_off_amount'], as_dict=1)
+
+		loan.update({
+			'total_amount_paid': loan.total_amount_paid + self.amount_paid,
+			'total_principal_paid': loan.total_principal_paid + self.principal_amount_paid
+		})
+
+		pending_principal_amount = get_pending_principal_amount(loan)
+		if not loan.is_secured_loan and pending_principal_amount < 0:
+			loan.update({'status': 'Loan Closure Requested'})
 
 		for payment in self.repayment_details:
 			frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
@@ -133,17 +146,31 @@
 				WHERE name = %s""",
 				(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
 
-		frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
-			WHERE name = %s """, (loan.total_amount_paid + self.amount_paid,
-			loan.total_principal_paid + self.principal_amount_paid, self.against_loan))
+		frappe.db.sql(""" UPDATE `tabLoan`
+			SET total_amount_paid = %s, total_principal_paid = %s, status = %s
+			WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status,
+			self.against_loan))
 
 		update_shortfall_status(self.against_loan, self.principal_amount_paid)
 
 	def mark_as_unpaid(self):
-		loan = frappe.get_doc("Loan", self.against_loan)
+		loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid',
+			'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'total_interest_payable',
+			'written_off_amount'], as_dict=1)
 
 		no_of_repayments = len(self.repayment_details)
 
+		loan.update({
+			'total_amount_paid': loan.total_amount_paid - self.amount_paid,
+			'total_principal_paid': loan.total_principal_paid - self.principal_amount_paid
+		})
+
+		if loan.status == 'Loan Closure Requested':
+			if loan.disbursed_amount >= loan.loan_amount:
+				loan['status'] = 'Disbursed'
+			else:
+				loan['status'] = 'Partially Disbursed'
+
 		for payment in self.repayment_details:
 			frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
 				SET paid_principal_amount = `paid_principal_amount` - %s,
@@ -157,12 +184,13 @@
 				lia_doc = frappe.get_doc('Loan Interest Accrual', payment.loan_interest_accrual)
 				lia_doc.cancel()
 
-		frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
-			WHERE name = %s """, (loan.total_amount_paid - self.amount_paid,
-			loan.total_principal_paid - self.principal_amount_paid, self.against_loan))
+		frappe.db.sql(""" UPDATE `tabLoan`
+			SET total_amount_paid = %s, total_principal_paid = %s, status = %s
+			WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan))
 
-		if loan.status == "Loan Closure Requested":
-			frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
+	def update_repayment_schedule(self):
+		if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount:
+			regenerate_repayment_schedule(self.against_loan)
 
 	def allocate_amounts(self, repayment_details):
 		self.set('repayment_details', [])
@@ -185,50 +213,93 @@
 
 			interest_paid -= self.total_penalty_paid
 
-		total_interest_paid = 0
-		# interest_paid = self.amount_paid - self.principal_amount_paid - self.penalty_amount
+		if self.is_term_loan:
+			interest_paid, updated_entries = self.allocate_interest_amount(interest_paid, repayment_details)
+			self.allocate_principal_amount_for_term_loans(interest_paid, repayment_details, updated_entries)
+		else:
+			interest_paid, updated_entries = self.allocate_interest_amount(interest_paid, repayment_details)
+			self.allocate_excess_payment_for_demand_loans(interest_paid, repayment_details)
+
+	def allocate_interest_amount(self, interest_paid, repayment_details):
+		updated_entries = {}
+		self.total_interest_paid = 0
+		idx = 1
 
 		if interest_paid > 0:
 			for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])):
-				if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid:
+				interest_amount = 0
+				if amounts['interest_amount'] <= interest_paid:
 					interest_amount = amounts['interest_amount']
-					paid_principal = amounts['payable_principal_amount']
-					self.principal_amount_paid += paid_principal
-					interest_paid -= (interest_amount + paid_principal)
+					self.total_interest_paid += interest_amount
+					interest_paid -= interest_amount
 				elif interest_paid:
 					if interest_paid >= amounts['interest_amount']:
 						interest_amount = amounts['interest_amount']
-						paid_principal = interest_paid - interest_amount
-						self.principal_amount_paid += paid_principal
+						self.total_interest_paid += interest_amount
 						interest_paid = 0
 					else:
 						interest_amount = interest_paid
+						self.total_interest_paid += interest_amount
 						interest_paid = 0
-						paid_principal=0
 
-				total_interest_paid += interest_amount
-				self.append('repayment_details', {
-					'loan_interest_accrual': lia,
-					'paid_interest_amount': interest_amount,
-					'paid_principal_amount': paid_principal
-				})
+				if interest_amount:
+					self.append('repayment_details', {
+						'loan_interest_accrual': lia,
+						'paid_interest_amount': interest_amount,
+						'paid_principal_amount': 0
+					})
+					updated_entries[lia] = idx
+					idx += 1
 
+		return interest_paid, updated_entries
+
+	def allocate_principal_amount_for_term_loans(self, interest_paid, repayment_details, updated_entries):
+		if interest_paid > 0:
+			for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])):
+				paid_principal = 0
+				if amounts['payable_principal_amount'] <= interest_paid:
+					paid_principal = amounts['payable_principal_amount']
+					self.principal_amount_paid += paid_principal
+					interest_paid -= paid_principal
+				elif interest_paid:
+					if interest_paid >= amounts['payable_principal_amount']:
+						paid_principal = amounts['payable_principal_amount']
+						self.principal_amount_paid += paid_principal
+						interest_paid = 0
+					else:
+						paid_principal = interest_paid
+						self.principal_amount_paid += paid_principal
+						interest_paid = 0
+
+				if updated_entries.get(lia):
+					idx = updated_entries.get(lia)
+					self.get('repayment_details')[idx-1].paid_principal_amount += paid_principal
+				else:
+					self.append('repayment_details', {
+						'loan_interest_accrual': lia,
+						'paid_interest_amount': 0,
+						'paid_principal_amount': paid_principal
+					})
+
+		if interest_paid > 0:
+			self.principal_amount_paid += interest_paid
+
+	def allocate_excess_payment_for_demand_loans(self, interest_paid, repayment_details):
 		if repayment_details['unaccrued_interest'] and interest_paid > 0:
 			# no of days for which to accrue interest
 			# Interest can only be accrued for an entire day and not partial
 			if interest_paid > repayment_details['unaccrued_interest']:
 				interest_paid -= repayment_details['unaccrued_interest']
-				total_interest_paid += repayment_details['unaccrued_interest']
+				self.total_interest_paid += repayment_details['unaccrued_interest']
 			else:
 				# get no of days for which interest can be paid
 				per_day_interest = get_per_day_interest(self.pending_principal_amount,
 					self.rate_of_interest, self.posting_date)
 
 				no_of_days = cint(interest_paid/per_day_interest)
-				total_interest_paid += no_of_days * per_day_interest
+				self.total_interest_paid += no_of_days * per_day_interest
 				interest_paid -= no_of_days * per_day_interest
 
-		self.total_interest_paid = total_interest_paid
 		if interest_paid > 0:
 			self.principal_amount_paid += interest_paid
 
@@ -364,6 +435,62 @@
 	else:
 		return None, 0
 
+def regenerate_repayment_schedule(loan):
+	from erpnext.loan_management.doctype.loan.loan import (
+		add_single_month,
+		get_monthly_repayment_amount,
+	)
+
+	loan_doc = frappe.get_doc('Loan', loan)
+	next_accrual_date = None
+
+	for term in reversed(loan_doc.get('repayment_schedule')):
+		if not term.is_accrued:
+			next_accrual_date = term.payment_date
+
+		if not term.is_accrued:
+			loan_doc.remove(term)
+
+	loan_doc.save()
+
+	balance_amount = get_pending_principal_amount(loan_doc)
+
+	monthly_repayment_amount = get_monthly_repayment_amount(loan_doc.loan_amount,
+		loan_doc.rate_of_interest, loan_doc.repayment_periods)
+
+	payment_date = next_accrual_date
+
+	while(balance_amount > 0):
+		interest_amount = flt(balance_amount * flt(loan_doc.rate_of_interest) / (12*100))
+		principal_amount = monthly_repayment_amount - interest_amount
+		balance_amount = flt(balance_amount + interest_amount - monthly_repayment_amount)
+		if balance_amount < 0:
+			principal_amount += balance_amount
+			balance_amount = 0.0
+
+		total_payment = principal_amount + interest_amount
+		loan_doc.append("repayment_schedule", {
+			"payment_date": payment_date,
+			"principal_amount": principal_amount,
+			"interest_amount": interest_amount,
+			"total_payment": total_payment,
+			"balance_loan_amount": balance_amount
+		})
+		next_payment_date = add_single_month(payment_date)
+		payment_date = next_payment_date
+
+	loan_doc.save()
+
+def get_pending_principal_amount(loan):
+	if loan.status in ('Disbursed', 'Closed') or loan.disbursed_amount >= loan.loan_amount:
+		pending_principal_amount = flt(loan.total_payment) - flt(loan.total_principal_paid) \
+			- flt(loan.total_interest_payable) - flt(loan.written_off_amount)
+	else:
+		pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_principal_paid) \
+			- flt(loan.total_interest_payable) - flt(loan.written_off_amount)
+
+	return pending_principal_amount
+
 # This function returns the amounts that are payable at the time of loan repayment based on posting date
 # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
 
@@ -411,12 +538,7 @@
 		if due_date and not final_due_date:
 			final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
 
-	if against_loan_doc.status in ('Disbursed', 'Closed') or against_loan_doc.disbursed_amount >= against_loan_doc.loan_amount:
-		pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
-			- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
-	else:
-		pending_principal_amount = against_loan_doc.disbursed_amount - against_loan_doc.total_principal_paid \
-			- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
+	pending_principal_amount = get_pending_principal_amount(against_loan_doc)
 
 	unaccrued_interest = 0
 	if due_date:
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index 0af0de1..cb3ded7 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -30,6 +30,9 @@
 					d.idx, frappe.bold(d.loan_security)))
 
 	def validate_unpledge_qty(self):
+		from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
+			get_pending_principal_amount,
+		)
 		from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import (
 			get_ltv_ratio,
 		)
@@ -46,15 +49,10 @@
 				"valid_upto": (">=", get_datetime())
 			}, as_list=1))
 
-		loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+		loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid', 'loan_amount',
 			'total_interest_payable', 'written_off_amount', 'disbursed_amount', 'status'], as_dict=1)
 
-		if loan_details.status == 'Disbursed':
-			pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
-				- flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
-		else:
-			pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
-				- flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
+		pending_principal_amount = get_pending_principal_amount(loan_details)
 
 		security_value = 0
 		unpledge_qty_map = {}