Files
MABEA/backend/app/services/fehlbestand.py
T
patrickandClaude Sonnet 5 18fba28eae
CI / backend-tests (push) Successful in 40s
Sprint 4: Fehlbestand-Statusmaschine komplett, Nachfüllung sofort/später/teilweise
- Nachfuellung-Modell: einzige Aktion, die Objektposition.istmenge tatsächlich
  ändert (Vier-Kernbegriffe). Deckt Sofort-Nachfüllung während Kontrolle (Karte 07)
  und spätere/externe Nachfüllung (Prompt 02.5) mit derselben Logik ab
- Wichtiger Fix vor dem ersten Testlauf: Objektposition.istmenge wird bei Nachfüllung
  auf den neuen Wert GESETZT (fehlbestand.istmenge + menge), nicht unabhängig
  inkrementiert - sonst bliebe sie beim initialen "nicht kontrolliert"-Stand (0)
  stehen, obwohl die Kontrolle bereits einen realen Zählwert kennt
- Statusübergänge: offen -> nachgefuellt_teilweise (Fehlmenge>0) -> erledigt
  (Fehlmenge=0), ausschließlich automatisch bei Ist=Soll (U3)
- Überbestand durch Nachfüllung erlaubt, als Info gekennzeichnet, kein Fehler (E4)
- KontrollpositionRead liefert jetzt fehlbestand_id mit, damit die UI direkt eine
  Sofort-Nachfüllung anbieten kann, ohne separat nachzufragen
- Tests: U3, U4 (Sofort-Nachfüllung ohne Zwischenstatus), U5 (Teilnachfüllung),
  Überbestand-Fall, Nachfüllung auf bereits erledigten Fehlbestand -> 409

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
2026-09-03 23:46:21 +02:00

70 lines
2.8 KiB
Python

from datetime import datetime, timezone
from decimal import Decimal
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.fehlbestand import Fehlbestand, FehlbestandStatus
from app.models.nachfuellung import Nachfuellung
from app.models.objektposition import Objektposition
class FehlbestandBereitsErledigtError(Exception):
"""Ein bereits erledigter Fehlbestand kann nicht erneut nachgefüllt werden."""
async def nachfuellen(
db: AsyncSession, *, fehlbestand: Fehlbestand, menge: Decimal, benutzer_id: int
) -> tuple[Nachfuellung, Decimal]:
"""Vier-Kernbegriffe (Prompt 02.9): Nachfüllung ist die einzige Aktion, die
Objektposition.istmenge tatsächlich ändert. Deckt sowohl Sofort-Nachfüllung
während der Kontrolle (Karte 07) als auch spätere/externe Nachfüllung
(Prompt 02.5) ab - technisch derselbe Vorgang, nur anderer Zeitpunkt.
Überbestand durch Nachfüllung ist erlaubt (Prompt 21.9/E4): kein Fehler, nur
als Info im Rückgabewert `ueberbestand` gekennzeichnet.
Wichtig: `fehlbestand.istmenge` ist die zuletzt GEZÄHLTE Wahrheit (aus der
Kontrolle, die den Fehlbestand ausgelöst hat) - Objektposition.istmenge wird
hier auf den neuen Wert GESETZT, nicht unabhängig davon inkrementiert. Sonst
bliebe Objektposition.istmenge bei ihrem alten (ggf. 0/"nicht kontrolliert")
Stand stehen, obwohl die Kontrolle bereits einen realen Zählwert kennt -
"Ist-Menge wird auf Soll korrigiert" (Prompt 02.3) meint genau dieses Setzen.
"""
if fehlbestand.status == FehlbestandStatus.erledigt:
raise FehlbestandBereitsErledigtError()
neue_istmenge = fehlbestand.istmenge + menge
ueberbestand = max(Decimal(0), neue_istmenge - fehlbestand.sollmenge)
neue_fehlmenge = max(Decimal(0), fehlbestand.sollmenge - neue_istmenge)
result = await db.execute(
select(Objektposition).where(
Objektposition.objekt_id == fehlbestand.objekt_id,
Objektposition.material_id == fehlbestand.material_id,
)
)
objektposition = result.scalar_one_or_none()
if objektposition is not None:
objektposition.istmenge = neue_istmenge
fehlbestand.istmenge = neue_istmenge
fehlbestand.fehlmenge = neue_fehlmenge
if neue_fehlmenge <= 0:
fehlbestand.status = FehlbestandStatus.erledigt
fehlbestand.erledigt_am = datetime.now(timezone.utc)
else:
fehlbestand.status = FehlbestandStatus.nachgefuellt_teilweise
nachfuellung = Nachfuellung(
fehlbestand_id=fehlbestand.id,
objekt_id=fehlbestand.objekt_id,
material_id=fehlbestand.material_id,
menge=menge,
benutzer_id=benutzer_id,
zeitpunkt=datetime.now(timezone.utc),
)
db.add(nachfuellung)
await db.flush()
return nachfuellung, ueberbestand