feat: T4 wave 3 - notifications per user, suggestions API, varroa bands, split/queen events

- Schema III: per-user notifications (broadcast fan-out migration,
  category, link, requiredPermissions filter), notification preferences
  and settings endpoints, calendar withdrawal switch, occupiedFrames
- Suggestions API: list/summary with permission visibility and paging,
  accept/dismiss/reopen incl. bulk, SuggestionView on all responses
- Varroa traffic light by season and method, young colonies, treatment
  effect window, sample-based percentage; shared withdrawal-window helper
  on Berlin calendar days; GET /varroa-measurements/thresholds
- ColonySplit and QueenAssigned domain events with calendar and webhook
  handlers
- Outbox claims compare against now() rounded to timestamp(3); fixes the
  flaky e2e claims of rows written with SQL now()

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-14 14:25:24 +02:00
1 parent d99a697739
commit f468dc5f75
87 files changed
+6022 -510

No files matched your search

+70 -36
View File
@@ -103,33 +103,34 @@ model User {
refreshTokens RefreshToken[]
inspections Inspection[]
varroaMeasurements VarroaMeasurement[]
swarmControlChecks SwarmControlCheck[]
winterChecks WinterCheck[]
treatments Treatment[]
feedings Feeding[]
harvests Harvest[]
extractions Extraction[]
honeyMeasurements HoneyMeasurement[]
stockMovements StockMovement[]
assignedTasks Task[] @relation("TaskAssignee")
createdTasks Task[] @relation("TaskCreator")
decidedSuggestions Suggestion[] @relation("SuggestionDecider")
assignedTaskTemplates TaskTemplate[] @relation("TaskTemplateAssignee")
createdTaskTemplates TaskTemplate[] @relation("TaskTemplateCreator")
colonyMoves ColonyApiaryHistory[]
auditLogs AuditLog[]
bottlingBatches BottlingBatch[]
productionBatches ProductionBatch[]
batchMovements BatchMovement[]
rawMaterialLots RawMaterialLot[]
notifications Notification[]
queenEvaluations QueenEvaluation[]
webhookSubscriptions WebhookSubscription[]
documents Document[]
honeyBatchRelocations HoneyBatchRelocation[]
apiKeys ApiKey[]
inspections Inspection[]
varroaMeasurements VarroaMeasurement[]
swarmControlChecks SwarmControlCheck[]
winterChecks WinterCheck[]
treatments Treatment[]
feedings Feeding[]
harvests Harvest[]
extractions Extraction[]
honeyMeasurements HoneyMeasurement[]
stockMovements StockMovement[]
assignedTasks Task[] @relation("TaskAssignee")
createdTasks Task[] @relation("TaskCreator")
decidedSuggestions Suggestion[] @relation("SuggestionDecider")
assignedTaskTemplates TaskTemplate[] @relation("TaskTemplateAssignee")
createdTaskTemplates TaskTemplate[] @relation("TaskTemplateCreator")
colonyMoves ColonyApiaryHistory[]
auditLogs AuditLog[]
bottlingBatches BottlingBatch[]
productionBatches ProductionBatch[]
batchMovements BatchMovement[]
rawMaterialLots RawMaterialLot[]
notifications Notification[]
notificationPreference NotificationPreference?
queenEvaluations QueenEvaluation[]
webhookSubscriptions WebhookSubscription[]
documents Document[]
honeyBatchRelocations HoneyBatchRelocation[]
apiKeys ApiKey[]
@@index([roleId])
}
@@ -291,6 +292,7 @@ model Colony {
colonyType ColonyType
status ColonyStatus @default(ACTIVE)
strength Int?
occupiedFrames Int?
broodStatus ColonyCondition?
feedStatus ColonyCondition?
healthStatus ColonyCondition?
@@ -1374,21 +1376,52 @@ enum NotificationPriority {
KRITISCH
}
enum NotificationCategory {
TASK_DUE
DIGEST
SUGGESTION
WITHDRAWAL
SEASON
SYSTEM
}
// Jede Zeile gehoert genau einem Empfaenger (T4: Fan-out mit Rechtepruefung
// beim Schreiben). requiredPermissions = Rechte des Empfaengers plus Rechte
// des Inhalts; dedupeKey = "<praefix>:<userId>".
model Notification {
id String @id @default(cuid())
userId String?
user User? @relation(fields: [userId], references: [id])
priority NotificationPriority
message String
relatedType String?
relatedId String?
read Boolean @default(false)
createdAt DateTime @default(now())
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
priority NotificationPriority
category NotificationCategory
message String
relatedType String?
relatedId String?
link String?
requiredPermissions String[] @default([])
dedupeKey String? @unique
read Boolean @default(false)
createdAt DateTime @default(now())
@@index([userId, read])
@@index([priority])
}
// Einstellungen je Nutzer; Standard beim ersten Lesen angelegt.
model NotificationPreference {
userId String @id
user User @relation(fields: [userId], references: [id])
taskDueEnabled Boolean @default(true)
digestEnabled Boolean @default(true)
digestTime String @default("06:30")
quietFrom String? @default("21:00")
quietUntil String? @default("06:30")
telegramEnabled Boolean @default(false)
telegramCategories NotificationCategory[] @default([TASK_DUE, DIGEST])
telegramDetail Boolean @default(true)
updatedAt DateTime @updatedAt
}
// ---------------------------------------------------------------------------
// Webhooks (Abschnitt 27) – v1.x Modul 28
// ---------------------------------------------------------------------------
@@ -1574,6 +1607,7 @@ model CalendarSyncSettings {
syncFeeding Boolean @default(true)
syncHarvest Boolean @default(true)
syncQueenAssignment Boolean @default(true)
syncWithdrawal Boolean @default(true)
updatedAt DateTime @updatedAt
}