feat: Wiederkehrende Aufgaben-Vorlagen (recurring task templates)

Neue TaskTemplate-Entität mit YEARLY/MONTHLY/INTERVAL_DAYS-Wiederholung.
Fällige Aufgaben werden lazy bei GET /tasks erzeugt (Compare-and-Swap auf
nextDueDate), damit mehrere API-Replicas im Kubernetes-Cluster dieselbe
Vorlage nicht doppelt feuern - ohne zusätzliche Cron-Abhängigkeit.

Frontend: Verwaltungsseite unter /settings/task-templates zum Anlegen,
Auflisten und Deaktivieren von Vorlagen.
This commit is contained in:
Nick Szaunig committed 2026-08-25 05:36:55 +02:00
1 parent e458a7cf07
commit 4609a0f9ec
20 files changed
+1341

No files matched your search

@@ -0,0 +1,37 @@
-- CreateEnum
CREATE TYPE "TaskTemplateRecurrenceType" AS ENUM ('YEARLY', 'MONTHLY', 'INTERVAL_DAYS');
-- AlterTable
ALTER TABLE "Task" ADD COLUMN "sourceTemplateId" TEXT;
-- CreateTable
CREATE TABLE "TaskTemplate" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"priority" "TaskPriority" NOT NULL DEFAULT 'NORMAL',
"assignedUserId" TEXT,
"createdByUserId" TEXT,
"recurrenceType" "TaskTemplateRecurrenceType" NOT NULL,
"recurrenceMonth" INTEGER,
"recurrenceDayOfMonth" INTEGER,
"intervalDays" INTEGER,
"nextDueDate" TIMESTAMP(3) NOT NULL,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TaskTemplate_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "TaskTemplate_active_nextDueDate_idx" ON "TaskTemplate"("active", "nextDueDate");
-- AddForeignKey
ALTER TABLE "Task" ADD CONSTRAINT "Task_sourceTemplateId_fkey" FOREIGN KEY ("sourceTemplateId") REFERENCES "TaskTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TaskTemplate" ADD CONSTRAINT "TaskTemplate_assignedUserId_fkey" FOREIGN KEY ("assignedUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TaskTemplate" ADD CONSTRAINT "TaskTemplate_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+35
View File
@@ -47,6 +47,8 @@ model User {
stockMovements StockMovement[]
assignedTasks Task[] @relation("TaskAssignee")
createdTasks Task[] @relation("TaskCreator")
assignedTaskTemplates TaskTemplate[] @relation("TaskTemplateAssignee")
createdTaskTemplates TaskTemplate[] @relation("TaskTemplateCreator")
colonyMoves ColonyApiaryHistory[]
auditLogs AuditLog[]
bottlingBatches BottlingBatch[]
@@ -386,6 +388,8 @@ model Task {
relatedId String?
sourceInspectionId String?
sourceInspection Inspection? @relation(fields: [sourceInspectionId], references: [id])
sourceTemplateId String?
sourceTemplate TaskTemplate? @relation(fields: [sourceTemplateId], references: [id])
createdAt DateTime @default(now())
completedAt DateTime?
@@ -393,6 +397,37 @@ model Task {
@@index([relatedType, relatedId])
}
enum TaskTemplateRecurrenceType {
YEARLY
MONTHLY
INTERVAL_DAYS
}
// Wiederkehrende Aufgaben-Vorlagen – erzeugen automatisch eine neue Task,
// sobald nextDueDate erreicht ist (siehe TasksService.generateDueTasks).
model TaskTemplate {
id String @id @default(cuid())
title String
description String?
priority TaskPriority @default(NORMAL)
assignedUserId String?
assignedUser User? @relation("TaskTemplateAssignee", fields: [assignedUserId], references: [id])
createdByUserId String?
createdByUser User? @relation("TaskTemplateCreator", fields: [createdByUserId], references: [id])
recurrenceType TaskTemplateRecurrenceType
recurrenceMonth Int?
recurrenceDayOfMonth Int?
intervalDays Int?
nextDueDate DateTime
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
generatedTasks Task[]
@@index([active, nextDueDate])
}
// ---------------------------------------------------------------------------
// Varroamanagement (Abschnitt 4.10)
// ---------------------------------------------------------------------------