Initial commit – TimeMaster Zeiterfassung & HR-Tool

Stand: agent-06 (Audit-Log), agent-05 (Krankmeldung), agent-07 Phase 1 (Personalnummer),
Busylight-Pull-Integration, TOTP/2FA, Abwesenheiten, Zeiterfassung, Kiosk-Grundgerüst.
Migrations 0001–0023 deployed auf 192.168.1.137 + .164.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-05-23 20:03:27 +02:00
commit 1fedd683e0
178 changed files with 29896 additions and 0 deletions
+190
View File
@@ -0,0 +1,190 @@
import uuid
from datetime import date, time
from pydantic import BaseModel
# ── Employee Dashboard ─────────────────────────────────────────────────────────
class EmployeeDashboard(BaseModel):
today_open: bool
today_start: time | None
today_hours_so_far: float | None
week_hours_worked: float
week_hours_expected: float
week_overtime: float
vacation_remaining_days: int | None
vacation_used_days: int
vacation_entitled_days: int
pending_absences: int
overtime_balance_hours: float | None # verfügbares Überstundenguthaben
schedule_name: str | None # zugewiesener Arbeitsplan
# ── Team Dashboard ─────────────────────────────────────────────────────────────
class TeamMemberStatus(BaseModel):
user_id: uuid.UUID
user_name: str
department: str | None
status: str # "present" | "on_leave" | "absent"
absence_type: str | None
time_in: time | None
hours_today: float | None
class TeamDashboard(BaseModel):
present_count: int
on_leave_count: int
absent_count: int
pending_time_approvals: int
pending_absence_approvals: int
members: list[TeamMemberStatus]
# ── Company Dashboard ──────────────────────────────────────────────────────────
class UpcomingAbsence(BaseModel):
user_id: uuid.UUID
user_name: str
absence_type: str
start_date: date
end_date: date
working_days: float
class CompanyDashboard(BaseModel):
total_employees: int
active_today: int
attendance_rate: float
month_hours_worked: float
month_hours_expected: float
month_overtime: float
pending_time_approvals: int
pending_absence_approvals: int
upcoming_absences: list[UpcomingAbsence]
# ── Time Report ────────────────────────────────────────────────────────────────
class HoursBreakdown(BaseModel):
"""Stunden-Aufteilung nach §3b EStG für den Steuerberater."""
normal_hours: float # MoFr, 0620 Uhr, kein Feiertag
night_25_hours: float # 2024 + 0406 Uhr (25% Zuschlag)
night_40_hours: float # 0004 Uhr (40% Zuschlag)
sunday_hours: float # Sonntag 0024 Uhr (50% Zuschlag)
holiday_125_hours: float # gesetzl. Feiertag (125% Zuschlag)
holiday_150_hours: float # besondere Feiertage 25.12, 26.12, 01.05 etc. (150%)
holiday_name: str | None # Name des Feiertags falls zutreffend
class TimeReportRow(BaseModel):
date: date
user_id: uuid.UUID
user_name: str
personnel_number: str | None = None
department: str | None
start_time: time
end_time: time | None
break_minutes: int
worked_hours: float | None
status: str
source: str
note: str | None
breakdown: HoursBreakdown | None = None # None wenn kein Bundesland konfiguriert
class TimeReport(BaseModel):
date_from: date
date_to: date
total_rows: int
total_hours: float
rows: list[TimeReportRow]
# ── Absence Report ─────────────────────────────────────────────────────────────
class AbsenceReportRow(BaseModel):
user_id: uuid.UUID
user_name: str
personnel_number: str | None = None
department: str | None
absence_type: str
start_date: date
end_date: date
working_days: float
status: str
note: str | None
class AbsenceReport(BaseModel):
date_from: date
date_to: date
total_rows: int
total_days: float
rows: list[AbsenceReportRow]
# ── Overtime Report ────────────────────────────────────────────────────────────
class OvertimeReportRow(BaseModel):
user_id: uuid.UUID
user_name: str
personnel_number: str | None = None
department: str | None
hours_worked: float
hours_expected: float
overtime_hours: float
class OvertimeReport(BaseModel):
date_from: date
date_to: date
total_employees: int
total_overtime: float
rows: list[OvertimeReportRow]
# ── Overtime Detail Report (Option A: Inline-Expand) ──────────────────────────
class DayEntry(BaseModel):
"""Einzelner Zeiteintrag innerhalb eines Tages (mehrere möglich)."""
start_time: time
end_time: time
break_minutes: int
hours_worked: float
status: str
arbzg_warnings: list[str] = []
breakdown: HoursBreakdown | None = None
class OvertimeDay(BaseModel):
date: date
weekday: str # "Mo", "Di", …
hours_worked: float # Summe aller Einträge des Tages
hours_expected: float
overtime: float
entries: list[DayEntry] = [] # leer = kein Eintrag an dem Tag
class OvertimeWeek(BaseModel):
week_nr: int
week_start: date
week_end: date
hours_worked: float
hours_expected: float
overtime: float
days: list[OvertimeDay]
class OvertimeReportRowDetailed(OvertimeReportRow):
weeks: list[OvertimeWeek] = []
arbzg_violation_days: int = 0 # Tage > 10h
special_hours_total: HoursBreakdown | None = None
class OvertimeReportDetailed(BaseModel):
date_from: date
date_to: date
total_employees: int
total_overtime: float
rows: list[OvertimeReportRowDetailed]