Karte 14 Schritt 1: geraet_instanz-Tabelle (additiv)
CI / backend-tests (push) Successful in 1m13s
CI / frontend-build (push) Successful in 23s

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
This commit is contained in:
2026-09-04 20:45:04 +02:00
co-authored by Claude Sonnet 5
parent 4c87bf2d84
commit d61aabadf3
8 changed files with 353 additions and 1 deletions
+3
View File
@@ -1,6 +1,7 @@
from app.models.auth import Benutzer, BenutzerRolle, RolleTyp, Systemknoten, KnotenTyp
from app.models.eskalation import EskalationKonfiguration
from app.models.fehlbestand import Fehlbestand, FehlbestandStatus
from app.models.geraet_instanz import GeraetInstanz, GeraetStatus
from app.models.historie import Historie
from app.models.kontrolle import Kontrolle, KontrollStatus, Kontrollposition
from app.models.mindermenge import MindermengeStatus, MindermengenGenehmigung
@@ -20,6 +21,8 @@ __all__ = [
"EskalationKonfiguration",
"Fehlbestand",
"FehlbestandStatus",
"GeraetInstanz",
"GeraetStatus",
"Historie",
"Kontrolle",
"KontrollStatus",
+40
View File
@@ -0,0 +1,40 @@
import enum
import uuid
from datetime import date
from sqlalchemy import Date, ForeignKey, String
from sqlalchemy.dialects.postgresql import ENUM as PgEnum, UUID
from sqlalchemy.orm import Mapped, mapped_column
from app.db.base import Base
class GeraetStatus(str, enum.Enum):
einsatzbereit = "einsatzbereit"
defekt = "defekt"
in_reparatur = "in_reparatur"
geraet_status_pg = PgEnum(GeraetStatus, name="geraet_status", create_type=False)
class GeraetInstanz(Base):
"""Karte 14: löst objektposition.seriennummer als Einzelfeld ab - das konnte
nur EIN Gerät pro Materialtyp/Objekt abbilden (z.B. 2 Pulsoxymeter im selben
Rucksack nicht darstellbar). Ist-Menge bei materialtyp='geraet_sn' wird aus
der Anzahl der Zeilen mit status='einsatzbereit' berechnet, nicht mehr als
Zahl eingegeben (Umsetzungsschritt 2, siehe Karte 14)."""
__tablename__ = "geraet_instanz"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
objektposition_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("objektposition.id"), nullable=False
)
seriennummer: Mapped[str] = mapped_column(String, nullable=False)
pruefdatum: Mapped[date | None] = mapped_column(Date)
naechste_pruefung: Mapped[date | None] = mapped_column(Date)
status: Mapped[GeraetStatus] = mapped_column(
geraet_status_pg, nullable=False, default=GeraetStatus.einsatzbereit
)
bemerkung: Mapped[str | None] = mapped_column(String)
+5 -1
View File
@@ -3,7 +3,7 @@ import uuid
from datetime import date
from decimal import Decimal
from sqlalchemy import Date, ForeignKey, Numeric, String, UniqueConstraint
from sqlalchemy import Date, ForeignKey, Integer, Numeric, String, UniqueConstraint
from sqlalchemy.dialects.postgresql import ENUM as PgEnum, UUID
from sqlalchemy.orm import Mapped, mapped_column
@@ -43,3 +43,7 @@ class Objektposition(Base):
# separat von seriennummer: Code identifiziert das gedruckte Label, SN das
# Herstellerattribut - beide können divergieren (Etikett verloren/neu vergeben).
code: Mapped[str | None] = mapped_column(String, unique=True)
# Karte 14: Prüf-/Wartungspflicht, individuell überschreibbar je Objektposition
# (wie sollmenge_override) - NULL = kein Prüfintervall (nicht prüfpflichtig,
# oder Standard aus dem Material/der Vorlage folgt später bei Bedarf).
pruefintervall_monate: Mapped[int | None] = mapped_column(Integer)