Skip to content

feat: add scheduler based reverse GL creation for cancelled loan records#1236

Open
Nihantra-Patel wants to merge 6 commits into
frappe:developfrom
Nihantra-Patel:cancel-gl-in-bg
Open

feat: add scheduler based reverse GL creation for cancelled loan records#1236
Nihantra-Patel wants to merge 6 commits into
frappe:developfrom
Nihantra-Patel:cancel-gl-in-bg

Conversation

@Nihantra-Patel
Copy link
Copy Markdown
Member

@Nihantra-Patel Nihantra-Patel commented May 18, 2026

Implemented optional scheduler-based reverse GL Entry creation for cancelled:

  • Loan Demand
  • Loan Interest Accrual

What’s Added
Added new Company level settings:

  • Enable Demand Cancel GL Queue
  • Enable Interest Cancel GL Queue

These settings control whether reverse GL Entries should be created immediately or through background scheduler jobs.


Added new field in:

  • Loan Demand
  • Loan Interest Accrual

Field:

  • Cancel GL Pending

This field is used to track records whose reverse GL Entry creation is pending in background jobs.


Flow Changes

When cancellation happens:

If queue setting is disabled

  • Existing flow continues.
  • Reverse GL Entries are created immediately.

If queue setting is enabled

  • Immediate reverse GL creation is skipped.
  • Cancel GL Pending is set to checked.
  • Scheduler job creates reverse GL Entries later.

Scheduler Job

  • Runs every 30 minutes
  • Processes cancelled Loan Demand and Loan Interest Accrual records
  • Creates reverse GL Entries in background
  • Resets Cancel GL Pending after successful processing

Patches Added
Added patches to create custom fields


Summary by CodeRabbit

  • New Features

    • Company-level settings to optionally defer GL reversal for cancelled loan demands and loan interest accruals.
    • Deferred cancellations mark documents as "cancel GL pending" and are processed by a background job every 30 minutes.
    • Separate toggles to control loan demand and interest accrual behavior independently.
  • Chores

    • Migration/patch added to register the new company settings and backfill existing data.

Review Change Stack

@Nihantra-Patel Nihantra-Patel marked this pull request as ready for review May 18, 2026 06:35
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe67796a-c423-4c9d-9e68-6180505ba3f5

📥 Commits

Reviewing files that changed from the base of the PR and between 3f8dd5c and 2826f33.

📒 Files selected for processing (1)
  • lending/loan_management/utils.py

Walkthrough

