feat(03-05): TechnicianProfile/JobTypeRate schema, migration, tenant scoping
- Added CompensationModel enum (PER_JOB, SALARY, HYBRID) - Added TechnicianProfile model with userId FK, skills[], zoneId?, compensationModel, monthlySalary?, isActive, @@unique([tenantId,userId]) - Added JobTypeRate model with jobType, rate, isActive, @@unique([tenantId,jobType]) - Added reverse relations: User.technicianProfiles[], Zone.technicianProfiles[] - Applied migration: 20260305000851_add_technician_profiles - Added technicianProfile and jobTypeRate to TENANT_SCOPED_MODELS with full 12-operation extension blocks
This commit is contained in:
@@ -136,6 +136,12 @@ enum JobOrderStatus {
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
enum CompensationModel {
|
||||
PER_JOB
|
||||
SALARY
|
||||
HYBRID
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MODELS
|
||||
// =============================================================================
|
||||
@@ -318,8 +324,9 @@ model Zone {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
subscribers Subscriber[]
|
||||
assignments ZoneAssignment[]
|
||||
subscribers Subscriber[]
|
||||
assignments ZoneAssignment[]
|
||||
technicianProfiles TechnicianProfile[]
|
||||
|
||||
/// Zone names must be unique within a tenant
|
||||
@@unique([tenantId, name])
|
||||
@@ -392,6 +399,8 @@ model User {
|
||||
assignedJobOrders JobOrder[] @relation("JobOrderAssignedTo")
|
||||
/// Job orders created by this user (staff)
|
||||
createdJobOrders JobOrder[] @relation("JobOrderCreatedBy")
|
||||
/// Technician profiles for this user (one per tenant, enforced by @@unique([tenantId, userId]))
|
||||
technicianProfiles TechnicianProfile[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -795,3 +804,54 @@ model InvoiceLine {
|
||||
@@index([invoiceId])
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
/// A TechnicianProfile holds ISP-specific attributes for a technician user.
|
||||
/// Compensation model determines how the technician is paid:
|
||||
/// PER_JOB: sum of job type rates for completed jobs
|
||||
/// SALARY: fixed monthly salary only
|
||||
/// HYBRID: monthly salary + per-job bonuses
|
||||
model TechnicianProfile {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
/// The technician user (FK to User) — one profile per user per tenant
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
phone String?
|
||||
/// PostgreSQL array of skill tags (e.g., ["fiber", "wireless", "installation"])
|
||||
skills String[]
|
||||
/// Zone the technician is primarily assigned to (optional)
|
||||
zoneId String?
|
||||
zone Zone? @relation(fields: [zoneId], references: [id])
|
||||
compensationModel CompensationModel @default(PER_JOB)
|
||||
/// Fixed monthly salary component (used for SALARY and HYBRID models)
|
||||
monthlySalary Decimal? @db.Decimal(10, 2)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
/// One profile per user per tenant
|
||||
@@unique([tenantId, userId])
|
||||
/// RLS-ready index — always present on tenant-scoped models
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
/// A JobTypeRate defines the per-job bonus rate for a specific job type at tenant level.
|
||||
/// Used to calculate per-job compensation for technicians (PER_JOB and HYBRID models).
|
||||
/// Missing rates default to 0 bonus (not an error).
|
||||
model JobTypeRate {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
/// The job type identifier (e.g., "Installation", "Repair", "Maintenance")
|
||||
jobType String
|
||||
/// Per-job bonus rate for this job type
|
||||
rate Decimal @db.Decimal(10, 2)
|
||||
description String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
/// One rate per job type per tenant
|
||||
@@unique([tenantId, jobType])
|
||||
/// RLS-ready index — always present on tenant-scoped models
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user