feat(personal): Personal/Qualifikationen-Modul (Roadmap Phase 1)
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
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
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
|
||||
)
|
||||
Reference in New Issue
Block a user