Assist_Design/docs/plans/2026-03-05-invoice-optimization-design.md

87 lines
3.7 KiB
Markdown
Raw Permalink Normal View History

# Invoice System Optimization Design
**Date:** 2026-03-05
**Status:** Approved
## Problem
Subscription invoice fetching is slow due to an N+1 query pattern. When viewing a subscription's invoices, the BFF:
1. Fetches ALL client invoices page by page (full table scan)
2. For each invoice, makes an individual `GetInvoice` WHMCS API call to get line items
3. Filters in-memory by `item.serviceId === subscriptionId`
For a client with 19 invoices: 1 list call + 19 individual detail calls = 20 WHMCS API calls with batching delays.
## Decision
**Remove subscription-specific invoice fetching entirely.** Most billing portals (Stripe, AWS, DigitalOcean) don't offer per-subscription invoice lists. The WHMCS `GetInvoices` API doesn't support filtering by service/subscription ID, making this fundamentally expensive.
Instead:
- Subscription detail page shows billing summary (already available on subscription object) + link to main invoices page
- Main invoices page uses efficient single `GetInvoices` call (already working well)
- Individual invoice detail already shows which subscription each line item belongs to
Additionally, clean up the billing service layer by removing the pass-through `BillingOrchestrator`.
## Changes
### Files to DELETE
1. `apps/bff/src/modules/billing/services/billing-orchestrator.service.ts` - zero-logic pass-through
### BFF Files to EDIT
2. **`subscriptions-orchestrator.service.ts`** - Remove:
- `getSubscriptionInvoices()`, `tryGetCachedInvoices()`, `fetchAllRelatedInvoices()`, `paginateInvoices()`, `cacheInvoiceResults()` (~150 lines)
- Related type imports (`InvoiceItem`, `InvoiceList`)
- `WhmcsInvoiceService` dependency (if no other method uses it)
3. **`subscriptions.controller.ts`** - Remove:
- `GET :id/invoices` endpoint
- `SubscriptionInvoiceQueryDto`, `InvoiceListDto` DTOs
- `invoiceListSchema`, `InvoiceList`, `Validation` imports
- `subscriptionInvoiceQuerySchema`
4. **`whmcs-invoice.service.ts`** - Remove:
- `getInvoicesWithItems()` method (~70 lines)
- `chunkArray`, `sleep` imports (if unused after removal)
5. **`whmcs-cache.service.ts`** - Remove:
- `subscriptionInvoices` + `subscriptionInvoicesAll` cache configs
- `getSubscriptionInvoices()`, `setSubscriptionInvoices()`, `getSubscriptionInvoicesAll()`, `setSubscriptionInvoicesAll()`
- `buildSubscriptionInvoicesKey()`, `buildSubscriptionInvoicesAllKey()`
- Subscription invoice patterns in invalidation methods
6. **`billing.controller.ts`** - Replace `BillingOrchestrator` with direct `WhmcsPaymentService` + `WhmcsSsoService` injection
7. **`billing.module.ts`** - Remove `BillingOrchestrator` from providers/exports
### Portal Files to EDIT
8. **`InvoiceList.tsx`** - Remove dual-mode logic:
- Remove `subscriptionId`, `showFilters` props
- Remove `useSubscriptionInvoices` import
- Remove `useInvoicesData()` helper, use `useInvoices()` directly
- Remove `isSubscriptionMode` conditionals
- Simplify `InvoicesFilterBar` (remove conditional spread)
9. **`SubscriptionDetail.tsx`** - Replace `<InvoicesList>` with billing link:
- Remove `InvoicesList` and `InvoiceListSkeleton` imports
- Replace billing history section with "View all invoices" link to `/account/billing`
10. **`app/account/subscriptions/[id]/loading.tsx`** - Remove `InvoiceListSkeleton`
11. **`useSubscriptions.ts`** - Remove `useSubscriptionInvoices()` hook
12. **`core/api/index.ts`** - Remove `subscriptions.invoices` query key
## Impact
- Eliminates 20+ WHMCS API calls per subscription detail view
- Removes ~300 lines of scanning/caching/batching code
- Simplifies InvoicesList to single-purpose component
- Removes unused service abstraction layer (BillingOrchestrator)
- No feature regression: invoice data still accessible via main billing page