fix: Consider paid repayment entries in subsequent loan repayments
This commit is contained in:
parent
d77ea7c88b
commit
37d6c19a34
@ -523,33 +523,7 @@ class TestLoan(unittest.TestCase):
|
||||
self.assertEqual(flt(repayment_entry.total_interest_paid, 0), flt(interest_amount, 0))
|
||||
|
||||
def test_penalty(self):
|
||||
pledge = [{
|
||||
"loan_security": "Test Security 1",
|
||||
"qty": 4000.00
|
||||
}]
|
||||
|
||||
loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
|
||||
create_pledge(loan_application)
|
||||
|
||||
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
|
||||
loan.submit()
|
||||
|
||||
self.assertEquals(loan.loan_amount, 1000000)
|
||||
|
||||
first_date = '2019-10-01'
|
||||
last_date = '2019-10-30'
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
|
||||
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
|
||||
|
||||
amounts = calculate_amounts(loan.name, add_days(last_date, 1))
|
||||
paid_amount = amounts['interest_amount']/2
|
||||
|
||||
repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
|
||||
paid_amount)
|
||||
|
||||
repayment_entry.submit()
|
||||
|
||||
loan = create_loan_scenario_for_penalty(self)
|
||||
# 30 days - grace period
|
||||
penalty_days = 30 - 4
|
||||
penalty_applicable_amount = flt(amounts['interest_amount']/2)
|
||||
@ -559,8 +533,28 @@ class TestLoan(unittest.TestCase):
|
||||
calculated_penalty_amount = frappe.db.get_value('Loan Interest Accrual',
|
||||
{'process_loan_interest_accrual': process, 'loan': loan.name}, 'penalty_amount')
|
||||
|
||||
self.assertEquals(loan.loan_amount, 1000000)
|
||||
self.assertEquals(calculated_penalty_amount, penalty_amount)
|
||||
|
||||
def test_penalty_repayment(self):
|
||||
loan = create_loan_scenario_for_penalty(self)
|
||||
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:00')
|
||||
|
||||
first_penalty = 10000
|
||||
second_penalty = amounts['penalty_amount'] - 10000
|
||||
|
||||
repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:00', 10000)
|
||||
repayment_entry.submit()
|
||||
|
||||
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:01')
|
||||
self.assertEquals(amounts['penalty_amount'], second_penalty)
|
||||
|
||||
repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2019-11-30 00:00:01', second_penalty)
|
||||
repayment_entry.submit()
|
||||
|
||||
amounts = calculate_amounts(loan.name, '2019-11-30 00:00:02')
|
||||
self.assertEquals(amounts['penalty_amount'], 0)
|
||||
|
||||
def test_loan_write_off_limit(self):
|
||||
pledge = [{
|
||||
"loan_security": "Test Security 1",
|
||||
@ -651,6 +645,32 @@ class TestLoan(unittest.TestCase):
|
||||
amounts = calculate_amounts(loan.name, add_days(last_date, 5))
|
||||
self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
|
||||
|
||||
def create_loan_scenario_for_penalty(doc):
|
||||
pledge = [{
|
||||
"loan_security": "Test Security 1",
|
||||
"qty": 4000.00
|
||||
}]
|
||||
|
||||
loan_application = create_loan_application('_Test Company', doc.applicant2, 'Demand Loan', pledge)
|
||||
create_pledge(loan_application)
|
||||
loan = create_demand_loan(doc.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
|
||||
loan.submit()
|
||||
|
||||
first_date = '2019-10-01'
|
||||
last_date = '2019-10-30'
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
|
||||
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
|
||||
|
||||
amounts = calculate_amounts(loan.name, add_days(last_date, 1))
|
||||
paid_amount = amounts['interest_amount']/2
|
||||
|
||||
repayment_entry = create_repayment_entry(loan.name, doc.applicant2, add_days(last_date, 5),
|
||||
paid_amount)
|
||||
|
||||
repayment_entry.submit()
|
||||
|
||||
return loan
|
||||
|
||||
def create_loan_accounts():
|
||||
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
|
||||
|
@ -75,10 +75,6 @@ class LoanRepayment(AccountsController):
|
||||
if not self.amount_paid:
|
||||
frappe.throw(_("Amount paid cannot be zero"))
|
||||
|
||||
if not self.shortfall_amount and self.amount_paid < self.penalty_amount:
|
||||
msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
|
||||
frappe.throw(msg)
|
||||
|
||||
def book_unaccrued_interest(self):
|
||||
precision = cint(frappe.db.get_default("currency_precision")) or 2
|
||||
if self.total_interest_paid > self.interest_payable:
|
||||
@ -327,6 +323,18 @@ def get_accrued_interest_entries(against_loan):
|
||||
|
||||
return unpaid_accrued_entries
|
||||
|
||||
def get_penalty_details(against_loan):
|
||||
penalty_details = frappe.db.sql("""
|
||||
SELECT posting_date, (penalty_amount - total_penalty_paid) as pending_penalty_amount
|
||||
FROM `tabLoan Repayment` where posting_date >= (SELECT MAX(posting_date) from `tabLoan Repayment`
|
||||
where against_loan = %s) and docstatus = 1 and against_loan = %s
|
||||
""", (against_loan, against_loan))
|
||||
|
||||
if penalty_details:
|
||||
return penalty_details[0][0], flt(penalty_details[0][1])
|
||||
else:
|
||||
return None, 0
|
||||
|
||||
# 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
|
||||
|
||||
@ -337,6 +345,7 @@ def get_amounts(amounts, against_loan, posting_date):
|
||||
loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
|
||||
accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name)
|
||||
|
||||
computed_penalty_date, pending_penalty_amount = get_penalty_details(against_loan)
|
||||
pending_accrual_entries = {}
|
||||
|
||||
total_pending_interest = 0
|
||||
@ -351,8 +360,13 @@ def get_amounts(amounts, against_loan, posting_date):
|
||||
# and if no_of_late days are positive then penalty is levied
|
||||
|
||||
due_date = add_days(entry.posting_date, 1)
|
||||
no_of_late_days = date_diff(posting_date,
|
||||
add_days(due_date, loan_type_details.grace_period_in_days)) + 1
|
||||
due_date_after_grace_period = add_days(due_date, loan_type_details.grace_period_in_days)
|
||||
|
||||
# Consider one day after already calculated penalty
|
||||
if computed_penalty_date and getdate(computed_penalty_date) >= due_date_after_grace_period:
|
||||
due_date_after_grace_period = add_days(computed_penalty_date, 1)
|
||||
|
||||
no_of_late_days = date_diff(posting_date, due_date_after_grace_period) + 1
|
||||
|
||||
if no_of_late_days > 0 and (not against_loan_doc.repay_from_salary) and entry.accrual_type == 'Regular':
|
||||
penalty_amount += (entry.interest_amount * (loan_type_details.penalty_interest_rate / 100) * no_of_late_days)
|
||||
@ -390,7 +404,7 @@ def get_amounts(amounts, against_loan, posting_date):
|
||||
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
|
||||
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
|
||||
amounts["interest_amount"] = flt(total_pending_interest, precision)
|
||||
amounts["penalty_amount"] = flt(penalty_amount, precision)
|
||||
amounts["penalty_amount"] = flt(penalty_amount + pending_penalty_amount, precision)
|
||||
amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
|
||||
amounts["pending_accrual_entries"] = pending_accrual_entries
|
||||
amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
|
||||
|
Loading…
x
Reference in New Issue
Block a user