From 5c0a25012c602ed0d47136468e3b0bee11ddf5dd Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Wed, 29 Jun 2022 14:16:23 +0530 Subject: [PATCH] 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