194 lines
6.2 KiB
Python
194 lines
6.2 KiB
Python
# models/schema/customer.py
|
|
"""
|
|
Pydantic schema for customer-related operations.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import Optional, Dict, Any, List
|
|
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: Optional[str] = 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: Optional[EmailStr] = None
|
|
first_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
last_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
phone: Optional[str] = Field(None, max_length=50)
|
|
marketing_consent: Optional[bool] = None
|
|
|
|
@field_validator('email')
|
|
@classmethod
|
|
def email_lowercase(cls, v: Optional[str]) -> Optional[str]:
|
|
"""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: Optional[str]
|
|
last_name: Optional[str]
|
|
phone: Optional[str]
|
|
customer_number: str
|
|
marketing_consent: bool
|
|
last_order_date: Optional[datetime]
|
|
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: Optional[str] = Field(None, max_length=200)
|
|
address_line_1: str = Field(..., min_length=1, max_length=255)
|
|
address_line_2: Optional[str] = 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: Optional[str] = Field(None, pattern="^(billing|shipping)$")
|
|
first_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
last_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
company: Optional[str] = Field(None, max_length=200)
|
|
address_line_1: Optional[str] = Field(None, min_length=1, max_length=255)
|
|
address_line_2: Optional[str] = Field(None, max_length=255)
|
|
city: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
postal_code: Optional[str] = Field(None, min_length=1, max_length=20)
|
|
country: Optional[str] = Field(None, min_length=2, max_length=100)
|
|
is_default: Optional[bool] = 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: Optional[str]
|
|
address_line_1: str
|
|
address_line_2: Optional[str]
|
|
city: str
|
|
postal_code: str
|
|
country: str
|
|
is_default: bool
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
model_config = {
|
|
"from_attributes": True
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# Customer Statistics
|
|
# ============================================================================
|
|
|
|
class CustomerStatsResponse(BaseModel):
|
|
"""Schema for customer statistics."""
|
|
|
|
customer_id: int
|
|
total_orders: int
|
|
total_spent: Decimal
|
|
average_order_value: Decimal
|
|
last_order_date: Optional[datetime]
|
|
first_order_date: Optional[datetime]
|
|
lifetime_value: Decimal
|
|
|
|
|
|
# ============================================================================
|
|
# Customer Preferences
|
|
# ============================================================================
|
|
|
|
class CustomerPreferencesUpdate(BaseModel):
|
|
"""Schema for updating customer preferences."""
|
|
|
|
marketing_consent: Optional[bool] = None
|
|
language: Optional[str] = Field(None, max_length=10)
|
|
currency: Optional[str] = Field(None, max_length=3)
|
|
notification_preferences: Optional[Dict[str, bool]] = None
|