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