Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Missing ledger entry for due amount within limit after waiver #257

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions lending/loan_management/doctype/loan/test_loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ def setUp(self):
repayment_schedule_type=loan_product[3],
)

if loan_product[0] == "Term Loan Product 4":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this from setup, don't have to add charges this way

add_or_update_loan_charges("Term Loan Product 4")

for loan_product in loc_loans:
create_loan_product(
loan_product[0],
Expand Down Expand Up @@ -1465,6 +1468,52 @@ def test_backdated_pre_payment(self):
)
repayment_entry.submit()

def test_excess_amount_for_waiver(self):
loan = create_loan(
"_Test Customer 1",
"Term Loan Product 4",
100000,
"Repay Over Number of Periods",
6,
"Customer",
"2024-07-15",
"2024-06-25",
10,
)
loan.submit()

make_loan_disbursement_entry(
loan.name, loan.loan_amount, disbursement_date="2024-06-25", repayment_start_date="2024-07-15"
)
process_daily_loan_demands(posting_date="2025-01-05", loan=loan.name)

sales_invoice = frappe.get_doc(
{
"doctype": "Sales Invoice",
"customer": "_Test Customer 1",
"company": "_Test Company",
"loan": loan.name,
"posting_date": "2025-01-15",
"posting_time": "00:06:10",
"set_posting_time": 1,
"items": [{"item_code": "Processing Fee", "qty": 1, "rate": 5000}],
}
)
sales_invoice.submit()

repayment_entry = create_repayment_entry(loan.name, "2025-01-16", 106684.69)
repayment_entry.submit()

loan_adjustment = frappe.get_doc(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not testing anything in this, please check if charge waiver entry is getting generated if only charge is pending under shortfall acceptance limit.

Also test for Interest and penalty and principal as well

{
"doctype": "Loan Adjustment",
"loan": loan.name,
"posting_date": "2025-01-16",
"adjustments": [{"loan_repayment_type": "Charges Waiver", "amount": 4500}],
}
)
loan_adjustment.submit()

def test_dpd_calculation(self):
loan = create_loan(
"_Test Customer 1",
Expand Down Expand Up @@ -1735,6 +1784,30 @@ def create_loan_accounts():
"Balance Sheet",
)

create_account(
"Processing Fee Income Account",
"Direct Income - _TC",
"Income",
"Income Account",
"Profit and Loss",
)

create_account(
"Processing Fee Receivable Account",
"Loans and Advances (Assets) - _TC",
"Asset",
"Receivable",
"Balance Sheet",
)

create_account(
"Processing Fee Waiver Account",
"Direct Expenses - _TC",
"Expense",
"Expense Account",
"Profit and Loss",
)


