Add Checkbox ETTN receipts for Nova Poshta COD waybills
Cashier creates an ETTN receipt in Checkbox bound to the TTN with payment control; Checkbox fiscalizes it itself when the parcel is paid for. - cash_registers (Fernet-encrypted license key / PIN) and receipts tables - Checkbox HTTP client + stub (ETTN does not work on test registers) - two-phase create via ARQ job, timeout reconciliation, cron status polling - /receipts and /cash-registers API, audit records - dashboard: per-order and bulk create, prepayment, cancel; cash registers page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
"""Постановка задач ARQ worker'у из API.
|
||||
|
||||
Пул Redis создаётся лениво при первой задаче, а не в lifespan: недоступный
|
||||
Redis не должен ронять старт API. Если задачу поставить не удалось, чек
|
||||
остаётся `pending`, и cron `poll_receipts` подхватит его в течение минуты.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Protocol
|
||||
|
||||
from arq import ArqRedis, create_pool
|
||||
from arq.connections import RedisSettings
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.logging import get_logger
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
||||
|
||||
class TaskQueue(Protocol):
|
||||
async def enqueue(self, function: str, *args: Any) -> None: ...
|
||||
|
||||
|
||||
class ArqTaskQueue:
|
||||
def __init__(self) -> None:
|
||||
self._pool: ArqRedis | None = None
|
||||
|
||||
async def enqueue(self, function: str, *args: Any) -> None:
|
||||
try:
|
||||
if self._pool is None:
|
||||
self._pool = await create_pool(
|
||||
RedisSettings.from_dsn(settings.redis_url), retry=0
|
||||
)
|
||||
await self._pool.enqueue_job(function, *args)
|
||||
except Exception as exc: # noqa: BLE001 — см. докстринг модуля
|
||||
log.warning("enqueue_failed", function=function, error=repr(exc))
|
||||
|
||||
async def close(self) -> None:
|
||||
if self._pool is not None:
|
||||
await self._pool.aclose()
|
||||
self._pool = None
|
||||
|
||||
|
||||
_queue = ArqTaskQueue()
|
||||
|
||||
|
||||
def get_task_queue() -> TaskQueue:
|
||||
return _queue
|
||||
|
||||
|
||||
async def close_task_queue() -> None:
|
||||
await _queue.close()
|
||||
Reference in New Issue
Block a user