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
66 lines
2.8 KiB
Python
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")
|