"""Схемы заказов CRM. Поля валидируются напрямую из "сырых" PascalCase-ключей ответа CRM через `Field(alias=...)`, а наружу (в JSON фронту) отдаются как чистые snake_case имена. Денежные значения остаются строками "как есть" от CRM — конвенция integer-kopecks из CLAUDE.md относится к будущим персистентным моделям `Order`/`Receipt`, а не к этому read-only проксирующему эндпоинту. """ from __future__ import annotations from datetime import datetime from typing import TYPE_CHECKING, Any from pydantic import BaseModel, ConfigDict, Field, field_validator if TYPE_CHECKING: from app.db.models.order import Order class OrderGoodOut(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str = Field(alias="ID") sku: str = Field(alias="SKU") name: str = Field(alias="Name") price: str = Field(alias="Price") quantity: str = Field(alias="Quantity") discount_amount: str | None = Field(default=None, alias="DiscountAmount") discount_percent: str | None = Field(default=None, alias="DiscountPercent") amount: str = Field(alias="Amount") class OrderTotalOut(BaseModel): model_config = ConfigDict(populate_by_name=True) cost: str = Field(alias="Cost") quantity: str = Field(alias="Quantity") weight: str = Field(alias="Weight") discount_amount: str = Field(alias="DiscountAmount") discount_percent: str = Field(alias="DiscountPercent") amount: str = Field(alias="Amount") # CRM возвращает Quantity/Weight то числом, то строкой в зависимости от # заказа — приводим к строке единообразно для фронта. @field_validator("quantity", "weight", mode="before") @classmethod def _stringify(cls, value: Any) -> Any: return str(value) if value is not None else value class OrderOut(BaseModel): model_config = ConfigDict(populate_by_name=True) id: str = Field(alias="ID") create_date_time: str = Field(alias="CreateDateTime") recipient_name: str | None = Field(default=None, alias="RecipientDName") recipient_phone: str | None = Field(default=None, alias="RecipientPhone") recipient_email: str | None = Field(default=None, alias="RecipientEmail") waybill_number: str | None = Field(default=None, alias="Waybill_Number") notes: str | None = Field(default=None, alias="Notes") total: OrderTotalOut = Field(alias="Total") goods: list[OrderGoodOut] = Field(default_factory=list, alias="Goods") class OrderRowOut(BaseModel): """Ответ `GET /orders` — строится из локальной таблицы `orders`, не из CRM напрямую.""" model_config = ConfigDict(from_attributes=True) id: str create_date_time: datetime recipient_name: str | None recipient_phone: str | None recipient_email: str | None waybill_number: str | None notes: str | None total_amount: str goods: list[OrderGoodOut] has_receipt: bool @classmethod def from_order(cls, order: Order) -> OrderRowOut: return cls( id=order.id, create_date_time=order.create_date_time, recipient_name=order.recipient_name, recipient_phone=order.recipient_phone, recipient_email=order.recipient_email, waybill_number=order.waybill_number, notes=order.notes, total_amount=f"{order.total_amount_kopecks / 100:.2f}", goods=[OrderGoodOut.model_validate(good) for good in order.goods], has_receipt=order.receipt_created_at is not None, )