Files
NetForge/.planning/phases/05-visibility-and-client-portal/05-01-PLAN.md
kevin-asprec ea58620c7d docs(05): create phase plan
Phase 05: Visibility and Client Portal
- 5 plans in 3 waves
- 2 parallel (wave 1), 1 sequential (wave 2), 2 parallel (wave 3)
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:14:54 +08:00

7.7 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
05-visibility-and-client-portal 01 execute 1
src/lib/services/dashboard-service.ts
src/app/api/dashboard/route.ts
src/lib/__tests__/dashboard-service.test.ts
true
truths artifacts key_links
Dashboard returns revenue collected today and this month
Dashboard returns overdue subscriber count and total outstanding amount
Dashboard returns active/suspended/cancelled subscriber counts
Dashboard returns cash flow summary (money in vs money out)
Dashboard returns collector summary (today's collections, unverified remittances)
path provides exports
src/lib/services/dashboard-service.ts DashboardService with all metric aggregation methods
getRevenueMetrics
getSubscriberMetrics
getOverdueMetrics
getCashFlowSummary
getCollectorSummary
getDashboardSummary
path provides exports
src/app/api/dashboard/route.ts GET endpoint returning all dashboard metrics
GET
path provides
src/lib/__tests__/dashboard-service.test.ts Integration tests verifying metric accuracy
from to via pattern
src/lib/services/dashboard-service.ts prisma aggregate queries on Payment, Invoice, Subscriber, Collection, Remittance, JournalEntryLine prisma.(payment|invoice|subscriber|collection|remittance|journalEntryLine).
from to via pattern
src/app/api/dashboard/route.ts src/lib/services/dashboard-service.ts import and call getDashboardSummary getDashboardSummary
Build the DashboardService that aggregates financial and operational metrics for the ISP owner dashboard, plus a GET /api/dashboard endpoint and integration tests.

Purpose: DASH-01, DASH-02, DASH-03, DASH-04 — the ISP owner's single-page business health overview. Output: DashboardService, API route, passing tests.

<execution_context> @C:\Users\KevinAsprec.claude/get-shit-done/workflows/execute-plan.md @C:\Users\KevinAsprec.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/05-visibility-and-client-portal/05-CONTEXT.md

@src/lib/services/financial-report-service.ts @src/lib/services/outstanding-report-service.ts @src/lib/services/collection-report-service.ts @src/lib/services/expense-report-service.ts @src/lib/services/payment-service.ts @src/lib/services/collector-service.ts @src/lib/middleware/authorize.ts @prisma/schema.prisma

Task 1: DashboardService with metric aggregation src/lib/services/dashboard-service.ts Create DashboardService with these methods, all accepting a TenantPrismaClient (same pattern as all other services):
  1. getRevenueMetrics(db, tenantId) — Revenue collected today and this month (DASH-01):

    • Query Payment table with status COMPLETED, grouped by date range
    • revenueToday: sum of payments where createdAt is today (midnight to now)
    • revenueThisMonth: sum of payments where createdAt is current month
    • Return { revenueToday: Decimal, revenueThisMonth: Decimal }
  2. getOverdueMetrics(db, tenantId) — Overdue subscriber count and outstanding total (DASH-02):

    • Query Invoice table for OVERDUE status invoices
    • Count distinct subscriberIds with overdue invoices
    • Sum (totalAmount - amountPaid) for all overdue invoices
    • Return { overdueCount: number, totalOutstanding: Decimal }
  3. getSubscriberMetrics(db, tenantId) — Status breakdown (DASH-03):

    • Query Subscriber table grouped by status
    • Return { active: number, suspended: number, cancelled: number, total: number }
  4. getCashFlowSummary(db, tenantId, startDate, endDate) — Money in vs money out (DASH-04):

    • Money in: sum of POSTED JE lines where account is revenue-type (4xxx codes) — use debit/credit with normal balance logic, same as FinancialReportService
    • Money out: sum of POSTED JE lines where account is expense-type (5xxx codes)
    • Return { moneyIn: Decimal, moneyOut: Decimal, netCashFlow: Decimal }
    • Default date range: current month
  5. getCollectorSummary(db, tenantId) — Today's collections and unverified remittances (from CONTEXT.md):

    • collectionsToday: sum of non-voided Collection amounts where createdAt is today
    • unverifiedRemittances: count of Remittance records with status SUBMITTED (not VERIFIED)
    • Return { collectionsToday: Decimal, unverifiedRemittances: number }
  6. getDashboardSummary(db, tenantId) — Aggregator that calls all five methods above and returns a single object.

Use Prisma aggregate/groupBy for efficiency. Follow the existing TenantPrismaClient pattern (type as any). Use Decimal from Prisma for all money fields. All date comparisons use UTC. TypeScript compiles: npx tsc --noEmit src/lib/services/dashboard-service.ts (or full project compile) DashboardService exports all 6 methods with correct return types

Task 2: Dashboard API route and integration tests src/app/api/dashboard/route.ts, src/lib/__tests__/dashboard-service.test.ts **API Route (src/app/api/dashboard/route.ts):** - GET handler wrapped with `withPermission("read", "Report")` — ADMIN and OFFICE_STAFF have this permission - Calls getDashboardSummary with tenant-scoped Prisma client - Returns JSON with all metrics - Accepts optional query params: `startDate`, `endDate` for cash flow date range (defaults to current month) - Follow existing route patterns (see any route.ts in src/app/api/)

Integration Tests (src/lib/tests/dashboard-service.test.ts): Create tests using vitest with live PostgreSQL (same pattern as all other test files):

Setup: Create tenant, seed COA, create subscriber, generate invoice, record payment, create collection, create remittance.

Tests (minimum 6):

  1. getRevenueMetrics returns today's revenue — after recording a payment, revenueToday > 0
  2. getOverdueMetrics counts overdue invoices — create invoice with past due date, verify overdueCount = 1
  3. getSubscriberMetrics returns status breakdown — create active + suspended subscribers, verify counts
  4. getCashFlowSummary computes net cash flow — after payment + expense, verify moneyIn and moneyOut
  5. getCollectorSummary returns today's collections — after collection, verify collectionsToday > 0
  6. getDashboardSummary aggregates all metrics — verify the composite object has all fields

Cleanup order: follow the most comprehensive cleanup pattern from 04-04 (expense report tests) since dashboard touches payments, invoices, collections, remittances, expenses, and JEs.

Run tests with: npx vitest run src/lib/__tests__/dashboard-service.test.ts npx vitest run src/lib/__tests__/dashboard-service.test.ts — all tests pass GET /api/dashboard returns all dashboard metrics; 6+ tests pass verifying metric accuracy

- `npx tsc --noEmit` passes (no type errors) - `npx vitest run src/lib/__tests__/dashboard-service.test.ts` — all tests pass - DashboardService correctly derives all metrics from existing data (no new stored balances)

<success_criteria>

  • DashboardService aggregates revenue, overdue, subscriber status, cash flow, and collector metrics
  • GET /api/dashboard returns the composite dashboard object
  • All integration tests pass, verifying metric accuracy against seeded data
  • DASH-01, DASH-02, DASH-03, DASH-04 requirements satisfied </success_criteria>
After completion, create `.planning/phases/05-visibility-and-client-portal/05-01-SUMMARY.md`