Files
lauadminandClaude Opus 5.5 b8f2fe6b6e Translate UI and user-facing messages to Ukrainian (#5)
- All frontend pages, labels, notices and errors; html lang=uk, uk-UA money format
- Brand "Assistant System" in the top bar and page title
- Backend error details returned to the UI (auth, orders, receipts,
  cash registers, Checkbox/CRM/NP errors) and CLI output
- Tests updated for the new messages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:27:39 +03:00

128 lines
4.2 KiB
Python

"""Схемы 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