Skip to content

fix: Add request timeout handling and auto-retry logic for e-Invoice/e-Waybill#4545

Open
vorasmit wants to merge 5 commits into
developfrom
claude/session-fbj6g9
Open

fix: Add request timeout handling and auto-retry logic for e-Invoice/e-Waybill#4545
vorasmit wants to merge 5 commits into
developfrom
claude/session-fbj6g9

Conversation

@vorasmit

@vorasmit vorasmit commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

This PR implements request timeout handling for GST API calls and adds auto-retry logic during outages to prevent queue flooding. When a GST service outage is detected, invoices are marked with "Auto-Retry" status instead of being enqueued, allowing a centralized 5-minute drain process to handle retries.

Key Changes

  • Request Timeout Configuration: Added configurable request timeouts to BaseAPI with sensible defaults:

    • Base API: 10s connect, unlimited read (for long-running operations like downloads)
    • e-Invoice/e-Waybill APIs: 10s connect, 60s read (fail fast on slow government servers)
    • Configurable via ic_request_timeout in frappe.conf
  • Timeout Exception Handling: Enhanced _make_request() to catch timeout and connection errors, converting them to appropriate exceptions (GatewayTimeoutError, GSPServerError) that trigger auto-retry logic

  • Auto-Retry During Outages: Modified on_submit() in sales_invoice.py to:

    • Check if retry mode is active (enable_retry_einv_ewb_generation + is_retry_einv_ewb_generation_pending)
    • Mark documents with "Auto-Retry" status instead of enqueueing during outages
    • Prevents queue flooding by centralizing retry handling in a 5-minute drain process
  • New Imports: Added set_einvoice_status and set_ewaybill_status utility functions to support status updates

Implementation Details

  • Timeout handling uses requests library's built-in exception types for robust error detection
  • Auto-retry logic only activates when both retry settings are enabled, ensuring backward compatibility
  • The notify parameter in status setters respects whether the request came from an API call

https://claude.ai/code/session_01J1Vg8LfdPy4Z4KAdEsS8kY

@codacy-production

codacy-production Bot commented Jul 6, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 15 complexity

Metric Results
Complexity 15

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

A slow or unresponsive government server had no request timeout, so each
bulk e-Invoice/e-Waybill job held its worker until the RQ job timeout (up
to 300s on the short queue). A bulk submit of many invoices could occupy
all short-queue workers and stall other jobs. A raw hang was also killed
as a generic exception, so it never tripped the existing Auto-Retry path.

- BaseAPI now sends a (connect, read) timeout on every request: a finite
  connect timeout for all APIs, and a 60s read timeout for e-Invoice /
  e-Waybill so they fail fast instead of holding a worker. Overridable
  per-site via the `ic_request_timeout` site config key.
- requests Timeout / ConnectionError are mapped to GatewayTimeoutError /
  GSPServerError so a client-side hang flows into the existing
  handle_server_errors -> Auto-Retry path instead of hanging.
- Sales Invoice on_submit skips enqueuing a per-invoice job when an
  auto-retry is already pending, marking the document Auto-Retry so the
  5-minute scheduler drains the backlog in a batch. This stops a flood of
  jobs from piling up on the short queue during an outage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Vg8LfdPy4Z4KAdEsS8kY
@vorasmit
vorasmit force-pushed the claude/session-fbj6g9 branch from a4bee67 to d1aba1b Compare July 6, 2026 09:34
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR updates request timeout handling in BaseAPI with a configurable timeout resolver and exception translation, adds REQUEST_TIMEOUT values for e-Invoice and e-Waybill APIs, changes Sales Invoice submit behavior to set Auto-Retry status when retry is pending, and adds e-Waybill recovery for error code 604 plus shared matching/linking logic.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly summarizes the main change: request timeout handling plus auto-retry logic for e-Invoice/e-Waybill.
Description check ✅ Passed The description matches the changeset and describes the timeout and auto-retry behavior accurately.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5996ad80-5a9e-4685-bc1f-6bb5f101c4e4

📥 Commits

Reviewing files that changed from the base of the PR and between a8439f8 and a4bee67.

📒 Files selected for processing (4)
  • india_compliance/gst_india/api_classes/base.py
  • india_compliance/gst_india/api_classes/nic/e_invoice.py
  • india_compliance/gst_india/api_classes/nic/e_waybill.py
  • india_compliance/gst_india/overrides/sales_invoice.py

Comment thread india_compliance/gst_india/api_classes/base.py
Comment thread india_compliance/gst_india/overrides/sales_invoice.py Outdated
is_retry_einv_ewb_generation_pending can persist after GST Settings is
switched to sandbox mode, where retry_e_invoice_e_waybill_generation()
early-returns. Without this gate, invoices submitted in that state were
marked Auto-Retry but never drained. Mirror the condition already used
in handle_server_errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Vg8LfdPy4Z4KAdEsS8kY
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge; all new code paths are guarded and consistent with existing retry infrastructure.

