From de301bc475c3e350aca24ec0845ae24c9e12e6a4 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Thu, 19 Oct 2023 07:42:13 +0530 Subject: [PATCH 1/5] feat: loan collateral --- lending/loan_management/doctype/loan/loan.js | 33 ++- .../loan_management/doctype/loan/loan.json | 11 +- lending/loan_management/doctype/loan/loan.py | 62 +++- .../loan_management/doctype/loan/test_loan.py | 3 + .../loan_application/loan_application.js | 3 - .../loan_application/loan_application.json | 40 ++- .../loan_application/loan_application.py | 63 ++-- .../doctype/loan_collateral/__init__.py | 0 .../loan_collateral/loan_collateral.js | 26 ++ .../loan_collateral/loan_collateral.json | 154 ++++++++++ .../loan_collateral/loan_collateral.py | 278 ++++++++++++++++++ .../loan_collateral/loan_collateral_list.js | 11 + .../loan_collateral/test_loan_collateral.py | 9 + .../__init__.py | 0 ...collateral_assignment_loan_collateral.json | 50 ++++ ...n_collateral_assignment_loan_collateral.py | 9 + .../__init__.py | 0 ...llateral_deassignment_loan_collateral.json | 40 +++ ...collateral_deassignment_loan_collateral.py | 9 + .../__init__.py | 0 .../loan_collateral_third_party_owner.js | 8 + .../loan_collateral_third_party_owner.json | 55 ++++ .../loan_collateral_third_party_owner.py | 9 + .../test_loan_collateral_third_party_owner.py | 9 + .../loan_collateral_values_log/__init__.py | 0 .../loan_collateral_values_log.js | 8 + .../loan_collateral_values_log.json | 113 +++++++ .../loan_collateral_values_log.py | 36 +++ .../test_loan_collateral_values_log.py | 9 + .../loan_disbursement/loan_disbursement.py | 63 +++- .../doctype/loan_repayment/loan_repayment.py | 16 + .../loan_security_pledge.js | 34 ++- .../loan_security_pledge.json | 113 ++++--- .../loan_security_pledge.py | 67 ++++- .../loan_security_unpledge.json | 36 ++- .../loan_security_unpledge.py | 58 +++- .../proposed_pledge/proposed_pledge.json | 6 +- lending/patches.txt | 3 +- lending/patches/v15_0/set_collateral_type.py | 21 ++ 39 files changed, 1312 insertions(+), 153 deletions(-) create mode 100644 lending/loan_management/doctype/loan_collateral/__init__.py create mode 100644 lending/loan_management/doctype/loan_collateral/loan_collateral.js create mode 100644 lending/loan_management/doctype/loan_collateral/loan_collateral.json create mode 100644 lending/loan_management/doctype/loan_collateral/loan_collateral.py create mode 100644 lending/loan_management/doctype/loan_collateral/loan_collateral_list.js create mode 100644 lending/loan_management/doctype/loan_collateral/test_loan_collateral.py create mode 100644 lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/__init__.py create mode 100644 lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.json create mode 100644 lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.py create mode 100644 lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/__init__.py create mode 100644 lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.json create mode 100644 lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.py create mode 100644 lending/loan_management/doctype/loan_collateral_third_party_owner/__init__.py create mode 100644 lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.js create mode 100644 lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.json create mode 100644 lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.py create mode 100644 lending/loan_management/doctype/loan_collateral_third_party_owner/test_loan_collateral_third_party_owner.py create mode 100644 lending/loan_management/doctype/loan_collateral_values_log/__init__.py create mode 100644 lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.js create mode 100644 lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.json create mode 100644 lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.py create mode 100644 lending/loan_management/doctype/loan_collateral_values_log/test_loan_collateral_values_log.py create mode 100644 lending/patches/v15_0/set_collateral_type.py diff --git a/lending/loan_management/doctype/loan/loan.js b/lending/loan_management/doctype/loan/loan.js index f9f72b5e..ac3a8e2d 100644 --- a/lending/loan_management/doctype/loan/loan.js +++ b/lending/loan_management/doctype/loan/loan.js @@ -245,23 +245,34 @@ 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", "is_secured_loan", "collateral_type"] loan_fields.forEach(field => { frm.set_value(field, r.message[field]); }); if (frm.doc.is_secured_loan) { - $.each(r.message.proposed_pledges, function(i, d) { - let row = frm.add_child("securities"); - row.loan_security = d.loan_security; - row.qty = d.qty; - row.loan_security_price = d.loan_security_price; - row.amount = d.amount; - row.haircut = d.haircut; - }); - - frm.refresh_fields("securities"); + if (frm.doc.collateral_type === "Loan Security") { + $.each(r.message.proposed_pledges, function(i, d) { + let row = frm.add_child("securities"); + row.loan_security = d.loan_security; + row.qty = d.qty; + row.loan_security_price = d.loan_security_price; + row.amount = d.amount; + row.haircut = d.haircut; + }); + + frm.refresh_fields("securities"); + } else if (frm.doc.collateral_type === "Loan Collateral") { + $.each(r.message.proposed_collaterals, function(i, d) { + let row = frm.add_child("collaterals"); + row.loan_collateral = d.loan_collateral; + row.loan_collateral_name = d.loan_collateral_name; + row.available_collateral_value = d.available_collateral_value; + }); + + frm.refresh_fields("collaterals"); + } } } } diff --git a/lending/loan_management/doctype/loan/loan.json b/lending/loan_management/doctype/loan/loan.json index c1600f3c..be7b8247 100644 --- a/lending/loan_management/doctype/loan/loan.json +++ b/lending/loan_management/doctype/loan/loan.json @@ -23,6 +23,7 @@ "loan_amount", "rate_of_interest", "is_secured_loan", + "collateral_type", "disbursement_date", "closure_date", "disbursed_amount", @@ -474,12 +475,20 @@ "fieldname": "loan_classification_details_section", "fieldtype": "Section Break", "label": "Loan Classification Details" + }, + { + "depends_on": "eval:doc.is_secured_loan", + "fieldname": "collateral_type", + "fieldtype": "Select", + "label": "Collateral Type", + "mandatory_depends_on": "eval:doc.is_secured_loan", + "options": "\nLoan Security\nLoan Collateral" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-10-11 13:26:31.406754", + "modified": "2023-10-19 05:37:08.804432", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/lending/loan_management/doctype/loan/loan.py b/lending/loan_management/doctype/loan/loan.py index 2411c43c..648648e5 100644 --- a/lending/loan_management/doctype/loan/loan.py +++ b/lending/loan_management/doctype/loan/loan.py @@ -22,6 +22,9 @@ from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry from erpnext.controllers.accounts_controller import AccountsController +from lending.loan_management.doctype.loan_collateral.loan_collateral import ( + get_pending_deassignment_collaterals, +) from lending.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import ( get_pledged_security_qty, ) @@ -458,31 +461,55 @@ def make_loan_write_off(loan, company=None, posting_date=None, amount=0, as_dict @frappe.whitelist() def unpledge_security( - loan=None, loan_security_pledge=None, security_map=None, as_dict=0, save=0, submit=0, approve=0 + loan=None, + loan_security_pledge=None, + security_map=None, + collaterals=None, + collateral_type=None, + as_dict=0, + save=0, + submit=0, + approve=0, ): # if no security_map is passed it will be considered as full unpledge if security_map and isinstance(security_map, str): security_map = json.loads(security_map) if loan: - pledge_qty_map = security_map or get_pledged_security_qty(loan) - loan_doc = frappe.get_doc("Loan", loan) + loan_company, loan_applicant_type, loan_applicant, loan_collateral_type = frappe.db.get_value( + "Loan", loan, ["company", "applicant_type", "applicant", "collateral_type"] + ) + + unpledge_map = None + deassignment_collaterals = None + + if loan_collateral_type == "Loan Security": + unpledge_map = security_map or get_pledged_security_qty(loan) + else: + deassignment_collaterals = collaterals or get_pending_deassignment_collaterals(loan) unpledge_request = create_loan_security_unpledge( - pledge_qty_map, loan_doc.name, loan_doc.company, loan_doc.applicant_type, loan_doc.applicant + loan, + loan_company, + loan_applicant_type, + loan_applicant, + loan_collateral_type, + unpledge_map, + deassignment_collaterals, ) # will unpledge qty based on loan security pledge elif loan_security_pledge: - security_map = {} + unpledge_map = {} pledge_doc = frappe.get_doc("Loan Security Pledge", loan_security_pledge) for security in pledge_doc.securities: - security_map.setdefault(security.loan_security, security.qty) + unpledge_map.setdefault(security.loan_security, security.qty) unpledge_request = create_loan_security_unpledge( - security_map, pledge_doc.loan, pledge_doc.company, pledge_doc.applicant_type, pledge_doc.applicant, + collateral_type, + unpledge_map, ) if save: @@ -504,16 +531,29 @@ def unpledge_security( return unpledge_request -def create_loan_security_unpledge(unpledge_map, loan, company, applicant_type, applicant): +def create_loan_security_unpledge( + loan, + company, + applicant_type, + applicant, + collateral_type, + unpledge_map=None, + deassignment_collaterals=None, +): unpledge_request = frappe.new_doc("Loan Security Unpledge") unpledge_request.applicant_type = applicant_type unpledge_request.applicant = applicant unpledge_request.loan = loan unpledge_request.company = company + unpledge_request.collateral_type = collateral_type - for security, qty in unpledge_map.items(): - if qty: - unpledge_request.append("securities", {"loan_security": security, "qty": qty}) + if collateral_type == "Loan Security": + for security, qty in unpledge_map.items(): + if qty: + unpledge_request.append("securities", {"loan_security": security, "qty": qty}) + else: + for loan_collateral in deassignment_collaterals: + unpledge_request.append("collaterals", {"loan_collateral": loan_collateral}) return unpledge_request diff --git a/lending/loan_management/doctype/loan/test_loan.py b/lending/loan_management/doctype/loan/test_loan.py index ed1c2de9..b35cc4ee 100644 --- a/lending/loan_management/doctype/loan/test_loan.py +++ b/lending/loan_management/doctype/loan/test_loan.py @@ -1237,6 +1237,7 @@ def create_loan_application( loan_application.loan_product = loan_product loan_application.posting_date = posting_date or nowdate() loan_application.is_secured_loan = 1 + loan_application.collateral_type = "Loan Security" if repayment_method: loan_application.repayment_method = repayment_method @@ -1307,6 +1308,7 @@ def create_loan_with_security( "loan_product": loan_product, "is_term_loan": 1, "is_secured_loan": 1, + "collateral_type": "Loan Security", "repayment_method": repayment_method, "repayment_periods": repayment_periods, "repayment_start_date": repayment_start_date or nowdate(), @@ -1336,6 +1338,7 @@ def create_demand_loan(applicant, loan_product, loan_application, posting_date=N "loan_product": loan_product, "is_term_loan": 0, "is_secured_loan": 1, + "collateral_type": "Loan Security", "mode_of_payment": frappe.db.get_value("Mode of Payment", {"type": "Cash"}, "name"), "payment_account": "Payment Account - _TC", "loan_account": "Loan Account - _TC", diff --git a/lending/loan_management/doctype/loan_application/loan_application.js b/lending/loan_management/doctype/loan_application/loan_application.js index 96136cee..37203ae5 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.js +++ b/lending/loan_management/doctype/loan_application/loan_application.js @@ -89,9 +89,6 @@ 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); - }, calculate_amounts: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; diff --git a/lending/loan_management/doctype/loan_application/loan_application.json b/lending/loan_management/doctype/loan_application/loan_application.json index b2089cac..8619560a 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.json +++ b/lending/loan_management/doctype/loan_application/loan_application.json @@ -17,12 +17,16 @@ "loan_product", "is_term_loan", "loan_amount", - "is_secured_loan", "rate_of_interest", + "is_secured_loan", + "collateral_type", "column_break_7", "description", "loan_security_details_section", "proposed_pledges", + "loan_collateral_details_section", + "proposed_collaterals", + "section_break_rutm", "maximum_loan_amount", "repayment_info", "repayment_method", @@ -184,16 +188,17 @@ "label": "Is Secured Loan" }, { - "depends_on": "eval:doc.is_secured_loan == 1", + "depends_on": "eval:doc.collateral_type == \"Loan Security\"", "fieldname": "loan_security_details_section", "fieldtype": "Section Break", "label": "Loan Security Details" }, { - "depends_on": "eval:doc.is_secured_loan == 1", + "depends_on": "eval:doc.collateral_type == \"Loan Security\"", "fieldname": "proposed_pledges", "fieldtype": "Table", "label": "Proposed Pledges", + "mandatory_depends_on": "eval:doc.collateral_type == \"Loan Security\"", "options": "Proposed Pledge" }, { @@ -210,15 +215,42 @@ "fieldtype": "Check", "label": "Is Term Loan", "read_only": 1 + }, + { + "depends_on": "eval:doc.is_secured_loan", + "fieldname": "collateral_type", + "fieldtype": "Select", + "label": "Collateral Type", + "mandatory_depends_on": "eval:doc.is_secured_loan", + "options": "\nLoan Security\nLoan Collateral" + }, + { + "depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "fieldname": "loan_collateral_details_section", + "fieldtype": "Section Break", + "label": "Loan Collateral Details" + }, + { + "fieldname": "section_break_rutm", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "fieldname": "proposed_collaterals", + "fieldtype": "Table", + "label": "Proposed Collaterals", + "mandatory_depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "options": "Loan Collateral Assignment Loan Collateral" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-10-02 22:14:22.606618", + "modified": "2023-10-19 04:46:39.891046", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Application", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { diff --git a/lending/loan_management/doctype/loan_application/loan_application.py b/lending/loan_management/doctype/loan_application/loan_application.py index 067f03c4..c26152bf 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.py +++ b/lending/loan_management/doctype/loan_application/loan_application.py @@ -143,15 +143,27 @@ 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: - frappe.throw(_("Proposed Pledges are mandatory for secured Loans")) - - if self.is_secured_loan 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 self.is_secured_loan: + if self.collateral_type == "Loan Security" and not self.proposed_pledges: + frappe.throw(_("Proposed Pledges are mandatory for secured Loans")) + if self.collateral_type == "Loan Collateral" and not self.proposed_collaterals: + frappe.throw(_("Proposed Collaterals are mandatory for secured Loans")) + + if self.is_secured_loan: + if self.proposed_pledges: + self.maximum_loan_amount = 0 + for security in self.proposed_pledges: + self.maximum_loan_amount += flt(security.post_haircut_amount) + else: + self.maximum_loan_amount = 0 + for collateral in self.proposed_collaterals: + self.maximum_loan_amount += flt(collateral.available_collateral_value) + + if ( + not self.loan_amount + and self.is_secured_loan + and (self.proposed_pledges or self.proposed_collaterals) + ): self.loan_amount = self.maximum_loan_amount @@ -208,21 +220,32 @@ def create_pledge(loan_application, loan=None): lsp.applicant = loan_application_doc.applicant lsp.loan_application = loan_application_doc.name lsp.company = loan_application_doc.company + lsp.collateral_type = loan_application_doc.collateral_type if loan: lsp.loan = loan - for pledge in loan_application_doc.proposed_pledges: - - lsp.append( - "securities", - { - "loan_security": pledge.loan_security, - "qty": pledge.qty, - "loan_security_price": pledge.loan_security_price, - "haircut": pledge.haircut, - }, - ) + if lsp.collateral_type == "Loan Security": + for security in loan_application_doc.proposed_pledges: + lsp.append( + "securities", + { + "loan_security": security.loan_security, + "qty": security.qty, + "loan_security_price": security.loan_security_price, + "haircut": security.haircut, + }, + ) + else: + for collateral in loan_application_doc.proposed_collaterals: + lsp.append( + "collaterals", + { + "loan_security": collateral.loan_collateral, + "loan_collateral_name": collateral.loan_collateral_name, + "available_collateral_value": collateral.available_collateral_value, + }, + ) lsp.save() lsp.submit() diff --git a/lending/loan_management/doctype/loan_collateral/__init__.py b/lending/loan_management/doctype/loan_collateral/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lending/loan_management/doctype/loan_collateral/loan_collateral.js b/lending/loan_management/doctype/loan_collateral/loan_collateral.js new file mode 100644 index 00000000..5b4daea3 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral/loan_collateral.js @@ -0,0 +1,26 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Loan Collateral', { + refresh: function(frm) { + if (frm.doc.status === "Hypothecated") { + frm.add_custom_button(__("Release"), function() { + frm.trigger("release_loan_collateral"); + }) + } + }, + + release_loan_collateral: function(frm) { + frappe.confirm(__("Do you really want to release this loan collateral?"), function () { + frappe.call({ + args: { + "loan_collateral": frm.doc.name, + }, + method: "lending.loan_management.doctype.loan_collateral.loan_collateral.release_loan_collateral", + callback: function(r) { + cur_frm.reload_doc(); + } + }) + }) + }, +}); \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral/loan_collateral.json b/lending/loan_management/doctype/loan_collateral/loan_collateral.json new file mode 100644 index 00000000..b84a378e --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral/loan_collateral.json @@ -0,0 +1,154 @@ +{ + "actions": [], + "autoname": "field:collateral_code", + "creation": "2023-10-18 22:17:56.180433", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "collateral_code", + "collateral_name", + "status", + "collateral_value", + "available_collateral_value", + "utilized_collateral_value", + "column_break_njyy", + "loan_applicant_type", + "loan_applicant", + "collateral_owner_type", + "collateral_owner", + "amended_from", + "released_date", + "disabled" + ], + "fields": [ + { + "fieldname": "collateral_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Collateral Code", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Collateral", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "collateral_name", + "fieldtype": "Data", + "label": "Collateral Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "column_break_njyy", + "fieldtype": "Column Break" + }, + { + "default": "Pending Hypothecation", + "depends_on": "eval:!doc.__islocal", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Pending Hypothecation\nHypothecated\nReleased\nRepossessed", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "depends_on": "eval:!doc.__islocal", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "collateral_value", + "fieldtype": "Currency", + "label": "Collateral Value", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fieldname": "available_collateral_value", + "fieldtype": "Currency", + "label": "Available Collateral Value", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "utilized_collateral_value", + "fieldtype": "Currency", + "label": "Utilized Collateral Value", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "loan_applicant_type", + "fieldtype": "Select", + "label": "Loan Applicant Type", + "options": "Employee\nMember\nCustomer", + "reqd": 1 + }, + { + "fieldname": "loan_applicant", + "fieldtype": "Dynamic Link", + "label": "Loan Applicant", + "options": "loan_applicant_type", + "reqd": 1 + }, + { + "fieldname": "collateral_owner_type", + "fieldtype": "Select", + "label": "Collateral Owner Type", + "options": "Employee\nMember\nCustomer\nLoan Collateral Third Party Owner\nCompany", + "reqd": 1 + }, + { + "fieldname": "collateral_owner", + "fieldtype": "Dynamic Link", + "label": "Collateral Owner", + "options": "collateral_owner_type", + "reqd": 1 + }, + { + "fieldname": "released_date", + "fieldtype": "Date", + "label": "Released Date", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2023-10-18 22:48:08.914757", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Collateral", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral/loan_collateral.py b/lending/loan_management/doctype/loan_collateral/loan_collateral.py new file mode 100644 index 00000000..86e0a41b --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral/loan_collateral.py @@ -0,0 +1,278 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import itertools + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt, nowdate + +from lending.loan_management.doctype.loan_collateral_values_log.loan_collateral_values_log import ( + create_loan_collateral_values_log, +) + + +class LoanCollateral(Document): + def validate(self): + self.set_available_collateral_value() + + def set_available_collateral_value(self): + self.available_collateral_value = self.collateral_value + + +def update_loan_collaterals_values( + loan, + amount, + trigger_doctype, + trigger_doc, + disbursement=False, + repayment=False, + on_trigger_doc_cancel=0, +): + if (disbursement and repayment) or (not disbursement and not repayment): + frappe.throw(_("The action needs to be either disbursement or repayment")) + + if not frappe.db.get_value("Loan", loan, "is_secured_loan"): + return + + utilized_value_increased = ( + True + if (disbursement and not on_trigger_doc_cancel) or (repayment and on_trigger_doc_cancel) + else False + ) + + sorted_loan_collaterals = _get_sorted_loan_collaterals_to_update_values( + loan, utilized_value_increased + ) + + _update_loan_collaterals_values( + sorted_loan_collaterals, + amount, + utilized_value_increased, + trigger_doctype, + trigger_doc, + on_trigger_doc_cancel, + ) + + +def _get_sorted_loan_collaterals_to_update_values(loan, utilized_value_increased): + loan_collaterals_w_ratio = [] + + for loan_security_pledge in frappe.db.get_all( + "Loan Security Pledge", {"loan": loan}, pluck="name" + ): + loan_collaterals = frappe.db.get_all( + "Loan Collateral Assignment Loan Collateral", + {"parent": loan_security_pledge}, + pluck="loan_collateral", + ) + + for loan_collateral in loan_collaterals: + ( + collateral_value, + available_collateral_value, + utilized_collateral_value, + ) = frappe.db.get_value( + "Loan Collateral", + loan_collateral, + [ + "collateral_value", + "available_collateral_value", + "utilized_collateral_value", + ], + ) + + utilized_to_original_value_ratio = flt(utilized_collateral_value / collateral_value) + + loan_collaterals_w_ratio.append( + frappe._dict( + { + "loan_collateral": loan_collateral, + "collateral_value": collateral_value, + "utilized_collateral_value": utilized_collateral_value, + "available_collateral_value": available_collateral_value, + "ratio": utilized_to_original_value_ratio, + } + ) + ) + + sorted_loan_collaterals = sorted( + loan_collaterals_w_ratio, + key=lambda k: k["ratio"], + reverse=utilized_value_increased, + ) + + return sorted_loan_collaterals + + +def _update_loan_collaterals_values( + sorted_loan_collaterals, + amount, + utilized_value_increased, + trigger_doctype, + trigger_doc, + on_trigger_doc_cancel, +): + for loan_collateral in sorted_loan_collaterals: + if amount <= 0: + break + + if utilized_value_increased: + if loan_collateral.utilized_collateral_value + amount > loan_collateral.collateral_value: + new_utilized_collateral_value = loan_collateral.collateral_value + new_available_collateral_value = 0 + amount = amount + loan_collateral.utilized_collateral_value - loan_collateral.collateral_value + else: + new_utilized_collateral_value = loan_collateral.utilized_collateral_value + amount + new_available_collateral_value = loan_collateral.available_collateral_value - amount + amount = 0 + else: + if loan_collateral.available_collateral_value + amount > loan_collateral.collateral_value: + new_available_collateral_value = loan_collateral.collateral_value + new_utilized_collateral_value = 0 + amount = amount + loan_collateral.available_collateral_value - loan_collateral.collateral_value + else: + new_utilized_collateral_value = loan_collateral.utilized_collateral_value - amount + new_available_collateral_value = loan_collateral.available_collateral_value + amount + amount = 0 + + frappe.db.set_value( + "Loan Collateral", + loan_collateral.loan_collateral, + { + "utilized_collateral_value": new_utilized_collateral_value, + "available_collateral_value": new_available_collateral_value, + }, + ) + + create_loan_collateral_values_log( + loan_collateral=loan_collateral.loan_collateral, + trigger_doctype=trigger_doctype, + trigger_document=trigger_doc, + on_trigger_doc_cancel=on_trigger_doc_cancel, + new_available_collateral_value=new_available_collateral_value, + new_utilized_collateral_value=new_utilized_collateral_value, + previous_available_collateral_value=loan_collateral.available_collateral_value, + previous_utilized_collateral_value=loan_collateral.utilized_collateral_value, + ) + + +@frappe.whitelist() +def release_loan_collateral(loan_collateral): + active_loan_collaterals = get_active_loan_collaterals(loan_collateral) + + if active_loan_collaterals: + msg = _("Loan Collateral {0} is linked with active loans:").format(frappe.bold(loan_collateral)) + for loan_and_pledge in active_loan_collaterals: + msg += "

" + msg += _("Loan {0} through Loan Security Pledge {1}").format( + frappe.bold(loan_and_pledge.loan), frappe.bold(loan_and_pledge.pledge) + ) + frappe.throw(msg, title=_("Loan Collateral cannot be released")) + else: + frappe.db.set_value( + "Loan Collateral", loan_collateral, {"status": "Released", "released_date": nowdate()} + ) + + +def get_active_loan_collaterals(loan_collateral): + active_loan_collaterals = [] + + lcalcs = frappe.db.sql( + """ + SELECT lp.loan, lp.name as pledge + FROM `tabLoan Security Pledge` lp, `tabLoan Collateral Assignment Loan Collateral` p + WHERE p.loan_collateral = %s + AND p.parent = lp.name + AND lp.status = 'Pledged' + """, + (loan_collateral), + as_dict=True, + ) + + lcdcs = frappe.db.sql( + """ + SELECT up.loan + FROM `tabLoan Security Unpledge` up, `tabLoan Collateral Deassignment Loan Collateral` u + WHERE u.loan_collateral = %s + AND u.parent = up.name + AND up.status = 'Approved' + """, + (loan_collateral), + as_list=True, + ) + lcdcs = list(itertools.chain(*lcdcs)) + + for loan_and_pledge in lcalcs: + if loan_and_pledge.loan not in lcdcs: + active_loan_collaterals.append(loan_and_pledge) + + return active_loan_collaterals + + +def check_loan_collaterals_availability(loan, amount=None): + loan_amount, is_secured_loan = frappe.db.get_value( + "Loan", loan, ["loan_amount", "is_secured_loan"] + ) + + if not is_secured_loan: + return + + amount = amount or loan_amount + + total_available_collateral_value = 0 + + loan_security_pledges = frappe.db.get_all( + "Loan Security Pledge", {"loan": loan, "collateral_type": "Loan Collateral"}, pluck="name" + ) + + if not loan_security_pledges: + return + + for loan_security_pledge in loan_security_pledges: + loan_collaterals = frappe.db.get_all( + "Loan Collateral Assignment Loan Collateral", + {"parent": loan_security_pledge}, + pluck="loan_collateral", + ) + for loan_collateral in loan_collaterals: + total_available_collateral_value += frappe.db.get_value( + "Loan Collateral", loan_collateral, "available_collateral_value" + ) + + if total_available_collateral_value < amount: + frappe.throw( + _("Loan Collaterals worth {0} needed more to book the loan.").format( + frappe.bold(amount - total_available_collateral_value), + ) + ) + + +@frappe.whitelist() +def get_pending_deassignment_collaterals(loan): + assignment_collaterals = frappe.db.sql( + """ + SELECT lcalc.loan_collateral + FROM `tabLoan Security Pledge` lsp, `tabLoan Collateral Assignment Loan Collateral` lcalc + WHERE lsp.loan = %s + AND lcalc.parent = lsp.name + """, + (loan), + as_list=True, + ) + assignment_collaterals = list(itertools.chain(*assignment_collaterals)) + + deassignment_collaterals = frappe.db.sql( + """ + SELECT lcdlc.loan_collateral + FROM `tabLoan Security Unpledge` lsu, `tabLoan Collateral Deassignment Loan Collateral` lcdlc + WHERE lsu.loan = %s + AND lcdlc.parent = lsu.name + """, + (loan), + as_list=True, + ) + deassignment_collaterals = list(itertools.chain(*deassignment_collaterals)) + + return list(set(assignment_collaterals) - set(deassignment_collaterals)) diff --git a/lending/loan_management/doctype/loan_collateral/loan_collateral_list.js b/lending/loan_management/doctype/loan_collateral/loan_collateral_list.js new file mode 100644 index 00000000..319e409c --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral/loan_collateral_list.js @@ -0,0 +1,11 @@ +frappe.listview_settings['Loan Collateral'] = { + get_indicator: function(doc) { + var status_color = { + "Pending Hypothecation": "grey", + "Hypothecated": "blue", + "Released": "green", + "Repossessed": "red" + }; + return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; + }, +}; \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral/test_loan_collateral.py b/lending/loan_management/doctype/loan_collateral/test_loan_collateral.py new file mode 100644 index 00000000..7fb7f900 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral/test_loan_collateral.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLoanCollateral(FrappeTestCase): + pass diff --git a/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/__init__.py b/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.json b/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.json new file mode 100644 index 00000000..f2842788 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-10-19 00:40:53.780767", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan_collateral", + "loan_collateral_name", + "available_collateral_value" + ], + "fields": [ + { + "fieldname": "loan_collateral", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan Collateral", + "options": "Loan Collateral", + "reqd": 1 + }, + { + "fetch_from": "loan_collateral.collateral_name", + "fieldname": "loan_collateral_name", + "fieldtype": "Data", + "label": "Loan Collateral Name" + }, + { + "fetch_from": "loan_collateral.available_collateral_value", + "fieldname": "available_collateral_value", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Available Collateral Value", + "options": "Company:company:default_currency", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-10-19 00:46:05.909347", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Collateral Assignment Loan Collateral", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.py b/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.py new file mode 100644 index 00000000..80cfa2c9 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_assignment_loan_collateral/loan_collateral_assignment_loan_collateral.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class LoanCollateralAssignmentLoanCollateral(Document): + pass diff --git a/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/__init__.py b/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.json b/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.json new file mode 100644 index 00000000..4ab0934c --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-10-19 03:33:30.702413", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan_collateral", + "loan_collateral_name" + ], + "fields": [ + { + "fieldname": "loan_collateral", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan Collateral", + "options": "Loan Collateral", + "reqd": 1 + }, + { + "fetch_from": "loan_collateral.collateral_name", + "fieldname": "loan_collateral_name", + "fieldtype": "Data", + "label": "Loan Collateral Name" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-10-19 03:33:49.442060", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Collateral Deassignment Loan Collateral", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.py b/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.py new file mode 100644 index 00000000..125a365e --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_deassignment_loan_collateral/loan_collateral_deassignment_loan_collateral.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class LoanCollateralDeassignmentLoanCollateral(Document): + pass diff --git a/lending/loan_management/doctype/loan_collateral_third_party_owner/__init__.py b/lending/loan_management/doctype/loan_collateral_third_party_owner/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.js b/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.js new file mode 100644 index 00000000..cb5c31b6 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Loan Collateral Third Party Owner", { +// refresh(frm) { + +// }, +// }); diff --git a/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.json b/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.json new file mode 100644 index 00000000..ac98a2b4 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:collateral_third_party_owner_code", + "creation": "2023-10-18 22:42:55.359452", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "collateral_third_party_owner_code", + "collateral_third_party_owner_name" + ], + "fields": [ + { + "fieldname": "collateral_third_party_owner_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Collateral Third Party Owner Code", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "collateral_third_party_owner_name", + "fieldtype": "Date", + "label": "Collateral Third Party Owner Name", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-10-18 22:43:12.150354", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Collateral Third Party Owner", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.py b/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.py new file mode 100644 index 00000000..c4d222a2 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_third_party_owner/loan_collateral_third_party_owner.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class LoanCollateralThirdPartyOwner(Document): + pass diff --git a/lending/loan_management/doctype/loan_collateral_third_party_owner/test_loan_collateral_third_party_owner.py b/lending/loan_management/doctype/loan_collateral_third_party_owner/test_loan_collateral_third_party_owner.py new file mode 100644 index 00000000..c0da3be8 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_third_party_owner/test_loan_collateral_third_party_owner.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLoanCollateralThirdPartyOwner(FrappeTestCase): + pass diff --git a/lending/loan_management/doctype/loan_collateral_values_log/__init__.py b/lending/loan_management/doctype/loan_collateral_values_log/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.js b/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.js new file mode 100644 index 00000000..e5618a86 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Loan Collateral Values Log", { +// refresh(frm) { + +// }, +// }); diff --git a/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.json b/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.json new file mode 100644 index 00000000..f3bf2e47 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.json @@ -0,0 +1,113 @@ +{ + "actions": [], + "creation": "2023-10-19 01:20:27.384150", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan_collateral", + "trigger_doctype", + "trigger_document_docstatus", + "column_break_hime", + "posting_date", + "trigger_document", + "section_break_roay", + "previous_utilized_collateral_value", + "previous_available_collateral_value", + "column_break_nkjt", + "new_utilized_collateral_value", + "new_available_collateral_value" + ], + "fields": [ + { + "fieldname": "loan_collateral", + "fieldtype": "Link", + "label": "Loan Collateral", + "options": "Loan Collateral" + }, + { + "fieldname": "trigger_doctype", + "fieldtype": "Link", + "label": "Trigger DocType", + "options": "DocType" + }, + { + "fieldname": "trigger_document", + "fieldtype": "Dynamic Link", + "label": "Trigger Document", + "options": "trigger_doctype" + }, + { + "fieldname": "trigger_document_docstatus", + "fieldtype": "Int", + "label": "Trigger Document Docstatus" + }, + { + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date" + }, + { + "fieldname": "column_break_hime", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_roay", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_nkjt", + "fieldtype": "Column Break" + }, + { + "fieldname": "previous_utilized_collateral_value", + "fieldtype": "Currency", + "label": "Previous Utilized Collateral Value", + "options": "Company:company:default_currency" + }, + { + "fieldname": "previous_available_collateral_value", + "fieldtype": "Currency", + "label": "Previous Available Collateral Value", + "options": "Company:company:default_currency" + }, + { + "fieldname": "new_utilized_collateral_value", + "fieldtype": "Currency", + "label": "New Utilized Collateral Value", + "options": "Company:company:default_currency" + }, + { + "fieldname": "new_available_collateral_value", + "fieldtype": "Currency", + "label": "New Available Collateral Value", + "options": "Company:company:default_currency" + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-10-19 01:27:42.731347", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Collateral Values Log", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.py b/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.py new file mode 100644 index 00000000..b5cb7088 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_values_log/loan_collateral_values_log.py @@ -0,0 +1,36 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from frappe.utils import nowdate + + +class LoanCollateralValuesLog(Document): + pass + + +def create_loan_collateral_values_log( + loan_collateral, + trigger_doctype, + trigger_document, + on_trigger_doc_cancel, + new_available_collateral_value, + new_utilized_collateral_value, + previous_available_collateral_value, + previous_utilized_collateral_value, +): + doc = frappe.new_doc("Loan Collateral Values Log") + + doc.loan_collateral = loan_collateral + doc.posting_date = nowdate() + doc.trigger_doctype = trigger_doctype + doc.trigger_document = trigger_document + doc.trigger_document_docstatus = 2 if on_trigger_doc_cancel else 1 + + doc.new_available_collateral_value = new_available_collateral_value + doc.new_utilized_collateral_value = new_utilized_collateral_value + doc.previous_available_collateral_value = previous_available_collateral_value + doc.previous_utilized_collateral_value = previous_utilized_collateral_value + + doc.insert() diff --git a/lending/loan_management/doctype/loan_collateral_values_log/test_loan_collateral_values_log.py b/lending/loan_management/doctype/loan_collateral_values_log/test_loan_collateral_values_log.py new file mode 100644 index 00000000..4d41bd68 --- /dev/null +++ b/lending/loan_management/doctype/loan_collateral_values_log/test_loan_collateral_values_log.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLoanCollateralValuesLog(FrappeTestCase): + pass diff --git a/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py b/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py index bcf178d4..4fee571d 100644 --- a/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -10,6 +10,10 @@ from erpnext.accounts.general_ledger import make_gl_entries from erpnext.controllers.accounts_controller import AccountsController +from lending.loan_management.doctype.loan_collateral.loan_collateral import ( + check_loan_collaterals_availability, + update_loan_collaterals_values, +) from lending.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import ( get_pledged_security_qty, ) @@ -22,6 +26,7 @@ class LoanDisbursement(AccountsController): def validate(self): self.set_missing_values() self.validate_disbursal_amount() + check_loan_collaterals_availability(self.against_loan, self.disbursed_amount) def on_submit(self): if self.is_term_loan: @@ -29,8 +34,34 @@ def on_submit(self): self.set_status_and_amounts() self.withheld_security_deposit() + + update_loan_collaterals_values( + self.against_loan, self.disbursed_amount, "Loan Disbursement", self.name, disbursement=True + ) + self.set_status_of_loan_collaterals() + self.make_gl_entries() + def on_cancel(self): + if self.is_term_loan: + self.update_repayment_schedule_status(cancel=1) + + self.delete_security_deposit() + self.set_status_and_amounts(cancel=1) + + update_loan_collaterals_values( + self.against_loan, + self.disbursed_amount, + "Loan Disbursement", + self.name, + disbursement=True, + on_trigger_doc_cancel=1, + ) + self.set_status_of_loan_collaterals(cancel=1) + + self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] + def update_repayment_schedule_status(self, cancel=0): if cancel: status = "Initiated" @@ -47,15 +78,6 @@ def update_repayment_schedule_status(self, cancel=0): frappe.db.set_value("Loan Repayment Schedule", schedule, "status", status) - def on_cancel(self): - if self.is_term_loan: - self.update_repayment_schedule_status(cancel=1) - - self.delete_security_deposit() - self.set_status_and_amounts(cancel=1) - self.make_gl_entries(cancel=1) - self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] - def set_missing_values(self): if not self.disbursement_date: self.disbursement_date = nowdate() @@ -93,6 +115,29 @@ def validate_disbursal_amount(self): elif self.disbursed_amount > possible_disbursal_amount: frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(possible_disbursal_amount)) + def set_status_of_loan_collaterals(self, cancel=0): + if not frappe.db.get_value("Loan", self.against_loan, "is_secured_loan"): + return + + if not cancel: + new_status = "Hypothecated" + old_status = "Pending Hypothecation" + else: + new_status = "Pending Hypothecation" + old_status = "Hypothecated" + + frappe.db.sql( + """ + UPDATE `tabLoan Collateral` + JOIN `tabLoan Collateral Assignment Loan Collateral` ON `tabLoan Collateral Assignment Loan Collateral`.`loan_collateral`=`tabLoan Collateral`.`name` + JOIN `tabLoan Security Pledge` ON `tabLoan Security Pledge`.`name`=`tabLoan Collateral Assignment Loan Collateral`.`parent` + JOIN `tabLoan` ON `tabLoan`.`name`=`tabLoan Security Pledge`.`loan` + SET `tabLoan Collateral`.`status`=%s + WHERE `tabLoan`.`name`=%s AND `tabLoan Collateral`.`status`=%s + """, + (new_status, self.against_loan, old_status), + ) + def set_status_and_amounts(self, cancel=0): loan_details = frappe.get_all( "Loan", diff --git a/lending/loan_management/doctype/loan_repayment/loan_repayment.py b/lending/loan_management/doctype/loan_repayment/loan_repayment.py index 9d9a7908..9e40d3c5 100644 --- a/lending/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/lending/loan_management/doctype/loan_repayment/loan_repayment.py @@ -11,6 +11,9 @@ from erpnext.controllers.accounts_controller import AccountsController from lending.loan_management.doctype.loan.loan import update_all_linked_loan_customer_npa_status +from lending.loan_management.doctype.loan_collateral.loan_collateral import ( + update_loan_collaterals_values, +) from lending.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import ( get_last_accrual_date, get_per_day_interest, @@ -53,6 +56,10 @@ def on_submit(self): if self.repayment_type == "Charges Waiver": self.make_credit_note() + update_loan_collaterals_values( + self.against_loan, self.amount_paid, "Loan Repayment", self.name, repayment=True + ) + self.make_gl_entries() def on_cancel(self): @@ -68,6 +75,15 @@ def on_cancel(self): frappe.db.set_value("Loan", self.against_loan, "days_past_due", self.days_past_due) + update_loan_collaterals_values( + self.against_loan, + self.amount_paid, + "Loan Repayment", + self.name, + repayment=True, + on_trigger_doc_cancel=1, + ) + self.ignore_linked_doctypes = [ "GL Entry", "Payment Ledger Entry", diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js b/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js index e61868e8..81f81d87 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js +++ b/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js @@ -2,7 +2,19 @@ // For license information, please see license.txt frappe.ui.form.on('Loan Security Pledge', { - calculate_amounts: function(frm, cdt, cdn) { + loan: function(frm, cdt, cdn) { + frappe.db.get_value("Loan", frm.doc.loan, "collateral_type", (r) => { + frm.set_value('collateral_type', r.collateral_type); + }); + }, + + loan_application: function(frm, cdt, cdn) { + frappe.db.get_value("Loan Application", frm.doc.loan_application, "collateral_type", (r) => { + frm.set_value('collateral_type', r.collateral_type); + }); + }, + + calculate_loan_securities_amounts: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'amount', row.qty * row.loan_security_price); frappe.model.set_value(cdt, cdn, 'post_haircut_amount', cint(row.amount - (row.amount * row.haircut/100))); @@ -16,6 +28,16 @@ frappe.ui.form.on('Loan Security Pledge', { frm.set_value('total_security_value', amount); frm.set_value('maximum_loan_value', maximum_amount); + }, + + calculate_loan_collaterals_amounts: function(frm, cdt, cdn) { + let amount = 0; + $.each(frm.doc.collaterals || [], function(i, item){ + amount += item.available_collateral_value; + }); + + frm.set_value('total_security_value', amount); + frm.set_value('maximum_loan_value', amount); } }); @@ -31,13 +53,19 @@ frappe.ui.form.on("Pledge", { }, callback: function(r) { frappe.model.set_value(cdt, cdn, 'loan_security_price', r.message); - frm.events.calculate_amounts(frm, cdt, cdn); + frm.events.calculate_loan_securities_amounts(frm, cdt, cdn); } }); } }, qty: function(frm, cdt, cdn) { - frm.events.calculate_amounts(frm, cdt, cdn); + frm.events.calculate_loan_securities_amounts(frm, cdt, cdn); + }, +}); + +frappe.ui.form.on("Loan Collateral Assignment Loan Collateral", { + loan_collateral: function(frm, cdt, cdn) { + frm.events.calculate_loan_collaterals_amounts(frm, cdt, cdn); }, }); diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json b/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json index 68bac8ed..3a5c6bcc 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json +++ b/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json @@ -11,12 +11,15 @@ "applicant", "loan", "loan_application", + "collateral_type", "column_break_3", "company", "pledge_time", "status", "loan_security_details_section", "securities", + "loan_collateral_details_section", + "collaterals", "section_break_10", "total_security_value", "column_break_11", @@ -35,9 +38,7 @@ "no_copy": 1, "options": "Loan Security Pledge", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fetch_from": "loan_application.applicant", @@ -47,63 +48,48 @@ "in_standard_filter": 1, "label": "Applicant", "options": "applicant_type", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { + "depends_on": "eval:doc.collateral_type == \"Loan Security\"", "fieldname": "loan_security_details_section", "fieldtype": "Section Break", - "label": "Loan Security Details", - "show_days": 1, - "show_seconds": 1 + "label": "Loan Security Details" }, { "fieldname": "column_break_3", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "loan", "fieldtype": "Link", "label": "Loan", - "options": "Loan", - "show_days": 1, - "show_seconds": 1 + "options": "Loan" }, { "fieldname": "loan_application", "fieldtype": "Link", "label": "Loan Application", - "options": "Loan Application", - "show_days": 1, - "show_seconds": 1 + "options": "Loan Application" }, { "fieldname": "total_security_value", "fieldtype": "Currency", "label": "Total Security Value", "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "maximum_loan_value", "fieldtype": "Currency", "label": "Maximum Loan Value", "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "loan_details_section", "fieldtype": "Section Break", - "label": "Loan Details", - "show_days": 1, - "show_seconds": 1 + "label": "Loan Details" }, { "default": "Requested", @@ -113,48 +99,37 @@ "in_standard_filter": 1, "label": "Status", "options": "Requested\nUnpledged\nPledged\nPartially Pledged\nCancelled", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "pledge_time", "fieldtype": "Datetime", "label": "Pledge Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { + "depends_on": "eval:doc.collateral_type == \"Loan Security\"", "fieldname": "securities", "fieldtype": "Table", "label": "Securities", - "options": "Pledge", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "mandatory_depends_on": "eval:doc.collateral_type == \"Loan Security\"", + "options": "Pledge" }, { "fieldname": "column_break_11", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "section_break_10", "fieldtype": "Section Break", - "label": "Totals", - "show_days": 1, - "show_seconds": 1 + "label": "Totals" }, { "fieldname": "company", "fieldtype": "Link", "label": "Company", "options": "Company", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fetch_from": "loan.applicant_type", @@ -162,48 +137,60 @@ "fieldtype": "Select", "label": "Applicant Type", "options": "Employee\nMember\nCustomer", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "collapsible": 1, "fieldname": "more_information_section", "fieldtype": "Section Break", - "label": "More Information", - "show_days": 1, - "show_seconds": 1 + "label": "More Information" }, { "allow_on_submit": 1, "fieldname": "reference_no", "fieldtype": "Data", - "label": "Reference No", - "show_days": 1, - "show_seconds": 1 + "label": "Reference No" }, { "fieldname": "column_break_18", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "allow_on_submit": 1, "fieldname": "description", "fieldtype": "Text", - "label": "Description", - "show_days": 1, - "show_seconds": 1 + "label": "Description" + }, + { + "fieldname": "collateral_type", + "fieldtype": "Select", + "label": "Collateral Type", + "options": "\nLoan Security\nLoan Collateral", + "read_only": 1 + }, + { + "depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "fieldname": "loan_collateral_details_section", + "fieldtype": "Section Break", + "label": "Loan Collateral Details" + }, + { + "depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "fieldname": "collaterals", + "fieldtype": "Table", + "label": "Collaterals", + "mandatory_depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "options": "Loan Collateral Assignment Loan Collateral" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-29 17:15:16.082256", + "modified": "2023-10-19 05:41:35.477481", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Security Pledge", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -237,9 +224,9 @@ "write": 1 } ], - "quick_entry": 1, "search_fields": "applicant", "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py index 5b68157a..30bd7458 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py +++ b/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py @@ -17,10 +17,19 @@ class LoanSecurityPledge(Document): def validate(self): + self.set_missing_values() self.set_pledge_amount() - self.validate_duplicate_securities() + self.validate_duplicate_securities_and_collaterals() + self.validate_loan_collaterals() self.validate_loan_security_type() + def set_missing_values(self): + if not self.collateral_type: + if self.loan: + self.collateral_type = frappe.db.get_value("Loan", self.loan, "collateral_type") + elif self.loan_application: + self.collateral_type = frappe.db.get_value("Loan", self.loan_application, "collateral_type") + def on_submit(self): if self.loan: self.db_set("status", "Pledged") @@ -34,7 +43,21 @@ def on_cancel(self): self.db_set("pledge_time", None) update_loan(self.loan, self.maximum_loan_value, cancel=1) - def validate_duplicate_securities(self): + def validate_loan_collaterals(self): + for collateral in self.collaterals: + status, loan_applicant = frappe.db.get_value( + "Loan Collateral", collateral, ["status", "loan_applicant"] + ) + if status in ["Released", "Repossessed"]: + frappe.throw( + _("Row {0}: released or repossessed collateral cannot be hypothecated").format(collateral.idx) + ) + if loan_applicant != self.applicant: + frappe.throw( + _("Row {0}: collateral applicant does not match loan applicant").format(collateral.idx) + ) + + def validate_duplicate_securities_and_collaterals(self): security_list = [] for security in self.securities: if security.loan_security not in security_list: @@ -44,7 +67,19 @@ def validate_duplicate_securities(self): _("Loan Security {0} added multiple times").format(frappe.bold(security.loan_security)) ) + collateral_list = [] + for collateral in self.collaterals: + if collateral.loan_collateral not in collateral_list: + collateral_list.append(collateral.loan_collateral) + else: + frappe.throw( + _("Loan Collateral {0} added multiple times").format(frappe.bold(collateral.loan_collateral)) + ) + def validate_loan_security_type(self): + if not self.collateral_type == "Loan Security": + return + existing_pledge = "" if self.loan: @@ -73,22 +108,26 @@ def set_pledge_amount(self): total_security_value = 0 maximum_loan_value = 0 - for pledge in self.securities: - - if not pledge.qty and not pledge.amount: - frappe.throw(_("Qty or Amount is mandatory for loan security!")) + if self.collateral_type == "Loan Security": + for pledge in self.securities: + if not pledge.qty and not pledge.amount: + frappe.throw(_("Qty or Amount is mandatory for loan security!")) - if not (self.loan_application and pledge.loan_security_price): - pledge.loan_security_price = get_loan_security_price(pledge.loan_security) + if not (self.loan_application and pledge.loan_security_price): + pledge.loan_security_price = get_loan_security_price(pledge.loan_security) - if not pledge.qty: - pledge.qty = cint(pledge.amount / pledge.loan_security_price) + if not pledge.qty: + pledge.qty = cint(pledge.amount / pledge.loan_security_price) - pledge.amount = pledge.qty * pledge.loan_security_price - pledge.post_haircut_amount = cint(pledge.amount - (pledge.amount * pledge.haircut / 100)) + pledge.amount = pledge.qty * pledge.loan_security_price + pledge.post_haircut_amount = cint(pledge.amount - (pledge.amount * pledge.haircut / 100)) - total_security_value += pledge.amount - maximum_loan_value += pledge.post_haircut_amount + total_security_value += pledge.amount + maximum_loan_value += pledge.post_haircut_amount + else: + for collateral in self.collaterals: + total_security_value += collateral.available_collateral_value + maximum_loan_value = total_security_value self.total_security_value = total_security_value self.maximum_loan_value = maximum_loan_value diff --git a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json b/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json index 92923bbf..51f226c8 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json +++ b/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json @@ -10,12 +10,15 @@ "loan", "applicant_type", "applicant", + "collateral_type", "column_break_3", "company", "unpledge_time", "status", "loan_security_details_section", "securities", + "loan_collateral_details_section", + "collaterals", "more_information_section", "reference_no", "column_break_13", @@ -65,6 +68,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.collateral_type == \"Loan Security\"", "fieldname": "loan_security_details_section", "fieldtype": "Section Break", "label": "Loan Security Details" @@ -79,11 +83,12 @@ "read_only": 1 }, { + "depends_on": "eval:doc.collateral_type == \"Loan Security\"", "fieldname": "securities", "fieldtype": "Table", "label": "Securities", - "options": "Unpledge", - "reqd": 1 + "mandatory_depends_on": "eval:doc.collateral_type == \"Loan Security\"", + "options": "Unpledge" }, { "fieldname": "company", @@ -121,15 +126,39 @@ "fieldname": "description", "fieldtype": "Text", "label": "Description" + }, + { + "fetch_from": "loan.collateral_type", + "fieldname": "collateral_type", + "fieldtype": "Select", + "label": "Collateral Type", + "options": "\nLoan Security\nLoan Collateral", + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "fieldname": "loan_collateral_details_section", + "fieldtype": "Section Break", + "label": "Loan Collateral Details" + }, + { + "depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "fieldname": "collaterals", + "fieldtype": "Table", + "label": "Collaterals", + "mandatory_depends_on": "eval:doc.collateral_type == \"Loan Collateral\"", + "options": "Loan Collateral Deassignment Loan Collateral" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-04-19 18:12:01.401744", + "modified": "2023-10-19 05:41:14.454180", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Security Unpledge", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -179,5 +208,6 @@ "search_fields": "applicant", "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py index a9edd3e0..64e4ca5b 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py +++ b/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py @@ -10,23 +10,31 @@ class LoanSecurityUnpledge(Document): def validate(self): - self.validate_duplicate_securities() + self.validate_duplicate_securities_and_collaterals() self.validate_unpledge_qty() + self.validate_unpledge_loan_collaterals() def on_cancel(self): self.update_loan_status(cancel=1) self.db_set("status", "Requested") - def validate_duplicate_securities(self): + def validate_duplicate_securities_and_collaterals(self): security_list = [] - for d in self.securities: - if d.loan_security not in security_list: - security_list.append(d.loan_security) + for security in self.securities: + if security.loan_security not in security_list: + security_list.append(security.loan_security) + else: + frappe.throw( + _("Loan Security {0} added multiple times").format(frappe.bold(security.loan_security)) + ) + + collateral_list = [] + for collateral in self.collaterals: + if collateral.loan_collateral not in collateral_list: + collateral_list.append(collateral.loan_collateral) else: frappe.throw( - _("Row {0}: Loan Security {1} added multiple times").format( - d.idx, frappe.bold(d.loan_security) - ) + _("Loan Collateral {0} added multiple times").format(frappe.bold(collateral.loan_collateral)) ) def validate_unpledge_qty(self): @@ -37,6 +45,9 @@ def validate_unpledge_qty(self): get_ltv_ratio, ) + if self.collateral_type != "Loan Security": + return + pledge_qty_map = get_pledged_security_qty(self.loan) ltv_ratio_map = frappe._dict( @@ -115,6 +126,37 @@ def _throw(self, security_value, pending_principal_amount, ltv_ratio): msg += _("Loan To Security Value ratio must always be {0}").format(frappe.bold(ltv_ratio)) frappe.throw(msg, title=_("Loan To Value ratio breach")) + def validate_unpledge_loan_collaterals(self): + from lending.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, + ) + + if self.collateral_type != "Loan Collateral": + return + + loan_details = frappe.get_value( + "Loan", + self.loan, + [ + "total_payment", + "debit_adjustment_amount", + "credit_adjustment_amount", + "refund_amount", + "total_principal_paid", + "loan_amount", + "total_interest_payable", + "written_off_amount", + "disbursed_amount", + "status", + ], + as_dict=1, + ) + + pending_principal_amount = get_pending_principal_amount(loan_details) + + if flt(pending_principal_amount, 2) > 0: + frappe.throw(_("Loan Collaterals cannot be unpledged since there is pending principal amount.")) + def on_update_after_submit(self): self.approve() diff --git a/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json b/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json index a0b3a79b..cffe917f 100644 --- a/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json +++ b/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json @@ -49,7 +49,8 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Loan Security", - "options": "Loan Security" + "options": "Loan Security", + "reqd": 1 }, { "fieldname": "post_haircut_amount", @@ -69,7 +70,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-01-17 07:29:01.671722", + "modified": "2023-10-19 04:47:47.266184", "modified_by": "Administrator", "module": "Loan Management", "name": "Proposed Pledge", @@ -78,5 +79,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/lending/patches.txt b/lending/patches.txt index e20037fa..eea2df32 100644 --- a/lending/patches.txt +++ b/lending/patches.txt @@ -26,4 +26,5 @@ lending.patches.v15_0.rename_loan_partner_charge_type lending.patches.v15_0.migrate_loan_type_to_loan_product lending.patches.v15_0.add_loan_product_code_and_rename_loan_name lending.patches.v15_0.update_penalty_interest_method_in_loan_products -lending.patches.v15_0.update_min_bpi_application_days \ No newline at end of file +lending.patches.v15_0.update_min_bpi_application_days +lending.patches.v15_0.set_collateral_type \ No newline at end of file diff --git a/lending/patches/v15_0/set_collateral_type.py b/lending/patches/v15_0/set_collateral_type.py new file mode 100644 index 00000000..caed9bea --- /dev/null +++ b/lending/patches/v15_0/set_collateral_type.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe + + +def execute(): + lsp = frappe.qb.DocType("Loan Security Pledge") + frappe.qb.update(lsp).set(lsp.collateral_type, "Loan Security").where( + lsp.collateral_type.isnull() + ).run() + + la = frappe.qb.DocType("Loan Application") + frappe.qb.update(la).set(la.collateral_type, "Loan Security").where( + la.is_secured_loan == 1 + ).where(la.collateral_type.isnull()).run() + + l = frappe.qb.DocType("Loan") + frappe.qb.update(l).set(l.collateral_type, "Loan Security").where(l.is_secured_loan == 1).where( + l.collateral_type.isnull() + ).run() From dc62bd81705b362ff0dd1cd51de5120e52115f6b Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Thu, 19 Oct 2023 08:28:38 +0530 Subject: [PATCH 2/5] chore: rename_loan_security_pledge_and_unpledge --- .github/helper/install.sh | 18 ++++----- .github/workflows/ci.yml | 2 +- .../top_10_pledged_loan_securities.py | 6 +-- lending/loan_management/doctype/loan/loan.js | 12 +++--- lending/loan_management/doctype/loan/loan.py | 40 ++++++++++--------- .../doctype/loan/loan_dashboard.py | 11 ++++- .../loan_management/doctype/loan/test_loan.py | 20 +++++----- .../loan_application/loan_application.js | 14 +++---- .../loan_application/loan_application.py | 4 +- .../loan_application_dashboard.py | 2 +- .../loan_collateral/loan_collateral.py | 28 ++++++------- .../__init__.py | 0 .../loan_collateral_assignment.js} | 2 +- .../loan_collateral_assignment.json} | 8 ++-- .../loan_collateral_assignment.py} | 6 +-- .../loan_collateral_assignment_list.js} | 7 ++-- .../test_loan_collateral_assignment.py} | 2 +- .../__init__.py | 0 .../loan_collateral_deassignment.js} | 2 +- .../loan_collateral_deassignment.json} | 7 ++-- .../loan_collateral_deassignment.py} | 10 ++--- .../loan_collateral_deassignment_list.js} | 2 +- .../test_loan_collateral_deassignment.py} | 2 +- .../loan_disbursement/loan_disbursement.py | 10 +++-- .../test_loan_disbursement.py | 4 +- .../loan_security/loan_security_dashboard.py | 2 +- .../loan_security_shortfall.py | 14 +++---- .../loan_security_type_dashboard.py | 2 +- .../proposed_pledge/proposed_pledge.json | 2 +- .../applicant_wise_loan_security_exposure.py | 6 +-- .../loan_interest_report.py | 6 +-- .../loan_security_status.js | 2 +- .../loan_security_status.py | 14 +++---- .../loan_management/loan_management.json | 8 ++-- .../workspace/loans/loans.json | 8 ++-- lending/patches.txt | 4 +- .../change_status_of_loan_security_pledges.py | 21 ++++++++++ ...ename_loan_security_pledge_and_unpledge.py | 21 ++++++++++ lending/patches/v15_0/set_collateral_type.py | 11 +++-- 39 files changed, 200 insertions(+), 140 deletions(-) rename lending/loan_management/doctype/{loan_security_pledge => loan_collateral_assignment}/__init__.py (100%) rename lending/loan_management/doctype/{loan_security_pledge/loan_security_pledge.js => loan_collateral_assignment/loan_collateral_assignment.js} (97%) rename lending/loan_management/doctype/{loan_security_pledge/loan_security_pledge.json => loan_collateral_assignment/loan_collateral_assignment.json} (96%) rename lending/loan_management/doctype/{loan_security_pledge/loan_security_pledge.py => loan_collateral_assignment/loan_collateral_assignment.py} (96%) rename lending/loan_management/doctype/{loan_security_pledge/loan_security_pledge_list.js => loan_collateral_assignment/loan_collateral_assignment_list.js} (69%) rename lending/loan_management/doctype/{loan_security_pledge/test_loan_security_pledge.py => loan_collateral_assignment/test_loan_collateral_assignment.py} (69%) rename lending/loan_management/doctype/{loan_security_unpledge => loan_collateral_deassignment}/__init__.py (100%) rename lending/loan_management/doctype/{loan_security_unpledge/loan_security_unpledge.js => loan_collateral_deassignment/loan_collateral_deassignment.js} (84%) rename lending/loan_management/doctype/{loan_security_unpledge/loan_security_unpledge.json => loan_collateral_deassignment/loan_collateral_deassignment.json} (97%) rename lending/loan_management/doctype/{loan_security_unpledge/loan_security_unpledge.py => loan_collateral_deassignment/loan_collateral_deassignment.py} (95%) rename lending/loan_management/doctype/{loan_security_unpledge/loan_security_unpledge_list.js => loan_collateral_deassignment/loan_collateral_deassignment_list.js} (85%) rename lending/loan_management/doctype/{loan_security_unpledge/test_loan_security_unpledge.py => loan_collateral_deassignment/test_loan_collateral_deassignment.py} (69%) create mode 100644 lending/patches/v15_0/change_status_of_loan_security_pledges.py create mode 100644 lending/patches/v15_0/rename_loan_security_pledge_and_unpledge.py diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 125c8d94..4b8651f1 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -4,8 +4,9 @@ set -e cd ~ || exit -sudo apt-get update -sudo apt-get -y install redis-server libcups2-dev -qq +sudo apt update +sudo apt remove mysql-server mysql-client +sudo apt install libcups2-dev redis-server mariadb-client-10.6 pip install frappe-bench @@ -15,15 +16,14 @@ bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frapp mkdir ~/frappe-bench/sites/test_site cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/ -mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'" -mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" +mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'" +mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" -mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'" -mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe" -mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'" +mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'" +mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE DATABASE test_frappe" +mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'" -mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'" -mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES" +mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "FLUSH PRIVILEGES" install_whktml() { wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45421ff4..113a97b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: MYSQL_ALLOW_EMPTY_PASSWORD: YES ports: - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3 steps: - name: Clone diff --git a/lending/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/lending/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py index 85fa48df..0fe80aec 100644 --- a/lending/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py +++ b/lending/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py @@ -47,7 +47,7 @@ def get_data( frappe.db.sql( """ SELECT u.loan_security, sum(u.qty) as qty - FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + FROM `tabLoan Collateral Deassignment` up, `tabUnpledge` u WHERE u.parent = up.name AND up.status = 'Approved' {conditions} @@ -64,9 +64,9 @@ def get_data( frappe.db.sql( """ SELECT p.loan_security, sum(p.qty) as qty - FROM `tabLoan Security Pledge` lp, `tabPledge`p + FROM `tabLoan Collateral Assignment` lp, `tabPledge`p WHERE p.parent = lp.name - AND lp.status = 'Pledged' + AND lp.status = 'Assigned' {conditions} GROUP BY p.loan_security """.format( diff --git a/lending/loan_management/doctype/loan/loan.js b/lending/loan_management/doctype/loan/loan.js index ac3a8e2d..48157d00 100644 --- a/lending/loan_management/doctype/loan/loan.js +++ b/lending/loan_management/doctype/loan/loan.js @@ -7,13 +7,13 @@ frappe.ui.form.on('Loan', { setup: function(frm) { frm.make_methods = { 'Loan Disbursement': function() { frm.trigger('make_loan_disbursement') }, - 'Loan Security Unpledge': function() { frm.trigger('create_loan_security_unpledge') }, + 'Loan Collateral Deassignment': function() { frm.trigger('create_loan_collateral_deassignment') }, 'Loan Write Off': function() { frm.trigger('make_loan_write_off_entry') } } }, onload: function (frm) { - // Ignore loan security pledge on cancel of loan - frm.ignore_doctypes_on_cancel_all = ["Loan Security Pledge", "Loan Repayment Schedule"]; + // Ignore Loan Collateral Assignment on cancel of loan + frm.ignore_doctypes_on_cancel_all = ["Loan Collateral Assignment", "Loan Repayment Schedule"]; frm.set_query("loan_application", function () { return { @@ -82,8 +82,8 @@ frappe.ui.form.on('Loan', { } if (frm.doc.status == "Loan Closure Requested") { - frm.add_custom_button(__('Loan Security Unpledge'), function() { - frm.trigger("create_loan_security_unpledge"); + frm.add_custom_button(__('Loan Collateral Deassignment'), function() { + frm.trigger("create_loan_collateral_deassignment"); },__('Create')); } @@ -219,7 +219,7 @@ frappe.ui.form.on('Loan', { ); }, - create_loan_security_unpledge: function(frm) { + create_loan_collateral_deassignment: function(frm) { frappe.call({ method: "lending.loan_management.doctype.loan.loan.unpledge_security", args : { diff --git a/lending/loan_management/doctype/loan/loan.py b/lending/loan_management/doctype/loan/loan.py index 648648e5..38cfa03f 100644 --- a/lending/loan_management/doctype/loan/loan.py +++ b/lending/loan_management/doctype/loan/loan.py @@ -25,7 +25,7 @@ from lending.loan_management.doctype.loan_collateral.loan_collateral import ( get_pending_deassignment_collaterals, ) -from lending.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import ( +from lending.loan_management.doctype.loan_collateral_deassignment.loan_collateral_deassignment import ( get_pledged_security_qty, ) @@ -93,13 +93,13 @@ def set_cyclic_date(self): self.repayment_start_date = cyclic_date def on_submit(self): - self.link_loan_security_pledge() + self.link_loan_collateral_assignment() # Interest accrual for backdated term loans self.accrue_loan_interest() self.submit_draft_schedule() def on_cancel(self): - self.unlink_loan_security_pledge() + self.unlink_loan_collateral_assignment() self.cancel_and_delete_repayment_schedule() self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] @@ -224,10 +224,10 @@ def validate_loan_amount(self): if not self.loan_amount: frappe.throw(_("Loan amount is mandatory")) - def link_loan_security_pledge(self): + def link_loan_collateral_assignment(self): if self.is_secured_loan and self.loan_application: maximum_loan_value = frappe.db.get_value( - "Loan Security Pledge", + "Loan Collateral Assignment", {"loan_application": self.loan_application, "status": "Requested"}, "sum(maximum_loan_value)", ) @@ -235,8 +235,8 @@ def link_loan_security_pledge(self): if maximum_loan_value: frappe.db.sql( """ - UPDATE `tabLoan Security Pledge` - SET loan = %s, pledge_time = %s, status = 'Pledged' + UPDATE `tabLoan Collateral Assignment` + SET loan = %s, pledge_time = %s, status = 'Assigned' WHERE status = 'Requested' and loan_application = %s """, (self.name, now_datetime(), self.loan_application), @@ -254,13 +254,15 @@ def accrue_loan_interest(self): posting_date=getdate(), loan_product=self.loan_product, loan=self.name ) - def unlink_loan_security_pledge(self): - pledges = frappe.get_all("Loan Security Pledge", fields=["name"], filters={"loan": self.name}) + def unlink_loan_collateral_assignment(self): + pledges = frappe.get_all( + "Loan Collateral Assignment", fields=["name"], filters={"loan": self.name} + ) pledge_list = [d.name for d in pledges] if pledge_list: frappe.db.sql( - """UPDATE `tabLoan Security Pledge` SET - loan = '', status = 'Unpledged' + """UPDATE `tabLoan Collateral Assignment` SET + loan = '', status = 'Unassigned' where name in (%s) """ % (", ".join(["%s"] * len(pledge_list))), tuple(pledge_list), @@ -462,7 +464,7 @@ def make_loan_write_off(loan, company=None, posting_date=None, amount=0, as_dict @frappe.whitelist() def unpledge_security( loan=None, - loan_security_pledge=None, + loan_collateral_assignment=None, security_map=None, collaterals=None, collateral_type=None, @@ -487,7 +489,7 @@ def unpledge_security( unpledge_map = security_map or get_pledged_security_qty(loan) else: deassignment_collaterals = collaterals or get_pending_deassignment_collaterals(loan) - unpledge_request = create_loan_security_unpledge( + unpledge_request = create_loan_collateral_deassignment( loan, loan_company, loan_applicant_type, @@ -496,14 +498,14 @@ def unpledge_security( unpledge_map, deassignment_collaterals, ) - # will unpledge qty based on loan security pledge - elif loan_security_pledge: + # will unpledge qty based on Loan Collateral Assignment + elif loan_collateral_assignment: unpledge_map = {} - pledge_doc = frappe.get_doc("Loan Security Pledge", loan_security_pledge) + pledge_doc = frappe.get_doc("Loan Collateral Assignment", loan_collateral_assignment) for security in pledge_doc.securities: unpledge_map.setdefault(security.loan_security, security.qty) - unpledge_request = create_loan_security_unpledge( + unpledge_request = create_loan_collateral_deassignment( pledge_doc.loan, pledge_doc.company, pledge_doc.applicant_type, @@ -531,7 +533,7 @@ def unpledge_security( return unpledge_request -def create_loan_security_unpledge( +def create_loan_collateral_deassignment( loan, company, applicant_type, @@ -540,7 +542,7 @@ def create_loan_security_unpledge( unpledge_map=None, deassignment_collaterals=None, ): - unpledge_request = frappe.new_doc("Loan Security Unpledge") + unpledge_request = frappe.new_doc("Loan Collateral Deassignment") unpledge_request.applicant_type = applicant_type unpledge_request.applicant = applicant unpledge_request.loan = loan diff --git a/lending/loan_management/doctype/loan/loan_dashboard.py b/lending/loan_management/doctype/loan/loan_dashboard.py index 48eae0e3..31831fb6 100644 --- a/lending/loan_management/doctype/loan/loan_dashboard.py +++ b/lending/loan_management/doctype/loan/loan_dashboard.py @@ -10,12 +10,19 @@ def get_data(): { "items": [ "Loan Repayment Schedule", - "Loan Security Pledge", + "Loan Collateral Assignment", "Loan Security Shortfall", "Loan Disbursement", ] }, {"items": ["Loan Repayment", "Loan Interest Accrual", "Loan Write Off", "Loan Restructure"]}, - {"items": ["Loan Security Unpledge", "Days Past Due Log", "Journal Entry", "Sales Invoice"]}, + { + "items": [ + "Loan Collateral Deassignment", + "Days Past Due Log", + "Journal Entry", + "Sales Invoice", + ] + }, ], } diff --git a/lending/loan_management/doctype/loan/test_loan.py b/lending/loan_management/doctype/loan/test_loan.py index b35cc4ee..f9907e40 100644 --- a/lending/loan_management/doctype/loan/test_loan.py +++ b/lending/loan_management/doctype/loan/test_loan.py @@ -24,6 +24,9 @@ unpledge_security, ) from lending.loan_management.doctype.loan_application.loan_application import create_pledge +from lending.loan_management.doctype.loan_collateral_deassignment.loan_collateral_deassignment import ( + get_pledged_security_qty, +) from lending.loan_management.doctype.loan_disbursement.loan_disbursement import ( get_disbursal_amount, ) @@ -31,9 +34,6 @@ days_in_year, ) from lending.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts -from lending.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import ( - get_pledged_security_qty, -) from lending.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( process_loan_interest_accrual_for_demand_loans, process_loan_interest_accrual_for_term_loans, @@ -222,7 +222,9 @@ def test_sanctioned_amount_limit(self): # Clear loan docs before checking frappe.db.sql("DELETE FROM `tabLoan` where applicant = '_Test Loan Customer 1'") frappe.db.sql("DELETE FROM `tabLoan Application` where applicant = '_Test Loan Customer 1'") - frappe.db.sql("DELETE FROM `tabLoan Security Pledge` where applicant = '_Test Loan Customer 1'") + frappe.db.sql( + "DELETE FROM `tabLoan Collateral Assignment` where applicant = '_Test Loan Customer 1'" + ) if not frappe.db.get_value( "Sanctioned Loan Amount", @@ -458,7 +460,7 @@ def test_security_shortfall(self): self.assertEqual(loan_security_shortfall.status, "Completed") self.assertEqual(loan_security_shortfall.shortfall_amount, 0) - def test_loan_security_unpledge(self): + def test_loan_collateral_deassignment(self): pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}] loan_application = create_loan_application( @@ -515,7 +517,7 @@ def test_loan_security_unpledge(self): self.assertEqual(amounts["payable_principal_amount"], 0.0) self.assertEqual(amounts["interest_amount"], 0) - def test_partial_loan_security_unpledge(self): + def test_partial_loan_collateral_deassignment(self): pledge = [ {"loan_security": "Test Security 1", "qty": 2000.00}, {"loan_security": "Test Security 2", "qty": 4000.00}, @@ -554,7 +556,7 @@ def test_partial_loan_security_unpledge(self): unpledge_request.load_from_db() self.assertEqual(unpledge_request.docstatus, 1) - def test_sanctioned_loan_security_unpledge(self): + def test_sanctioned_loan_collateral_deassignment(self): pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}] loan_application = create_loan_application( @@ -1144,9 +1146,9 @@ def create_loan_security(): ).insert(ignore_permissions=True) -def create_loan_security_pledge(applicant, pledges, loan_application=None, loan=None): +def create_loan_collateral_assignment(applicant, pledges, loan_application=None, loan=None): - lsp = frappe.new_doc("Loan Security Pledge") + lsp = frappe.new_doc("Loan Collateral Assignment") lsp.applicant_type = "Customer" lsp.applicant = applicant lsp.company = "_Test Company" diff --git a/lending/loan_management/doctype/loan_application/loan_application.js b/lending/loan_management/doctype/loan_application/loan_application.js index 37203ae5..10742586 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.js +++ b/lending/loan_management/doctype/loan_application/loan_application.js @@ -8,7 +8,7 @@ frappe.ui.form.on('Loan Application', { setup: function(frm) { frm.make_methods = { 'Loan': function() { frm.trigger('create_loan') }, - 'Loan Security Pledge': function() { frm.trigger('create_loan_security_pledge') }, + 'Loan Collateral Assignment': function() { frm.trigger('create_loan_collateral_assignment') }, } }, refresh: function(frm) { @@ -39,10 +39,10 @@ frappe.ui.form.on('Loan Application', { if (frm.doc.status == "Approved") { if (frm.doc.is_secured_loan) { - frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { + frappe.db.get_value("Loan Collateral Assignment", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { if (Object.keys(r).length === 0) { - frm.add_custom_button(__('Loan Security Pledge'), function() { - frm.trigger('create_loan_security_pledge'); + frm.add_custom_button(__('Loan Collateral Assignment'), function() { + frm.trigger('create_loan_collateral_assignment'); },__('Create')) } }); @@ -69,10 +69,10 @@ frappe.ui.form.on('Loan Application', { frm: frm }); }, - create_loan_security_pledge: function(frm) { + create_loan_collateral_assignment: function(frm) { if(!frm.doc.is_secured_loan) { - frappe.throw(__("Loan Security Pledge can only be created for secured loans")); + frappe.throw(__("Loan Collateral Assignment can only be created for secured loans")); } frappe.call({ @@ -81,7 +81,7 @@ frappe.ui.form.on('Loan Application', { loan_application: frm.doc.name }, callback: function(r) { - frappe.set_route("Form", "Loan Security Pledge", r.message); + frappe.set_route("Form", "Loan Collateral Assignment", r.message); } }) }, diff --git a/lending/loan_management/doctype/loan_application/loan_application.py b/lending/loan_management/doctype/loan_application/loan_application.py index c26152bf..91ee7a54 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.py +++ b/lending/loan_management/doctype/loan_application/loan_application.py @@ -215,7 +215,7 @@ def update_accounts(source_doc, target_doc, source_parent): def create_pledge(loan_application, loan=None): loan_application_doc = frappe.get_doc("Loan Application", loan_application) - lsp = frappe.new_doc("Loan Security Pledge") + lsp = frappe.new_doc("Loan Collateral Assignment") lsp.applicant_type = loan_application_doc.applicant_type lsp.applicant = loan_application_doc.applicant lsp.loan_application = loan_application_doc.name @@ -250,7 +250,7 @@ def create_pledge(loan_application, loan=None): lsp.save() lsp.submit() - message = _("Loan Security Pledge Created : {0}").format(lsp.name) + message = _("Loan Collateral Assignment Created : {0}").format(lsp.name) frappe.msgprint(message) return lsp.name diff --git a/lending/loan_management/doctype/loan_application/loan_application_dashboard.py b/lending/loan_management/doctype/loan_application/loan_application_dashboard.py index 1d90e9bb..da87e798 100644 --- a/lending/loan_management/doctype/loan_application/loan_application_dashboard.py +++ b/lending/loan_management/doctype/loan_application/loan_application_dashboard.py @@ -2,6 +2,6 @@ def get_data(): return { "fieldname": "loan_application", "transactions": [ - {"items": ["Loan", "Loan Security Pledge"]}, + {"items": ["Loan", "Loan Collateral Assignment"]}, ], } diff --git a/lending/loan_management/doctype/loan_collateral/loan_collateral.py b/lending/loan_management/doctype/loan_collateral/loan_collateral.py index 86e0a41b..d8732841 100644 --- a/lending/loan_management/doctype/loan_collateral/loan_collateral.py +++ b/lending/loan_management/doctype/loan_collateral/loan_collateral.py @@ -59,12 +59,12 @@ def update_loan_collaterals_values( def _get_sorted_loan_collaterals_to_update_values(loan, utilized_value_increased): loan_collaterals_w_ratio = [] - for loan_security_pledge in frappe.db.get_all( - "Loan Security Pledge", {"loan": loan}, pluck="name" + for loan_collateral_assignment in frappe.db.get_all( + "Loan Collateral Assignment", {"loan": loan}, pluck="name" ): loan_collaterals = frappe.db.get_all( "Loan Collateral Assignment Loan Collateral", - {"parent": loan_security_pledge}, + {"parent": loan_collateral_assignment}, pluck="loan_collateral", ) @@ -166,7 +166,7 @@ def release_loan_collateral(loan_collateral): msg = _("Loan Collateral {0} is linked with active loans:").format(frappe.bold(loan_collateral)) for loan_and_pledge in active_loan_collaterals: msg += "

" - msg += _("Loan {0} through Loan Security Pledge {1}").format( + msg += _("Loan {0} through Loan Collateral Assignment {1}").format( frappe.bold(loan_and_pledge.loan), frappe.bold(loan_and_pledge.pledge) ) frappe.throw(msg, title=_("Loan Collateral cannot be released")) @@ -182,10 +182,10 @@ def get_active_loan_collaterals(loan_collateral): lcalcs = frappe.db.sql( """ SELECT lp.loan, lp.name as pledge - FROM `tabLoan Security Pledge` lp, `tabLoan Collateral Assignment Loan Collateral` p + FROM `tabLoan Collateral Assignment` lp, `tabLoan Collateral Assignment Loan Collateral` p WHERE p.loan_collateral = %s AND p.parent = lp.name - AND lp.status = 'Pledged' + AND lp.status = 'Assigned' """, (loan_collateral), as_dict=True, @@ -194,7 +194,7 @@ def get_active_loan_collaterals(loan_collateral): lcdcs = frappe.db.sql( """ SELECT up.loan - FROM `tabLoan Security Unpledge` up, `tabLoan Collateral Deassignment Loan Collateral` u + FROM `tabLoan Collateral Deassignment` up, `tabLoan Collateral Deassignment Loan Collateral` u WHERE u.loan_collateral = %s AND u.parent = up.name AND up.status = 'Approved' @@ -223,17 +223,17 @@ def check_loan_collaterals_availability(loan, amount=None): total_available_collateral_value = 0 - loan_security_pledges = frappe.db.get_all( - "Loan Security Pledge", {"loan": loan, "collateral_type": "Loan Collateral"}, pluck="name" + loan_collateral_assignments = frappe.db.get_all( + "Loan Collateral Assignment", {"loan": loan, "collateral_type": "Loan Collateral"}, pluck="name" ) - if not loan_security_pledges: + if not loan_collateral_assignments: return - for loan_security_pledge in loan_security_pledges: + for loan_collateral_assignment in loan_collateral_assignments: loan_collaterals = frappe.db.get_all( "Loan Collateral Assignment Loan Collateral", - {"parent": loan_security_pledge}, + {"parent": loan_collateral_assignment}, pluck="loan_collateral", ) for loan_collateral in loan_collaterals: @@ -254,7 +254,7 @@ def get_pending_deassignment_collaterals(loan): assignment_collaterals = frappe.db.sql( """ SELECT lcalc.loan_collateral - FROM `tabLoan Security Pledge` lsp, `tabLoan Collateral Assignment Loan Collateral` lcalc + FROM `tabLoan Collateral Assignment` lsp, `tabLoan Collateral Assignment Loan Collateral` lcalc WHERE lsp.loan = %s AND lcalc.parent = lsp.name """, @@ -266,7 +266,7 @@ def get_pending_deassignment_collaterals(loan): deassignment_collaterals = frappe.db.sql( """ SELECT lcdlc.loan_collateral - FROM `tabLoan Security Unpledge` lsu, `tabLoan Collateral Deassignment Loan Collateral` lcdlc + FROM `tabLoan Collateral Deassignment` lsu, `tabLoan Collateral Deassignment Loan Collateral` lcdlc WHERE lsu.loan = %s AND lcdlc.parent = lsu.name """, diff --git a/lending/loan_management/doctype/loan_security_pledge/__init__.py b/lending/loan_management/doctype/loan_collateral_assignment/__init__.py similarity index 100% rename from lending/loan_management/doctype/loan_security_pledge/__init__.py rename to lending/loan_management/doctype/loan_collateral_assignment/__init__.py diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.js similarity index 97% rename from lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js rename to lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.js index 81f81d87..dcb31ff9 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.js +++ b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Pledge', { +frappe.ui.form.on('Loan Collateral Assignment', { loan: function(frm, cdt, cdn) { frappe.db.get_value("Loan", frm.doc.loan, "collateral_type", (r) => { frm.set_value('collateral_type', r.collateral_type); diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.json similarity index 96% rename from lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json rename to lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.json index 3a5c6bcc..7a22eaf3 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.json +++ b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.json @@ -36,7 +36,7 @@ "fieldtype": "Link", "label": "Amended From", "no_copy": 1, - "options": "Loan Security Pledge", + "options": "Loan Collateral Assignment", "print_hide": 1, "read_only": 1 }, @@ -98,7 +98,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Status", - "options": "Requested\nUnpledged\nPledged\nPartially Pledged\nCancelled", + "options": "Requested\nUnassigned\nAssigned\nCancelled", "read_only": 1 }, { @@ -186,10 +186,10 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-10-19 05:41:35.477481", + "modified": "2023-10-19 08:17:16.100919", "modified_by": "Administrator", "module": "Loan Management", - "name": "Loan Security Pledge", + "name": "Loan Collateral Assignment", "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.py similarity index 96% rename from lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py rename to lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.py index 30bd7458..31fe6f46 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge.py +++ b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment.py @@ -15,7 +15,7 @@ ) -class LoanSecurityPledge(Document): +class LoanCollateralAssignment(Document): def validate(self): self.set_missing_values() self.set_pledge_amount() @@ -32,7 +32,7 @@ def set_missing_values(self): def on_submit(self): if self.loan: - self.db_set("status", "Pledged") + self.db_set("status", "Assigned") self.db_set("pledge_time", now_datetime()) update_shortfall_status(self.loan, self.total_security_value) update_loan(self.loan, self.maximum_loan_value) @@ -84,7 +84,7 @@ def validate_loan_security_type(self): if self.loan: existing_pledge = frappe.db.get_value( - "Loan Security Pledge", {"loan": self.loan, "docstatus": 1}, ["name"] + "Loan Collateral Assignment", {"loan": self.loan, "docstatus": 1}, ["name"] ) if existing_pledge: diff --git a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment_list.js similarity index 69% rename from lending/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js rename to lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment_list.js index 174d1b0d..180cdc90 100644 --- a/lending/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js +++ b/lending/loan_management/doctype/loan_collateral_assignment/loan_collateral_assignment_list.js @@ -2,13 +2,12 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['Loan Security Pledge'] = { +frappe.listview_settings['Loan Collateral Assignment'] = { add_fields: ["status"], get_indicator: function(doc) { var status_color = { - "Unpledged": "orange", - "Pledged": "green", - "Partially Pledged": "green" + "Unassigned": "orange", + "Assigned": "green", }; return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; } diff --git a/lending/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py b/lending/loan_management/doctype/loan_collateral_assignment/test_loan_collateral_assignment.py similarity index 69% rename from lending/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py rename to lending/loan_management/doctype/loan_collateral_assignment/test_loan_collateral_assignment.py index b9d05c28..93a91101 100644 --- a/lending/loan_management/doctype/loan_security_pledge/test_loan_security_pledge.py +++ b/lending/loan_management/doctype/loan_collateral_assignment/test_loan_collateral_assignment.py @@ -5,5 +5,5 @@ import unittest -class TestLoanSecurityPledge(unittest.TestCase): +class TestLoanCollateralAssignment(unittest.TestCase): pass diff --git a/lending/loan_management/doctype/loan_security_unpledge/__init__.py b/lending/loan_management/doctype/loan_collateral_deassignment/__init__.py similarity index 100% rename from lending/loan_management/doctype/loan_security_unpledge/__init__.py rename to lending/loan_management/doctype/loan_collateral_deassignment/__init__.py diff --git a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.js similarity index 84% rename from lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js rename to lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.js index 82232062..78e3be12 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js +++ b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Unpledge', { +frappe.ui.form.on('Loan Collateral Deassignment', { refresh: function(frm) { if (frm.doc.docstatus == 1 && frm.doc.status == 'Approved') { diff --git a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.json similarity index 97% rename from lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json rename to lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.json index 51f226c8..370560dc 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.json +++ b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.json @@ -78,7 +78,7 @@ "fieldtype": "Link", "label": "Amended From", "no_copy": 1, - "options": "Loan Security Unpledge", + "options": "Loan Collateral Deassignment", "print_hide": 1, "read_only": 1 }, @@ -154,10 +154,10 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-10-19 05:41:14.454180", + "modified": "2023-10-19 07:44:49.615145", "modified_by": "Administrator", "module": "Loan Management", - "name": "Loan Security Unpledge", + "name": "Loan Collateral Deassignment", "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ @@ -204,7 +204,6 @@ "write": 1 } ], - "quick_entry": 1, "search_fields": "applicant", "sort_field": "modified", "sort_order": "DESC", diff --git a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.py similarity index 95% rename from lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py rename to lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.py index 64e4ca5b..95ee1938 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py +++ b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment.py @@ -8,7 +8,7 @@ from frappe.utils import flt, get_datetime, getdate -class LoanSecurityUnpledge(Document): +class LoanCollateralDeassignment(Document): def validate(self): self.validate_duplicate_securities_and_collaterals() self.validate_unpledge_qty() @@ -99,7 +99,7 @@ def validate_unpledge_qty(self): ) msg += "
" msg += _("You are trying to unpledge more.") - frappe.throw(msg, title=_("Loan Security Unpledge Error")) + frappe.throw(msg, title=_("Loan Collateral Deassignment Error")) unpledge_qty_map.setdefault(security.loan_security, 0) unpledge_qty_map[security.loan_security] += security.qty @@ -190,7 +190,7 @@ def get_pledged_security_qty(loan): frappe.db.sql( """ SELECT u.loan_security, sum(u.qty) as qty - FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + FROM `tabLoan Collateral Deassignment` up, `tabUnpledge` u WHERE up.loan = %s AND u.parent = up.name AND up.status = 'Approved' @@ -204,10 +204,10 @@ def get_pledged_security_qty(loan): frappe.db.sql( """ SELECT p.loan_security, sum(p.qty) as qty - FROM `tabLoan Security Pledge` lp, `tabPledge`p + FROM `tabLoan Collateral Assignment` lp, `tabPledge`p WHERE lp.loan = %s AND p.parent = lp.name - AND lp.status = 'Pledged' + AND lp.status = 'Assigned' GROUP BY p.loan_security """, (loan), diff --git a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment_list.js similarity index 85% rename from lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js rename to lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment_list.js index 196ebbb9..32a6db5b 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js +++ b/lending/loan_management/doctype/loan_collateral_deassignment/loan_collateral_deassignment_list.js @@ -2,7 +2,7 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['Loan Security Unpledge'] = { +frappe.listview_settings['Loan Collateral Deassignment'] = { add_fields: ["status"], get_indicator: function(doc) { var status_color = { diff --git a/lending/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py b/lending/loan_management/doctype/loan_collateral_deassignment/test_loan_collateral_deassignment.py similarity index 69% rename from lending/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py rename to lending/loan_management/doctype/loan_collateral_deassignment/test_loan_collateral_deassignment.py index 2f124e49..5516c58b 100644 --- a/lending/loan_management/doctype/loan_security_unpledge/test_loan_security_unpledge.py +++ b/lending/loan_management/doctype/loan_collateral_deassignment/test_loan_collateral_deassignment.py @@ -5,5 +5,5 @@ import unittest -class TestLoanSecurityUnpledge(unittest.TestCase): +class TestLoanCollateralDeassignment(unittest.TestCase): pass diff --git a/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py b/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py index 4fee571d..1a961dff 100644 --- a/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/lending/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -14,7 +14,7 @@ check_loan_collaterals_availability, update_loan_collaterals_values, ) -from lending.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import ( +from lending.loan_management.doctype.loan_collateral_deassignment.loan_collateral_deassignment import ( get_pledged_security_qty, ) from lending.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( @@ -130,8 +130,8 @@ def set_status_of_loan_collaterals(self, cancel=0): """ UPDATE `tabLoan Collateral` JOIN `tabLoan Collateral Assignment Loan Collateral` ON `tabLoan Collateral Assignment Loan Collateral`.`loan_collateral`=`tabLoan Collateral`.`name` - JOIN `tabLoan Security Pledge` ON `tabLoan Security Pledge`.`name`=`tabLoan Collateral Assignment Loan Collateral`.`parent` - JOIN `tabLoan` ON `tabLoan`.`name`=`tabLoan Security Pledge`.`loan` + JOIN `tabLoan Collateral Assignment` ON `tabLoan Collateral Assignment`.`name`=`tabLoan Collateral Assignment Loan Collateral`.`parent` + JOIN `tabLoan` ON `tabLoan`.`name`=`tabLoan Collateral Assignment`.`loan` SET `tabLoan Collateral`.`status`=%s WHERE `tabLoan`.`name`=%s AND `tabLoan Collateral`.`status`=%s """, @@ -418,4 +418,6 @@ def get_disbursal_amount(loan, on_current_security_price=0): def get_maximum_amount_as_per_pledged_security(loan): - return flt(frappe.db.get_value("Loan Security Pledge", {"loan": loan}, "sum(maximum_loan_value)")) + return flt( + frappe.db.get_value("Loan Collateral Assignment", {"loan": loan}, "sum(maximum_loan_value)") + ) diff --git a/lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py index b3698220..9852843a 100644 --- a/lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py +++ b/lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py @@ -21,9 +21,9 @@ create_demand_loan, create_loan_accounts, create_loan_application, + create_loan_collateral_assignment, create_loan_product, create_loan_security, - create_loan_security_pledge, create_loan_security_price, create_loan_security_type, create_repayment_entry, @@ -154,7 +154,7 @@ def test_loan_topup_with_additional_pledge(self): pledge1 = [{"loan_security": "Test Security 1", "qty": 2000.00}] - create_loan_security_pledge(self.applicant, pledge1, loan=loan.name) + create_loan_collateral_assignment(self.applicant, pledge1, loan=loan.name) # Topup 500000 make_loan_disbursement_entry(loan.name, 500000, disbursement_date=add_days(last_date, 1)) diff --git a/lending/loan_management/doctype/loan_security/loan_security_dashboard.py b/lending/loan_management/doctype/loan_security/loan_security_dashboard.py index 1d96885e..adb84386 100644 --- a/lending/loan_management/doctype/loan_security/loan_security_dashboard.py +++ b/lending/loan_management/doctype/loan_security/loan_security_dashboard.py @@ -3,6 +3,6 @@ def get_data(): "fieldname": "loan_security", "transactions": [ {"items": ["Loan Application", "Loan Security Price"]}, - {"items": ["Loan Security Pledge", "Loan Security Unpledge"]}, + {"items": ["Loan Collateral Assignment", "Loan Collateral Deassignment"]}, ], } diff --git a/lending/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/lending/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py index 4fa18136..225e83a4 100644 --- a/lending/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py +++ b/lending/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py @@ -6,7 +6,7 @@ from frappe.model.document import Document from frappe.utils import flt, get_datetime -from lending.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import ( +from lending.loan_management.doctype.loan_collateral_deassignment.loan_collateral_deassignment import ( get_pledged_security_qty, ) @@ -51,13 +51,13 @@ def add_security(loan): "Loan", loan, ["applicant", "company", "applicant_type"], as_dict=1 ) - loan_security_pledge = frappe.new_doc("Loan Security Pledge") - loan_security_pledge.loan = loan - loan_security_pledge.company = loan_details.company - loan_security_pledge.applicant_type = loan_details.applicant_type - loan_security_pledge.applicant = loan_details.applicant + loan_collateral_assignment = frappe.new_doc("Loan Collateral Assignment") + loan_collateral_assignment.loan = loan + loan_collateral_assignment.company = loan_details.company + loan_collateral_assignment.applicant_type = loan_details.applicant_type + loan_collateral_assignment.applicant = loan_details.applicant - return loan_security_pledge.as_dict() + return loan_collateral_assignment.as_dict() def check_for_ltv_shortfall(process_loan_security_shortfall): diff --git a/lending/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py b/lending/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py index 8fc4520c..6c56d242 100644 --- a/lending/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py +++ b/lending/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py @@ -3,6 +3,6 @@ def get_data(): "fieldname": "loan_security_type", "transactions": [ {"items": ["Loan Security", "Loan Security Price"]}, - {"items": ["Loan Security Pledge", "Loan Security Unpledge"]}, + {"items": ["Loan Collateral Assignment", "Loan Collateral Deassignment"]}, ], } diff --git a/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json b/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json index cffe917f..9c7e6668 100644 --- a/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json +++ b/lending/loan_management/doctype/proposed_pledge/proposed_pledge.json @@ -37,7 +37,7 @@ "read_only": 1 }, { - "fetch_from": "loan_security_pledge.qty", + "fetch_from": "loan_collateral_assignment.qty", "fieldname": "qty", "fieldtype": "Float", "in_list_view": 1, diff --git a/lending/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/lending/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py index 02da8109..9f0001e2 100644 --- a/lending/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py +++ b/lending/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py @@ -190,7 +190,7 @@ def get_applicant_wise_total_loan_security_qty(filters, loan_security_details): unpledges = frappe.db.sql( """ SELECT up.applicant, u.loan_security, sum(u.qty) as qty - FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + FROM `tabLoan Collateral Deassignment` up, `tabUnpledge` u WHERE u.parent = up.name AND up.status = 'Approved' {conditions} @@ -208,9 +208,9 @@ def get_applicant_wise_total_loan_security_qty(filters, loan_security_details): pledges = frappe.db.sql( """ SELECT lp.applicant_type, lp.applicant, p.loan_security, sum(p.qty) as qty - FROM `tabLoan Security Pledge` lp, `tabPledge`p + FROM `tabLoan Collateral Assignment` lp, `tabPledge`p WHERE p.parent = lp.name - AND lp.status = 'Pledged' + AND lp.status = 'Assigned' {conditions} GROUP BY lp.applicant, p.loan_security """.format( diff --git a/lending/loan_management/report/loan_interest_report/loan_interest_report.py b/lending/loan_management/report/loan_interest_report/loan_interest_report.py index 7614fb26..3cf82166 100644 --- a/lending/loan_management/report/loan_interest_report/loan_interest_report.py +++ b/lending/loan_management/report/loan_interest_report/loan_interest_report.py @@ -327,7 +327,7 @@ def get_loan_wise_pledges(filters): unpledges = frappe.db.sql( """ SELECT up.loan, u.loan_security, sum(u.qty) as qty - FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + FROM `tabLoan Collateral Deassignment` up, `tabUnpledge` u WHERE u.parent = up.name AND up.status = 'Approved' {conditions} @@ -345,9 +345,9 @@ def get_loan_wise_pledges(filters): pledges = frappe.db.sql( """ SELECT lp.loan, p.loan_security, sum(p.qty) as qty - FROM `tabLoan Security Pledge` lp, `tabPledge`p + FROM `tabLoan Collateral Assignment` lp, `tabPledge`p WHERE p.parent = lp.name - AND lp.status = 'Pledged' + AND lp.status = 'Assigned' {conditions} GROUP BY lp.loan, p.loan_security """.format( diff --git a/lending/loan_management/report/loan_security_status/loan_security_status.js b/lending/loan_management/report/loan_security_status/loan_security_status.js index 6e6191c7..29ac4e85 100644 --- a/lending/loan_management/report/loan_security_status/loan_security_status.js +++ b/lending/loan_management/report/loan_security_status/loan_security_status.js @@ -40,7 +40,7 @@ frappe.query_reports["Loan Security Status"] = { "fieldname":"pledge_status", "label": __("Pledge Status"), "fieldtype": "Select", - "options": ["", "Requested", "Pledged", "Partially Pledged", "Unpledged"], + "options": ["", "Requested", "Assigned", "Unassigned"], }, ] }; diff --git a/lending/loan_management/report/loan_security_status/loan_security_status.py b/lending/loan_management/report/loan_security_status/loan_security_status.py index 9a5a1800..f4ef6abf 100644 --- a/lending/loan_management/report/loan_security_status/loan_security_status.py +++ b/lending/loan_management/report/loan_security_status/loan_security_status.py @@ -15,10 +15,10 @@ def execute(filters=None): def get_columns(filters): columns = [ { - "label": _("Loan Security Pledge"), + "label": _("Loan Collateral Assignment"), "fieldtype": "Link", - "fieldname": "loan_security_pledge", - "options": "Loan Security Pledge", + "fieldname": "loan_collateral_assignment", + "options": "Loan Collateral Assignment", "width": 200, }, {"label": _("Loan"), "fieldtype": "Link", "fieldname": "loan", "options": "Loan", "width": 200}, @@ -65,13 +65,13 @@ def get_data(filters): data = [] conditions = get_conditions(filters) - loan_security_pledges = frappe.db.sql( + loan_collateral_assignments = frappe.db.sql( """ SELECT p.name, p.applicant, p.loan, p.status, p.pledge_time, c.loan_security, c.qty, c.loan_security_price, c.amount FROM - `tabLoan Security Pledge` p, `tabPledge` c + `tabLoan Collateral Assignment` p, `tabPledge` c WHERE p.docstatus = 1 AND c.parent = p.name @@ -86,9 +86,9 @@ def get_data(filters): default_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") - for pledge in loan_security_pledges: + for pledge in loan_collateral_assignments: row = {} - row["loan_security_pledge"] = pledge.name + row["loan_collateral_assignment"] = pledge.name row["loan"] = pledge.loan row["applicant"] = pledge.applicant row["status"] = pledge.status diff --git a/lending/loan_management/workspace/loan_management/loan_management.json b/lending/loan_management/workspace/loan_management/loan_management.json index 6de28579..de46f981 100644 --- a/lending/loan_management/workspace/loan_management/loan_management.json +++ b/lending/loan_management/workspace/loan_management/loan_management.json @@ -178,9 +178,9 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Loan Security Pledge", + "label": "Loan Collateral Assignment", "link_count": 0, - "link_to": "Loan Security Pledge", + "link_to": "Loan Collateral Assignment", "link_type": "DocType", "onboard": 0, "type": "Link" @@ -189,9 +189,9 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Loan Security Unpledge", + "label": "Loan Collateral Deassignment", "link_count": 0, - "link_to": "Loan Security Unpledge", + "link_to": "Loan Collateral Deassignment", "link_type": "DocType", "onboard": 0, "type": "Link" diff --git a/lending/loan_management/workspace/loans/loans.json b/lending/loan_management/workspace/loans/loans.json index ba5623b3..f6128dd7 100644 --- a/lending/loan_management/workspace/loans/loans.json +++ b/lending/loan_management/workspace/loans/loans.json @@ -139,9 +139,9 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Loan Security Pledge", + "label": "Loan Collateral Assignment", "link_count": 0, - "link_to": "Loan Security Pledge", + "link_to": "Loan Collateral Assignment", "link_type": "DocType", "onboard": 0, "type": "Link" @@ -150,9 +150,9 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Loan Security Unpledge", + "label": "Loan Collateral Deassignment", "link_count": 0, - "link_to": "Loan Security Unpledge", + "link_to": "Loan Collateral Deassignment", "link_type": "DocType", "onboard": 0, "type": "Link" diff --git a/lending/patches.txt b/lending/patches.txt index eea2df32..6939fccf 100644 --- a/lending/patches.txt +++ b/lending/patches.txt @@ -5,6 +5,7 @@ lending.patches.v15_0.generate_loan_repayment_schedule lending.patches.v15_0.rename_process_asset_classification_doctype lending.patches.v15_0.rename_process_asset_classification_doctype_2 lending.patches.v15_0.rename_loan_type_to_loan_product +lending.patches.v15_0.rename_loan_security_pledge_and_unpledge [post_model_sync] # Patches added in this section will be executed after doctypes are migrated @@ -27,4 +28,5 @@ lending.patches.v15_0.migrate_loan_type_to_loan_product lending.patches.v15_0.add_loan_product_code_and_rename_loan_name lending.patches.v15_0.update_penalty_interest_method_in_loan_products lending.patches.v15_0.update_min_bpi_application_days -lending.patches.v15_0.set_collateral_type \ No newline at end of file +lending.patches.v15_0.set_collateral_type +lending.patches.v15_0.change_status_of_loan_security_pledges \ No newline at end of file diff --git a/lending/patches/v15_0/change_status_of_loan_security_pledges.py b/lending/patches/v15_0/change_status_of_loan_security_pledges.py new file mode 100644 index 00000000..6965978c --- /dev/null +++ b/lending/patches/v15_0/change_status_of_loan_security_pledges.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + lca = frappe.qb.DocType("Loan Collateral Assignment") + + ( + frappe.qb.update(lca).set( + lca.status, + ( + frappe.qb.terms.Case() + .when(lca.status == "Unpledged", "Unassigned") + .when(lca.status == "Pledged", "Assigned") + .else_(lca.status) + ), + ) + ).run() diff --git a/lending/patches/v15_0/rename_loan_security_pledge_and_unpledge.py b/lending/patches/v15_0/rename_loan_security_pledge_and_unpledge.py new file mode 100644 index 00000000..0c6c2abb --- /dev/null +++ b/lending/patches/v15_0/rename_loan_security_pledge_and_unpledge.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +import frappe + + +def execute(): + if frappe.db.table_exists("Loan Security Pledge") and not frappe.db.table_exists( + "Loan Collateral Assignment" + ): + frappe.rename_doc("DocType", "Loan Security Pledge", "Loan Collateral Assignment", force=True) + frappe.reload_doc("loan_management", "doctype", "loan_collateral_assignment") + + if frappe.db.table_exists("Loan Security Unpledge") and not frappe.db.table_exists( + "Loan Collateral Deassignment" + ): + frappe.rename_doc( + "DocType", "Loan Security Unpledge", "Loan Collateral Deassignment", force=True + ) + frappe.reload_doc("loan_management", "doctype", "loan_collateral_deassignment") diff --git a/lending/patches/v15_0/set_collateral_type.py b/lending/patches/v15_0/set_collateral_type.py index caed9bea..9715efcf 100644 --- a/lending/patches/v15_0/set_collateral_type.py +++ b/lending/patches/v15_0/set_collateral_type.py @@ -5,9 +5,14 @@ def execute(): - lsp = frappe.qb.DocType("Loan Security Pledge") - frappe.qb.update(lsp).set(lsp.collateral_type, "Loan Security").where( - lsp.collateral_type.isnull() + lca = frappe.qb.DocType("Loan Collateral Assignment") + frappe.qb.update(lca).set(lca.collateral_type, "Loan Security").where( + lca.collateral_type.isnull() + ).run() + + lcd = frappe.qb.DocType("Loan Collateral Deassignment") + frappe.qb.update(lcd).set(lcd.collateral_type, "Loan Security").where( + lcd.collateral_type.isnull() ).run() la = frappe.qb.DocType("Loan Application") From 7358fb816a526766414d47ca466d6df28dbfbdbf Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Thu, 19 Oct 2023 08:34:08 +0530 Subject: [PATCH 3/5] ci: use mariadb:10.6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 113a97b9..0362a096 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: services: mysql: - image: mariadb:10.3 + image: mariadb:10.6 env: MYSQL_ALLOW_EMPTY_PASSWORD: YES ports: From b417cdbbb4b7a2de1cb30d70a1589765a9ed9678 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Thu, 19 Oct 2023 08:40:15 +0530 Subject: [PATCH 4/5] ci: minor fix --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0362a096..6c564a87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,13 +36,13 @@ jobs: strategy: fail-fast: false - name: Server + name: Python Unit Tests services: mysql: image: mariadb:10.6 env: - MYSQL_ALLOW_EMPTY_PASSWORD: YES + MARIADB_ROOT_PASSWORD: 'root' ports: - 3306:3306 options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3 From e093ebce77f255d4385344acf3af9844b58ba595 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Thu, 19 Oct 2023 08:45:51 +0530 Subject: [PATCH 5/5] ci: minor fix --- .github/helper/site_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/helper/site_config.json b/.github/helper/site_config.json index 28847e40..21fdbf31 100644 --- a/.github/helper/site_config.json +++ b/.github/helper/site_config.json @@ -9,7 +9,7 @@ "mail_password": "test", "admin_password": "admin", "root_login": "root", - "root_password": "travis", + "root_password": "root", "host_name": "http://test_site:8000", "install_apps": ["payments", "erpnext"], "throttle_user_limit": 100