Commit ff7f1d41 authored by Administrator's avatar Administrator

Update prisma/schema.prisma via Son of Anton

parent 0f56e115
// ============================================================
// AL-ARCADE HR PLATFORM v3.0 "THE GRIND"
// Complete Prisma Schema ALL modules, ALL phases
// ============================================================
// This is your main Prisma schema file.
// Since we're using multi-file schemas, ensure your package.json
// has the correct prisma configuration.
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch", "fullTextIndex"]
previewFeatures = ["multiSchema", "prismaSchemaFolder"]
}
datasource db {
......@@ -13,51 +12,34 @@ datasource db {
url = env("DATABASE_URL")
}
// ============================================================
// ============================================
// ENUMS
// ============================================================
// ============================================
enum Role {
SUPER_ADMIN
ADMIN
TEAM_LEAD
PROJECT_LEADER
CONTRACTOR
}
enum ContractorType {
FULL_TIME
PART_TIME
PROJECT_BASED
INTERN
}
enum UserStatus {
INVITED
enum ContractorStatus {
ONBOARDING
ACTIVE
ON_PIP
SUSPENDED
OFFBOARDING
OFFBOARDED
}
enum InviteStatus {
PENDING
ACCEPTED
EXPIRED
REVOKED
}
enum BoardVisibility {
PUBLIC
PRIVATE
TEAM
}
enum BoardMemberRole {
OWNER
ADMIN
MEMBER
VIEWER
enum DayType {
IN_OFFICE
REMOTE
OFF
}
enum CardPriority {
......@@ -68,163 +50,137 @@ enum CardPriority {
NONE
}
enum DeductionType {
MANUAL
AUTO_LATE_REPORT
AUTO_MISSED_REPORT
AUTO_PIP
PRESET
}
enum DeductionStatus {
PENDING
APPROVED
REJECTED
APPEALED
REVERSED
}
enum AdjustmentType {
ADVANCE
REIMBURSEMENT
BONUS
CORRECTION
OTHER
enum ColumnType {
BACKLOG
TODO
DOING
FROZEN
IN_REVIEW
DONE
CUSTOM
}
enum AdjustmentStatus {
PENDING
APPROVED
REJECTED
enum DeductionCategory {
A
B
C
D
}
enum BountyStatus {
PENDING
EARNED
PAID
enum DeductionStatus {
DRAFT
PENDING_ADMIN_REVIEW
PENDING_ACKNOWLEDGMENT
PENDING_RESPONSE
UPHELD
REDUCED
DISMISSED
AUTO_APPLIED
CANCELLED
}
enum PayrollStatus {
enum ReportStatus {
DRAFT
CALCULATING
REVIEW
SUBMITTED
LATE
APPROVED
PAID
CANCELLED
}
enum NotificationType {
PASSIVE
IMPORTANT
BLOCKING
}
enum NotificationCategory {
CARD
BOARD
SALARY
DEDUCTION
BOUNTY
ADJUSTMENT
PAYROLL
REPORT
EVALUATION
PIP
MESSAGE
MEETING
NOTICE
POLICY
ONBOARDING
UNAVAILABILITY
SCHEDULE
SYSTEM
}
enum ConversationType {
DIRECT
GROUP
}
enum MessageType {
TEXT
FILE
SYSTEM
AUTO_APPROVED
FLAGGED_VAGUE
FLAGGED_INCONSISTENT
REVISION_REQUESTED
AMENDED
UNREPORTED
}
enum ReportStatus {
DRAFT
enum PayrollStatus {
PENDING_CALCULATION
CALCULATED
UNDER_REVIEW
SUBMITTED
APPROVED
REJECTED
AMENDMENT_REQUESTED
PROCESSING
PAID
}
enum EvaluationStatus {
DRAFT
IN_PROGRESS
COMPLETED
PENDING_TECHNICAL
PENDING_PROFESSIONAL
COMPILED
ACKNOWLEDGED
RESPONDED
}
enum PIPStatus {
enum PipStatus {
ACTIVE
PASSED
FAILED
EXTENDED
CANCELLED
}
enum LearningGoalStatus {
NOT_STARTED
IN_PROGRESS
COMPLETED
CANCELLED
ACTIVE
OVERDUE
PASSED
FAILED
EXTENDED
}
enum UnavailabilityType {
VACATION
SICK
PERSONAL
EMERGENCY
enum NotificationType {
BLOCKING
IMPORTANT
INFORMATIONAL
}
enum NoticeType {
GENERAL_ANNOUNCEMENT
OFFICIAL_WARNING
POLICY_UPDATE
CUSTOM
}
enum AdjustmentType {
POSITIVE
NEGATIVE
}
enum AdjustmentCategory {
ADVANCE
REIMBURSEMENT
BONUS
CORRECTION
LOAN
OTHER
}
enum RequestStatus {
PENDING
enum AdjustmentStatus {
PENDING_APPROVAL
APPROVED
REJECTED
CANCELLED
}
enum MeetingStatus {
SCHEDULED
IN_PROGRESS
COMPLETED
CANCELLED
}
enum ContractStatus {
DRAFT
enum InviteStatus {
ACTIVE
USED
EXPIRED
TERMINATED
REVOKED
}
enum OffboardingStatus {
PENDING
IN_PROGRESS
enum MeetingStatus {
SCHEDULED
COMPLETED
CANCELLED
}
enum WebhookStatus {
ACTIVE
INACTIVE
FAILED
enum UnavailabilityReason {
PERSONAL
MEDICAL
RELIGIOUS
EMERGENCY
OTHER
}
enum RecurrencePattern {
enum RecurrenceType {
DAILY
WEEKLY
BIWEEKLY
......@@ -232,501 +188,321 @@ enum RecurrencePattern {
CUSTOM
}
enum AuditAction {
CREATE
UPDATE
DELETE
ARCHIVE
RESTORE
LOGIN
LOGOUT
PASSWORD_RESET
PASSWORD_CHANGE
ROLE_CHANGE
STATUS_CHANGE
APPROVE
REJECT
SUBMIT
MOVE
ASSIGN
UNASSIGN
LOCK
UNLOCK
ACKNOWLEDGE
BULK_ACTION
EXPORT
IMPORT
SETTINGS_CHANGE
GENERATE
SEND
}
// ============================================================
// AUTH & USERS
// ============================================================
enum LabelScope {
ORGANIZATION
BOARD
}
enum ApiKeyScope {
READ_ONLY
READ_WRITE
ADMIN
}
enum ConversationType {
DIRECT
GROUP
}
enum TerminationType {
VOLUNTARY
FOR_CAUSE
MUTUAL
CONTRACT_EXPIRY
}
// ============================================
// CORE MODELS
// ============================================
model User {
id String @id @default(uuid())
email String @unique
username String @unique
passwordHash String
firstName String
lastName String
displayName String?
avatar String?
role Role @default(CONTRACTOR)
contractorType ContractorType?
status UserStatus @default(INVITED)
department String?
title String?
phone String?
timezone String @default("Africa/Cairo")
bio String?
dateOfBirth DateTime?
startDate DateTime?
lastLoginAt DateTime?
isOnline Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
id String @id @default(uuid())
username String @unique
passwordHash String
firstName String
lastName String
nameArabic String?
nationalId String? @unique
dateOfBirth DateTime?
phone String?
phoneSecondary String?
address String?
avatar String?
role Role @default(CONTRACTOR)
contractorType ContractorType?
status ContractorStatus @default(ONBOARDING)
weeklySchedule Json?
baseSalaryPiasters Int?
actualSalaryPiasters Int?
bankName String?
bankAccountNumber String?
bankAccountHolderName String?
emergencyContactName String?
emergencyContactPhone String?
emergencyContactRelationship String?
currentStreak Int @default(0)
bestStreak Int @default(0)
forcePasswordChange Boolean @default(false)
activatedAt DateTime?
contractSignedAt DateTime?
lastLoginAt DateTime?
loginAttempts Int @default(0)
lockedUntil DateTime?
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations Auth & Sessions
// Relations
boards BoardMember[]
assignedCards CardAssignee[]
watchedCards CardWatcher[]
createdCards Card[] @relation("CardCreator")
comments Comment[]
attachments Attachment[]
reports DailyReport[] @relation("ReportAuthor")
reviewedReports DailyReport[] @relation("ReportReviewer")
deductions Deduction[] @relation("DeductionTarget")
initiatedDeductions Deduction[] @relation("DeductionInitiator")
reviewedDeductions Deduction[] @relation("DeductionReviewer")
bountyPayouts BountyPayout[]
adjustments Adjustment[] @relation("AdjustmentTarget")
createdAdjustments Adjustment[] @relation("AdjustmentCreator")
reviewedAdjustments Adjustment[] @relation("AdjustmentReviewer")
evaluations Evaluation[] @relation("EvaluationTarget")
techEvaluator Evaluation[] @relation("TechEvaluator")
profEvaluator Evaluation[] @relation("ProfEvaluator")
pips Pip[] @relation("PipTarget")
createdPips Pip[] @relation("PipCreator")
learningGoals LearningGoal[] @relation("GoalTarget")
createdGoals LearningGoal[] @relation("GoalCreator")
unavailability Unavailability[]
scheduleRequests ScheduleChangeRequest[] @relation("ScheduleRequester")
reviewedSchedules ScheduleChangeRequest[] @relation("ScheduleReviewer")
conversations ConversationParticipant[]
sentMessages Message[]
notifications Notification[]
noticeAcknowledgments NoticeAcknowledgment[]
policyAcknowledgments PolicyAcknowledgment[]
contracts Contract[]
meetings MeetingInvitee[]
createdMeetings Meeting[] @relation("MeetingCreator")
invites Invite[]
refreshTokens RefreshToken[]
payrollLines PayrollLine[]
createdNotices Notice[]
sessions Session[]
passwordResetTokens PasswordResetToken[]
// Relations Onboarding
sentInvites Invite[] @relation("InviteSender")
receivedInvite Invite? @relation("InviteRecipient")
onboardingProgress ContractorOnboarding?
// Relations Boards & Cards
ownedBoards Board[] @relation("BoardCreator")
boardMemberships BoardMember[]
createdCards Card[] @relation("CardCreator")
cardAssignments CardAssignee[]
cardWatches CardWatcher[]
cardComments CardComment[]
cardActivities CardActivity[] @relation("CardActivityActor")
lockedCards Card[] @relation("CardLocker")
// Relations Financial
contractorSalaries ContractorSalary[] @relation("SalaryContractor")
createdSalaries ContractorSalary[] @relation("SalaryCreator")
earnedBounties Bounty[] @relation("BountyContractor")
createdBounties Bounty[] @relation("BountyCreator")
receivedDeductions Deduction[] @relation("DeductionContractor")
createdDeductions Deduction[] @relation("DeductionCreator")
approvedDeductions Deduction[] @relation("DeductionApprover")
reversedDeductions Deduction[] @relation("DeductionReverser")
receivedAdjustments SalaryAdjustment[] @relation("AdjustmentContractor")
createdAdjustments SalaryAdjustment[] @relation("AdjustmentCreator")
approvedAdjustments SalaryAdjustment[] @relation("AdjustmentApprover")
payrollItems PayrollItem[]
generatedPayrolls PayrollPeriod[] @relation("PayrollGenerator")
approvedPayrolls PayrollPeriod[] @relation("PayrollApprover")
// Relations Communication
conversationParticipations ConversationParticipant[]
sentMessages Message[]
createdNotices Notice[] @relation("NoticeCreator")
createdPolicies Policy[] @relation("PolicyCreator")
policyAcknowledgments PolicyAcknowledgment[]
// Relations Notifications
notifications Notification[]
notificationPreferences NotificationPreference[]
// Relations Reports
dailyReports DailyReport[] @relation("ReportContractor")
reviewedReports DailyReport[] @relation("ReportReviewer")
// Relations Performance
evaluationsReceived Evaluation[] @relation("EvaluationContractor")
evaluationsGiven Evaluation[] @relation("EvaluationEvaluator")
createdEvalTemplates EvaluationTemplate[]
pipsReceived PIP[] @relation("PIPContractor")
pipsManaged PIP[] @relation("PIPManager")
learningGoals LearningGoal[]
competencyAssessments CompetencyAssessment[] @relation("AssessmentContractor")
givenAssessments CompetencyAssessment[] @relation("AssessmentAssessor")
// Relations Time & Scheduling
unavailabilityRequests UnavailabilityRequest[] @relation("UnavailabilityContractor")
reviewedUnavailabilities UnavailabilityRequest[] @relation("UnavailabilityReviewer")
scheduleChangeRequests ScheduleChangeRequest[] @relation("ScheduleChangeContractor")
reviewedScheduleChanges ScheduleChangeRequest[] @relation("ScheduleChangeReviewer")
workSchedule WorkSchedule?
// Relations Meetings
createdMeetings Meeting[] @relation("MeetingCreator")
meetingParticipations MeetingParticipant[]
// Relations Contracts & Offboarding
contracts Contract[]
offboardingProcesses OffboardingProcess[] @relation("OffboardingContractor")
initiatedOffboardings OffboardingProcess[] @relation("OffboardingInitiator")
// Relations Admin
apiKeys ApiKey[]
createdWebhooks Webhook[]
// Relations Notes & Activity
privateNotesAbout PrivateNote[] @relation("NoteSubject")
privateNotesAuthored PrivateNote[] @relation("NoteAuthor")
activityFeedItems ActivityFeedItem[] @relation("ActivityActor")
uploadedFiles File[]
// Relations Templates
createdBoardTemplates BoardTemplate[]
createdCardTemplates CardTemplate[]
@@index([email])
@@index([username])
@@index([role])
@@index([status])
@@index([role])
@@index([contractorType])
@@index([deletedAt])
@@index([department])
@@map("users")
}
model Session {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
token String @unique
refreshToken String @unique
ipAddress String?
userAgent String?
expiresAt DateTime
lastActiveAt DateTime @default(now())
createdAt DateTime @default(now())
model RefreshToken {
id String @id @default(uuid())
token String @unique
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
expiresAt DateTime
createdAt DateTime @default(now())
@@index([userId])
@@index([token])
@@index([expiresAt])
@@map("sessions")
}
model PasswordResetToken {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
token String @unique
expiresAt DateTime
usedAt DateTime?
createdAt DateTime @default(now())
model Session {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
ipAddress String?
userAgent String?
lastActivity DateTime @default(now())
createdAt DateTime @default(now())
@@index([token])
@@index([userId])
@@map("password_reset_tokens")
}
// ============================================================
// SYSTEM SETTINGS
// ============================================================
model SystemSetting {
model Setting {
id String @id @default(uuid())
key String @unique
value Json
description String?
category String @default("general")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
@@index([key])
@@index([category])
@@map("system_settings")
}
// ============================================================
// AUDIT TRAIL (IMMUTABLE)
// ============================================================
model AuditLog {
id String @id @default(uuid())
userId String?
action AuditAction
entityType String
entityId String?
oldValue Json?
newValue Json?
metadata Json?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
// NO updatedAt. NO deletedAt. IMMUTABLE.
model AuditTrail {
id String @id @default(uuid())
userId String?
action String
entityType String
entityId String?
method String?
url String?
before Json?
after Json?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
@@index([userId])
@@index([entityType, entityId])
@@index([entityType])
@@index([action])
@@index([createdAt])
@@map("audit_logs")
}
// ============================================================
// ONBOARDING
// ============================================================
model Invite {
id String @id @default(uuid())
email String
role Role @default(CONTRACTOR)
senderId String
sender User @relation("InviteSender", fields: [senderId], references: [id])
recipientId String? @unique
recipient User? @relation("InviteRecipient", fields: [recipientId], references: [id])
token String @unique
status InviteStatus @default(PENDING)
message String?
expiresAt DateTime
acceptedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@index([token])
@@index([status])
@@map("invites")
}
model OnboardingChecklist {
id String @id @default(uuid())
name String
description String?
role Role @default(CONTRACTOR)
isActive Boolean @default(true)
items OnboardingChecklistItem[]
contractors ContractorOnboarding[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@map("onboarding_checklists")
}
model OnboardingChecklistItem {
id String @id @default(uuid())
checklistId String
checklist OnboardingChecklist @relation(fields: [checklistId], references: [id], onDelete: Cascade)
title String
description String?
order Int
isRequired Boolean @default(true)
progressItems ContractorOnboardingItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([checklistId])
@@map("onboarding_checklist_items")
}
model ContractorOnboarding {
id String @id @default(uuid())
contractorId String @unique
contractor User @relation(fields: [contractorId], references: [id], onDelete: Cascade)
checklistId String
checklist OnboardingChecklist @relation(fields: [checklistId], references: [id])
completedAt DateTime?
items ContractorOnboardingItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@map("contractor_onboardings")
}
model ContractorOnboardingItem {
id String @id @default(uuid())
onboardingId String
onboarding ContractorOnboarding @relation(fields: [onboardingId], references: [id], onDelete: Cascade)
checklistItemId String
checklistItem OnboardingChecklistItem @relation(fields: [checklistItemId], references: [id])
isCompleted Boolean @default(false)
completedAt DateTime?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([onboardingId, checklistItemId])
@@map("contractor_onboarding_items")
}
// ============================================================
// ============================================
// BOARDS & KANBAN
// ============================================================
// ============================================
model Board {
id String @id @default(uuid())
name String
description String?
visibility BoardVisibility @default(TEAM)
createdById String
createdBy User @relation("BoardCreator", fields: [createdById], references: [id])
prefix String?
color String?
icon String?
isArchived Boolean @default(false)
archivedAt DateTime?
columns Column[]
members BoardMember[]
labels Label[]
cards Card[]
id String @id @default(uuid())
name String
description String?
key String @unique
visibility String @default("PRIVATE")
icon String?
color String?
allowContractorCreation Boolean @default(true)
autoArchiveDoneCardsDays Int @default(30)
isArchived Boolean @default(false)
nextCardNumber Int @default(1)
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
columns BoardColumn[]
members BoardMember[]
cards Card[]
labels Label[] @relation("BoardLabels")
savedFilters SavedFilter[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([createdById])
@@index([visibility])
@@index([deletedAt])
@@index([key])
@@index([isArchived])
@@map("boards")
@@index([deletedAt])
}
model BoardMember {
id String @id @default(uuid())
model BoardColumn {
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role BoardMemberRole @default(MEMBER)
joinedAt DateTime @default(now())
@@unique([boardId, userId])
@@index([userId])
@@map("board_members")
}
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
name String
type ColumnType
icon String?
position Int
wipLimit Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model BoardTemplate {
id String @id @default(uuid())
name String
description String?
structure Json // { columns: [...], labels: [...] }
createdById String
createdBy User @relation(fields: [createdById], references: [id])
isPublic Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
cards Card[]
@@map("board_templates")
@@index([boardId])
@@index([type])
}
model Column {
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
name String
position Float
color String?
wipLimit Int?
isDone Boolean @default(false)
cards Card[]
model BoardMember {
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role String @default("MEMBER")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([boardId, userId])
@@index([boardId])
@@index([boardId, position])
@@map("columns")
@@index([userId])
}
model Label {
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
name String
color String
cards CardLabel[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(uuid())
name String
color String
textColor String @default("#FFFFFF")
scope LabelScope @default(ORGANIZATION)
boardId String?
board Board? @relation("BoardLabels", fields: [boardId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cards CardLabel[]
@@index([scope])
@@index([boardId])
@@map("labels")
}
// ============================================================
// CARDS
// ============================================================
model Card {
id String @id @default(uuid())
title String
description String?
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
columnId String
column Column @relation(fields: [columnId], references: [id])
position Float
priority CardPriority @default(NONE)
column BoardColumn @relation(fields: [columnId], references: [id], onDelete: Cascade)
cardNumber String
title String
description String?
priority CardPriority @default(NONE)
position Float @default(0)
dueDate DateTime?
startDate DateTime?
estimatedHours Float?
actualHours Float?
bountyPiasters Int @default(0)
coverImage String?
isLocked Boolean @default(false)
lockedById String?
lockedBy User? @relation("CardLocker", fields: [lockedById], references: [id])
lockedAt DateTime?
parentCardId String?
parentCard Card? @relation("CardSubtasks", fields: [parentCardId], references: [id])
subtasks Card[] @relation("CardSubtasks")
createdById String
createdBy User @relation("CardCreator", fields: [createdById], references: [id])
bountyPiasters Int @default(0)
bountyPaidOut Boolean @default(false)
frozenReason String?
version Int @default(1)
isArchived Boolean @default(false)
completedAt DateTime?
archivedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
firstDoingAt DateTime?
totalFrozenMs Int @default(0)
createdById String
createdBy User @relation("CardCreator", fields: [createdById], references: [id])
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
assignees CardAssignee[]
watchers CardWatcher[]
labels CardLabel[]
comments CardComment[]
checklists Checklist[]
attachments CardAttachment[]
activities CardActivity[]
bounties Bounty[]
reportEntries DailyReportEntry[]
assignees CardAssignee[]
watchers CardWatcher[]
labels CardLabel[]
comments Comment[]
attachments Attachment[]
checklists Checklist[]
bountyPayouts BountyPayout[]
deductions Deduction[]
@@index([boardId])
@@index([columnId])
@@index([createdById])
@@index([priority])
@@index([cardNumber])
@@index([isArchived])
@@index([dueDate])
@@index([deletedAt])
@@index([boardId, columnId, position])
@@index([parentCardId])
@@map("cards")
@@index([createdById])
}
model CardAssignee {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
assignedAt DateTime @default(now())
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([cardId, userId])
@@index([cardId])
@@index([userId])
@@map("card_assignees")
}
model CardWatcher {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
addedAt DateTime @default(now())
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([cardId, userId])
@@index([cardId])
@@index([userId])
@@map("card_watchers")
}
model CardLabel {
......@@ -737,359 +513,374 @@ model CardLabel {
label Label @relation(fields: [labelId], references: [id], onDelete: Cascade)
@@unique([cardId, labelId])
@@map("card_labels")
@@index([cardId])
@@index([labelId])
}
model CardComment {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
authorId String
author User @relation(fields: [authorId], references: [id])
content String
isEdited Boolean @default(false)
parentId String?
parent CardComment? @relation("CommentReplies", fields: [parentId], references: [id])
replies CardComment[] @relation("CommentReplies")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
// ============================================
// COMMENTS, CHECKLISTS, ATTACHMENTS
// ============================================
model Comment {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
authorId String
author User @relation(fields: [authorId], references: [id])
content String
editedAt DateTime?
editableUntil DateTime?
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([cardId])
@@index([authorId])
@@index([parentId])
@@map("card_comments")
}
model Checklist {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
title String
position Float
items ChecklistItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
name String
position Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
items ChecklistItem[]
@@index([cardId])
@@map("checklists")
}
model ChecklistItem {
id String @id @default(uuid())
checklistId String
checklist Checklist @relation(fields: [checklistId], references: [id], onDelete: Cascade)
title String
isCompleted Boolean @default(false)
completedAt DateTime?
assigneeId String?
position Float
dueDate DateTime?
text String
isChecked Boolean @default(false)
position Int @default(0)
checkedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([checklistId])
@@map("checklist_items")
}
model CardAttachment {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
fileId String
file File @relation(fields: [fileId], references: [id])
createdAt DateTime @default(now())
@@index([cardId])
@@map("card_attachments")
}
model CardActivity {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id], onDelete: Cascade)
actorId String
actor User @relation("CardActivityActor", fields: [actorId], references: [id])
action String // "moved", "assigned", "commented", "priority_changed", etc.
details Json? // { from: "To Do", to: "In Progress" }
createdAt DateTime @default(now())
model Attachment {
id String @id @default(uuid())
cardId String?
card Card? @relation(fields: [cardId], references: [id], onDelete: Cascade)
uploadedById String
uploadedBy User @relation(fields: [uploadedById], references: [id])
fileName String
fileSize Int
mimeType String
storagePath String
entityType String @default("card")
entityId String?
createdAt DateTime @default(now())
@@index([cardId])
@@index([cardId, createdAt])
@@map("card_activities")
@@index([entityType, entityId])
}
model CardTemplate {
id String @id @default(uuid())
name String
description String?
boardId String?
defaultColumn String?
priority CardPriority @default(NONE)
bountyPiasters Int @default(0)
templateData Json // { checklists: [...], labels: [...], description: "..." }
createdById String
createdBy User @relation(fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
// ============================================
// FINANCIAL
// ============================================
recurringCards RecurringCard[]
model Deduction {
id String @id @default(uuid())
userId String
user User @relation("DeductionTarget", fields: [userId], references: [id])
initiatedById String?
initiatedBy User? @relation("DeductionInitiator", fields: [initiatedById], references: [id])
reviewedById String?
reviewedBy User? @relation("DeductionReviewer", fields: [reviewedById], references: [id])
cardId String?
card Card? @relation(fields: [cardId], references: [id], onDelete: SetNull)
category DeductionCategory
subCategory String
status DeductionStatus @default(DRAFT)
amountPiasters Int
appliedAmountPiasters Int?
description String
evidence Json?
violationDate DateTime?
acknowledgedAt DateTime?
contractorResponse String?
respondedAt DateTime?
reviewNotes String?
reviewedAt DateTime?
payrollMonth Int?
payrollYear Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("card_templates")
@@index([userId])
@@index([status])
@@index([category])
@@index([payrollMonth, payrollYear])
@@index([createdAt])
}
model RecurringCard {
id String @id @default(uuid())
templateId String
template CardTemplate @relation(fields: [templateId], references: [id])
boardId String
columnId String
pattern RecurrencePattern
cronExpression String?
customDays Json? // [1,3,5] for Mon/Wed/Fri
nextRunAt DateTime
lastRunAt DateTime?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model BountyPayout {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
amountPiasters Int
splitPercentage Float @default(100)
payrollMonth Int
payrollYear Int
paidAt DateTime @default(now())
createdAt DateTime @default(now())
@@index([isActive, nextRunAt])
@@map("recurring_cards")
@@index([userId])
@@index([cardId])
@@index([payrollMonth, payrollYear])
}
model SavedFilter {
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
userId String
name String
filters Json // { priority: [...], assignees: [...], labels: [...], dueDate: ... }
isDefault Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model Adjustment {
id String @id @default(uuid())
userId String
user User @relation("AdjustmentTarget", fields: [userId], references: [id])
createdById String
createdBy User @relation("AdjustmentCreator", fields: [createdById], references: [id])
reviewedById String?
reviewedBy User? @relation("AdjustmentReviewer", fields: [reviewedById], references: [id])
type AdjustmentType
category AdjustmentCategory
amountPiasters Int
description String
status AdjustmentStatus @default(PENDING_APPROVAL)
effectiveMonth Int
effectiveYear Int
reviewNotes String?
reviewedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([boardId, userId])
@@map("saved_filters")
}
// ============================================================
// FINANCIAL SALARY
// ============================================================
model ContractorSalary {
id String @id @default(uuid())
contractorId String
contractor User @relation("SalaryContractor", fields: [contractorId], references: [id])
baseSalaryPiasters Int
contractorType ContractorType
effectiveDate DateTime
endDate DateTime?
workDaysPerWeek Int @default(5)
hoursPerDay Float @default(8)
isActive Boolean @default(true)
notes String?
createdById String
createdBy User @relation("SalaryCreator", fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([contractorId, isActive])
@@index([effectiveDate])
@@map("contractor_salaries")
}
// ============================================================
// FINANCIAL BOUNTIES
// ============================================================
model Bounty {
@@index([userId])
@@index([status])
@@index([effectiveMonth, effectiveYear])
}
model Payroll {
id String @id @default(uuid())
month Int
year Int
status PayrollStatus @default(PENDING_CALCULATION)
totalGrossPiasters Int @default(0)
totalNetPiasters Int @default(0)
contractorCount Int @default(0)
calculatedAt DateTime?
submittedAt DateTime?
approvedAt DateTime?
paidAt DateTime?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lines PayrollLine[]
@@unique([month, year])
@@index([status])
}
model PayrollLine {
id String @id @default(uuid())
payrollId String
payroll Payroll @relation(fields: [payrollId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id])
actualSalaryPiasters Int
totalBountiesPiasters Int @default(0)
totalDeductionsPiasters Int @default(0)
totalAdjustmentsPiasters Int @default(0)
netPayablePiasters Int @default(0)
breakdown Json?
createdAt DateTime @default(now())
@@index([payrollId])
@@index([userId])
}
// ============================================
// REPORTS
// ============================================
model DailyReport {
id String @id @default(uuid())
cardId String
card Card @relation(fields: [cardId], references: [id])
contractorId String
contractor User @relation("BountyContractor", fields: [contractorId], references: [id])
amountPiasters Int
status BountyStatus @default(PENDING)
earnedAt DateTime?
payrollItemId String?
payrollItem PayrollItem? @relation(fields: [payrollItemId], references: [id])
createdById String
createdBy User @relation("BountyCreator", fields: [createdById], references: [id])
month Int
year Int
userId String
user User @relation("ReportAuthor", fields: [userId], references: [id])
reviewedById String?
reviewedBy User? @relation("ReportReviewer", fields: [reviewedById], references: [id])
reportDate DateTime
status ReportStatus @default(DRAFT)
taskEntries Json?
blockers String?
additionalNotes String?
mood String?
totalHours Float?
submittedAt DateTime?
reviewedAt DateTime?
reviewNotes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([contractorId, month, year])
@@index([cardId])
amendments ReportAmendment[]
@@index([userId])
@@index([reportDate])
@@index([status])
@@map("bounties")
}
// ============================================================
// FINANCIAL DEDUCTIONS
// ============================================================
model ReportAmendment {
id String @id @default(uuid())
reportId String
report DailyReport @relation(fields: [reportId], references: [id], onDelete: Cascade)
reason String
content Json
status String @default("PENDING")
reviewNotes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model DeductionPreset {
id String @id @default(uuid())
name String
description String?
amountPiasters Int
type DeductionType
isActive Boolean @default(true)
deductions Deduction[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([reportId])
}
// ============================================
// EVALUATIONS & PERFORMANCE
// ============================================
model Evaluation {
id String @id @default(uuid())
userId String
user User @relation("EvaluationTarget", fields: [userId], references: [id])
techEvaluatorId String?
techEvaluator User? @relation("TechEvaluator", fields: [techEvaluatorId], references: [id])
profEvaluatorId String?
profEvaluator User? @relation("ProfEvaluator", fields: [profEvaluatorId], references: [id])
month Int
year Int
status EvaluationStatus @default(PENDING_TECHNICAL)
technicalScores Json?
technicalScore Float?
technicalNotes String?
professionalScores Json?
professionalScore Float?
professionalNotes String?
autoMetrics Json?
overallScore Float?
rating String?
contractorResponse String?
acknowledgedAt DateTime?
respondedAt DateTime?
compiledAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, month, year])
@@index([userId])
@@index([status])
@@index([month, year])
}
model Pip {
id String @id @default(uuid())
userId String
user User @relation("PipTarget", fields: [userId], references: [id])
createdById String
createdBy User @relation("PipCreator", fields: [createdById], references: [id])
status PipStatus @default(ACTIVE)
durationDays Int
startDate DateTime @default(now())
endDate DateTime
specificIssues String
improvementTargets String
successCriteria String
consequenceOfFailure String @default("Termination of engagement.")
checkInSchedule String @default("WEEKLY")
checkIns Json?
resultNotes String?
acknowledgedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("deduction_presets")
@@index([userId])
@@index([status])
}
model Deduction {
id String @id @default(uuid())
contractorId String
contractor User @relation("DeductionContractor", fields: [contractorId], references: [id])
amountPiasters Int
type DeductionType
reason String
presetId String?
preset DeductionPreset? @relation(fields: [presetId], references: [id])
triggerCardId String?
triggerReportId String?
status DeductionStatus @default(PENDING)
approvedById String?
approvedBy User? @relation("DeductionApprover", fields: [approvedById], references: [id])
approvedAt DateTime?
appealReason String?
appealedAt DateTime?
reversedById String?
reversedBy User? @relation("DeductionReverser", fields: [reversedById], references: [id])
reversedAt DateTime?
reversalReason String?
month Int
year Int
payrollItemId String?
payrollItem PayrollItem? @relation(fields: [payrollItemId], references: [id])
createdById String
createdBy User @relation("DeductionCreator", fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
model LearningGoal {
id String @id @default(uuid())
userId String
user User @relation("GoalTarget", fields: [userId], references: [id])
createdById String
createdBy User @relation("GoalCreator", fields: [createdById], references: [id])
title String
description String
competencyAreaId String?
competencyArea CompetencyArea? @relation(fields: [competencyAreaId], references: [id])
deadline DateTime
status LearningGoalStatus @default(ACTIVE)
assessmentMethod String?
passCriteria String?
assessmentNotes String?
assessedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([contractorId, month, year])
@@index([userId])
@@index([status])
@@index([type])
@@index([deletedAt])
@@map("deductions")
}
// ============================================================
// FINANCIAL SALARY ADJUSTMENTS
// ============================================================
model CompetencyArea {
id String @id @default(uuid())
name String
description String?
position Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model SalaryAdjustment {
id String @id @default(uuid())
contractorId String
contractor User @relation("AdjustmentContractor", fields: [contractorId], references: [id])
amountPiasters Int
type AdjustmentType
reason String
status AdjustmentStatus @default(PENDING)
approvedById String?
approvedBy User? @relation("AdjustmentApprover", fields: [approvedById], references: [id])
approvedAt DateTime?
month Int
year Int
payrollItemId String?
payrollItem PayrollItem? @relation(fields: [payrollItemId], references: [id])
createdById String
createdBy User @relation("AdjustmentCreator", fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
learningGoals LearningGoal[]
}
@@index([contractorId])
@@index([contractorId, month, year])
@@index([status])
@@map("salary_adjustments")
}
// ============================================================
// FINANCIAL PAYROLL
// ============================================================
model PayrollPeriod {
id String @id @default(uuid())
month Int
year Int
status PayrollStatus @default(DRAFT)
totalGrossPiasters Int @default(0)
totalDeductionsPiasters Int @default(0)
totalBountiesPiasters Int @default(0)
totalAdjustmentsPiasters Int @default(0)
totalNetPiasters Int @default(0)
contractorCount Int @default(0)
generatedById String?
generatedBy User? @relation("PayrollGenerator", fields: [generatedById], references: [id])
approvedById String?
approvedBy User? @relation("PayrollApprover", fields: [approvedById], references: [id])
approvedAt DateTime?
paidAt DateTime?
notes String?
items PayrollItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// ============================================
// COMMUNICATION
// ============================================
@@unique([month, year])
@@index([status])
@@map("payroll_periods")
}
model PayrollItem {
id String @id @default(uuid())
payrollPeriodId String
payrollPeriod PayrollPeriod @relation(fields: [payrollPeriodId], references: [id], onDelete: Cascade)
contractorId String
contractor User @relation(fields: [contractorId], references: [id])
baseSalaryPiasters Int
totalBountiesPiasters Int @default(0)
totalAdjustmentsPiasters Int @default(0)
totalDeductionsPiasters Int @default(0)
netSalaryPiasters Int
breakdown Json? // detailed breakdown
bounties Bounty[]
deductions Deduction[]
adjustments SalaryAdjustment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([payrollPeriodId, contractorId])
@@index([contractorId])
@@map("payroll_items")
}
// ============================================================
// COMMUNICATION CONVERSATIONS & MESSAGES
// ============================================================
model Notification {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
type NotificationType @default(INFORMATIONAL)
title String
message String?
link String?
isRead Boolean @default(false)
isAcknowledged Boolean @default(false)
acknowledgedAt DateTime?
createdAt DateTime @default(now())
@@index([userId])
@@index([type])
@@index([isRead])
@@index([createdAt])
}
model Conversation {
id String @id @default(uuid())
type ConversationType
name String?
avatar String?
id String @id @default(uuid())
name String?
type ConversationType @default(DIRECT)
lastMessageAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
participants ConversationParticipant[]
messages Message[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([type])
@@index([lastMessageAt])
@@map("conversations")
}
model ConversationParticipant {
......@@ -1098,15 +889,12 @@ model ConversationParticipant {
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
isAdmin Boolean @default(false)
isMuted Boolean @default(false)
lastReadAt DateTime?
joinedAt DateTime @default(now())
leftAt DateTime?
createdAt DateTime @default(now())
@@unique([conversationId, userId])
@@index([conversationId])
@@index([userId])
@@map("conversation_participants")
}
model Message {
......@@ -1115,691 +903,292 @@ model Message {
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
senderId String
sender User @relation(fields: [senderId], references: [id])
type MessageType @default(TEXT)
content String?
fileId String?
file File? @relation(fields: [fileId], references: [id])
replyToId String?
replyTo Message? @relation("MessageReplies", fields: [replyToId], references: [id])
replies Message[] @relation("MessageReplies")
isEdited Boolean @default(false)
isPinned Boolean @default(false)
readBy MessageRead[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
content String
attachments Json?
deletedAt DateTime?
createdAt DateTime @default(now())
@@index([conversationId])
@@index([conversationId, createdAt])
@@index([senderId])
@@map("messages")
}
model MessageRead {
id String @id @default(uuid())
messageId String
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
userId String
readAt DateTime @default(now())
@@unique([messageId, userId])
@@map("message_reads")
@@index([createdAt])
}
// ============================================================
// COMMUNICATION NOTICES & POLICIES
// ============================================================
model Notice {
id String @id @default(uuid())
id String @id @default(uuid())
title String
content String
priority NotificationType @default(IMPORTANT)
isPinned Boolean @default(false)
targetRoles Role[]
publishedAt DateTime?
expiresAt DateTime?
type NoticeType @default(GENERAL_ANNOUNCEMENT)
isBlocking Boolean @default(false)
targetRoles Json?
createdById String
createdBy User @relation("NoticeCreator", fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
createdBy User @relation(fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([publishedAt])
@@index([deletedAt])
@@map("notices")
}
model Policy {
id String @id @default(uuid())
title String
content String
version Int @default(1)
requiresAck Boolean @default(true)
targetRoles Role[]
effectiveDate DateTime
createdById String
createdBy User @relation("PolicyCreator", fields: [createdById], references: [id])
acknowledgments PolicyAcknowledgment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([effectiveDate])
@@index([deletedAt])
@@map("policies")
}
acknowledgments NoticeAcknowledgment[]
model PolicyAcknowledgment {
id String @id @default(uuid())
policyId String
policy Policy @relation(fields: [policyId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
acknowledgedAt DateTime @default(now())
@@unique([policyId, userId])
@@map("policy_acknowledgments")
}
// ============================================================
// NOTIFICATIONS
// ============================================================
model Notification {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
type NotificationType @default(PASSIVE)
category NotificationCategory
title String
message String
actionUrl String?
metadata Json?
isRead Boolean @default(false)
readAt DateTime?
acknowledgedAt DateTime?
expiresAt DateTime?
createdAt DateTime @default(now())
@@index([userId])
@@index([userId, isRead])
@@index([userId, category])
@@index([type])
@@index([createdAt])
@@map("notifications")
}
model NotificationPreference {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
category NotificationCategory
inApp Boolean @default(true)
sound Boolean @default(true)
enabled Boolean @default(true)
@@unique([userId, category])
@@map("notification_preferences")
}
// ============================================================
// DAILY REPORTS
// ============================================================
model DailyReport {
id String @id @default(uuid())
contractorId String
contractor User @relation("ReportContractor", fields: [contractorId], references: [id])
date DateTime @db.Date
status ReportStatus @default(DRAFT)
summary String?
submittedAt DateTime?
reviewedById String?
reviewedBy User? @relation("ReportReviewer", fields: [reviewedById], references: [id])
reviewedAt DateTime?
reviewNotes String?
isLate Boolean @default(false)
entries DailyReportEntry[]
amendments ReportAmendment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([contractorId, date])
@@index([contractorId])
@@index([contractorId, date])
@@index([status])
@@index([date])
@@map("daily_reports")
}
model DailyReportEntry {
id String @id @default(uuid())
reportId String
report DailyReport @relation(fields: [reportId], references: [id], onDelete: Cascade)
description String
hoursSpent Float
cardId String?
card Card? @relation(fields: [cardId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([reportId])
@@index([cardId])
@@map("daily_report_entries")
}
model ReportAmendment {
id String @id @default(uuid())
reportId String
report DailyReport @relation(fields: [reportId], references: [id], onDelete: Cascade)
content String
requestedAt DateTime @default(now())
resolvedAt DateTime?
createdAt DateTime @default(now())
@@index([reportId])
@@map("report_amendments")
}
// ============================================================
// PERFORMANCE EVALUATIONS
// ============================================================
model EvaluationTemplate {
id String @id @default(uuid())
name String
description String?
criteria EvaluationCriteria[]
evaluations Evaluation[]
isActive Boolean @default(true)
createdById String
createdBy User @relation(fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@map("evaluation_templates")
}
model EvaluationCriteria {
id String @id @default(uuid())
templateId String
template EvaluationTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
name String
description String?
weight Float // percentage (0-100)
maxScore Int @default(5)
order Int
scores EvaluationScore[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model NoticeAcknowledgment {
id String @id @default(uuid())
noticeId String
notice Notice @relation(fields: [noticeId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@index([templateId])
@@map("evaluation_criteria")
@@unique([noticeId, userId])
@@index([noticeId])
@@index([userId])
}
model Evaluation {
id String @id @default(uuid())
contractorId String
contractor User @relation("EvaluationContractor", fields: [contractorId], references: [id])
evaluatorId String
evaluator User @relation("EvaluationEvaluator", fields: [evaluatorId], references: [id])
templateId String?
template EvaluationTemplate? @relation(fields: [templateId], references: [id])
period String // "2024-Q1", "2024-01", etc.
month Int
year Int
status EvaluationStatus @default(DRAFT)
overallScore Float?
overallComments String?
autoMetrics Json? // snapshot of computed metrics
acknowledgedAt DateTime?
scores EvaluationScore[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([evaluatorId])
@@index([contractorId, month, year])
@@index([status])
@@map("evaluations")
}
model EvaluationScore {
id String @id @default(uuid())
evaluationId String
evaluation Evaluation @relation(fields: [evaluationId], references: [id], onDelete: Cascade)
criteriaId String
criteria EvaluationCriteria @relation(fields: [criteriaId], references: [id])
score Float
comments String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([evaluationId, criteriaId])
@@map("evaluation_scores")
}
// ============================================================
// PERFORMANCE PIPs
// ============================================================
model PIP {
id String @id @default(uuid())
contractorId String
contractor User @relation("PIPContractor", fields: [contractorId], references: [id])
managerId String
manager User @relation("PIPManager", fields: [managerId], references: [id])
reason String
status PIPStatus @default(ACTIVE)
startDate DateTime
endDate DateTime
extendedDate DateTime?
goals PIPGoal[]
checkIns PIPCheckIn[]
outcome String?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([status])
@@index([managerId])
@@map("pips")
}
// ============================================
// SCHEDULING & TIME
// ============================================
model PIPGoal {
model Holiday {
id String @id @default(uuid())
pipId String
pip PIP @relation(fields: [pipId], references: [id], onDelete: Cascade)
title String
description String?
targetDate DateTime?
isCompleted Boolean @default(false)
completedAt DateTime?
order Int
name String
startDate DateTime
endDate DateTime
isRecurring Boolean @default(false)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([pipId])
@@map("pip_goals")
@@index([startDate])
}
model PIPCheckIn {
id String @id @default(uuid())
pipId String
pip PIP @relation(fields: [pipId], references: [id], onDelete: Cascade)
meetingId String? @unique
meeting Meeting? @relation(fields: [meetingId], references: [id])
model Unavailability {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
startDate DateTime
endDate DateTime
reason UnavailabilityReason @default(PERSONAL)
notes String?
progress String?
date DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([pipId])
@@map("pip_check_ins")
@@index([userId])
@@index([startDate])
}
// ============================================================
// PERFORMANCE LEARNING GOALS & COMPETENCIES
// ============================================================
model ScheduleChangeRequest {
id String @id @default(uuid())
userId String
user User @relation("ScheduleRequester", fields: [userId], references: [id])
reviewedById String?
reviewedBy User? @relation("ScheduleReviewer", fields: [reviewedById], references: [id])
currentSchedule Json
proposedSchedule Json
effectiveDate DateTime
reason String
status String @default("PENDING")
reviewNotes String?
reviewedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model LearningGoal {
id String @id @default(uuid())
contractorId String
contractor User @relation(fields: [contractorId], references: [id])
title String
description String?
status LearningGoalStatus @default(NOT_STARTED)
targetDate DateTime?
completedAt DateTime?
progress Int @default(0) // 0-100
resources Json? // [{ title, url, type }]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([userId])
@@index([status])
@@map("learning_goals")
}
model Competency {
id String @id @default(uuid())
name String
model Meeting {
id String @id @default(uuid())
title String
description String?
category String?
levels Json // [{ level: 1, description: "..." }, ...]
assessments CompetencyAssessment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@map("competencies")
}
startTime DateTime
endTime DateTime
location String?
status MeetingStatus @default(SCHEDULED)
recurrence String?
notes Json?
createdById String
createdBy User @relation("MeetingCreator", fields: [createdById], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model CompetencyAssessment {
id String @id @default(uuid())
competencyId String
competency Competency @relation(fields: [competencyId], references: [id])
contractorId String
contractor User @relation("AssessmentContractor", fields: [contractorId], references: [id])
assessorId String
assessor User @relation("AssessmentAssessor", fields: [assessorId], references: [id])
level Int
notes String?
assessedAt DateTime @default(now())
createdAt DateTime @default(now())
invitees MeetingInvitee[]
@@index([contractorId])
@@index([competencyId, contractorId])
@@map("competency_assessments")
@@index([startTime])
@@index([status])
}
// ============================================================
// TIME & SCHEDULING
// ============================================================
model MeetingInvitee {
id String @id @default(uuid())
meetingId String
meeting Meeting @relation(fields: [meetingId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
attended Boolean @default(false)
model WorkSchedule {
id String @id @default(uuid())
contractorId String @unique
contractor User @relation(fields: [contractorId], references: [id])
workDays Json // [1,2,3,4,5] = Mon-Fri
startTime String // "09:00"
endTime String // "17:00"
timezone String @default("Africa/Cairo")
effectiveDate DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("work_schedules")
}
model UnavailabilityRequest {
id String @id @default(uuid())
contractorId String
contractor User @relation("UnavailabilityContractor", fields: [contractorId], references: [id])
type UnavailabilityType
startDate DateTime
endDate DateTime
reason String?
status RequestStatus @default(PENDING)
reviewedById String?
reviewedBy User? @relation("UnavailabilityReviewer", fields: [reviewedById], references: [id])
reviewedAt DateTime?
reviewNotes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([status])
@@index([startDate, endDate])
@@map("unavailability_requests")
@@unique([meetingId, userId])
@@index([meetingId])
@@index([userId])
}
model Holiday {
id String @id @default(uuid())
name String
date DateTime @db.Date
isRecurring Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// ============================================
// DOCUMENTS
// ============================================
@@unique([date])
@@map("holidays")
}
model Contract {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
contractorType String?
contractText String?
scheduleSnapshot Json?
salaryAtSigning Int?
signedAt DateTime?
signatureData Json?
startDate DateTime?
endDate DateTime?
status String @default("ACTIVE")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model ScheduleChangeRequest {
id String @id @default(uuid())
contractorId String
contractor User @relation("ScheduleChangeContractor", fields: [contractorId], references: [id])
currentData Json // snapshot of current schedule
requestedData Json // requested changes
reason String
status RequestStatus @default(PENDING)
reviewedById String?
reviewedBy User? @relation("ScheduleChangeReviewer", fields: [reviewedById], references: [id])
reviewedAt DateTime?
reviewNotes String?
effectiveDate DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([userId])
@@index([status])
@@map("schedule_change_requests")
}
// ============================================================
// MEETINGS
// ============================================================
model Policy {
id String @id @default(uuid())
title String
content String
version Int @default(1)
requiresAcknowledgment Boolean @default(true)
publishedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
model Meeting {
id String @id @default(uuid())
title String
description String?
startTime DateTime
endTime DateTime
location String?
meetingUrl String?
status MeetingStatus @default(SCHEDULED)
createdById String
createdBy User @relation("MeetingCreator", fields: [createdById], references: [id])
participants MeetingParticipant[]
pipCheckIn PIPCheckIn?
notes String?
agenda String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
acknowledgments PolicyAcknowledgment[]
@@index([startTime])
@@index([createdById])
@@index([status])
@@map("meetings")
@@index([title])
}
model MeetingParticipant {
id String @id @default(uuid())
meetingId String
meeting Meeting @relation(fields: [meetingId], references: [id], onDelete: Cascade)
model PolicyAcknowledgment {
id String @id @default(uuid())
policyId String
policy Policy @relation(fields: [policyId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
status RequestStatus @default(PENDING) // PENDING=invited, APPROVED=accepted, REJECTED=declined
respondedAt DateTime?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([meetingId, userId])
@@unique([policyId, userId])
@@index([policyId])
@@index([userId])
@@map("meeting_participants")
}
// ============================================================
// CONTRACTS & OFFBOARDING
// ============================================================
// ============================================
// OFFBOARDING
// ============================================
model Contract {
id String @id @default(uuid())
contractorId String
contractor User @relation(fields: [contractorId], references: [id])
title String
content String?
fileId String?
file File? @relation(fields: [fileId], references: [id])
status ContractStatus @default(DRAFT)
startDate DateTime
endDate DateTime?
salaryPiasters Int?
contractorType ContractorType?
terms Json?
signedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
model Termination {
id String @id @default(uuid())
userId String
type TerminationType
reason String
effectiveDate DateTime
noticeDate DateTime @default(now())
finalSettlement Json?
checklistItems Json?
status String @default("INITIATED")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([status])
@@map("contracts")
}
model OffboardingProcess {
id String @id @default(uuid())
contractorId String
contractor User @relation("OffboardingContractor", fields: [contractorId], references: [id])
initiatedById String
initiatedBy User @relation("OffboardingInitiator", fields: [initiatedById], references: [id])
status OffboardingStatus @default(PENDING)
reason String?
effectiveDate DateTime
items OffboardingItem[]
notes String?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([contractorId])
@@index([userId])
@@index([status])
@@map("offboarding_processes")
}
model OffboardingItem {
id String @id @default(uuid())
offboardingId String
offboarding OffboardingProcess @relation(fields: [offboardingId], references: [id], onDelete: Cascade)
title String
description String?
isCompleted Boolean @default(false)
completedAt DateTime?
order Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// ============================================
// ONBOARDING
// ============================================
model Invite {
id String @id @default(uuid())
code String @unique
contractorType ContractorType
status InviteStatus @default(ACTIVE)
welcomeNote String?
expiresAt DateTime
usedAt DateTime?
usedById String?
createdById String
createdBy User @relation(fields: [createdById], references: [id])
createdAt DateTime @default(now())
@@index([offboardingId])
@@map("offboarding_items")
@@index([code])
@@index([status])
}
// ============================================================
// API KEYS & WEBHOOKS
// ============================================================
// ============================================
// API & INTEGRATIONS
// ============================================
model ApiKey {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
id String @id @default(uuid())
name String
keyHash String @unique
keyPrefix String // first 8 chars for identification
permissions Json // ["cards:read", "cards:write", ...]
expiresAt DateTime?
keyHash String @unique
keyPrefix String
scope ApiKeyScope @default(READ_ONLY)
description String?
isActive Boolean @default(true)
lastUsedAt DateTime?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([keyHash])
@@index([userId])
@@map("api_keys")
@@index([isActive])
}
model Webhook {
id String @id @default(uuid())
userId String
user User @relation(fields: [userId], references: [id])
name String
url String
secret String
events String[] // ["card.created", "card.moved", ...]
status WebhookStatus @default(ACTIVE)
failCount Int @default(0)
deliveries WebhookDelivery[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
id String @id @default(uuid())
url String
secret String?
events Json
isActive Boolean @default(true)
lastTriggeredAt DateTime?
failureCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([status])
@@map("webhooks")
@@index([isActive])
}
model WebhookDelivery {
id String @id @default(uuid())
webhookId String
webhook Webhook @relation(fields: [webhookId], references: [id], onDelete: Cascade)
event String
payload Json
responseCode Int?
responseBody String?
success Boolean
attemptCount Int @default(1)
deliveredAt DateTime @default(now())
@@index([webhookId])
@@index([webhookId, deliveredAt])
@@map("webhook_deliveries")
}
// ============================================================
// FILES
// ============================================================
model File {
id String @id @default(uuid())
originalName String
storagePath String
storageKey String @unique
mimeType String
sizeBytes Int
uploadedById String
uploadedBy User @relation(fields: [uploadedById], references: [id])
createdAt DateTime @default(now())
// Reverse relations
cardAttachments CardAttachment[]
messages Message[]
contracts Contract[]
@@index([uploadedById])
@@index([storageKey])
@@map("files")
}
// ============================================================
// PRIVATE NOTES (Admin-only, invisible to contractor)
// ============================================================
model PrivateNote {
id String @id @default(uuid())
subjectId String
subject User @relation("NoteSubject", fields: [subjectId], references: [id])
authorId String
author User @relation("NoteAuthor", fields: [authorId], references: [id])
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
model SavedFilter {
id String @id @default(uuid())
boardId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
userId String
name String
filters Json
createdAt DateTime @default(now())
@@index([subjectId])
@@index([authorId])
@@map("private_notes")
@@index([boardId, userId])
}
// ============================================================
// ACTIVITY FEED
// ============================================================
model ActivityFeedItem {
id String @id @default(uuid())
actorId String
actor User @relation("ActivityActor", fields: [actorId], references: [id])
action String // "completed_card", "earned_bounty", "submitted_report", etc.
entityType String
entityId String
metadata Json? // { cardTitle: "...", boardName: "...", amount: 5000 }
visibility String @default("org") // "org", "team", "admin"
createdAt DateTime @default(now())
model RecurringCardDefinition {
id String @id @default(uuid())
boardId String
title String
description String?
labels Json?
priority CardPriority @default(NONE)
assigneeIds Json?
checklists Json?
estimatedHours Float?
recurrenceType RecurrenceType
recurrenceConfig Json?
isActive Boolean @default(true)
nextRunAt DateTime?
lastRunAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([actorId])
@@index([entityType, entityId])
@@index([createdAt])
@@index([visibility, createdAt])
@@map("activity_feed_items")
@@index([boardId])
@@index([isActive])
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment