All notable changes to this project are documented in this file.
The format is based on Keep a Changelog.
(Nothing at the moment.)
- Demo orders on virgin deploy: Bootstrap now runs
seed_demo_ordersso tenant 1 gets paid and active orders (spread over ±90 days, biased to last 30). Reports (Informes) show meaningful revenue, by product, by table, etc. without manual seeding. New seedback/app/seeds/seed_demo_orders.py(idempotent: runs only when tenant 1 has no orders).back/run_seeds.shsupports--demo-ordersto run the seed manually.
- Bartender role: New user role for staff who prepare drinks and beverages. Same permissions as kitchen (order:read, order:item_status, product/catalog read); can access Orders and Kitchen display. Backend:
UserRole.bartenderinmodels.py, permissions inpermissions.py; migration20260315130000_add_bartender_role.sqladds enum value. Frontend: role in Users (create/edit), i18n in all locales. Puppeteer test:test:bartender-role(admin/owner → Users → Add user → role dropdown includes Bartender). Seedocs/testing.md§12.
- Product images on /products: Demo products (from
seed_demo_products) had no images. New seedlink_demo_products_to_catalogruns after catalog imports (beer, pizza, wine) and links tenant products without images to provider products that have images.GET /productsthen backfillsProduct.image_filenamefrom the catalog when staff load the Products page. Deploy script runs the seed automatically; on existing installs rundocker compose exec back python -m app.seeds.link_demo_products_to_catalogthen reload/products. Seeback/app/seeds/link_demo_products_to_catalog.py.
- Reports – average payment per client: New KPI in the Reports (Informes) summary: average revenue per order (total revenue ÷ number of orders), shown as "Average payment per client" in a summary card. Backend:
average_revenue_per_order_centsinGET /reports/salessummary. i18n for all locales (en, es, de, ca, hi, zh-CN).
- Reports – reservation stats: Reports page now shows total reservations in the date range and breakdown by source (Public book page vs Staff). Source is inferred from reservation token (token set = public, no token = staff). Summary card and "By source" block; Excel export includes a Reservations sheet.
- Dashboard sections (
/dashboard): Quick-action cards for Catalog, Reservations, Kitchen display, Reports, Inventory, Users, and Configuration. Reports, Inventory, Users, and Configuration are shown only to owner/admin; Catalog, Reservations, and Kitchen display are shown to all authenticated staff with route access. - Dashboard Help section: Links to GitHub Issues and GitHub Discussions for documentation and support. i18n for all new dashboard labels (en, es, de, ca, hi, zh-CN).
- Reports payload: API
GET /reports/salesand export now includereservations: { total, by_source: [{ source, count }] }. Reports empty state refined so summary and reservation stats are always visible; sales sections only when there are orders.
- Reports (Sales & Revenue) (
/reports): New section for restaurant owners and admins. Sales by date range (from/to), summary (total revenue, order count, daily series), by product, by category, by table, and by waiter. Simple CSS bar charts; export to CSV or Excel (full workbook). Uses existing order and product data (paid/completed orders only). Permissionreport:readfor owner and admin. Backend:GET /reports/sales,GET /reports/export; dependencyopenpyxlfor Excel. See docs/0016-reports.md. - Smoke tests required (AGENTS.md): New section stating that smoke tests are required after every new feature, fix, or code change; minimum (curl or landing test) and flow-specific tests (e.g.
npm run test:reports). - Puppeteer test:
test:reports— login as owner/admin, open/reports, assert page and date range load. Scriptfront/scripts/test-reports.mjs; npm scripttest:reports. Documented indocs/testing.md.
- Sidebar: Reports link (chart icon) for users with report access (owner/admin).
- Migration
20260314000000_add_user_provider_id.sql: Addsuser.provider_idanduser_rolevalue'provider'(required for provider portal login/register). Tracked in repo for deploy consistency. - CI/CD amvara9 doc: Sections on login/register 500 (migrations to run), demo login (ralf@roeber.de) and how to restore it, and that deploy does not run
remove_extra_tenants.
- remove_extra_tenants seed: Docstring WARNING that it deletes all users of removed tenants (e.g. demo account); not run by deploy; how to restore demo login or use set_user_password.
- deploy-amvara9.sh: Comment clarifying the script does not run
remove_extra_tenantsand that that seed deletes other tenants and their users.
- Kitchen display (
/kitchen): Dedicated full-screen view for the kitchen — large, readable order cards; auto-refresh every 15 seconds and live updates via WebSocket; optional sound on new orders (toggle persisted in localStorage). Read-only: shows active orders (pending, preparing, ready, partially_delivered) with table, items, and item status. Access: same roles as Orders (owner, admin, kitchen, waiter, receptionist). Nav link "Kitchen display" in sidebar. i18n: EN, DE, ES, CA. See docs/0015-kitchen-display.md.
- Provider dashboard: List and tile view toggle plus search (by name, catalog name, external ID) on
/provider. - Company details toast: Success toast "Company details saved." after saving provider company details.
- Puppeteer test:
test:provider-add-product(login as provider, add product, assert it appears in list). Migration20260315100000_add_provider_company_fields.sqlfor provider table company/bank columns.
- Provider create product 500: Endpoint returns
model_dump(mode="json")and wraps in try/except so DB/serialization errors return a clear 500 message. - Landing provider links test: Navigate by URL to
/provider/registerinstead of waiting for client-side navigation after click (fixes timeout with Angular routing).
- Provider portal: Providers can register and log in to manage their catalog. New routes:
/provider/login,/provider/register,/provider(dashboard). Provider users haveprovider_idonUser; JWT supportsprovider_idfor provider-scoped auth. API:POST /register/provider,POST /token?scope=provider,GET/PUT /provider/me,GET/POST/PUT/DELETE /provider/products,POST /provider/products/:id/image,GET /provider/catalog. Landing page footer includes a "Provider portal" link.provider.guard.tsand provider routes inapp.routes.ts. - Provider registration company details: Registration and profile support full company name, address, tax number, phone, company email, and bank details (IBAN, BIC, bank name, account holder).
PUT /provider/meupdates company details; dashboard shows a "Company details" section and edit modal. - Catalog on deploy: Deploy script runs beer, pizza, and wine catalog imports so production (amvara9) has the same catalog as development. Deploy ensures
back/uploadsis writable by the back container (uid 1000) so import images are saved. - Puppeteer tests:
front/scripts/test-catalog.mjs(npmtest:catalog) for catalog page and image loading;test-order-8-status.mjs(npmtest:order-8-status) for order status dropdown on a given order;test-register-page.mjs(npmtest:register-page) for register page "Who is this for?" explanation;test-landing-provider-links.mjsandtest-provider-register.mjsfor provider portal flows. - Register page explanation: "Who is this for?" block on
/registerclarifying that the form is for restaurant/business owners (providers), not for guests. Guest hint: use "Book a table" or "Enter table code" on the homepage. i18n keysREGISTER_WHO_IS_THIS_FOR,REGISTER_FOR_PROVIDERS,REGISTER_GUEST_HINTin en, de, es, ca, zh-CN, hi. - Git hooks:
scripts/git-hooks/prepare-commit-msgstrips Cursor/agent attribution from commit messages;scripts/install-git-hooks.shinstalls hooks fromscripts/git-hooks/into.git/hooks/. - Documentation:
docs/0014-provider-portal.mdfor provider portal;docs/testing.mdfor testing notes.
- Mark as paid:
PUT /orders/{order_id}/mark-paidnow uses computed order status from items (all active items delivered) instead of storedorder.status, so completed orders can be marked paid even when DB status was out of sync. Stored status is synced tocompletedbefore setting topaid. Seedocs/0008-order-management-logic.mdedge case. - Order status dropdown:
getOrderStatusTransitionsandgetItemStatusTransitionsnormalize status with(currentStatus ?? '').toString().toLowerCase()so the transition map always matches; fixes pending orders not showing "Preparing" when status came in a different casing or type. - AGENTS.md: Updates for provider tests and hooks as needed.
- Nginx production:
location ^~ /api/so that/api/uploads/.../image.jpgis proxied to the backend instead of being handled by the static-asset regex (which was returning 404 for catalog images). - beer_import --clear: Use
session.execute(text(...))for raw SQL when checking tenant product references;session.exec()is for ORM only.
- User.provider_id:
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS provider_id INTEGER REFERENCES provider(id); - user_role enum: For provider registration to work, add the new value:
ALTER TYPE user_role ADD VALUE IF NOT EXISTS 'provider';
(PostgreSQL; without this, provider registration returns 500.) - Provider company fields: For provider registration/company details to persist, add columns to
provider(PostgreSQL):
ALTER TABLE provider ADD COLUMN IF NOT EXISTS full_company_name VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS address VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS tax_number VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS phone VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS email VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS bank_iban VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS bank_bic VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS bank_name VARCHAR; ALTER TABLE provider ADD COLUMN IF NOT EXISTS bank_account_holder VARCHAR;
(No notable changes.)
- Landing page version bar: Footer shows app version and commit hash (from environment). Puppeteer test
front/scripts/test-landing-version.mjsand npm scripttest:landing-version. - Products/Catalog placeholders: When a product has no image, Products list and Catalog show a clear image-icon placeholder instead of empty/broken area; same for image load errors in Products.
- remove_extra_tenants seed:
back/app/seeds/remove_extra_tenants.pykeeps only the tenant named "Cobalto" (or renames tenant id=1 to Cobalto) and deletes all other tenants and their data. Used to clean amvara9 to a single Cobalto restaurant. - set_user_password seed:
back/app/seeds/set_user_password.pysets a user's password from env (NEW_PASSWORD, optionalUSER_EMAIL). For server/admin use (e.g. match dev password).
- Landing version Puppeteer test: Uses fallback selector
.landing-version-barand 15s timeout for lazy route.
- Public landing page (
/): Tenant/restaurant list with "Book a table", "Login", and "Enter table code" for ordering. Logout redirects to/. - Booking page (
/book/:id): Hero header matching menu (logo, restaurant name, description, phone, email). Language selector. Extended public tenant API withdescription,phone,emailandGET /public/tenants/:id. - Reservation view (
/reservation?token=...): Same hero header as book/menu with restaurant branding and language selector. - Language selector: On landing, booking, and menu pages. Default language from browser;
LanguageServiceinitialized at app bootstrap. - Reservation number: Unique reservation number (#id) shown to client on booking success and on reservation view page. i18n key
RESERVATIONS.RESERVATION_NUMBERin all locales.
- AVIF image upload support: Accept AVIF format for all photo/picture uploads.
- Settings (tenant logo): File input and backend accept
image/avif; logo upload validates and optimizes AVIF (Pillow), keeps.avifextension. - Product details: Product image upload accepts
image/avifin the file picker and API; backendALLOWED_IMAGE_TYPESandoptimize_image()handle AVIF; stored filenames may use.avif. - Backend:
ALLOWED_IMAGE_TYPESincludesimage/avif;optimize_image()saves AVIF withAVIF_QUALITY; allowed extensions for logo and product image include.avif. - Frontend:
acceptattributes updated toimage/jpeg,image/png,image/webp,image/aviffor both settings and products.
- Settings (tenant logo): File input and backend accept
- Table reservations
- Staff: Reservations list (
/reservations) with filters (date, phone, status); create, edit, cancel, seat at table, finish. Table column always visible (name or "—" when not assigned). Permissionsreservation:readandreservation:writefor owner, admin, waiter, receptionist. Tables canvas: status "Reserved" (amber) when a reservation is assigned. - End users (public): Book at
/book/:tenantId(date, time, party size, name, phone; no login). After booking, link to/reservation?token=...to view or cancel. Seedocs/0011-table-reservation-user-guide.mdfor URLs and flow. - API:
POST/GET/PUT /reservations, seat/finish/cancel; public create (withtenant_id),GET /reservation/by-token,PUT /reservation/{id}/cancel?token=.... Reservation responses includetable_namewhen assigned. Table status inGET /tables/with-status:available|reserved|occupied.
- Staff: Reservations list (
- Order history (public menu): Backend
GET /menu/{table_token}/order-history; frontend menu shows order history section andgetOrderHistory();OrderHistoryItemin API service. - WebSocket: Token-based auth for WS (
/ws-token, token in URL); ws-bridge Dockerfile and main.py updates; frontendgetWsToken()and URL handling for relative/absolute WS URLs. Scriptfront/scripts/test-websocket.mjsfor owner login and WS connectivity check. - Documentation
docs/0011-table-reservation-user-guide.md: End-user flow, URL reference (book, view/cancel), testing steps.docs/0010-table-reservation-implementation-plan.md: Implementation plan (existing).- Documentation consolidated under
docs/: CUSTOMER_FEATURES_PLAN, DEPLOYMENT, EMAIL_SENDING_OPTIONS, GMAIL_SETUP_INSTRUCTIONS, IMPLEMENTATION_VERIFICATION, ORDER_MANAGEMENT_LOGIC, TABLE_PIN_SECURITY, TRANSLATION_IMPLEMENTATION, VERIFICATION_ALTERNATIVES (moved from repo root). - README rewritten: POS2 branding, features table, built-with, getting started; references to
docs/and ROADMAP. ROADMAP updated: completed/missing features and doc references.
- Agent / ops
- AGENTS.md: Docker status, port detection, log commands, reservation Puppeteer tests, demo tables seed/test instructions.
- Frontend debug script
scripts/debug-reservations.mjs(Puppeteer: login, create reservation, cancel)..envfor demo credentials (gitignored);puppeteer-coredev dependency. - Public user test
scripts/debug-reservations-public.mjs(Puppeteer: open/book/:tenantIdwithout login, fill form, submit, then view/cancel by token). npm script:debug:reservations:public. - WebSocket test script
scripts/test-websocket.mjs(Puppeteer: login, check WS connection after navigating to /orders). - Frontend dev proxy config
proxy.conf.jsonfor local API/WS proxying.
- Demo tables: Seed script
back/app/seeds/seed_demo_tables.py(floor "Main" + T01–T10 for tenant 1; idempotent). Check scriptback/app/seeds/check_demo_tables.pyto verify T01–T10 exist. Deploy (scripts/deploy-amvara9.sh) runs the seed after migrations so tenant 1 always has 10 demo tables on new deployment. See AGENTS.md. - Demo products: Seed script
back/app/seeds/seed_demo_products.py(default menu for tenant 1: main courses + beverages; idempotent, no images). Deploy runs it after demo tables so the Demo Restaurant has tables and products on new deployment. - Puppeteer test (demo data):
front/scripts/test-demo-data.mjschecks ≥10 tables, ≥10 products, and public /book/:id; useLOGIN_EMAIL/LOGIN_PASSWORDfor full check. OptionalBOOK_TENANT_ID(default 1).npm run test:demo-dataornode front/scripts/test-demo-data.mjs. - Seeds for all tenants:
seed_demo_tablesandseed_demo_productsnow run for every tenant that has no tables/products (not only tenant 1), so e.g. ralf@roeber.de (tenant 2) gets demo data on deploy. Table seed setsis_active=falsefor prod NOT NULL. - Deploy guide:
docs/0003-deploy-server.mdfor deploying latest master to a server (e.g. amvara8 at/development/pos). - Reservation tests (localhost + production): Script
scripts/run-reservation-tests.shruns public (and optional staff) Puppeteer reservation tests against configurableBASE_URLS(default:http://127.0.0.1:4203andhttp://satisfecho.de). See AGENTS.md. - CI/CD (amvara9): GitHub Actions workflow
.github/workflows/deploy-amvara9.ymldeploys to amvara9 on push to master/main (SSH key in repo secretSSH_PRIVATE_KEY_AMVARA9). Server setup: deploy key inauthorized_keys, repo at/development/pos,config.envfrom example. Seedocs/0001-ci-cd-amvara9.md.
- Migration 20260313150000 (tenant timezone): Idempotent
ADD COLUMN IF NOT EXISTSso re-run or pre-existing column does not fail. - Production nginx (satisfecho.de): Front container’s
nginx.confnow strips the/apiprefix when proxying to the backend (location /api→proxy_pass http://pos-back:8020/), so the backend receives/reservationsetc. and public reservation booking works on production. - Reservation create "failed to create": DB columns
reservation_dateandreservation_timeweretimestamp; migration updates them toDATEandTIME. - Reservations route and sidebar: Staff route
/reservationsbefore public/reservation; permission-basedreservationAccessGuard; frontend build (Router,minDate(),LowerCasePipe). - Reservation API: invalid date/time return HTTP 400 with clear message; parsing validates length and format.
- Reservations list: Table column always shown; API returns
table_name; frontend shows name or "—" (RESERVATIONS.TABLE_NOT_ASSIGNED). - Puppeteer test: create/cancel uses DOM form values and date filter; cancel confirmation works.
- Admin layout: main content full width (removed
max-widthon.main). - API service: resolved merge (OrderHistoryItem, WebSocket URL handling); reservation and public menu APIs.