Files
MABEA/backend/app/services/akte.py
T
patrickandClaude Sonnet 5 209b541220
CI / backend-tests (push) Successful in 2m2s
CI / frontend-build (push) Successful in 19s
feat(ui): UI-005 begründete Einsatzbereitschaft + Dokumente in der Akte
Backend: objekt_readiness() in dashboard.py liefert Status+Gründe für
ein einzelnes Objekt, Fallunterscheidung aus einsatzbereitschaft()
extrahiert (_kategorie_und_gruende, keine Duplizierung). Neue Felder
an /akte/objekt/{id}. Frontend: ReadinessBadge + Gründeliste ersetzt
den rohen objekt.status-Badge (Objekt-Lebenszyklus-Status bleibt
separat sichtbar). Dokumente waren in der Akte bisher nur Text ohne
Interaktion - jetzt DokumentePanel eingebunden plus neuer "Ansehen"-
Klick (PDF/Bild im neuen Tab statt Zwangs-Download).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
2026-09-06 21:35:52 +02:00

119 lines
4.9 KiB
Python

from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.auth import Benutzer
from app.models.dokument import Dokument
from app.models.fahrzeugdetails import Fahrzeugdetails
from app.models.geraet_instanz import GeraetInstanz
from app.models.historie import Historie
from app.models.mangel import Mangel
from app.models.objekt import Objekt
from app.models.objektposition import Objektposition
from app.models.stammdaten import Objekttyp, Standort
from app.models.zustaendigkeit import Kontrollverantwortung, Zustaendigkeit
from app.schemas.akte import AkteRead, ZustaendigerBenutzer
from app.schemas.dokument import DokumentRead
from app.schemas.geraet_instanz import GeraetInstanzRead
from app.schemas.historie import HistorieRead
from app.schemas.mangel import MangelRead
from app.schemas.objekt import FahrzeugdetailsRead, ObjektRead
from app.schemas.zustaendigkeit import KontrollverantwortungRead
from app.services.dashboard import objekt_readiness
from app.services.objekte import letzte_kontrollen_map
async def _zustaendige_benutzer(db: AsyncSession, objekt: Objekt) -> list[ZustaendigerBenutzer]:
"""Karte 04: Umkehrung von zustaendige_objekte() - wer ist für DIESES Objekt
zuständig (Vereinigung Standort-Vererbung + objektspezifische Zeile)."""
stmt = (
select(Benutzer.id, Benutzer.name, Zustaendigkeit.standort_id, Zustaendigkeit.objekt_id)
.join(
Zustaendigkeit,
or_(
Zustaendigkeit.standort_id == objekt.standort_id,
Zustaendigkeit.objekt_id == objekt.id,
),
)
.where(Benutzer.id == Zustaendigkeit.benutzer_id)
)
result = await db.execute(stmt)
gesehen: set[int] = set()
ergebnis: list[ZustaendigerBenutzer] = []
for benutzer_id, name, standort_id, objekt_id in result.all():
if benutzer_id in gesehen:
continue
gesehen.add(benutzer_id)
quelle = "objekt" if objekt_id == objekt.id else "standort"
ergebnis.append(ZustaendigerBenutzer(id=benutzer_id, name=name, quelle=quelle))
return ergebnis
async def akte_fuer_objekt(db: AsyncSession, objekt_id: int) -> AkteRead | None:
"""FILE-001: bündelt Stammdaten/Standort/Verantwortlichkeit/Historie/
Dokumente/Prüfungen/Mängel für ein Objekt zu einer Aggregations-Sicht."""
objekt = await db.get(Objekt, objekt_id)
if objekt is None:
return None
objekttyp = await db.get(Objekttyp, objekt.objekttyp_id)
standort = await db.get(Standort, objekt.standort_id)
fahrzeugdetails = await db.get(Fahrzeugdetails, objekt_id)
letzte_map = await letzte_kontrollen_map(db, [objekt_id])
zustaendige = await _zustaendige_benutzer(db, objekt)
kontrollverantwortung_result = await db.execute(
select(Kontrollverantwortung).where(Kontrollverantwortung.objekt_id == objekt_id)
)
historie_result = await db.execute(
select(Historie, Benutzer.name)
.outerjoin(Benutzer, Benutzer.id == Historie.benutzer_id)
.where(Historie.entitaet_typ == "objekt", Historie.entitaet_id == str(objekt_id))
.order_by(Historie.zeitpunkt.desc())
.limit(50)
)
dokumente_result = await db.execute(
select(Dokument).where(Dokument.entitaet_typ == "objekt", Dokument.entitaet_id == str(objekt_id))
)
geraete_result = await db.execute(
select(GeraetInstanz).where(
GeraetInstanz.objektposition_id.in_(
select(Objektposition.id).where(Objektposition.objekt_id == objekt_id)
)
)
)
geraete = list(geraete_result.scalars().all())
maengel_result = await db.execute(select(Mangel).where(Mangel.objekt_id == objekt_id))
readiness = await objekt_readiness(db, objekt_id)
objekt_read = ObjektRead.model_validate(objekt).model_copy(
update={"letzte_kontrolle_am": letzte_map.get(objekt_id)}
)
return AkteRead(
objekt=objekt_read,
einsatzbereitschaft_status=readiness["status"],
einsatzbereitschaft_gruende=readiness["gruende"],
objekttyp_name=objekttyp.name if objekttyp else "",
standort_name=standort.name if standort else "",
fahrzeugdetails=FahrzeugdetailsRead.model_validate(fahrzeugdetails) if fahrzeugdetails else None,
zustaendige_benutzer=zustaendige,
kontrollverantwortung=[
KontrollverantwortungRead.model_validate(k) for k in kontrollverantwortung_result.scalars().all()
],
letzte_kontrolle_am=letzte_map.get(objekt_id),
historie=[
HistorieRead.model_validate(eintrag).model_copy(update={"benutzer_name": benutzer_name})
for eintrag, benutzer_name in historie_result.all()
],
dokumente=[DokumentRead.model_validate(d) for d in dokumente_result.scalars().all()],
geraete=[GeraetInstanzRead.model_validate(g) for g in geraete],
maengel=[MangelRead.model_validate(m) for m in maengel_result.scalars().all()],
)