Add Loan Balance Adjustment doctype
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 0000000..e69de29
--- /dev/null
+++ b/erpnext/loan_management/doctype/loan_balance_adjustment/__init__.py
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 0000000..8aec63a
--- /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 0000000..5fb25de
--- /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 0000000..8f98ce6
--- /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 0000000..7658d7b
--- /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