fix: Unsecured loan status update
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 6d9d4f4..d072422 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 4cd4c75..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
@@ -117,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
@@ -125,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
@@ -168,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)
@@ -225,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 53ff43a..9f13ee6 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -126,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`
@@ -135,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,
@@ -159,12 +184,9 @@
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))
-
- if loan.status == "Loan Closure Requested":
- frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
+ 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))
def update_repayment_schedule(self):
if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount:
@@ -431,12 +453,7 @@
loan_doc.save()
- if loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
- balance_amount = loan_doc.total_payment - loan_doc.total_principal_paid \
- - loan_doc.total_interest_payable - loan_doc.written_off_amount
- else:
- balance_amount = loan_doc.disbursed_amount - loan_doc.total_principal_paid \
- - loan_doc.total_interest_payable - loan_doc.written_off_amount
+ 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)
@@ -464,6 +481,16 @@
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
@@ -511,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', 'Loan Closure Requested', 'Closed'):
- 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 = {}