// P4 Tasks (AK-P4b) — lightweight cross-cutting task module. // // AD-21: one self-contained schema file per module. `tenantId` / // `createdByUserId` / `assignedToUserId` / `assignedToGroupId` are intentionally // BARE String scalars (no `@relation`) so this file has zero merge-conflict // surface against `core.prisma`/`accounts.prisma` (no back-relation fields on // Tenant/User/Group needed). Referential integrity is carried by app-level // scoping (taskService + taskAccess) plus the RLS policies from the merge // migration (see local/AK-P4b-merge-migration.sql). // // Assignment rule (DoD 2): a task is assigned to EXACTLY ONE of a user or a // group; default = creator. Enforced in the service layer and by a DB CHECK // constraint (num_nonnulls(...) = 1) added in the merge migration. // // Entity linking: the primary "this task is about X" association lives inline on // the Task (`linkedEntityType`/`linkedEntityId`). The generic, reusable // cross-entity link table is `EntityLink` (entityLinks.prisma, Gate W1); wiring // tasks to concrete CRM entities happens in P6. enum TaskStatus { open in_progress blocked done cancelled } enum TaskPriority { low normal high urgent } enum TaskSource { manual chat form_submission automation api } enum TaskEntityType { contact company lead deal project user order appointment } /// A task, scoped per tenant. Soft-delete via `archivedAt` (archive flag). A /// completed task (`status = done`, `completedAt` set) is auto-archived 30 days /// later by a maintenance cron. model Task { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String createdByUserId String title String description String? // markdown status TaskStatus @default(open) priority TaskPriority @default(normal) deadline DateTime? // Assignment: EXACTLY ONE of user/group (DB CHECK in merge migration); // default = creator (filled by the service when neither is provided). assignedToUserId String? assignedToGroupId String? // Inline primary entity association (polymorphic) — "this task is about X". linkedEntityType TaskEntityType? linkedEntityId String? source TaskSource @default(manual) sourceRef String? // marker/link back to origin (chat msg / form submission / automation run) completedAt DateTime? isDemoData Boolean @default(false) archivedAt DateTime? watchers TaskWatcher[] @@index([tenantId, archivedAt, status, deadline]) @@index([tenantId, assignedToUserId]) @@index([tenantId, assignedToGroupId]) @@index([tenantId, createdByUserId]) @@index([tenantId, linkedEntityType, linkedEntityId]) @@index([tenantId, status, completedAt]) // for the 30-day archive sweep } /// A user watching a task (M:N). Watchers receive reminder notifications in /// addition to the assignee. model TaskWatcher { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String taskId String userId String task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) @@unique([taskId, userId]) @@index([tenantId, userId]) }