import uuid from datetime import date, datetime from pydantic import BaseModel, Field from app.models.absence import AbsenceStatus # ── AbsenceType ─────────────────────────────────────────────────────────────── class AbsenceTypeOut(BaseModel): model_config = {"from_attributes": True} id: uuid.UUID company_id: uuid.UUID name: str color: str requires_approval: bool deducts_vacation: bool is_paid: bool max_days_per_year: int | None is_active: bool class AbsenceTypeCreate(BaseModel): name: str = Field(min_length=1, max_length=255) color: str = Field("#3B82F6", pattern=r"^#[0-9A-Fa-f]{6}$") requires_approval: bool = True deducts_vacation: bool = False is_paid: bool = True max_days_per_year: int | None = Field(None, ge=1) class AbsenceTypeUpdate(BaseModel): name: str | None = Field(None, min_length=1, max_length=255) color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$") requires_approval: bool | None = None deducts_vacation: bool | None = None is_paid: bool | None = None max_days_per_year: int | None = Field(None, ge=1) is_active: bool | None = None # ── Absence ─────────────────────────────────────────────────────────────────── class AbsenceOut(BaseModel): model_config = {"from_attributes": True} id: uuid.UUID user_id: uuid.UUID type_id: uuid.UUID start_date: date end_date: date half_day_start: bool half_day_end: bool working_days: float status: AbsenceStatus approved_by: uuid.UUID | None substitute_id: uuid.UUID | None note: str | None correction_note: str | None rejection_reason: str | None created_at: datetime class AbsenceCreate(BaseModel): type_id: uuid.UUID start_date: date end_date: date half_day_start: bool = False half_day_end: bool = False substitute_id: uuid.UUID | None = None note: str | None = None for_user_id: uuid.UUID | None = None # HR/Admin: Abwesenheit für anderen Mitarbeiter anlegen def model_post_init(self, __context) -> None: if self.end_date < self.start_date: raise ValueError("end_date must be >= start_date") class AbsenceUpdate(BaseModel): type_id: uuid.UUID | None = None start_date: date | None = None end_date: date | None = None half_day_start: bool | None = None half_day_end: bool | None = None substitute_id: uuid.UUID | None = None note: str | None = None correction_note: str | None = None # Pflicht bei Änderung genehmigter Anträge (Mitarbeiter) def model_post_init(self, __context) -> None: if self.start_date and self.end_date and self.end_date < self.start_date: raise ValueError("end_date must be >= start_date") class AbsenceReject(BaseModel): rejection_reason: str = Field(min_length=1) class AbsenceListResponse(BaseModel): total: int items: list[AbsenceOut] # ── VacationBalance ─────────────────────────────────────────────────────────── class VacationBalanceOut(BaseModel): model_config = {"from_attributes": True} id: uuid.UUID user_id: uuid.UUID year: int entitled_days: int carried_over: int used_days: int remaining_days: int pending_days: float = 0 # Ausstehende Anträge (noch nicht genehmigt) # ── PublicHoliday ───────────────────────────────────────────────────────────── class PublicHolidayOut(BaseModel): model_config = {"from_attributes": True} id: uuid.UUID country: str state: str | None date: date name: str year: int class PublicHolidayCreate(BaseModel): country: str = Field("DE", min_length=2, max_length=10) state: str | None = Field(None, max_length=10) date: date name: str = Field(min_length=1, max_length=255) # ── OvertimeBalance ─────────────────────────────────────────────────────────── class OvertimeBalanceOut(BaseModel): total_hours: float taken_hours: float available_hours: float # ── Calendar ────────────────────────────────────────────────────────────────── class CalendarEntry(BaseModel): user_id: uuid.UUID user_name: str absence_id: uuid.UUID type_name: str type_color: str start_date: date end_date: date status: AbsenceStatus working_days: float