Files
NetForge/.planning/phases/05-visibility-and-client-portal/05-03-SUMMARY.md
kevin-asprec 82c8dc39d1 docs(05-03): complete Portal Ticket Submission and Payment Scaffold plan
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>
2026-03-05 17:36:59 +08:00

5.6 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established duration completed
05-visibility-and-client-portal 03 portal
portal
tickets
comments
conversation
payment-scaffold
subscriber
phase provides
03-operational-modules Ticket model, TicketService with createTicket and status transitions
phase plan provides
05-visibility-and-client-portal 02 Portal auth (withPortalAuth, subscriberId in session), PortalService
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
05-04 (network monitoring may reference tickets)
05-05 (e2e tests)
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)
created modified
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
prisma/schema.prisma
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
Shadow User pattern: portal subscribers get lazy-created User records for FK constraints
Ticket conversation threads via TicketComment with chronological ordering
5min 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