"""Pydantic schemas for contact-related request and response bodies.""" import uuid from datetime import datetime from typing import Literal, Optional from pydantic import BaseModel, ConfigDict, Field, field_validator from app.utils.ust_validation import validate_vat_id ContactRole = Literal["kaeufer", "verkaeufer", "beide"] VatIdStatus = Literal["ungeprueft", "geprueft", "ungueltig", "manuell_bestaetigt"] class ContactPersonBase(BaseModel): """Base contact person fields.""" name: str = Field(..., min_length=1, max_length=255) function: Optional[str] = Field(None, max_length=100) phone: Optional[str] = Field(None, max_length=50) email: Optional[str] = Field(None, max_length=255) class ContactPersonCreate(ContactPersonBase): """POST /api/v1/contacts/:id/persons request body.""" pass class ContactPersonResponse(ContactPersonBase): """Contact person response schema.""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID contact_id: uuid.UUID created_at: Optional[datetime] = None class ContactBase(BaseModel): """Base contact fields shared across schemas.""" company_name: str = Field(..., min_length=1, max_length=255) legal_form: Optional[str] = Field(None, max_length=50) address_street: Optional[str] = Field(None, max_length=255) address_zip: Optional[str] = Field(None, max_length=10) address_city: Optional[str] = Field(None, max_length=100) address_country: str = Field("DE", min_length=2, max_length=2) vat_id: Optional[str] = Field(None, max_length=20) phone: Optional[str] = Field(None, max_length=50) email: Optional[str] = Field(None, max_length=255) website: Optional[str] = Field(None, max_length=255) role: ContactRole is_private: bool = False @field_validator("vat_id") @classmethod def validate_vat_id_format(cls, v: str | None) -> str | None: """Validate VAT ID format if provided.""" if v is None or v == "": return None if not validate_vat_id(v): raise ValueError(f"Invalid VAT ID format: '{v}'") return v.strip().upper().replace(" ", "") @field_validator("address_country") @classmethod def validate_country_code(cls, v: str) -> str: """Normalise country code to uppercase.""" return v.upper() class ContactCreate(ContactBase): """POST /api/v1/contacts request body.""" contact_persons: list[ContactPersonCreate] = Field( default_factory=list, description="Contact persons to create with the contact" ) class ContactUpdate(BaseModel): """PUT /api/v1/contacts/:id request body (all fields optional).""" company_name: Optional[str] = Field(None, min_length=1, max_length=255) legal_form: Optional[str] = Field(None, max_length=50) address_street: Optional[str] = Field(None, max_length=255) address_zip: Optional[str] = Field(None, max_length=10) address_city: Optional[str] = Field(None, max_length=100) address_country: Optional[str] = Field(None, min_length=2, max_length=2) vat_id: Optional[str] = Field(None, max_length=20) phone: Optional[str] = Field(None, max_length=50) email: Optional[str] = Field(None, max_length=255) website: Optional[str] = Field(None, max_length=255) role: Optional[ContactRole] = None is_private: Optional[bool] = None @field_validator("vat_id") @classmethod def validate_vat_id_format(cls, v: str | None) -> str | None: """Validate VAT ID format if provided.""" if v is None or v == "": return None if not validate_vat_id(v): raise ValueError(f"Invalid VAT ID format: '{v}'") return v.strip().upper().replace(" ", "") @field_validator("address_country") @classmethod def validate_country_code(cls, v: str | None) -> str | None: """Normalise country code to uppercase.""" if v is not None: return v.upper() return v class ContactResponse(BaseModel): """Contact response schema.""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID company_name: str legal_form: Optional[str] = None address_street: Optional[str] = None address_zip: Optional[str] = None address_city: Optional[str] = None address_country: str vat_id: Optional[str] = None phone: Optional[str] = None email: Optional[str] = None website: Optional[str] = None role: str vat_id_status: str = "ungeprueft" is_private: bool = False created_at: Optional[datetime] = None updated_at: Optional[datetime] = None deleted_at: Optional[datetime] = None contact_persons: list[ContactPersonResponse] = Field(default_factory=list) class ContactListResponse(BaseModel): """Paginated contact list response.""" items: list[ContactResponse] total: int page: int page_size: int