Skip to content

Commit

Permalink
Merge pull request #125 from deepeshgarg007/loc_loans_staging_new
Browse files Browse the repository at this point in the history
feat: Line of credit loans
  • Loading branch information
deepeshgarg007 authored Nov 3, 2023
2 parents 58f133c + 2f9d67b commit e129e72
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 77 deletions.
40 changes: 38 additions & 2 deletions lending/loan_management/doctype/loan/loan.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
"moratorium_tenure",
"treatment_of_interest",
"is_term_loan",
"loan_credit_limits_section",
"limit_applicable_start",
"minimum_limit_amount",
"column_break_foeo",
"limit_applicable_end",
"maximum_limit_amount",
"loan_classification_details_section",
"days_past_due",
"classification_code",
Expand Down Expand Up @@ -139,7 +145,7 @@
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"options": "Sanctioned\nPartially Disbursed\nDisbursed\nLoan Closure Requested\nClosed",
"options": "Sanctioned\nPartially Disbursed\nDisbursed\nActive\nLoan Closure Requested\nClosed",
"read_only": 1
},
{
Expand Down Expand Up @@ -199,6 +205,7 @@
"fieldname": "monthly_repayment_amount",
"fieldtype": "Currency",
"label": "Monthly Repayment Amount",
"no_copy": 1,
"options": "Company:company:default_currency"
},
{
Expand Down Expand Up @@ -509,12 +516,41 @@
"fieldname": "moratorium_tenure",
"fieldtype": "Int",
"label": "Moratorium Tenure"
},
{
"fieldname": "loan_credit_limits_section",
"fieldtype": "Section Break",
"label": "Loan Credit Limits"
},
{
"fieldname": "limit_applicable_start",
"fieldtype": "Date",
"label": "Limit Applicable Start"
},
{
"fieldname": "minimum_limit_amount",
"fieldtype": "Currency",
"label": "Minimum Limit Amount"
},
{
"fieldname": "column_break_foeo",
"fieldtype": "Column Break"
},
{
"fieldname": "limit_applicable_end",
"fieldtype": "Date",
"label": "Limit Applicable End"
},
{
"fieldname": "maximum_limit_amount",
"fieldtype": "Currency",
"label": "Maximum Limit Amount"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-27 10:03:51.322866",
"modified": "2023-11-02 20:30:24.141977",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
Expand Down
145 changes: 96 additions & 49 deletions lending/loan_management/doctype/loan/loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,35 @@ def validate(self):
self.set_cyclic_date()
self.set_default_charge_account()

if self.is_term_loan and not self.is_new():
self.update_draft_schedule()
if self.is_term_loan and not self.is_new() and self.repayment_schedule_type != "Line of Credit":
update_draft_schedule(
self.name,
self.repayment_method,
self.repayment_start_date,
self.repayment_periods,
self.monthly_repayment_amount,
self.posting_date,
self.repayment_frequency,
moratorium_tenure=self.moratorium_tenure,
treatment_of_interest=self.treatment_of_interest,
)

if not self.is_term_loan or (self.is_term_loan and not self.is_new()):
self.calculate_totals()

def after_insert(self):
if self.is_term_loan:
self.make_draft_schedule()
if self.is_term_loan and self.repayment_schedule_type != "Line of Credit":
make_draft_schedule(
self.name,
self.repayment_method,
self.repayment_start_date,
self.repayment_periods,
self.monthly_repayment_amount,
self.posting_date,
self.repayment_frequency,
moratorium_tenure=self.moratorium_tenure,
treatment_of_interest=self.treatment_of_interest,
)
self.calculate_totals(on_insert=True)

def validate_accounts(self):
Expand Down Expand Up @@ -116,7 +136,9 @@ def on_submit(self):
self.link_loan_security_assignment()
# Interest accrual for backdated term loans
self.accrue_loan_interest()
self.submit_draft_schedule()

if self.repayment_schedule_type != "Line of Credit":
self.submit_draft_schedule()

def on_cancel(self):
self.unlink_loan_security_assignment()
Expand Down Expand Up @@ -160,48 +182,6 @@ def check_sanctioned_amount_limit(self):
)
)

def make_draft_schedule(self):
frappe.get_doc(
{
"doctype": "Loan Repayment Schedule",
"loan": self.name,
"repayment_method": self.repayment_method,
"repayment_start_date": self.repayment_start_date,
"repayment_periods": self.repayment_periods,
"loan_amount": self.loan_amount,
"monthly_repayment_amount": self.monthly_repayment_amount,
"loan_product": self.loan_product,
"rate_of_interest": self.rate_of_interest,
"posting_date": self.posting_date,
"repayment_frequency": self.repayment_frequency,
"moratorium_tenure": self.moratorium_tenure,
"treatment_of_interest": self.treatment_of_interest,
}
).insert()

def update_draft_schedule(self):
draft_schedule = frappe.db.get_value(
"Loan Repayment Schedule", {"loan": self.name, "docstatus": 0}, "name"
)
if draft_schedule:
schedule = frappe.get_doc("Loan Repayment Schedule", draft_schedule)
schedule.update(
{
"loan": self.name,
"loan_product": self.loan_product,
"repayment_periods": self.repayment_periods,
"repayment_method": self.repayment_method,
"repayment_start_date": self.repayment_start_date,
"posting_date": self.posting_date,
"loan_amount": self.loan_amount,
"monthly_repayment_amount": self.monthly_repayment_amount,
"repayment_frequency": self.repayment_frequency,
"moratorium_tenure": self.moratorium_tenure,
"treatment_of_interest": self.treatment_of_interest,
}
)
schedule.save()

