import uuid from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select 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 KnotenTyp, RolleTyp, Systemknoten from app.models.fehlbestand import Fehlbestand from app.models.mindermenge import MindermengeStatus, MindermengenGenehmigung from app.schemas.fehlbestand import ( FehlbestandRead, NachfuellungCreate, NachfuellungRead, NachfuellungResponse, ) from app.schemas.mindermenge import MindermengeGenehmigenRequest, MindermengeRead from app.services.fehlbestand import FehlbestandBereitsErledigtError, nachfuellen from app.services.mindermenge import BereitsGenehmigtError from app.services.mindermenge import FehlbestandBereitsErledigtError as MindermengeAufErledigtemError from app.services.mindermenge import genehmigen router = APIRouter() _mitarbeiter_plus = require_roles( RolleTyp.mitarbeiter, RolleTyp.materialverantwortlicher, RolleTyp.leitungsverantwortlicher, RolleTyp.administration, ) _verantwortliche = require_roles( RolleTyp.administration, RolleTyp.materialverantwortlicher, RolleTyp.leitungsverantwortlicher ) async def _hauptserver_id(db: AsyncSession) -> int: result = await db.execute(select(Systemknoten.id).where(Systemknoten.typ == KnotenTyp.haupt)) return result.scalar_one() @router.get("/fehlbestaende", response_model=list[FehlbestandRead]) async def liste_fehlbestaende( status_filter: str | None = None, standort_id: int | None = None, objekt_id: int | None = None, material_id: int | None = None, nur_genehmigte: bool = False, db: AsyncSession = Depends(get_db), _=Depends(_verantwortliche), ) -> list[Fehlbestand]: """Prompt 12.3 (Minimalversion): Filter Standort/Objekt/Material/Status, Standardsortierung nach Alter absteigend (älteste zuerst, entstanden_am aufsteigend). Vollständige Zuständigkeitsfilterung (Karte 04) ist spätere Ausbaustufe.""" stmt = select(Fehlbestand) if status_filter is not None: stmt = stmt.where(Fehlbestand.status == status_filter) if standort_id is not None: stmt = stmt.where(Fehlbestand.standort_id == standort_id) if objekt_id is not None: stmt = stmt.where(Fehlbestand.objekt_id == objekt_id) if material_id is not None: stmt = stmt.where(Fehlbestand.material_id == material_id) if nur_genehmigte: stmt = stmt.join( MindermengenGenehmigung, MindermengenGenehmigung.fehlbestand_id == Fehlbestand.id ).where(MindermengenGenehmigung.status == MindermengeStatus.aktiv) stmt = stmt.order_by(Fehlbestand.entstanden_am.asc()) result = await db.execute(stmt) return list(result.scalars().all()) @router.get("/fehlbestaende/{fehlbestand_id}", response_model=FehlbestandRead) async def hole_fehlbestand( fehlbestand_id: uuid.UUID, db: AsyncSession = Depends(get_db), _=Depends(_mitarbeiter_plus) ) -> Fehlbestand: fehlbestand = await db.get(Fehlbestand, fehlbestand_id) if fehlbestand is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fehlbestand nicht gefunden") return fehlbestand @router.post( "/fehlbestaende/{fehlbestand_id}/nachfuellungen", response_model=NachfuellungResponse, status_code=status.HTTP_201_CREATED, ) async def erfasse_nachfuellung( fehlbestand_id: uuid.UUID, payload: NachfuellungCreate, db: AsyncSession = Depends(get_db), current_user=Depends(_mitarbeiter_plus), ) -> NachfuellungResponse: """Prompt 02.3/02.5: deckt Sofort-Nachfüllung während der Kontrolle (Karte 07) und spätere/externe Nachfüllung gleichermaßen ab.""" fehlbestand = await db.get(Fehlbestand, fehlbestand_id) if fehlbestand is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fehlbestand nicht gefunden") zustaendiger_server_id = await _hauptserver_id(db) try: nachfuellung, ueberbestand = await nachfuellen( db, fehlbestand=fehlbestand, menge=payload.menge, benutzer_id=current_user.id, zustaendiger_server_id=zustaendiger_server_id, ) except FehlbestandBereitsErledigtError as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Fehlbestand ist bereits erledigt" ) from exc return NachfuellungResponse( nachfuellung=NachfuellungRead.model_validate(nachfuellung), fehlbestand=FehlbestandRead.model_validate(fehlbestand), ueberbestand=ueberbestand, ) @router.post( "/fehlbestaende/{fehlbestand_id}/mindermenge", response_model=MindermengeRead, status_code=status.HTTP_201_CREATED, ) async def genehmige_mindermenge( fehlbestand_id: uuid.UUID, payload: MindermengeGenehmigenRequest, db: AsyncSession = Depends(get_db), current_user=Depends(_verantwortliche), ) -> MindermengeRead: """Prompt 04: NIEMALS durch Mitarbeiter (Rollen-Dependency erzwingt das bereits), ändert NIEMALS Ist-Menge/Fehlmenge/Fehlbestand-Status (U2).""" fehlbestand = await db.get(Fehlbestand, fehlbestand_id) if fehlbestand is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Fehlbestand nicht gefunden") zustaendiger_server_id = await _hauptserver_id(db) try: genehmigung = await genehmigen( db, fehlbestand=fehlbestand, begruendung=payload.begruendung, genehmigt_von=current_user.id, zustaendiger_server_id=zustaendiger_server_id, ) except MindermengeAufErledigtemError as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Fehlbestand ist bereits erledigt" ) from exc except BereitsGenehmigtError as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Fehlbestand hat bereits eine aktive Genehmigung" ) from exc return MindermengeRead.model_validate(genehmigung)