Files
MABEA/backend/tests/test_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

139 lines
5.0 KiB
Python

import pytest
from sqlalchemy import select
from app.models.objektposition import Objektposition
from tests.conftest import auth_header, login
async def _fehlbestand_erzeugen(client, token, objekt, material, istmenge: str):
start = await client.post(
f"/api/v1/objekte/{objekt.id}/kontrollen", json={"uebernehmen": False}, headers=auth_header(token)
)
kontrolle_id = start.json()["id"]
put_response = await client.put(
f"/api/v1/kontrollen/{kontrolle_id}/positionen/{material.id}",
json={"istmenge": istmenge},
headers=auth_header(token),
)
return put_response.json()["fehlbestand_id"]
@pytest.mark.asyncio
async def test_sofort_nachfuellung_erledigt_ohne_zwischenstatus(
client, db_session, objekt_mit_position, mitarbeiter_user
):
"""U4: Sofort-Nachfüllung während der Kontrolle - Fehlbestand entsteht und
wird im selben Moment erledigt, Ist-Menge wird auf Soll korrigiert."""
objekt, material = objekt_mit_position
token = await login(client, "mitarbeiter1")
fehlbestand_id = await _fehlbestand_erzeugen(client, token, objekt, material, "7")
response = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "3"},
headers=auth_header(token),
)
assert response.status_code == 201
body = response.json()
assert body["fehlbestand"]["status"] == "erledigt"
assert float(body["fehlbestand"]["fehlmenge"]) == 0
assert float(body["ueberbestand"]) == 0
objektposition = (
await db_session.execute(
select(Objektposition).where(
Objektposition.objekt_id == objekt.id, Objektposition.material_id == material.id
)
)
).scalar_one()
assert float(objektposition.istmenge) == 10
@pytest.mark.asyncio
async def test_teilnachfuellung_bleibt_offen_mit_reduzierter_fehlmenge(
client, db_session, objekt_mit_position, mitarbeiter_user
):
"""U5: Teilnachfüllung reduziert Fehlmenge, Status bleibt (fachlich weiterhin) offen."""
objekt, material = objekt_mit_position
token = await login(client, "mitarbeiter1")
fehlbestand_id = await _fehlbestand_erzeugen(client, token, objekt, material, "6")
response = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "2"},
headers=auth_header(token),
)
assert response.status_code == 201
body = response.json()["fehlbestand"]
assert body["status"] == "nachgefuellt_teilweise"
assert float(body["fehlmenge"]) == 2
assert float(body["istmenge"]) == 8
@pytest.mark.asyncio
async def test_erledigt_ausschliesslich_bei_ist_gleich_soll(
client, db_session, objekt_mit_position, mitarbeiter_user
):
"""U3: Fehlbestand wird ausschließlich automatisch bei Ist=Soll erledigt."""
objekt, material = objekt_mit_position
token = await login(client, "mitarbeiter1")
fehlbestand_id = await _fehlbestand_erzeugen(client, token, objekt, material, "4")
teil = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "3"},
headers=auth_header(token),
)
assert teil.json()["fehlbestand"]["status"] == "nachgefuellt_teilweise"
rest = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "3"},
headers=auth_header(token),
)
assert rest.json()["fehlbestand"]["status"] == "erledigt"
assert float(rest.json()["fehlbestand"]["fehlmenge"]) == 0
@pytest.mark.asyncio
async def test_ueberbestand_durch_nachfuellung_kein_fehler(
client, db_session, objekt_mit_position, mitarbeiter_user
):
"""E4/Prompt 21.9: Nachfüllung über Soll hinaus erlaubt, als Überbestand markiert."""
objekt, material = objekt_mit_position
token = await login(client, "mitarbeiter1")
fehlbestand_id = await _fehlbestand_erzeugen(client, token, objekt, material, "7")
response = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "5"},
headers=auth_header(token),
)
assert response.status_code == 201
body = response.json()
assert body["fehlbestand"]["status"] == "erledigt"
assert float(body["ueberbestand"]) == 2
@pytest.mark.asyncio
async def test_nachfuellung_auf_erledigten_fehlbestand_ist_409(
client, objekt_mit_position, mitarbeiter_user
):
objekt, material = objekt_mit_position
token = await login(client, "mitarbeiter1")
fehlbestand_id = await _fehlbestand_erzeugen(client, token, objekt, material, "7")
erste = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "3"},
headers=auth_header(token),
)
assert erste.json()["fehlbestand"]["status"] == "erledigt"
zweite = await client.post(
f"/api/v1/fehlbestaende/{fehlbestand_id}/nachfuellungen",
json={"menge": "1"},
headers=auth_header(token),
)
assert zweite.status_code == 409