Infrastructure needed to bring up an instance of quicktill.tillweb,
plus the public-facing web pages for https://bar.emf.camp/.
This is the EMF-specific fork of the project and contains assumptions about how the EMF till is configured. The generic upstream version is here.
The infrastructure code in tillweb/config has been modified as
little as possible. Most EMF-specific code is in emf/.
You need poetry and npm installed. You need a postgresql database available.
The package reads ~/.config/emftillweb.toml at startup. Example
suitable for development:
[django]
time_zone = "Europe/London"
mode = "devel"
[till]
database_name = "emfcamp"
currency_symbol = "£"
site_name = "EMF Bars"
[kiosk.tokens.my-dev-token] # section key IS the bearer token — use random string in prod
locations = ["Spacebar"]
source = "spacebar-kiosk-1" # human-readable label for audit logs
user = "kiosk"The optional kiosk.tokens section configures bearer tokens for the
kiosk order API. Each token is scoped to one or more stockline locations.
user is the quicktill user under whose name kiosk transactions are recorded.
locations must exactly match the KIOSK_LOCATION env var on the kiosk and
the location name assigned to stocklines in the database.
Per-token limits (all optional):
[kiosk.tokens.my-badge-token]
locations = ["Spacebar"]
source = "badge"
user = "kiosk"
timeout = 120 # order TTL in seconds (default: 900)
max_items = 1 # max quantity per order
rate_limit = 300 # min seconds between orders from same IPThe shared barcode secret (used to generate and verify HMAC check digits on order barcodes):
[kiosk]
barcode_secret = "<random-secret>" # shared with quicktill-spacebar-pluginTo set up a development environment, ensure you have poetry
installed, and then in the project root run poetry install and npm install.
Ensure there is a postgresql database called emfcamp. (createdb emfcamp if unsure.) Either restore an existing quicktill database
dump into it (zcat emfcamp.sql.gz | psql emfcamp assuming the
emfcamp database is empty), or set up a new empty database using
poetry run runtill -d dbname=emfcamp syncdb
(If you are a bar team member, you can obtain a database dump from your profile page.)
The various Django project management commands are then available via
poetry run tillweb, for example poetry run tillweb check, poetry run tillweb migrate, poetry run tillweb createsuperuser and poetry run tillweb runserver to run the development server.
The tillweb repository does not include the packed Javascript and CSS
necessary for the quicktill web interface project to run in
tillweb/static/bundles/. To regenerate these run npm run build.
The SCSS files in emf/static/emf/scss/ can be converted to CSS by
running npm run emfsass, and you can start a process that watches
the SCSS files for changes by running npm run emfsass-watch.
After running poetry install, apply migrations:
poetry run tillweb migrateThe kiosk API lets a self-service kiosk place orders against the live till database without needing direct database access. It is used by spacebar-kiosk and monitored by spacebar-oms. Orders recalled at the till are handled by the quicktill-spacebar-plugin.
| Endpoint | Auth | Purpose |
|---|---|---|
GET /api/stocklines.json?location=<name> |
None | Product list and live stock levels |
GET /api/kiosk/orders.json?location=<name> |
None | List live kiosk orders (OMS poll) |
POST /api/kiosk/orders.json |
Bearer token | Place a new kiosk order — returns { order_ref, barcode, qr_rows } |
POST /api/kiosk/orders/cancel.json |
Bearer token + valid HMAC barcode | Cancel an unpaid order. Body: { order_ref, barcode }. Verifies HMAC before deleting. 403 bad barcode, 404 not found, 409 paid/active. |
POST /api/kiosk/orders/expire.json |
Bearer token | Manually expire stale orders (operator escape hatch — normal expiry runs in the till plugin) |
Order refs are the quicktill Transaction ID — no separate counter. Barcodes use HMAC-SHA1 check digits (KIOSK:<trans_id><3-digit-decimal-check>) to prevent forgery; both this server and quicktill-spacebar-plugin must share the same kiosk.barcode_secret.
Unpaid orders older than 15 minutes are expired automatically by the recall plugin's timer, and as a belt-and-braces by the expire_orders function called on each POST /api/kiosk/orders.json.
If you are not restoring from a dump, you need to seed the emfcamp
database with test data before kiosk orders will work. Run syncdb
first — it populates transcodes, stockremove, and other reference
tables:
poetry run runtill -d dbname=emfcamp syncdbThen seed test stock. Key constraints to be aware of:
Departmentconstructor kwarg isid=, notdept=(the Python attribute isid; the DB column isdept).- Continuous
StockLinerows must have nodept_id,capacity, orpullthru— those are only valid fordisplay/regularlinetypes. StockItemrows for a continuous line must havestocklineid=Noneandchecked=True. Ifstocklineidis set,StockType.stockonsale()returns empty and the kiosk sees no stock.- An active
Sessionrow (withstarttimeanddate) is required before any order can be placed. Without it, the kiosk getsno-active-session.
After seeding, set a price for your test stocktype in the tillweb admin
(Stocktypes → set selling price for location Spacebar).