87 lines
3.7 KiB
Markdown
87 lines
3.7 KiB
Markdown
|
|
# 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
|