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
This commit is contained in:
2026-08-06 22:19:54 +02:00
co-authored by Claude Sonnet 5
parent f3ed234e56
commit 1dcc04ae07
10 changed files with 901 additions and 2 deletions
+42
View File
@@ -0,0 +1,42 @@
"""TEMPLATE Mustervorlage für Pydantic-v2-Schemas.
Anleitung:
1. Nach app/schemas/<name>.py kopieren, XxxThing ersetzen
2. Computed-Felder (z.B. *_name) im Router nachträglich befüllen, nie im Schema
selbst berechnen (Router = einzige Stelle mit DB-Zugriff für den Response-Bau)
"""
import uuid
from datetime import datetime
from pydantic import BaseModel, Field
class XxxThingCreate(BaseModel):
user_id: uuid.UUID
note: str | None = Field(None, max_length=500)
class XxxThingReject(BaseModel):
rejection_reason: str | None = Field(None, max_length=500)
class XxxThingOut(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
company_id: uuid.UUID
user_id: uuid.UUID
user_name: str = "" # Computed im Router (first_name + last_name)
note: str | None
status: str = "requested"
rejection_reason: str | None = None
created_by: uuid.UUID
created_by_name: str = "" # Computed im Router
decided_by: uuid.UUID | None = None
decided_by_name: str = "" # Computed im Router
decided_at: datetime | None = None
created_at: datetime
class XxxThingListResponse(BaseModel):
items: list[XxxThingOut]
total_count: int