import uuid from datetime import datetime from pydantic import BaseModel, EmailStr, Field, model_validator from app.models.user import AuthProvider, UserRole PERSONNEL_NUMBER_PATTERN = r"^[0-9]+$" class UserOut(BaseModel): model_config = {"from_attributes": True} id: uuid.UUID company_id: uuid.UUID | None department_id: uuid.UUID | None email: str first_name: str last_name: str full_name: str role: UserRole auth_provider: AuthProvider is_active: bool last_login: datetime | None created_at: datetime kuerzel: str | None = None personnel_number: str | None = None can_manual_time_entry: bool = False class UserUpdate(BaseModel): first_name: str | None = Field(None, min_length=1, max_length=100) last_name: str | None = Field(None, min_length=1, max_length=100) department_id: uuid.UUID | None = None role: UserRole | None = None work_schedule_id: uuid.UUID | None = None kuerzel: str | None = Field(None, max_length=20) personnel_number: str | None = Field(None, max_length=50, pattern=PERSONNEL_NUMBER_PATTERN) can_manual_time_entry: bool | None = None is_active: bool | None = None class InviteRequest(BaseModel): email: EmailStr first_name: str = Field(min_length=1, max_length=100) last_name: str = Field(min_length=1, max_length=100) role: UserRole = UserRole.EMPLOYEE department_id: uuid.UUID | None = None personnel_number: str | None = Field(None, max_length=50, pattern=PERSONNEL_NUMBER_PATTERN) # Wenn gesetzt → User wird sofort aktiv (kein Invite-E-Mail nötig) initial_password: str | None = Field(None, min_length=8, max_length=128) @model_validator(mode="after") def password_strength(self): pw = self.initial_password if pw is None: return self if not any(c.isupper() for c in pw): raise ValueError("initial_password must contain at least one uppercase letter") if not any(c.isdigit() for c in pw): raise ValueError("initial_password must contain at least one digit") return self class InviteAccept(BaseModel): token: str password: str = Field(min_length=8, max_length=128) @model_validator(mode="after") def password_strength(self): pw = self.password if not any(c.isupper() for c in pw): raise ValueError("Password must contain at least one uppercase letter") if not any(c.isdigit() for c in pw): raise ValueError("Password must contain at least one digit") return self class UserListResponse(BaseModel): total: int items: list[UserOut] class SetKioskPinRequest(BaseModel): pin: str = Field(min_length=4, max_length=6, pattern=r"^\d+$") class NextPersonnelNumberResponse(BaseModel): next: str class UserImportRowError(BaseModel): row: int email: str | None = None message: str class UserImportRowResult(BaseModel): row: int email: str personnel_number: str | None = None action: str # "created" | "reactivated" | "skipped" | "error" message: str | None = None class UserImportResult(BaseModel): total_rows: int created: int reactivated: int errors: int items: list[UserImportRowResult]