Files
MABEA/backend/app/services/mindermenge.py
T
patrickandClaude Sonnet 5 1e9012cba3
CI / backend-tests (push) Successful in 47s
Sprint 5: Mindermengen-Genehmigung + automatischer Ablauf, Historie für Kernkette
- MindermengenGenehmigung-Modell/Service (Prompt 04): reine fachliche Bewertung,
  ändert NIEMALS Ist-Menge/Fehlmenge/Fehlbestand-Status (Leitplanke, U2)
- Automatischer Ablauf bei Start der nächsten Kontrolle desselben Objekts (Karte 08,
  U6) - kein Zeit-Trigger, der Kontrollstart selbst ist der Ablauf-Zeitpunkt
- Genehmigung endet gemeinsam mit dem Fehlbestand, wenn dieser durch Nachfüllung
  erledigt wird (U7), unabhängig davon ob vorher eine neue Kontrolle stattfand
- Historie-Service (app/services/historie.py) + Verdrahtung in die komplette
  Kernkette: kontrolle_gestartet/-abgeschlossen/-abgebrochen/-uebernommen,
  istmenge_erfasst, fehlbestand_entstanden/-erledigt, nachfuellung_erfasst,
  mindermenge_genehmigt/-abgelaufen/-beendet_durch_erledigung (U14)
  Hinweis: Stammdaten-/Vorlagen-/Benutzerverwaltung noch nicht retrofittet -
  Sprint 5 deckt bewusst die im Testkonzept referenzierte Kernkette ab, keine
  flächendeckende Audit-Abdeckung aller CRUD-Endpunkte.
- Tests: U2, U6, U7, vollständige Historie-Kette nach Prompt-13.3-Beispiel (U14)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
2026-09-03 23:53:14 +02:00

68 lines
2.5 KiB
Python

from datetime import datetime, timezone
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.fehlbestand import Fehlbestand, FehlbestandStatus
from app.models.mindermenge import MindermengeStatus, MindermengenGenehmigung
from app.services import historie as historie_service
class FehlbestandBereitsErledigtError(Exception):
"""Ein bereits erledigter Fehlbestand braucht keine Mindermengen-Bewertung mehr."""
class BereitsGenehmigtError(Exception):
"""Für diesen Fehlbestand existiert bereits eine aktive Genehmigung."""
async def genehmigen(
db: AsyncSession,
*,
fehlbestand: Fehlbestand,
begruendung: str,
genehmigt_von: int,
zustaendiger_server_id: int,
) -> MindermengenGenehmigung:
"""Prompt 04: reine fachliche Bewertung - ändert NIEMALS Ist-Menge, Fehlmenge
oder den Fehlbestand-Status selbst (Leitplanke, U2). Verknüpft mit der Kontrolle,
die den Fehlbestand ausgelöst hat ("aktuelle Kontrolle des Objekts", Prompt 04.3.4)."""
if fehlbestand.status == FehlbestandStatus.erledigt:
raise FehlbestandBereitsErledigtError()
if fehlbestand.kontrolle_id is None:
# Sollte praktisch nicht vorkommen (jeder Fehlbestand entsteht aus einer
# Kontrolle, Sprint 3), aber das Schema erlaubt kontrolle_id=NULL.
raise ValueError("Fehlbestand ohne auslösende Kontrolle kann nicht genehmigt werden")
result = await db.execute(
select(MindermengenGenehmigung).where(
MindermengenGenehmigung.fehlbestand_id == fehlbestand.id,
MindermengenGenehmigung.status == MindermengeStatus.aktiv,
)
)
if result.scalar_one_or_none() is not None:
raise BereitsGenehmigtError()
genehmigung = MindermengenGenehmigung(
fehlbestand_id=fehlbestand.id,
genehmigt_von=genehmigt_von,
begruendung=begruendung,
genehmigt_am=datetime.now(timezone.utc),
ausloesende_kontrolle_id=fehlbestand.kontrolle_id,
status=MindermengeStatus.aktiv,
)
db.add(genehmigung)
await db.flush()
await historie_service.log(
db,
zustaendiger_server_id=zustaendiger_server_id,
benutzer_id=genehmigt_von,
ereignistyp="mindermenge_genehmigt",
entitaet_typ="mindermengen_genehmigung",
entitaet_id=genehmigung.id,
neuer_wert={"fehlbestand_id": str(fehlbestand.id), "status": "aktiv"},
begruendung=begruendung,
)
return genehmigung