import re from datetime import datetime from decimal import Decimal from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator def money(value: Decimal) -> str: return f"{value.quantize(Decimal('0.01')):.2f}" class CategoryOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int name: str class ProductOut(BaseModel): id: int category_id: int category_name: str title: str description: str price: Decimal @field_serializer("price") def serialize_price(self, value: Decimal) -> str: return money(value) class CatalogOut(BaseModel): categories: list[CategoryOut] products: list[ProductOut] class CartAdd(BaseModel): product_id: int qty: int = Field(default=1, ge=1, le=99) class CartItemOut(BaseModel): product_id: int title: str price: str qty: int line_total: str class CartOut(BaseModel): items: list[CartItemOut] total: str class OrderCreate(BaseModel): customer_name: str = Field(min_length=1, max_length=120) customer_phone: str = Field(min_length=5, max_length=32) @field_validator("customer_name", "customer_phone", mode="before") @classmethod def strip_value(cls, value: object) -> object: if isinstance(value, str): return value.strip() return value @field_validator("customer_phone") @classmethod def check_phone(cls, value: str) -> str: if re.fullmatch(r"[0-9+()\-\s]{5,32}", value) is None: raise ValueError("Телефон: цифры, пробелы и символы +()-") return value class OrderItemOut(BaseModel): product_id: int title: str price: str qty: int class OrderOut(BaseModel): id: int status: str customer_name: str customer_phone: str total: str created_at: datetime items: list[OrderItemOut] @field_serializer("created_at") def serialize_created_at(self, value: datetime) -> str: return value.isoformat() class DialogIn(BaseModel): step: str = Field(pattern=r"^(name|phone)$") name: str | None = Field(default=None, max_length=120) @field_validator("name", mode="before") @classmethod def strip_name(cls, value: object) -> object: if isinstance(value, str): stripped = value.strip() return stripped or None return value class DialogOut(BaseModel): model_config = ConfigDict(from_attributes=True) telegram_user_id: int step: str name: str | None class BotOrderCreate(OrderCreate): telegram_user_id: int = Field(gt=0) class BotCartAdd(CartAdd): telegram_user_id: int = Field(gt=0)