Merge pull request #31543 from lebmatter/loan_balance_adjustments
feat: Loan balance adjustment doctypes
This commit is contained in:
commit
d7446fd3ae
@ -48,6 +48,10 @@
|
||||
"total_payment",
|
||||
"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",
|
||||
@ -379,12 +383,39 @@
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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-03-10 11:50:31.957360",
|
||||
"modified": "2022-06-30 12:04:13.728880",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan",
|
||||
|
@ -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) {
|
||||
|
||||
// }
|
||||
});
|
@ -0,0 +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
|
||||
},
|
||||
{
|
||||
"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 Adjustment\nDebit Adjustment",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Data",
|
||||
"label": "Remarks"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-07-08 16:48:54.480066",
|
||||
"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
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
class LoanBalanceAdjustment(AccountsController):
|
||||
"""
|
||||
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 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.db.get_value(
|
||||
"Loan",
|
||||
self.loan,
|
||||
[
|
||||
"loan_amount",
|
||||
"credit_adjustment_amount",
|
||||
"debit_adjustment_amount",
|
||||
"total_payment",
|
||||
"total_principal_paid",
|
||||
"total_interest_payable",
|
||||
"status",
|
||||
"is_term_loan",
|
||||
"is_secured_loan",
|
||||
],
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
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)
|
@ -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
|
@ -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"
|
||||
},
|
||||
@ -158,8 +163,11 @@
|
||||
{
|
||||
"fieldname": "accrual_type",
|
||||
"fieldtype": "Select",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Accrual Type",
|
||||
"options": "Regular\nRepayment\nDisbursement"
|
||||
"options": "Regular\nRepayment\nDisbursement\nCredit Adjustment\nDebit Adjustment\nRefund"
|
||||
},
|
||||
{
|
||||
"fieldname": "penalty_amount",
|
||||
@ -185,10 +193,11 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-19 18:26:38.871889",
|
||||
"modified": "2022-06-30 11:51:31.911794",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan Interest Accrual",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@ -225,5 +234,6 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
@ -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) {
|
||||
|
||||
// }
|
||||
});
|
176
erpnext/loan_management/doctype/loan_refund/loan_refund.json
Normal file
176
erpnext/loan_management/doctype/loan_refund/loan_refund.json
Normal file
@ -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
|
||||
}
|
97
erpnext/loan_management/doctype/loan_refund/loan_refund.py
Normal file
97
erpnext/loan_management/doctype/loan_refund/loan_refund.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
class LoanRefund(AccountsController):
|
||||
"""
|
||||
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):
|
||||
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 {0}").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", "Payment Ledger 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)
|
@ -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
|
@ -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}.<br>Repayment against Loan: {1}").format(
|
||||
remarks = "Shortfall repayment of {0}.<br>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),
|
||||
}
|
||||
@ -623,16 +627,22 @@ 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.debit_adjustment_amount)
|
||||
- flt(loan.credit_adjustment_amount)
|
||||
- flt(loan.total_principal_paid)
|
||||
- flt(loan.total_interest_payable)
|
||||
- flt(loan.written_off_amount)
|
||||
+ flt(loan.refund_amount)
|
||||
)
|
||||
else:
|
||||
pending_principal_amount = (
|
||||
flt(loan.disbursed_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)
|
||||
+ flt(loan.refund_amount)
|
||||
)
|
||||
|
||||
return pending_principal_amount
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user