Karte 12: Eskalationslogik für lange offene Fehlbestände
CI / backend-tests (push) Successful in 1m6s
CI / frontend-build (push) Successful in 21s

Zwei Stufen (Erinnerung an Materialverantwortliche, Eskalation an
Leitungsverantwortliche/Administration) mit Tracking-Zeitstempel je Stufe
gegen Mehrfachversand. Zeitschwellen in eskalation_konfiguration (DB,
Singleton), admin-editierbar über GET/PUT /eskalation/konfiguration - bewusst
nicht fix im Code. Prüflauf per POST /eskalation/pruefen angestoßen,
Scheduling (Cron/systemd-Timer) bleibt Betriebsaufgabe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-04 18:04:52 +02:00
co-authored by Claude Sonnet 5
parent 7f06d1cecc
commit fb676a4a0b
12 changed files with 340 additions and 0 deletions
+2
View File
@@ -4,6 +4,7 @@ from app.api.v1.endpoints import (
auth,
benutzer,
dashboard,
eskalation,
fehlbestaende,
health,
kontrollen,
@@ -23,4 +24,5 @@ api_router.include_router(vorlagen.router, tags=["vorlagen"])
api_router.include_router(objekte.router, tags=["objekte"])
api_router.include_router(kontrollen.router, tags=["kontrollen"])
api_router.include_router(fehlbestaende.router, tags=["fehlbestaende"])
api_router.include_router(eskalation.router, tags=["eskalation"])
api_router.include_router(dashboard.router, tags=["dashboard"])
@@ -0,0 +1,47 @@
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import require_roles
from app.db.session import get_db
from app.models.auth import RolleTyp
from app.models.eskalation import EskalationKonfiguration
from app.schemas.eskalation import (
EskalationKonfigurationRead,
EskalationKonfigurationUpdate,
EskalationPruefungResult,
)
from app.services.eskalation import hole_konfiguration, pruefe_offene_fehlbestaende
router = APIRouter()
_admin_only = require_roles(RolleTyp.administration)
@router.get("/eskalation/konfiguration", response_model=EskalationKonfigurationRead)
async def lese_konfiguration(
db: AsyncSession = Depends(get_db), _=Depends(_admin_only)
) -> EskalationKonfiguration:
return await hole_konfiguration(db)
@router.put("/eskalation/konfiguration", response_model=EskalationKonfigurationRead)
async def aendere_konfiguration(
payload: EskalationKonfigurationUpdate,
db: AsyncSession = Depends(get_db),
_=Depends(_admin_only),
) -> EskalationKonfiguration:
"""Karte 12: Zeitschwellen zur Laufzeit änderbar (Nutzer-Vorgabe: nicht fix
im Code)."""
konfiguration = await hole_konfiguration(db)
konfiguration.erinnerung_tage = payload.erinnerung_tage
konfiguration.leitung_tage = payload.leitung_tage
await db.flush()
return konfiguration
@router.post("/eskalation/pruefen", response_model=EskalationPruefungResult)
async def pruefen(db: AsyncSession = Depends(get_db), _=Depends(_admin_only)) -> dict[str, int]:
"""Trigger für den Eskalations-Prüflauf (Karte 12). Scheduling selbst ist
Betriebsaufgabe (z.B. System-Cron ruft diesen Endpunkt periodisch auf,
Prompt 19 Deployment-Regel: kein eigener Scheduler-Prozess im Code)."""
return await pruefe_offene_fehlbestaende(db)