"""Схемы API ЕТТН-чеков и касс Checkbox.""" from __future__ import annotations import uuid from datetime import datetime from typing import Any from pydantic import BaseModel, ConfigDict, Field from app.core import crypto from app.db.models.cash_register import CashRegister from app.db.models.receipt import Receipt, ReceiptStatus def _money(kopecks: int) -> str: return f"{kopecks / 100:.2f}" class ReceiptOut(BaseModel): id: uuid.UUID order_id: str waybill_number: str status: ReceiptStatus total_amount: str prepayment_amount: str cod_amount: str checkbox_ettn_id: str | None checkbox_status: str | None checkbox_receipt_id: str | None error: str | None created_at: datetime last_checked_at: datetime | None @classmethod def from_receipt(cls, receipt: Receipt) -> ReceiptOut: return cls( id=receipt.id, order_id=receipt.order_id, waybill_number=receipt.waybill_number, status=receipt.status, total_amount=_money(receipt.total_kopecks), prepayment_amount=_money(receipt.prepayment_kopecks), cod_amount=_money(receipt.cod_kopecks), checkbox_ettn_id=receipt.checkbox_ettn_id, checkbox_status=receipt.checkbox_status, checkbox_receipt_id=receipt.checkbox_receipt_id, error=receipt.error, created_at=receipt.created_at, last_checked_at=receipt.last_checked_at, ) class ReceiptRequestItem(BaseModel): order_id: str = Field(min_length=1, max_length=32) # None — предоплата = сумма заказа − наложка НП. prepayment_kopecks: int | None = Field(default=None, ge=0) class ReceiptCreateRequest(BaseModel): items: list[ReceiptRequestItem] = Field(min_length=1, max_length=200) class ReceiptCreateResponse(BaseModel): created: list[ReceiptOut] # order_id → причина, по которой чек не создан. errors: dict[str, str] # --- Кассы ------------------------------------------------------------------- class CashRegisterOut(BaseModel): id: uuid.UUID name: str fiscal_number: str | None license_key_masked: str tax_codes: list[Any] is_active: bool is_default: bool @classmethod def from_register(cls, register: CashRegister) -> CashRegisterOut: try: masked = crypto.mask(crypto.decrypt(register.license_key_enc)) except crypto.DecryptionError: masked = "не расшифровывается — введите заново" return cls( id=register.id, name=register.name, fiscal_number=register.fiscal_number, license_key_masked=masked, tax_codes=register.tax_codes, is_active=register.is_active, is_default=register.is_default, ) class CashRegisterCreate(BaseModel): name: str = Field(min_length=1, max_length=255) fiscal_number: str | None = Field(default=None, max_length=64) license_key: str = Field(min_length=1, max_length=255) pin_code: str = Field(min_length=1, max_length=32) tax_codes: list[int | str] = Field(default_factory=list, max_length=2) is_default: bool = True class CashRegisterUpdate(BaseModel): model_config = ConfigDict(extra="forbid") name: str | None = Field(default=None, min_length=1, max_length=255) fiscal_number: str | None = Field(default=None, max_length=64) license_key: str | None = Field(default=None, min_length=1, max_length=255) pin_code: str | None = Field(default=None, min_length=1, max_length=32) tax_codes: list[int | str] | None = Field(default=None, max_length=2) is_active: bool | None = None is_default: bool | None = None