import uuid from datetime import datetime, timezone from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import require_roles_or_permission from app.db.session import get_db from app.models.auth import KnotenTyp, RolleTyp, Systemknoten from app.models.mangel import Mangel, MangelStatus from app.models.objekt import Objekt from app.schemas.mangel import MangelCreate, MangelRead, MangelStatusUpdate from app.services import historie as historie_service from app.services.mangel import aendere_status router = APIRouter() _ALLE_ROLLEN = ( RolleTyp.mitarbeiter, RolleTyp.materialverantwortlicher, RolleTyp.leitungsverantwortlicher, RolleTyp.administration, ) # Roadmap Phase 6: zusätzlich über eine granulare custom-Rolle (z.B. "Helfer") # erreichbar, auch wenn diese keine der vier festen RolleTyp-Rollen trägt. _darf_lesen = require_roles_or_permission(*_ALLE_ROLLEN, berechtigung="mangel.lesen") _darf_melden = require_roles_or_permission(*_ALLE_ROLLEN, berechtigung="mangel.melden") _materialverantwortliche = require_roles_or_permission( RolleTyp.administration, RolleTyp.materialverantwortlicher, RolleTyp.leitungsverantwortlicher, berechtigung="mangel.bearbeiten", ) 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("/maengel", response_model=list[MangelRead]) async def liste_maengel( objekt_id: int | None = None, status_filter: MangelStatus | None = None, db: AsyncSession = Depends(get_db), _=Depends(_darf_lesen), ) -> list[Mangel]: """Prompt-Erweiterung (Mangel-Modul): jeder Mitarbeiter+ darf lesen (Mängel sind für alle im Einsatzalltag relevant), Status ändern bleibt Materialverantwortlichen+ vorbehalten (Melden vs. Bearbeiten getrennt).""" stmt = select(Mangel) if objekt_id is not None: stmt = stmt.where(Mangel.objekt_id == objekt_id) if status_filter is not None: stmt = stmt.where(Mangel.status == status_filter) stmt = stmt.order_by(Mangel.gemeldet_am.desc()) result = await db.execute(stmt) return list(result.scalars().all()) @router.post("/maengel", response_model=MangelRead, status_code=status.HTTP_201_CREATED) async def melde_mangel( payload: MangelCreate, db: AsyncSession = Depends(get_db), current_user=Depends(_darf_melden) ) -> Mangel: objekt = await db.get(Objekt, payload.objekt_id) if objekt is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Objekt nicht gefunden") mangel = Mangel( objekt_id=payload.objekt_id, entitaet_typ=payload.entitaet_typ, entitaet_id=payload.entitaet_id, beschreibung=payload.beschreibung, prioritaet=payload.prioritaet, gemeldet_von=current_user.id, gemeldet_am=datetime.now(timezone.utc), ) db.add(mangel) await db.flush() await historie_service.log( db, zustaendiger_server_id=await _hauptserver_id(db), benutzer_id=current_user.id, ereignistyp="mangel_gemeldet", entitaet_typ="mangel", entitaet_id=mangel.id, neuer_wert={"beschreibung": mangel.beschreibung, "prioritaet": mangel.prioritaet.value}, ) return mangel @router.patch("/maengel/{mangel_id}", response_model=MangelRead) async def aendere_mangel_status( mangel_id: uuid.UUID, payload: MangelStatusUpdate, db: AsyncSession = Depends(get_db), current_user=Depends(_materialverantwortliche), ) -> Mangel: mangel = await db.get(Mangel, mangel_id) if mangel is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Mangel nicht gefunden") return await aendere_status( db, mangel=mangel, status=payload.status, benutzer_id=current_user.id, zustaendiger_server_id=await _hauptserver_id(db), )