From 85ca441216979d8fa6ae8627b766cdc3622528f0 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 7 Sep 2026 00:06:55 +0200 Subject: [PATCH] =?UTF-8?q?fix(vorlagen):=20fehlendes=20Fach=20wird=20beim?= =?UTF-8?q?=20Anlegen=20einer=20Vorlage=20automatisch=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vorlagenposition.fach ist Freitext ohne FK zur festen Fächer-Liste (Fach-Tabelle) - beide liefen auseinander: ein Fach tauchte in der Kontrolle auf ("Güdeltuben"), existierte aber nicht in der Fächer-Verwaltung des Objekttyps. erstelle_vorlage/neue_version/ aktualisiere_positionen legen fehlende Fächer jetzt automatisch an. Bestehende Lücken (22 Fächer über mehrere Objekttypen) direkt in Produktion nachgetragen. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV --- backend/app/services/vorlagen.py | 21 +++++++++++++++++++++ backend/tests/test_vorlagen.py | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/backend/app/services/vorlagen.py b/backend/app/services/vorlagen.py index 2c4f5af..f83eb24 100644 --- a/backend/app/services/vorlagen.py +++ b/backend/app/services/vorlagen.py @@ -1,13 +1,32 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession +from app.models.stammdaten import Fach from app.models.vorlage import Beladungsvorlage, VorlageStatus, Vorlagenposition from app.schemas.vorlage import VorlagenpositionCreate +async def _stelle_faecher_sicher( + db: AsyncSession, *, objekttyp_id: int, positionen: list[VorlagenpositionCreate] +) -> None: + """Vorlagenposition.fach ist Freitext ohne FK zur festen Fächer-Liste (Fach- + Tabelle) - ohne diesen Abgleich laufen beide auseinander: ein Fach taucht in + der Kontrolle auf, existiert aber nicht in der Fächer-Verwaltung des + Objekttyps (Nutzer-Fund: "Güdeltuben" bei Kontrolle, aber kein Fach dazu).""" + benoetigt = {pos.fach for pos in positionen if pos.fach} + if not benoetigt: + return + result = await db.execute(select(Fach.name).where(Fach.objekttyp_id == objekttyp_id)) + vorhanden = set(result.scalars().all()) + for name in benoetigt - vorhanden: + db.add(Fach(objekttyp_id=objekttyp_id, name=name)) + await db.flush() + + async def erstelle_vorlage( db: AsyncSession, *, objekttyp_id: int, name: str, positionen: list[VorlagenpositionCreate] ) -> Beladungsvorlage: + await _stelle_faecher_sicher(db, objekttyp_id=objekttyp_id, positionen=positionen) vorlage = Beladungsvorlage( objekttyp_id=objekttyp_id, name=name, version=1, status=VorlageStatus.aktiv ) @@ -24,6 +43,7 @@ async def neue_version( ) -> Beladungsvorlage: """Prompt 08 Punkt 4: Änderung erzeugt neue Version, alte bleibt unverändert/lesbar (Historie-Integrität) statt überschrieben zu werden.""" + await _stelle_faecher_sicher(db, objekttyp_id=aktuelle_vorlage.objekttyp_id, positionen=positionen) aktuelle_vorlage.status = VorlageStatus.veraltet await db.flush() @@ -61,6 +81,7 @@ async def aktualisiere_positionen( sich NICHT auf bereits angelegte Objekte aus, da deren Sollmenge fest in `objektposition.sollmenge_vorlage` kopiert ist (siehe services/objekte.py), nicht mehr live aus der Vorlage gelesen wird.""" + await _stelle_faecher_sicher(db, objekttyp_id=vorlage.objekttyp_id, positionen=positionen) await loesche_alle_positionen(db, vorlage.id) for pos in positionen: db.add(Vorlagenposition(vorlage_id=vorlage.id, **pos.model_dump())) diff --git a/backend/tests/test_vorlagen.py b/backend/tests/test_vorlagen.py index f333d92..6944519 100644 --- a/backend/tests/test_vorlagen.py +++ b/backend/tests/test_vorlagen.py @@ -1,9 +1,31 @@ import pytest +from sqlalchemy import select +from app.models.stammdaten import Fach from app.services.vorlagen import aktualisiere_positionen, erstelle_vorlage, hole_positionen, neue_version from app.schemas.vorlage import VorlagenpositionCreate +@pytest.mark.asyncio +async def test_erstelle_vorlage_legt_fehlendes_fach_automatisch_an(db_session, objekttyp_factory, material_factory): + """Nutzer-Fund: Vorlagenposition.fach ist Freitext ohne FK zur Fächer-Liste - + ein Fach tauchte in der Kontrolle auf ("Güdeltuben"), existierte aber nicht + in der Fächer-Verwaltung des Objekttyps.""" + objekttyp = await objekttyp_factory("GA San") + material = await material_factory("Güdeltubus Gr 3") + + await erstelle_vorlage( + db_session, + objekttyp_id=objekttyp.id, + name="GA San Standard", + positionen=[VorlagenpositionCreate(material_id=material.id, sollmenge=2, fach="Güdeltuben")], + ) + + result = await db_session.execute(select(Fach).where(Fach.objekttyp_id == objekttyp.id)) + faecher = [f.name for f in result.scalars().all()] + assert "Güdeltuben" in faecher + + @pytest.mark.asyncio async def test_neue_version_alte_bleibt_lesbar(db_session, objekttyp_factory, material_factory): """U10: Vorlagenänderung erzeugt neue Version, alte Version bleibt unverändert lesbar."""