From 900c878e03be5d66a6ff5929826f4e44bb471f26 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 28 Apr 2022 14:54:42 +0530 Subject: [PATCH 01/23] update loan interest accrual types --- .../loan_interest_accrual/loan_interest_accrual.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json index 30e2328442..c05deeb938 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json @@ -159,7 +159,7 @@ "fieldname": "accrual_type", "fieldtype": "Select", "label": "Accrual Type", - "options": "Regular\nRepayment\nDisbursement" + "options": "Regular\nRepayment\nDisbursement\nInterest Credit\nInterest Debit\nRefund" }, { "fieldname": "penalty_amount", @@ -185,10 +185,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-04-19 18:26:38.871889", + "modified": "2022-04-28 14:46:38.131536", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Interest Accrual", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -225,5 +226,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From a81da2ea85990de18a8d954d76bdb590320c52dd Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Fri, 24 Jun 2022 16:07:16 +0530 Subject: [PATCH 02/23] Add more loan interest accrual types --- .../doctype/loan_interest_accrual/loan_interest_accrual.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json index c05deeb938..5fbe26c3d3 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json @@ -158,8 +158,9 @@ { "fieldname": "accrual_type", "fieldtype": "Select", + "in_filter": 1, "label": "Accrual Type", - "options": "Regular\nRepayment\nDisbursement\nInterest Credit\nInterest Debit\nRefund" + "options": "Regular\nRepayment\nDisbursement\nCredit Adjustment\nDebit Adjustment\nRefund" }, { "fieldname": "penalty_amount", @@ -185,7 +186,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-04-28 14:46:38.131536", + "modified": "2022-06-24 15:45:07.014679", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Interest Accrual", From 88cd780ca17ae1312fa3b4d7a183201cb479e467 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Fri, 24 Jun 2022 17:47:15 +0530 Subject: [PATCH 03/23] Add refund amount to loan --- erpnext/loan_management/doctype/loan/loan.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index ef78a640aa..0797084380 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -48,6 +48,7 @@ "total_payment", "total_principal_paid", "written_off_amount", + "refund_amount", "column_break_19", "total_interest_payable", "total_amount_paid", @@ -379,12 +380,20 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "fieldname": "refund_amount", + "fieldtype": "Currency", + "label": "Refund amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-03-10 11:50:31.957360", + "modified": "2022-06-24 16:17:57.018272", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", From e1682965c55b638ca1e0ca6182e59b1b607bdf6c Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Mon, 27 Jun 2022 15:21:39 +0530 Subject: [PATCH 04/23] Add Loan Refund doctype --- .../doctype/loan_refund/__init__.py | 0 .../doctype/loan_refund/loan_refund.js | 8 + .../doctype/loan_refund/loan_refund.json | 176 ++++++++++++++++++ .../doctype/loan_refund/loan_refund.py | 95 ++++++++++ .../doctype/loan_refund/test_loan_refund.py | 9 + 5 files changed, 288 insertions(+) create mode 100644 erpnext/loan_management/doctype/loan_refund/__init__.py create mode 100644 erpnext/loan_management/doctype/loan_refund/loan_refund.js create mode 100644 erpnext/loan_management/doctype/loan_refund/loan_refund.json create mode 100644 erpnext/loan_management/doctype/loan_refund/loan_refund.py create mode 100644 erpnext/loan_management/doctype/loan_refund/test_loan_refund.py diff --git a/erpnext/loan_management/doctype/loan_refund/__init__.py b/erpnext/loan_management/doctype/loan_refund/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.js b/erpnext/loan_management/doctype/loan_refund/loan_refund.js new file mode 100644 index 0000000000..f108bf7a28 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.js @@ -0,0 +1,8 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Loan Refund', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.json b/erpnext/loan_management/doctype/loan_refund/loan_refund.json new file mode 100644 index 0000000000..f78e55e9f9 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.json @@ -0,0 +1,176 @@ +{ + "actions": [], + "autoname": "LM-RF-.#####", + "creation": "2022-06-24 15:51:03.165498", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan", + "applicant_type", + "applicant", + "column_break_3", + "company", + "posting_date", + "accounting_dimensions_section", + "cost_center", + "section_break_9", + "refund_account", + "column_break_11", + "refund_amount", + "reference_number", + "amended_from" + ], + "fields": [ + { + "fieldname": "loan", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan", + "options": "Loan", + "reqd": 1 + }, + { + "fetch_from": "loan.applicant_type", + "fieldname": "applicant_type", + "fieldtype": "Select", + "label": "Applicant Type", + "options": "Employee\nMember\nCustomer", + "read_only": 1 + }, + { + "fetch_from": "loan.applicant", + "fieldname": "applicant", + "fieldtype": "Dynamic Link", + "label": "Applicant ", + "options": "applicant_type", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "loan.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Refund Details" + }, + { + "fieldname": "refund_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Refund Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "refund_amount", + "fieldtype": "Currency", + "label": "Refund Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Refund", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Refund", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "reference_number", + "fieldtype": "Data", + "label": "Reference Number" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-06-24 16:13:48.793486", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Refund", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Loan Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.py b/erpnext/loan_management/doctype/loan_refund/loan_refund.py new file mode 100644 index 0000000000..2a7f47871f --- /dev/null +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.py @@ -0,0 +1,95 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class LoanRefund(Document): + """ + Add refund if total repayment is more than that is owed. + """ + def validate(self): + self.set_missing_values() + self.validate_refund_amount() + + def set_missing_values(self): + if not self.cost_center: + self.cost_center = erpnext.get_default_cost_center(self.company) + + def validate_refund_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value( + "Loan", + self.loan, + ["total_payment", "total_principal_paid", "total_interest_payable", "written_off_amount"], + ) + + excess_amount = flt( + flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount), + precision, + ) + + if self.refund_amount > excess_amount: + frappe.throw(_( + "Refund amount cannot be greater than excess amount {}".format( + excess_amount + ))) + + def on_submit(self): + self.update_outstanding_amount() + self.make_gl_entries() + + def on_cancel(self): + self.update_outstanding_amount(cancel=1) + self.ignore_linked_doctypes = ["GL Entry"] + self.make_gl_entries(cancel=1) + + def update_outstanding_amount(self, cancel=0): + refund_amount = frappe.db.get_value("Loan", self.loan, "refund_amount") + + if cancel: + refund_amount -= self.refund_amount + else: + refund_amount += self.refund_amount + + frappe.db.set_value("Loan", self.loan, "refund_amount", refund_amount) + + def make_gl_entries(self, cancel=0): + gl_entries = [] + loan_details = frappe.get_doc("Loan", self.loan) + + gl_entries.append( + self.get_gl_dict( + { + "account": self.refund_account, + "against": loan_details.loan_account, + "credit": self.refund_amount, + "credit_in_account_currency": self.refund_amount, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("Against Loan:") + self.loan, + "cost_center": self.cost_center, + "posting_date": getdate(self.posting_date), + } + ) + ) + + gl_entries.append( + self.get_gl_dict( + { + "account": loan_details.loan_account, + "party_type": loan_details.applicant_type, + "party": loan_details.applicant, + "against": self.refund_account, + "debit": self.refund_amount, + "debit_in_account_currency": self.refund_amount, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("Against Loan:") + self.loan, + "cost_center": self.cost_center, + "posting_date": getdate(self.posting_date), + } + ) + ) + + make_gl_entries(gl_entries, cancel=cancel, merge_entries=False) diff --git a/erpnext/loan_management/doctype/loan_refund/test_loan_refund.py b/erpnext/loan_management/doctype/loan_refund/test_loan_refund.py new file mode 100644 index 0000000000..de2f9e1372 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_refund/test_loan_refund.py @@ -0,0 +1,9 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLoanRefund(FrappeTestCase): + pass From 2e8f0565147c2e176c8ab43fbc79686b238e4d23 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Tue, 28 Jun 2022 17:51:11 +0530 Subject: [PATCH 05/23] Add Loan Balance Adjustment doctype --- .../loan_balance_adjustment/__init__.py | 0 .../loan_balance_adjustment.js | 8 + .../loan_balance_adjustment.json | 190 ++++++++++++++++++ .../loan_balance_adjustment.py | 132 ++++++++++++ .../test_loan_balance_adjustment.py | 9 + 5 files changed, 339 insertions(+) create mode 100644 erpnext/loan_management/doctype/loan_balance_adjustment/__init__.py create mode 100644 erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js create mode 100644 erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json create mode 100644 erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py create mode 100644 erpnext/loan_management/doctype/loan_balance_adjustment/test_loan_balance_adjustment.py diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/__init__.py b/erpnext/loan_management/doctype/loan_balance_adjustment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js new file mode 100644 index 0000000000..8aec63ad75 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js @@ -0,0 +1,8 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Loan Balance Adjustment', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json new file mode 100644 index 0000000000..5fb25defce --- /dev/null +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json @@ -0,0 +1,190 @@ +{ + "actions": [], + "autoname": "LM-ADJ-.#####", + "creation": "2022-06-28 14:48:47.736269", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan", + "applicant_type", + "applicant", + "column_break_3", + "company", + "posting_date", + "accounting_dimensions_section", + "cost_center", + "section_break_9", + "adjustment_account", + "column_break_11", + "adjustment_type", + "amount", + "reference_number", + "remarks", + "amended_from" + ], + "fields": [ + { + "fieldname": "loan", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan", + "options": "Loan", + "reqd": 1 + }, + { + "fetch_from": "loan.applicant_type", + "fieldname": "applicant_type", + "fieldtype": "Select", + "label": "Applicant Type", + "options": "Employee\nMember\nCustomer", + "read_only": 1 + }, + { + "fetch_from": "loan.applicant", + "fieldname": "applicant", + "fieldtype": "Dynamic Link", + "label": "Applicant ", + "options": "applicant_type", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "loan.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Adjustment Details" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "reference_number", + "fieldtype": "Data", + "label": "Reference Number" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Balance Adjustment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Balance Adjustment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "adjustment_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Adjustment Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fieldname": "adjustment_type", + "fieldtype": "Select", + "label": "Adjustment Type", + "options": "Credit Adjustment\nDebit Adjustment", + "reqd": 1 + }, + { + "fieldname": "remarks", + "fieldtype": "Data", + "label": "Remarks" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-06-28 14:54:52.792592", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Balance Adjustment", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Loan Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py new file mode 100644 index 0000000000..8f98ce64d8 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -0,0 +1,132 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class LoanBalanceAdjustment(Document): + """ + Add credit/debit adjustments to loan ledger. + """ + def validate(self): + self.set_missing_values() + + def on_submit(self): + self.set_status_and_amounts() + self.make_gl_entries() + + def on_cancel(self): + self.set_status_and_amounts(cancel=1) + self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ["GL Entry"] + + def set_missing_values(self): + if not self.posting_date: + self.posting_date = nowdate() + + if not self.cost_center: + self.cost_center = erpnext.get_default_cost_center(self.company) + + if not self.posting_date: + self.posting_date = self.posting_date or nowdate() + + def set_status_and_amounts(self, cancel=0): + loan_details = frappe.get_all( + "Loan", + fields=[ + "loan_amount", + "disbursed_amount", + "total_payment", + "total_principal_paid", + "total_interest_payable", + "status", + "is_term_loan", + "is_secured_loan", + ], + filters={"name": self.against_loan}, + )[0] + + if cancel: + disbursed_amount = self.get_values_on_cancel(loan_details) + else: + disbursed_amount = self.get_values_on_submit(loan_details) + + frappe.db.set_value( + "Loan", + self.against_loan, + { + "disbursed_amount": disbursed_amount, + }, + ) + + def get_values_on_cancel(self, loan_details): + if self.adjustment_type == "Credit Adjustment": + disbursed_amount = loan_details.disbursed_amount - self.amount + elif self.adjustment_type == "Debit Adjustment": + disbursed_amount = loan_details.disbursed_amount + self.amount + + return disbursed_amount + + def get_values_on_submit(self, loan_details): + if self.adjustment_type == "Credit": + disbursed_amount = loan_details.disbursed_amount + self.amount + elif self.adjustment_type == "Debit": + disbursed_amount = loan_details.disbursed_amount - self.amount + + total_payment = loan_details.total_payment + + if loan_details.status in ("Disbursed", "Partially Disbursed") and not loan_details.is_term_loan: + process_loan_interest_accrual_for_demand_loans( + posting_date=add_days(self.posting_date, -1), + loan=self.against_loan, + accrual_type=self.adjustment_type, + ) + + return disbursed_amount + + def make_gl_entries(self, cancel=0, adv_adj=0): + gle_map = [] + + loan_entry = { + "account": self.loan_account, + "against": self.adjustment_account, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": _("{} against loan:".format(self.adjustment_type)) \ + + self.against_loan, + "cost_center": self.cost_center, + "party_type": self.applicant_type, + "party": self.applicant, + "posting_date": self.posting_date, + } + company_entry = { + "account": self.adjustment_account, + "against": self.loan_account, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": _("{} against loan:".format(self.adjustment_type)) \ + + self.against_loan, + "cost_center": self.cost_center, + "posting_date": self.posting_date, + } + if self.adjustment_type == "Credit Adjustment": + loan_entry["credit"] = self.amount + loan_entry["credit_in_account_currency"] = self.amount + + company_entry["debit"] = self.amount + company_entry["debit_in_account_currency"] = self.amount + + elif self.adjustment_type == "Debit Adjustment": + loan_entry["debit"] = self.amount + loan_entry["debit_in_account_currency"] = self.amount + + company_entry["credit"] = self.amount + company_entry["credit_in_account_currency"] = self.amount + + + gle_map.append(self.get_gl_dict(loan_entry)) + + gle_map.append(self.get_gl_dict(company_entry)) + + if gle_map: + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/test_loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/test_loan_balance_adjustment.py new file mode 100644 index 0000000000..7658d7b215 --- /dev/null +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/test_loan_balance_adjustment.py @@ -0,0 +1,9 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLoanBalanceAdjustment(FrappeTestCase): + pass From 5c0a25012c602ed0d47136468e3b0bee11ddf5dd Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Wed, 29 Jun 2022 14:16:23 +0530 Subject: [PATCH 06/23] feat: add adjustment amount to loan - fix: bugs in loan balance adjustment --- .../loan_management/doctype/loan/loan.json | 13 +- .../loan_balance_adjustment.json | 11 +- .../loan_balance_adjustment.py | 258 +++++++++--------- .../doctype/loan_repayment/loan_repayment.py | 2 + .../process_loan_interest_accrual.json | 6 +- 5 files changed, 160 insertions(+), 130 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index 0797084380..f4ab7ed835 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -49,6 +49,7 @@ "total_principal_paid", "written_off_amount", "refund_amount", + "adjustment_amount", "column_break_19", "total_interest_payable", "total_amount_paid", @@ -388,12 +389,20 @@ "no_copy": 1, "options": "Company:company:default_currency", "read_only": 1 - } + }, + { + "fieldname": "adjustment_amount", + "fieldtype": "Currency", + "label": "Adjustment amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-24 16:17:57.018272", + "modified": "2022-06-29 13:17:57.018272", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json index 5fb25defce..35be555cb7 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json @@ -115,12 +115,13 @@ "read_only": 1 }, { + "fetch_from": "loan.payment_account", "fieldname": "adjustment_account", "fieldtype": "Link", "in_list_view": 1, "label": "Adjustment Account", "options": "Account", - "reqd": 1 + "read_only": 1 }, { "fieldname": "amount", @@ -129,6 +130,14 @@ "options": "Company:company:default_currency", "reqd": 1 }, + { + "fetch_from": "loan.loan_account", + "fieldname": "loan_account", + "fieldtype": "Link", + "label": "Loan Account", + "options": "Account", + "read_only": 1 + }, { "fieldname": "adjustment_type", "fieldtype": "Select", diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 8f98ce64d8..4b57060ce8 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -1,132 +1,140 @@ # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe -from frappe.model.document import Document - -class LoanBalanceAdjustment(Document): - """ - Add credit/debit adjustments to loan ledger. - """ - def validate(self): - self.set_missing_values() - - def on_submit(self): - self.set_status_and_amounts() - self.make_gl_entries() - - def on_cancel(self): - self.set_status_and_amounts(cancel=1) - self.make_gl_entries(cancel=1) - self.ignore_linked_doctypes = ["GL Entry"] - - def set_missing_values(self): - if not self.posting_date: - self.posting_date = nowdate() - - if not self.cost_center: - self.cost_center = erpnext.get_default_cost_center(self.company) - - if not self.posting_date: - self.posting_date = self.posting_date or nowdate() - - def set_status_and_amounts(self, cancel=0): - loan_details = frappe.get_all( - "Loan", - fields=[ - "loan_amount", - "disbursed_amount", - "total_payment", - "total_principal_paid", - "total_interest_payable", - "status", - "is_term_loan", - "is_secured_loan", - ], - filters={"name": self.against_loan}, - )[0] - - if cancel: - disbursed_amount = self.get_values_on_cancel(loan_details) - else: - disbursed_amount = self.get_values_on_submit(loan_details) - - frappe.db.set_value( - "Loan", - self.against_loan, - { - "disbursed_amount": disbursed_amount, - }, - ) - - def get_values_on_cancel(self, loan_details): - if self.adjustment_type == "Credit Adjustment": - disbursed_amount = loan_details.disbursed_amount - self.amount - elif self.adjustment_type == "Debit Adjustment": - disbursed_amount = loan_details.disbursed_amount + self.amount - - return disbursed_amount - - def get_values_on_submit(self, loan_details): - if self.adjustment_type == "Credit": - disbursed_amount = loan_details.disbursed_amount + self.amount - elif self.adjustment_type == "Debit": - disbursed_amount = loan_details.disbursed_amount - self.amount - - total_payment = loan_details.total_payment - - if loan_details.status in ("Disbursed", "Partially Disbursed") and not loan_details.is_term_loan: - process_loan_interest_accrual_for_demand_loans( - posting_date=add_days(self.posting_date, -1), - loan=self.against_loan, - accrual_type=self.adjustment_type, - ) - - return disbursed_amount - - def make_gl_entries(self, cancel=0, adv_adj=0): - gle_map = [] - - loan_entry = { - "account": self.loan_account, - "against": self.adjustment_account, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": _("{} against loan:".format(self.adjustment_type)) \ - + self.against_loan, - "cost_center": self.cost_center, - "party_type": self.applicant_type, - "party": self.applicant, - "posting_date": self.posting_date, - } - company_entry = { - "account": self.adjustment_account, - "against": self.loan_account, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": _("{} against loan:".format(self.adjustment_type)) \ - + self.against_loan, - "cost_center": self.cost_center, - "posting_date": self.posting_date, - } - if self.adjustment_type == "Credit Adjustment": - loan_entry["credit"] = self.amount - loan_entry["credit_in_account_currency"] = self.amount - - company_entry["debit"] = self.amount - company_entry["debit_in_account_currency"] = self.amount - - elif self.adjustment_type == "Debit Adjustment": - loan_entry["debit"] = self.amount - loan_entry["debit_in_account_currency"] = self.amount - - company_entry["credit"] = self.amount - company_entry["credit_in_account_currency"] = self.amount +import frappe +import erpnext +from frappe import _ +from frappe.utils import nowdate, add_days +from erpnext.controllers.accounts_controller import AccountsController +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( + process_loan_interest_accrual_for_demand_loans, +) +from erpnext.accounts.general_ledger import make_gl_entries - gle_map.append(self.get_gl_dict(loan_entry)) +class LoanBalanceAdjustment(AccountsController): + """ + Add credit/debit adjustments to loan ledger. + """ - gle_map.append(self.get_gl_dict(company_entry)) + def validate(self): + if self.amount == 0: + frappe.throw(_("Amount cannot be zero")) + if self.amount < 0: + frappe.throw(_("Amount cannot be negative")) + self.set_missing_values() - if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj) + def on_submit(self): + self.set_status_and_amounts() + self.make_gl_entries() + + def on_cancel(self): + 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.posting_date: + self.posting_date = nowdate() + + if not self.cost_center: + self.cost_center = erpnext.get_default_cost_center(self.company) + + def set_status_and_amounts(self, cancel=0): + loan_details = frappe.get_all( + "Loan", + fields=[ + "loan_amount", + "adjustment_amount", + "total_payment", + "total_principal_paid", + "total_interest_payable", + "status", + "is_term_loan", + "is_secured_loan", + ], + filters={"name": self.loan}, + )[0] + + if cancel: + adjustment_amount = self.get_values_on_cancel(loan_details) + else: + adjustment_amount = self.get_values_on_submit(loan_details) + + frappe.db.set_value( + "Loan", + self.loan, + { + "adjustment_amount": adjustment_amount, + }, + ) + + def get_values_on_cancel(self, loan_details): + if self.adjustment_type == "Credit Adjustment": + adjustment_amount = loan_details.adjustment_amount - self.amount + elif self.adjustment_type == "Debit Adjustment": + adjustment_amount = loan_details.adjustment_amount + self.amount + + return adjustment_amount + + def get_values_on_submit(self, loan_details): + if self.adjustment_type == "Credit Adjustment": + adjustment_amount = loan_details.adjustment_amount + self.amount + elif self.adjustment_type == "Debit Adjustment": + adjustment_amount = loan_details.adjustment_amount - self.amount + + if ( + loan_details.status in ("Disbursed", "Partially Disbursed") + and not loan_details.is_term_loan + ): + process_loan_interest_accrual_for_demand_loans( + posting_date=add_days(self.posting_date, -1), + loan=self.loan, + accrual_type=self.adjustment_type, + ) + + return adjustment_amount + + def make_gl_entries(self, cancel=0, adv_adj=0): + gle_map = [] + + loan_entry = { + "account": self.loan_account, + "against": self.adjustment_account, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("{} against loan:".format(self.adjustment_type)) + self.loan, + "cost_center": self.cost_center, + "party_type": self.applicant_type, + "party": self.applicant, + "posting_date": self.posting_date, + } + company_entry = { + "account": self.adjustment_account, + "against": self.loan_account, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("{} against loan:".format(self.adjustment_type)) + self.loan, + "cost_center": self.cost_center, + "posting_date": self.posting_date, + } + if self.adjustment_type == "Credit Adjustment": + loan_entry["credit"] = self.amount + loan_entry["credit_in_account_currency"] = self.amount + + company_entry["debit"] = self.amount + company_entry["debit_in_account_currency"] = self.amount + + elif self.adjustment_type == "Debit Adjustment": + loan_entry["debit"] = self.amount + loan_entry["debit_in_account_currency"] = self.amount + + company_entry["credit"] = self.amount + company_entry["credit_in_account_currency"] = self.amount + + gle_map.append(self.get_gl_dict(loan_entry)) + + gle_map.append(self.get_gl_dict(company_entry)) + + if gle_map: + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 51f40d9a20..32b257b7d0 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -623,6 +623,7 @@ def get_pending_principal_amount(loan): if loan.status in ("Disbursed", "Closed") or loan.disbursed_amount >= loan.loan_amount: pending_principal_amount = ( flt(loan.total_payment) + + flt(loan.adjustment_amount) - flt(loan.total_principal_paid) - flt(loan.total_interest_payable) - flt(loan.written_off_amount) @@ -630,6 +631,7 @@ def get_pending_principal_amount(loan): else: pending_principal_amount = ( flt(loan.disbursed_amount) + + flt(loan.adjustment_amount) - flt(loan.total_principal_paid) - flt(loan.total_interest_payable) - flt(loan.written_off_amount) diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json index 828df2e35f..7fc4736216 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json @@ -54,17 +54,18 @@ "fieldtype": "Select", "hidden": 1, "label": "Accrual Type", - "options": "Regular\nRepayment\nDisbursement", + "options": "Regular\nRepayment\nDisbursement\nCredit Adjustment\nDebit Adjustment\nRefund", "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-11-06 13:28:51.478909", + "modified": "2022-06-29 11:19:33.203088", "modified_by": "Administrator", "module": "Loan Management", "name": "Process Loan Interest Accrual", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -98,5 +99,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From d6f632a7701894e6e09f3ee10ce8dccb22e848c5 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 11:29:17 +0530 Subject: [PATCH 07/23] Seperate credit and debit adjust amount fields in Loan --- .../loan_management/doctype/loan/loan.json | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index f4ab7ed835..68d511ef40 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -49,11 +49,12 @@ "total_principal_paid", "written_off_amount", "refund_amount", - "adjustment_amount", "column_break_19", "total_interest_payable", "total_amount_paid", - "amended_from" + "amended_from", + "credit_adjustment_amount", + "debit_adjustment_amount" ], "fields": [ { @@ -391,18 +392,22 @@ "read_only": 1 }, { - "fieldname": "adjustment_amount", - "fieldtype": "Currency", - "label": "Adjustment amount", - "no_copy": 1, - "options": "Company:company:default_currency", - "read_only": 1 - } + "fieldname": "credit_adjustment_amount", + "fieldtype": "Currency", + "label": "Credit Adjustment Amount", + "options": "Company:company:default_currency" + }, + { + "fieldname": "debit_adjustment_amount", + "fieldtype": "Currency", + "label": "Debit Adjustment Amount", + "options": "Company:company:default_currency" + } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-29 13:17:57.018272", + "modified": "2022-06-30 11:27:54.281113", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", From 7d468a8778a32abf14f5373f51380f96cf8ce24e Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 11:42:01 +0530 Subject: [PATCH 08/23] Use new adjustment amount fields --- .../loan_balance_adjustment.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 4b57060ce8..69ab6468be 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -61,27 +61,28 @@ class LoanBalanceAdjustment(AccountsController): else: adjustment_amount = self.get_values_on_submit(loan_details) + if self.adjustment_type == "Credit Adjustment": + adj_field = "credit_adjustment_amount" + elif self.adjustment_type == "Debit Adjustment": + adj_field = "debit_adjustment_amount" + frappe.db.set_value( - "Loan", - self.loan, - { - "adjustment_amount": adjustment_amount, - }, + "Loan", self.loan, {adj_field: adjustment_amount} ) def get_values_on_cancel(self, loan_details): if self.adjustment_type == "Credit Adjustment": - adjustment_amount = loan_details.adjustment_amount - self.amount + adjustment_amount = loan_details.credit_adjustment_amount - self.amount elif self.adjustment_type == "Debit Adjustment": - adjustment_amount = loan_details.adjustment_amount + self.amount + adjustment_amount = loan_details.debit_adjustment_amount - self.amount return adjustment_amount def get_values_on_submit(self, loan_details): if self.adjustment_type == "Credit Adjustment": - adjustment_amount = loan_details.adjustment_amount + self.amount + adjustment_amount = loan_details.credit_adjustment_amount + self.amount elif self.adjustment_type == "Debit Adjustment": - adjustment_amount = loan_details.adjustment_amount - self.amount + adjustment_amount = loan_details.debit_adjustment_amount + self.amount if ( loan_details.status in ("Disbursed", "Partially Disbursed") @@ -98,8 +99,10 @@ class LoanBalanceAdjustment(AccountsController): def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] + loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") + loan_entry = { - "account": self.loan_account, + "account": loan_account, "against": self.adjustment_account, "against_voucher_type": "Loan", "against_voucher": self.loan, @@ -111,7 +114,7 @@ class LoanBalanceAdjustment(AccountsController): } company_entry = { "account": self.adjustment_account, - "against": self.loan_account, + "against": loan_account, "against_voucher_type": "Loan", "against_voucher": self.loan, "remarks": _("{} against loan:".format(self.adjustment_type)) + self.loan, From 6febcd529b088aaa86991b11b9941b537ac67e9d Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 11:42:33 +0530 Subject: [PATCH 09/23] Remove loan account field from doctype --- .../loan_balance_adjustment.json | 384 +++++++++--------- 1 file changed, 187 insertions(+), 197 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json index 35be555cb7..cfbde0f6ce 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json @@ -1,199 +1,189 @@ { - "actions": [], - "autoname": "LM-ADJ-.#####", - "creation": "2022-06-28 14:48:47.736269", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "loan", - "applicant_type", - "applicant", - "column_break_3", - "company", - "posting_date", - "accounting_dimensions_section", - "cost_center", - "section_break_9", - "adjustment_account", - "column_break_11", - "adjustment_type", - "amount", - "reference_number", - "remarks", - "amended_from" - ], - "fields": [ - { - "fieldname": "loan", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Loan", - "options": "Loan", - "reqd": 1 - }, - { - "fetch_from": "loan.applicant_type", - "fieldname": "applicant_type", - "fieldtype": "Select", - "label": "Applicant Type", - "options": "Employee\nMember\nCustomer", - "read_only": 1 - }, - { - "fetch_from": "loan.applicant", - "fieldname": "applicant", - "fieldtype": "Dynamic Link", - "label": "Applicant ", - "options": "applicant_type", - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fetch_from": "loan.company", - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Posting Date", - "reqd": 1 - }, - { - "collapsible": 1, - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "label": "Adjustment Details" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "fieldname": "reference_number", - "fieldtype": "Data", - "label": "Reference Number" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Loan Balance Adjustment", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Loan Balance Adjustment", - "print_hide": 1, - "read_only": 1 - }, - { - "fetch_from": "loan.payment_account", - "fieldname": "adjustment_account", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Adjustment Account", - "options": "Account", - "read_only": 1 - }, - { - "fieldname": "amount", - "fieldtype": "Currency", - "label": "Amount", - "options": "Company:company:default_currency", - "reqd": 1 - }, - { - "fetch_from": "loan.loan_account", - "fieldname": "loan_account", - "fieldtype": "Link", - "label": "Loan Account", - "options": "Account", - "read_only": 1 - }, - { - "fieldname": "adjustment_type", - "fieldtype": "Select", - "label": "Adjustment Type", - "options": "Credit Adjustment\nDebit Adjustment", - "reqd": 1 - }, - { - "fieldname": "remarks", - "fieldtype": "Data", - "label": "Remarks" - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2022-06-28 14:54:52.792592", - "modified_by": "Administrator", - "module": "Loan Management", - "name": "Loan Balance Adjustment", - "naming_rule": "Expression (old style)", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Loan Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "states": [], - "track_changes": 1 + "actions": [], + "autoname": "LM-ADJ-.#####", + "creation": "2022-06-28 14:48:47.736269", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loan", + "applicant_type", + "applicant", + "column_break_3", + "company", + "posting_date", + "accounting_dimensions_section", + "cost_center", + "section_break_9", + "adjustment_account", + "column_break_11", + "adjustment_type", + "amount", + "reference_number", + "remarks", + "amended_from" + ], + "fields": [ + { + "fieldname": "loan", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan", + "options": "Loan", + "reqd": 1 + }, + { + "fetch_from": "loan.applicant_type", + "fieldname": "applicant_type", + "fieldtype": "Select", + "label": "Applicant Type", + "options": "Employee\nMember\nCustomer", + "read_only": 1 + }, + { + "fetch_from": "loan.applicant", + "fieldname": "applicant", + "fieldtype": "Dynamic Link", + "label": "Applicant ", + "options": "applicant_type", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "loan.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Adjustment Details" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "reference_number", + "fieldtype": "Data", + "label": "Reference Number" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Balance Adjustment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan Balance Adjustment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "adjustment_account", + "fieldtype": "Link", + "label": "Adjustment Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fieldname": "adjustment_type", + "fieldtype": "Select", + "label": "Adjustment Type", + "options": "Credit\nDebit", + "reqd": 1 + }, + { + "fieldname": "remarks", + "fieldtype": "Data", + "label": "Remarks" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-06-30 11:38:16.631994", + "modified_by": "Administrator", + "module": "Loan Management", + "name": "Loan Balance Adjustment", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Loan Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 } \ No newline at end of file From 7d6e4898c48a28405d78c58f41f7f99b316989be Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 11:59:38 +0530 Subject: [PATCH 10/23] Update list view for Accrual and Shortfall --- .../loan_interest_accrual.json | 9 ++++++- .../loan_security_shortfall.json | 25 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json index 5fbe26c3d3..08dc98c830 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json @@ -35,12 +35,15 @@ { "fieldname": "loan", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Loan", "options": "Loan" }, { "fieldname": "posting_date", "fieldtype": "Date", + "in_list_view": 1, "label": "Posting Date" }, { @@ -75,6 +78,8 @@ "fetch_from": "loan.applicant", "fieldname": "applicant", "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Applicant", "options": "applicant_type" }, @@ -159,6 +164,8 @@ "fieldname": "accrual_type", "fieldtype": "Select", "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, "label": "Accrual Type", "options": "Regular\nRepayment\nDisbursement\nCredit Adjustment\nDebit Adjustment\nRefund" }, @@ -186,7 +193,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-24 15:45:07.014679", + "modified": "2022-06-30 11:51:31.911794", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Interest Accrual", diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.json b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.json index 99b5c72b2d..d4007cb62d 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.json +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.json @@ -7,6 +7,8 @@ "engine": "InnoDB", "field_order": [ "loan", + "applicant_type", + "applicant", "status", "column_break_3", "shortfall_time", @@ -23,6 +25,8 @@ { "fieldname": "loan", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Loan ", "options": "Loan", "read_only": 1 @@ -91,17 +95,35 @@ { "fieldname": "shortfall_percentage", "fieldtype": "Percent", + "in_list_view": 1, "label": "Shortfall Percentage", "read_only": 1 + }, + { + "fetch_from": "loan.applicant_type", + "fieldname": "applicant_type", + "fieldtype": "Select", + "label": "Applicant Type", + "options": "Employee\nMember\nCustomer" + }, + { + "fetch_from": "loan.applicant", + "fieldname": "applicant", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Applicant", + "options": "applicant_type" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-04-01 08:13:43.263772", + "modified": "2022-06-30 11:57:09.378089", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Security Shortfall", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -132,5 +154,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From 27a8e16b28e5876b4dc965039addf626956f5bbe Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 12:04:45 +0530 Subject: [PATCH 11/23] Add NPA checkbox in Loan --- erpnext/loan_management/doctype/loan/loan.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index 68d511ef40..fb5f1339ba 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -49,12 +49,13 @@ "total_principal_paid", "written_off_amount", "refund_amount", + "debit_adjustment_amount", + "credit_adjustment_amount", + "is_npa", "column_break_19", "total_interest_payable", "total_amount_paid", - "amended_from", - "credit_adjustment_amount", - "debit_adjustment_amount" + "amended_from" ], "fields": [ { @@ -402,12 +403,19 @@ "fieldtype": "Currency", "label": "Debit Adjustment Amount", "options": "Company:company:default_currency" + }, + { + "default": "0", + "description": "Mark Loan as a Nonperforming asset", + "fieldname": "is_npa", + "fieldtype": "Check", + "label": "Is NPA" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-30 11:27:54.281113", + "modified": "2022-06-30 12:04:13.728880", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", From 74dbf8c5d93ce389813f88e43d01b5d70b899148 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 13:50:40 +0530 Subject: [PATCH 12/23] Add reference number to repayment remarks --- .../doctype/loan_repayment/loan_repayment.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 32b257b7d0..3c2e5003fe 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -386,15 +386,19 @@ class LoanRepayment(AccountsController): def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] - if self.shortfall_amount and self.amount_paid > self.shortfall_amount: - remarks = _("Shortfall Repayment of {0}.
Repayment against Loan: {1}").format( + remarks = "Shortfall repayment of {0}.
Repayment against loan {1}".format( self.shortfall_amount, self.against_loan ) elif self.shortfall_amount: - remarks = _("Shortfall Repayment of {0}").format(self.shortfall_amount) + remarks = "Shortfall repayment of {0} against loan {1}".format( + self.shortfall_amount, self.against_loan + ) else: - remarks = _("Repayment against Loan:") + " " + self.against_loan + remarks = "Repayment against loan " + self.against_loan + + if self.reference_number: + remarks += "with reference no. {}".format(self.reference_number) if self.repay_from_salary: payment_account = self.payroll_payable_account @@ -445,7 +449,7 @@ class LoanRepayment(AccountsController): "debit_in_account_currency": self.amount_paid, "against_voucher_type": "Loan", "against_voucher": self.against_loan, - "remarks": remarks, + "remarks": _(remarks), "cost_center": self.cost_center, "posting_date": getdate(self.posting_date), } @@ -463,7 +467,7 @@ class LoanRepayment(AccountsController): "credit_in_account_currency": self.amount_paid, "against_voucher_type": "Loan", "against_voucher": self.against_loan, - "remarks": remarks, + "remarks": _(remarks), "cost_center": self.cost_center, "posting_date": getdate(self.posting_date), } From a1a51ce1a6b6ab2415078eb94d5b19b66b8e16ed Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 13:58:13 +0530 Subject: [PATCH 13/23] Add ref no to balance adjustment remarks --- .../loan_balance_adjustment/loan_balance_adjustment.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 69ab6468be..a582cfecc4 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -100,13 +100,18 @@ class LoanBalanceAdjustment(AccountsController): gle_map = [] loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") + remarks = "{} against loan {}".format( + self.adjustment_type.capitalize(), self.loan + ) + if self.reference_number: + remarks += "with reference no. {}".format(self.reference_number) loan_entry = { "account": loan_account, "against": self.adjustment_account, "against_voucher_type": "Loan", "against_voucher": self.loan, - "remarks": _("{} against loan:".format(self.adjustment_type)) + self.loan, + "remarks": _(remarks), "cost_center": self.cost_center, "party_type": self.applicant_type, "party": self.applicant, @@ -117,7 +122,7 @@ class LoanBalanceAdjustment(AccountsController): "against": loan_account, "against_voucher_type": "Loan", "against_voucher": self.loan, - "remarks": _("{} against loan:".format(self.adjustment_type)) + self.loan, + "remarks": _(remarks), "cost_center": self.cost_center, "posting_date": self.posting_date, } From 1b5b2138ee95d376d77b07ea2f4c06946971a13a Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 30 Jun 2022 14:04:19 +0530 Subject: [PATCH 14/23] Use adjustment amounts in pending principal amnt --- .../doctype/loan_repayment/loan_repayment.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 3c2e5003fe..7b0028deb4 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -627,7 +627,8 @@ def get_pending_principal_amount(loan): if loan.status in ("Disbursed", "Closed") or loan.disbursed_amount >= loan.loan_amount: pending_principal_amount = ( flt(loan.total_payment) - + flt(loan.adjustment_amount) + + flt(loan.debit_adjustment_amount) + - flt(loan.credit_adjustment_amount) - flt(loan.total_principal_paid) - flt(loan.total_interest_payable) - flt(loan.written_off_amount) @@ -635,7 +636,8 @@ def get_pending_principal_amount(loan): else: pending_principal_amount = ( flt(loan.disbursed_amount) - + flt(loan.adjustment_amount) + + flt(loan.debit_adjustment_amount) + - flt(loan.credit_adjustment_amount) - flt(loan.total_principal_paid) - flt(loan.total_interest_payable) - flt(loan.written_off_amount) From 8434ec09c39b08652d7f6463bd77111e9b3a974a Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 7 Jul 2022 17:20:21 +0530 Subject: [PATCH 15/23] fix lint --- .../loan_balance_adjustment.py | 219 +++++++++--------- 1 file changed, 109 insertions(+), 110 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index a582cfecc4..92e9b6acde 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -7,142 +7,141 @@ from frappe import _ from frappe.utils import nowdate, add_days from erpnext.controllers.accounts_controller import AccountsController from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( - process_loan_interest_accrual_for_demand_loans, + process_loan_interest_accrual_for_demand_loans, ) from erpnext.accounts.general_ledger import make_gl_entries class LoanBalanceAdjustment(AccountsController): - """ - Add credit/debit adjustments to loan ledger. - """ + """ + Add credit/debit adjustments to loan ledger. + """ - def validate(self): - if self.amount == 0: - frappe.throw(_("Amount cannot be zero")) - if self.amount < 0: - frappe.throw(_("Amount cannot be negative")) - self.set_missing_values() + def validate(self): + if self.amount == 0: + frappe.throw(_("Amount cannot be zero")) + if self.amount < 0: + frappe.throw(_("Amount cannot be negative")) + self.set_missing_values() - def on_submit(self): - self.set_status_and_amounts() - self.make_gl_entries() + def on_submit(self): + self.set_status_and_amounts() + self.make_gl_entries() - def on_cancel(self): - self.set_status_and_amounts(cancel=1) - self.make_gl_entries(cancel=1) - self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] + def on_cancel(self): + 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.posting_date: - self.posting_date = nowdate() + def set_missing_values(self): + if not self.posting_date: + self.posting_date = nowdate() - if not self.cost_center: - self.cost_center = erpnext.get_default_cost_center(self.company) + if not self.cost_center: + self.cost_center = erpnext.get_default_cost_center(self.company) - def set_status_and_amounts(self, cancel=0): - loan_details = frappe.get_all( - "Loan", - fields=[ - "loan_amount", - "adjustment_amount", - "total_payment", - "total_principal_paid", - "total_interest_payable", - "status", - "is_term_loan", - "is_secured_loan", - ], - filters={"name": self.loan}, - )[0] + def set_status_and_amounts(self, cancel=0): + loan_details = frappe.get_all( + "Loan", + fields=[ + "loan_amount", + "adjustment_amount", + "total_payment", + "total_principal_paid", + "total_interest_payable", + "status", + "is_term_loan", + "is_secured_loan", + ], + filters={"name": self.loan}, + )[0] - if cancel: - adjustment_amount = self.get_values_on_cancel(loan_details) - else: - adjustment_amount = self.get_values_on_submit(loan_details) + if cancel: + adjustment_amount = self.get_values_on_cancel(loan_details) + else: + adjustment_amount = self.get_values_on_submit(loan_details) - if self.adjustment_type == "Credit Adjustment": - adj_field = "credit_adjustment_amount" - elif self.adjustment_type == "Debit Adjustment": - adj_field = "debit_adjustment_amount" + if self.adjustment_type == "Credit Adjustment": + adj_field = "credit_adjustment_amount" + elif self.adjustment_type == "Debit Adjustment": + adj_field = "debit_adjustment_amount" - frappe.db.set_value( - "Loan", self.loan, {adj_field: adjustment_amount} - ) + frappe.db.set_value( + "Loan", self.loan, {adj_field: adjustment_amount} + ) - def get_values_on_cancel(self, loan_details): - if self.adjustment_type == "Credit Adjustment": - adjustment_amount = loan_details.credit_adjustment_amount - self.amount - elif self.adjustment_type == "Debit Adjustment": - adjustment_amount = loan_details.debit_adjustment_amount - self.amount + def get_values_on_cancel(self, loan_details): + if self.adjustment_type == "Credit Adjustment": + adjustment_amount = loan_details.credit_adjustment_amount - self.amount + elif self.adjustment_type == "Debit Adjustment": + adjustment_amount = loan_details.debit_adjustment_amount - self.amount - return adjustment_amount + return adjustment_amount - def get_values_on_submit(self, loan_details): - if self.adjustment_type == "Credit Adjustment": - adjustment_amount = loan_details.credit_adjustment_amount + self.amount - elif self.adjustment_type == "Debit Adjustment": - adjustment_amount = loan_details.debit_adjustment_amount + self.amount + def get_values_on_submit(self, loan_details): + if self.adjustment_type == "Credit Adjustment": + adjustment_amount = loan_details.credit_adjustment_amount + self.amount + elif self.adjustment_type == "Debit Adjustment": + adjustment_amount = loan_details.debit_adjustment_amount + self.amount - if ( - loan_details.status in ("Disbursed", "Partially Disbursed") - and not loan_details.is_term_loan - ): - process_loan_interest_accrual_for_demand_loans( - posting_date=add_days(self.posting_date, -1), - loan=self.loan, - accrual_type=self.adjustment_type, - ) + if ( + loan_details.status in ("Disbursed", "Partially Disbursed") + and not loan_details.is_term_loan + ): + process_loan_interest_accrual_for_demand_loans( + posting_date=add_days(self.posting_date, -1), + loan=self.loan, + accrual_type=self.adjustment_type, + ) - return adjustment_amount + return adjustment_amount - def make_gl_entries(self, cancel=0, adv_adj=0): - gle_map = [] - - loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") - remarks = "{} against loan {}".format( - self.adjustment_type.capitalize(), self.loan - ) + def make_gl_entries(self, cancel=0, adv_adj=0): + gle_map = [] + loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") + remarks = "{} against loan {}".format( + self.adjustment_type.capitalize(), self.loan + ) if self.reference_number: remarks += "with reference no. {}".format(self.reference_number) - loan_entry = { - "account": loan_account, - "against": self.adjustment_account, - "against_voucher_type": "Loan", - "against_voucher": self.loan, - "remarks": _(remarks), - "cost_center": self.cost_center, - "party_type": self.applicant_type, - "party": self.applicant, - "posting_date": self.posting_date, - } - company_entry = { - "account": self.adjustment_account, - "against": loan_account, - "against_voucher_type": "Loan", - "against_voucher": self.loan, - "remarks": _(remarks), - "cost_center": self.cost_center, - "posting_date": self.posting_date, - } - if self.adjustment_type == "Credit Adjustment": - loan_entry["credit"] = self.amount - loan_entry["credit_in_account_currency"] = self.amount + loan_entry = { + "account": loan_account, + "against": self.adjustment_account, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _(remarks), + "cost_center": self.cost_center, + "party_type": self.applicant_type, + "party": self.applicant, + "posting_date": self.posting_date, + } + company_entry = { + "account": self.adjustment_account, + "against": loan_account, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _(remarks), + "cost_center": self.cost_center, + "posting_date": self.posting_date, + } + if self.adjustment_type == "Credit Adjustment": + loan_entry["credit"] = self.amount + loan_entry["credit_in_account_currency"] = self.amount - company_entry["debit"] = self.amount - company_entry["debit_in_account_currency"] = self.amount + company_entry["debit"] = self.amount + company_entry["debit_in_account_currency"] = self.amount - elif self.adjustment_type == "Debit Adjustment": - loan_entry["debit"] = self.amount - loan_entry["debit_in_account_currency"] = self.amount + elif self.adjustment_type == "Debit Adjustment": + loan_entry["debit"] = self.amount + loan_entry["debit_in_account_currency"] = self.amount - company_entry["credit"] = self.amount - company_entry["credit_in_account_currency"] = self.amount + company_entry["credit"] = self.amount + company_entry["credit_in_account_currency"] = self.amount - gle_map.append(self.get_gl_dict(loan_entry)) + gle_map.append(self.get_gl_dict(loan_entry)) - gle_map.append(self.get_gl_dict(company_entry)) + gle_map.append(self.get_gl_dict(company_entry)) - if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) + if gle_map: + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) From 0ed6382ab6e3250801489fcb2c87e281c97143cb Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Thu, 7 Jul 2022 17:40:50 +0530 Subject: [PATCH 16/23] fix indent and imports --- .../loan_balance_adjustment.py | 130 +++++++++--------- .../doctype/loan_refund/loan_refund.py | 14 +- 2 files changed, 71 insertions(+), 73 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 92e9b6acde..e714545a31 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -2,14 +2,15 @@ # For license information, please see license.txt import frappe -import erpnext from frappe import _ -from frappe.utils import nowdate, add_days +from frappe.utils import add_days, nowdate + +import erpnext +from erpnext.accounts.general_ledger import make_gl_entries from erpnext.controllers.accounts_controller import AccountsController from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( - process_loan_interest_accrual_for_demand_loans, + process_loan_interest_accrual_for_demand_loans, ) -from erpnext.accounts.general_ledger import make_gl_entries class LoanBalanceAdjustment(AccountsController): @@ -19,9 +20,9 @@ class LoanBalanceAdjustment(AccountsController): def validate(self): if self.amount == 0: - frappe.throw(_("Amount cannot be zero")) + frappe.throw(_("Amount cannot be zero")) if self.amount < 0: - frappe.throw(_("Amount cannot be negative")) + frappe.throw(_("Amount cannot be negative")) self.set_missing_values() def on_submit(self): @@ -35,113 +36,106 @@ class LoanBalanceAdjustment(AccountsController): def set_missing_values(self): if not self.posting_date: - self.posting_date = nowdate() + self.posting_date = nowdate() if not self.cost_center: - self.cost_center = erpnext.get_default_cost_center(self.company) + self.cost_center = erpnext.get_default_cost_center(self.company) def set_status_and_amounts(self, cancel=0): loan_details = frappe.get_all( - "Loan", - fields=[ - "loan_amount", - "adjustment_amount", - "total_payment", - "total_principal_paid", - "total_interest_payable", - "status", - "is_term_loan", - "is_secured_loan", - ], - filters={"name": self.loan}, + "Loan", + fields=[ + "loan_amount", + "adjustment_amount", + "total_payment", + "total_principal_paid", + "total_interest_payable", + "status", + "is_term_loan", + "is_secured_loan", + ], + filters={"name": self.loan}, )[0] if cancel: - adjustment_amount = self.get_values_on_cancel(loan_details) + adjustment_amount = self.get_values_on_cancel(loan_details) else: - adjustment_amount = self.get_values_on_submit(loan_details) + adjustment_amount = self.get_values_on_submit(loan_details) if self.adjustment_type == "Credit Adjustment": - adj_field = "credit_adjustment_amount" + adj_field = "credit_adjustment_amount" elif self.adjustment_type == "Debit Adjustment": - adj_field = "debit_adjustment_amount" + adj_field = "debit_adjustment_amount" - frappe.db.set_value( - "Loan", self.loan, {adj_field: adjustment_amount} - ) + frappe.db.set_value("Loan", self.loan, {adj_field: adjustment_amount}) def get_values_on_cancel(self, loan_details): if self.adjustment_type == "Credit Adjustment": - adjustment_amount = loan_details.credit_adjustment_amount - self.amount + adjustment_amount = loan_details.credit_adjustment_amount - self.amount elif self.adjustment_type == "Debit Adjustment": - adjustment_amount = loan_details.debit_adjustment_amount - self.amount + adjustment_amount = loan_details.debit_adjustment_amount - self.amount return adjustment_amount def get_values_on_submit(self, loan_details): if self.adjustment_type == "Credit Adjustment": - adjustment_amount = loan_details.credit_adjustment_amount + self.amount + adjustment_amount = loan_details.credit_adjustment_amount + self.amount elif self.adjustment_type == "Debit Adjustment": - adjustment_amount = loan_details.debit_adjustment_amount + self.amount + adjustment_amount = loan_details.debit_adjustment_amount + self.amount - if ( - loan_details.status in ("Disbursed", "Partially Disbursed") - and not loan_details.is_term_loan - ): - process_loan_interest_accrual_for_demand_loans( - posting_date=add_days(self.posting_date, -1), - loan=self.loan, - accrual_type=self.adjustment_type, - ) + if loan_details.status in ("Disbursed", "Partially Disbursed") and not loan_details.is_term_loan: + process_loan_interest_accrual_for_demand_loans( + posting_date=add_days(self.posting_date, -1), + loan=self.loan, + accrual_type=self.adjustment_type, + ) return adjustment_amount def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] loan_account = frappe.db.get_value("Loan", self.loan, "loan_account") - remarks = "{} against loan {}".format( - self.adjustment_type.capitalize(), self.loan - ) + remarks = "{} against loan {}".format(self.adjustment_type.capitalize(), self.loan) if self.reference_number: remarks += "with reference no. {}".format(self.reference_number) loan_entry = { - "account": loan_account, - "against": self.adjustment_account, - "against_voucher_type": "Loan", - "against_voucher": self.loan, - "remarks": _(remarks), - "cost_center": self.cost_center, - "party_type": self.applicant_type, - "party": self.applicant, - "posting_date": self.posting_date, + "account": loan_account, + "against": self.adjustment_account, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _(remarks), + "cost_center": self.cost_center, + "party_type": self.applicant_type, + "party": self.applicant, + "posting_date": self.posting_date, } company_entry = { - "account": self.adjustment_account, - "against": loan_account, - "against_voucher_type": "Loan", - "against_voucher": self.loan, - "remarks": _(remarks), - "cost_center": self.cost_center, - "posting_date": self.posting_date, + "account": self.adjustment_account, + "against": loan_account, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _(remarks), + "cost_center": self.cost_center, + "posting_date": self.posting_date, } if self.adjustment_type == "Credit Adjustment": - loan_entry["credit"] = self.amount - loan_entry["credit_in_account_currency"] = self.amount + loan_entry["credit"] = self.amount + loan_entry["credit_in_account_currency"] = self.amount - company_entry["debit"] = self.amount - company_entry["debit_in_account_currency"] = self.amount + company_entry["debit"] = self.amount + company_entry["debit_in_account_currency"] = self.amount elif self.adjustment_type == "Debit Adjustment": - loan_entry["debit"] = self.amount - loan_entry["debit_in_account_currency"] = self.amount + loan_entry["debit"] = self.amount + loan_entry["debit_in_account_currency"] = self.amount - company_entry["credit"] = self.amount - company_entry["credit_in_account_currency"] = self.amount + company_entry["credit"] = self.amount + company_entry["credit_in_account_currency"] = self.amount gle_map.append(self.get_gl_dict(loan_entry)) gle_map.append(self.get_gl_dict(company_entry)) if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.py b/erpnext/loan_management/doctype/loan_refund/loan_refund.py index 2a7f47871f..f63cb27512 100644 --- a/erpnext/loan_management/doctype/loan_refund/loan_refund.py +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.py @@ -1,13 +1,20 @@ # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ from frappe.model.document import Document +from frappe.utils import cint, flt, getdate + +import erpnext +from erpnext.accounts.general_ledger import make_gl_entries + class LoanRefund(Document): """ Add refund if total repayment is more than that is owed. """ + def validate(self): self.set_missing_values() self.validate_refund_amount() @@ -30,10 +37,7 @@ class LoanRefund(Document): ) if self.refund_amount > excess_amount: - frappe.throw(_( - "Refund amount cannot be greater than excess amount {}".format( - excess_amount - ))) + frappe.throw(_("Refund amount cannot be greater than excess amount {}".format(excess_amount))) def on_submit(self): self.update_outstanding_amount() From 6cc09ef3a203724c73d4fa583cf4b7720dacdd65 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Fri, 8 Jul 2022 16:38:41 +0530 Subject: [PATCH 17/23] fix adjustment amount field name --- .../doctype/loan_balance_adjustment/loan_balance_adjustment.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index e714545a31..2b5b1f7426 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -46,7 +46,8 @@ class LoanBalanceAdjustment(AccountsController): "Loan", fields=[ "loan_amount", - "adjustment_amount", + "credit_adjustment_amount", + "debit_adjustment_amount", "total_payment", "total_principal_paid", "total_interest_payable", From 245b0c7818587b21874cdc5ddadd6eac787beeb2 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Fri, 8 Jul 2022 16:49:19 +0530 Subject: [PATCH 18/23] Update adjustment_type field options --- .../loan_balance_adjustment/loan_balance_adjustment.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json index cfbde0f6ce..80c3389ba1 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.json @@ -132,7 +132,7 @@ "fieldname": "adjustment_type", "fieldtype": "Select", "label": "Adjustment Type", - "options": "Credit\nDebit", + "options": "Credit Adjustment\nDebit Adjustment", "reqd": 1 }, { @@ -144,7 +144,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-30 11:38:16.631994", + "modified": "2022-07-08 16:48:54.480066", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Balance Adjustment", From 9df1413adb94ec59cae19e3cdf1afe62ce8b5ce6 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Fri, 8 Jul 2022 17:52:36 +0530 Subject: [PATCH 19/23] fix excess amount calculation in loan refund --- .../doctype/loan_refund/loan_refund.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.py b/erpnext/loan_management/doctype/loan_refund/loan_refund.py index f63cb27512..766cdb6f04 100644 --- a/erpnext/loan_management/doctype/loan_refund/loan_refund.py +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.py @@ -4,10 +4,13 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cint, flt, getdate +from frappe.utils import cint, getdate import erpnext from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, +) class LoanRefund(Document): @@ -31,10 +34,12 @@ class LoanRefund(Document): ["total_payment", "total_principal_paid", "total_interest_payable", "written_off_amount"], ) - excess_amount = flt( - flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount), - precision, - ) + loan = frappe.get_doc("Loan", self.loan) + pending_amount = get_pending_principal_amount(loan) + if pending_amount >= 0: + frappe.throw(_("No excess amount to refund.")) + else: + excess_amount = pending_amount * -1 if self.refund_amount > excess_amount: frappe.throw(_("Refund amount cannot be greater than excess amount {}".format(excess_amount))) From 35f2717ad2d1e5c8b40455d3c58636fd6f51c5e0 Mon Sep 17 00:00:00 2001 From: Labeeb Mattra Date: Fri, 8 Jul 2022 17:53:29 +0530 Subject: [PATCH 20/23] Consider refund_amount in pending principal amount --- .../loan_management/doctype/loan_repayment/loan_repayment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 7b0028deb4..e7d6ca8d70 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -396,7 +396,7 @@ class LoanRepayment(AccountsController): ) else: remarks = "Repayment against loan " + self.against_loan - + if self.reference_number: remarks += "with reference no. {}".format(self.reference_number) @@ -632,6 +632,7 @@ def get_pending_principal_amount(loan): - flt(loan.total_principal_paid) - flt(loan.total_interest_payable) - flt(loan.written_off_amount) + + flt(loan.refund_amount) ) else: pending_principal_amount = ( @@ -641,6 +642,7 @@ def get_pending_principal_amount(loan): - flt(loan.total_principal_paid) - flt(loan.total_interest_payable) - flt(loan.written_off_amount) + + flt(loan.refund_amount) ) return pending_principal_amount From 13b7ed1e2c3f22155d59026ebc23d72bc7cbd263 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Fri, 8 Jul 2022 17:58:36 +0530 Subject: [PATCH 21/23] fix: on cancel for loan refund --- erpnext/loan_management/doctype/loan_refund/loan_refund.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.py b/erpnext/loan_management/doctype/loan_refund/loan_refund.py index 766cdb6f04..903494c5ad 100644 --- a/erpnext/loan_management/doctype/loan_refund/loan_refund.py +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.py @@ -3,7 +3,7 @@ import frappe from frappe import _ -from frappe.model.document import Document +from erpnext.controllers.accounts_controller import AccountsController from frappe.utils import cint, getdate import erpnext @@ -13,7 +13,7 @@ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( ) -class LoanRefund(Document): +class LoanRefund(AccountsController): """ Add refund if total repayment is more than that is owed. """ @@ -50,7 +50,7 @@ class LoanRefund(Document): def on_cancel(self): self.update_outstanding_amount(cancel=1) - self.ignore_linked_doctypes = ["GL Entry"] + self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] self.make_gl_entries(cancel=1) def update_outstanding_amount(self, cancel=0): From d933ff5cf6c9bb7146f38b2c43b28b6edc48b8b0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 11 Jul 2022 21:50:01 +0530 Subject: [PATCH 22/23] chore: Linting Issues --- .../loan_balance_adjustment.py | 9 +++++---- .../doctype/loan_refund/loan_refund.py | 11 ++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py index 2b5b1f7426..0a576d6969 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.py @@ -42,9 +42,10 @@ class LoanBalanceAdjustment(AccountsController): self.cost_center = erpnext.get_default_cost_center(self.company) def set_status_and_amounts(self, cancel=0): - loan_details = frappe.get_all( + loan_details = frappe.db.get_value( "Loan", - fields=[ + self.loan, + [ "loan_amount", "credit_adjustment_amount", "debit_adjustment_amount", @@ -55,8 +56,8 @@ class LoanBalanceAdjustment(AccountsController): "is_term_loan", "is_secured_loan", ], - filters={"name": self.loan}, - )[0] + as_dict=1, + ) if cancel: adjustment_amount = self.get_values_on_cancel(loan_details) diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.py b/erpnext/loan_management/doctype/loan_refund/loan_refund.py index 903494c5ad..265c969211 100644 --- a/erpnext/loan_management/doctype/loan_refund/loan_refund.py +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.py @@ -3,11 +3,11 @@ import frappe from frappe import _ -from erpnext.controllers.accounts_controller import AccountsController -from frappe.utils import cint, getdate +from frappe.utils import getdate import erpnext from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.controllers.accounts_controller import AccountsController from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( get_pending_principal_amount, ) @@ -27,13 +27,6 @@ class LoanRefund(AccountsController): self.cost_center = erpnext.get_default_cost_center(self.company) def validate_refund_amount(self): - precision = cint(frappe.db.get_default("currency_precision")) or 2 - total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value( - "Loan", - self.loan, - ["total_payment", "total_principal_paid", "total_interest_payable", "written_off_amount"], - ) - loan = frappe.get_doc("Loan", self.loan) pending_amount = get_pending_principal_amount(loan) if pending_amount >= 0: From 0bac030ca7b798e0c87d0f71318f22a1bf6563e3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 11 Jul 2022 22:04:52 +0530 Subject: [PATCH 23/23] chore: Linting Issues --- erpnext/loan_management/doctype/loan_refund/loan_refund.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.py b/erpnext/loan_management/doctype/loan_refund/loan_refund.py index 265c969211..d7ab54ca97 100644 --- a/erpnext/loan_management/doctype/loan_refund/loan_refund.py +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.py @@ -35,7 +35,7 @@ class LoanRefund(AccountsController): excess_amount = pending_amount * -1 if self.refund_amount > excess_amount: - frappe.throw(_("Refund amount cannot be greater than excess amount {}".format(excess_amount))) + frappe.throw(_("Refund amount cannot be greater than excess amount {0}").format(excess_amount)) def on_submit(self): self.update_outstanding_amount()