Skip to content

Commit 68eb359

Browse files
Merge pull request #103 from frappe/mergify/copy/poc-staging/pr-100
feat: Loan booking updates (copy #100)
2 parents ab8d43c + 51d272b commit 68eb359

File tree

8 files changed

+146
-40
lines changed

8 files changed

+146
-40
lines changed

lending/loan_management/doctype/loan/loan.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"repayment_periods",
3434
"monthly_repayment_amount",
3535
"repayment_start_date",
36+
"repayment_frequency",
3637
"is_term_loan",
3738
"loan_classification_details_section",
3839
"days_past_due",
@@ -44,6 +45,8 @@
4445
"tenure_post_restructure",
4546
"accounting_dimensions_section",
4647
"cost_center",
48+
"loan_charges_section",
49+
"loan_charges",
4750
"account_info",
4851
"mode_of_payment",
4952
"disbursement_account",
@@ -483,12 +486,29 @@
483486
"label": "Loan Category",
484487
"options": "Loan Category",
485488
"read_only": 1
489+
},
490+
{
491+
"fieldname": "repayment_frequency",
492+
"fieldtype": "Select",
493+
"label": "Repayment Frequency",
494+
"options": "Monthly\nDaily\nWeekly\nQuarterly\nOne Time"
495+
},
496+
{
497+
"fieldname": "loan_charges",
498+
"fieldtype": "Table",
499+
"label": "Loan Charges",
500+
"options": "Loan Disbursement Charge"
501+
},
502+
{
503+
"fieldname": "loan_charges_section",
504+
"fieldtype": "Section Break",
505+
"label": "Loan Charges"
486506
}
487507
],
488508
"index_web_pages_for_search": 1,
489509
"is_submittable": 1,
490510
"links": [],
491-
"modified": "2023-10-11 13:26:31.406754",
511+
"modified": "2023-10-13 19:15:47.245190",
492512
"modified_by": "Administrator",
493513
"module": "Loan Management",
494514
"name": "Loan",

lending/loan_management/doctype/loan/loan.py

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def validate(self):
3636
self.validate_accounts()
3737
self.check_sanctioned_amount_limit()
3838
self.set_cyclic_date()
39+
self.set_default_charge_account()
3940

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

7475
def set_cyclic_date(self):
75-
if self.repayment_schedule_type == "Monthly as per cycle date":
76+
if (
77+
self.repayment_schedule_type == "Monthly as per cycle date"
78+
and self.repayment_frequency == "Monthly"
79+
):
7680
cycle_day, min_days_bw_disbursement_first_repayment = frappe.db.get_value(
7781
"Loan Product",
7882
self.loan_product,
@@ -89,6 +93,20 @@ def set_cyclic_date(self):
8993

9094
self.repayment_start_date = cyclic_date
9195

96+
def set_default_charge_account(self):
97+
for charge in self.get("loan_charges"):
98+
if not charge.account:
99+
account = frappe.get_cached_value(
100+
"Loan Charges", {"parent": self.loan_product, "charge_type": charge.charge}, "income_account"
101+
)
102+
103+
if not account:
104+
account = frappe.get_cached_value(
105+
"Item Default", {"parent": charge.charge, "company": self.company}, "income_account"
106+
)
107+
108+
charge.account = account
109+
92110
def on_submit(self):
93111
self.link_loan_security_pledge()
94112
# Interest accrual for backdated term loans
@@ -150,6 +168,7 @@ def make_draft_schedule(self):
150168
"loan_product": self.loan_product,
151169
"rate_of_interest": self.rate_of_interest,
152170
"posting_date": self.posting_date,
171+
"repayment_frequency": self.repayment_frequency,
153172
}
154173
).insert()
155174

@@ -169,6 +188,7 @@ def update_draft_schedule(self):
169188
"posting_date": self.posting_date,
170189
"loan_amount": self.loan_amount,
171190
"monthly_repayment_amount": self.monthly_repayment_amount,
191+
"repayment_frequency": self.repayment_frequency,
172192
}
173193
)
174194
schedule.save()
@@ -389,16 +409,36 @@ def close_loan(loan, total_amount_paid):
389409

390410

