def set_loan_repayment(self): self.total_loan_repayment = 0 self.total_interest_amount = 0 self.total_principal_amount = 0 if not self.get('loans'): for loan in self.get_loan_details(): amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") if amounts['interest_amount'] or amounts['payable_principal_amount']: self.append('loans', { 'loan': loan.name, 'total_payment': amounts['interest_amount'] + amounts['payable_principal_amount'], 'interest_amount': amounts['interest_amount'], 'principal_amount': amounts['payable_principal_amount'], 'loan_account': loan.loan_account, 'interest_income_account': loan.interest_income_account }) for payment in self.get('loans'): amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") total_amount = amounts['interest_amount'] + amounts['payable_principal_amount'] if payment.total_payment > total_amount: frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2} against loan {3}""").format(payment.idx, frappe.bold(payment.total_payment), frappe.bold(total_amount), frappe.bold(payment.loan))) self.total_interest_amount += payment.interest_amount self.total_principal_amount += payment.principal_amount self.total_loan_repayment += payment.total_payment
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.assertEqual(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.assertEqual(flt(amount, 0), flt(accrued_interest_amount, 0)) self.assertEqual(flt(repayment_entry.penalty_amount, 5), 0) amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEqual(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.assertEqual(flt(amounts["pending_principal_amount"], 0), 0)
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.assertEqual(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.assertEqual(loan.status, "Loan Closure Requested") amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEqual(amounts["pending_principal_amount"], 0.0)
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=get_first_day(nowdate())) 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 += 6 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, 6), "Regular Repayment") repayment_entry = create_repayment_entry( loan.name, self.applicant2, add_days(last_date, 6), "Loan Closure", 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']) loan.load_from_db() self.assertEquals(loan.status, "Loan Closure Requested") amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment") self.assertEquals(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, amounts
def set_loan_repayment(self): self.set('loans', []) self.total_loan_repayment = 0 self.total_interest_amount = 0 self.total_principal_amount = 0 for loan in self.get_loan_details(): amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") total_payment = amounts['interest_amount'] + amounts[ 'payable_principal_amount'] if total_payment: self.append( 'loans', { 'loan': loan.name, 'total_payment': total_payment, 'interest_amount': amounts['interest_amount'], 'principal_amount': amounts['payable_principal_amount'], 'loan_account': loan.loan_account, 'interest_income_account': loan.interest_income_account }) self.total_loan_repayment += total_payment self.total_interest_amount += amounts['interest_amount'] self.total_principal_amount += amounts['payable_principal_amount']
def test_loan_security_unpledge(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.assertEqual(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) repayment_entry = create_repayment_entry( loan.name, self.applicant2, add_days(last_date, 5), flt(loan.loan_amount + accrued_interest_amount), ) repayment_entry.submit() request_loan_closure(loan.name) loan.load_from_db() self.assertEqual(loan.status, "Loan Closure Requested") unpledge_request = unpledge_security(loan=loan.name, save=1) unpledge_request.submit() unpledge_request.status = "Approved" unpledge_request.save() loan.load_from_db() pledged_qty = get_pledged_security_qty(loan.name) self.assertEqual(loan.status, "Closed") self.assertEqual(sum(pledged_qty.values()), 0) amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5)) self.assertEqual(amounts["pending_principal_amount"], 0) self.assertEqual(amounts["payable_principal_amount"], 0.0) self.assertEqual(amounts["interest_amount"], 0)
def make_loan_write_off(loan, company=None, posting_date=None, amount=0, as_dict=0): if not company: company = frappe.get_value("Loan", loan, "company") if not posting_date: posting_date = getdate() amounts = calculate_amounts(loan, posting_date) pending_amount = amounts["pending_principal_amount"] if amount and (amount > pending_amount): frappe.throw(_("Write Off amount cannot be greater than pending loan amount")) if not amount: amount = pending_amount # get default write off account from company master write_off_account = frappe.get_value("Company", company, "write_off_account") write_off = frappe.new_doc("Loan Write Off") write_off.loan = loan write_off.posting_date = posting_date write_off.write_off_account = write_off_account write_off.write_off_amount = amount write_off.save() if as_dict: return write_off.as_dict() else: return write_off
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_loan_security_unpledge(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=get_first_day(nowdate())) 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 += 6 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, 6), "Loan Closure", flt(loan.loan_amount + accrued_interest_amount)) repayment_entry.submit() loan.load_from_db() self.assertEquals(loan.status, "Loan Closure Requested") unpledge_request = unpledge_security(loan=loan.name, save=1) unpledge_request.submit() unpledge_request.status = 'Approved' unpledge_request.save() loan.load_from_db() pledged_qty = get_pledged_security_qty(loan.name) self.assertEqual(loan.status, 'Closed') self.assertEquals(sum(pledged_qty.values()), 0) amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment") self.assertEqual(amounts['pending_principal_amount'], 0) self.assertEqual(amounts['payable_principal_amount'], 0) self.assertEqual(amounts['interest_amount'], 0)
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 no_of_days = get_no_of_days_for_interest_accural(loan, posting_date) precision = cint(frappe.db.get_default("currency_precision")) or 2 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) interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date) payable_interest = interest_per_day * no_of_days pending_amounts = calculate_amounts(loan.name, posting_date, payment_type='Loan Closure') args = frappe._dict({ 'loan': loan.name, 'applicant_type': loan.applicant_type, 'applicant': loan.applicant, 'interest_income_account': loan.interest_income_account, 'loan_account': loan.loan_account, 'pending_principal_amount': pending_principal_amount, 'interest_amount': payable_interest, 'total_pending_interest_amount': pending_amounts['interest_amount'], 'penalty_amount': pending_amounts['penalty_amount'], 'process_loan_interest': process_loan_interest, 'posting_date': posting_date, 'accrual_type': accrual_type }) if flt(payable_interest, precision) > 0.0: make_loan_interest_accrual_entry(args)
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, 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 if no_of_days <= 0: return 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 pending_amounts = calculate_amounts(loan.name, posting_date, payment_type="Loan Closure") args = frappe._dict({ "loan": loan.name, "applicant_type": loan.applicant_type, "applicant": loan.applicant, "interest_income_account": loan.interest_income_account, "loan_account": loan.loan_account, "pending_principal_amount": pending_principal_amount, "interest_amount": payable_interest, "total_pending_interest_amount": pending_amounts["interest_amount"], "penalty_amount": pending_amounts["penalty_amount"], "process_loan_interest": process_loan_interest, "posting_date": posting_date, "accrual_type": accrual_type, }) if flt(payable_interest, precision) > 0.0: make_loan_interest_accrual_entry(args)
def test_penalty_repayment(self): loan, dummy = 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_penalty_repayment(self): loan, dummy = 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.assertEqual(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.assertEqual(amounts["penalty_amount"], 0)
def test_loan_write_off_limit(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 50 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 - 50)) 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), 50) request_loan_closure(loan.name) loan.load_from_db() self.assertEquals(loan.status, "Loan Closure Requested")
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.assertEqual(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, 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 request_loan_closure(loan, posting_date=None): if not posting_date: posting_date = getdate() amounts = calculate_amounts(loan, posting_date) pending_amount = amounts['payable_amount'] + amounts['unaccrued_interest'] loan_type = frappe.get_value('Loan', loan, 'loan_type') write_off_limit = frappe.get_value('Loan Type', loan_type, 'write_off_amount') # checking greater than 0 as there may be some minor precision error if pending_amount < write_off_limit: # update status as loan closure requested frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested') else: frappe.throw(_("Cannot close loan as there is an outstanding of {0}").format(pending_amount))
def request_loan_closure(loan, posting_date=None): if not posting_date: posting_date = getdate() amounts = calculate_amounts(loan, posting_date) pending_amount = ( amounts["pending_principal_amount"] + amounts["unaccrued_interest"] + amounts["interest_amount"] + amounts["penalty_amount"] ) loan_type = frappe.get_value("Loan", loan, "loan_type") write_off_limit = frappe.get_value("Loan Type", loan_type, "write_off_amount") if pending_amount and abs(pending_amount) < write_off_limit: # Auto create loan write off and update status as loan closure requested write_off = make_loan_write_off(loan) write_off.submit() elif pending_amount > 0: frappe.throw(_("Cannot close loan as there is an outstanding of {0}").format(pending_amount)) frappe.db.set_value("Loan", loan, "status", "Loan Closure Requested")