def allocate_amounts(self, repayment_details): precision = cint(frappe.db.get_default("currency_precision")) or 2 self.set('repayment_details', []) self.principal_amount_paid = 0 total_interest_paid = 0 interest_paid = self.amount_paid - self.penalty_amount if self.amount_paid - self.penalty_amount > 0: interest_paid = self.amount_paid - self.penalty_amount for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])): if amounts['interest_amount'] + amounts['payable_principal_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) 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 interest_paid = 0 else: interest_amount = interest_paid 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 repayment_details['unaccrued_interest'] and interest_paid: # 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']: per_day_interest = flt(get_per_day_interest(self.pending_principal_amount, self.rate_of_interest, self.posting_date), precision) interest_paid -= repayment_details['unaccrued_interest'] total_interest_paid += repayment_details['unaccrued_interest'] else: # get no of days for which interest can be paid per_day_interest = flt(get_per_day_interest(self.pending_principal_amount, self.rate_of_interest, self.posting_date), precision) no_of_days = cint(interest_paid/per_day_interest) 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: self.principal_amount_paid += interest_paid
def book_unaccrued_interest(self): precision = cint(frappe.db.get_default("currency_precision")) or 2 if self.total_interest_paid > self.interest_payable: if not self.is_term_loan: # get last loan interest accrual date last_accrual_date = get_last_accrual_date(self.against_loan) # get posting date upto which interest has to be accrued per_day_interest = flt(get_per_day_interest(self.pending_principal_amount, self.rate_of_interest, self.posting_date), 2) no_of_days = flt(flt(self.total_interest_paid - self.interest_payable, precision)/per_day_interest, 0) - 1 posting_date = add_days(last_accrual_date, no_of_days) # book excess interest paid process = process_loan_interest_accrual_for_demand_loans(posting_date=posting_date, loan=self.against_loan, accrual_type="Repayment") # get loan interest accrual to update paid amount lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual': process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1) self.append('repayment_details', { 'loan_interest_accrual': lia.name, 'paid_interest_amount': flt(self.total_interest_paid - self.interest_payable, precision), 'paid_principal_amount': 0.0, 'accrual_type': 'Repayment' })
def book_unaccrued_interest(self): precision = cint(frappe.db.get_default("currency_precision")) or 2 if flt(self.total_interest_paid, precision) > flt( self.interest_payable, precision): if not self.is_term_loan: # get last loan interest accrual date last_accrual_date = get_last_accrual_date(self.against_loan) # get posting date upto which interest has to be accrued per_day_interest = get_per_day_interest( self.pending_principal_amount, self.rate_of_interest, self.posting_date) no_of_days = (flt( flt(self.total_interest_paid - self.interest_payable, precision) / per_day_interest, 0) - 1) posting_date = add_days(last_accrual_date, no_of_days) # book excess interest paid process = process_loan_interest_accrual_for_demand_loans( posting_date=posting_date, loan=self.against_loan, accrual_type="Repayment") # get loan interest accrual to update paid amount lia = frappe.db.get_value( "Loan Interest Accrual", {"process_loan_interest_accrual": process}, ["name", "interest_amount", "payable_principal_amount"], as_dict=1, ) if lia: self.append( "repayment_details", { "loan_interest_accrual": lia.name, "paid_interest_amount": flt( self.total_interest_paid - self.interest_payable, precision), "paid_principal_amount": 0.0, "accrual_type": "Repayment", }, )
def test_loan_topup_with_additional_pledge(self): pledge = [{ "loan_security": "Test Security 1", "qty": 4000.00 }] loan_application = create_loan_application( '_Test Company', self.applicant, 'Demand Loan', pledge) create_pledge(loan_application) loan = create_demand_loan( self.applicant, "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' # Disbursed 10,00,000 amount 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)) previous_interest = amounts['interest_amount'] pledge1 = [{ "loan_security": "Test Security 1", "qty": 2000.00 }] create_loan_security_pledge(self.applicant, pledge1, loan=loan.name) # Topup 500000 make_loan_disbursement_entry( loan.name, 500000, disbursement_date=add_days(last_date, 1)) process_loan_interest_accrual_for_demand_loans( posting_date=add_days(last_date, 15)) amounts = calculate_amounts(loan.name, add_days(last_date, 15)) per_day_interest = get_per_day_interest(1500000, 13.5, '2019-10-30') interest = per_day_interest * 15 self.assertEquals(amounts['pending_principal_amount'], 1500000) self.assertEquals(amounts['interest_amount'], flt(interest + previous_interest, 2))
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"] 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) self.total_interest_paid += no_of_days * per_day_interest interest_paid -= no_of_days * per_day_interest if interest_paid > 0: self.principal_amount_paid += interest_paid
def get_amounts(amounts, against_loan, posting_date): precision = cint(frappe.db.get_default("currency_precision")) or 2 against_loan_doc = frappe.get_doc("Loan", against_loan) loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type) accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name) pending_accrual_entries = {} total_pending_interest = 0 penalty_amount = 0 payable_principal_amount = 0 final_due_date = '' due_date = '' for entry in accrued_interest_entries: # Loan repayment due date is one day after the loan interest is accrued # no of late days are calculated based on loan repayment 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 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) total_pending_interest += entry.interest_amount payable_principal_amount += entry.payable_principal_amount pending_accrual_entries.setdefault(entry.name, { 'interest_amount': flt(entry.interest_amount, precision), 'payable_principal_amount': flt(entry.payable_principal_amount, precision) }) 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 unaccrued_interest = 0 if due_date: pending_days = date_diff(posting_date, due_date) + 1 else: last_accrual_date = get_last_accrual_date(against_loan_doc.name) pending_days = date_diff(posting_date, last_accrual_date) + 1 if pending_days > 0: principal_amount = flt(pending_principal_amount, precision) per_day_interest = get_per_day_interest(principal_amount, loan_type_details.rate_of_interest, posting_date) unaccrued_interest += (pending_days * flt(per_day_interest, precision)) 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["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision) amounts["pending_accrual_entries"] = pending_accrual_entries amounts["unaccrued_interest"] = unaccrued_interest if final_due_date: amounts["due_date"] = final_due_date return amounts
def get_amounts(amounts, against_loan, posting_date): precision = cint(frappe.db.get_default("currency_precision")) or 2 against_loan_doc = frappe.get_doc("Loan", against_loan) loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type) accrued_interest_entries = get_accrued_interest_entries( against_loan_doc.name, posting_date) computed_penalty_date, pending_penalty_amount = get_penalty_details( against_loan) pending_accrual_entries = {} total_pending_interest = 0 penalty_amount = 0 payable_principal_amount = 0 final_due_date = "" due_date = "" for entry in accrued_interest_entries: # Loan repayment due date is one day after the loan interest is accrued # no of late days are calculated based on loan repayment posting date # and if no_of_late days are positive then penalty is levied due_date = add_days(entry.posting_date, 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) total_pending_interest += entry.interest_amount payable_principal_amount += entry.payable_principal_amount pending_accrual_entries.setdefault( entry.name, { "interest_amount": flt(entry.interest_amount, precision), "payable_principal_amount": flt(entry.payable_principal_amount, precision), }, ) if due_date and not final_due_date: final_due_date = add_days(due_date, loan_type_details.grace_period_in_days) pending_principal_amount = get_pending_principal_amount(against_loan_doc) unaccrued_interest = 0 if due_date: pending_days = date_diff(posting_date, due_date) + 1 else: last_accrual_date = get_last_accrual_date(against_loan_doc.name) pending_days = date_diff(posting_date, last_accrual_date) + 1 if pending_days > 0: principal_amount = flt(pending_principal_amount, precision) per_day_interest = get_per_day_interest( principal_amount, loan_type_details.rate_of_interest, posting_date) unaccrued_interest += pending_days * per_day_interest 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 + 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) if final_due_date: amounts["due_date"] = final_due_date return amounts