391411
@frappe.whitelist()
392-
def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0):
412+
def make_loan_disbursement(
413+
loan,
414+
disbursement_amount=0,
415+
as_dict=0,
416+
submit=0,
417+
posting_date=None,
418+
disbursement_date=None,
419+
bank_account=None,
420+
):
421+
loan_doc = frappe.get_doc("Loan", loan)
393422
disbursement_entry = frappe.new_doc("Loan Disbursement")
394-
disbursement_entry.against_loan = loan
395-
disbursement_entry.applicant_type = applicant_type
396-
disbursement_entry.applicant = applicant
397-
disbursement_entry.company = company
398-
disbursement_entry.disbursement_date = nowdate()
399-
disbursement_entry.posting_date = nowdate()
400-
401-
disbursement_entry.disbursed_amount = pending_amount
423+
disbursement_entry.against_loan = loan_doc.name
424+
disbursement_entry.applicant_type = loan_doc.applicant_type
425+
disbursement_entry.applicant = loan_doc.applicant
426+
disbursement_entry.company = loan_doc.company
427+
disbursement_entry.disbursement_date = posting_date or nowdate()
428+
disbursement_entry.posting_date = disbursement_date or nowdate()
429+
disbursement_entry.bank_account = bank_account
430+
431+
disbursement_entry.disbursed_amount = disbursement_amount
432+
433+
for charge in loan_doc.get("loan_charges"):
434+
disbursement_entry.append(
435+
"loan_disbursement_charges",
436+
{"charge": charge.charge, "amount": charge.amount, "account": charge.account},
437+
)
438+
439+
if submit:
440+
disbursement_entry.submit()
441+
402442
if as_dict:
403443
return disbursement_entry.as_dict()
404444
else:

lending/loan_management/doctype/loan_charge_reference/loan_charge_reference.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"engine": "InnoDB",
88
"field_order": [
99
"sales_invoice",
10+
"charge",
1011
"pending_charge_amount",
1112
"allocated_amount"
1213
],
@@ -29,12 +30,19 @@
2930
"fieldtype": "Currency",
3031
"in_list_view": 1,
3132
"label": "Allocated Amount"
33+
},
34+
{
35+
"fieldname": "charge",
36+
"fieldtype": "Link",
37+
"in_list_view": 1,
38+
"label": "Charge",
39+
"options": "Item"
3240
}
3341
],
3442
"index_web_pages_for_search": 1,
3543
"istable": 1,
3644
"links": [],
37-
"modified": "2023-05-16 09:14:13.972294",
45+
"modified": "2023-10-11 16:50:15.722251",
3846
"modified_by": "Administrator",
3947
"module": "Loan Management",
4048
"name": "Loan Charge Reference",

lending/loan_management/doctype/loan_disbursement/loan_disbursement.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ def set_missing_values(self):
6666
if not self.posting_date:
6767
self.posting_date = self.disbursement_date or nowdate()
6868

69+
if not self.disbursement_account and self.bank_account:
70+
self.disbursement_account = frappe.db.get_value("Bank Account", self.bank_account, "account")
71+
6972
def withheld_security_deposit(self):
7073
if self.withhold_security_deposit:
7174
sd = frappe.get_doc(

lending/loan_management/doctype/loan_disbursement_charge/loan_disbursement_charge.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"index_web_pages_for_search": 1,
3636
"istable": 1,
3737
"links": [],
38-
"modified": "2023-10-10 19:47:07.918342",
38+
"modified": "2023-10-11 19:47:07.918342",
3939
"modified_by": "Administrator",
4040
"module": "Loan Management",
4141
"name": "Loan Disbursement Charge",

lending/loan_management/doctype/loan_repayment/loan_repayment.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"applicant",
1212
"loan_product",
1313
"repayment_type",
14+
"select_charge_manually",
1415
"loan_restructure",
1516
"column_break_3",
1617
"company",
@@ -373,12 +374,19 @@
373374
"label": "Loan Restructure",
374375
"options": "Loan Restructure",
375376
"read_only": 1
377+
},
378+
{
379+
"default": "0",
380+
"depends_on": "eval:doc.repayment_type == \"Charges Waiver\"",
381+
"fieldname": "select_charge_manually",
382+
"fieldtype": "Check",
383+
"label": "Select Charge Manually"
376384
}
377385
],
378386
"index_web_pages_for_search": 1,
379387
"is_submittable": 1,
380388
"links": [],
381-
"modified": "2023-10-11 09:25:15.123899",
389+
"modified": "2023-10-11 17:45:09.856637",
382390
"modified_by": "Administrator",
383391
"module": "Loan Management",
384392
"name": "Loan Repayment",

lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"adjusted_interest",
1717
"column_break_n6iy",
1818
"loan_product",
19+
"repayment_frequency",
1920
"repayment_schedule_type",
2021
"repayment_method",
2122
"repayment_periods",
@@ -143,12 +144,18 @@
143144
"fieldtype": "Link",
144145
"label": "Company",
145146
"options": "Company"
147+
},
148+
{
149+
"fieldname": "repayment_frequency",
150+
"fieldtype": "Select",
151+
"label": "Repayment Frequency",
152+
"options": "Monthly\nDaily\nWeekly\nQuarterly\nOne Time"
146153
}
147154
],
148155
"index_web_pages_for_search": 1,
149156
"is_submittable": 1,
150157
"links": [],
151-
"modified": "2023-10-02 22:14:24.172876",
158+
"modified": "2023-10-13 16:55:43.615976",
152159
"modified_by": "Administrator",
153160
"module": "Loan Management",
154161
"name": "Loan Repayment Schedule",

lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.py

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def make_repayment_schedule(self):
4545
interest_amount, principal_amount, balance_amount, total_payment, days = self.get_amounts(
4646
payment_date,
4747
balance_amount,
48+
self.repayment_frequency,
4849
schedule_type_details.repayment_schedule_type,
4950
schedule_type_details.repayment_date_on,
5051
broken_period_interest_days,
@@ -78,11 +79,16 @@ def make_repayment_schedule(self):
7879
schedule_type_details.repayment_schedule_type
7980
in ["Monthly as per repayment start date", "Monthly as per cycle date"]
8081
or schedule_type_details.repayment_date_on == "End of the current month"
81-
):
82+
) and self.repayment_frequency == "Monthly":
8283
next_payment_date = add_single_month(payment_date)
8384
payment_date = next_payment_date
85+
elif self.repayment_frequency == "Weekly":
86+
payment_date = add_days(payment_date, 7)
87+
elif self.repayment_frequency == "Daily":
88+
payment_date = add_days(payment_date, 1)
89+
elif self.repayment_type == "Quarterly":
90+
payment_date = add_months(payment_date, 3)
8491

85-
bmi_days = 0
8692
carry_forward_interest = 0
8793

8894
def validate_repayment_method(self):
@@ -99,36 +105,50 @@ def get_amounts(
99105
self,
100106
payment_date,
101107
balance_amount,
108+
repayment_frequency,
102109
schedule_type,
103110
repayment_date_on,
104111
additional_days,
105112
carry_forward_interest=0,
106113
):
107-
if schedule_type == "Monthly as per repayment start date":
114+
if repayment_frequency == "Monthly":
115+
if schedule_type == "Monthly as per repayment start date":
116+
days = 1
117+
months = 12
118+
else:
119+
expected_payment_date = get_last_day(payment_date)
120+
if repayment_date_on == "Start of the next month":
121+
expected_payment_date = add_days(expected_payment_date, 1)
122+
123+
if schedule_type == "Monthly as per cycle date":
124+
days = date_diff(payment_date, add_months(payment_date, -1))
125+
if additional_days < 0:
126+
days = date_diff(self.repayment_start_date, self.posting_date)
127+
additional_days = 0
128+
129+
months = 365
130+
if additional_days:
131+
days += additional_days
132+
additional_days = 0
133+
elif expected_payment_date == payment_date:
134+
# using 30 days for calculating interest for all full months
135+
days = 30
136+
months = 365
137+
else:
138+
days = date_diff(get_last_day(payment_date), payment_date)
139+
months = 365
140+
elif repayment_frequency == "Weekly":
141+
days = 7
142+
months = 52
143+
elif repayment_frequency == "Daily":
108144
days = 1
145+
months = 365
146+
elif repayment_frequency == "Quarterly":
147+
days = 3
109148
months = 12
110-
else:
111-
expected_payment_date = get_last_day(payment_date)
112-
if repayment_date_on == "Start of the next month":
113-
expected_payment_date = add_days(expected_payment_date, 1)
114-
115-
if schedule_type == "Monthly as per cycle date":
116-
days = date_diff(payment_date, add_months(payment_date, -1))
117-
if additional_days < 0:
118-
days = date_diff(self.repayment_start_date, self.posting_date)
119-
additional_days = 0
120-
121-
months = 365
122-
if additional_days:
123-
days += additional_days
124-
additional_days = 0
125-
elif expected_payment_date == payment_date:
126-
# using 30 days for calculating interest for all full months
127-
days = 30
128-
months = 365
129-
else:
130-
days = date_diff(get_last_day(payment_date), payment_date)
131-
months = 365
149+
elif repayment_frequency == "One Time":
150+
days = date_diff(self.repayment_start_date, self.posting_date)
151+
months = 365
132152

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

0 commit comments

Comments
 (0)