diff --git a/backend/app/api/v1/endpoints/vorlagen.py b/backend/app/api/v1/endpoints/vorlagen.py index e16b460..653f938 100644 --- a/backend/app/api/v1/endpoints/vorlagen.py +++ b/backend/app/api/v1/endpoints/vorlagen.py @@ -5,7 +5,8 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_current_user, require_roles from app.db.session import get_db from app.models.auth import RolleTyp -from app.models.vorlage import Beladungsvorlage +from app.models.objekt import Objekt +from app.models.vorlage import Beladungsvorlage, Vorlagenposition from app.schemas.vorlage import BeladungsvorlageCreate, BeladungsvorlageRead, VorlagenAenderung from app.services.vorlagen import erstelle_vorlage, hole_positionen, neue_version @@ -76,3 +77,29 @@ async def erzeuge_neue_version( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Vorlage nicht gefunden") neue = await neue_version(db, aktuelle_vorlage=aktuelle_vorlage, positionen=payload.positionen) return await _mit_positionen(db, neue) + + +@router.delete("/vorlagen/{vorlage_id}", status_code=status.HTTP_204_NO_CONTENT) +async def loesche_vorlage( + vorlage_id: int, db: AsyncSession = Depends(get_db), _=Depends(_ersteller) +) -> None: + """Vorlagen sind bewusst versioniert (Prompt 08) - normalerweise wird eine + Version nie gelöscht, nur durch eine neue ersetzt (status=veraltet). Echtes + Löschen ist nur erlaubt, wenn KEIN Objekt diese Version referenziert (sonst + FK-Verletzung bzw. Datenverlust an echten Objekten) - z.B. versehentlich + angelegte/Test-Versionen entfernen.""" + vorlage = await db.get(Beladungsvorlage, vorlage_id) + if vorlage is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Vorlage nicht gefunden") + + result = await db.execute(select(Objekt.id).where(Objekt.vorlage_id == vorlage_id).limit(1)) + if result.scalar_one_or_none() is not None: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Vorlage wird von mindestens einem Objekt verwendet und kann nicht gelöscht werden", + ) + + positionen = await db.execute(select(Vorlagenposition).where(Vorlagenposition.vorlage_id == vorlage_id)) + for position in positionen.scalars().all(): + await db.delete(position) + await db.delete(vorlage) diff --git a/backend/tests/test_vorlagen.py b/backend/tests/test_vorlagen.py index 616e79e..8ac5491 100644 --- a/backend/tests/test_vorlagen.py +++ b/backend/tests/test_vorlagen.py @@ -32,3 +32,29 @@ async def test_neue_version_alte_bleibt_lesbar(db_session, objekttyp_factory, ma v2_positionen = await hole_positionen(db_session, v2.id) assert v1_positionen[0].sollmenge == 10 assert v2_positionen[0].sollmenge == 15 + + +@pytest.mark.asyncio +async def test_loesche_unbenutzte_vorlage(client, db_session, objekttyp_factory, admin_user): + from tests.conftest import auth_header, login + + objekttyp = await objekttyp_factory("Rucksack Loeschtest") + vorlage = await erstelle_vorlage(db_session, objekttyp_id=objekttyp.id, name="Loesch-Vorlage", positionen=[]) + token = await login(client, "admin1") + + response = await client.delete(f"/api/v1/vorlagen/{vorlage.id}", headers=auth_header(token)) + assert response.status_code == 204 + + nachher = await client.get(f"/api/v1/vorlagen/{vorlage.id}", headers=auth_header(token)) + assert nachher.status_code == 404 + + +@pytest.mark.asyncio +async def test_loeschen_von_benutzter_vorlage_ist_409(client, db_session, objekt_mit_position, admin_user): + from tests.conftest import auth_header, login + + objekt, _material = objekt_mit_position + token = await login(client, "admin1") + + response = await client.delete(f"/api/v1/vorlagen/{objekt.vorlage_id}", headers=auth_header(token)) + assert response.status_code == 409