This pull request converts GL entry reversal from synchronous to deferred processing during loan document cancellation. When enabled at the Company level and outside test mode, LoanDemand and LoanInterestAccrual cancellations now set a pending flag instead of immediately reversing GL entries. A new scheduled job running every 30 minutes processes these flagged documents asynchronously, executing the GL reversals with transactional safety and error logging. Two new Company custom fields control queueing behavior per company, and a data migration patch initializes these settings for existing installations.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing scheduler-based (background) reverse GL entry creation for cancelled loan records, which is the core feature of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@lending/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py`:
- Around line 148-157: The queued cancellation can post reversal GL entries with
the worker's current date because make_gl_entries computes posting dates with
getdate() at runtime; capture the intended posting/accounting date at cancel
time and pass it to the background job so the reversal uses that fixed date.
Modify the cancel flow that enqueues self.make_gl_entries to compute and pass a
posting_date (or accounting_date) argument (e.g., posting_date=self.posting_date
or frappe.utils.getdate()) and update make_gl_entries to accept and use that
posting_date instead of calling getdate() internally (also ensure the non-queued
branch calls make_gl_entries(cancel=1, posting_date=...) for parity).

In `@lending/loan_management/doctype/loan_repayment/loan_repayment.py`:
- Around line 727-736: The queued background path for cancellation causes
make_reverse_gl_entries to call posting_date=getdate() (worker run date) rather
than the actual cancellation date; update the enqueue call for
self.make_gl_entries (and the synchronous call path) to pass the cancellation
posting date explicitly (e.g., posting_date=self.posting_date or the
cancellation_date variable) and ensure make_gl_entries and its downstream
make_reverse_gl_entries accept and use the posting_date argument instead of
defaulting to getdate(); adjust the signature/ callers of make_gl_entries and
make_reverse_gl_entries accordingly so queued jobs preserve the original
cancellation posting date.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b80fb692-f2fe-4539-9b45-9342c6df7c91

📥 Commits

Reviewing files that changed from the base of the PR and between e36bda1 and defae11.

📒 Files selected for processing (3)
  • lending/loan_management/doctype/loan_demand/loan_demand.py
  • lending/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
  • lending/loan_management/doctype/loan_repayment/loan_repayment.py

Comment on lines +105 to +114
# Reverse GL Entries update in the background job to avoid timeout issues during demand cancellation
if not frappe.flags.in_test:
frappe.enqueue(
self.make_gl_entries,
cancel=1,
queue="long",
enqueue_after_commit=True,
)
else:
self.make_gl_entries(cancel=1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Freeze reversal posting date before enqueueing.

Line 107-Line 114 defers GL reversal, but reversal entries use runtime getdate() in cancel flow (for example Line 205 / Line 221 in this file). If the "long" queue is delayed, reversals can land on a later accounting date than the cancellation transaction date.

Suggested direction
+		reversal_posting_date = getdate()
 		if not frappe.flags.in_test:
 			frappe.enqueue(
 				self.make_gl_entries,
 				cancel=1,
+				reversal_posting_date=reversal_posting_date,
 				queue="long",
 				enqueue_after_commit=True,
 			)
 		else:
-			self.make_gl_entries(cancel=1)
+			self.make_gl_entries(cancel=1, reversal_posting_date=reversal_posting_date)

Then thread reversal_posting_date through make_gl_entries/add_gl_entries and use it instead of runtime getdate() for cancel entries.

Comment on lines +148 to +157
# Reverse GL Entries update in the background job to avoid timeout issues during accrual cancellation
if not frappe.flags.in_test:
frappe.enqueue(
self.make_gl_entries,
cancel=1,
queue="long",
enqueue_after_commit=True,
)
else:
self.make_gl_entries(cancel=1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Asynchronous cancel can post reversal on the wrong accounting date.

Line 149-Line 157 now queues reversal, but cancel-path GL posting dates are computed with runtime getdate() (Line 264 / Line 282 / Line 301 / Line 319). A delayed worker can post reversal entries on a later date than when the document was canceled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lending/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py`
around lines 148 - 157, The queued cancellation can post reversal GL entries
with the worker's current date because make_gl_entries computes posting dates
with getdate() at runtime; capture the intended posting/accounting date at
cancel time and pass it to the background job so the reversal uses that fixed
date. Modify the cancel flow that enqueues self.make_gl_entries to compute and
pass a posting_date (or accounting_date) argument (e.g.,
posting_date=self.posting_date or frappe.utils.getdate()) and update
make_gl_entries to accept and use that posting_date instead of calling getdate()
internally (also ensure the non-queued branch calls make_gl_entries(cancel=1,
posting_date=...) for parity).

Comment thread lending/loan_management/doctype/loan_repayment/loan_repayment.py Outdated
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 18, 2026

Codecov Report

❌ Patch coverage is 36.66667% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.25%. Comparing base (e50d464) to head (2826f33).

Files with missing lines Patch % Lines
lending/loan_management/utils.py 10.52% 17 Missing ⚠️
...loan_management/doctype/loan_demand/loan_demand.py 80.00% 1 Missing ⚠️
...ype/loan_interest_accrual/loan_interest_accrual.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1236      +/-   ##
===========================================
- Coverage    76.37%   76.25%   -0.13%     
===========================================
  Files          142      142              
  Lines        10207    10234      +27     
