fix(vorlagen): fehlendes Fach wird beim Anlegen einer Vorlage automatisch ergänzt
CI / backend-tests (push) Failing after 2m2s
CI / frontend-build (push) Successful in 19s

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-07 00:06:55 +02:00
co-authored by Claude Sonnet 5
parent cc016f09d3
commit 85ca441216
2 changed files with 43 additions and 0 deletions
+21
View File
@@ -1,13 +1,32 @@
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.models.stammdaten import Fach
from app.models.vorlage import Beladungsvorlage, VorlageStatus, Vorlagenposition from app.models.vorlage import Beladungsvorlage, VorlageStatus, Vorlagenposition
from app.schemas.vorlage import VorlagenpositionCreate 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( async def erstelle_vorlage(
db: AsyncSession, *, objekttyp_id: int, name: str, positionen: list[VorlagenpositionCreate] db: AsyncSession, *, objekttyp_id: int, name: str, positionen: list[VorlagenpositionCreate]
) -> Beladungsvorlage: ) -> Beladungsvorlage:
await _stelle_faecher_sicher(db, objekttyp_id=objekttyp_id, positionen=positionen)
vorlage = Beladungsvorlage( vorlage = Beladungsvorlage(
objekttyp_id=objekttyp_id, name=name, version=1, status=VorlageStatus.aktiv objekttyp_id=objekttyp_id, name=name, version=1, status=VorlageStatus.aktiv
) )
@@ -24,6 +43,7 @@ async def neue_version(
) -> Beladungsvorlage: ) -> Beladungsvorlage:
"""Prompt 08 Punkt 4: Änderung erzeugt neue Version, alte bleibt unverändert/lesbar """Prompt 08 Punkt 4: Änderung erzeugt neue Version, alte bleibt unverändert/lesbar
(Historie-Integrität) statt überschrieben zu werden.""" (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 aktuelle_vorlage.status = VorlageStatus.veraltet
await db.flush() await db.flush()
@@ -61,6 +81,7 @@ async def aktualisiere_positionen(
sich NICHT auf bereits angelegte Objekte aus, da deren Sollmenge fest in sich NICHT auf bereits angelegte Objekte aus, da deren Sollmenge fest in
`objektposition.sollmenge_vorlage` kopiert ist (siehe services/objekte.py), `objektposition.sollmenge_vorlage` kopiert ist (siehe services/objekte.py),
nicht mehr live aus der Vorlage gelesen wird.""" 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) await loesche_alle_positionen(db, vorlage.id)
for pos in positionen: for pos in positionen:
db.add(Vorlagenposition(vorlage_id=vorlage.id, **pos.model_dump())) db.add(Vorlagenposition(vorlage_id=vorlage.id, **pos.model_dump()))
+22
View File
@@ -1,9 +1,31 @@
import pytest 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.services.vorlagen import aktualisiere_positionen, erstelle_vorlage, hole_positionen, neue_version
from app.schemas.vorlage import VorlagenpositionCreate 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 @pytest.mark.asyncio
async def test_neue_version_alte_bleibt_lesbar(db_session, objekttyp_factory, material_factory): 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.""" """U10: Vorlagenänderung erzeugt neue Version, alte Version bleibt unverändert lesbar."""