Skip to content

Commit

Permalink
Merge pull request #103 from frappe/mergify/copy/poc-staging/pr-100
Browse files Browse the repository at this point in the history
feat: Loan booking updates (copy #100)
  • Loading branch information
deepeshgarg007 committed Oct 18, 2023
2 parents ab8d43c + 51d272b commit 68eb359
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 40 deletions.
22 changes: 21 additions & 1 deletion lending/loan_management/doctype/loan/loan.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"repayment_periods",
"monthly_repayment_amount",
"repayment_start_date",
"repayment_frequency",
"is_term_loan",
"loan_classification_details_section",
"days_past_due",
Expand All @@ -44,6 +45,8 @@
"tenure_post_restructure",
"accounting_dimensions_section",
"cost_center",
"loan_charges_section",
"loan_charges",
"account_info",
"mode_of_payment",
"disbursement_account",
Expand Down Expand Up @@ -483,12 +486,29 @@
"label": "Loan Category",
"options": "Loan Category",
"read_only": 1
},
{
"fieldname": "repayment_frequency",
"fieldtype": "Select",
"label": "Repayment Frequency",
"options": "Monthly\nDaily\nWeekly\nQuarterly\nOne Time"
},
{
"fieldname": "loan_charges",
"fieldtype": "Table",
"label": "Loan Charges",
"options": "Loan Disbursement Charge"
},
{
"fieldname": "loan_charges_section",
"fieldtype": "Section Break",
"label": "Loan Charges"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-11 13:26:31.406754",
"modified": "2023-10-13 19:15:47.245190",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
Expand Down
60 changes: 50 additions & 10 deletions lending/loan_management/doctype/loan/loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def validate(self):
self.validate_accounts()
self.check_sanctioned_amount_limit()
self.set_cyclic_date()
self.set_default_charge_account()

if self.is_term_loan and not self.is_new():
self.update_draft_schedule()
Expand Down Expand Up @@ -72,7 +73,10 @@ def validate_cost_center(self):
frappe.throw(_("Cost center is mandatory for loans having rate of interest greater than 0"))

def set_cyclic_date(self):
if self.repayment_schedule_type == "Monthly as per cycle date":
if (
self.repayment_schedule_type == "Monthly as per cycle date"
and self.repayment_frequency == "Monthly"
):
cycle_day, min_days_bw_disbursement_first_repayment = frappe.db.get_value(
"Loan Product",
self.loan_product,
Expand All @@ -89,6 +93,20 @@ def set_cyclic_date(self):

self.repayment_start_date = cyclic_date

def set_default_charge_account(self):
for charge in self.get("loan_charges"):
if not charge.account:
account = frappe.get_cached_value(
"Loan Charges", {"parent": self.loan_product, "charge_type": charge.charge}, "income_account"
)

if not account:
account = frappe.get_cached_value(
"Item Default", {"parent": charge.charge, "company": self.company}, "income_account"
)

charge.account = account

def on_submit(self):
self.link_loan_security_pledge()
# Interest accrual for backdated term loans
Expand Down Expand Up @@ -150,6 +168,7 @@ def make_draft_schedule(self):
"loan_product": self.loan_product,
"rate_of_interest": self.rate_of_interest,
"posting_date": self.posting_date,
"repayment_frequency": self.repayment_frequency,
}
).insert()

Expand All @@ -169,6 +188,7 @@ def update_draft_schedule(self):
"posting_date": self.posting_date,
"loan_amount": self.loan_amount,
"monthly_repayment_amount": self.monthly_repayment_amount,
"repayment_frequency": self.repayment_frequency,
}
)
schedule.save()
Expand Down Expand Up @@ -389,16 +409,36 @@ def close_loan(loan, total_amount_paid):


@frappe.whitelist()
def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0):
def make_loan_disbursement(
loan,
disbursement_amount=0,
as_dict=0,
submit=0,
posting_date=None,
disbursement_date=None,
bank_account=None,
):
loan_doc = frappe.get_doc("Loan", loan)
disbursement_entry = frappe.new_doc("Loan Disbursement")
disbursement_entry.against_loan = loan
disbursement_entry.applicant_type = applicant_type
disbursement_entry.applicant = applicant
disbursement_entry.company = company
disbursement_entry.disbursement_date = nowdate()
disbursement_entry.posting_date = nowdate()

disbursement_entry.disbursed_amount = pending_amount
disbursement_entry.against_loan = loan_doc.name
disbursement_entry.applicant_type = loan_doc.applicant_type
disbursement_entry.applicant = loan_doc.applicant
disbursement_entry.company = loan_doc.company
disbursement_entry.disbursement_date = posting_date or nowdate()
disbursement_entry.posting_date = disbursement_date or nowdate()
disbursement_entry.bank_account = bank_account

disbursement_entry.disbursed_amount = disbursement_amount

for charge in loan_doc.get("loan_charges"):
disbursement_entry.append(
"loan_disbursement_charges",
{"charge": charge.charge, "amount": charge.amount, "account": charge.account},
)

if submit:
disbursement_entry.submit()

