- CRM_USE_STUB: local runs no longer reach the live CRM. With only the
Checkbox stub, a stub receipt would still move the real order to PACKED.
Refused in production, same as CHECKBOX_USE_STUB.
- scripts/deploy.sh: backup, fast-forward main, build, health check and
code rollback on failure. scripts/backup.sh: pg_dump with verification
and 14-day rotation (used by cron and deploy.sh).
- Gitea Actions CI: ruff + pytest, oxlint + build.
- DEPLOY.md runbook; CLAUDE.md rules for safe local development.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each cash register stores its own encrypted NP API key. Status polling uses
register keys and binds an order to the register whose key sees the TTN as
its own (PhoneSender present); ETTN receipts are created from that register.
Migration 0008 moves the old NOVA_POSHTA_API_KEY into the default register.
Closes#3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GET /orders/summary: count and sum of cash-on-delivery for parcels not
yet picked up (excludes received, refused, deleted/unknown TTNs, paid COD);
shown as a card above the tabs.
- New «Полученные» tab: orders with NP received codes (9, 10, 11, 106) move
there automatically, regardless of receipt; 106 is now a final status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checkbox relays Nova Poshta's "To many requests" (20000401501) as a 4xx
third_party.generic error, not a 429, so bursts of receipt requests ended
up as failed receipts. Such responses are now CheckboxRateLimitedError:
the receipt stays pending and the worker job is retried with arq.Retry
after the "Try again after N seconds" delay plus backoff. NP timeouts
relayed the same way are treated as unavailable (unknown outcome).
The HTTP client also sends Checkbox requests one at a time with a
CHECKBOX_MIN_REQUEST_INTERVAL_MS pause and signs the cashier in once for
concurrent jobs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Orders whose Nova Poshta status is a refusal (102/103/105/108) now show
only under «Отказы», whether or not they have a receipt. GET /orders takes
tab=no_receipt|has_receipt|refused instead of has_receipt. NP statuses are
also polled for orders that already have a receipt, until NP reports a
final status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ExoCrmClient.set_status: live SetStatus needs {Orders: [id], Status} and
replies per order, unlike the documented {ID, Status}
- receipts.crm_status_set_at (migration 0006); set right after creation,
retried by cron, row-locked to avoid a repeat PACKED overwriting a newer status
- CRM errors under capitalized 'Errors' and non-JSON replies are reported
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- prepayment as DISCOUNT: live API rejects PRE_PAYMENT with 400 third_party.generic
- normalize lowercase ETTN statuses returned by the live API
- show Checkbox error code instead of bare 'Internal Server Error'
- page size 50 for ETTN list, 429 rate limit is retryable, pause polling on it
- phone in 380XXXXXXXXX format like portal receipts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>