===========================================
+ Hits          7796     7804       +8     
- Misses        2411     2430      +19     
Files with missing lines Coverage Δ
lending/hooks.py 100.00% <ø> (ø)
lending/install.py 0.00% <ø> (ø)
...loan_management/doctype/loan_demand/loan_demand.py 86.50% <80.00%> (-0.19%) ⬇️
...ype/loan_interest_accrual/loan_interest_accrual.py 87.52% <83.33%> (-0.12%) ⬇️
lending/loan_management/utils.py 20.97% <10.52%> (-1.61%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Nihantra-Patel Nihantra-Patel marked this pull request as draft May 21, 2026 09:44
@Nihantra-Patel Nihantra-Patel changed the title fix: Reverse GL Entries update in the background job to avoid timeout issues feat: add scheduler based reverse GL creation for cancelled loan records May 25, 2026
@Nihantra-Patel Nihantra-Patel marked this pull request as ready for review May 25, 2026 06:47
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lending/loan_management/utils.py (1)

372-381: ⚡ Quick win

Add scheduler-path tests for pending-flag lifecycle and retry behavior.

Given the new async flow, please add tests for: success clears cancel_gl_pending, failure keeps it set, and repeated runs don’t double-post reversals.

Also applies to: 384-408

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lending/loan_management/utils.py` around lines 372 - 381, Add scheduler-path
unit tests around process_cancelled_gl_entries and the underlying
process_cancelled_documents flow that validate the cancel_gl_pending lifecycle
and idempotence: write three tests per doctype ("Loan Demand" and "Loan Interest
Accrual") that (1) simulate a successful GL reversal (mock the GL-posting call
used by process_cancelled_documents) and assert cancel_gl_pending is cleared
after running process_cancelled_gl_entries, (2) simulate a failing GL reversal
(mock to raise/return error) and assert cancel_gl_pending remains set, and (3)
run the scheduler twice with a successful mock and assert the GL reversal
posting function is only invoked once (no double-post), using the real
process_cancelled_documents/process_cancelled_gl_entries calls and checking the
record's cancel_gl_pending flag before/after. Ensure mocks target the GL-posting
helper used by process_cancelled_documents so tests cover both "Loan Demand" and
"Loan Interest Accrual" paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lending/loan_management/utils.py`:
- Around line 386-399: The loop currently reads rows and processes reversals
without claiming them, allowing concurrent workers to process the same document;
change the flow to atomically claim a pending reversal before doing work: for
each row (row.name) perform an atomic UPDATE that sets cancel_gl_pending from 1
to a "claimed" value (e.g. 2) only when cancel_gl_pending = 1 and check the
affected row count, and only if affected_rows == 1 load the doc
(frappe.get_doc), call document.make_gl_entries(cancel=1), then set
cancel_gl_pending to 0 (document.db_set) and commit; this ensures
document.make_gl_entries is executed by a single worker and prevents duplicate
reversal postings.

---

Nitpick comments:
In `@lending/loan_management/utils.py`:
- Around line 372-381: Add scheduler-path unit tests around
process_cancelled_gl_entries and the underlying process_cancelled_documents flow
that validate the cancel_gl_pending lifecycle and idempotence: write three tests
per doctype ("Loan Demand" and "Loan Interest Accrual") that (1) simulate a
successful GL reversal (mock the GL-posting call used by
process_cancelled_documents) and assert cancel_gl_pending is cleared after
running process_cancelled_gl_entries, (2) simulate a failing GL reversal (mock
to raise/return error) and assert cancel_gl_pending remains set, and (3) run the
scheduler twice with a successful mock and assert the GL reversal posting
function is only invoked once (no double-post), using the real
process_cancelled_documents/process_cancelled_gl_entries calls and checking the
record's cancel_gl_pending flag before/after. Ensure mocks target the GL-posting
helper used by process_cancelled_documents so tests cover both "Loan Demand" and
"Loan Interest Accrual" paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c953ccdc-cd9a-4665-9c25-57332823d9cb

📥 Commits

Reviewing files that changed from the base of the PR and between defae11 and 3f8dd5c.

📒 Files selected for processing (9)
  • lending/hooks.py
  • lending/install.py
  • lending/loan_management/doctype/loan_demand/loan_demand.json
  • lending/loan_management/doctype/loan_demand/loan_demand.py
  • lending/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
  • lending/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
  • lending/loan_management/utils.py
  • lending/patches.txt
  • lending/patches/v16_0/add_cancel_gl_queue_settings_in_company.py
✅ Files skipped from review due to trivial changes (1)
  • lending/patches.txt

Comment thread lending/loan_management/utils.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants