Files
timemaster/backend/templates/model_template.py
T
patrickandClaude Sonnet 5 1dcc04ae07 feat(reports): DATEV-Monatsblatt-Export (Blanko-Vorlage) für Steuerberater
Neuer PDF-Export GET /reports/datev-monthly/export: ein Blatt pro
Mitarbeiter/Monat im Layout der DATEV-Stundenaufzeichnungs-Vorlage
(Beginn/Pause/Ende/Dauer + K/U/UU/F/SA/SU-Kürzel + Bemerkungen +
Summe + Unterschriftsfelder). Führt time_entries, Absences und
Feiertage pro Kalendertag zusammen.

Außerdem: Mustervorlagen für neue Backend-Module (Model/Schema/Router/
Migration/Test) und eine Frontend-Page-Vorlage, abgeleitet vom
hours_payouts-Modul als aktuellstem sauberen Muster.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CviFgc739S89xS97pvjszj
2026-08-06 22:19:54 +02:00

68 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""TEMPLATE Mustervorlage für ein neues Model.
Anleitung:
1. Datei nach app/models/<name>.py kopieren
2. XxxThing, xxx_things, xxx_thing_id konsequent ersetzen
3. Enum/Felder an das reale Fachmodell anpassen
4. In app/models/__init__.py importieren (Alembic autodiscovery)
5. RLS-Policy in der zugehörigen Migration NICHT vergessen (company_id-Fenced)
siehe conftest.py, dort muss die Policy für Tests manuell repliziert werden.
"""
import enum
import uuid
from datetime import datetime
from typing import TYPE_CHECKING
from sqlalchemy import DateTime, ForeignKey, String, Text, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.database import Base
if TYPE_CHECKING:
from app.models.user import User
from app.models.company import Company
class XxxThingStatus(str, enum.Enum):
REQUESTED = "requested"
APPROVED = "approved"
REJECTED = "rejected"
CANCELLED = "cancelled"
class XxxThing(Base):
"""Kurzbeschreibung was dieses Model fachlich abbildet."""
__tablename__ = "xxx_things"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
# Mandanten-Fenced Pflicht für RLS-Isolation (DSGVO)
company_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("companies.id", ondelete="CASCADE"),
nullable=False, index=True
)
user_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"),
nullable=False, index=True
)
note: Mapped[str | None] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(12), nullable=False, default=XxxThingStatus.REQUESTED.value)
created_by: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=False
)
decided_by: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL")
)
decided_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), index=True
)
user: Mapped["User"] = relationship("User", foreign_keys=[user_id], lazy="noload")
creator: Mapped["User"] = relationship("User", foreign_keys=[created_by], lazy="noload")
decider: Mapped["User"] = relationship("User", foreign_keys=[decided_by], lazy="noload")
company: Mapped["Company"] = relationship("Company", lazy="noload")