Skip to content

OUT-4030: warn IUs which mixed-payout fees are already recorded - #280

Merged
SandipBajracharya merged 3 commits into
feature/payout-reconciliationfrom
OUT-4030
Aug 7, 2026
Merged

OUT-4030: warn IUs which mixed-payout fees are already recorded#280
SandipBajracharya merged 3 commits into
feature/payout-reconciliationfrom
OUT-4030

Conversation

@SandipBajracharya

Copy link
Copy Markdown
Collaborator

Problem

A Stripe payout can straddle a bankDepositFeeFlag toggle, mixing invoices set to batch into a bank deposit with invoices that are not. These payouts are unsupported and fail terminally (FAILED PAYOUT/SETTLED, no auto-retry) with an IU notification.

The old notification told the IU to record the deposit manually and stated nothing was double-booked. But the non-batched invoices already have their absorbed Stripe fee posted as a QBO expense. Following that guidance — booking a normal batched deposit (gross lines minus a fee line) — double-books those fees.

Change

Keep mixed payouts terminal + manual (no auto delete/rebuild — the fee-funding account assetAccountRef is user-selectable to a real, reconciled bank, so deleting posted fee expenses isn't safe). Instead, make the notification actionable:

  • Ground-truth lookup (getInvoiceNumbersWithRecordedFee): invoices that have a SUCCESS PAYMENT/SUCCEEDED sync log — the fee Purchase exists in QBO only when that row is written, so it's ground truth, not inferred from the batched-intent flag. Standalone @/db/service fn to avoid a syncLog.service ↔ syncErrorNotifier import cycle.
  • Notifier derives the list from the payout's affected invoices and populates NotificationContext.invoiceNumbersWithFee. A lookup failure drops only that detail — the terminal payout notification still fires.
  • Copy (in-product + email) names the already-recorded invoices and warns not to record those fees again.
  • Shared MIXED_INTENT_INVOICE_DELIMITER so the remark writer and the notifier split can't drift.

Testing

  • New integration test exercises the query against real Postgres: SUCCESS included; FAILED / wrong-entity / wrong-event / soft-deleted / other-portal excluded; empty input → empty set.
  • Unit tests: copy renders the warning when fees are recorded and omits it otherwise; notifier populates the field, preserves remark order, and still dispatches when the lookup throws.
  • Full suite green (407 tests); tsc/eslint/prettier clean for all files in this PR.

Sub-issue of OUT-3604. Linear: https://linear.app/assemblycom/issue/OUT-4030

🤖 Generated with Claude Code

SandipBajracharya and others added 2 commits August 7, 2026 12:48
Query invoices that have a SUCCESS PAYMENT/SUCCEEDED sync log. The absorbed-fee
Purchase exists in QBO only when that row is written, so its presence is ground
truth rather than inferring from the batched-intent flag. Standalone @/db/service
fn to avoid a syncLog.service <-> syncErrorNotifier import cycle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A mixed-intent payout fails terminally; the notification told the IU to record
the deposit manually but did not flag that the non-batched invoices already have
their fee expensed, so following it would double-book those fees. Name the
already-recorded invoices in the copy (in-product + email) and warn against
re-recording. A recorded-fee lookup failure now drops only that detail, not the
whole notification. Share the remark delimiter so the writer and notifier split
can't drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 7, 2026

Copy link
Copy Markdown

OUT-4030

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
quickbooks-sync Building Building Aug 7, 2026 11:02am
quickbooks-sync (dev) Ready Ready Preview Aug 7, 2026 11:02am

Request Review

@SandipBajracharya SandipBajracharya changed the title feat(OUT-4030): warn IUs which mixed-payout fees are already recorded OUT-4030: warn IUs which mixed-payout fees are already recorded Aug 7, 2026
@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR improves mixed-payout reconciliation notifications by identifying invoices whose Stripe fees were already recorded and warning users not to record those fees twice.

  • Adds a portal-scoped lookup for successful payment-fee sync records.
  • Enriches in-product and email notification context and copy.
  • Shares the invoice-list delimiter between serialization and parsing.
  • Adds integration and unit coverage for lookup filtering, ordering, copy rendering, and lookup failures.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/app/api/quickbooks/syncLog/syncErrorNotifier.ts Enriches mixed-payout notifications with recorded-fee invoice numbers while preserving notification delivery when the lookup fails.
src/db/service/syncLog.service.ts Adds a portal-scoped query that selects active successful PAYMENT/SUCCEEDED logs for the requested invoice numbers.
src/app/api/notification/notification.helper.ts Updates mixed-payout in-product and email copy to explain payment state and prevent duplicate fee recording.
src/app/api/quickbooks/webhook/webhook.service.ts Uses the shared delimiter when storing affected mixed-payout invoice numbers.
test/integration/quickbooks/syncLog/getInvoiceNumbersWithRecordedFee.test.ts Covers successful matches and exclusion of failed, unrelated, deleted, and cross-portal records.

Sequence Diagram

sequenceDiagram
    participant W as Payout webhook
    participant L as Sync log
    participant N as SyncErrorNotifier
    participant DB as Fee-log lookup
    participant IU as Integration user
    W->>L: Record terminal mixed-payout failure
    L->>N: Notify with affected invoice numbers
    N->>DB: Find successful PAYMENT/SUCCEEDED logs
    DB-->>N: Invoices with recorded fees
    N->>IU: Send manual-reconciliation warning
    Note over IU: Do not record listed fees again
Loading

Reviews (2): Last reviewed commit: "feat(OUT-4030): warn IUs which mixed-pay..." | Re-trigger Greptile

Comment thread src/app/api/quickbooks/syncLog/syncErrorNotifier.ts Outdated
@SandipBajracharya

Copy link
Copy Markdown
Collaborator Author

@greptileai review PR again

Comment thread src/app/api/core/types/notification.ts
Comment thread src/app/api/quickbooks/syncLog/syncErrorNotifier.ts Outdated
…lper

Move the remark parse + recorded-fee lookup out of notify() into a private
resolveMixedPayoutInvoices helper returning { affectedInvoiceNumbers,
invoiceNumbersWithFee }. Behavior-preserving; keeps the fault-isolation so a
lookup failure still dispatches the notification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@priosshrsth priosshrsth left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm. 🏆

@SandipBajracharya
SandipBajracharya merged commit d73f5ab into feature/payout-reconciliation Aug 7, 2026
5 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