feat(lager): Warehouse-Epic Kern (WH-001..004+007)
Lagerort-Hierarchie (organisation/standort/gebaeude/raum/lager, self-referenzierend), Lagerplätze mit eindeutigem Code, Bestand je Lagerplatz+Material, Ein-/Auslagerung mit lückenlosem Materialbewegungsprotokoll (append-only, analog Lagerbewegung auf Objekt-Ebene, hier aber Material-/Lagerplatz-fein). - Migration 0019: 4 neue Tabellen (lagerort, lagerplatz, bestand, materialbewegung) - Backend: CRUD Lagerort/Lagerplatz, Bestandsabfrage, Ein-/Auslagern-Endpunkte, Negativbestand vorab abgefangen (kein DB-Constraint-Exception-Pfad, gleiches Muster wie objekte.py-Fix vom 2026-09-05) - Frontend: neuer Admin-Tab "Lager" (Lagerort/-platz-Verwaltung, Bestand+Buchung je Lagerplatz) - 2 neue Tests, 139 Tests weiterhin grün WH-005 (Umlagerung) und WH-006 (Inventur) bewusst zurückgestellt - Kern zuerst, laut Nutzer-Entscheidung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import pytest
|
||||
|
||||
from tests.conftest import auth_header, login
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lagerort_hierarchie_und_lagerplatz(client, admin_user):
|
||||
token = await login(client, "admin1")
|
||||
|
||||
wurzel = await client.post(
|
||||
"/api/v1/lagerorte", json={"name": "Hauptwache", "typ": "standort"}, headers=auth_header(token)
|
||||
)
|
||||
assert wurzel.status_code == 201
|
||||
kind = await client.post(
|
||||
"/api/v1/lagerorte",
|
||||
json={"name": "Lager Halle 1", "typ": "lager", "parent_id": wurzel.json()["id"]},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert kind.status_code == 201
|
||||
assert kind.json()["parent_id"] == wurzel.json()["id"]
|
||||
|
||||
platz = await client.post(
|
||||
"/api/v1/lagerplaetze",
|
||||
json={"lagerort_id": kind.json()["id"], "code": "REGAL-A1", "bezeichnung": "Regal A, Fach 1"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert platz.status_code == 201
|
||||
|
||||
doppelt = await client.post(
|
||||
"/api/v1/lagerplaetze",
|
||||
json={"lagerort_id": kind.json()["id"], "code": "REGAL-A1"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert doppelt.status_code == 409
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ein_und_auslagerung_veraendert_bestand_korrekt(client, admin_user, material_factory):
|
||||
token = await login(client, "admin1")
|
||||
material = await material_factory("Verbandpäckchen")
|
||||
|
||||
lagerort = await client.post(
|
||||
"/api/v1/lagerorte", json={"name": "Lager X", "typ": "lager"}, headers=auth_header(token)
|
||||
)
|
||||
platz = await client.post(
|
||||
"/api/v1/lagerplaetze",
|
||||
json={"lagerort_id": lagerort.json()["id"], "code": "PLATZ-1"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
platz_id = platz.json()["id"]
|
||||
|
||||
ein = await client.post(
|
||||
f"/api/v1/lagerplaetze/{platz_id}/einlagern",
|
||||
json={"material_id": material.id, "menge": "10"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert ein.status_code == 201
|
||||
assert ein.json()["von_lagerplatz_id"] is None
|
||||
assert ein.json()["nach_lagerplatz_id"] == platz_id
|
||||
|
||||
bestand = await client.get(f"/api/v1/lagerplaetze/{platz_id}/bestand", headers=auth_header(token))
|
||||
assert bestand.json()[0]["menge"] == "10"
|
||||
|
||||
aus = await client.post(
|
||||
f"/api/v1/lagerplaetze/{platz_id}/auslagern",
|
||||
json={"material_id": material.id, "menge": "3"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert aus.status_code == 201
|
||||
|
||||
bestand2 = await client.get(f"/api/v1/lagerplaetze/{platz_id}/bestand", headers=auth_header(token))
|
||||
assert bestand2.json()[0]["menge"] == "7"
|
||||
|
||||
zu_viel = await client.post(
|
||||
f"/api/v1/lagerplaetze/{platz_id}/auslagern",
|
||||
json={"material_id": material.id, "menge": "999"},
|
||||
headers=auth_header(token),
|
||||
)
|
||||
assert zu_viel.status_code == 409
|
||||
|
||||
bewegungen = await client.get(
|
||||
f"/api/v1/materialbewegungen?lagerplatz_id={platz_id}", headers=auth_header(token)
|
||||
)
|
||||
assert len(bewegungen.json()) == 2
|
||||
Reference in New Issue
Block a user