feat: SQLAlchemy models for users, cases, reports, audit

11 models across 4 files matching the MariaDB schema:
- user.py: User, RefreshToken, InvitationLink, AllowedDomain
- case.py: Case, CaseICDCode
- report.py: WeeklyReport, YearlySummary
- audit.py: ImportLog, AuditLog, Notification

All CHECK constraints, indexes (incl. prefix index), foreign keys,
and server defaults match the SQL DDL specification exactly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-02-24 07:28:33 +00:00
parent 5d57b1f349
commit e7befe78b6
5 changed files with 703 additions and 0 deletions

View file

@ -0,0 +1,24 @@
"""SQLAlchemy models for the DAK Zweitmeinungs-Portal."""
from app.models.user import AllowedDomain, InvitationLink, RefreshToken, User
from app.models.case import Case, CaseICDCode
from app.models.report import WeeklyReport, YearlySummary
from app.models.audit import AuditLog, ImportLog, Notification
__all__ = [
# User & Auth
"User",
"RefreshToken",
"InvitationLink",
"AllowedDomain",
# Cases
"Case",
"CaseICDCode",
# Reports
"WeeklyReport",
"YearlySummary",
# Logging & Notifications
"ImportLog",
"AuditLog",
"Notification",
]

130
backend/app/models/audit.py Normal file
View file

@ -0,0 +1,130 @@
"""Import log, audit log, and notification models."""
from __future__ import annotations
import datetime as dt
from typing import Optional
from sqlalchemy import (
Boolean,
CheckConstraint,
DateTime,
ForeignKey,
Index,
Integer,
String,
Text,
func,
)
from sqlalchemy.dialects.mysql import JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class ImportLog(Base):
__tablename__ = "import_log"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
filename: Mapped[str] = mapped_column(String(255), nullable=False)
import_type: Mapped[str] = mapped_column(String(50), nullable=False)
cases_imported: Mapped[int] = mapped_column(
Integer, nullable=False, server_default="0"
)
cases_skipped: Mapped[int] = mapped_column(
Integer, nullable=False, server_default="0"
)
cases_updated: Mapped[int] = mapped_column(
Integer, nullable=False, server_default="0"
)
errors: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
details: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
imported_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
imported_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
imported_by_user: Mapped[Optional["User"]] = relationship(
foreign_keys=[imported_by]
)
__table_args__ = (
CheckConstraint(
"import_type IN ('csv_crm','icd_xlsx','historical_excel','excel_sync')",
name="chk_imp_type",
),
)
class AuditLog(Base):
__tablename__ = "audit_log"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
user_id: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
action: Mapped[str] = mapped_column(String(100), nullable=False)
entity_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
entity_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
old_values: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
new_values: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
ip_address: Mapped[Optional[str]] = mapped_column(String(45), nullable=True)
user_agent: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
user: Mapped[Optional["User"]] = relationship(foreign_keys=[user_id])
__table_args__ = (
Index("idx_user", "user_id"),
Index("idx_entity", "entity_type", "entity_id"),
Index("idx_created", "created_at"),
)
class Notification(Base):
__tablename__ = "notifications"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
recipient_id: Mapped[int] = mapped_column(
Integer, ForeignKey("users.id"), nullable=False
)
notification_type: Mapped[str] = mapped_column(String(50), nullable=False)
title: Mapped[str] = mapped_column(String(255), nullable=False)
message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
related_entity_type: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True
)
related_entity_id: Mapped[Optional[int]] = mapped_column(
Integer, nullable=True
)
is_read: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
email_sent: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
email_sent_at: Mapped[Optional[dt.datetime]] = mapped_column(
DateTime, nullable=True
)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
recipient: Mapped["User"] = relationship(foreign_keys=[recipient_id])
__table_args__ = (
Index("idx_recipient", "recipient_id", "is_read"),
CheckConstraint(
"notification_type IN ("
"'new_cases_uploaded','icd_entered','icd_uploaded',"
"'report_ready','coding_completed')",
name="chk_notif",
),
)

198
backend/app/models/case.py Normal file
View file

