Skip to content

Commit

Permalink
Merge pull request #26106 from deepeshgarg007/loan_repay_via_salary
Browse files Browse the repository at this point in the history
fix: Sanctioned loan amount limit check
  • Loading branch information
deepeshgarg007 committed Jun 18, 2021
2 parents 4e88dcf + 22e7572 commit 52992b3
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 53 deletions.
29 changes: 25 additions & 4 deletions erpnext/loan_management/doctype/loan/loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ def set_missing_fields(self):
self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)

def check_sanctioned_amount_limit(self):
total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)
if sanctioned_amount_limit:
total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)

if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))
Expand Down Expand Up @@ -155,9 +156,29 @@ def update_total_amount_paid(doc):
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)

def get_total_loan_amount(applicant_type, applicant, company):
return frappe.db.get_value('Loan',
{'applicant_type': applicant_type, 'company': company, 'applicant': applicant, 'docstatus': 1},
'sum(loan_amount)')
pending_amount = 0
loan_details = frappe.db.get_all("Loan",
filters={"applicant_type": applicant_type, "company": company, "applicant": applicant, "docstatus": 1,
"status": ("!=", "Closed")},
fields=["status", "total_payment", "disbursed_amount", "total_interest_payable", "total_principal_paid",
"written_off_amount"])

interest_amount = flt(frappe.db.get_value("Loan Interest Accrual", {"applicant_type": applicant_type,
"company": company, "applicant": applicant, "docstatus": 1}, "sum(interest_amount - paid_interest_amount)"))

for loan in loan_details:
if loan.status in ("Disbursed", "Loan Closure Requested"):
pending_amount += flt(loan.total_payment) - flt(loan.total_interest_payable) \
- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
elif loan.status == "Partially Disbursed":
pending_amount += flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \
- flt(loan.total_principal_paid) - flt(loan.written_off_amount)
elif loan.status == "Sanctioned":
pending_amount += flt(loan.total_payment)

pending_amount += interest_amount

return pending_amount

def get_sanctioned_amount_limit(applicant_type, applicant, company):
return frappe.db.get_value('Sanctioned Loan Amount',
Expand Down
45 changes: 42 additions & 3 deletions erpnext/loan_management/doctype/loan/test_loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ def setUp(self):
if not frappe.db.exists("Customer", "_Test Loan Customer"):
frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)

self.applicant2 = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name')
if not frappe.db.exists("Customer", "_Test Loan Customer 1"):
frappe.get_doc(get_customer_dict("_Test Loan Customer 1")).insert(ignore_permissions=True)

self.applicant2 = frappe.db.get_value("Customer", {"name": "_Test Loan Customer"}, "name")
self.applicant3 = frappe.db.get_value("Customer", {"name": "_Test Loan Customer 1"}, "name")

create_loan(self.applicant1, "Personal Loan", 280000, "Repay Over Number of Periods", 20)

Expand Down Expand Up @@ -125,6 +129,38 @@ def test_loan_disbursement(self):
self.assertTrue(gl_entries1)
self.assertTrue(gl_entries2)

def test_sanctioned_amount_limit(self):
# Clear loan docs before checking
frappe.db.sql("DELETE FROM `tabLoan` where applicant = '_Test Loan Customer 1'")
frappe.db.sql("DELETE FROM `tabLoan Application` where applicant = '_Test Loan Customer 1'")
frappe.db.sql("DELETE FROM `tabLoan Security Pledge` where applicant = '_Test Loan Customer 1'")

if not frappe.db.get_value("Sanctioned Loan Amount", filters={"applicant_type": "Customer",
"applicant": "_Test Loan Customer 1", "company": "_Test Company"}):
frappe.get_doc({
"doctype": "Sanctioned Loan Amount",
"applicant_type": "Customer",
"applicant": "_Test Loan Customer 1",
"sanctioned_amount_limit": 1500000,
"company": "_Test Company"
}).insert(ignore_permissions=True)

# Make First Loan
pledge = [{
"loan_security": "Test Security 1",
"qty": 4000.00
}]

loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge)
create_pledge(loan_application)
loan = create_demand_loan(self.applicant3, "Demand Loan", loan_application, posting_date='2019-10-01')
loan.submit()

# Make second loan greater than the sanctioned amount
loan_application = create_loan_application('_Test Company', self.applicant3, 'Demand Loan', pledge,
do_not_save=True)
self.assertRaises(frappe.ValidationError, loan_application.save)

