Skip to content

OUT-3700: remove find-or-map invoice logic from webhook handlers#243

Merged
SandipBajracharya merged 2 commits intomasterfrom
OUT-3700
May 7, 2026
Merged

OUT-3700: remove find-or-map invoice logic from webhook handlers#243
SandipBajracharya merged 2 commits intomasterfrom
OUT-3700

Conversation

@SandipBajracharya
Copy link
Copy Markdown
Collaborator

@SandipBajracharya SandipBajracharya commented May 7, 2026

Summary

Surgical revert of 1ed7b57's find-or-map fallback in the live webhook handlers, restoring the original behavior where missing local mapping throws APIError so the webhook-level catch records a FAILED row and the resync cron retries once a CREATED resync establishes the mapping.

Reverted (the find-or-map plumbing):

  • webhookInvoiceCreated: dropped early-exit short-circuit
  • webhookInvoicePaid / webhookInvoiceVoided: dropped lazy mapping fallback; now throws APIError(NOT_FOUND) on missing mapping
  • handleInvoiceDeleted: dropped find-or-map call in the "QBO present + no local mapping" branch; now throws APIError(NOT_FOUND)
  • Removed unused qbInvoice param from findOrMapInvoiceFromQBO

Kept intentionally:

  • checkIfInvoiceExistsInQBO body unchanged — used by the syncMissedInvoices cron path where mapping-on-find is desirable
  • findOrMapInvoiceFromQBO retained as a private helper (only caller is checkIfInvoiceExistsInQBO)
  • handleInvoiceDeleted's "QBO absent" branch (soft-delete prior sync logs + record pre-soft-deleted DELETED log)
  • OUT-3644 onConflictDoNothing race guard and OUT-3655 PENDING-status checks on CREATED log lookups — independent improvements, untouched

Recovery flow: throw → webhook.service.ts catch transitions PENDING claim → FAILED via updateOrCreateQBSyncLog → resync cron retries → succeeds once CREATED resync populates qb_invoice_sync.

Linear: https://linear.app/assemblycom/issue/OUT-3700

Test plan

  • Webhook delivery for paid/voided/deleted with no local mapping records a FAILED row (not silent return)
  • Resync cron retries those FAILED rows; once a CREATED resync succeeds, subsequent paid/voided/deleted retries find the mapping and resolve to SUCCESS

🤖 Generated with Claude Code

The find-or-map fallback (1ed7b57) lazily inserted qb_invoice_sync rows
from inside live webhook handlers when QBO already had the invoice. Drop
that path from webhookInvoiceCreated/Paid/Voided and from the QBO-present
branch of handleInvoiceDeleted; restore the original behavior where
missing local mapping throws an APIError so the webhook-level catch
records a FAILED row and the resync cron retries once a CREATED resync
establishes the mapping.

Kept intentionally:
- checkIfInvoiceExistsInQBO body unchanged; serves the syncMissedInvoices
  cron path where mapping-on-find is desirable.
- findOrMapInvoiceFromQBO retained as a private helper (only caller is
  checkIfInvoiceExistsInQBO now); dropped its unused qbInvoice param.
- handleInvoiceDeleted's QBO-absent branch (soft-delete prior sync logs +
  record pre-soft-deleted DELETED log) stays.
- OUT-3644 onConflictDoNothing race guard and OUT-3655 PENDING-status
  checks on CREATED log lookups are independent and unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 7, 2026

@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
quickbooks-sync Building Building May 7, 2026 6:30am
quickbooks-sync (dev) Ready Ready Preview, Comment May 7, 2026 6:30am

Request Review

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR surgically reverts the find-or-map fallback introduced in 1ed7b57, restoring the simpler behavior where webhook handlers throw APIError(NOT_FOUND) when no local sync-table mapping exists, allowing the webhook-level catch to record a FAILED log and the resync cron to retry once a CREATED resync populates qb_invoice_sync.

  • webhookInvoicePaid / webhookInvoiceVoided: let invoiceSyncconst invoiceSync; throws immediately on missing mapping instead of falling back to findOrMapInvoiceFromQBO.
  • handleInvoiceDeleted: throws NOT_FOUND when QBO has the invoice but the local mapping is absent; the "QBO absent" soft-delete branch is preserved.
  • findOrMapInvoiceFromQBO: removes the unused qbInvoice pre-fetch parameter; remains as the sole helper for the checkIfInvoiceExistsInQBO cron path.

Confidence Score: 5/5

Safe to merge — the revert is surgical and the webhook catch blocks in webhook.service.ts correctly transition PENDING claims to FAILED for all four affected event types.

All changed call sites correctly use const now that the variables are no longer reassigned. The findOrMapInvoiceFromQBO signature removal of the pre-fetched qbInvoice param leaves checkIfInvoiceExistsInQBO — its only remaining caller — untouched and working as before. The "QBO absent" soft-delete branch in handleInvoiceDeleted is fully preserved.

No files require special attention.

Important Files Changed

Filename Overview
src/app/api/quickbooks/invoice/invoice.service.ts Reverts find-or-map fallbacks in all four webhook handler paths; letconst conversions are correct since those variables are no longer reassigned; findOrMapInvoiceFromQBO signature cleanup is clean and still satisfies its only caller (checkIfInvoiceExistsInQBO).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Copilot Webhook Event\nPAID / VOIDED / DELETED] --> B{Local mapping\nin qb_invoice_sync?}
    B -- Yes --> C[Process event\nwrite SUCCESS log]
    B -- No --> D[throw APIError NOT_FOUND]
    D --> E[webhook.service.ts catch\npushFailedInvoiceToSyncLog\nstatus = FAILED]

    F[CREATE webhook] --> G{Mapping present?}
    G -- Yes --> H[Skip / idempotent]
    G -- No --> I[Create invoice in QBO\nwrite mapping row\nwrite SUCCESS CREATED log]
    I --> J{FAILED PAID/VOIDED/DELETED\nlogs in queue?}
    J -- Yes --> K[Resync cron retries\nNow finds mapping - SUCCESS]
    J -- No --> L[Done]

    subgraph Cron Path
        M[syncMissedInvoices cron] --> N[checkIfInvoiceExistsInQBO]
        N --> O[findOrMapInvoiceFromQBO\nmaps manually-created QBO invoices]
    end

    style D fill:#f66,color:#fff
    style E fill:#f99,color:#fff
    style C fill:#6a6,color:#fff
    style K fill:#6a6,color:#fff
Loading

Reviews (1): Last reviewed commit: "revert(OUT-3700): remove find-or-map inv..." | Re-trigger Greptile

@SandipBajracharya SandipBajracharya requested review from arpandhakal and priosshrsth and removed request for priosshrsth May 7, 2026 06:16
Copy link
Copy Markdown
Collaborator

@priosshrsth priosshrsth left a comment

Choose a reason for hiding this comment

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

lgtm. But I think we can work on idenfiying pre existing invoice with the invoice number and show error accordingly in another PR.

Note the DocNumber-collision failure mode that motivates the revert and
points future readers at the surviving syncMissedInvoices cron caller.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@SandipBajracharya SandipBajracharya changed the title revert(OUT-3700): remove find-or-map invoice logic from webhook handlers OUT-3700: remove find-or-map invoice logic from webhook handlers May 7, 2026
@SandipBajracharya SandipBajracharya merged commit 6427657 into master May 7, 2026
4 checks passed
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.

2 participants