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

feat: one collateral to many loans #101

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions lending/loan_management/doctype/loan/loan.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ frappe.ui.form.on('Loan', {
},__('Create'));
}

if (frm.doc.status == "Loan Closure Requested" && frm.doc.is_term_loan && !frm.doc.is_secured_loan) {
if (frm.doc.status == "Loan Closure Requested" && frm.doc.is_term_loan && frm.doc.loan_security_preference === "Unsecured") {
frm.add_custom_button(__('Close Loan'), function() {
frm.trigger("close_unsecured_term_loan");
},__('Status'));
Expand Down Expand Up @@ -245,13 +245,13 @@ frappe.ui.form.on('Loan', {
if (!r.exc && r.message) {

let loan_fields = ["loan_product", "loan_amount", "repayment_method",
"monthly_repayment_amount", "repayment_periods", "rate_of_interest", "is_secured_loan"]
"monthly_repayment_amount", "repayment_periods", "rate_of_interest", "loan_security_preference"]

loan_fields.forEach(field => {
frm.set_value(field, r.message[field]);
});

if (frm.doc.is_secured_loan) {
if (frm.doc.loan_security_preference !== "Unsecured") {
$.each(r.message.proposed_pledges, function(i, d) {
let row = frm.add_child("securities");
row.loan_security = d.loan_security;
Expand Down
19 changes: 10 additions & 9 deletions lending/loan_management/doctype/loan/loan.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"repayment_schedule_type",
"loan_amount",
"rate_of_interest",
"is_secured_loan",
"loan_security_preference",
"disbursement_date",
"closure_date",
"disbursed_amount",
Expand Down Expand Up @@ -288,12 +288,6 @@
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fieldname": "is_secured_loan",
"fieldtype": "Check",
"label": "Is Secured Loan"
},
{
"default": "0",
"fetch_from": "loan_product.is_term_loan",
Expand Down Expand Up @@ -328,7 +322,7 @@
"read_only": 1
},
{
"depends_on": "eval:doc.is_secured_loan",
"depends_on": "eval:doc.loan_security_preference != \"Unsecured\"",
"fieldname": "maximum_loan_amount",
"fieldtype": "Currency",
"label": "Maximum Loan Amount",
Expand Down Expand Up @@ -474,12 +468,19 @@
"fieldname": "loan_classification_details_section",
"fieldtype": "Section Break",
"label": "Loan Classification Details"
},
{
"default": "Unsecured",
"fieldname": "loan_security_preference",
"fieldtype": "Select",
"label": "Loan Security Preference",
"options": "Unsecured\nSemi-secured\nSecured"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-11 13:26:31.406754",
"modified": "2023-10-18 05:45:54.077529",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
Expand Down
6 changes: 3 additions & 3 deletions lending/loan_management/doctype/loan/loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def validate_loan_amount(self):
frappe.throw(_("Loan amount is mandatory"))

def link_loan_security_pledge(self):
if self.is_secured_loan and self.loan_application:
if self.loan_security_preference != "Unsecured" and self.loan_application:
maximum_loan_value = frappe.db.get_value(
"Loan Security Pledge",
{"loan_application": self.loan_application, "status": "Requested"},
Expand Down Expand Up @@ -370,13 +370,13 @@ def get_loan_application(loan_application):
@frappe.whitelist()
def close_unsecured_term_loan(loan):
loan_details = frappe.db.get_value(
"Loan", {"name": loan}, ["status", "is_term_loan", "is_secured_loan"], as_dict=1
"Loan", {"name": loan}, ["status", "is_term_loan", "loan_security_preference"], as_dict=1
)

if (
loan_details.status == "Loan Closure Requested"
and loan_details.is_term_loan
and not loan_details.is_secured_loan
and loan_details.loan_security_preference == "Unsecured"
):
frappe.db.set_value("Loan", loan, "status", "Closed")
else:
Expand Down
39 changes: 24 additions & 15 deletions lending/loan_management/doctype/loan/test_loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,26 +109,26 @@ def setUp(self):
"Penalty Income Account - _TC",
)

create_loan_security_type()
create_loan_security()

create_loan_security_price(
"Test Security 1", 500, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
)
create_loan_security_price(
"Test Security 2", 250, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
)

self.applicant1 = make_employee("[email protected]")
if not frappe.db.exists("Customer", "_Test Loan Customer"):
frappe.get_doc(get_customer_dict("_Test Loan Customer")).insert(ignore_permissions=True)

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.applicant1 = make_employee("[email protected]")
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_security_type()
create_loan_security(self.applicant2)

create_loan_security_price(
"Test Security 1", 500, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
)
create_loan_security_price(
"Test Security 2", 250, "Nos", get_datetime(), get_datetime(add_to_date(nowdate(), hours=24))
)

def test_loan(self):
loan = create_loan(self.applicant1, "Personal Loan", 280000, "Repay Over Number of Periods", 20)

Expand Down Expand Up @@ -1114,11 +1114,12 @@ def create_loan_security_type():
"unit_of_measure": "Nos",
"haircut": 50.00,
"loan_to_value_ratio": 50,
"quantifiable": 1,
}
).insert(ignore_permissions=True)


def create_loan_security():
def create_loan_security(applicant):
if not frappe.db.exists("Loan Security", "Test Security 1"):
frappe.get_doc(
{
Expand All @@ -1128,6 +1129,10 @@ def create_loan_security():
"loan_security_name": "Test Security 1",
"unit_of_measure": "Nos",
"haircut": 50.00,
"loan_security_owner_type": "Customer",
"loan_security_owner": applicant,
"quantity": 5,
"original_security_price": 100,
}
).insert(ignore_permissions=True)

Expand All @@ -1140,6 +1145,10 @@ def create_loan_security():
"loan_security_name": "Test Security 2",
"unit_of_measure": "Nos",
"haircut": 50.00,
"loan_security_owner_type": "Customer",
"loan_security_owner": applicant,
"quantity": 5,
"original_security_price": 100,
}
).insert(ignore_permissions=True)

Expand Down Expand Up @@ -1236,7 +1245,7 @@ def create_loan_application(
loan_application.applicant = applicant
loan_application.loan_product = loan_product
loan_application.posting_date = posting_date or nowdate()
loan_application.is_secured_loan = 1
loan_application.loan_security_preference = "Secured"

if repayment_method:
loan_application.repayment_method = repayment_method
Expand Down Expand Up @@ -1306,7 +1315,7 @@ def create_loan_with_security(
"applicant": applicant,
"loan_product": loan_product,
"is_term_loan": 1,
"is_secured_loan": 1,
"loan_security_preference": "Secured",
"repayment_method": repayment_method,
"repayment_periods": repayment_periods,
"repayment_start_date": repayment_start_date or nowdate(),
Expand Down Expand Up @@ -1335,7 +1344,7 @@ def create_demand_loan(applicant, loan_product, loan_application, posting_date=N
"applicant": applicant,
"loan_product": loan_product,
"is_term_loan": 0,
"is_secured_loan": 1,
"loan_security_preference": "Secured",
"mode_of_payment": frappe.db.get_value("Mode of Payment", {"type": "Cash"}, "name"),
"payment_account": "Payment Account - _TC",
"loan_account": "Loan Account - _TC",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ frappe.ui.form.on('Loan Application', {
add_toolbar_buttons: function(frm) {
if (frm.doc.status == "Approved") {

if (frm.doc.is_secured_loan) {
if (frm.doc.loan_security_preference !== "Unsecured") {
frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => {
if (Object.keys(r).length === 0) {
frm.add_custom_button(__('Loan Security Pledge'), function() {
Expand Down Expand Up @@ -71,7 +71,7 @@ frappe.ui.form.on('Loan Application', {
},
create_loan_security_pledge: function(frm) {

if(!frm.doc.is_secured_loan) {
if(frm.doc.loan_security_preference === "Unsecured") {
frappe.throw(__("Loan Security Pledge can only be created for secured loans"));
}

Expand All @@ -89,8 +89,12 @@ frappe.ui.form.on('Loan Application', {
frm.set_df_property('repayment_method', 'hidden', 1 - frm.doc.is_term_loan);
frm.set_df_property('repayment_method', 'reqd', frm.doc.is_term_loan);
},
is_secured_loan: function(frm) {
frm.set_df_property('proposed_pledges', 'reqd', frm.doc.is_secured_loan);
loan_security_preference: function(frm) {
if (frm.doc.loan_security_preference === "Unsecured") {
frm.set_df_property('proposed_pledges', 'reqd', 0);
} else {
frm.set_df_property('proposed_pledges', 'reqd', 1);
}
},

calculate_amounts: function(frm, cdt, cdn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"loan_product",
"is_term_loan",
"loan_amount",
"is_secured_loan",
"loan_security_preference",
"rate_of_interest",
"column_break_7",
"description",
Expand Down Expand Up @@ -178,19 +178,13 @@
"read_only": 1
},
{
"default": "0",
"fieldname": "is_secured_loan",
"fieldtype": "Check",
"label": "Is Secured Loan"
},
{
"depends_on": "eval:doc.is_secured_loan == 1",
"depends_on": "eval:doc.loan_security_preference != \"Unsecured\"",
"fieldname": "loan_security_details_section",
"fieldtype": "Section Break",
"label": "Loan Security Details"
},
{
"depends_on": "eval:doc.is_secured_loan == 1",
"depends_on": "eval:doc.loan_security_preference != \"Unsecured\"",
"fieldname": "proposed_pledges",
"fieldtype": "Table",
"label": "Proposed Pledges",
Expand All @@ -210,15 +204,23 @@
"fieldtype": "Check",
"label": "Is Term Loan",
"read_only": 1
},
{
"default": "Unsecured",
"fieldname": "loan_security_preference",
"fieldtype": "Select",
"label": "Loan Security Preference",
"options": "Unsecured\nSemi-secured\nSecured"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-10-02 22:14:22.606618",
"modified": "2023-10-18 05:33:57.484668",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Application",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,17 @@ def calculate_payable_amount(self):
self.total_payable_amount = self.loan_amount + self.total_payable_interest

def set_loan_amount(self):
if self.is_secured_loan and not self.proposed_pledges:
if self.loan_security_preference != "Unsecured" and not self.proposed_pledges:
frappe.throw(_("Proposed Pledges are mandatory for secured Loans"))

if self.is_secured_loan and self.proposed_pledges:
if self.loan_security_preference != "Unsecured" and self.proposed_pledges:
self.maximum_loan_amount = 0
for security in self.proposed_pledges:
self.maximum_loan_amount += flt(security.post_haircut_amount)

if not self.loan_amount and self.is_secured_loan and self.proposed_pledges:
if (
not self.loan_amount and self.loan_security_preference != "Unsecured" and self.proposed_pledges
):
self.loan_amount = self.maximum_loan_amount


Expand All @@ -170,7 +172,7 @@ def update_accounts(source_doc, target_doc, source_parent):
filters={"name": source_doc.loan_product},
)[0]

if source_doc.is_secured_loan:
if source_doc.loan_security_preference != "Unsecured":
target_doc.maximum_loan_amount = 0

target_doc.mode_of_payment = account_details.mode_of_payment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def set_status_and_amounts(self, cancel=0):
"total_interest_payable",
"status",
"is_term_loan",
"is_secured_loan",
"loan_security_preference",
],
as_dict=1,
)
Expand Down
Loading
Loading