def test_pending_loan_amount_after_closure_request(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' no_of_days = date_diff(last_date, first_date) + 1 no_of_days += 5 accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime(first_date).year) * 100) 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, 5)) repayment_entry = create_repayment_entry( loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount)) repayment_entry.submit() amounts = frappe.db.get_value( 'Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount', 'paid_principal_amount']) request_loan_closure(loan.name) loan.load_from_db() self.assertEquals(loan.status, "Loan Closure Requested") amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertTrue(amounts['pending_principal_amount'] < 0.0)
def test_partial_unaccrued_interest_payment(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' no_of_days = date_diff(last_date, first_date) + 1 no_of_days += 5.5 # get partial unaccrued interest amount paid_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime(first_date).year) * 100) 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, 5)) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), paid_amount) repayment_entry.submit() repayment_entry.load_from_db() partial_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 5) \ / (days_in_year(get_datetime(first_date).year) * 100) interest_amount = flt( amounts['interest_amount'] + partial_accrued_interest_amount, 2) 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() # 30 days - grace period penalty_days = 30 - 4 penalty_applicable_amount = flt(amounts['interest_amount'] / 2) penalty_amount = flt( (((penalty_applicable_amount * 25) / 100) * penalty_days), 2) process = process_loan_interest_accrual_for_demand_loans( posting_date='2019-11-30') calculated_penalty_amount = frappe.db.get_value( 'Loan Interest Accrual', { 'process_loan_interest_accrual': process, 'loan': loan.name }, 'penalty_amount') self.assertEquals(calculated_penalty_amount, penalty_amount)
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 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 test_partial_loan_security_unpledge(self): pledge = [{ "loan_security": "Test Security 1", "qty": 2000.00 }, { "loan_security": "Test Security 2", "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) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), 600000) repayment_entry.submit() unpledge_map = {'Test Security 2': 2000} unpledge_request = unpledge_security(loan=loan.name, security_map=unpledge_map, save=1) unpledge_request.submit() unpledge_request.status = 'Approved' unpledge_request.save() unpledge_request.submit() unpledge_request.load_from_db() self.assertEqual(unpledge_request.docstatus, 1)
def test_loan_amount_write_off(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' no_of_days = date_diff(last_date, first_date) + 1 no_of_days += 5 accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime(first_date).year) * 100) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) process_loan_interest_accrual_for_demand_loans(posting_date = last_date) # repay 100 less so that it can be automatically written off repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount - 100)) repayment_entry.submit() amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)']) self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0)) self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEquals(flt(amounts['pending_principal_amount'], 0), 100) we = make_loan_write_off(loan.name, amount=amounts['pending_principal_amount']) we.submit() amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEquals(flt(amounts['pending_principal_amount'], 0), 0)
def test_loan_closure(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' no_of_days = date_diff(last_date, first_date) + 1 # Adding 5 since repayment is made 5 days late after due date # and since payment type is loan closure so interest should be considered for those # 5 days as well though in grace period no_of_days += 5 accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime(first_date).year) * 100) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) process_loan_interest_accrual_for_demand_loans(posting_date = last_date) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount)) repayment_entry.submit() amount = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['sum(paid_interest_amount)']) self.assertEquals(flt(amount, 0),flt(accrued_interest_amount, 0)) self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0) request_loan_closure(loan.name) loan.load_from_db() self.assertEquals(loan.status, "Loan Closure Requested")
def set_status_and_amounts(self, cancel=0): loan_details = frappe.get_all("Loan", fields=[ "loan_amount", "disbursed_amount", "total_principal_paid", "status", "is_term_loan" ], filters={"name": self.against_loan})[0] if loan_details.status == "Disbursed" and not loan_details.is_term_loan: process_loan_interest_accrual_for_demand_loans( posting_date=add_days(self.disbursement_date, -1), loan=self.against_loan) if cancel: disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount if disbursed_amount == 0: status = "Sanctioned" elif disbursed_amount >= loan_details.disbursed_amount: status = "Disbursed" else: status = "Partially Disbursed" else: disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount if flt(disbursed_amount) - flt( loan_details.total_principal_paid) > flt( loan_details.loan_amount): frappe.throw( _("Disbursed Amount cannot be greater than loan amount")) if flt(disbursed_amount) >= loan_details.disbursed_amount: status = "Disbursed" else: status = "Partially Disbursed" frappe.db.set_value( "Loan", self.against_loan, { "disbursement_date": self.disbursement_date, "disbursed_amount": disbursed_amount, "status": status })
def test_regular_loan_repayment(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' no_of_days = date_diff(last_date, first_date) + 1 accrued_interest_amount = flt((loan.loan_amount * loan.rate_of_interest * no_of_days) / (days_in_year(get_datetime(first_date).year) * 100), 2) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) process_loan_interest_accrual_for_demand_loans(posting_date = last_date) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), 111119) repayment_entry.save() repayment_entry.submit() penalty_amount = (accrued_interest_amount * 5 * 25) / 100 self.assertEquals(flt(repayment_entry.penalty_amount,0), flt(penalty_amount, 0)) amounts = frappe.db.get_all('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount']) loan.load_from_db() total_interest_paid = amounts[0]['paid_interest_amount'] + amounts[1]['paid_interest_amount'] self.assertEquals(amounts[1]['paid_interest_amount'], repayment_entry.interest_payable) self.assertEquals(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid - penalty_amount - total_interest_paid, 0))
def test_loan_topup(self): pledges = [] pledges.append({ "loan_security": "Test Security 1", "qty": 4000.00, "haircut": 50, "loan_security_price": 500.00 }) loan_security_pledge = create_loan_security_pledge(self.applicant, pledges) loan = create_demand_loan(self.applicant, "Demand Loan", loan_security_pledge.name, posting_date=get_first_day(nowdate())) loan.submit() first_date = get_first_day(nowdate()) last_date = get_last_day(nowdate()) no_of_days = date_diff(last_date, first_date) + 1 accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime().year) * 100) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) process_loan_interest_accrual_for_demand_loans(posting_date=add_days(last_date, 1)) # Should not be able to create loan disbursement entry before repayment self.assertRaises(frappe.ValidationError, make_loan_disbursement_entry, loan.name, 500000, first_date) repayment_entry = create_repayment_entry(loan.name, self.applicant, add_days(get_last_day(nowdate()), 5), "Regular Payment", 611095.89) repayment_entry.submit() loan.reload() # After repayment loan disbursement entry should go through make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 16))
def test_accumulated_amounts(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=get_first_day(nowdate())) loan.submit() first_date = '2019-10-01' last_date = '2019-10-30' no_of_days = date_diff(last_date, first_date) + 1 accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime(first_date).year) * 100) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) process_loan_interest_accrual_for_demand_loans(posting_date=last_date) loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) self.assertEquals(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0)) next_start_date = '2019-10-31' next_end_date = '2019-11-29' no_of_days = date_diff(next_end_date, next_start_date) + 1 process = process_loan_interest_accrual_for_demand_loans(posting_date=next_end_date) new_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \ / (days_in_year(get_datetime(first_date).year) * 100) total_pending_interest_amount = flt(accrued_interest_amount + new_accrued_interest_amount, 0) loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name, 'process_loan_interest_accrual': process}) self.assertEquals(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount)
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_penalty(self): loan, amounts = create_loan_scenario_for_penalty(self) # 30 days - grace period penalty_days = 30 - 4 penalty_applicable_amount = flt(amounts['interest_amount'] / 2) penalty_amount = flt( (((penalty_applicable_amount * 25) / 100) * penalty_days), 2) process = process_loan_interest_accrual_for_demand_loans( posting_date='2019-11-30') 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 book_unaccrued_interest(self): if self.payment_type == 'Loan Closure': total_interest_paid = 0 for payment in self.repayment_details: total_interest_paid += payment.paid_interest_amount if total_interest_paid < self.interest_payable: if not self.is_term_loan: process = process_loan_interest_accrual_for_demand_loans(posting_date=self.posting_date, loan=self.against_loan) 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': lia.interest_amount, 'paid_principal_amount': lia.payable_principal_amount })
def test_penalty(self): loan, amounts = create_loan_scenario_for_penalty(self) # 30 days - grace period penalty_days = 30 - 4 penalty_applicable_amount = flt(amounts["interest_amount"] / 2) penalty_amount = flt( (((penalty_applicable_amount * 25) / 100) * penalty_days), 2) process = process_loan_interest_accrual_for_demand_loans( posting_date="2019-11-30") calculated_penalty_amount = frappe.db.get_value( "Loan Interest Accrual", { "process_loan_interest_accrual": process, "loan": loan.name }, "penalty_amount", ) self.assertEqual(loan.loan_amount, 1000000) self.assertEqual(calculated_penalty_amount, penalty_amount)
def set_status_and_amounts(self, cancel=0): loan_details = frappe.get_all("Loan", fields=[ "loan_amount", "disbursed_amount", "total_payment", "total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan" ], filters={"name": self.against_loan})[0] if cancel: disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount total_payment = loan_details.total_payment if loan_details.disbursed_amount > loan_details.loan_amount: topup_amount = loan_details.disbursed_amount - loan_details.loan_amount if topup_amount > self.disbursed_amount: topup_amount = self.disbursed_amount total_payment = total_payment - topup_amount if disbursed_amount == 0: status = "Sanctioned" elif disbursed_amount >= loan_details.loan_amount: status = "Disbursed" else: status = "Partially Disbursed" else: disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount total_payment = loan_details.total_payment if disbursed_amount > loan_details.loan_amount and loan_details.is_term_loan: frappe.throw( _("Disbursed Amount cannot be greater than loan amount")) 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 = loan_details.disbursed_amount security_value = 0.0 if loan_details.is_secured_loan: security_value = get_total_pledged_security_value( self.against_loan) if not security_value: security_value = loan_details.loan_amount if pending_principal_amount + self.disbursed_amount > flt( security_value): allowed_amount = security_value - pending_principal_amount if allowed_amount < 0: allowed_amount = 0 frappe.throw( _("Disbursed Amount cannot be greater than {0}").format( allowed_amount)) if loan_details.status == "Disbursed" and not loan_details.is_term_loan: process_loan_interest_accrual_for_demand_loans( posting_date=add_days(self.disbursement_date, -1), loan=self.against_loan) if disbursed_amount > loan_details.loan_amount: topup_amount = disbursed_amount - loan_details.loan_amount if topup_amount < 0: topup_amount = 0 if topup_amount > self.disbursed_amount: topup_amount = self.disbursed_amount total_payment = total_payment + topup_amount if flt(disbursed_amount) >= loan_details.loan_amount: status = "Disbursed" else: status = "Partially Disbursed" frappe.db.set_value( "Loan", self.against_loan, { "disbursement_date": self.disbursement_date, "disbursed_amount": disbursed_amount, "status": status, "total_payment": total_payment })