Add educator leave management and working time accounts

This commit is contained in:
t.indorf
2026-10-07 15:25:55 +02:00
parent ec3f4f933d
commit 043fed2e46
19 changed files with 5631 additions and 200 deletions
+227 -21
View File
@@ -144,6 +144,8 @@ model Kita {
notdienstAssignments NotdienstAssignment[]
notdienstAlerts NotdienstAlert[]
notdienstSwapRequests NotdienstSwapRequest[]
staffProfiles StaffProfile[]
staffHolidays StaffHoliday[]
@@map("kitas")
}
@@ -163,9 +165,9 @@ model Family {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users User[]
children Child[]
dutyAssignments DutyAssignment[]
users User[]
children Child[]
dutyAssignments DutyAssignment[]
notdienstSwapRequestsStarted NotdienstSwapRequest[] @relation("NotdienstSwapRequesterFamily")
@@index([kitaId])
@@ -224,21 +226,23 @@ model User {
// Relations
dutyAssignments ParentDutyAssignment[]
notdienstAvailabilities NotdienstAvailability[]
notdienstAlertsAssigned NotdienstAlert[] @relation("NotdienstAlertParent")
notdienstAlertsTriggered NotdienstAlert[] @relation("NotdienstAlertTrigger")
notdienstPlansCreated NotdienstPlan[] @relation("NotdienstPlanCreator")
notdienstSwapRequestsStarted NotdienstSwapRequest[] @relation("NotdienstSwapRequester")
notdienstSwapRequestsResponded NotdienstSwapRequest[] @relation("NotdienstSwapResponder")
announcementsAuthored Announcement[] @relation("AnnouncementAuthor")
announcementReads AnnouncementRead[]
notdienstAvailabilities NotdienstAvailability[]
notdienstAlertsAssigned NotdienstAlert[] @relation("NotdienstAlertParent")
notdienstAlertsTriggered NotdienstAlert[] @relation("NotdienstAlertTrigger")
notdienstPlansCreated NotdienstPlan[] @relation("NotdienstPlanCreator")
notdienstSwapRequestsStarted NotdienstSwapRequest[] @relation("NotdienstSwapRequester")
notdienstSwapRequestsResponded NotdienstSwapRequest[] @relation("NotdienstSwapResponder")
announcementsAuthored Announcement[] @relation("AnnouncementAuthor")
announcementReads AnnouncementRead[]
termineCreated Termin[] @relation("TerminCreator")
termineApproved Termin[] @relation("TerminApprover")
mitbringselItems MitbringselItem[]
invitationsCreated Invitation[] @relation("InvitationCreator")
auditLogsAuthored AuditLog[] @relation("AuditLogActor")
invitationsCreated Invitation[] @relation("InvitationCreator")
auditLogsAuthored AuditLog[] @relation("AuditLogActor")
staffProfile StaffProfile?
staffNotifications StaffNotification[]
@@index([kitaId])
@@index([familyId])
@@ -295,11 +299,213 @@ model Educator {
updatedAt DateTime @updatedAt
notdienstAlerts NotdienstAlert[]
staffProfile StaffProfile?
@@index([kitaId])
@@map("educators")
}
// Personalbereich: Tage als UTC-Datum, Zeiten als Minuten, Anspruch in halben Tagen.
enum StaffRequestStatus {
PENDING
APPROVED
REJECTED
CANCEL_REQUESTED
CANCELLED
}
enum StaffLeaveKind {
VACATION
TIME_OFF
SICKNESS
}
enum StaffWeekStatus {
DRAFT
SUBMITTED
APPROVED
}
model StaffProfile {
id String @id @default(cuid())
kitaId String
kita Kita @relation(fields: [kitaId], references: [id], onDelete: Cascade)
educatorId String @unique
educator Educator @relation(fields: [educatorId], references: [id], onDelete: NoAction)
userId String? @unique
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
employmentStart DateTime @db.Date
employmentEnd DateTime? @db.Date
trackingStart DateTime @db.Date
openingMinutes Int @default(0)
createdAt DateTime @default(now())
workPatterns StaffWorkPattern[]
dayOverrides StaffDayOverride[]
leaveAccounts StaffLeaveAccount[]
leaves StaffLeave[]
timeEntries StaffTimeEntry[]
weeks StaffWeek[]
adjustments StaffAdjustment[]
history StaffHistory[]
notifications StaffNotification[]
@@index([kitaId])
@@map("staff_profiles")
}
model StaffWorkPattern {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
effectiveFrom DateTime @db.Date
// Montag bis Sonntag; 0 bedeutet arbeitsfrei.
dailyMinutes Int[]
@@unique([profileId, effectiveFrom])
@@map("staff_work_patterns")
}
model StaffDayOverride {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
date DateTime @db.Date
minutes Int
reason String
@@unique([profileId, date])
@@map("staff_day_overrides")
}
model StaffHoliday {
id String @id @default(cuid())
kitaId String
kita Kita @relation(fields: [kitaId], references: [id], onDelete: Cascade)
date DateTime @db.Date
name String
@@unique([kitaId, date])
@@map("staff_holidays")
}
model StaffLeaveAccount {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
year Int
entitlementUnits Int
carryUnits Int @default(0)
@@unique([profileId, year])
@@map("staff_leave_accounts")
}
model StaffLeave {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
kind StaffLeaveKind
status StaffRequestStatus @default(PENDING)
startDate DateTime @db.Date
endDate DateTime @db.Date
halfDay Boolean @default(false)
minutes Int?
note String?
decisionNote String?
requestedById String
reviewedById String?
reviewedAt DateTime?
createdAt DateTime @default(now())
days StaffLeaveDay[]
@@index([profileId, status, startDate])
@@map("staff_leaves")
}
model StaffLeaveDay {
id String @id @default(cuid())
leaveId String
leave StaffLeave @relation(fields: [leaveId], references: [id], onDelete: Cascade)
date DateTime @db.Date
vacationUnits Int @default(0)
creditMinutes Int
@@unique([leaveId, date])
@@map("staff_leave_days")
}
model StaffTimeEntry {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
date DateTime @db.Date
startMinute Int
endMinute Int
breakMinutes Int @default(0)
note String?
@@index([profileId, date])
@@map("staff_time_entries")
}
model StaffWeek {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
weekStart DateTime @db.Date
status StaffWeekStatus @default(DRAFT)
submittedAt DateTime?
reviewedAt DateTime?
reviewedById String?
decisionNote String?
// Snapshot von Soll-, Ist- und Abwesenheitsminuten bei Genehmigung.
dailySnapshot Json?
@@unique([profileId, weekStart])
@@map("staff_weeks")
}
model StaffAdjustment {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
date DateTime @db.Date
minutes Int
reason String
actorId String
createdAt DateTime @default(now())
@@index([profileId, date])
@@map("staff_adjustments")
}
model StaffHistory {
id String @id @default(cuid())
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
actorId String
action String
details String
createdAt DateTime @default(now())
@@index([profileId, createdAt])
@@map("staff_history")
}
model StaffNotification {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
profileId String
profile StaffProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
message String
readAt DateTime?
createdAt DateTime @default(now())
@@index([userId, readAt, createdAt])
@@map("staff_notifications")
}
// =====================================================================
// PARENT DUTIES (Feste Ämter / Elterndienste, Modul 3)
// =====================================================================
@@ -657,7 +863,7 @@ model NotdienstAssignment {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
alerts NotdienstAlert[]
alerts NotdienstAlert[]
swapRequestsAsTarget NotdienstSwapRequest[] @relation("NotdienstSwapTargetAssignment")
swapRequestsAsOffered NotdienstSwapRequest[] @relation("NotdienstSwapOfferedAssignment")
@@ -685,11 +891,11 @@ model NotdienstSwapRequest {
offeredAssignmentId String
offeredAssignment NotdienstAssignment @relation("NotdienstSwapOfferedAssignment", fields: [offeredAssignmentId], references: [id], onDelete: Cascade)
status NotdienstSwapStatus @default(PENDING)
status NotdienstSwapStatus @default(PENDING)
message String?
respondedById String?
respondedBy User? @relation("NotdienstSwapResponder", fields: [respondedById], references: [id], onDelete: SetNull)
respondedBy User? @relation("NotdienstSwapResponder", fields: [respondedById], references: [id], onDelete: SetNull)
respondedAt DateTime?
createdAt DateTime @default(now())
@@ -756,13 +962,13 @@ model AuditLog {
actorUser User? @relation("AuditLogActor", fields: [actorUserId], references: [id], onDelete: SetNull)
actorEmail String
action String
targetType AuditTargetType
targetId String
targetLabel String
action String
targetType AuditTargetType
targetId String
targetLabel String
targetKitaId String?
metadata Json?
metadata Json?
createdAt DateTime @default(now())
@@index([actorUserId])