refactor: Code-Review-Fixes (Reuse/Simplification/Efficiency) aus dieser Session
CI / backend-tests (push) Successful in 1m57s
CI / frontend-build (push) Successful in 18s

4 parallele Review-Agenten (Reuse/Simplification/Efficiency/Altitude) gegen den
Diff dieser Session (c7dd47f...HEAD) laufen lassen, echte Funde angewendet:

- lager.py: 5x wiederholtes "db.get(...) or 404 raisen" durch _get_or_404()-
  Helper ersetzt
- akte.py: zwei Queries für Geräte-Instanzen (erst Positions-IDs, dann Geräte)
  zu einer Query mit Subquery zusammengefasst
- akte.py: manuelle Feld-für-Feld-Rekonstruktion von ObjektRead/HistorieRead
  durch model_validate()+model_copy() ersetzt (HistorieRead.benutzer_name
  bekommt dafür einen Default, harmlos für den bestehenden Endpunkt)
- vorlagen.py: doppelte "Positionen löschen + flush"-Logik (Vorlage-Löschen
  und Positionen-Ersetzen) in loesche_alle_positionen() zusammengeführt

Bewusst nicht angewendet:
- Zentrale Session-Rollback-Vereinheitlichung für die drei Pre-Check-Stellen
  (objekte.py Selbstbezug, geraet_instanz.py SN-Duplikat, lager.py Eltern-
  Selbstbezug) - der Altitude-Review schlug das vor, aber get_db() rollt in
  Produktion bei jeder Exception bereits korrekt zurück (jede Anfrage hat eine
  eigene Session); das PendingRollbackError-Problem trat nur in der geteilten
  Test-Session auf und wurde bereits gezielt per Pre-Check vermieden - eine
  zusätzliche zentrale Rollback-Schicht würde nichts mehr reparieren, was
  nicht schon repariert ist.
- Postgres-Cluster-Encoding (template1) auf beiden Hosts fixen, nicht nur in
  CI/für die eine migrierte DB - echter Infra-Eingriff, braucht Rücksprache.
- LagerSection.tsx "inkonsistente" Formular-Resets - beim genaueren Hinsehen
  gewolltes UX (Ort/Typ bleiben ausgewählt für mehrere Anlagen hintereinander).

140 Tests weiterhin grün.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-05 23:49:28 +02:00
co-authored by Claude Sonnet 5
parent a6f0c51842
commit 0c8f375a51
5 changed files with 51 additions and 62 deletions
+13 -7
View File
@@ -41,6 +41,18 @@ async def neue_version(
return neue
async def loesche_alle_positionen(db: AsyncSession, vorlage_id: int) -> None:
"""Gemeinsame Vorstufe für Vorlage-Löschen und Positionen-Ersetzen: Flush
zwischen Löschen und dem nächsten Schritt ist Pflicht - Beladungsvorlage/
Vorlagenposition haben keine ORM-relationship() (nur rohe FK-Spalten),
SQLAlchemy kennt die Abhängigkeit also nicht automatisch und könnte DELETEs
in falscher Reihenfolge absetzen (FK-Verletzung, beim echten Löschversuch
gefunden)."""
for position in await hole_positionen(db, vorlage_id):
await db.delete(position)
await db.flush()
async def aktualisiere_positionen(
db: AsyncSession, *, vorlage: Beladungsvorlage, positionen: list[VorlagenpositionCreate]
) -> Beladungsvorlage:
@@ -49,13 +61,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."""
bisherige = await hole_positionen(db, vorlage.id)
for position in bisherige:
await db.delete(position)
# Flush zwischen Löschen und Neuanlegen: gleiches Muster wie beim Löschen
# einer ganzen Vorlage (DELETE /vorlagen/{id}) - keine relationship(), also
# keine automatische Abhängigkeits-Reihenfolge durch SQLAlchemy.
await db.flush()
await loesche_alle_positionen(db, vorlage.id)
for pos in positionen:
db.add(Vorlagenposition(vorlage_id=vorlage.id, **pos.model_dump()))
await db.flush()