Converts the ad-hoc scratch-script verification pattern used throughout
this session's development (temp SQLite DB + create_app() + monkeypatched
send_message/fiscalize_in_background/get_invoice_status, print-based
check() assertions) into a real, repo-committed pytest suite under tests/.
Covers: claim_order_paid() atomicity, cross-path payment race dedup
(webhook/checkout-poll/reconciliation all observing the same paid
invoice), fiscalization error alerting + cooldown, admin login
brute-force lockout, the insecure-defaults startup check, supplier-import
path-traversal protection, basic checkout smoke, and the supplier
restock -> stock/supplier-link bug fixed earlier this session.
Adds requirements-dev.txt (pytest only, keeping the existing
monkeypatch-only style rather than a new mocking framework), pytest.ini
so `pytest` runs from the repo root with no flags, and docs/TESTING.md
covering how to run it and what's explicitly not covered yet (the React
webapp, bot conversation flows beyond the checkout API, live
Checkbox/Monobank/Google Drive integrations).
38 tests, all passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Groundwork for reusing this codebase per-client (own VPS/DB/bot per
cafe): the operational config (bot token, DB URL, secrets, Checkbox PRRO
creds) was already .env/Setting-driven, but ~13 spots still hardcoded the
literal string "Bober BBQ" instead of reading the existing cafe_name
Setting, and the webapp's static shell (index.html title, PWA manifest)
had no templating at all since Settings only load after JS boots.
- Route the remaining hardcoded strings through Setting.get("cafe_name",
"Bober BBQ") — same pattern already used correctly in bot/handlers/
start.py. Every fallback stays "Bober BBQ", so production is byte-
identical with no cafe_name override.
- Template webapp/index.html via Vite's native %VITE_CAFE_NAME% HTML
replacement, backed by a committed webapp/.env (default "Bober BBQ",
no secrets) with a per-client override via gitignored .env.local.
- Add webapp/scripts/gen-manifest.mjs to generate manifest.json from a
new manifest.template.json the same way, since Vite doesn't process
public/ assets — wired into the build script.
- Parameterize deploy.sh's systemd unit names and the admin log-viewer's
log paths via optional SERVICE_WEB/SERVICE_BOT/LOG_FILE_WEB/
LOG_FILE_BOT env vars, defaulting to today's literal values.
- Rewrite docs/DEPLOYMENT.md into a repeatable new-client runbook (fixed
an existing /opt/bober-bbq vs /opt/bober-bbq-bot inconsistency, added
a rebranding checklist).
Verified via a smoke test that every changed string still renders
"Bober BBQ" with no overrides present (matching prod's actual .env/DB
state), and that webapp builds both with and without a VITE_CAFE_NAME
override produce the expected output — including a no-override rebuild
confirming byte-identical output to before this change.
New Supplier/SupplierDelivery models track what was delivered, paid,
and owed per supplier. Add/edit/deactivate/delete suppliers manually,
or import an Excel ledger of any layout — the admin maps which column
is which (with an auto-guess, including a fuzzy fallback for typo'd
headers) rather than relying on a fixed format, since real supplier
files vary. Rows with no product name (empty template/totals rows)
are skipped automatically. A delivery can optionally be linked to an
existing Ingredient and top up its stock on entry, opt-in per action.
Admin always has access; managers only if "suppliers_manager_access"
is turned on in Settings — hard delete stays admin-only regardless,
matching the order-delete precedent. Verified end-to-end against a
real supplier ledger file, including its date-format quirk and a
typo'd column header.
Flask API/admin backend, aiogram bot with delivery/pickup FSM flows,
monobank payment integration, and a Vite/React Telegram Mini App for
menu browsing and cart management.