# 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 `` 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