diff --git a/DEVLOG.md b/DEVLOG.md index 714ae9d..cd648b9 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -15991,3 +15991,90 @@ Keine Commits in dieser Session. - frontend/src/pages/admin/ObjekttypSection.tsx | 163 ++++++++++++++++++++++++++++++++++++++++-------------------------------- --- +## 2026-09-14 23:04 – 23:07 (3m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +- 6c05d45 feat(zustaendigkeit): ZUST-004 Runde 4 - Historie und Dashboard-Aggregate + +### Geänderte Dateien +- DEVLOG.md | 36 ++++++++++++++++++++++++++++++++++++ +- arbeitskacheln/17_zustaendigkeit.md | 24 +++++++++++++++++++----- +- backend/app/api/v1/endpoints/dashboard.py | 21 +++++++++++++++------ +- backend/app/api/v1/endpoints/historie.py | 11 +++++++++-- +- backend/app/services/dashboard.py | 43 +++++++++++++++++++++++++------------------ +- backend/tests/test_zust004_standort_sichtbarkeit.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +--- +## 2026-09-14 23:11 – 23:11 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- DEVLOG.md | 36 ++++++++++++++++++++++++++++++++++++ +- arbeitskacheln/17_zustaendigkeit.md | 24 +++++++++++++++++++----- +- backend/app/api/v1/endpoints/dashboard.py | 21 +++++++++++++++------ +- backend/app/api/v1/endpoints/historie.py | 11 +++++++++-- +- backend/app/services/dashboard.py | 43 +++++++++++++++++++++++++------------------ +- backend/tests/test_zust004_standort_sichtbarkeit.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +--- +## 2026-09-14 23:14 – 23:15 (1m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +- 6f116ed fix(tests): test_historie_standort_eingeschraenkt falsche Rolle + +### Geänderte Dateien +- backend/tests/test_zust004_standort_sichtbarkeit.py | 11 ++++++++--- + +--- +## 2026-09-14 23:20 – 23:20 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- backend/tests/test_zust004_standort_sichtbarkeit.py | 11 ++++++++--- + +--- +## 2026-09-14 23:29 – 23:29 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- backend/tests/test_zust004_standort_sichtbarkeit.py | 11 ++++++++--- + +--- +## 2026-09-14 23:30 – 23:30 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- backend/tests/test_zust004_standort_sichtbarkeit.py | 11 ++++++++--- + +--- +## 2026-09-14 23:30 – 23:31 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- backend/tests/test_zust004_standort_sichtbarkeit.py | 11 ++++++++--- + +--- diff --git a/arbeitskacheln/17_zustaendigkeit.md b/arbeitskacheln/17_zustaendigkeit.md index 2867615..b44d543 100644 --- a/arbeitskacheln/17_zustaendigkeit.md +++ b/arbeitskacheln/17_zustaendigkeit.md @@ -191,13 +191,9 @@ war die Vorgängerlücke, diese hier ist die zweite gefundene Lücke). Umgesetzt (Runde 3): `GET /wartungsauftraege`, `GET /verleihe`, `GET /verleihe/{id}/uebergabeliste`, `PATCH /objekte/{id}/zaehlerstand`, `GET /objekte/{id}/uebergabeliste` (Aussonderung). - **Blockiert, braucht erst eine eigene Entscheidung:** Lagerbewegungen/ - Lagerplätze - `Lagerort` hat KEINE FK auf die echte `Standort`-Tabelle - (eigene, unabhängige Hierarchie mit `standort` nur als Typ-Label, siehe - Docstring `app/models/lager.py:Lagerort` - "Objekt/Zustaendigkeit bleiben - unverändert an `standort` hängen, Lagerort ist ein zusätzliches, feineres - Konzept"). ZUST-004 lässt sich hier nicht 1:1 anwenden, ohne vorher zu - klären, wie ein `Lagerort`-Knoten einem echten `Standort` zugeordnet wird. + **War blockiert bis Runde 5 (siehe unten):** Lagerbewegungen/Lagerplätze - + `Lagerort` hatte KEINE FK auf die echte `Standort`-Tabelle (eigene, + unabhängige Hierarchie mit `standort` nur als Typ-Label). Umgesetzt (Runde 4, 2026-09-14): `GET /historie` (nur wenn `entitaet_typ`+`entitaet_id` gesetzt sind - ohne diese Filter ist es eine globale Liste ohne Einzelobjekt-Bezug, bleibt unrestricted). **Nutzer- @@ -212,15 +208,33 @@ war die Vorgängerlücke, diese hier ist die zweite gefundene Lücke). `bevorstehende_ablaufdaten()`, `alle_faelligen_wartungen()` (dashboard- weit), `bevorstehende_qualifikationsablaeufe()` (Personal-bezogen, kein Objekt-/Standort-Konzept) - eigene Follow-up-Runde bei Bedarf, gleiche - "kein Big-Bang"-Linie. Lagerbewegungen/Lagerplätze weiterhin blockiert - (siehe oben). Tests: + "kein Big-Bang"-Linie. + **Runde 5 (2026-09-14): Lagerbewegungen/Lagerplätze-Blockade aufgehoben.** + `Lagerort` bekommt ein neues, nullable `standort_id`-Feld (Migration + `0060_lagerort_standort_id.py`), das NUR fachlich sinnvoll an einem + Knoten mit `typ=standort` gesetzt wird (Anwendungslogik, kein DB- + Constraint). `app/services/lager.py:standort_fuer_lagerort()`/ + `standort_fuer_lagerplatz()` lösen für einen beliebigen Lagerort/ + Lagerplatz den zuständigen Standort über die `parent_id`-Kette auf (erster + Vorfahre mit gesetzter `standort_id`, `None` = weiterhin unrestricted - + bestehende Lagerorte ohne Zuordnung ändern ihr Verhalten nicht). Umgesetzt: + `GET /lagerplaetze` (Filterung je Eintrag statt SQL-Join, da Kettenlänge + variabel), `GET /lagerplaetze/{id}/bestand`, `POST .../einlagern`, + `POST .../auslagern`, `POST .../umlagern` (BEIDE Lagerplätze geprüft - + Standort wechseln braucht Sichtbarkeit an Quelle UND Ziel). Admin-UI: + `LagerSection.tsx` zeigt eine Standort-Auswahl nur, wenn beim Anlegen + `typ=standort` gewählt ist. `Lagerbewegung` (Objekt-Ebene, WH-007) bleibt + unberührt - das ist ein anderes Modell als `Materialbewegung` + (Lagerplatz-Ebene), war nie Teil dieser Blockade. + Tests: `test_mangel_liste_standort_eingeschraenkt`, `test_kontroll_fortschritt_standort_eingeschraenkt`, `test_wartungsauftraege_liste_standort_eingeschraenkt`, `test_verleih_liste_und_uebergabeliste_standort_eingeschraenkt`, `test_objekt_zaehlerstand_und_aussonderung_uebergabeliste_standort_eingeschraenkt`, `test_historie_standort_eingeschraenkt`, - `test_dashboard_einsatzbereitschaft_und_prueftermine_standort_eingeschraenkt` + `test_dashboard_einsatzbereitschaft_und_prueftermine_standort_eingeschraenkt`, + `test_lagerplaetze_und_bestand_standort_eingeschraenkt` in `backend/tests/test_zust004_standort_sichtbarkeit.py`. --- diff --git a/backend/alembic/versions/0060_lagerort_standort_id.py b/backend/alembic/versions/0060_lagerort_standort_id.py new file mode 100644 index 0000000..e855149 --- /dev/null +++ b/backend/alembic/versions/0060_lagerort_standort_id.py @@ -0,0 +1,28 @@ +"""ZUST-004-Nachtrag: Lagerort mit echtem Standort verknüpfen. Nullable FK +nur fachlich sinnvoll an typ=standort-Knoten, Anwendungslogik erzwingt das +(kein DB-CHECK, da SQLAlchemy-Enum-Vergleich in einem CHECK umständlich wäre +und der Wert ohnehin nur über den Service-Layer gesetzt wird). + +Revision ID: 0060_lagerort_standort_id +Revises: 0059_objekttyp_wartung_aktiv +Create Date: 2026-09-14 +""" + +from typing import Sequence, Union + +from alembic import op + +revision: str = "0060_lagerort_standort_id" +down_revision: Union[str, None] = "0059_objekttyp_wartung_aktiv" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.execute( + "ALTER TABLE lagerort ADD COLUMN standort_id INTEGER REFERENCES standort(id) ON DELETE SET NULL" + ) + + +def downgrade() -> None: + op.execute("ALTER TABLE lagerort DROP COLUMN standort_id") diff --git a/backend/app/api/v1/endpoints/lager.py b/backend/app/api/v1/endpoints/lager.py index 2d37b8a..aef8a47 100644 --- a/backend/app/api/v1/endpoints/lager.py +++ b/backend/app/api/v1/endpoints/lager.py @@ -24,8 +24,10 @@ from app.services.lager import ( NichtGenugBestandError, auslagern, einlagern, + standort_fuer_lagerplatz, umlagern, ) +from app.services.standort_sichtbarkeit import pruefe_standort_sichtbar, sichtbare_standort_ids router = APIRouter() @@ -80,13 +82,26 @@ async def aendere_lagerort( @router.get("/lagerplaetze", response_model=list[LagerplatzRead]) async def liste_lagerplaetze( - lagerort_id: int | None = None, db: AsyncSession = Depends(get_db), _=Depends(get_current_user) + lagerort_id: int | None = None, db: AsyncSession = Depends(get_db), current_user=Depends(get_current_user) ) -> list[Lagerplatz]: + """ZUST-004-Erweiterung: Standort-Auflösung je Lagerplatz (Eltern-Kette, + kein direktes Feld) - Filterung nach Laden statt per SQL-Join, da die + Kettenlänge variabel ist und Lagerplatz-Listen typischerweise klein sind.""" stmt = select(Lagerplatz) if lagerort_id is not None: stmt = stmt.where(Lagerplatz.lagerort_id == lagerort_id) result = await db.execute(stmt) - return list(result.scalars().all()) + lagerplaetze = list(result.scalars().all()) + + sichtbare = await sichtbare_standort_ids(db, current_user.id) + if sichtbare is None: + return lagerplaetze + gefiltert = [] + for lagerplatz in lagerplaetze: + standort_id = await standort_fuer_lagerplatz(db, lagerplatz.id) + if standort_id is None or standort_id in sichtbare: + gefiltert.append(lagerplatz) + return gefiltert @router.post("/lagerplaetze", response_model=LagerplatzRead, status_code=status.HTTP_201_CREATED) @@ -107,9 +122,11 @@ async def erstelle_lagerplatz( @router.get("/lagerplaetze/{lagerplatz_id}/bestand", response_model=list[BestandRead]) async def hole_bestand( - lagerplatz_id: int, db: AsyncSession = Depends(get_db), _=Depends(get_current_user) + lagerplatz_id: int, db: AsyncSession = Depends(get_db), current_user=Depends(get_current_user) ) -> list[Bestand]: await _get_or_404(db, Lagerplatz, lagerplatz_id, "Lagerplatz nicht gefunden") + standort_id = await standort_fuer_lagerplatz(db, lagerplatz_id) + await pruefe_standort_sichtbar(db, benutzer_id=current_user.id, standort_id=standort_id) result = await db.execute(select(Bestand).where(Bestand.lagerplatz_id == lagerplatz_id)) return list(result.scalars().all()) @@ -126,6 +143,8 @@ async def route_einlagern( current_user=Depends(get_current_user), ) -> Materialbewegung: await _get_or_404(db, Lagerplatz, lagerplatz_id, "Lagerplatz nicht gefunden") + standort_id = await standort_fuer_lagerplatz(db, lagerplatz_id) + await pruefe_standort_sichtbar(db, benutzer_id=current_user.id, standort_id=standort_id) return await einlagern( db, lagerplatz_id=lagerplatz_id, @@ -148,6 +167,8 @@ async def route_auslagern( current_user=Depends(get_current_user), ) -> Materialbewegung: await _get_or_404(db, Lagerplatz, lagerplatz_id, "Lagerplatz nicht gefunden") + standort_id = await standort_fuer_lagerplatz(db, lagerplatz_id) + await pruefe_standort_sichtbar(db, benutzer_id=current_user.id, standort_id=standort_id) try: return await auslagern( db, @@ -175,6 +196,10 @@ async def route_umlagern( """WH-005: Verschieben in einem Schritt statt Aus+Ein manuell.""" await _get_or_404(db, Lagerplatz, lagerplatz_id, "Quell-Lagerplatz nicht gefunden") await _get_or_404(db, Lagerplatz, payload.nach_lagerplatz_id, "Ziel-Lagerplatz nicht gefunden") + von_standort_id = await standort_fuer_lagerplatz(db, lagerplatz_id) + await pruefe_standort_sichtbar(db, benutzer_id=current_user.id, standort_id=von_standort_id) + nach_standort_id = await standort_fuer_lagerplatz(db, payload.nach_lagerplatz_id) + await pruefe_standort_sichtbar(db, benutzer_id=current_user.id, standort_id=nach_standort_id) try: return await umlagern( db, diff --git a/backend/app/models/lager.py b/backend/app/models/lager.py index 0674c95..4f0b688 100644 --- a/backend/app/models/lager.py +++ b/backend/app/models/lager.py @@ -24,7 +24,15 @@ lagerort_typ_pg = PgEnum(LagerortTyp, name="lagerort_typ", create_type=False) class Lagerort(Base): """WH-001: mehrstufige Hierarchie statt flachem `standort`-Feld (Objekt/ Zustaendigkeit bleiben unverändert an `standort` hängen - Lagerort ist ein - zusätzliches, feineres Konzept für das Warehouse-Epic, kein Ersatz).""" + zusätzliches, feineres Konzept für das Warehouse-Epic, kein Ersatz). + + ZUST-004-Nachtrag (2026-09-14): `standort_id` verknüpft NUR Knoten mit + `typ=standort` mit der echten `Standort`-Tabelle (fachlich sinnlos an + einem `raum`/`lager`-Knoten - dessen Standort ergibt sich aus dem + nächsten `standort`-Vorfahren in der Kette, siehe + app/services/lager.py:standort_fuer_lagerort()). Nullable, weil ein + `standort`-Knoten ohne Zuordnung weiterhin funktionieren muss + (Rückwärtskompatibilität - bestehende Lagerorte haben keine Zuordnung).""" __tablename__ = "lagerort" @@ -32,6 +40,7 @@ class Lagerort(Base): name: Mapped[str] = mapped_column(String, nullable=False) parent_id: Mapped[int | None] = mapped_column(ForeignKey("lagerort.id", ondelete="SET NULL")) typ: Mapped[LagerortTyp] = mapped_column(lagerort_typ_pg, nullable=False) + standort_id: Mapped[int | None] = mapped_column(ForeignKey("standort.id", ondelete="SET NULL")) class Lagerplatz(Base): diff --git a/backend/app/schemas/lager.py b/backend/app/schemas/lager.py index 983d58f..10bd8f3 100644 --- a/backend/app/schemas/lager.py +++ b/backend/app/schemas/lager.py @@ -11,12 +11,14 @@ class LagerortCreate(BaseModel): name: str parent_id: int | None = None typ: LagerortTyp + standort_id: int | None = None class LagerortUpdate(BaseModel): name: str | None = None parent_id: int | None = None typ: LagerortTyp | None = None + standort_id: int | None = None class LagerortRead(BaseModel): @@ -25,6 +27,7 @@ class LagerortRead(BaseModel): name: str parent_id: int | None typ: LagerortTyp + standort_id: int | None class LagerplatzCreate(BaseModel): diff --git a/backend/app/services/lager.py b/backend/app/services/lager.py index 847fc5a..6e8ae9f 100644 --- a/backend/app/services/lager.py +++ b/backend/app/services/lager.py @@ -4,13 +4,40 @@ from decimal import Decimal from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from app.models.lager import Bestand, Materialbewegung +from app.models.lager import Bestand, Lagerort, Lagerplatz, Materialbewegung class NichtGenugBestandError(Exception): pass +async def standort_fuer_lagerort(db: AsyncSession, lagerort_id: int | None) -> int | None: + """ZUST-004-Nachtrag: läuft die `parent_id`-Kette nach oben bis zum + nächsten Knoten mit gesetztem `standort_id` (nicht zwingend `typ= + standort` - ein Admin könnte die Zuordnung theoretisch an jeder Ebene + setzen, das schadet nicht, es zählt einfach der nächste Treffer). + `None`, wenn kein Vorfahre eine Zuordnung hat (unrestricted, wie bisher). + Feste Obergrenze gegen einen versehentlichen Parent-Zyklus.""" + aktuelle_id = lagerort_id + for _ in range(50): + if aktuelle_id is None: + return None + lagerort = await db.get(Lagerort, aktuelle_id) + if lagerort is None: + return None + if lagerort.standort_id is not None: + return lagerort.standort_id + aktuelle_id = lagerort.parent_id + return None + + +async def standort_fuer_lagerplatz(db: AsyncSession, lagerplatz_id: int) -> int | None: + lagerplatz = await db.get(Lagerplatz, lagerplatz_id) + if lagerplatz is None: + return None + return await standort_fuer_lagerort(db, lagerplatz.lagerort_id) + + async def _bestand_zeile(db: AsyncSession, lagerplatz_id: int, material_id: int) -> Bestand | None: result = await db.execute( select(Bestand).where(Bestand.lagerplatz_id == lagerplatz_id, Bestand.material_id == material_id) diff --git a/backend/tests/test_zust004_standort_sichtbarkeit.py b/backend/tests/test_zust004_standort_sichtbarkeit.py index d9e84da..7fda8f0 100644 --- a/backend/tests/test_zust004_standort_sichtbarkeit.py +++ b/backend/tests/test_zust004_standort_sichtbarkeit.py @@ -351,6 +351,63 @@ async def test_dashboard_einsatzbereitschaft_und_prueftermine_standort_eingeschr assert prueftermine.status_code == 200 +@pytest.mark.asyncio +async def test_lagerplaetze_und_bestand_standort_eingeschraenkt( + client, admin_user, materialverantwortlicher_user, standort_factory, material_factory +): + """ZUST-004 Runde 5: Lagerort mit echtem Standort verknüpft (neues Feld + Lagerort.standort_id), Sichtbarkeit löst über die parent_id-Kette auf.""" + standort_a = await standort_factory("Standort A12") + standort_b = await standort_factory("Standort B12") + material = await material_factory("Verbandpäckchen ZUST4") + admin_token = await login(client, "admin1") + + ort_a = await client.post( + "/api/v1/lagerorte", + json={"name": "Wache A12", "typ": "standort", "standort_id": standort_a.id}, + headers=auth_header(admin_token), + ) + ort_b = await client.post( + "/api/v1/lagerorte", + json={"name": "Wache B12", "typ": "standort", "standort_id": standort_b.id}, + headers=auth_header(admin_token), + ) + # Unterknoten OHNE eigene standort_id - muss die Kette hoch zum Standort- + # Knoten auflösen. + unterordner_b = await client.post( + "/api/v1/lagerorte", + json={"name": "Regal B12", "typ": "lager", "parent_id": ort_b.json()["id"]}, + headers=auth_header(admin_token), + ) + platz_b = await client.post( + "/api/v1/lagerplaetze", + json={"lagerort_id": unterordner_b.json()["id"], "code": "ZUST4-PLATZ-B12"}, + headers=auth_header(admin_token), + ) + platz_b_id = platz_b.json()["id"] + + await client.put( + f"/api/v1/benutzer/{materialverantwortlicher_user.id}/standorte", + json={"standort_ids": [standort_a.id]}, + headers=auth_header(admin_token), + ) + token = await login(client, "materialverantwortlicher1") + + liste = await client.get("/api/v1/lagerplaetze", headers=auth_header(token)) + ids = {p["id"] for p in liste.json()} + assert platz_b_id not in ids + + bestand_verboten = await client.get(f"/api/v1/lagerplaetze/{platz_b_id}/bestand", headers=auth_header(token)) + assert bestand_verboten.status_code == 403 + + einlagern_verboten = await client.post( + f"/api/v1/lagerplaetze/{platz_b_id}/einlagern", + json={"material_id": material.id, "menge": "5"}, + headers=auth_header(token), + ) + assert einlagern_verboten.status_code == 403 + + @pytest.mark.asyncio async def test_standort_zugriff_setzen_nur_admin(client, mitarbeiter_user, mitarbeiter2_user): token = await login(client, "mitarbeiter1") diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index e54614e..865824c 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -255,6 +255,7 @@ export interface Lagerort { name: string; parent_id: number | null; typ: LagerortTyp; + standort_id: number | null; } export interface Lagerplatz { diff --git a/frontend/src/pages/AdminPage.tsx b/frontend/src/pages/AdminPage.tsx index 60d12fa..ce8c6ed 100644 --- a/frontend/src/pages/AdminPage.tsx +++ b/frontend/src/pages/AdminPage.tsx @@ -349,7 +349,9 @@ export function AdminPage() { {aktiverTab === "lagerbewegungen" && ( )} - {aktiverTab === "lager" && } + {aktiverTab === "lager" && ( + + )} {aktiverTab === "ausgabe" && ( )} diff --git a/frontend/src/pages/admin/LagerSection.tsx b/frontend/src/pages/admin/LagerSection.tsx index b0488c5..f632f2b 100644 --- a/frontend/src/pages/admin/LagerSection.tsx +++ b/frontend/src/pages/admin/LagerSection.tsx @@ -1,11 +1,12 @@ import { useEffect, useState } from "react"; import { apiRequest, ApiError } from "../../api/client"; -import type { Bestand, Lagerort, LagerortTyp, Lagerplatz, Material, Materialbewegung } from "../../api/types"; +import type { Bestand, Lagerort, LagerortTyp, Lagerplatz, Material, Materialbewegung, Standort } from "../../api/types"; import * as lagerbewegungQueue from "../../offline/lagerbewegungQueue"; interface Props { materialien: Material[]; + standorte: Standort[]; onFehler: (text: string) => void; } @@ -19,13 +20,19 @@ const TYP_LABEL: Record = { // WH-001..005+007: Lagerort-Hierarchie, Lagerplätze, Bestand je Lagerplatz, // Ein-/Auslagerung, Umlagerung. Inventur (WH-006) noch offen (arbeitskacheln/07_warehouse.md). -export function LagerSection({ materialien, onFehler }: Props) { +export function LagerSection({ materialien, standorte, onFehler }: Props) { const [lagerorte, setLagerorte] = useState([]); const [lagerplaetze, setLagerplaetze] = useState([]); const [neuerOrtName, setNeuerOrtName] = useState(""); const [neuerOrtTyp, setNeuerOrtTyp] = useState("lager"); const [neuerOrtParent, setNeuerOrtParent] = useState(""); + const [neuerOrtStandortId, setNeuerOrtStandortId] = useState(""); + + function standortName(id: number | null): string { + if (id === null) return "–"; + return standorte.find((s) => s.id === id)?.name ?? `#${id}`; + } const [neuerPlatzOrt, setNeuerPlatzOrt] = useState(""); const [neuerPlatzCode, setNeuerPlatzCode] = useState(""); @@ -99,10 +106,16 @@ export function LagerSection({ materialien, onFehler }: Props) { try { await apiRequest("/lagerorte", { method: "POST", - body: { name: neuerOrtName, typ: neuerOrtTyp, parent_id: neuerOrtParent === "" ? null : neuerOrtParent }, + body: { + name: neuerOrtName, + typ: neuerOrtTyp, + parent_id: neuerOrtParent === "" ? null : neuerOrtParent, + standort_id: neuerOrtTyp === "standort" && neuerOrtStandortId !== "" ? neuerOrtStandortId : null, + }, }); setNeuerOrtName(""); setNeuerOrtParent(""); + setNeuerOrtStandortId(""); await laden(); } catch { onFehler("Lagerort konnte nicht angelegt werden."); @@ -223,6 +236,21 @@ export function LagerSection({ materialien, onFehler }: Props) { ))} + {neuerOrtTyp === "standort" && ( + + )}