Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lending/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,12 @@
],
"hourly_long": [
"lending.loan_management.doctype.loan_repayment.loan_repayment.process_pending_credit_notes",
]
],
"cron": {
"*/30 * * * *": [
"lending.loan_management.utils.process_cancelled_gl_entries",
]
},
}

bank_reconciliation_doctypes = [
Expand Down
16 changes: 15 additions & 1 deletion lending/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,26 @@
"fieldtype": "Check",
"insert_after": "loan_column_break",
},
{
"fieldname": "enable_demand_cancel_gl_queue",
"label": "Enable Demand Cancel GL Queue",
"fieldtype": "Check",
"default": "1",
"insert_after": "enable_loan_accounting",
},
{
"fieldname": "enable_interest_cancel_gl_queue",
"label": "Enable Interest Cancel GL Queue",
"fieldtype": "Check",
"default": "1",
"insert_after": "enable_demand_cancel_gl_queue",
},
{
"fieldname": "collection_offset_logic_based_on",
"label": "Collection Offset Logic Based On",
"fieldtype": "Select",
"options": "NPA Flag\nDays Past Due",
"insert_after": "enable_loan_accounting",
"insert_after": "enable_interest_cancel_gl_queue",
},
{
"fieldname": "days_past_due_threshold",
Expand Down
11 changes: 10 additions & 1 deletion lending/loan_management/doctype/loan_demand/loan_demand.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"accounting_dimensions_section",
"cost_center",
"is_imported",
"cancel_gl_pending",
"column_break_hmrs",
"amended_from",
"process_loan_demand",
Expand Down Expand Up @@ -294,6 +295,14 @@
"label": "Is Imported",
"read_only": 1
},
{
"default": "0",
"fieldname": "cancel_gl_pending",
"fieldtype": "Check",
"hidden": 1,
"label": "Cancel GL Pending",
"read_only": 1
},
{
"default": "0",
"fieldname": "is_partial_pre_paid_interest",
Expand All @@ -305,7 +314,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2026-02-16 15:13:09.765918",
"modified": "2026-05-25 10:35:43.539786",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Demand",
Expand Down
11 changes: 10 additions & 1 deletion lending/loan_management/doctype/loan_demand/loan_demand.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class LoanDemand(LoanController):
amended_from: DF.Link | None
applicant: DF.DynamicLink | None
applicant_type: DF.Link | None
cancel_gl_pending: DF.Check
company: DF.Link | None
cost_center: DF.Link | None
demand_amount: DF.Currency
Expand Down Expand Up @@ -101,10 +102,18 @@ def update_repayment_schedule(self, cancel=0):

def on_cancel(self):
self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
self.make_gl_entries(cancel=1)

if self.queue_cancel_gl() and not frappe.flags.in_test:
self.db_set("cancel_gl_pending", 1)
else:
self.make_gl_entries(cancel=1)

self.update_repayment_schedule(cancel=1)
self.make_credit_note()

def queue_cancel_gl(self):
return frappe.db.get_value("Company", self.company, "enable_demand_cancel_gl_queue")

def make_credit_note(self):
if not self.demand_type == "Charges":
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"process_loan_interest_accrual",
"amended_from",
"is_imported",
"cancel_gl_pending",
"column_break_svgc",
"normal_interest_journal_entry",
"additional_interest_suspense_entry"
Expand Down Expand Up @@ -291,13 +292,21 @@
"fieldtype": "Check",
"label": "Is Imported",
"read_only": 1
},
{
"default": "0",
"fieldname": "cancel_gl_pending",
"fieldtype": "Check",
"hidden": 1,
"label": "Cancel GL Pending",
"read_only": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2026-01-28 15:20:16.650685",
"modified": "2026-05-25 10:36:03.518964",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Interest Accrual",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class LoanInterestAccrual(LoanController):
applicant: DF.DynamicLink | None
applicant_type: DF.Literal["Employee", "Member", "Customer"]
base_amount: DF.Currency
cancel_gl_pending: DF.Check
company: DF.Link | None
cost_center: DF.Link | None
interest_amount: DF.Currency
Expand Down Expand Up @@ -145,7 +146,12 @@ def on_submit(self):
self.db_set("additional_interest_suspense_entry", additional_interest_jv)

def on_cancel(self):
self.make_gl_entries(cancel=1)
self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]

