From bdd0fda7d2974449987c3690d2c06b2093754080 Mon Sep 17 00:00:00 2001 From: kevin-asprec Date: Thu, 5 Mar 2026 08:05:50 +0800 Subject: [PATCH] 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 --- .planning/STATE.md | 22 +-- .../03-operational-modules/03-04-SUMMARY.md | 142 ++++++++++++++++++ 2 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 .planning/phases/03-operational-modules/03-04-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 7f62a0e..7adc162 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -10,11 +10,11 @@ See: .planning/PROJECT.md (updated 2026-03-04) ## Current Position Phase: 3 of 5 (Operational Modules) — In progress -Plan: 4 of 5 in phase 3 (15/20 total complete) -Status: Phase 3 in progress. 03-01 (Zones), 03-02 (Collections/Remittances), 03-03 (Ticketing) complete. -Last activity: 2026-03-05 — Completed 03-02-PLAN.md (Collections/Remittances — 26 tests, 1030 COA, zone enforcement) +Plan: 5 of 5 in phase 3 (16/20 total complete) +Status: Phase 3 in progress. 03-01 (Zones), 03-02 (Collections/Remittances), 03-03 (Ticketing), 03-04 (Job Orders) complete. +Last activity: 2026-03-05 — Completed 03-04-PLAN.md (Job Orders — 17 tests, bidirectional ticket sync, PENDING/IN_PROGRESS/COMPLETED/CANCELLED lifecycle) -Progress: [███████████████░] 75% (15/20 plans across all phases) +Progress: [████████████████░] 80% (16/20 plans across all phases) ## Performance Metrics @@ -29,11 +29,11 @@ Progress: [███████████████░] 75% (15/20 plans ac |-------|-------|-------|----------| | 01-foundation | 5/5 complete | 41 min | 8.2 min | | 02-subscriber-and-billing-core | 5/5 complete | 57 min | 11.4 min | -| 03-operational-modules | 3/5 in progress | ~50 min | ~17 min | +| 03-operational-modules | 4/5 in progress | ~56 min | ~14 min | **Recent Trend:** -- Last 10 plans: 01-03 (9 min), 01-04 (7 min), 01-05 (6 min), 02-01 (7 min), 02-03 (7 min), 02-02 (16 min), 02-04 (12 min), 02-05 (8 min), 03-01 (15 min), 03-02 (11 min) -- Trend: stable — 03-02 migration applied cleanly, 26 tests passed on first run +- Last 10 plans: 01-04 (7 min), 01-05 (6 min), 02-01 (7 min), 02-03 (7 min), 02-02 (16 min), 02-04 (12 min), 02-05 (8 min), 03-01 (15 min), 03-02 (11 min), 03-04 (6 min) +- Trend: stable — 03-04 migration applied cleanly, 17 tests passed on second run (1 fix for FK cleanup ordering) *Updated after each plan completion* @@ -111,6 +111,10 @@ Recent decisions affecting current work: - [03-02]: Variance is non-blocking — any discrepancy is an audit record; remittance proceeds to VERIFIED regardless - [03-02]: Collection cleanup order: collectionAllocations → collections → invoiceLines → invoices → journalEntryLines → null reversesEntryId → journalEntries → zoneAssignments → subscribers → zones → servicePlans → accountingPeriods → accounts → users → tenant - [03-02]: ISP COA now has 29 accounts — added 1030 Cash in Transit between 1020 Cash in Bank and 1100 AR +- [03-04]: checkTicketAutoResolve counts non-cancelled jobs: if count > 0 AND all COMPLETED -> resolve; if count == 0 (all cancelled) -> skip (revertToOpen handles that path) +- [03-04]: checkTicketRevertToOpen only triggers on ASSIGNED tickets — RESOLVED/CLOSED tickets not reverted even if all jobs are cancelled +- [03-04]: COMPLETED requires outcomeNotes validated at service layer (not API) — enforces completeness regardless of caller +- [03-04]: TECHNICIAN self-service via getMyJobOrders delegates to listJobOrders with assignedToId filter; GET /api/job-orders checks !ADMIN && !OFFICE_STAFF for auto-filter to handle multi-role users ### Pending Todos @@ -128,6 +132,6 @@ None. ## Session Continuity -Last session: 2026-03-04T23:54:44Z -Stopped at: Completed 03-02-PLAN.md (Collections/Remittances — 26 tests, 1030 COA, zone enforcement, two-party verification) +Last session: 2026-03-05T00:04:26Z +Stopped at: Completed 03-04-PLAN.md (Job Orders — 17 tests, bidirectional ticket sync, lifecycle enforcement) Resume file: None diff --git a/.planning/phases/03-operational-modules/03-04-SUMMARY.md b/.planning/phases/03-operational-modules/03-04-SUMMARY.md new file mode 100644 index 0000000..2f22365 --- /dev/null +++ b/.planning/phases/03-operational-modules/03-04-SUMMARY.md @@ -0,0 +1,142 @@ +--- +phase: 03-operational-modules +plan: "04" +subsystem: api +tags: [prisma, postgresql, job-orders, tickets, technician, lifecycle, tenant-isolation] + +# Dependency graph +requires: + - phase: 03-03 + provides: Ticket model with lifecycle (OPEN/ASSIGNED/RESOLVED/CLOSED), resolveTicket, transitionTicketStatus +provides: + - 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 +affects: + - 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) + +# Tech tracking +tech-stack: + 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)" + +key-files: + created: + - 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 + modified: + - prisma/schema.prisma + - src/lib/prisma-tenant.ts + - src/lib/services/ticket-service.ts + +key-decisions: + - "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)" + +patterns-established: + - "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" + +# Metrics +duration: 6min +completed: 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*