Files
patrickandClaude Sonnet 5 d61aabadf3
CI / backend-tests (push) Successful in 1m13s
CI / frontend-build (push) Successful in 23s
Karte 14 Schritt 1: geraet_instanz-Tabelle (additiv)
objektposition.seriennummer war ein Einzelfeld, konnte nur EIN Gerät pro
Materialtyp/Objekt abbilden (z.B. 2 Pulsoxymeter im selben Rucksack nicht
darstellbar). Neue Tabelle geraet_instanz erlaubt beliebig viele Exemplare
pro Position (Seriennummer, Prüfdatum, nächste Prüfung, Status). Zusätzlich
objektposition.pruefintervall_monate (individuell überschreibbar).

Bewusst additiv: seriennummer-Spalte bleibt vorerst stehen (Service/
Endpunkte/Frontend hängen noch daran), bestehende Werte werden per
INSERT...SELECT nach geraet_instanz kopiert. Drop + Umbau der abhängigen
Schichten folgt in Umsetzungsschritt 2 (siehe arbeitskarten/14_...).

Migration von postgres-expert gegengeprüft (ON CONFLICT DO NOTHING als
Sicherheitsnetz, DROP TYPE IF EXISTS ergänzt), Tests decken Mehrfach-
Instanzen, UNIQUE-Constraint je Position, gleiche SN an verschiedenen
Positionen und pruefintervall_monate ab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
2026-09-04 20:45:04 +02:00

66 lines
2.8 KiB
Python

"""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")