From 91eb9bb5c22510b3203471b2cd79484073a9449b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 24 Jan 2021 19:15:57 +0530 Subject: [PATCH] fix: Add loan to value field in Loan Interest Report --- .../applicant_wise_loan_security_exposure.py | 26 ++++++-- .../loan_interest_report.py | 61 ++++++++++++++++++- .../loan_security_exposure.py | 10 ++- 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py index 6d7c3b730d..f280402ce9 100644 --- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py +++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py @@ -26,6 +26,7 @@ def get_columns(filters): {"label": _("Disabled"), "fieldname": "disabled", "fieldtype": "Check", "width": 80}, {"label": _("Total Qty"), "fieldname": "total_qty", "fieldtype": "Float", "width": 100}, {"label": _("Latest Price"), "fieldname": "latest_price", "fieldtype": "Currency", "options": "currency", "width": 100}, + {"label": _("Price Valid Upto"), "fieldname": "price_valid_upto", "fieldtype": "Datetime", "width": 100}, {"label": _("Current Value"), "fieldname": "current_value", "fieldtype": "Currency", "options": "currency", "width": 100}, {"label": _("% Of Applicant Portfolio"), "fieldname": "portfolio_percent", "fieldtype": "Percentage", "width": 100}, {"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100}, @@ -43,13 +44,16 @@ def get_data(filters): for key, qty in iteritems(pledge_values): row = {} - current_value = flt(qty * loan_security_details.get(key[1])['latest_price']) + current_value = flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0)) + valid_upto = loan_security_details.get(key[1], {}).get('valid_upto') + row.update(loan_security_details.get(key[1])) row.update({ 'applicant_type': applicant_type_map.get(key[0]), 'applicant_name': key[0], 'total_qty': qty, 'current_value': current_value, + 'price_valid_upto': valid_upto, 'portfolio_percent': flt(current_value * 100 / total_value_map.get(key[0]), 2), 'currency': currency }) @@ -60,20 +64,30 @@ def get_data(filters): def get_loan_security_details(filters): security_detail_map = {} + loan_security_price_map = {} + lsp_validity_map = {} - loan_security_price_map = frappe._dict(frappe.db.sql(""" - SELECT loan_security, loan_security_price + loan_security_prices = frappe.db.sql(""" + SELECT loan_security, loan_security_price, valid_upto FROM `tabLoan Security Price` t1 WHERE valid_from >= (SELECT MAX(valid_from) FROM `tabLoan Security Price` t2 WHERE t1.loan_security = t2.loan_security) - """, as_list=1)) + """, as_dict=1) + + for security in loan_security_prices: + loan_security_price_map.setdefault(security.loan_security, security.loan_security_price) + lsp_validity_map.setdefault(security.loan_security, security.valid_upto) loan_security_details = frappe.get_all('Loan Security', fields=['name as loan_security', 'loan_security_code', 'loan_security_name', 'haircut', 'loan_security_type', 'disabled']) for security in loan_security_details: - security.update({'latest_price': flt(loan_security_price_map.get(security.loan_security))}) + security.update({ + 'latest_price': flt(loan_security_price_map.get(security.loan_security)), + 'valid_upto': lsp_validity_map.get(security.loan_security) + }) + security_detail_map.setdefault(security.loan_security, security) return security_detail_map @@ -118,6 +132,6 @@ def get_applicant_wise_total_loan_security_qty(filters, loan_security_details): applicant_wise_unpledges.get((security.applicant, security.loan_security), 0.0) total_value_map[security.applicant] += current_pledges.get((security.applicant, security.loan_security)) \ - * loan_security_details.get(security.loan_security)['latest_price'] + * loan_security_details.get(security.loan_security, {}).get('latest_price', 0) return current_pledges, total_value_map, applicant_type_map \ No newline at end of file diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py index aa0325ef35..2bfe6d3c33 100644 --- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py +++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py @@ -6,6 +6,8 @@ import frappe import erpnext from frappe import _ from frappe.utils import flt, getdate, add_days +from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \ + import get_loan_security_details def execute(filters=None): @@ -31,6 +33,7 @@ def get_columns(filters): {"label": _("Undue Booked Interest"), "fieldname": "undue_interest", "fieldtype": "Currency", "options": "currency", "width": 120}, {"label": _("Interest %"), "fieldname": "rate_of_interest", "fieldtype": "Percent", "width": 100}, {"label": _("Penalty Interest %"), "fieldname": "penalty_interest", "fieldtype": "Percent", "width": 100}, + {"label": _("Loan To Value Ratio"), "fieldname": "loan_to_value", "fieldtype": "Percent", "width": 100}, {"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100}, ] @@ -50,6 +53,9 @@ def get_active_loan_details(filters): loan_list = [d.loan for d in loan_details] + current_pledges = get_loan_wise_pledges(filters) + loan_wise_security_value = get_loan_wise_security_value(filters, current_pledges) + sanctioned_amount_map = get_sanctioned_amount_map() penal_interest_rate_map = get_penal_interest_rate_map() payments = get_payments(loan_list) @@ -67,12 +73,16 @@ def get_active_loan_details(filters): "penalty": flt(accrual_map.get(loan.loan, {}).get("penalty")), "penalty_interest": penal_interest_rate_map.get(loan.loan_type), "undue_interest": flt(accrual_map.get(loan.loan, {}).get("undue_interest")), + "loan_to_value": 0.0, "currency": currency }) loan['total_outstanding'] = loan['principal_outstanding'] + loan['interest_outstanding'] \ + loan['penalty'] + if loan_wise_security_value.get(loan.loan): + loan['loan_to_value'] = (loan['principal_outstanding'] * 100) / loan_wise_security_value.get(loan.loan) + return loan_details def get_sanctioned_amount_map(): @@ -121,4 +131,53 @@ def get_interest_accruals(loans): return accrual_map def get_penal_interest_rate_map(): - return frappe._dict(frappe.get_all("Loan Type", fields=["name", "penalty_interest_rate"], as_list=1)) \ No newline at end of file + return frappe._dict(frappe.get_all("Loan Type", fields=["name", "penalty_interest_rate"], as_list=1)) + +def get_loan_wise_pledges(filters): + loan_wise_unpledges = {} + current_pledges = {} + + conditions = "" + + if filters.get('company'): + conditions = "AND company = %(company)s" + + unpledges = frappe.db.sql(""" + SELECT up.loan, u.loan_security, sum(u.qty) as qty + FROM `tabLoan Security Unpledge` up, `tabUnpledge` u + WHERE u.parent = up.name + AND up.status = 'Approved' + {conditions} + GROUP BY up.loan + """.format(conditions=conditions), filters, as_dict=1) + + for unpledge in unpledges: + loan_wise_unpledges.setdefault((unpledge.loan, unpledge.loan_security), unpledge.qty) + + pledges = frappe.db.sql(""" + SELECT lp.loan, p.loan_security, sum(p.qty) as qty + FROM `tabLoan Security Pledge` lp, `tabPledge`p + WHERE p.parent = lp.name + AND lp.status = 'Pledged' + {conditions} + GROUP BY lp.loan + """.format(conditions=conditions), filters, as_dict=1) + + for security in pledges: + current_pledges.setdefault((security.loan, security.loan_security), security.qty) + current_pledges[(security.loan, security.loan_security)] -= \ + loan_wise_unpledges.get((security.loan, security.loan_security), 0.0) + + return current_pledges + +def get_loan_wise_security_value(filters, current_pledges): + loan_security_details = get_loan_security_details(filters) + loan_wise_security_value = {} + + for key in current_pledges: + qty = current_pledges.get(key) + loan_wise_security_value.setdefault(key[0], 0.0) + loan_wise_security_value[key[0]] += \ + flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0)) + + return loan_wise_security_value \ No newline at end of file diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py index 3ef10c0f41..ff88052df5 100644 --- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py +++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py @@ -24,6 +24,7 @@ def get_columns(filters): {"label": _("Disabled"), "fieldname": "disabled", "fieldtype": "Check", "width": 80}, {"label": _("Total Qty"), "fieldname": "total_qty", "fieldtype": "Float", "width": 100}, {"label": _("Latest Price"), "fieldname": "latest_price", "fieldtype": "Currency", "options": "currency", "width": 100}, + {"label": _("Price Valid Upto"), "fieldname": "price_valid_upto", "fieldtype": "Datetime", "width": 100}, {"label": _("Current Value"), "fieldname": "current_value", "fieldtype": "Currency", "options": "currency", "width": 100}, {"label": _("% Of Total Portfolio"), "fieldname": "portfolio_percent", "fieldtype": "Percentage", "width": 100}, {"label": _("Pledged Applicant Count"), "fieldname": "pledged_applicant_count", "fieldtype": "Percentage", "width": 100}, @@ -40,13 +41,16 @@ def get_data(filters): for security, value in iteritems(current_pledges): row = {} - current_value = flt(value['qty'] * loan_security_details.get(security)['latest_price']) + current_value = flt(value.get('qty', 0) * loan_security_details.get(security, {}).get('latest_price', 0)) + valid_upto = loan_security_details.get(security, {}).get('valid_upto') + row.update(loan_security_details.get(security)) row.update({ - 'total_qty': value['qty'], + 'total_qty': value.get('qty'), 'current_value': current_value, + 'price_valid_upto': valid_upto, 'portfolio_percent': flt(current_value * 100 / total_portfolio_value, 2), - 'pledged_applicant_count': value['applicant_count'], + 'pledged_applicant_count': value.get('applicant_count'), 'currency': currency })