@ -0,0 +1,198 @@
"""Case and ICD code models."""
from __future__ import annotations
import datetime as dt
from typing import Optional
from sqlalchemy import (
Boolean,
CheckConstraint,
Date,
DateTime,
ForeignKey,
Index,
Integer,
SmallInteger,
String,
Text,
UniqueConstraint,
func,
text,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.schema import FetchedValue
from app.database import Base
class Case(Base):
__tablename__ = "cases"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
fall_id: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
crm_ticket_id: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
jahr: Mapped[int] = mapped_column(SmallInteger, nullable=False)
kw: Mapped[int] = mapped_column(SmallInteger, nullable=False)
datum: Mapped[dt.date] = mapped_column(Date, nullable=False)
anrede: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
vorname: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
nachname: Mapped[str] = mapped_column(String(100), nullable=False)
geburtsdatum: Mapped[Optional[dt.date]] = mapped_column(Date, nullable=True)
kvnr: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
versicherung: Mapped[str] = mapped_column(
String(50), nullable=False, server_default="DAK"
)
icd: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
fallgruppe: Mapped[str] = mapped_column(String(20), nullable=False)
strasse: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
plz: Mapped[Optional[str]] = mapped_column(String(10), nullable=True)
ort: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
email: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
ansprechpartner: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
telefonnummer: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
mobiltelefon: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
email2: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
telefon2: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
unterlagen: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
unterlagen_verschickt: Mapped[Optional[dt.date]] = mapped_column(
Date, nullable=True
)
erhalten: Mapped[Optional[bool]] = mapped_column(Boolean, nullable=True)
unterlagen_erhalten: Mapped[Optional[dt.date]] = mapped_column(
Date, nullable=True
)
unterlagen_an_gutachter: Mapped[Optional[dt.date]] = mapped_column(
Date, nullable=True
)
gutachten: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
gutachter: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
gutachten_erstellt: Mapped[Optional[dt.date]] = mapped_column(
Date, nullable=True
)
gutachten_versendet: Mapped[Optional[dt.date]] = mapped_column(
Date, nullable=True
)
schweigepflicht: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
ablehnung: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
abbruch: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
abbruch_datum: Mapped[Optional[dt.date]] = mapped_column(Date, nullable=True)
gutachten_typ: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
therapieaenderung: Mapped[Optional[str]] = mapped_column(String(5), nullable=True)
ta_diagnosekorrektur: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
ta_unterversorgung: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
ta_uebertherapie: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
kurzbeschreibung: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
fragestellung: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
kommentar: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
sonstiges: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
abgerechnet: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
abrechnung_datum: Mapped[Optional[dt.date]] = mapped_column(Date, nullable=True)
import_source: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
imported_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
updated_at: Mapped[dt.datetime] = mapped_column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
server_onupdate=FetchedValue(),
)
updated_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
icd_entered_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
icd_entered_at: Mapped[Optional[dt.datetime]] = mapped_column(
DateTime, nullable=True
)
coding_completed_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
coding_completed_at: Mapped[Optional[dt.datetime]] = mapped_column(
DateTime, nullable=True
)
# Relationships
icd_codes: Mapped[list[CaseICDCode]] = relationship(
back_populates="case", cascade="all, delete-orphan"
)
updated_by_user: Mapped[Optional["User"]] = relationship(
foreign_keys=[updated_by]
)
icd_entered_by_user: Mapped[Optional["User"]] = relationship(
foreign_keys=[icd_entered_by]
)
coding_completed_by_user: Mapped[Optional["User"]] = relationship(
foreign_keys=[coding_completed_by]
)
__table_args__ = (
UniqueConstraint("fall_id", name="uk_fall_id"),
Index("idx_jahr_kw", "jahr", "kw"),
Index("idx_kvnr", "kvnr"),
Index("idx_fallgruppe", "fallgruppe"),
Index("idx_datum", "datum"),
Index("idx_nachname_vorname", "nachname", "vorname"),
# Note: idx_pending_icd uses a prefix length icd(20) which SQLAlchemy
# does not support natively. We use text() for the prefix column.
Index("idx_pending_icd", "jahr", "kw", "fallgruppe", text("icd(20)")),
Index("idx_pending_coding", "gutachten", "gutachten_typ"),
CheckConstraint(
"fallgruppe IN ('onko','kardio','intensiv','galle','sd')",
name="chk_fallgruppe",
),
CheckConstraint(
"gutachten_typ IS NULL OR gutachten_typ IN ('Bestätigung','Alternative')",
name="chk_gutachten_typ",
),
CheckConstraint(
"therapieaenderung IS NULL OR therapieaenderung IN ('Ja','Nein')",
name="chk_ta",
),
)
class CaseICDCode(Base):
__tablename__ = "case_icd_codes"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
case_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("cases.id", ondelete="CASCADE"),
nullable=False,
)
icd_code: Mapped[str] = mapped_column(String(20), nullable=False)
icd_hauptgruppe: Mapped[Optional[str]] = mapped_column(String(10), nullable=True)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
case: Mapped[Case] = relationship(back_populates="icd_codes")
__table_args__ = (
Index("idx_case", "case_id"),
Index("idx_code", "icd_code"),
Index("idx_haupt", "icd_hauptgruppe"),
)

View file

