Files
MABEA/backend/app/models/fehlbestand.py
T
patrickandClaude Sonnet 5 fb676a4a0b
CI / backend-tests (push) Successful in 1m6s
CI / frontend-build (push) Successful in 21s
Karte 12: Eskalationslogik für lange offene Fehlbestände
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
2026-09-04 18:04:52 +02:00

52 lines
2.6 KiB
Python

import enum
import uuid
from datetime import datetime
from decimal import Decimal
from sqlalchemy import ForeignKey, Numeric, String
from sqlalchemy.dialects.postgresql import ENUM as PgEnum, TIMESTAMP, UUID
from sqlalchemy.orm import Mapped, mapped_column
from app.db.base import Base
class FehlbestandStatus(str, enum.Enum):
offen = "offen"
in_bearbeitung = "in_bearbeitung"
nachgefuellt_teilweise = "nachgefuellt_teilweise"
erledigt = "erledigt"
fehlbestand_status_pg = PgEnum(FehlbestandStatus, name="fehlbestand_status", create_type=False)
class Fehlbestand(Base):
"""Prompt 03: automatisch entstandener Vorgang bei Ist < Soll, ändert selbst
NIEMALS die Ist-Menge (nur Nachfüllung tut das, Sprint 4). Sollmenge/Istmenge/
Fehlmenge hier sind Snapshot-Werte zum Feststellungszeitpunkt, nicht live
referenziert (Prompt 03 Punkt 2: "Sollmenge zum Zeitpunkt der Feststellung")."""
__tablename__ = "fehlbestand"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
erzeugt_von_server_id: Mapped[int] = mapped_column(ForeignKey("systemknoten.id"), nullable=False)
objekt_id: Mapped[int] = mapped_column(ForeignKey("objekt.id"), nullable=False)
material_id: Mapped[int] = mapped_column(ForeignKey("material.id"), nullable=False)
standort_id: Mapped[int] = mapped_column(ForeignKey("standort.id"), nullable=False)
sollmenge: Mapped[Decimal] = mapped_column(Numeric, nullable=False)
istmenge: Mapped[Decimal] = mapped_column(Numeric, nullable=False)
fehlmenge: Mapped[Decimal] = mapped_column(Numeric, nullable=False)
entstanden_am: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), nullable=False)
festgestellt_von: Mapped[int] = mapped_column(ForeignKey("benutzer.id"), nullable=False)
kontrolle_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("kontrolle.id"))
ursache: Mapped[str | None] = mapped_column(String)
verantwortlicher_id: Mapped[int | None] = mapped_column(ForeignKey("benutzer.id"))
status: Mapped[FehlbestandStatus] = mapped_column(
fehlbestand_status_pg, nullable=False, default=FehlbestandStatus.offen
)
erledigt_am: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True))
# Karte 12: Eskalationsstufen-Tracking, damit dieselbe Stufe nicht bei jedem
# Prüflauf erneut verschickt wird (NULL = Stufe noch nicht ausgelöst).
erinnerung_gesendet_am: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True))
eskalation_gesendet_am: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True))