Skip to content

fix(billing): stop showing "View all" when there are no more invoices#5387

Merged
waleedlatif1 merged 3 commits into
stagingfrom
fix-invoices-view-all
Jul 3, 2026
Merged

fix(billing): stop showing "View all" when there are no more invoices#5387
waleedlatif1 merged 3 commits into
stagingfrom
fix-invoices-view-all

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Emir flagged that the invoices list in Billing settings shows a "View all" button even when there are no more invoices to see
  • Root cause: the route fetched exactly MAX_INVOICES (12) raw Stripe invoices, filtered out drafts client-side, but derived hasMore from Stripe's raw has_more — which counts drafts we then hide, so it could report more invoices than actually existed
  • Fix: overfetch by one and page (bounded) until we've collected enough finalized invoices to know for certain whether more exist, then derive hasMore from that finalized count
  • Also trimmed the shown list from 12 → 10 invoices for a tighter, less scrolly list

Type of Change

  • Bug fix

Testing

  • Added route.test.ts covering: drafts at the fetch boundary no longer trigger hasMore, a genuine 11th finalized invoice still does, and multi-page draft runs are paged through correctly
  • bun run check:api-validation passes
  • Typecheck and biome clean

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Show fewer invoices (10) and derive hasMore from the finalized-invoice
count instead of Stripe's raw has_more, which counted drafts we filter
out client-side and could report more invoices than actually exist.
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 3, 2026 6:41pm

Request Review

@cursor

cursor Bot commented Jul 3, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Billing read-path behavior change with bounded Stripe pagination; no auth or payment mutation, covered by new tests.

Overview
Fixes billing settings showing View all when no additional finalized invoices exist, by changing how GET /api/billing/invoices computes hasMore and how many rows it returns.

The route now pages Stripe (up to 5 requests, page size 11) via collectFinalizedInvoices, keeps only non-draft invoices, and sets hasMore when more than 10 finalized invoices were found or Stripe still has more after the scan—so drafts no longer inflate the flag. The API response is capped at 10 invoices (down from 12).

Adds route.test.ts for draft-at-boundary, genuine 11th finalized invoice, multi-page draft paging, and the pagination safety cap.

Reviewed by Cursor Bugbot for commit 2bee0c3. Configure here.

Extract collectFinalizedInvoices with a TSDoc comment explaining the
Stripe has_more/draft-filtering rationale, instead of an inline
comment block.
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a billing UI bug where "View all" appeared even when there were no more invoices to show. The root cause was that hasMore was derived from Stripe's raw has_more flag, which counts draft invoices that are subsequently filtered out client-side.

  • route.ts: Replaces a single Stripe invoices.list call with a bounded paginator (collectFinalizedInvoices) that overfetches by one (STRIPE_PAGE_SIZE = 11) and pages up to MAX_STRIPE_PAGES = 5 times, keeping only finalized invoices. hasMore is now derived from finalized.length > MAX_INVOICES || stripeHasMore, correctly handling both the overfetch probe and the safety-cap fallback. MAX_INVOICES was also trimmed from 12 → 10.
  • route.test.ts: Adds four focused tests covering: drafts at the fetch boundary, a genuine 11th finalized invoice, multi-page draft traversal (with cursor assertion), and MAX_STRIPE_PAGES cap with stripeHasMore: true — each previously identified gap from earlier review comments is now exercised.

Confidence Score: 5/5

Safe to merge — the paginator logic is correct, the hasMore derivation handles all loop-exit paths, and the four new tests cover every edge case including the safety-cap fallback

The fix is well-scoped: a single route and its test file. The overfetch probe correctly detects a genuine 11th finalized invoice, drafts at the boundary no longer bleed into hasMore, and the stripeHasMore OR-gate ensures the MAX_STRIPE_PAGES cap never silently hides a real View all. Both gaps flagged in previous reviews (cursor propagation and cap-exit behaviour) are now verified by tests. No auth, schema, or data-integrity changes are involved.

No files require special attention

Important Files Changed

Filename Overview
apps/sim/app/api/billing/invoices/route.ts Introduces collectFinalizedInvoices paginator with correct loop termination, overfetch probe, and stripeHasMore OR-ed into hasMore to prevent silent suppression at the page cap
apps/sim/app/api/billing/invoices/route.test.ts New test file covering all four key scenarios including cursor propagation assertion and MAX_STRIPE_PAGES cap behaviour; both previously flagged gaps now have coverage

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([Start collectFinalizedInvoices]) --> B{page < MAX_STRIPE_PAGES\nAND stripeHasMore\nAND finalized.length <= 10?}
    B -- No --> G
    B -- Yes --> C[Fetch stripe.invoices.list\nlimit=11, starting_after=cursor]
    C --> D[Filter out drafts\nappend to finalized\nupdate stripeHasMore\nadvance cursor]
    D --> E{finalized.length > 10?}
    E -- Yes: overfetch probe triggered --> F[Exit loop early\nstripeHasMore may still be true]
    E -- No: keep scanning --> B
    F --> G[Return invoices + stripeHasMore]
    G --> H[Caller computes\nhasMore = finalized.length > 10\nOR stripeHasMore]
    H --> I{hasMore?}
    I -- true --> J[Show View all button]
    I -- false --> K[Hide View all button]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A([Start collectFinalizedInvoices]) --> B{page < MAX_STRIPE_PAGES\nAND stripeHasMore\nAND finalized.length <= 10?}
    B -- No --> G
    B -- Yes --> C[Fetch stripe.invoices.list\nlimit=11, starting_after=cursor]
    C --> D[Filter out drafts\nappend to finalized\nupdate stripeHasMore\nadvance cursor]
    D --> E{finalized.length > 10?}
    E -- Yes: overfetch probe triggered --> F[Exit loop early\nstripeHasMore may still be true]
    E -- No: keep scanning --> B
    F --> G[Return invoices + stripeHasMore]
    G --> H[Caller computes\nhasMore = finalized.length > 10\nOR stripeHasMore]
    H --> I{hasMore?}
    I -- true --> J[Show View all button]
    I -- false --> K[Hide View all button]
Loading

Reviews (2): Last reviewed commit: "fix(billing): don't hide View all when t..." | Re-trigger Greptile

…vely

Greptile P1: if MAX_STRIPE_PAGES is exhausted while the finalized count
sits exactly at MAX_INVOICES and Stripe still has_more, hasMore was
silently returned as false. collectFinalizedInvoices now also returns
whether Stripe's cursor was still open at exit, and the route ORs that
into hasMore so the safety cap can never suppress "View all".

Also asserts starting_after cursor propagation across pages and adds a
test for the safety-cap-hit case.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2bee0c3. Configure here.

@waleedlatif1 waleedlatif1 merged commit 331875b into staging Jul 3, 2026
18 checks passed
@waleedlatif1 waleedlatif1 deleted the fix-invoices-view-all branch July 3, 2026 18:49
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.

1 participant