1fedd683e0
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>
191 lines
5.8 KiB
Python
191 lines
5.8 KiB
Python
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 # Mo–Fr, 06–20 Uhr, kein Feiertag
|
||
night_25_hours: float # 20–24 + 04–06 Uhr (25% Zuschlag)
|
||
night_40_hours: float # 00–04 Uhr (40% Zuschlag)
|
||
sunday_hours: float # Sonntag 00–24 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]
|