def test_regular_loan_repayment(self):
pledge = [{
"loan_security": "Test Security 1",
Expand Down Expand Up @@ -367,7 +403,7 @@ def test_partial_loan_security_unpledge(self):
unpledge_request.load_from_db()
self.assertEqual(unpledge_request.docstatus, 1)

def test_santined_loan_security_unpledge(self):
def test_sanctioned_loan_security_unpledge(self):
pledge = [{
"loan_security": "Test Security 1",
"qty": 4000.00
Expand Down Expand Up @@ -858,7 +894,7 @@ def create_repayment_entry(loan, applicant, posting_date, paid_amount):
return lr

def create_loan_application(company, applicant, loan_type, proposed_pledges, repayment_method=None,
repayment_periods=None, posting_date=None):
repayment_periods=None, posting_date=None, do_not_save=False):
loan_application = frappe.new_doc('Loan Application')
loan_application.applicant_type = 'Customer'
loan_application.company = company
Expand All @@ -874,6 +910,9 @@ def create_loan_application(company, applicant, loan_type, proposed_pledges, rep
for pledge in proposed_pledges:
loan_application.append('proposed_pledges', pledge)

if do_not_save:
return loan_application

loan_application.save()
loan_application.submit()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ def validate_loan_amount(self):
frappe.throw(_("Loan Amount exceeds maximum loan amount of {0} as per proposed securities").format(self.maximum_loan_amount))

def check_sanctioned_amount_limit(self):
total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)

if sanctioned_amount_limit:
total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)

if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))

Expand Down
91 changes: 46 additions & 45 deletions erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,70 +235,71 @@ def make_gl_entries(self, cancel=0, adv_adj=0):
else:
remarks = _("Repayment against Loan: ") + self.against_loan

if self.total_penalty_paid:
if not loan_details.repay_from_salary:
if self.total_penalty_paid:
gle_map.append(
self.get_gl_dict({
"account": loan_details.loan_account,
"against": loan_details.payment_account,
"debit": self.total_penalty_paid,
"debit_in_account_currency": self.total_penalty_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": _("Penalty against loan:") + self.against_loan,
"cost_center": self.cost_center,
"party_type": self.applicant_type,
"party": self.applicant,
"posting_date": getdate(self.posting_date)
})
)

gle_map.append(
self.get_gl_dict({
"account": loan_details.penalty_income_account,
"against": loan_details.payment_account,
"credit": self.total_penalty_paid,
"credit_in_account_currency": self.total_penalty_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": _("Penalty against loan:") + self.against_loan,
"cost_center": self.cost_center,
"posting_date": getdate(self.posting_date)
})
)

gle_map.append(
self.get_gl_dict({
"account": loan_details.loan_account,
"against": loan_details.payment_account,
"debit": self.total_penalty_paid,
"debit_in_account_currency": self.total_penalty_paid,
"account": loan_details.payment_account,
"against": loan_details.loan_account + ", " + loan_details.interest_income_account
+ ", " + loan_details.penalty_income_account,
"debit": self.amount_paid,
"debit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": _("Penalty against loan:") + self.against_loan,
"remarks": remarks,
"cost_center": self.cost_center,
"party_type": self.applicant_type,
"party": self.applicant,
"posting_date": getdate(self.posting_date)
})
)

gle_map.append(
self.get_gl_dict({
"account": loan_details.penalty_income_account,
"account": loan_details.loan_account,
"party_type": loan_details.applicant_type,
"party": loan_details.applicant,
"against": loan_details.payment_account,
"credit": self.total_penalty_paid,
"credit_in_account_currency": self.total_penalty_paid,
"credit": self.amount_paid,
"credit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": _("Penalty against loan:") + self.against_loan,
"remarks": remarks,
"cost_center": self.cost_center,
"posting_date": getdate(self.posting_date)
})
)

gle_map.append(
self.get_gl_dict({
"account": loan_details.payment_account,
"against": loan_details.loan_account + ", " + loan_details.interest_income_account
+ ", " + loan_details.penalty_income_account,
"debit": self.amount_paid,
"debit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": remarks,
"cost_center": self.cost_center,
"posting_date": getdate(self.posting_date)
})
)

gle_map.append(
self.get_gl_dict({
"account": loan_details.loan_account,
"party_type": loan_details.applicant_type,
"party": loan_details.applicant,
"against": loan_details.payment_account,
"credit": self.amount_paid,
"credit_in_account_currency": self.amount_paid,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": remarks,
"cost_center": self.cost_center,
"posting_date": getdate(self.posting_date)
})
)

if gle_map:
make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False)
if gle_map:
make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False)

def create_repayment_entry(loan, applicant, company, posting_date, loan_type,
payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None):
Expand Down

0 comments on commit 52992b3

Please sign in to comment.