Files

149 lines
4.8 KiB
Python
Raw Permalink Normal View History

"""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