From 300b30e0b12ae799bcc0820a3ec05d6ae9c70170 Mon Sep 17 00:00:00 2001 From: patrick Date: Sun, 6 Sep 2026 23:31:18 +0200 Subject: [PATCH] =?UTF-8?q?fix(akte):=20Beladung=20zeigt=20bei=20Ger=C3=A4?= =?UTF-8?q?ten=20mit=20SN=20falsche=20Ist-Menge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Objektposition.istmenge bleibt bei materialtyp='geraet_sn' auf 0 - die tatsächliche Ist-Menge ergibt sich aus der Anzahl einsatzbereiter geraet_instanz-Zeilen (Karte 14). Die neue Beladung-Karte in der Akte zeigte den rohen istmenge-Wert, also immer 0 statt z.B. 1 Defibrillator oder 2 Sauerstoffflaschen. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV --- backend/app/services/akte.py | 17 ++++++++-- backend/tests/test_akte.py | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/backend/app/services/akte.py b/backend/app/services/akte.py index e146bde..6bae501 100644 --- a/backend/app/services/akte.py +++ b/backend/app/services/akte.py @@ -4,12 +4,12 @@ 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.geraet_instanz import GeraetInstanz, GeraetStatus 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 Material, Objekttyp, Standort +from app.models.stammdaten import Material, MaterialTyp, Objekttyp, Standort from app.models.vorlage import Vorlagenposition from app.models.zustaendigkeit import Kontrollverantwortung, Zustaendigkeit from app.schemas.akte import AkteRead, BeladungsPosition, ZustaendigerBenutzer @@ -98,6 +98,13 @@ async def akte_fuer_objekt(db: AsyncSession, objekt_id: int) -> AkteRead | None: ) fach_by_material_id = {vp.material_id: vp.fach for vp in vorlagenpositionen_result.scalars().all()} + einsatzbereite_geraete_je_position: dict = {} + for geraet in geraete: + if geraet.status == GeraetStatus.einsatzbereit: + einsatzbereite_geraete_je_position[geraet.objektposition_id] = ( + einsatzbereite_geraete_je_position.get(geraet.objektposition_id, 0) + 1 + ) + beladung_result = await db.execute( select(Objektposition, Material) .join(Material, Objektposition.material_id == Material.id) @@ -111,7 +118,11 @@ async def akte_fuer_objekt(db: AsyncSession, objekt_id: int) -> AkteRead | None: einheit=material.einheit, fach=fach_by_material_id.get(material.id), sollmenge=sollmenge_effektiv(position), - istmenge=position.istmenge, + istmenge=( + einsatzbereite_geraete_je_position.get(position.id, 0) + if material.materialtyp == MaterialTyp.geraet_sn + else position.istmenge + ), ) for position, material in beladung_result.all() ] diff --git a/backend/tests/test_akte.py b/backend/tests/test_akte.py index 6d0444a..fdbdb4a 100644 --- a/backend/tests/test_akte.py +++ b/backend/tests/test_akte.py @@ -69,6 +69,69 @@ async def test_akte_readiness_unbekannt_bei_nie_kontrolliert(client, objekt_mit_ assert body["einsatzbereitschaft_status"] != "ready" +@pytest.mark.asyncio +async def test_beladung_zeigt_einsatzbereite_geraete_als_istmenge(client, db_session, admin_user, hauptserver_id): + """Ein Objektposition.istmenge bleibt bei materialtyp='geraet_sn' auf 0 + stehen (nie über Kontroll-Zähler erfasst) - die Akte muss stattdessen die + Anzahl einsatzbereiter geraet_instanz-Zeilen zeigen.""" + from sqlalchemy import select + + from app.models.geraet_instanz import GeraetInstanz, GeraetStatus + from app.models.objektposition import Objektposition + from app.models.stammdaten import Bereich, MaterialTyp, Objekttyp, Standort + from app.models.stammdaten import Material as MaterialModel + from app.services.objekte import objekt_aus_vorlage + from app.services.vorlagen import erstelle_vorlage + from app.schemas.vorlage import VorlagenpositionCreate + + bereich = Bereich(name="Bereich-Geraet") + db_session.add(bereich) + await db_session.flush() + objekttyp = Objekttyp(bereich_id=bereich.id, name="Typ-Geraet") + db_session.add(objekttyp) + await db_session.flush() + material = MaterialModel(name="Defibrillator", einheit="Stück", materialtyp=MaterialTyp.geraet_sn) + db_session.add(material) + await db_session.flush() + standort = Standort(name="Standort-Geraet") + db_session.add(standort) + await db_session.flush() + + vorlage = await erstelle_vorlage( + db_session, + objekttyp_id=objekttyp.id, + name="Vorlage-Geraet", + positionen=[VorlagenpositionCreate(material_id=material.id, sollmenge=1)], + ) + objekt = await objekt_aus_vorlage( + db_session, + code="GERAET-01", + name="Geraet-Objekt", + standort_id=standort.id, + vorlage=vorlage, + zustaendiger_server_id=hauptserver_id, + ) + + result = await db_session.execute( + select(Objektposition).where( + Objektposition.objekt_id == objekt.id, Objektposition.material_id == material.id + ) + ) + position = result.scalar_one() + db_session.add( + GeraetInstanz(objektposition_id=position.id, seriennummer="SN-1", status=GeraetStatus.einsatzbereit) + ) + db_session.add( + GeraetInstanz(objektposition_id=position.id, seriennummer="SN-2", status=GeraetStatus.defekt) + ) + await db_session.flush() + + token = await login(client, "admin1") + antwort = await client.get(f"/api/v1/akte/objekt/{objekt.id}", headers=auth_header(token)) + body = antwort.json() + assert body["beladung"][0]["istmenge"] == "1" + + @pytest.mark.asyncio async def test_akte_unbekanntes_objekt_ist_404(client, admin_user): token = await login(client, "admin1")