"""Warehouse-Epic Kern (WH-001..004+007): Lagerort-Hierarchie, Lagerplätze, Bestand je Lagerplatz, Materialbewegungsprotokoll. Additiv, ersetzt nicht `standort`/`Lagerbewegung` (Objekt-Ebene bleibt unverändert). Revision ID: 0019_lager Revises: 0018_funkkenner Create Date: 2026-09-05 """ from typing import Sequence, Union from alembic import op revision: str = "0019_lager" down_revision: Union[str, None] = "0018_funkkenner" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.execute( "CREATE TYPE lagerort_typ AS ENUM ('organisation', 'standort', 'gebaeude', 'raum', 'lager')" ) op.execute( """ CREATE TABLE lagerort ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, parent_id INTEGER REFERENCES lagerort(id) ON DELETE SET NULL, typ lagerort_typ NOT NULL ) """ ) op.execute("CREATE INDEX idx_lagerort_parent ON lagerort (parent_id)") op.execute( """ CREATE TABLE lagerplatz ( id SERIAL PRIMARY KEY, lagerort_id INTEGER NOT NULL REFERENCES lagerort(id), code TEXT UNIQUE NOT NULL, bezeichnung TEXT ) """ ) op.execute("CREATE INDEX idx_lagerplatz_lagerort ON lagerplatz (lagerort_id)") op.execute( """ CREATE TABLE bestand ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lagerplatz_id INTEGER NOT NULL REFERENCES lagerplatz(id), material_id INTEGER NOT NULL REFERENCES material(id), menge NUMERIC NOT NULL DEFAULT 0, CONSTRAINT bestand_lagerplatz_material_uq UNIQUE (lagerplatz_id, material_id) ) """ ) op.execute("CREATE INDEX idx_bestand_material ON bestand (material_id)") op.execute( """ CREATE TABLE materialbewegung ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), material_id INTEGER NOT NULL REFERENCES material(id), menge NUMERIC NOT NULL, von_lagerplatz_id INTEGER REFERENCES lagerplatz(id), nach_lagerplatz_id INTEGER REFERENCES lagerplatz(id), benutzer_id INTEGER NOT NULL REFERENCES benutzer(id), zeitpunkt TIMESTAMPTZ NOT NULL, grund TEXT, CONSTRAINT materialbewegung_von_oder_nach CHECK (von_lagerplatz_id IS NOT NULL OR nach_lagerplatz_id IS NOT NULL) ) """ ) op.execute("CREATE INDEX idx_materialbewegung_material ON materialbewegung (material_id)") op.execute("CREATE INDEX idx_materialbewegung_von ON materialbewegung (von_lagerplatz_id)") op.execute("CREATE INDEX idx_materialbewegung_nach ON materialbewegung (nach_lagerplatz_id)") def downgrade() -> None: op.execute("DROP TABLE materialbewegung") op.execute("DROP TABLE bestand") op.execute("DROP TABLE lagerplatz") op.execute("DROP TABLE lagerort") op.execute("DROP TYPE lagerort_typ")