Tasks completed: 2/2
- Task 1: TicketComment model and portal ticket service
- Task 2: Portal ticket API routes, payment scaffold, and tests
SUMMARY: .planning/phases/05-visibility-and-client-portal/05-03-SUMMARY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GET/POST /api/portal/tickets for listing and creating tickets
- GET /api/portal/tickets/[id] for ticket detail with conversation
- GET/POST /api/portal/tickets/[id]/comments for conversation threads
- GET /api/portal/payments/coming-soon returns outstanding balance and payment instructions
- 6 integration tests: SUBSCRIBER source, staff visibility, isolation, comments, closed rejection, thread ordering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add TicketComment model for conversation threads (append-only)
- Add paymentInstructions field to TenantSettings
- Add ticketComments relation to User model
- Create portal-ticket-service with ensurePortalUser shadow User pattern
- Implements createPortalTicket, listPortalTickets, getPortalTicket, addTicketComment
- Portal ticket creation delegates to existing createTicket with source=SUBSCRIBER
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- Portal authentication via subscriber account number
- PortalService, API routes, and integration tests (5 tests)
SUMMARY: .planning/phases/05-visibility-and-client-portal/05-02-SUMMARY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create PortalService with getPortalAccount, getPortalInvoices, getPortalPayments
- All queries scoped to single subscriberId (no cross-subscriber access)
- Create withPortalAuth middleware (validates subscriberId in session)
- Add GET /api/portal/account (subscriber profile + plan details)
- Add GET /api/portal/invoices (paginated, includes line items)
- Add GET /api/portal/payments (paginated payment history)
- Add 5 integration tests: account details, pagination, isolation, line items
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- DashboardService with 6 metric aggregation methods
- GET /api/dashboard endpoint and 6 integration tests
SUMMARY: .planning/phases/05-visibility-and-client-portal/05-01-SUMMARY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GET /api/dashboard with read Report permission (ADMIN, OFFICE_STAFF)
- Optional startDate/endDate query params for cash flow date range
- 6 integration tests verifying all metric methods against seeded data
- Tests cover revenue, overdue, subscriber status, cash flow, collectors
- Comprehensive cleanup order for dashboard test data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add passwordHash field to Subscriber model (nullable for existing subscribers)
- Add portal-credentials NextAuth provider (accountNumber + password + tenantId)
- Persist subscriberId in JWT token and session for portal user identification
- Extend next-auth types with optional subscriberId on Session, User, and JWT
- Exclude /portal/login and /api/portal/auth from middleware auth requirement
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- getRevenueMetrics: today + month revenue from COMPLETED payments
- getOverdueMetrics: overdue subscriber count + outstanding total
- getSubscriberMetrics: active/suspended/cancelled status breakdown
- getCashFlowSummary: money in vs out from POSTED JE lines
- getCollectorSummary: today's collections + unverified remittances
- getDashboardSummary: aggregator calling all five methods in parallel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 05: Visibility and Client Portal
- Implementation decisions documented
- Phase boundary established
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- ExpenseReportService and AuditTrailService with API routes
- 9 passing integration tests
SUMMARY: .planning/phases/04-inventory-expenses-and-financial-reports/04-04-SUMMARY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Expense by category: correct totals, excludes DRAFT/VOIDED
- Expense by vendor: correct totals, No Vendor bucket
- Expense summary: combined view with grand total
- Date range filtering works
- JE audit trail: createdBy, source, referenceType for expense JE
- Audit trail for entity: original + void reversal entries
- Cross-source audit: Payment JE referenceType correctly returned
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ExpenseReportService: getExpensesByCategory, getExpensesByVendor, getExpenseSummary
- AuditTrailService: getJournalEntryAudit, getAuditTrailForEntity (ACCT-08)
- GET /api/reports/expenses -- expense summary by category
- GET /api/reports/expenses/by-vendor -- expense summary by vendor
- GET /api/accounting/journal-entries/[id]/audit -- JE audit trail
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- AssetService with assign, return, dispose, history
- 11 integration tests all passing
SUMMARY: .planning/phases/04-inventory-expenses-and-financial-reports/04-02-SUMMARY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Assign to subscriber/technician creates correct ISSUED movements
- Return from subscriber/technician creates RETURNED with condition
- Rejects reassignment without return, rejects batch items
- Admin disposal creates write-off JE (DR 5030, CR 1200)
- Non-admin disposal rejected, field disposal rejected
- History returns chronological timeline with resolved names
- Full lifecycle: RECEIVED->ISSUED->RETURNED->ISSUED->RETURNED->DISPOSED
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- AssetService: assignToSubscriber, assignToTechnician, returnAsset, disposeAsset, getAssetHistory
- getCurrentLocation helper derives location from latest movement
- Disposal validates ADMIN role and creates write-off JE (DR 5030, CR 1200)
- API routes: POST assign, POST return, POST dispose, GET history
- Location history resolves subscriber/technician/warehouse names
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Trial Balance: debits = credits (isBalanced), per-account balances, asOfDate filter
- Income Statement: revenue/expense sections, net income = 600, header exclusion, date range
- Balance Sheet: A = L + E (isBalanced), cash = 600, net income in equity, date filter
- Drill-down: running balance, entry metadata (entryNumber, referenceType)
- Edge cases: empty tenant balanced, leaf-only accounts across all reports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GET /api/reports/trial-balance with optional asOfDate filter
- GET /api/reports/income-statement with required startDate/endDate
- GET /api/reports/balance-sheet with required asOfDate
- GET /api/reports/accounts/[id]/entries drill-down with optional date range
- All routes use withPermission("read", "Report") for ADMIN access
- FinancialReportService already existed with all 4 methods
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- VendorService: CRUD with unique name validation per tenant
- ExpenseService: create, approve, post, void with full JE integration
- Auto-generated EXP-NNNN expense numbers per tenant
- JE posting: DR category expense account, CR cash (1010) or bank (1020)
- Optional approval workflow: requireApproval flag controls DRAFT-only vs immediate post
- Void reverses JE via JournalEntryService.reverseEntry
- Custom category creation with COA account validation
- System categories protected from deletion
- API routes: expenses CRUD, approve, categories CRUD, vendors CRUD
- Migration: add_expense_vendor_models applied via db push + resolve
- 15 integration tests covering all expense lifecycle scenarios
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ExpenseStatus and ExpensePaymentMethod enums
- Add Vendor model (@@unique([tenantId, name]))
- Add ExpenseCategory model with accountCode linking to COA
- Add Expense model with approval workflow and JE reference
- Add User relations: createdExpenses, approvedExpenses
- Add COA accounts 5080 Fuel/Transportation, 5085 Rent Expense (31 total)
- Seed 9 default expense categories in createTenant transaction
- Add Vendor subject to CASL types, Expense/Vendor permissions for OFFICE_STAFF
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- InventoryService: registerItem, recordMovement, getStockLevels, getItemMovements, listItems
- RECEIVED movements auto-post JE (DR 1200 Equipment Inventory, CR 2010 AP)
- Stock levels derived from movement aggregation (no mutable quantity column)
- All 5 movement types validated with type-specific rules
- API routes: POST/GET items, GET item detail, POST/GET movements, GET stock-levels
- CASL: OFFICE_STAFF gets manage Inventory permission
- Migration applied: add_inventory_models (4 enums, 2 tables)
- 13 tests: registration, all movement types, stock derivation, JE posting, history
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ItemTrackingType (SERIALIZED/BATCH), ItemCondition, MovementType, LocationType enums
- Add InventoryItem model with dual tracking (serialized by serial number, batch by quantity)
- Add StockMovement model as immutable movement ledger (no updatedAt)
- Add recordedMovements relation on User model
- Indexes on tenantId, itemType, trackingType, inventoryItemId, movementType
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 04: Inventory, Expenses, and Financial Reports
- Implementation decisions documented
- Phase boundary established
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added checkTicketAutoResolve/checkTicketRevertToOpen to ticket-service.ts
- Created job-order-service.ts: createJobOrder, updateJobOrderStatus, updateJobOrder,
getJobOrder, listJobOrders, getMyJobOrders with VALID_JO_TRANSITIONS guard map
- PENDING->IN_PROGRESS->COMPLETED(outcomeNotes required)/CANCELLED lifecycle enforced
- OPEN ticket auto-transitions to ASSIGNED on first job order creation
- Auto-resolves ticket when all non-cancelled jobs COMPLETED
- Reverts ticket to OPEN when all jobs CANCELLED
- Created POST /api/tickets/[id]/job-orders, GET/PUT /api/job-orders/[id],
POST /api/job-orders/[id]/status, GET /api/job-orders (with TECHNICIAN filter)
- 17 integration tests: lifecycle, auto-resolve, revert-to-open, partial completion,
technician self-service, cross-tenant isolation — all green
- Added JobOrderStatus enum (PENDING, IN_PROGRESS, COMPLETED, CANCELLED)
- Added JobOrder model with ticket FK, assignedTo/createdBy user FKs
- Added orderNumber (JO-NNNN), scheduledDate, timestamps, outcomeNotes
- Added jobOrders reverse relation on Ticket model
- Added assignedJobOrders/createdJobOrders reverse relations on User model
- Applied migration: 20260304235859_add_job_orders
- Added jobOrder to TENANT_SCOPED_MODELS with full 12-operation extension block
- Added 1030 Cash in Transit to ISP_CHART_OF_ACCOUNTS (between 1020 and 1100)
- Added CollectionStatus (COMPLETED/VOIDED) and RemittanceStatus (PENDING/VERIFIED) enums
- Added Collection model with FIFO allocations, zone-scoped collector, JE link, void fields
- Added CollectionAllocation model linking collections to invoices
- Added Remittance model with two-party verification, variance field, JE link
- Added reverse relations on User (collections, remittances) and Subscriber (collections)
- Applied migration: add-collections-remittances
- Extended TENANT_SCOPED_MODELS with collection, collectionAllocation, remittance
- Added full 12-operation extension blocks for all three new models
Tasks completed: 2/2
- Task 1: Zone schema, migration, and tenant scoping
- Task 2: Zone service, API routes, and integration tests
SUMMARY: .planning/phases/03-operational-modules/03-01-SUMMARY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Zone model (id, tenantId, name, description, isActive)
- Add ZoneAssignment model (collector-to-zone join table)
- Replace Subscriber.zone String? with Subscriber.zoneId FK to Zone
- Add Zone + ZoneAssignment to TENANT_SCOPED_MODELS with full operation blocks
- Add "Zone" to AppSubjects in types.ts
- Grant OFFICE_STAFF manage Zone, COLLECTOR read Zone in permissions.ts
- Migration 20260305000000_add_zones applied to DB
Phase 03: Operational Modules
- Standard stack identified (no new dependencies)
- Architecture patterns documented (10 patterns from codebase)
- Pitfalls catalogued (8 pitfalls from direct code inspection)