Phase 03: Operational Modules - 5 plans in 3 waves - Wave 1: 03-01 (zones), 03-03 (tickets) — parallel - Wave 2: 03-02 (collector collections), 03-04 (job orders) — parallel - Wave 3: 03-05 (technician compensation) — sequential - Ready for execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.7 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-operational-modules | 04 | execute | 2 |
|
|
true |
|
Purpose: Job orders are how tickets become actionable work for technicians. A ticket can spawn multiple job orders (different visits or skill types). The critical synchronization rule: when all job orders on a ticket complete, the ticket auto-resolves, and staff then manually closes after confirming with the subscriber.
Output: JobOrder Prisma model, job order CRUD with status lifecycle, ticket-to-job conversion, auto-resolution sync, technician self-service status updates, integration 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/03-operational-modules/03-CONTEXT.md @.planning/phases/03-operational-modules/03-03-SUMMARY.md @prisma/schema.prisma @src/lib/services/ticket-service.ts Task 1: JobOrder Prisma model + migration prisma/schema.prisma, src/lib/prisma-tenant.ts **New enums:** - `JobOrderStatus { PENDING, IN_PROGRESS, COMPLETED, CANCELLED }` - `JobType { INSTALLATION, REPAIR, MAINTENANCE, RELOCATION, DISCONNECTION, OTHER }`JobOrder model:
- id (uuid PK), tenantId
- orderNumber (String) — auto-generated sequential per tenant, e.g., "JO-0001"
- ticketId (FK to Ticket) — parent ticket
- assignedToId (FK to User) — the technician
- jobType (JobType)
- description (String) — what needs to be done
- status (JobOrderStatus default PENDING)
- scheduledDate (DateTime?) — optional scheduled date
- startedAt (DateTime?) — when technician started work
- completedAt (DateTime?) — when work was completed
- outcomeNotes (String?) — technician fills in on completion
- cancelledAt (DateTime?)
- cancelReason (String?)
- createdById (FK to User) — staff who created the job order
- createdAt, updatedAt
- @@unique([tenantId, orderNumber])
- @@index([tenantId]), @@index([tenantId, assignedToId]), @@index([tenantId, status]), @@index([ticketId])
Update relations:
- Ticket: add
jobOrders JobOrder[] - User: add
assignedJobOrders JobOrder[] @relation("JobOrderAssignedTo"),createdJobOrders JobOrder[] @relation("JobOrderCreatedBy")
Add to TENANT_SCOPED_MODELS: "jobOrder"
Run npx prisma migrate dev --name add-job-orders
- npx prisma migrate dev completes without errors
- npx prisma generate succeeds
- Schema has JobOrder model with correct enums and relations
JobOrder model exists with status lifecycle, ticket relation (1:many), technician assignment, and job type classification. Migration applied.
-
updateJobOrder(db, jobOrderId, { description?, scheduledDate?, jobType? })— update editable fields -
updateStatus(db, jobOrderId, { status, outcomeNotes?, cancelReason? }): Status transitions:- PENDING -> IN_PROGRESS: set startedAt
- PENDING -> CANCELLED: set cancelledAt, cancelReason
- IN_PROGRESS -> COMPLETED: set completedAt, outcomeNotes (required). Then call
checkTicketAutoResolve. - IN_PROGRESS -> CANCELLED: set cancelledAt, cancelReason
- All other transitions: throw error
-
checkTicketAutoResolve(db, ticketId):- Load all job orders for this ticket
- If ALL non-cancelled job orders have status COMPLETED, auto-resolve the ticket via TicketService.resolveTicket
- If there are only cancelled job orders (no completed ones), do NOT auto-resolve
-
reassignJobOrder(db, jobOrderId, newAssignedToId)— reassign to different technician (only if PENDING or IN_PROGRESS) -
getJobOrder(db, jobOrderId)— get detail with ticket, subscriber, technician info -
listJobOrders(db, filters)— list with filters: assignedToId, status, jobType, ticketId, dateFrom, dateTo. Pagination. Sort by createdAt DESC. -
getTechnicianJobOrders(db, technicianId, filters)— convenience wrapper for technician self-service view
API Routes:
POST /api/tickets/[id]/job-orders— create job order from ticket. ADMIN, OFFICE_STAFF. Body: { assignedToId, jobType, description, scheduledDate? }GET /api/job-orders— list job orders with filters. ADMIN, OFFICE_STAFF see all. TECHNICIAN sees assigned only.GET /api/job-orders/[id]— get job order detailPUT /api/job-orders/[id]— update job order fields. ADMIN, OFFICE_STAFF.POST /api/job-orders/[id]/status— update status. TECHNICIAN can update own (PENDING->IN_PROGRESS, IN_PROGRESS->COMPLETED). ADMIN, OFFICE_STAFF can do any valid transition. Body: { status, outcomeNotes?, cancelReason? }
Integration Tests (src/lib/__tests__/job-order-service.test.ts):
- Create job order from ticket (auto-assigns ticket to ASSIGNED status)
- One ticket can have multiple job orders
- Status transitions: PENDING -> IN_PROGRESS -> COMPLETED (happy path)
- Completing last job order auto-resolves parent ticket
- Completing one of two job orders does NOT resolve ticket
- All job orders completed -> ticket auto-resolved -> staff closes ticket
- Cancelled job orders are excluded from auto-resolve check
- Cannot complete job order without outcomeNotes
- Invalid transitions rejected (e.g., COMPLETED -> IN_PROGRESS)
- Reassign job order to different technician
- Technician filter returns only their assigned orders
- Job order number sequential per tenant (JO-0001, JO-0002...)
- Cross-tenant isolation
npx vitest run src/lib/__tests__/job-order-service.test.ts— all tests passnpx vitest run— full suite passes (no regressions) JobOrderService handles job order lifecycle with ticket auto-resolution sync. Technicians update their own work, staff manages assignments. All status transitions enforced. Integration tests prove the ticket-to-job-order-to-resolution flow.
<success_criteria>
- JobOrder model with status lifecycle, ticket 1:many relation, and technician assignment
- JobOrderService handles creation from ticket, status transitions, auto-resolution sync
- Technicians can update their own job orders (view assigned, update status)
- Ticket auto-resolves when all non-cancelled job orders complete
- API routes enforce RBAC (staff creates, technician updates own)
- Integration tests prove full ticket-to-job-to-resolution workflow </success_criteria>