@ -0,0 +1,189 @@
"""Weekly report and yearly summary models."""
from __future__ import annotations
import datetime as dt
from typing import Optional
from sqlalchemy import (
Date,
DateTime,
ForeignKey,
Index,
Integer,
SmallInteger,
String,
UniqueConstraint,
func,
)
from sqlalchemy.dialects.mysql import JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class WeeklyReport(Base):
__tablename__ = "weekly_reports"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
jahr: Mapped[int] = mapped_column(SmallInteger, nullable=False)
kw: Mapped[int] = mapped_column(SmallInteger, nullable=False)
report_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
report_file_path: Mapped[Optional[str]] = mapped_column(
String(500), nullable=True
)
report_data: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
generated_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
generated_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
generated_by_user: Mapped[Optional["User"]] = relationship(
foreign_keys=[generated_by]
)
__table_args__ = (
UniqueConstraint("jahr", "kw", name="uk_jahr_kw"),
)
class YearlySummary(Base):
__tablename__ = "yearly_summary"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
jahr: Mapped[int] = mapped_column(SmallInteger, nullable=False)
kw: Mapped[int] = mapped_column(SmallInteger, nullable=False)
# Overall counts
erstberatungen: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
ablehnungen: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
unterlagen: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
keine_rueckmeldung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
gutachten_gesamt: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
gutachten_alternative: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
gutachten_bestaetigung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Per-Fallgruppe counts: onko
onko_anzahl: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
onko_gutachten: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
onko_keine_rm: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Per-Fallgruppe counts: kardio
kardio_anzahl: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
kardio_gutachten: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
kardio_keine_rm: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Per-Fallgruppe counts: intensiv
intensiv_anzahl: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
intensiv_gutachten: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
intensiv_keine_rm: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Per-Fallgruppe counts: galle
galle_anzahl: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
galle_gutachten: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
galle_keine_rm: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Per-Fallgruppe counts: sd
sd_anzahl: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
sd_gutachten: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
sd_keine_rm: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Gutachten-Typ per Fallgruppe
onko_alternative: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
onko_bestaetigung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
kardio_alternative: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
kardio_bestaetigung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
intensiv_alternative: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
intensiv_bestaetigung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
galle_alternative: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
galle_bestaetigung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
sd_alternative: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
sd_bestaetigung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
# Therapieaenderung counts
ta_ja: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
ta_nein: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
ta_diagnosekorrektur: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
ta_unterversorgung: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
ta_uebertherapie: Mapped[Optional[int]] = mapped_column(
Integer, server_default="0"
)
__table_args__ = (
UniqueConstraint("jahr", "kw", name="uk_jahr_kw"),
)

162
backend/app/models/user.py Normal file
View file

@ -0,0 +1,162 @@
"""User, authentication, and access control models."""
from __future__ import annotations
from datetime import datetime
from typing import Optional
from sqlalchemy import (
Boolean,
CheckConstraint,
DateTime,
ForeignKey,
Index,
Integer,
String,
UniqueConstraint,
func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.schema import FetchedValue
from app.database import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
username: Mapped[str] = mapped_column(String(100), nullable=False)
email: Mapped[str] = mapped_column(String(255), nullable=False)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
role: Mapped[str] = mapped_column(
String(20), nullable=False, server_default="dak_mitarbeiter"
)
mfa_secret: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
mfa_enabled: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
is_active: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="1"
)
must_change_password: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
failed_login_attempts: Mapped[int] = mapped_column(
Integer, nullable=False, server_default="0"
)
locked_until: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
server_onupdate=FetchedValue(),
)
# Relationships
refresh_tokens: Mapped[list[RefreshToken]] = relationship(
back_populates="user", cascade="all, delete-orphan"
)
invitations_created: Mapped[list[InvitationLink]] = relationship(
foreign_keys="InvitationLink.created_by", back_populates="creator"
)
invitations_used: Mapped[list[InvitationLink]] = relationship(
foreign_keys="InvitationLink.used_by", back_populates="used_by_user"
)
__table_args__ = (
UniqueConstraint("username", name="uk_username"),
UniqueConstraint("email", name="uk_email"),
CheckConstraint(
"role IN ('admin', 'dak_mitarbeiter')", name="chk_role"
),
)
class RefreshToken(Base):
__tablename__ = "refresh_tokens"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("users.id", ondelete="CASCADE"),
nullable=False,
)
token_hash: Mapped[str] = mapped_column(String(255), nullable=False)
expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
revoked: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0"
)
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
user: Mapped[User] = relationship(back_populates="refresh_tokens")
__table_args__ = (
Index("idx_user", "user_id"),
Index("idx_token", "token_hash"),
)
class InvitationLink(Base):
__tablename__ = "invitation_links"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
token: Mapped[str] = mapped_column(String(255), nullable=False)
email: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
role: Mapped[str] = mapped_column(
String(20), nullable=False, server_default="dak_mitarbeiter"
)
created_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
used_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
used_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
is_active: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="1"
)
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
creator: Mapped[Optional[User]] = relationship(
foreign_keys=[created_by], back_populates="invitations_created"
)
used_by_user: Mapped[Optional[User]] = relationship(
foreign_keys=[used_by], back_populates="invitations_used"
)
__table_args__ = (
UniqueConstraint("token", name="uk_token"),
)
class AllowedDomain(Base):
__tablename__ = "allowed_domains"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
domain: Mapped[str] = mapped_column(String(255), nullable=False)
role: Mapped[str] = mapped_column(
String(20), nullable=False, server_default="dak_mitarbeiter"
)
is_active: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="1"
)
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
__table_args__ = (
UniqueConstraint("domain", name="uk_domain"),
)