The timeout and auto-retry logic is well-scoped. Exception conversion in base.py preserves the original message via str(e), the on_submit guard mirrors handle_server_errors conditions, and the 604 recovery adds frappe.log_error so background failures are visible. The only nit is using a set for recovery_dates (non-deterministic iteration order) which is a style concern, not a correctness problem.

india_compliance/gst_india/utils/e_waybill.py — the 604 recovery date ordering is the one place worth a second look.

Important Files Changed

Filename Overview
india_compliance/gst_india/api_classes/base.py Adds configurable request timeout to BaseAPI with sensible defaults (10s connect, no read cap), catches Timeout/ConnectionError and converts them to GatewayTimeoutError/GSPServerError; implementation is sound.
india_compliance/gst_india/api_classes/nic/e_invoice.py Adds REQUEST_TIMEOUT = (10, 60) override to fail fast on slow government servers; trivial and correct.
india_compliance/gst_india/api_classes/nic/e_waybill.py Adds REQUEST_TIMEOUT = (10, 60) override to fail fast on slow government servers; trivial and correct.
india_compliance/gst_india/overrides/sales_invoice.py on_submit now checks retry_pending flag and sets "Auto-Retry" status directly instead of enqueuing during outages; logic mirrors handle_server_errors and is correctly gated on sandbox_mode.
india_compliance/gst_india/utils/e_waybill.py Refactors find_matching_e_waybill into shared link_matching_e_waybill; adds 604 auto-recovery inside the try block with frappe.log_error for background visibility; recovery_dates uses a set with non-deterministic iteration order.

Reviews (4): Last reviewed commit: "fix: broaden e-Waybill 604 recovery date..." | Re-trigger Greptile

Comment on lines +184 to +185
if retry_pending:
set_einvoice_status(doc, "Auto-Retry", notify=bool(frappe.request))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Missing user notification for Auto-Retry on submit

When retry_pending is True, set_einvoice_status / set_ewaybill_status are called with notify=bool(frappe.request). The notify argument triggers a WebSocket field update (the status chip changes in the form), but it does not show a popup to the user. In the previous flow, the background job would fail with GatewayTimeoutError/GSPServerError, and handle_server_errors would always call frappe.msgprint(...) with "Government services are currently slow/down. Your e-Invoice generation will be automatically retried every 5 minutes." — the only message that explains the "Auto-Retry" status to the operator. With this change, invoices submitted during an active outage silently flip to "Auto-Retry" without any explanatory popup, leaving users unaware of what happened or that the retry will be automatic.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread india_compliance/gst_india/api_classes/base.py Outdated
claude added 2 commits July 6, 2026 10:03
Bare `raise GatewayTimeoutError`/`GSPServerError` recorded an empty error
string in the integration request log for the new client-side timeout and
connection failure modes. Forward the original requests exception message
so these failures stay diagnosable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Vg8LfdPy4Z4KAdEsS8kY
When NIC returns 604 ("e-Waybill already generated for this document"),
generation previously threw a generic failure and left the existing
e-Waybill unlinked -- recoverable only via the manual "fetch by date"
flow. This is more likely now that client-side timeouts route to
auto-retry: a request can time out after NIC has already generated the
e-Waybill.

On 604, fetch active e-Waybills for today and link the one matching this
document, reusing the manual fetch logic (extracted into
link_matching_e_waybill). If none is found, fail with a clear message
pointing to the manual fetch-by-date action. Also removes the previously
unreachable 604 block that sat after the generic failure throw.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Vg8LfdPy4Z4KAdEsS8kY

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: faedecb9-eedc-4dba-845d-667ed0d1054e

📥 Commits

Reviewing files that changed from the base of the PR and between d1aba1b and 436c81a.

📒 Files selected for processing (3)
  • india_compliance/gst_india/api_classes/base.py
  • india_compliance/gst_india/overrides/sales_invoice.py
  • india_compliance/gst_india/utils/e_waybill.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • india_compliance/gst_india/api_classes/base.py
  • india_compliance/gst_india/overrides/sales_invoice.py

Comment thread india_compliance/gst_india/utils/e_waybill.py
Comment thread india_compliance/gst_india/utils/e_waybill.py
Search the document date(s) and today when recovering an already-generated
e-Waybill on 604, so a delayed auto-retry (running on a later day than the
original generation) can still find and link it. Using the posting date
alone would miss backdated invoices, whose e-Waybill is created on the
submit day.

When no active match is found, record a deferred Error Log (survives the
rollback in the except handler) so the orphaned e-Waybill can be reconciled
even on the background auto-retry path, which otherwise swallows the error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Vg8LfdPy4Z4KAdEsS8kY
@Abdeali099 Abdeali099 changed the title Add request timeout handling and auto-retry logic for e-Invoice/e-Waybill fix: Add request timeout handling and auto-retry logic for e-Invoice/e-Waybill Jul 7, 2026
@vorasmit vorasmit assigned ljain112 and unassigned Abdeali099 Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants