Files
NetForge/.planning/phases/03-operational-modules/03-04-SUMMARY.md
kevin-asprec bdd0fda7d2 docs(03-04): complete Job Orders plan
Tasks completed: 2/2
- Task 1: JobOrder schema, migration, and tenant scoping
- Task 2: Job order service, API routes, and integration tests

SUMMARY: .planning/phases/03-operational-modules/03-04-SUMMARY.md
2026-03-05 08:05:50 +08:00

7.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
03-operational-modules 04 api
prisma
postgresql
job-orders
tickets
technician
lifecycle
tenant-isolation
phase provides
03-03 Ticket model with lifecycle (OPEN/ASSIGNED/RESOLVED/CLOSED), resolveTicket, transitionTicketStatus
JobOrder model with 1:many ticket relation and PENDING/IN_PROGRESS/COMPLETED/CANCELLED lifecycle
createJobOrder with OPEN->ASSIGNED auto-transition
updateJobOrderStatus with guard map + ticket sync (auto-resolve, revert-to-open)
Ticket auto-resolve when all non-cancelled job orders COMPLETED
Ticket revert-to-OPEN when all job orders CANCELLED
Technician self-service status updates (getMyJobOrders)
4 API routes
POST /api/tickets/[id]/job-orders, GET+PUT /api/job-orders/[id], POST /api/job-orders/[id]/status, GET /api/job-orders
17 integration tests
lifecycle, bidirectional sync, partial completion, isolation
03-05 (if any final operational module uses job orders)
Phase 4 (inventory may link to job orders)
Phase 5 (reporting may aggregate job order metrics)
added patterns
VALID_JO_TRANSITIONS guard map: same pattern as VALID_TICKET_TRANSITIONS from 03-03
Bidirectional sync: job order service calls checkTicketAutoResolve/checkTicketRevertToOpen from ticket-service
TECHNICIAN self-service: getMyJobOrders delegates to listJobOrders with assignedToId filter
Idempotent ticket resolve: resolveTicket silently no-ops on already-RESOLVED tickets (prevents race conditions)
created modified
prisma/migrations/20260304235859_add_job_orders/migration.sql
src/lib/services/job-order-service.ts
src/app/api/tickets/[id]/job-orders/route.ts
src/app/api/job-orders/route.ts
src/app/api/job-orders/[id]/route.ts
src/app/api/job-orders/[id]/status/route.ts
src/lib/__tests__/job-order-service.test.ts
prisma/schema.prisma
src/lib/prisma-tenant.ts
src/lib/services/ticket-service.ts
checkTicketAutoResolve counts non-cancelled jobs: if count > 0 AND all COMPLETED -> resolve; if count == 0 (all cancelled) -> do nothing (checkTicketRevertToOpen handles that path)
checkTicketRevertToOpen only triggers on ASSIGNED tickets: RESOLVED/CLOSED tickets are not reverted even if all jobs are cancelled
COMPLETED requires outcomeNotes: validated in service, not API layer — ensures completeness regardless of caller
JO-NNNN sequential numbering: findFirst with orderBy desc on orderNumber field, same pattern as TKT-NNNN
TECHNICIAN role auto-filter in GET /api/job-orders: checks !ADMIN && !OFFICE_STAFF to allow staff with both TECHNICIAN and OFFICE_STAFF roles full visibility
Inline tech2 cleanup in getMyJobOrders test: must delete job orders before deleting user (FK constraint on assignedToId)
Job order lifecycle guard map: VALID_JO_TRANSITIONS mirrors VALID_TICKET_TRANSITIONS pattern
Ticket sync after status change: always call checkTicketAutoResolve (COMPLETED) or checkTicketRevertToOpen (CANCELLED) at end of updateJobOrderStatus
Outcome notes validation at service layer: error thrown in service, not in API handler
6min 2026-03-05

Phase 3 Plan 04: Job Orders Summary

JobOrder model (JO-NNNN) with PENDING/IN_PROGRESS/COMPLETED/CANCELLED lifecycle, bidirectional ticket sync (auto-resolve on all complete, revert-to-OPEN on all cancelled), and 17 passing integration tests

Performance

  • Duration: 6 min
  • Started: 2026-03-04T23:58:04Z
  • Completed: 2026-03-05T00:04:26Z
  • Tasks: 2
  • Files modified: 10

Accomplishments

  • JobOrder model with 1:many ticket relation, sequential JO-NNNN numbering, tenant scoping via full 12-operation extension block
  • Job order lifecycle enforced by VALID_JO_TRANSITIONS guard map: PENDING->IN_PROGRESS->COMPLETED(requires outcomeNotes)/CANCELLED
  • Bidirectional ticket sync: OPEN->ASSIGNED on first job creation; auto-resolve when all non-cancelled jobs complete; revert-to-OPEN when all jobs cancelled
  • 17 integration tests covering full lifecycle, auto-resolve, revert-to-open, partial completion, technician self-service, cross-tenant isolation — all green

Task Commits

Each task was committed atomically:

  1. Task 1: JobOrder schema, migration, and tenant scoping - d59f1d5 (feat)
  2. Task 2: Job order service, ticket sync, API routes, and integration tests - 86284f1 (feat)

Plan metadata: (docs commit below)

Files Created/Modified

  • prisma/schema.prisma - Added JobOrderStatus enum, JobOrder model, reverse relations on Ticket and User
  • prisma/migrations/20260304235859_add_job_orders/migration.sql - Migration adding job_orders table with all indexes
  • src/lib/prisma-tenant.ts - Added "jobOrder" to TENANT_SCOPED_MODELS with full 12-operation extension block
  • src/lib/services/ticket-service.ts - Added checkTicketAutoResolve and checkTicketRevertToOpen exports
  • src/lib/services/job-order-service.ts - Full job order service: createJobOrder, updateJobOrderStatus, updateJobOrder, getJobOrder, listJobOrders, getMyJobOrders
  • src/app/api/tickets/[id]/job-orders/route.ts - POST endpoint to create job order from ticket
  • src/app/api/job-orders/route.ts - GET with TECHNICIAN auto-filter
  • src/app/api/job-orders/[id]/route.ts - GET and PUT for single job order
  • src/app/api/job-orders/[id]/status/route.ts - POST for technician self-service status updates
  • src/lib/__tests__/job-order-service.test.ts - 17 integration tests, all passing

Decisions Made

  • checkTicketAutoResolve counts non-cancelled jobs: if count > 0 AND all COMPLETED -> resolve; if count == 0 (all cancelled) -> skip (checkTicketRevertToOpen handles that path)
  • checkTicketRevertToOpen only triggers on ASSIGNED tickets: RESOLVED/CLOSED tickets not reverted even if all jobs are cancelled
  • COMPLETED requires outcomeNotes: validated in service layer, not API layer, to enforce regardless of caller
  • JO-NNNN sequential numbering: same findFirst+orderBy+increment pattern as TKT-NNNN
  • TECHNICIAN role auto-filter in GET /api/job-orders: checks !ADMIN && !OFFICE_STAFF to handle users with multiple roles correctly

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Fixed FK constraint error on tech2 user cleanup in test

  • Found during: Task 2 (getMyJobOrders test)
  • Issue: Test created tech2 user and assigned job orders to them; prisma.user.delete() failed with FK violation on JobOrder_assignedToId_fkey
  • Fix: Added prisma.jobOrder.deleteMany({ where: { assignedToId: tech2Id } }) before user delete in cleanup block
  • Files modified: src/lib/tests/job-order-service.test.ts
  • Verification: All 17 tests pass after fix
  • Committed in: 86284f1 (Task 2 commit)

Total deviations: 1 auto-fixed (1 bug — cleanup ordering) Impact on plan: Minimal. FK cleanup ordering is a standard test concern. No scope creep.

Issues Encountered

None — plan executed cleanly, all tests green on second run after cleanup fix.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • Job order workflow complete and tested; ready for 03-05 (final operational module in Phase 3)
  • Ticket bidirectional sync is production-ready: idempotent resolve prevents race conditions, revert-to-open enforces clean lifecycle
  • CASL permissions for JobOrder were already defined in 01-04 (TECHNICIAN read/update own, OFFICE_STAFF manage, ADMIN manage all)

Phase: 03-operational-modules Completed: 2026-03-05