"""Схемы 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] # --- Кассы ------------------------------------------------------------------- def _masked(encrypted: str) -> str: try: return crypto.mask(crypto.decrypt(encrypted)) except crypto.DecryptionError: return "не расшифровывается — введите заново" class CashRegisterOut(BaseModel): id: uuid.UUID name: str fiscal_number: str | None license_key_masked: str # None — ключ НП не задан: касса не опрашивает посылки и не получает заказы. np_api_key_masked: str | None tax_codes: list[Any] is_active: bool is_default: bool @classmethod def from_register(cls, register: CashRegister) -> CashRegisterOut: return cls( id=register.id, name=register.name, fiscal_number=register.fiscal_number, license_key_masked=_masked(register.license_key_enc), np_api_key_masked=( _masked(register.np_api_key_enc) if register.np_api_key_enc else None ), 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) np_api_key: str | None = Field(default=None, min_length=1, max_length=255) 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) np_api_key: str | None = Field(default=None, min_length=1, max_length=255) tax_codes: list[int | str] | None = Field(default=None, max_length=2) is_active: bool | None = None is_default: bool | None = None