# models/schema/customer.py """ Pydantic schema for customer-related operations. """ from datetime import datetime from decimal import Decimal from pydantic import BaseModel, EmailStr, Field, field_validator # ============================================================================ # Customer Registration & Authentication # ============================================================================ class CustomerRegister(BaseModel): """Schema for customer registration.""" email: EmailStr = Field(..., description="Customer email address") password: str = Field( ..., min_length=8, description="Password (minimum 8 characters)" ) first_name: str = Field(..., min_length=1, max_length=100) last_name: str = Field(..., min_length=1, max_length=100) phone: str | None = Field(None, max_length=50) marketing_consent: bool = Field(default=False) @field_validator("email") @classmethod def email_lowercase(cls, v: str) -> str: """Convert email to lowercase.""" return v.lower() @field_validator("password") @classmethod def password_strength(cls, v: str) -> str: """Validate password strength.""" if len(v) < 8: raise ValueError("Password must be at least 8 characters") if not any(char.isdigit() for char in v): raise ValueError("Password must contain at least one digit") if not any(char.isalpha() for char in v): raise ValueError("Password must contain at least one letter") return v class CustomerUpdate(BaseModel): """Schema for updating customer profile.""" email: EmailStr | None = None first_name: str | None = Field(None, min_length=1, max_length=100) last_name: str | None = Field(None, min_length=1, max_length=100) phone: str | None = Field(None, max_length=50) marketing_consent: bool | None = None @field_validator("email") @classmethod def email_lowercase(cls, v: str | None) -> str | None: """Convert email to lowercase.""" return v.lower() if v else None # ============================================================================ # Customer Response # ============================================================================ class CustomerResponse(BaseModel): """Schema for customer response (excludes password).""" id: int vendor_id: int email: str first_name: str | None last_name: str | None phone: str | None customer_number: str marketing_consent: bool last_order_date: datetime | None total_orders: int total_spent: Decimal is_active: bool created_at: datetime updated_at: datetime model_config = {"from_attributes": True} @property def full_name(self) -> str: """Get customer full name.""" if self.first_name and self.last_name: return f"{self.first_name} {self.last_name}" return self.email class CustomerListResponse(BaseModel): """Schema for paginated customer list.""" customers: list[CustomerResponse] total: int page: int per_page: int total_pages: int # ============================================================================ # Customer Address # ============================================================================ class CustomerAddressCreate(BaseModel): """Schema for creating customer address.""" address_type: str = Field(..., pattern="^(billing|shipping)$") first_name: str = Field(..., min_length=1, max_length=100) last_name: str = Field(..., min_length=1, max_length=100) company: str | None = Field(None, max_length=200) address_line_1: str = Field(..., min_length=1, max_length=255) address_line_2: str | None = Field(None, max_length=255) city: str = Field(..., min_length=1, max_length=100) postal_code: str = Field(..., min_length=1, max_length=20) country: str = Field(..., min_length=2, max_length=100) is_default: bool = Field(default=False) class CustomerAddressUpdate(BaseModel): """Schema for updating customer address.""" address_type: str | None = Field(None, pattern="^(billing|shipping)$") first_name: str | None = Field(None, min_length=1, max_length=100) last_name: str | None = Field(None, min_length=1, max_length=100) company: str | None = Field(None, max_length=200) address_line_1: str | None = Field(None, min_length=1, max_length=255) address_line_2: str | None = Field(None, max_length=255) city: str | None = Field(None, min_length=1, max_length=100) postal_code: str | None = Field(None, min_length=1, max_length=20) country: str | None = Field(None, min_length=2, max_length=100) is_default: bool | None = None class CustomerAddressResponse(BaseModel): """Schema for customer address response.""" id: int vendor_id: int customer_id: int address_type: str first_name: str last_name: str company: str | None address_line_1: str address_line_2: str | None city: str postal_code: str country: str is_default: bool created_at: datetime updated_at: datetime model_config = {"from_attributes": True} # ============================================================================ # Customer Preferences # ============================================================================ class CustomerPreferencesUpdate(BaseModel): """Schema for updating customer preferences.""" marketing_consent: bool | None = None language: str | None = Field(None, max_length=10) currency: str | None = Field(None, max_length=3) notification_preferences: dict[str, bool] | None = None