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
+111
View File
@@ -0,0 +1,111 @@
import uuid
from datetime import date, datetime, time
from decimal import Decimal
from pydantic import BaseModel, Field
from app.models.time_entry import EntrySource, EntryStatus
class TimeEntryOut(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
user_id: uuid.UUID
date: date
start_time: time
end_time: time | None
break_minutes: int
break_start: time | None
project_id: uuid.UUID | None
note: str | None
status: EntryStatus
source: EntrySource
approved_by: uuid.UUID | None
correction_note: str | None
worked_hours: float | None
created_at: datetime
updated_at: datetime
class TimeEntryWithWarnings(BaseModel):
entry: TimeEntryOut
warnings: list[str] = []
class StampInRequest(BaseModel):
source: EntrySource = EntrySource.WEB
project_id: uuid.UUID | None = None
note: str | None = None
class StampOutRequest(BaseModel):
note: str | None = None
class ManualEntryCreate(BaseModel):
user_id: uuid.UUID | None = None # MANAGER/HR können für andere setzen
date: date
start_time: time
end_time: time
break_minutes: int = Field(0, ge=0, le=600)
project_id: uuid.UUID | None = None
note: str | None = None
source: EntrySource = EntrySource.MANUAL
class TimeEntryUpdate(BaseModel):
start_time: time | None = None
end_time: time | None = None
break_minutes: int | None = Field(None, ge=0, le=600)
project_id: uuid.UUID | None = None
note: str | None = None
correction_note: str | None = None
class RejectRequest(BaseModel):
rejection_note: str | None = None
class TimeEntryListResponse(BaseModel):
total: int
items: list[TimeEntryOut]
class WorkScheduleOut(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
company_id: uuid.UUID
name: str
mon_h: Decimal
tue_h: Decimal
wed_h: Decimal
thu_h: Decimal
fri_h: Decimal
sat_h: Decimal
sun_h: Decimal
valid_from: date
class WorkScheduleCreate(BaseModel):
name: str = Field(min_length=1, max_length=255)
mon_h: Decimal = Field(Decimal("8.00"), ge=0, le=24)
tue_h: Decimal = Field(Decimal("8.00"), ge=0, le=24)
wed_h: Decimal = Field(Decimal("8.00"), ge=0, le=24)
thu_h: Decimal = Field(Decimal("8.00"), ge=0, le=24)
fri_h: Decimal = Field(Decimal("8.00"), ge=0, le=24)
sat_h: Decimal = Field(Decimal("0.00"), ge=0, le=24)
sun_h: Decimal = Field(Decimal("0.00"), ge=0, le=24)
valid_from: date
class BalanceResponse(BaseModel):
user_id: uuid.UUID
period_start: date
period_end: date
total_hours_worked: float
expected_hours: float
overtime_hours: float
approved_entries: int
pending_entries: int