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>
10 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 | 05 | execute | 3 |
|
|
true |
|
Purpose: Technicians need profiles with skills and zone assignments for proper job routing. The compensation system supports the real-world ISP pattern where technicians can be paid per-job, monthly salary, or a hybrid of both. The compensation summary report gives management visibility into technician costs.
Output: TechnicianProfile model, JobTypeRate model (tenant-level rates), TechnicianService for profile management, CompensationService for period calculation, compensation summary report API, 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-04-SUMMARY.md @prisma/schema.prisma @src/lib/services/job-order-service.ts Task 1: TechnicianProfile and JobTypeRate Prisma models + migration prisma/schema.prisma, src/lib/prisma-tenant.ts **New enum:** - `CompensationModel { PER_JOB, SALARY, HYBRID }`TechnicianProfile model — extends User with technician-specific data:
- id (uuid PK), tenantId
- userId (FK to User, unique) — one profile per user
- phone (String?)
- skills (String[]) — array of skill tags, e.g., ["fiber splicing", "router config", "installation"]
- zoneId (String? FK to Zone) — primary assigned zone
- compensationModel (CompensationModel default PER_JOB)
- monthlySalary (Decimal? 10,2) — null if pure per-job
- isActive (Boolean default true)
- createdAt, updatedAt
- @@unique([tenantId, userId]) — one profile per user per tenant
- @@index([tenantId])
JobTypeRate model — tenant-level flat rates per job type:
- id (uuid PK), tenantId
- jobType (JobType enum — reuse from 03-04)
- rate (Decimal 10,2) — flat amount per completed job of this type (e.g., 500.00 for INSTALLATION)
- description (String?) — e.g., "Standard installation rate"
- isActive (Boolean default true)
- createdAt, updatedAt
- @@unique([tenantId, jobType]) — one rate per job type per tenant
- @@index([tenantId])
Update relations:
- User: add
technicianProfile TechnicianProfile? - Zone: add
technicianProfiles TechnicianProfile[]
Add to TENANT_SCOPED_MODELS: "technicianProfile", "jobTypeRate"
Run npx prisma migrate dev --name add-technician-compensation
- npx prisma migrate dev completes without errors
- npx prisma generate succeeds
- Schema has TechnicianProfile and JobTypeRate models
TechnicianProfile and JobTypeRate models exist with proper relations, compensation model enum, and migration applied.
CompensationService (src/lib/services/compensation-service.ts):
-
calculateTechnicianCompensation(db, technicianUserId, { periodStart, periodEnd }):- Load technician profile (for compensationModel and monthlySalary)
- Query completed job orders assigned to this technician within date range
- For each completed job order, look up JobTypeRate for the job type
- Calculate:
- jobCount: number of completed jobs
- jobBonusTotal: sum of (rate for each job type * count of that type)
- baseSalary: monthlySalary if SALARY or HYBRID model, else 0
- totalCompensation: baseSalary + jobBonusTotal
- Return: { technicianId, technicianName, compensationModel, baseSalary, jobCount, jobBonusTotal, totalCompensation, jobDetails: [{ jobOrderId, jobType, completedAt, rate }] }
-
getCompensationSummary(db, { periodStart, periodEnd, technicianId? }):- If technicianId provided, calculate for one technician
- Otherwise, calculate for all active technicians
- Return array of per-technician summaries (same structure as above)
- Include grand totals: totalJobs, totalBaseSalary, totalBonuses, grandTotal
API Routes:
GET /api/technicians— list technician profiles. ADMIN, OFFICE_STAFF.POST /api/technicians— create profile. ADMIN only. Body: { userId, phone?, skills?, zoneId?, compensationModel, monthlySalary? }GET /api/technicians/[id]— get profile detail. ADMIN, OFFICE_STAFF, TECHNICIAN (own only).PUT /api/technicians/[id]— update profile. ADMIN only.GET /api/technicians/[id]/compensation— get compensation for a technician for a period. ADMIN. Query params: periodStart, periodEnd.GET /api/job-type-rates— list rates. ADMIN.POST /api/job-type-rates— set rate (upsert). ADMIN. Body: { jobType, rate, description? }PUT /api/job-type-rates/[id]— update rate. ADMIN.GET /api/reports/compensation— compensation summary for all technicians. ADMIN. Query params: periodStart, periodEnd, technicianId?
Integration Tests (src/lib/__tests__/compensation-service.test.ts):
- Create technician profile (validates TECHNICIAN role)
- PER_JOB model: 3 completed installations at 500 each = 1500 total
- SALARY model: monthly salary of 15000, job count tracked but no per-job bonus
- HYBRID model: 15000 salary + 3 installations at 500 = 16500 total
- Only COMPLETED job orders count (PENDING, IN_PROGRESS, CANCELLED excluded)
- Jobs outside date range excluded
- Job type with no configured rate: 0 bonus for that job (not an error)
- Compensation summary across multiple technicians with grand totals
- Drill-down detail: each job with type, date, rate
- Job type rate CRUD (create, update, upsert by jobType)
npx vitest run src/lib/__tests__/compensation-service.test.ts— all tests passnpx vitest run— full suite passes (no regressions) TechnicianService handles profiles with compensation config. CompensationService calculates per-period compensation for per-job, salary, and hybrid models. Summary report shows per-technician totals with job-by-job drill-down. All models tested.
<success_criteria>
- TechnicianProfile and JobTypeRate models with migration applied
- TechnicianService manages profiles with hybrid compensation config
- CompensationService correctly calculates for PER_JOB, SALARY, and HYBRID models
- Period compensation summary with drill-down to individual jobs
- Job type rates are tenant-level and admin-configurable
- Integration tests prove all three compensation models with correct calculations
- Full test suite passes with no regressions </success_criteria>