diff --git a/.planning/STATE.md b/.planning/STATE.md index 68b7868..80d013d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -10,11 +10,11 @@ See: .planning/PROJECT.md (updated 2026-03-04) ## Current Position Phase: 5 of 5 (Visibility and Client Portal) -Plan: 2 of 5 in phase 5 (25/28 total complete) -Status: In progress. 05-01 and 05-02 complete. -Last activity: 2026-03-05 — Completed 05-02-PLAN.md (Subscriber Portal Auth and API — 5 tests, portal credentials provider, subscriber-scoped endpoints) +Plan: 3 of 5 in phase 5 (26/28 total complete) +Status: In progress. 05-01, 05-02, and 05-03 complete. +Last activity: 2026-03-05 — Completed 05-03-PLAN.md (Portal Ticket Submission and Payment Scaffold — 6 tests, conversation threads, payment coming-soon endpoint) -Progress: [█████████████████████████░░░] 89% (25/28 plans across all phases) +Progress: [██████████████████████████░░] 93% (26/28 plans across all phases) ## Performance Metrics @@ -31,10 +31,10 @@ Progress: [███████████████████████ | 02-subscriber-and-billing-core | 5/5 complete | 57 min | 11.4 min | | 03-operational-modules | 5/5 complete | ~65 min | ~13 min | | 04-inventory-expenses-reports | 5/5 complete | 71 min | 14.2 min | -| 05-visibility-and-client-portal | 2/5 complete | 7 min | 3.5 min | +| 05-visibility-and-client-portal | 3/5 complete | 12 min | 4 min | **Recent Trend:** -- Last 10 plans: 03-04 (6 min), 03-05 (9 min), 04-01 (16 min), 04-03 (17 min), 04-05 (10 min), 04-02 (8 min), 04-04 (20 min), 05-01 (3 min), 05-02 (4 min) +- Last 10 plans: 03-05 (9 min), 04-01 (16 min), 04-03 (17 min), 04-05 (10 min), 04-02 (8 min), 04-04 (20 min), 05-01 (3 min), 05-02 (4 min), 05-03 (5 min) - Trend: Phase 5 plans executing fast — service + API + tests pattern, minimal schema changes *Updated after each plan completion* @@ -152,6 +152,12 @@ Recent decisions affecting current work: - [05-02]: subscriberId persisted in JWT token and session — distinguishes portal users from staff users without DB lookup - [05-02]: withPortalAuth HOF validates subscriberId in session; 401 if no session, 403 if not portal user - [05-02]: Portal cleanup order: payments -> paymentAllocations -> invoiceLines -> invoices -> journalEntryLines -> null reversesEntryId -> journalEntries -> subscribers -> servicePlans -> tenantSettings -> accountingPeriods -> accounts -> users -> tenant +- [05-03]: ensurePortalUser creates shadow User with CLIENT role and portal-{accountNumber}@portal.local email — bridges Subscriber auth to User FK on Ticket.createdById +- [05-03]: Portal tickets use source=SUBSCRIBER (not PORTAL) — TicketSource enum has STAFF/SUBSCRIBER only +- [05-03]: TicketComment is append-only (no updatedAt, no edits/deletes) — conversation integrity preserved +- [05-03]: Closed tickets reject new comments — enforced at service layer in addTicketComment +- [05-03]: Payment scaffold computes outstanding balance in JS from SENT/PARTIAL/OVERDUE invoices — same derived-aggregation pattern +- [05-03]: Portal ticket cleanup order: ticketComments -> tickets -> ticketCategories -> subscribers -> servicePlans -> tenantSettings -> accountingPeriods -> accounts -> users -> tenant ### Pending Todos @@ -169,6 +175,6 @@ None. ## Session Continuity -Last session: 2026-03-05T09:25:00Z -Stopped at: Completed 05-02-PLAN.md (Subscriber Portal Auth and API — 5 tests, portal credentials, subscriber-scoped endpoints). +Last session: 2026-03-05T09:35:00Z +Stopped at: Completed 05-03-PLAN.md (Portal Ticket Submission and Payment Scaffold — 6 tests, conversation threads, payment coming-soon endpoint). Resume file: None diff --git a/.planning/phases/05-visibility-and-client-portal/05-03-SUMMARY.md b/.planning/phases/05-visibility-and-client-portal/05-03-SUMMARY.md new file mode 100644 index 0000000..44f7107 --- /dev/null +++ b/.planning/phases/05-visibility-and-client-portal/05-03-SUMMARY.md @@ -0,0 +1,118 @@ +--- +phase: 05-visibility-and-client-portal +plan: 03 +subsystem: portal +tags: [portal, tickets, comments, conversation, payment-scaffold, subscriber] + +# Dependency graph +requires: + - phase: 03-operational-modules + provides: Ticket model, TicketService with createTicket and status transitions + - phase: 05-visibility-and-client-portal + plan: 02 + provides: Portal auth (withPortalAuth, subscriberId in session), PortalService +provides: + - TicketComment model for conversation threads + - Portal ticket service (createPortalTicket, listPortalTickets, getPortalTicket, addTicketComment) + - ensurePortalUser shadow User pattern for subscriber->User FK bridge + - Portal ticket API routes (list, create, detail, comments) + - Payment scaffold endpoint with outstanding balance and instructions +affects: [05-04 (network monitoring may reference tickets), 05-05 (e2e tests)] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "ensurePortalUser shadow User for subscriber->User FK bridge" + - "TicketComment append-only conversation thread on tickets" + - "Payment scaffold returning balance + instructions (gateway deferred to v2)" + +key-files: + created: + - prisma/schema.prisma (TicketComment model, paymentInstructions field) + - src/lib/services/portal-ticket-service.ts + - src/app/api/portal/tickets/route.ts + - src/app/api/portal/tickets/[id]/route.ts + - src/app/api/portal/tickets/[id]/comments/route.ts + - src/app/api/portal/payments/coming-soon/route.ts + - src/lib/__tests__/portal-ticket-service.test.ts + modified: + - prisma/schema.prisma + +key-decisions: + - "ensurePortalUser creates shadow User with CLIENT role and portal-{accountNumber}@portal.local email -- bridges Subscriber auth to User FK on Ticket.createdById" + - "Portal tickets use source=SUBSCRIBER (not PORTAL) -- TicketSource enum has STAFF/SUBSCRIBER, SUBSCRIBER is semantically correct" + - "TicketComment is append-only (no edits/deletes) -- conversation integrity preserved" + - "Closed tickets reject new comments -- enforced at service layer" + - "Payment scaffold computes outstanding balance in JS from SENT/PARTIAL/OVERDUE invoices -- same pattern as other derived aggregations" + +patterns-established: + - "Shadow User pattern: portal subscribers get lazy-created User records for FK constraints" + - "Ticket conversation threads via TicketComment with chronological ordering" + +# Metrics +duration: 5min +completed: 2026-03-05 +--- + +# Phase 05 Plan 03: Portal Ticket Submission and Payment Scaffold Summary + +**Subscriber portal ticket creation with conversation threads, shadow User FK bridge, and payment coming-soon endpoint with outstanding balance -- 6 tests passing** + +## Performance + +- **Duration:** 5 min +- **Started:** 2026-03-05T09:30:50Z +- **Completed:** 2026-03-05T09:35:36Z +- **Tasks:** 2 +- **Files modified:** 8 + +## Accomplishments +- Subscribers can create support tickets from the portal (source=SUBSCRIBER) +- Conversation threads on tickets via TicketComment model (append-only, chronological) +- Shadow User pattern bridges Subscriber auth to User FK on Ticket.createdById +- Portal tickets visible in staff ticket queue (cross-system verified) +- Closed tickets reject new comments +- Payment scaffold returns outstanding balance and configurable payment instructions + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: TicketComment model and portal ticket service** - `2d5b9ca` (feat) +2. **Task 2: Portal ticket API routes, payment scaffold, and tests** - `3f44f5c` (feat) + +## Files Created/Modified +- `prisma/schema.prisma` - Added TicketComment model, paymentInstructions on TenantSettings, ticketComments relation on User +- `src/lib/services/portal-ticket-service.ts` - Portal ticket service with ensurePortalUser, createPortalTicket, listPortalTickets, getPortalTicket, addTicketComment +- `src/app/api/portal/tickets/route.ts` - GET (list) and POST (create) for portal tickets +- `src/app/api/portal/tickets/[id]/route.ts` - GET ticket detail with conversation thread +- `src/app/api/portal/tickets/[id]/comments/route.ts` - GET (list) and POST (add) ticket comments +- `src/app/api/portal/payments/coming-soon/route.ts` - GET outstanding balance and payment instructions +- `src/lib/__tests__/portal-ticket-service.test.ts` - 6 integration tests + +## Decisions Made +- **Shadow User pattern:** Portal subscribers authenticate as Subscriber (not User), but Ticket.createdById requires User.id. ensurePortalUser() lazily creates a User with CLIENT role and email `portal-{accountNumber}@portal.local` to bridge this gap. +- **SUBSCRIBER source (not PORTAL):** The TicketSource enum has STAFF and SUBSCRIBER. Used SUBSCRIBER which is semantically correct for portal-submitted tickets. +- **Append-only comments:** TicketComment has no updatedAt -- comments cannot be edited or deleted, preserving conversation integrity. +- **Closed ticket comment rejection:** Enforced at service layer -- addTicketComment checks ticket.status !== CLOSED before creating comment. +- **Outstanding balance in JS:** Payment scaffold computes balance from SENT/PARTIAL/OVERDUE invoices in JavaScript, consistent with the derived-aggregation pattern used throughout the project. + +## Deviations from Plan + +None -- plan executed exactly as written. + +## Issues Encountered +None. + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- PORT-03 (ticket submission) and PORT-05 (online payment scaffold) requirements satisfied +- Portal now has: auth, account view, invoices, payments, tickets, and payment scaffold +- Ready for 05-04 (network monitoring) and 05-05 (e2e tests) + +--- +*Phase: 05-visibility-and-client-portal* +*Completed: 2026-03-05*