Neues Modul für die BOS-Ressourcenplattform-Erweiterung: Einheit (Zug/Gruppe,
self-referenzierend), Qualifikationstyp (Führerschein/Lehrgang/Berechtigung),
BenutzerQualifikation (mit Gültigkeit) und ObjekttypQualifikationsanforderung
(M:N) - beantwortet "wer darf dieses Fahrzeug fahren?" über
GET /objekte/{id}/berechtigung/{benutzer_id}.
Benutzer und Objekt bekommen optionale einheit_id (additiv, analog
fahrzeug_id-Muster). Nebenbei Bugfix: PATCH /benutzer konnte einheit_id nicht
auf null setzen (Feld-vorhanden-Check via model_fields_set statt is not None).
Frontend: neuer Admin-Tab "Personal" (Einheiten, Qualifikationstypen,
Benutzer-Qualifikationszuordnung), Einheit-Auswahl im Benutzer-Formular.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
import enum
|
|
import uuid
|
|
from datetime import date
|
|
|
|
from sqlalchemy import CheckConstraint, Date, ForeignKey, String, UniqueConstraint
|
|
from sqlalchemy.dialects.postgresql import ENUM as PgEnum, UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class Qualifikationskategorie(str, enum.Enum):
|
|
fuehrerschein = "fuehrerschein"
|
|
lehrgang = "lehrgang"
|
|
berechtigung = "berechtigung"
|
|
|
|
|
|
qualifikationskategorie_pg = PgEnum(
|
|
Qualifikationskategorie, name="qualifikationskategorie", create_type=False
|
|
)
|
|
|
|
|
|
class Einheit(Base):
|
|
"""Roadmap-Erweiterung (Personal-Modul): Organisationseinheit (Zug/Gruppe),
|
|
self-referenzierend für Hierarchie (analog Kategorie.ueberkategorie_id).
|
|
Bewusst eigenständig statt an Standort gekoppelt - eine Einheit kann
|
|
ortsübergreifend sein, ein Standort mehrere Einheiten beherbergen."""
|
|
|
|
__tablename__ = "einheit"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
|
uebergeordnete_einheit_id: Mapped[int | None] = mapped_column(ForeignKey("einheit.id"))
|
|
standort_id: Mapped[int | None] = mapped_column(ForeignKey("standort.id"))
|
|
|
|
__table_args__ = (
|
|
CheckConstraint("uebergeordnete_einheit_id <> id", name="einheit_kein_selbstbezug"),
|
|
)
|
|
|
|
|
|
class Qualifikationstyp(Base):
|
|
"""Führerschein/Lehrgang/Berechtigung als ein Stammdaten-Konzept, damit
|
|
"welche Qualifikation braucht Objekttyp X" generisch bleibt (kein separates
|
|
Führerschein-Feld am Fahrzeug nötig, Karte-Nachtrag Personal-Modul)."""
|
|
|
|
__tablename__ = "qualifikationstyp"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
|
kategorie: Mapped[Qualifikationskategorie] = mapped_column(
|
|
qualifikationskategorie_pg, nullable=False
|
|
)
|
|
# NULL = unbefristet (z.B. manche Berechtigungen), sonst Gültigkeitsdauer in
|
|
# Monaten ab erworben_am - Ablauf wird nicht hier, sondern je
|
|
# BenutzerQualifikation über gueltig_bis geführt (individuelles Datum je
|
|
# Nachweis, nicht rein rechnerisch aus dem Typ abgeleitet).
|
|
gueltigkeit_monate: Mapped[int | None] = mapped_column()
|
|
|
|
|
|
class BenutzerQualifikation(Base):
|
|
__tablename__ = "benutzer_qualifikation"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
benutzer_id: Mapped[int] = mapped_column(ForeignKey("benutzer.id"), nullable=False)
|
|
qualifikationstyp_id: Mapped[int] = mapped_column(ForeignKey("qualifikationstyp.id"), nullable=False)
|
|
erworben_am: Mapped[date] = mapped_column(Date, nullable=False)
|
|
gueltig_bis: Mapped[date | None] = mapped_column(Date)
|
|
nachweis_nummer: Mapped[str | None] = mapped_column(String)
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint(
|
|
"benutzer_id", "qualifikationstyp_id", "erworben_am", name="uq_benutzer_qualifikation"
|
|
),
|
|
)
|
|
|
|
|
|
class ObjekttypQualifikationsanforderung(Base):
|
|
"""M:N: welche Qualifikationstypen braucht man, um Objekte dieses Objekttyps
|
|
zu bedienen/fahren (Nutzer-Beispiel: "wer darf GW-San 01 fahren?")."""
|
|
|
|
__tablename__ = "objekttyp_qualifikationsanforderung"
|
|
|
|
objekttyp_id: Mapped[int] = mapped_column(ForeignKey("objekttyp.id"), primary_key=True)
|
|
qualifikationstyp_id: Mapped[int] = mapped_column(
|
|
ForeignKey("qualifikationstyp.id"), primary_key=True
|
|
)
|