if self.queue_cancel_gl() and not frappe.flags.in_test:
self.db_set("cancel_gl_pending", 1)
else:
self.make_gl_entries(cancel=1)

if self.normal_interest_journal_entry and loan_accounting_enabled(self.company):
doc = frappe.get_doc("Journal Entry", self.normal_interest_journal_entry)
Expand All @@ -157,7 +163,8 @@ def on_cancel(self):
doc.flags.ignore_links = True
doc.cancel()

self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
def queue_cancel_gl(self):
return frappe.db.get_value("Company", self.company, "enable_interest_cancel_gl_queue")

def make_gl_entries(self, cancel=0, adv_adj=0):
if not loan_accounting_enabled(self.company):
Expand Down
41 changes: 41 additions & 0 deletions lending/loan_management/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,44 @@ def create_charge_master(charge_type):
"is_stock_item": 0,
}
).insert()


def process_cancelled_gl_entries():
process_cancelled_documents(
doctype="Loan Demand",
title="Loan Demand Cancel GL Queue Error",
)

process_cancelled_documents(
doctype="Loan Interest Accrual",
title="Loan Interest Accrual Cancel GL Queue Error",
)


def process_cancelled_documents(doctype, title):
doc = frappe.qb.DocType(doctype)
rows = (
frappe.qb.from_(doc)
.select(doc.name)
.where((doc.docstatus == 2) & (doc.cancel_gl_pending == 1))
.run(as_dict=True)
)

for row in rows:
try:
docname = row.name
document = frappe.get_doc(doctype, docname, for_update=True)
if not document.cancel_gl_pending:
frappe.db.rollback()
continue
document.make_gl_entries(cancel=1)
document.db_set("cancel_gl_pending", 0)
frappe.db.commit()
Comment thread
Nihantra-Patel marked this conversation as resolved.
except Exception:
frappe.db.rollback()
frappe.log_error(
title=title,
message=frappe.get_traceback(),
reference_doctype=doctype,
reference_name=row.name,
)
1 change: 1 addition & 0 deletions lending/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ lending.patches.v1_0.update_value_date_in_loan_refund
lending.patches.v1_0.update_value_date_in_pending_doctypes
lending.patches.v16_0.add_enable_loan_accounting_field
lending.patches.v16_0.update_is_invoice_generated_field
lending.patches.v16_0.add_cancel_gl_queue_settings_in_company
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields


def execute():
company_fields = []
created_demand_queue_field = False
created_interest_queue_field = False

if not frappe.db.exists(
"Custom Field", {"dt": "Company", "fieldname": "enable_demand_cancel_gl_queue"}
):
company_fields.append(
{
"fieldname": "enable_demand_cancel_gl_queue",
"label": "Enable Demand Cancel GL Queue",
"fieldtype": "Check",
"default": "1",
"insert_after": "enable_loan_accounting",
}
)
created_demand_queue_field = True

if not frappe.db.exists(
"Custom Field", {"dt": "Company", "fieldname": "enable_interest_cancel_gl_queue"}
):
company_fields.append(
{
"fieldname": "enable_interest_cancel_gl_queue",
"label": "Enable Interest Cancel GL Queue",
"fieldtype": "Check",
"default": "1",
"insert_after": "enable_demand_cancel_gl_queue",
}
)
created_interest_queue_field = True

if company_fields:
create_custom_fields({"Company": company_fields}, update=True)

if created_demand_queue_field:
frappe.db.sql(
"""
update `tabCompany`
set enable_demand_cancel_gl_queue = 1
where ifnull(enable_demand_cancel_gl_queue, 0) = 0
"""
)

if created_interest_queue_field:
frappe.db.sql(
"""
update `tabCompany`
set enable_interest_cancel_gl_queue = 1
where ifnull(enable_interest_cancel_gl_queue, 0) = 0
"""
)
Loading