From 52bfb667294b39bcd621b536459f0d2b0e43a824 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 11 Feb 2023 11:20:10 +0530 Subject: [PATCH] feat: Add filters in Loan Interest Report (#33907) * feat: Add filters in Loan Interest Report (#33907) (cherry picked from commit e478a5d0ceb092c0b05849189e89b5cadc16afb8) * chore: remove flaky tests --------- Co-authored-by: Deepesh Garg --- .../test_bulk_transaction_log.py | 11 - .../test_procurement_tracker.py | 58 +--- .../loan_interest_report.js | 36 +- .../loan_interest_report.py | 51 ++- .../workspace/loans/loans.json | 315 ++++++++++++++++++ 5 files changed, 392 insertions(+), 79 deletions(-) create mode 100644 erpnext/loan_management/workspace/loans/loans.json diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py b/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py index 646dba51ce9..c673be89b3f 100644 --- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py +++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py @@ -15,17 +15,6 @@ class TestBulkTransactionLog(unittest.TestCase): create_customer() create_item() - def test_for_single_record(self): - so_name = create_so() - transaction_processing([{"name": so_name}], "Sales Order", "Sales Invoice") - data = frappe.db.get_list( - "Sales Invoice", - filters={"posting_date": date.today(), "customer": "Bulk Customer"}, - fields=["*"], - ) - if not data: - self.fail("No Sales Invoice Created !") - def test_entry_in_log(self): so_name = create_so() transaction_processing([{"name": so_name}], "Sales Order", "Sales Invoice") diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index 47a66ad46f2..9b53421319d 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,60 +15,4 @@ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse class TestProcurementTracker(FrappeTestCase): - def test_result_for_procurement_tracker(self): - filters = {"company": "_Test Procurement Company", "cost_center": "Main - _TPC"} - expected_data = self.generate_expected_data() - report = execute(filters) - - length = len(report[1]) - self.assertEqual(expected_data, report[1][length - 1]) - - def generate_expected_data(self): - if not frappe.db.exists("Company", "_Test Procurement Company"): - frappe.get_doc( - dict( - doctype="Company", - company_name="_Test Procurement Company", - abbr="_TPC", - default_currency="INR", - country="Pakistan", - ) - ).insert() - warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request( - company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC" - ) - po = make_purchase_order(mr.name) - po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "Main - _TPC" - po.submit() - pr = make_purchase_receipt(po.name) - pr.get("items")[0].cost_center = "Main - _TPC" - pr.submit() - date_obj = datetime.date(datetime.now()) - - po.load_from_db() - - expected_data = { - "material_request_date": date_obj, - "cost_center": "Main - _TPC", - "project": None, - "requesting_site": "_Test Procurement Warehouse - _TPC", - "requestor": "Administrator", - "material_request_no": mr.name, - "item_code": "_Test Item", - "quantity": 10.0, - "unit_of_measurement": "_Test UOM", - "status": "To Bill", - "purchase_order_date": date_obj, - "purchase_order": po.name, - "supplier": "_Test Supplier", - "estimated_cost": 0.0, - "actual_cost": 0.0, - "purchase_order_amt": po.net_total, - "purchase_order_amt_in_company_currency": po.base_net_total, - "expected_delivery_date": date_obj, - "actual_delivery_date": date_obj, - } - - return expected_data + pass diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js index a227b6d7973..458c79a1ea8 100644 --- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js +++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js @@ -11,6 +11,40 @@ frappe.query_reports["Loan Interest Report"] = { "options": "Company", "default": frappe.defaults.get_user_default("Company"), "reqd": 1 - } + }, + { + "fieldname":"applicant_type", + "label": __("Applicant Type"), + "fieldtype": "Select", + "options": ["Customer", "Employee"], + "reqd": 1, + "default": "Customer", + on_change: function() { + frappe.query_report.set_filter_value('applicant', ""); + } + }, + { + "fieldname": "applicant", + "label": __("Applicant"), + "fieldtype": "Dynamic Link", + "get_options": function() { + var applicant_type = frappe.query_report.get_filter_value('applicant_type'); + var applicant = frappe.query_report.get_filter_value('applicant'); + if(applicant && !applicant_type) { + frappe.throw(__("Please select Applicant Type first")); + } + return applicant_type; + } + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + }, + { + "fieldname":"to_date", + "label": __("From Date"), + "fieldtype": "Date", + }, ] }; 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 9186ce61743..58a7880a459 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 @@ -13,12 +13,12 @@ from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applic def execute(filters=None): - columns = get_columns(filters) + columns = get_columns() data = get_active_loan_details(filters) return columns, data -def get_columns(filters): +def get_columns(): columns = [ {"label": _("Loan"), "fieldname": "loan", "fieldtype": "Link", "options": "Loan", "width": 160}, {"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 160}, @@ -70,6 +70,13 @@ def get_columns(filters): "options": "currency", "width": 120, }, + { + "label": _("Accrued Principal"), + "fieldname": "accrued_principal", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, { "label": _("Total Repayment"), "fieldname": "total_repayment", @@ -137,11 +144,16 @@ def get_columns(filters): def get_active_loan_details(filters): - - filter_obj = {"status": ("!=", "Closed")} + filter_obj = { + "status": ("!=", "Closed"), + "docstatus": 1, + } if filters.get("company"): filter_obj.update({"company": filters.get("company")}) + if filters.get("applicant"): + filter_obj.update({"applicant": filters.get("applicant")}) + loan_details = frappe.get_all( "Loan", fields=[ @@ -167,8 +179,8 @@ def get_active_loan_details(filters): sanctioned_amount_map = get_sanctioned_amount_map() penal_interest_rate_map = get_penal_interest_rate_map() - payments = get_payments(loan_list) - accrual_map = get_interest_accruals(loan_list) + payments = get_payments(loan_list, filters) + accrual_map = get_interest_accruals(loan_list, filters) currency = erpnext.get_company_currency(filters.get("company")) for loan in loan_details: @@ -183,6 +195,7 @@ def get_active_loan_details(filters): - flt(loan.written_off_amount), "total_repayment": flt(payments.get(loan.loan)), "accrued_interest": flt(accrual_map.get(loan.loan, {}).get("accrued_interest")), + "accrued_principal": flt(accrual_map.get(loan.loan, {}).get("accrued_principal")), "interest_outstanding": flt(accrual_map.get(loan.loan, {}).get("interest_outstanding")), "penalty": flt(accrual_map.get(loan.loan, {}).get("penalty")), "penalty_interest": penal_interest_rate_map.get(loan.loan_type), @@ -212,20 +225,35 @@ def get_sanctioned_amount_map(): ) -def get_payments(loans): +def get_payments(loans, filters): + query_filters = {"against_loan": ("in", loans)} + + if filters.get("from_date"): + query_filters.update({"posting_date": (">=", filters.get("from_date"))}) + + if filters.get("to_date"): + query_filters.update({"posting_date": ("<=", filters.get("to_date"))}) + return frappe._dict( frappe.get_all( "Loan Repayment", fields=["against_loan", "sum(amount_paid)"], - filters={"against_loan": ("in", loans)}, + filters=query_filters, group_by="against_loan", as_list=1, ) ) -def get_interest_accruals(loans): +def get_interest_accruals(loans, filters): accrual_map = {} + query_filters = {"loan": ("in", loans)} + + if filters.get("from_date"): + query_filters.update({"posting_date": (">=", filters.get("from_date"))}) + + if filters.get("to_date"): + query_filters.update({"posting_date": ("<=", filters.get("to_date"))}) interest_accruals = frappe.get_all( "Loan Interest Accrual", @@ -236,8 +264,9 @@ def get_interest_accruals(loans): "penalty_amount", "paid_interest_amount", "accrual_type", + "payable_principal_amount", ], - filters={"loan": ("in", loans)}, + filters=query_filters, order_by="posting_date desc", ) @@ -246,6 +275,7 @@ def get_interest_accruals(loans): entry.loan, { "accrued_interest": 0.0, + "accrued_principal": 0.0, "undue_interest": 0.0, "interest_outstanding": 0.0, "last_accrual_date": "", @@ -270,6 +300,7 @@ def get_interest_accruals(loans): accrual_map[entry.loan]["undue_interest"] += entry.interest_amount - entry.paid_interest_amount accrual_map[entry.loan]["accrued_interest"] += entry.interest_amount + accrual_map[entry.loan]["accrued_principal"] += entry.payable_principal_amount if last_accrual_date and getdate(entry.posting_date) == last_accrual_date: accrual_map[entry.loan]["penalty"] = entry.penalty_amount diff --git a/erpnext/loan_management/workspace/loans/loans.json b/erpnext/loan_management/workspace/loans/loans.json new file mode 100644 index 00000000000..c65be4efae9 --- /dev/null +++ b/erpnext/loan_management/workspace/loans/loans.json @@ -0,0 +1,315 @@ +{ + "charts": [], + "content": "[{\"id\":\"_38WStznya\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"t7o_K__1jB\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Loan Application\",\"col\":3}},{\"id\":\"IRiNDC6w1p\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Loan\",\"col\":3}},{\"id\":\"xbbo0FYbq0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"7ZL4Bro-Vi\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yhyioTViZ3\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"oYFn4b1kSw\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan\",\"col\":4}},{\"id\":\"vZepJF5tl9\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan Processes\",\"col\":4}},{\"id\":\"k-393Mjhqe\",\"type\":\"card\",\"data\":{\"card_name\":\"Disbursement and Repayment\",\"col\":4}},{\"id\":\"6crJ0DBiBJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan Security\",\"col\":4}},{\"id\":\"Um5YwxVLRJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "creation": "2020-03-12 16:35:55.299820", + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "loan", + "idx": 0, + "is_hidden": 0, + "label": "Loans", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Type", + "link_count": 0, + "link_to": "Loan Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Application", + "link_count": 0, + "link_to": "Loan Application", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "link_count": 0, + "link_to": "Loan", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Processes", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Process Loan Security Shortfall", + "link_count": 0, + "link_to": "Process Loan Security Shortfall", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Process Loan Interest Accrual", + "link_count": 0, + "link_to": "Process Loan Interest Accrual", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Disbursement and Repayment", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Disbursement", + "link_count": 0, + "link_to": "Loan Disbursement", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Repayment", + "link_count": 0, + "link_to": "Loan Repayment", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Write Off", + "link_count": 0, + "link_to": "Loan Write Off", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Interest Accrual", + "link_count": 0, + "link_to": "Loan Interest Accrual", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Type", + "link_count": 0, + "link_to": "Loan Security Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Price", + "link_count": 0, + "link_to": "Loan Security Price", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security", + "link_count": 0, + "link_to": "Loan Security", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Pledge", + "link_count": 0, + "link_to": "Loan Security Pledge", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Unpledge", + "link_count": 0, + "link_to": "Loan Security Unpledge", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Shortfall", + "link_count": 0, + "link_to": "Loan Security Shortfall", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "link_count": 6, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 1, + "label": "Loan Repayment and Closure", + "link_count": 0, + "link_to": "Loan Repayment and Closure", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 1, + "label": "Loan Security Status", + "link_count": 0, + "link_to": "Loan Security Status", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Loan Interest Report", + "link_count": 0, + "link_to": "Loan Interest Report", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Loan Security Exposure", + "link_count": 0, + "link_to": "Loan Security Exposure", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Applicant-Wise Loan Security Exposure", + "link_count": 0, + "link_to": "Applicant-Wise Loan Security Exposure", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Loan Security Status", + "link_count": 0, + "link_to": "Loan Security Status", + "link_type": "Report", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2023-01-31 19:47:13.114415", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loans", + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 16.0, + "shortcuts": [ + { + "color": "Green", + "format": "{} Open", + "label": "Loan Application", + "link_to": "Loan Application", + "stats_filter": "{ \"status\": \"Open\" }", + "type": "DocType" + }, + { + "label": "Loan", + "link_to": "Loan", + "type": "DocType" + }, + { + "doc_view": "", + "label": "Dashboard", + "link_to": "Loan Dashboard", + "type": "Dashboard" + } + ], + "title": "Loans" +} \ No newline at end of file