def submit_draft_schedule(self):
draft_schedule = frappe.db.get_value(
"Loan Repayment Schedule", {"loan": self.name, "docstatus": 0}, "name"
Expand All @@ -223,7 +203,7 @@ def calculate_totals(self, on_insert=False):
self.total_interest_payable = 0
self.total_amount_paid = 0

if self.is_term_loan:
if self.is_term_loan and self.repayment_schedule_type != "Line of Credit":
schedule = frappe.get_doc("Loan Repayment Schedule", {"loan": self.name, "docstatus": 0})
for data in schedule.repayment_schedule:
self.total_payment += data.total_payment
Expand All @@ -247,7 +227,7 @@ def validate_loan_amount(self):
msg = _("Loan amount cannot be greater than {0}").format(self.maximum_loan_amount)
frappe.throw(msg)

if not self.loan_amount:
if not self.loan_amount and self.repayment_schedule_type != "Line of Credit":
frappe.throw(_("Loan amount is mandatory"))

def link_loan_security_assignment(self):
Expand Down Expand Up @@ -974,3 +954,70 @@ def move_unpaid_interest_to_suspense_ledger(

jv.flags.ignore_mandatory = True
jv.submit()


def make_draft_schedule(
loan,
repayment_method,
start_date,
repayment_periods,
frequency_repayment_amount,
posting_date,
repayment_frequency,
disbursed_amount=None,
moratorium_tenure=None,
treatment_of_interest=None,
loan_disbursement=None,
):
frappe.get_doc(
{
"doctype": "Loan Repayment Schedule",
"loan": loan,
"repayment_method": repayment_method,
"repayment_start_date": start_date,
"repayment_periods": repayment_periods,
"monthly_repayment_amount": frequency_repayment_amount,
"posting_date": posting_date,
"repayment_frequency": repayment_frequency,
"disbursed_amount": disbursed_amount,
"moratorium_tenure": moratorium_tenure,
"treatment_of_interest": treatment_of_interest,
"loan_disbursement": loan_disbursement,
}
).insert()


def update_draft_schedule(
loan,
repayment_method,
start_date,
repayment_periods,
frequency_repayment_amount,
posting_date,
repayment_frequency,
disbursed_amount=None,
moratorium_tenure=None,
treatment_of_interest=None,
loan_disbursement=None,
):
draft_schedule = frappe.db.get_value(
"Loan Repayment Schedule", {"loan": loan, "docstatus": 0}, "name"
)
if draft_schedule:
schedule = frappe.get_doc("Loan Repayment Schedule", draft_schedule)
schedule.update(
{
"loan": loan,
"repayment_periods": repayment_periods,
"repayment_method": repayment_method,
"repayment_start_date": start_date,
"posting_date": posting_date,
"monthly_repayment_amount": frequency_repayment_amount,
"repayment_frequency": repayment_frequency,
"disbursed_amount": disbursed_amount,
"moratorium_tenure": moratorium_tenure,
"treatment_of_interest": treatment_of_interest,
"loan_disbursement": loan_disbursement,
}
)
schedule.save()
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ lending.common.setup_filters("Loan Disbursement");

frappe.ui.form.on('Loan Disbursement', {
setup(frm) {
frm.ignore_doctypes_on_cancel_all = ["Loan Security Deposit"];
frm.ignore_doctypes_on_cancel_all = ["Loan Security Deposit", "Loan Repayment Schedule"];
},
refresh: function(frm) {
frm.set_query('against_loan', function() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
"applicant_type",
"loan_product",
"monthly_repayment_amount",
"tenure",
"column_break_4",
"company",
"applicant",
"repayment_schedule_type",
"repayment_frequency",
"repayment_method",
"repayment_start_date",
"is_term_loan",
"withhold_security_deposit",
"section_break_7",
Expand Down Expand Up @@ -226,12 +231,41 @@
"fieldtype": "Table",
"label": "Loan Disbursement Charges",
"options": "Loan Disbursement Charge"
},
{
"fieldname": "repayment_frequency",
"fieldtype": "Select",
"label": "Repayment Frequency",
"options": "Monthly\nDaily\nWeekly\nQuarterly\nOne Time"
},
{
"fetch_from": "against_loan.repayment_schedule_type",
"fieldname": "repayment_schedule_type",
"fieldtype": "Data",
"label": "Repayment Schedule Type"
},
{
"fieldname": "repayment_start_date",
"fieldtype": "Date",
"label": "Repayment Start Date"
},
{
"fieldname": "tenure",
"fieldtype": "Int",
"label": "Tenure",
"mandatory_depends_on": "eval:doc.repayment_schedule_type==\"Line of Credit\""
},
{
"fieldname": "repayment_method",
"fieldtype": "Select",
"label": "Repayment Method",
"options": "Repay Over Number of Periods\nRepay Fixed Amount per Period"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-10 19:19:44.826241",
"modified": "2023-11-02 15:16:01.606208",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Disbursement",
Expand Down
Loading

0 comments on commit e129e72

Please sign in to comment.