if as_dict:
return disbursement_entry.as_dict()
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"engine": "InnoDB",
"field_order": [
"sales_invoice",
"charge",
"pending_charge_amount",
"allocated_amount"
],
Expand All @@ -29,12 +30,19 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Allocated Amount"
},
{
"fieldname": "charge",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Charge",
"options": "Item"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-05-16 09:14:13.972294",
"modified": "2023-10-11 16:50:15.722251",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Charge Reference",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def set_missing_values(self):
if not self.posting_date:
self.posting_date = self.disbursement_date or nowdate()

if not self.disbursement_account and self.bank_account:
self.disbursement_account = frappe.db.get_value("Bank Account", self.bank_account, "account")

def withheld_security_deposit(self):
if self.withhold_security_deposit:
sd = frappe.get_doc(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-10-10 19:47:07.918342",
"modified": "2023-10-11 19:47:07.918342",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Disbursement Charge",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"applicant",
"loan_product",
"repayment_type",
"select_charge_manually",
"loan_restructure",
"column_break_3",
"company",
Expand Down Expand Up @@ -373,12 +374,19 @@
"label": "Loan Restructure",
"options": "Loan Restructure",
"read_only": 1
},
{
"default": "0",
"depends_on": "eval:doc.repayment_type == \"Charges Waiver\"",
"fieldname": "select_charge_manually",
"fieldtype": "Check",
"label": "Select Charge Manually"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-11 09:25:15.123899",
"modified": "2023-10-11 17:45:09.856637",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Repayment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"adjusted_interest",
"column_break_n6iy",
"loan_product",
"repayment_frequency",
"repayment_schedule_type",
"repayment_method",
"repayment_periods",
Expand Down Expand Up @@ -143,12 +144,18 @@
"fieldtype": "Link",
"label": "Company",
"options": "Company"
},
{
"fieldname": "repayment_frequency",
"fieldtype": "Select",
"label": "Repayment Frequency",
"options": "Monthly\nDaily\nWeekly\nQuarterly\nOne Time"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-02 22:14:24.172876",
"modified": "2023-10-13 16:55:43.615976",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Repayment Schedule",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def make_repayment_schedule(self):
interest_amount, principal_amount, balance_amount, total_payment, days = self.get_amounts(
payment_date,
balance_amount,
self.repayment_frequency,
schedule_type_details.repayment_schedule_type,
schedule_type_details.repayment_date_on,
broken_period_interest_days,
Expand Down Expand Up @@ -78,11 +79,16 @@ def make_repayment_schedule(self):
schedule_type_details.repayment_schedule_type
in ["Monthly as per repayment start date", "Monthly as per cycle date"]
or schedule_type_details.repayment_date_on == "End of the current month"
):
) and self.repayment_frequency == "Monthly":
next_payment_date = add_single_month(payment_date)
payment_date = next_payment_date
elif self.repayment_frequency == "Weekly":
payment_date = add_days(payment_date, 7)
elif self.repayment_frequency == "Daily":
payment_date = add_days(payment_date, 1)
elif self.repayment_type == "Quarterly":
payment_date = add_months(payment_date, 3)

bmi_days = 0
carry_forward_interest = 0

def validate_repayment_method(self):
Expand All @@ -99,36 +105,50 @@ def get_amounts(
self,
payment_date,
balance_amount,
repayment_frequency,
schedule_type,
repayment_date_on,
additional_days,
carry_forward_interest=0,
):
if schedule_type == "Monthly as per repayment start date":
if repayment_frequency == "Monthly":
if schedule_type == "Monthly as per repayment start date":
days = 1
months = 12
else:
expected_payment_date = get_last_day(payment_date)
if repayment_date_on == "Start of the next month":
expected_payment_date = add_days(expected_payment_date, 1)

if schedule_type == "Monthly as per cycle date":
days = date_diff(payment_date, add_months(payment_date, -1))
if additional_days < 0:
days = date_diff(self.repayment_start_date, self.posting_date)
additional_days = 0

months = 365
if additional_days:
days += additional_days
additional_days = 0
elif expected_payment_date == payment_date:
# using 30 days for calculating interest for all full months
days = 30
months = 365
else:
days = date_diff(get_last_day(payment_date), payment_date)
months = 365
elif repayment_frequency == "Weekly":
days = 7
months = 52
elif repayment_frequency == "Daily":
days = 1
months = 365
elif repayment_frequency == "Quarterly":
days = 3
months = 12
else:
expected_payment_date = get_last_day(payment_date)
if repayment_date_on == "Start of the next month":
expected_payment_date = add_days(expected_payment_date, 1)

if schedule_type == "Monthly as per cycle date":
days = date_diff(payment_date, add_months(payment_date, -1))
if additional_days < 0:
days = date_diff(self.repayment_start_date, self.posting_date)
additional_days = 0

months = 365
if additional_days:
days += additional_days
additional_days = 0
elif expected_payment_date == payment_date:
# using 30 days for calculating interest for all full months
days = 30
months = 365
else:
days = date_diff(get_last_day(payment_date), payment_date)
months = 365
elif repayment_frequency == "One Time":
days = date_diff(self.repayment_start_date, self.posting_date)
months = 365

interest_amount = flt(balance_amount * flt(self.rate_of_interest) * days / (months * 100))
principal_amount = self.monthly_repayment_amount - flt(interest_amount)
Expand Down

0 comments on commit 68eb359

Please sign in to comment.