Recipe and cost manager for bars, pubs, cafés and restaurants — store your recipes (drinks and dishes) once, scale any pour to N servings, see real per-drink cost and ABV, price the menu honestly, and never lose track of what the shelf is doing.
Built by Vitor Vareiro. European Portuguese read this in Português.
| Summary — attention first | Recipe with derived cost & margin |
|---|---|
![]() |
![]() |
| Stock-take — count, order list, trends | Orders — POs, receiving, price drift |
![]() |
![]() |
| Menu — priced, printable, QR | Sales — actual GP and shrinkage |
![]() |
![]() |
Desktop captures of the demo dataset (48 items, a full month of counts and
sales). Run it yourself with ops/seed_argo.py --month — see Quick start.
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8777 # http://localhost:8777Docker: docker compose up -d --build (port 8780, data in ./data).
First visit sets your owner PIN — the app stays open until you do, by design.
Want to see it full of real data? Seed a month and poke around:
BARSPEC_DB=/tmp/argo-demo.db .venv/bin/python ops/seed_argo.py --month
BARSPEC_DB=/tmp/argo-demo.db .venv/bin/python -m uvicorn main:app --port 8791- Recipes: name, glass, method, garnish. Create / edit / duplicate / delete. Batch pours link to homemade syrups; allergens (EU-14) and dietary tags (V/VE/GF) ride along with badges.
- Stock: one line per real item —
Campari €19 / 700 ml, coffee€18 / 1 kg, limes€3.60 / 12 pc. Change a price once and every recipe using it recalculates — the impact report says which ones and by how much, even through batches. - Honest costing math: cost is derived, never stored. Volume/weight/ piece with yield %, dilution by ice, ABV weighted by volume. Price suggestions round up to €0.50 so the real margin never dips below your target (an invariant with a test).
- Batches (house syrups, prep): cost per litre derives from the stock
lines inside;
servings= real cost per portion on the prep sheet. - Stock-take: count in full bottles + open fractions, dated snapshots — trends, FBE/order list, dead-stock € (cash asleep on the shelf), "cash tied up" above par.
- Kitchen lane: loss log (signed adjustments with reasons), Sections P&L with margin chips, allergens, suppliers — the order list groups by supplier.
- Menu: priced, printable, grouped by section, venue name + IVA footer, QR deep-link share.
- Sales → actual GP & shrinkage: enter what you sold per spec per day; re-posting a day replaces it. Price/cost are snapshots frozen at posting (invoice semantics — future price changes never rewrite past GP). Shrinkage compares stock used between your last two counts vs what your sales explain — the leak in € is the headline number.
- Purchase orders + receiving: order by supplier with unit prices frozen at order time; receiving records the delivery, logs it, and flags price drift (stored cost vs invoice) with one-click apply + ripple. Partial receive keeps the PO open. Every received line feeds the item's price history. POs are the money trail — counts stay the owners of physical stock. Migration 015.
- Summary (attention page): one glance on every visit — items below par from the latest count (with suppliers), batches expiring within a week, losses this month in €, and a "count again" nudge when the last count is older than 7 days. Cards jump straight to the relevant view.
- First-run onboarding: an empty venue opens into a 3-step wizard (add stock → create a recipe → set a price) with direct action buttons.
- Exports & share: spec book / stock as .xlsx + .csv, training cards (print a spec deck — never a cost), menu QR.
- Owner security: first run asks for a PIN (pbkdf2-hashed, never stored plaintext). After that everything is locked behind a signed session cookie (14 days). Audit trail: every price edit and delete is logged old → new with a timestamp (append-only).
- Staff mode: the owner can enable a staff PIN (Settings). Staff see recipes and the menu — money is removed server-side (costs, prices, margins stripped from the JSON, not just hidden in the UI). Stock, counts, sales, exports and every write return 403 to staff.
- PT-PT: the whole UI is bilingual EN/PT (menu prices in PT format €19,00), text size A−/A/A+, responsive mobile layout with a floor-friendly bottom bar.
- Backups: nightly online sqlite snapshot, 14 kept, with a tested restore script (local only by design).
Python + FastAPI + SQLite + vanilla JS (no build step, no ORM). One data file, one process, no external services. Full architecture rationale lives in the Developer Guide; the why behind the design (derived costs, frozen snapshots, why POs don't touch stock) is in docs/WHY.md.
pytest # 185 tests: pricing, migrations, API, counts, units, batches, yield, categories, dilution, kitchen, reports, allergens, suppliers, audit, auth (incl. brute-force brake), staff, sales, dashboard, purchase orders, stats
npm run e2e # real-browser smoke (Playwright, 7 flows / 20 assertions): PIN, recipes, PT-PT, stock filter, sales view, menu
.venv/bin/python ops/docs-check.py # docs numbers vs reality (tests/migrations/e2e)The migration tests build a real v0 database and upgrade it — if they pass,
every future venue file upgrades safely. Money tests cover cent-rounding,
unpriced bottles, price-ripple, FBE/par/order math and sales snapshots. The
e2e suite boots the real app on a throwaway DB and drives the owner flows in
headless Chromium (npm install + the Playwright cache in
~/.cache/ms-playwright).
001_stock.sql— normalize ingredients → stock_items + spec_lines; price_eur + target_gp on specs002_stocktake.sql— par levels + dated count snapshots003_units.sql— dimension (volume|weight|count), units engine004_batches.sql— homemade batches; exactly-one price source CHECK005_yield.sql— yield_frac (usable ÷ bought)006_categories.sql— menu sections; partial PUTs never wipe them007_dilution.sql— ice dilution % (served volume/ABV; cost unchanged)008_settings.sql— venue profile (name, IVA %)009_kitchen.sql— servings on batches + stock adjustments (loss log)010_allergens.sql— EU-14 + V/VE/GF code lists on specs011_supplier.sql— supplier per stock item012_audit.sql— append-only audit trail (old → new, when)013_sales.sql— daily sales by (day, spec), frozen snapshots014_packs.sql— purchase packs (case/keg) on stock items015_purchase_orders.sql— PO + receiving ledger (price history) — daily sales per (day, spec); price/cost frozen at posting
DB file: barspec.db (override with BARSPEC_DB=/path for tests). Online
snapshots under backups/ (nightly 03:17, 14 kept); restore:
sudo ops/restore.sh backups/barspec-*.db.
| Method | Path | Purpose |
|---|---|---|
| GET/POST/PUT/DELETE | /api/specs, /api/specs/{id}, /api/specs/{id}/lines |
recipes, lines, duplicate |
| GET/POST/PUT/DELETE | /api/stock, /api/stock/{id} |
items, price ripple, supplier |
| PATCH | /api/stock/{id}/par |
count target |
| GET/POST/PUT/DELETE | /api/batches, /api/batches/{id}, lines |
house syrups & prep |
| GET/POST | /api/stock-takes, /api/stock-takes/{sheet|last|trends} |
count snapshots, order list, movement/dead stock |
| POST | /api/stock-adjustments |
loss log (signed amounts + reason) |
| GET | /api/menu |
printable priced menu |
| GET/PUT | /api/settings |
venue profile (name, IVA) |
| GET | /api/report/pnl |
Sections P&L + dead stock |
| GET | /api/export/specs.xlsx|.csv, /api/export/stock.xlsx|.csv |
owner files (costs included deliberately) |
| GET | /api/export/menu-qr.svg?url=… |
menu QR SVG |
| GET | /api/audit |
recent audit trail lines |
| POST/GET/DELETE | /api/sales, /api/sales/{id} |
post/list/delete a sales day |
| GET | /api/sales/summary?from_day&to_day |
actual GP per spec + totals |
| GET | /api/sales/shrinkage |
stock-vs-sales leak in € (last two counts) |
| GET | /api/dashboard |
attention summary: below-par, expiring, losses €, count age |
| POST | /api/pos |
open a PO (prices frozen) · GET /api/pos · POST /api/pos/{id}/receive (partial ok, drift report) · GET /api/stock/{id}/price-history |
| GET/POST/PUT | /api/auth/status|setup|login|logout|staff-login|staff-pin |
owner + staff PIN gate (protected routes 401 without a cookie; staff 403 outside read-only) |
Live service runs under systemd as system Python (/usr/bin/python3 —
SELinux blocks the repo venv, so live deps install via dnf; the venv is for
tests/dev). Deploys: snapshot the DB, restart, verify :8777, push. A
health watchdog (ops/healthcheck.sh, Hermes cron every 10 min) is
silent while the app answers and alerts if :8777 goes down. Build history:
CHANGELOG.md.
A complete fictional venue so nothing real is borrowed: a neighbourhood bar with a kitchen — 14 classic cocktails, draught and canned beer, wines by the glass and the bottle, sodas, coffee, and a short snack menu. 46 sellable items, 59 stock lines, and a full month of use behind them.
BARSPEC_DB=/tmp/barspec-demo.db .venv/bin/python ops/seed_demo.py --month
BARSPEC_DB=/tmp/barspec-demo.db .venv/bin/python -m uvicorn main:app --port 8791
# owner PIN 1234 · staff PIN 2468 (money-free staff screen)--month fabricates 30 days of real use (deterministic, rerun-safe):
6 weekly counts, daily sales for the whole month, 3 dated house batches
(simple syrup, ginger syrup, grenadine cordial — cocktails pour from them),
dated losses in the loss log, suppliers, pars, one PO received and one open.
The printed menu carries the venue name and the 23% IVA footer.
Reset the demo any time with ops/demo_reset.sh (fresh, deterministic
month) and rehearse with the 10-minute demo script
— the three moments, in order, with what to say.
ops/seed_argo.py still ships as an extra: a real public menu (The Argo,
Vilamoura) solved to its declared ABVs — useful when you want to demo with a
premium cocktail list instead of the fictional one.
Phase A complete (counting, units engine, batches, costing precision, PT-PT),
kitchen K1–K4, security S1/S2, sales & shrinkage, staff roles — all shipped
(2026-09-09, 185 tests). Phases B/C (multi-venue, VPS + Caddy, hosted
multi-tenant, PWA) are deliberately gated on a real paying venue. The full
product plan lives in the vault (Projects/Bar-Tech-Venture/ BarSpec-Vision-and-Dev-Plan.md).
GNU AGPL-3.0 — see LICENSE. You may use, study, modify and self-host BarSpec freely; if you run a modified version as a network service, the AGPL requires you to publish your changes.
Commercial license available on request — if you want to embed BarSpec in a closed product or offer it as a hosted service without the AGPL's source-disclosure obligations, contact me for a commercial license (dual-licensing; I hold the copyright).





