Merge pull request #26108 from deepeshgarg007/loan_repay_via_salary_v13
fix: Sanctioned loan amount limit check
This commit is contained in:
commit
be881c743e
@ -60,8 +60,9 @@ class Loan(AccountsController):
|
||||
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)))
|
||||
@ -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',
|
||||
|
@ -49,7 +49,11 @@ class TestLoan(unittest.TestCase):
|
||||
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)
|
||||
|
||||
@ -125,6 +129,38 @@ class TestLoan(unittest.TestCase):
|
||||
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",
|
||||
@ -367,7 +403,7 @@ class TestLoan(unittest.TestCase):
|
||||
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
|
||||
@ -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
|
||||
@ -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()
|
||||
|
||||
|
@ -46,9 +46,11 @@ class LoanApplication(Document):
|
||||
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)))
|
||||
|
||||
|
@ -235,70 +235,71 @@ class LoanRepayment(AccountsController):
|
||||
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):
|
||||
|
Loading…
Reference in New Issue
Block a user