fix(billing): stop showing "View all" when there are no more invoices#5387
Conversation
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview The route now pages Stripe (up to 5 requests, page size 11) via Adds 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 SummaryThis PR fixes a billing UI bug where "View all" appeared even when there were no more invoices to show. The root cause was that
Confidence Score: 5/5Safe 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
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]
%%{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]
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.
|
@cursor review |
There was a problem hiding this comment.
✅ 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.
Summary
hasMorefrom Stripe's rawhas_more— which counts drafts we then hide, so it could report more invoices than actually existedhasMorefrom that finalized countType of Change
Testing
route.test.tscovering: drafts at the fetch boundary no longer triggerhasMore, a genuine 11th finalized invoice still does, and multi-page draft runs are paged through correctlybun run check:api-validationpassesChecklist