def create_account(account_name, parent_account, root_type, account_type, report_type, is_group=0):
if not frappe.db.exists("Account", {"account_name": account_name}):
Expand Down Expand Up @@ -1811,6 +1884,7 @@ def create_loan_product(
"repayment_method": repayment_method,
"repayment_periods": repayment_periods,
"write_off_amount": 100,
"excess_amount_acceptance_limit": 100,
"days_past_due_threshold_for_npa": days_past_due_threshold_for_npa,
"min_days_bw_disbursement_first_repayment": min_days_bw_disbursement_first_repayment,
"min_auto_closure_tolerance_amount": -100,
Expand All @@ -1828,6 +1902,36 @@ def create_loan_product(
return loan_product


def add_or_update_loan_charges(product_name):
loan_product = frappe.get_doc("Loan Product", product_name)

charge_type = "Processing Fee"

if not frappe.db.exists("Item", charge_type):
frappe.get_doc(
{
"doctype": "Item",
"item_code": charge_type,
"item_group": "Services",
"is_stock_item": 0,
"income_account": "Processing Fee Income Account - _TC",
}
).insert()

loan_product.loan_charges = []

loan_product.append(
"loan_charges",
{
"charge_type": charge_type,
"income_account": "Processing Fee Income Account - _TC",
"receivable_account": "Processing Fee Receivable Account - _TC",
"waiver_account": "Processing Fee Waiver Account - _TC",
},
)
loan_product.save()


def create_loan_security_type():
if not frappe.db.exists("Loan Security Type", "Stock"):
frappe.get_doc(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ def set_missing_values(self, amounts):
elif not self.repay_from_salary and self.payroll_payable_account:
self.repay_from_salary = 1

if self.repayment_type in ("Full Settlement", "Write Off Settlement"):
if self.repayment_type in ("Full Settlement", "Write Off Settlement", "Charges Waiver"):
self.total_charges_payable = amounts.get("total_charges_payable")

def validate_disbursement_link(self):
Expand Down Expand Up @@ -618,7 +618,7 @@ def validate_security_deposit_amount(self):
def validate_repayment_type(self):
loan_status = frappe.db.get_value("Loan", self.against_loan, "status")

if loan_status == "Closed":
if loan_status == "Closed" and self.repayment_type != "Charges Waiver":
frappe.throw(_("Repayment cannot be made for closed loan"))

if loan_status == "Written Off":
Expand Down Expand Up @@ -882,7 +882,7 @@ def auto_close_loan(self):
"loan": self.against_loan,
"docstatus": 1,
"outstanding_amount": (">", 0),
"posting_date": ("<=", self.posting_date),
"demand_date": ("<=", self.posting_date),
},
"sum(outstanding_amount)",
)
Expand All @@ -896,10 +896,12 @@ def auto_close_loan(self):
and (total_payable - self.amount_paid <= shortfall_amount)
):
auto_close = True
self.set_excess_amount_for_waiver(total_payable)

excess_amount = self.principal_amount_paid - self.pending_principal_amount
if excess_amount > 0 and excess_amount <= excess_amount_limit:
auto_close = True
self.set_excess_amount_for_waiver(total_payable)

if (
self.principal_amount_paid >= self.pending_principal_amount
Expand All @@ -908,9 +910,14 @@ def auto_close_loan(self):
and (total_payable - self.amount_paid) <= flt(auto_write_off_amount)
):
auto_close = True
self.set_excess_amount_for_waiver(total_payable)

return auto_close

def set_excess_amount_for_waiver(self, total_payable):
if self.repayment_type in ("Interest Waiver", "Penalty Waiver", "Charges Waiver"):
self.excess_amount = self.amount_paid - total_payable

def mark_as_unpaid(self):
if self.repayment_type in (
"Normal Repayment",
Expand Down Expand Up @@ -1245,8 +1252,12 @@ def allocate_amount_against_demands(self, amounts, on_submit=False):
self.principal_amount_paid = flt(self.principal_amount_paid, precision)

if (
self.auto_close_loan() or self.principal_amount_paid - self.pending_principal_amount > 0
) and self.repayment_type not in ("Write Off Settlement", "Write Off Recovery"):
self.auto_close_loan() or flt(self.principal_amount_paid - self.pending_principal_amount) > 0
) and self.repayment_type not in (
"Write Off Settlement",
"Write Off Recovery",
"Charges Waiver",
):
self.excess_amount = self.principal_amount_paid - self.pending_principal_amount
self.principal_amount_paid -= self.excess_amount
elif self.repayment_type == "Write Off Settlement" and (
Expand Down Expand Up @@ -1463,7 +1474,14 @@ def get_overall_partner_share(self, paid_amount):
return flt(self.loan_partner_share_percentage * paid_amount)

def make_gl_entries(self, cancel=0, adv_adj=0):
from lending.loan_management.doctype.loan_restructure.loan_restructure import (
create_loan_repayment,
)

if self.repayment_type == "Charges Waiver":
payable_charges = self.total_charges_payable - self.total_charges_paid
if self.excess_amount < 0 and payable_charges > 0:
create_loan_repayment(self.against_loan, self.posting_date, "Charges Waiver", payable_charges)
return

if cancel:
Expand Down
Loading