"""Karte 14: geraet_instanz-Tabelle + pruefintervall_monate (additiv) Revision ID: 0008_geraet_instanz Revises: 0007_indizes_und_locks Create Date: 2026-09-04 Umsetzungsschritt 1 von Karte 14 (Prüf-/wartungspflichtige Geräte). Löst objektposition.seriennummer als Einzelfeld ab - das konnte nur EIN Gerät pro Materialtyp/Objekt abbilden. Bewusst ADDITIV: bestehende seriennummer-Werte werden nach geraet_instanz kopiert, die Spalte selbst bleibt vorerst stehen (Service/Endpunkte/Frontend hängen noch daran) - Drop folgt erst mit dem koordinierten Umbau dieser Schichten (Umsetzungsschritt 2, siehe Karte 14). """ from typing import Sequence, Union from alembic import op revision: str = "0008_geraet_instanz" down_revision: Union[str, None] = "0007_indizes_und_locks" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.execute("CREATE TYPE geraet_status AS ENUM ('einsatzbereit', 'defekt', 'in_reparatur')") op.execute( """ CREATE TABLE geraet_instanz ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), objektposition_id UUID NOT NULL REFERENCES objektposition(id), seriennummer TEXT NOT NULL, pruefdatum DATE, naechste_pruefung DATE, status geraet_status NOT NULL DEFAULT 'einsatzbereit', bemerkung TEXT, UNIQUE (objektposition_id, seriennummer) ) """ ) op.execute("CREATE INDEX idx_geraet_instanz_objektposition ON geraet_instanz (objektposition_id)") op.execute("ALTER TABLE objektposition ADD COLUMN pruefintervall_monate INTEGER") # Bestehende Seriennummern übernehmen, damit kein Datenverlust entsteht, # während seriennummer noch parallel existiert. objektposition_id ist pro # Quellzeile eindeutig (1:1-Kopie), UNIQUE(objektposition_id, seriennummer) # kann hier nie kollidieren - ON CONFLICT trotzdem als Sicherheitsnetz, falls # die Migration je manuell erneut auf einer bereits befüllten DB läuft # (postgres-expert-Review-Fund). op.execute( """ INSERT INTO geraet_instanz (objektposition_id, seriennummer) SELECT id, seriennummer FROM objektposition WHERE seriennummer IS NOT NULL AND seriennummer <> '' ON CONFLICT (objektposition_id, seriennummer) DO NOTHING """ ) def downgrade() -> None: # Achtung: droppt auch alle Instanzen/Prüfungen, die NACH diesem Upgrade # über Umsetzungsschritt 2 (Service/Endpunkte) angelegt wurden - sobald # Schritt 2 live ist, ist dieser Downgrade nicht mehr gefahrlos. op.execute("ALTER TABLE objektposition DROP COLUMN pruefintervall_monate") op.execute("DROP TABLE geraet_instanz") op.execute("DROP TYPE IF EXISTS geraet_status")