All notable changes to this project are documented in this file.
The format is based on Keep a Changelog.
- Reports / Spanish registro horario (GitHub #170):
GET /reports/attendance-registro-horario-excel— monthly.xlsxwith paper-style layout (company/CIF/CCC header from tenant settings, tenant timezone for days and planned vs clocked split at 12:00 local, Firma column on every day row, totals, RD-ley 8/2019 footer text). One sheet per employee; optionalstaff_idslike the session-based export.Tenant.ccc(migration20260407120000_tenant_ccc.sql) withTenantUpdate.cccand Settings → Business fieldSETTINGS.CCC. Reports UI: second download button;REPORTS.ATTENDANCE_REGISTRO_HORARIO_*in allfront/public/i18n/*.json. Tests:tests/test_registro_horario_excel.py.
- Reports / attendance (Excel):
GET /reports/attendance-excelfor users withreport:read— monthly XLSX export of work sessions per staff member (employee number, name, date, clock in/out, break minutes, net hours, notes). Requires openpyxl.User.employee_number(optional, max 64 chars) for spreadsheet labeling; migration20260406131500_add_user_employee_number.sql.
- Reports / attendance (GitHub #159): Users with
report:readcan Adjust work sessions from Who is on shift now and Staff attendance — modal with datetime-local clock in/out (local time → UTC ISO forPOST /reports/work-sessions/{id}/adjust), optional note, validation and API errors viaApiErrorMessageService; tables refresh on success. Removed duplicate live-attendance block on the Reports page. NewREPORTS.WORK_SESSION_ADJUST_*strings in allfront/public/i18n/*.jsonfiles;es.jsonalso gains the missing live attendance keys. Task:agents/tasks/UNTESTED-20260403-1202-reports-ui-adjust-staff-clock-in-out-work-session.md.
- Settings → Security (GitHub #155): After Generate new token, Staff clock-in QR offers Download QR for printing — client-side 1200px PNG of the full app URL with
/my-shift?clock_qr=…(same pattern as SETTINGS.CLOCK_QR_URL_HINT). Shown only when QR protection is active and the plain token is available in-session (unchanged from regenerate). Dependencies:qrcode,@types/qrcode.
-
Staff tables list & tiles (GitHub #174): Joined table groups show as one list row and one tile card per group (combined names and seat total; expand list rows for per-table actions). Activate / Open menu warn when another group member already has a session or open order; optional activity badges and a floor-plan dot for the same case. i18n:
TABLES.GROUP_*. Files:front/src/app/tables/tables.component.ts,front/src/app/tables/tables-canvas.component.ts,front/public/i18n/*.json. -
Public booking page (GitHub #173): Hero area uses a frosted panel (rounded corners, padding, backdrop blur, stronger tint when a header background image is set) so title and contact text stay readable.
GET /api/public/tenants/{id}includes normalizedwebsite; book page shows a Website link when set.BOOK.WEBSITEin allfront/public/i18n/*.json; tests intests/test_public_tenant_whatsapp.py. -
Tables / floor plan (GitHub #172): On first load and when switching floors (or after adding a floor / deleting the current floor), the canvas fits and centers all tables on that floor with padding; empty floors show the full canvas at 1×. The reset control uses the same fit logic. Repeated
loadDatarefreshes (e.g. after join) no longer reset pan/zoom.front/src/app/tables/tables-canvas.component.ts. -
Agents: Recorded latest
001-log-reviewersweep inagents/001-log-reviewer/time-of-last-review.txt.
-
Reports → Monthly attendance Excel (GitHub #171): Staff filter hint is shown above the staff dropdown (template order: label → hint → control); no i18n changes.
-
Reports / attendance Excel (GitHub #168):
GET /reports/attendance-excelwithstaff_idsno longer returns 500 — the Excel styling loop no longer shadowssqlmodel.col. Regression coverage:tests/test_attendance_excel.py.
-
Agents:
agents/001-gh-reviewer/— GitHub-focused backlog reviewer (issue sweep,ghcomment +agent:plannedlabel,FEAT-task creation); mirrors log-reviewer structure without the Docker log pass. -
Reports → Monthly attendance Excel (GitHub #168): Optional staff filter before download —
GET /reports/attendance-excelaccepts repeatedstaff_ids(tenant users only); Reports UI multi-select when the tenant has users. Omit selection or omit the query param to export all staff with sessions in the month. -
Settings → Security / Staff clock-in QR (GitHub #167): Encrypted storage of the plain token (Fernet, key from
SECRET_KEY) intenant.clock_qr_token_encrypted;GET /tenant/settings/clock-qr/tokenfor admins to reload/copy/download after page refresh.clock_qr_downloadableon tenant settings; legacy hash-only rows show a regenerate hint. Migration20260406140000_tenant_clock_qr_token_encrypted.sql. -
Reports → Monthly attendance (Excel) (GitHub #165): Users with
report:readcan pick a calendar month and Download Excel (GET /reports/attendance-excel) from the Reports page. NewREPORTS.ATTENDANCE_EXCEL_*strings in allfront/public/i18n/*.jsonfiles.
-
Reports → Monthly attendance Excel (GitHub #169): Optional staff filter uses a compact dropdown (checkbox list, search when more than ten users) instead of a tall native
<select multiple>; export behavior unchanged (no selection = all staff with attendance that month; specific checkboxes =staff_idsquery). -
Agents:
agents/001-log-reviewer/LOG-REVIEWER-PROMPT.md— GitHub sweep step explicitly requiresagent:plannedon each touched issue. -
Agents: Removed misplaced stubs under
agents/agents/tasks/(FEAT-20260406-1108-clarify-shared-venue-gps,NEW-20260406-1115-api-errors-auth-routes); canonical tasks live underagents/tasks/. -
Staff reservations / multi-zone (GitHub #164): The create/edit modal on
/reservationsnow mirrors public/bookfor seating, location zone (when multiple floors match), andReservationWeekSlotGridComponentbookFloorId; staff create/update sendspreferred_floor_id. Duplicate task fileWIP-20260406-0935-align-staff-reservations-formremoved (same scope asUNTESTED-20260406-0915-align-staff-reservations-form). -
Settings (GitHub #166): Clarified that Location verification coordinates and radius are shared with Require GPS at venue for clock under Staff clock-in QR—one venue pin for order checks and clock-in, not separate pins. Updated
SETTINGS.LOCATION_VERIFICATION_DESC,ENABLE_LOCATION_CHECK_HINT, andCLOCK_QR_LOCATION_VERIFY_HINTin allfront/public/i18n/*.jsonfiles.
-
Reports / attendance Excel (GitHub #165): Monthly export no longer calls a non-existent
WorkSession.notesfield (was HTTP 500 when any work sessions existed). Notes column is empty until the model supports it. -
Staff reservation edit (PUT
/reservations/{id}): Avoid 400 when the guest had a book zone (preferred_floor_id) and staff changes seating preference — the zone is dropped if it no longer matches the preference; explicitpreferred_floor_idin the body is validated against seating. Staff week grid and slot-capacity use the same floor scope while editing until seating is changed from the opened baseline. -
Obsolete agent loop script: Removed
agents/pi-pos-loop.sh.
-
Reservation confirmation & reminder email (GitHub #162, #163): Server-built copy uses
get_messageandreservation_transactional_lang.reservation.locale(migration20260403150000_reservation_locale.sql) is set from the booking request language onPOST /reservationsand overrides the tenant default for confirmations and reminders. Full confirmation-email keys added forca,de,zh-CN,hi,bg,fr. If a custom body includes both{{prepayment_notice}}and{{prepayment_text}}, the latter is cleared so prepay policy is not duplicated. HTML part usesnormalize_confirmation_html_fragmentfor tighter line breaks.docs/0030-reservation-confirmation-email-troubleshooting.mdupdated. Tests:back/tests/test_reservation_email_template.py(en, es, de + spacing/prepay cases);back/tests/test_reservation_reminder_email.pyunchanged API surface. -
Reports / attendance (GitHub #160): Work-session tables (Who is on shift now, Staff attendance) use
work-sessions-attendance-tablelayout (table-layout: auto, min width, nowrap) so columns no longer collide with the revenue-only fixed 4-columndata-tablerule; Adjust column useswork-sessions-actions(minimal width, right align, extra left padding). Task:agents/tasks/UNTESTED-20260403-1242-fix-attendance-table-column-overlap-on-reports.md. -
Agent loop — local LLM triage: Default
OLLAMA_MODELwhen unset isGemma4:latest(wasqwen2.5:1.5b) for Ollama fallback labeling and use inagents/pos-agent-loop.sh.
-
Reports / date range (GitHub #161): Quick range presets (Today, Last 7 days, This week Mon–Sun local, This month, Previous month) set From / To as local
YYYY-MM-DDand run the same load as Refresh (sales +getReportWorkSessions).fmtDateuses local calendar days (not UTCtoISOString). Staff attendance hint clarifies the table follows the header range and Refresh. NewREPORTS.PRESET_*/REPORTS.PRESETS_LABELkeys in allfront/public/i18n/*.json;test-reports.mjsasserts the preset bar. Task:agents/tasks/UNTESTED-20260403-1253-reports-date-range-presets-attendance-filter-hint.md. -
My shift / clock QR (GitHub #151): Scan venue QR on
/my-shiftopens a camera scanner (html5-qrcode) so staff can validate the sameclock_qrtoken as printed QR / staff links without manually adding query strings. Token storage and API payloads unchanged. Puppeteer:npm run test:my-shift-clock-qr --prefix front. Task:agents/tasks/UNTESTED-20260402-1507-my-shift-scan-venue-qr-from-page.md. -
Tables / floor canvas (GitHub #141): Tablet-oriented join by drag on
/tables/canvas— overlap is evaluated in floor canvas coordinates (stable with zoom/pan); brief hold (~160 ms) before release reduces accidental joins; confirmation dialog beforePOST /table-groups; Ctrl/Cmd multi-select + Join unchanged. Seedocs/0051-table-groups-mvp.md. (GitHub #142 tightens overlap to require real footprint intersection, not mere proximity.) -
Table groups / join tables (GitHub #140): Tenant-scoped
table_groupandtable.table_group_id. APIsPOST /table-groups(join same-floor tables) andDELETE /table-groups/{id}(dissolve).GET /tablesandGET /tables/with-statusexposegroup_member_ids,group_seat_total, merged canvas status for joined tables. Reservation seating uses combined capacity and blocks conflicts on any member;GET /ordersmay includetable_group_label. Floor plan: Ctrl/Cmd+click multi-select + Join / Unjoin; staff orders show optional group label. Seedocs/0051-table-groups-mvp.md. -
Reservations / floor seating (GitHub #139): Floors have
seating_zone(indoor|outdoor|any). Public/bookfilters the seating-area dropdown and slot capacity by the guest’s seating preference (terrace ↔ outdoor). API validates preferred floor vs preference; seat rejects tables on zones that don’t match. Tables list: per-floor Reservation seating control for staff. Task:agents/tasks/UNTESTED-20260401-1035-syncing-reservation-seating-with-floor-plan-sections.md. -
Working plan (GitHub #136): Calendar view — each real shift line is clickable to edit (same modal as week list), with a small delete control; overflow
+N morestays non-interactive. Task:agents/tasks/UNTESTED-20260401-0910-working-plan-calendar-click-shift-edit-delete.md. -
Working plan (GitHub #135): Add shift modal — optional split shift (checkbox; Shift A / Shift B) with a second start/end pair; saves as two planned shifts the same day so calendar, week list, and planned-minute totals include both blocks. Task:
agents/tasks/UNTESTED-20260401-0841-split-shift-add-shift-modal-working-plan.md. -
Landing (GitHub #133 / #134): Source on GitHub →
https://github.com/satisfecho/pos/as an icon link in the fixed landing-version bar (next to version/commit), with translated open-source tagline (LANDING.OPEN_SOURCE_TAGLINE, ♥, El Masnou & Los Mochis). Footer no longer duplicates the repo link—single primary location, responsive layout. -
PostgreSQL / operators: docs/0033-postgres-adhoc-sql-table-names.md — ad-hoc queries must use
"order"andorderitem, notrestaurantorder; README PostgreSQL section links to it. -
Documentation (GitHub #132): Root README.md — staff sidebar/navigation summary, Stripe + Revolut in payments and configuration, accurate rate limiting pointer to docs/0020-rate-limiting-production.md; docs/README.md quick link and feature row for docs/REVOLUT.md.
-
Working plan (GitHub #130): Single Staff dropdown with All staff (default) filters the Planned vs clocked table; shift Export Excel requires a selected person. Comparison block is collapsible (default collapsed) with persisted open state; table totals row; Export comparison (Excel) via
GET /schedule/planned-vs-actual/export(localized headers, optionaluser_id). Task:agents/tasks/UNTESTED-20260331-1425-working-plan-reuse-staff-dropdown-planned-clocked-totals-excel-export.md.
-
Tables / floor plan (GitHub #157):
/tables/canvasheader no longer shows the Unsaved changes text; dirty state is still reflected by Save layout (enabled when dirty) and unchanged autosave,canDeactivate, andbeforeunloadbehavior. -
Tables / floor canvas (GitHub #156): Move tables toggle and Alt+drag gating removed — table drag always updates layout positions (with autosave). Join-by-drag still uses overlap + short hold → Join tables? confirmation; cancel restores the whole floor layout from the drag-start snapshot. Successful join keeps the existing API + post-join snap-back path. i18n:
TABLES.JOIN_HINTupdated;ARRANGE_LAYOUT_*keys removed. -
Working plan / compliance (GitHub #153): Schedule compliance (heuristic checks) on
/working-plan/calendar(and week view) formats planned time, weekly limits, rest gaps, and yearly thresholds with the sameformatMinuteshelper as Planned vs clocked (Xh/Xh Ym), instead of raw minute counts in translated strings. Task:agents/tasks/UNTESTED-20260402-1606-change-minutes-to-hours.md. -
Agents (001 log reviewer):
agents/001-log-reviewer/LOG-REVIEWER-PROMPT.md— FEAT task template uses a GitHub Issues section (repo link,gh issue list, optional--json); High-level instructions for coder and NEW- queue guidance spell out actionable, doc-referenced bullets without code (duplicate placeholder text removed). -
Reservation emails (GitHub #150): Shared HTML shell for confirmation and reminder (muted background, card, optional tenant logo when
PUBLIC_APP_BASE_URLis set). Manage reservation links use a consistent CTA style andget_messagecopy (email_reservation_manage_link_text) in the tenant’sdefault_language. Reminder subject/body are localized; plain-text reminders append a timezone line when the tenant hastimezoneset. New keys inback/app/messages.py;normalize_lang_for_messages()maps UI codes to message bundles. -
Agent loop — local LLM triage:
scripts/agent-ollama-log-triage.shcalls llama.cpp (OpenAI-compatiblePOSTto…/v1/chat/completions, default basehttp://127.0.0.1:8080/v1, modelBonsai-8B.gguf) first, then Ollama if that fails or returns nothing.pos-agent-loop.shruns triage whenGET …/v1/modelssucceeds andpython3exists, or whenollama listshows ≥1 model (unlessAGENT_001_OLLAMA_LOG_TRIAGE=0). OptionalAGENT_001_SKIP_LLAMA_CPP=1uses only Ollama.docs/agent-loop.mdupdated. -
Tables / floor plan (GitHub #144):
/tables/canvasdebounced auto-save (~550 ms after the last drag) for table x/y positions using existingPUT /tables/{id}calls; saves are serialized so overlapping flush/debounce only persist the latest state. Unsaved changes stays accurate until a successful save. Switch floor, join/unjoin, delete table, and reassign-and-delete flush pending layout saves first. Route leave runscanDeactivate(save then optional confirm) plusbeforeunloadwhen still dirty. -
API errors / i18n (GitHub #143): Many FastAPI
HTTPExceptionresponses now use structureddetail(code,message, optionalparams) viaapi_error_payload, so the Angular app can mapAPI_ERRORS.*keys in all shipped languages. Staff/public surfaces updated include login/register, provider auth, booking, reservations, tables, working plan, and landing tenant list. Table delete “has orders” is detected bycode: table_has_orders. -
Working plan (GitHub #138): Calendar view — grid uses full width up to 100rem (was capped at 42rem); day cells min-height 7.75rem for more shift lines; weekday header row stays compact (
min-height: autoon header cells). Scoped toWorkingPlanComponentstyles only. -
Reservations / public booking (GitHub #128): Single allergies / special requirements textarea on
/book/:tenantIdand in the staff reservation modal (removed duplicate checkbox + second “customer notes” field that repeated the same intent). Radio controls use 1rem label text and 1.125em native inputs with theme accent-color. Staff list and guest reservation view show one merged dietary line (reservation-dietary-noteshelper).
-
Tenant settings / GPS (GitHub #158):
PUT /tenant/settingsnow persistslatitude,longitude,location_radius_meters, andlocation_check_enabledwith validation (finite lat/lon in range, non-negative radius), so venue coordinates save correctly and staff clock-in with GPS can succeed when Require GPS at venue is enabled. -
Reservations / reminder (GitHub #149): Manual Send reminder no longer returns 200 with both channels false while still marking reminder timestamps sent. Delivery failure returns 503 with a clear
detail(missing SMTP vs send/WhatsApp failure). Background reminder heartbeat only marks sent when a channel actually delivers. -
Password reset (GitHub #148): If
PUBLIC_APP_BASE_URLis unset,POST /password-reset/requestnow returns 503 withpassword_reset_not_configured(before user lookup — no email enumeration) instead of 200 with a message that implied an email would be sent. Forgot-password UI uses structured API errors + i18nAPI_ERRORS.PASSWORD_RESET_NOT_CONFIGURED.config.env.exampledocuments that the public base URL (and SMTP) are required for reset emails. -
Tables / floor canvas (GitHub #145): Drag-to-join — after a successful join, table canvas positions snap back to where they were before the overlap gesture (grouping stays logical); rename in the side panel updates the header and canvas label immediately while typing. (Layout still auto-saves on a debounce after moves.)
-
Landing (GitHub #146): Homepage footer always shows Terms of service and Privacy policy links to the in-app
/termsand/privacyroutes (no longer hidden when public legal URLs are unset). -
Tables / floor canvas (GitHub #147): After Unjoin, the selected table’s details and Unjoin control now follow server state —
GET /tables/with-statusresults re-sync the side panel selection, and the unjoin action is guarded against duplicate requests (avoids stale group ids and spurious errors). -
DELETE
/tables/{id}: Resolved 500 (Queryobject passed to language normalization) by injectinglangwithDepends(_get_requested_language)instead of calling_get_requested_language(request)directly. Regression:back/tests/test_delete_table_api.py. -
Tables / floor canvas (GitHub #142): Drag-to-join confirmation on
/tables/canvasfires only when table footprints overlap in layout coordinates (strict AABB for rectangle/booth/bar; ellipse metric for circle/oval pairs), not when tables are merely near each other—removes the previous inflated proximity margin. -
Tables / floor canvas (GitHub #137): Staff Tables floor-plan view — header (
tables-canvas-header) and floor tabs use the normal light/neutral theme again; the dark tablet canvas styling applies only to the drawing area, not the chrome above it. -
Database migrations:
python -m app.migratenow treats a migration as pending when its version is absent fromschema_version(instead ofversion > MAX(version)), so an older migration file is not skipped after a newer timestamp migration was applied first. Migration files are applied in strict version order.run_migrationsusesMigrationRunner.migrations_dirconsistently. -
Reservations / public booking (GitHub #127): On today (tenant calendar date), the time-slot dropdown in
ReservationWeekSlotGridComponentno longer lists API-marked past slots, so the first options align with the next bookable times. -
Product image upload:
POST /products/{id}/imageandPOST /provider/products/{id}/imagenow return explicitJSONResponseso slowapi can inject rate-limit headers without raisingparameter \response` must be an instance of starlette.responses.Response` (500 on successful upload). -
Table groups / SlowAPI:
POST/DELETE /table-groupsuseJSONResponse(same slowapi contract as product uploads). Addedtests/test_table_groups_slowapi_subprocess.pyto exercise join+delete withRATE_LIMIT_ENABLED=true(pytest normally disables limits viaconftest). -
Public booking / reservations (GitHub #119):
GET /public/tenants/{id}now returnsreservation_max_guests_per_slotin the JSON body (same as the public tenants list andTenantSummary), so the public book page can enforce the tenant party-size cap. Staff debug scriptfront/scripts/debug-reservations.mjsupdated for the reservation modal with the week grid (#res-modal-*,.week-slot.ws-available).
-
POS tips verification (GitHub #123):
back/tests/test_order_tip.pycovers overpayment resolution and mark-paid + waiter attribution; Puppeteernpm run test:order-tip-flows --prefix frontexercises Settings → Payments (data-testid="settings-payments-tab") and Reports tips card (data-testid="reports-summary-tips"). Documented indocs/testing.md§8b. -
Reservations / public booking week grid (GitHub #125): Shared
ReservationWeekSlotGridComponent— Mon–Sun column borders, single-letter weekday headers, state styling (available / full / closed / selected), dot legend, summary row (guests, service, date, time-slot dropdown); i18n updates across locales. Task:agents/tasks/UNTESTED-20260331-1105-improve-reservations-ui-weekly-calendar.md. -
Reservations / month calendar booking (GitHub #126): Public
/bookand staff reservation modal use a month day grid (weekday header row + day cells with available/full/closed styling) and a time dropdown after date selection — no time-slot table as the main surface. New APIsGET /reservations/book-month-day-statesandGET /reservations/book-day-slots(capacity rules aligned withbook-week-slots). Puppeteer scriptsfront/scripts/debug-reservations-public.mjsandfront/scripts/debug-reservations.mjsupdated; backend testsback/tests/test_book_month_day_slots_public.py. Child booking controls usengModelOptions: { standalone: true }so parentformDate/formTimestay in sync with the shared component. -
Version metadata: Bumped frontend package/version metadata to
2.0.65(front/package.json,front/package-lock.json, regeneratedfront/src/environments/commit-hash.ts) to keep landing version checks aligned. -
Agents task flow: Dynamic booking issue #119 handoff task moved from
TESTING-20260331-0921-...toWIP-20260331-0921-...after re-verify notes were appended, so the coder queue can continue follow-up work. -
Tables / floor canvas (GitHub #120): Tablet-oriented dark floor view, status legend, compact table name + seat count labels (no per-chair icons), and finer operational colors from
GET /tables/with-status(operational_status: available, reserved, seated, open order, bill/ready). Response also includesis_activeandactive_order_idfor the canvas panel. -
.gitignore: Ignoretime-of-last-review.txtanywhere in the tree (single pattern) instead of onlyagents/*/time-of-last-review.txt, so agent review timestamps stay untracked regardless of path. -
Tester prompt (
agents/003-tester/TESTER.md): Guidelines for tasks that include amvara9 / production deploy — wait for deploy success (CI, health/version polling, explicit handoff) instead of a fixed sleep before testing. -
Smoke tests:
front/scripts/test-landing-version.mjsacceptsLANDING_VERSION_ONLY=1to run only the landing/version assertion when.envcontains local demo login vars butBASE_URLpoints at a remote host (avoids spurious 401 on production smoke). For non-localBASE_URL, it runs a short HTTP reachability probe before Puppeteer (clear message on connection failure;LANDING_SMOKE_NO_REACHABILITY_PROBE=1to skip). Documented indocs/testing.md. -
Staff reservation debug script (
front/scripts/debug-reservations.mjs): Match POST reservation error responses by pathname ending with/reservations(instead of a broad URL substring); use E.164 test phone+14155550100aligned withback/tests/test_contact_validation.py.
-
Reservations / seating zones (GitHub #129): Public
GET /public/tenants/{id}/reservation-book-zoneslists active floors with tables;floor.is_active,reservation.preferred_floor_id. Public/bookshows a seating area control when there are two or more bookable zones; slot APIs accept optionalfloor_id. Tables list view: per-floor open for online booking toggle and reorder (↑↓). Venue-wide capacity still includes tables with nofloor_id; zone capacity counts only tables on that floor. -
Tables / Orders tablet flow (GitHub #124): Remember last list vs floor plan (
localStorage+TablesAreaPreferenceService); sidebar Tables link and segment switcher use it. Orders ↔ Tables toolbar (StaffPosToolbarComponent) on/staff/orders,/tables,/tables/canvaswith hide main nav (tablet+) and fullscreen icon buttons. Double-click a table on floor plan or tile list opens Staff Orders with?table=scope (filter + banner; clear to show all). i18n:STAFF_FLOW.*,ORDERS.TABLE_SCOPE_*. -
POS tips — overpayment mode & reporting (GitHub #123): Tenant
tip_entry_mode(presetvsoverpayment); mark-paid / finish withtip_amount_centsand optionalamount_paid_cents;order.tip_attributed_user_idfrom table/floor waiter; sales reporttotal_tips_cents/ dailytips_cents/ waitertips_cents; working-plan month Excel footer with attributed tips total; Settings and Orders payment UI. Migration20260331190000_tenant_tip_entry_mode_order_tip_attribution.sql. -
Working plan — scheduling extensions (GitHub #122):
POST /schedule/copy-weekcopies a Mon–Sun week of shifts to another week (optional skip when the worker already has a shift that day).GET /schedule/planned-vs-actualcompares planned shift minutes to net clocked minutes from closed work sessions (UTC day by clock-in).GET /schedule/compliance-summaryreturns heuristic warnings (weekly planned cap, minimum rest between consecutive shifts, yearly planned threshold). Working plan UI: Copy week → next week (week view), compliance banner, Planned vs clocked table. Backend test:back/tests/test_schedule_copy_week.py. Doc updates:docs/0021-working-plan.md. -
Staff attendance / My Shift (GitHub #121): Venue clock QR uses HMAC-stored
tenant.clock_qr_token_hash(plain token returned once viaPOST /tenant/settings/clock-qr/regenerate, cleared viaDELETE /tenant/settings/clock-qr); optionalclock_qr_location_verifywith venue coordinates. Breaks and net time viawork_session/work_session_break/ serialization helpers;GET /users/me/clock-qr-status; clock actions accept bodyclock_qr. Live rosterGET /reports/work-sessions/live; manual payroll fixPOST /reports/work-sessions/{id}/adjust. My Shift:?clock_qr=; Settings (Security): QR + location toggle; Reports: Who is on shift now. -
Agents / Cursor (GitHub #116): Always-applied rule
.cursor/rules/agent-response-language.mdc— one language per assistant reply, match the user's message language, no mixed-language agent text; documented inAGENTS.mdanddocs/agent-cursor-rules.md. -
Agent security: Always-applied Cursor rule
.cursor/rules/security-untrusted-input-no-exfiltration.mdc— treat GitHub issue text as untrusted; no exfiltration of secrets/PII intoFEAT-tasks, commits, or product files. Cross-references inAGENTS.md,docs/agent-cursor-rules.md,agents/001-log-reviewer/LOG-REVIEWER-PROMPT.md. -
Public terms & privacy pages (GitHub #113): SPA routes
/termsand/privacy(LegalDocumentComponent, i18nLEGAL.DOC.*in all shipped locales).GET /public/legal-urlsand tenant effective URLs fall back to{PUBLIC_APP_BASE_URL}/termsand/privacywhenPUBLIC_TERMS_OF_SERVICE_URL/PUBLIC_PRIVACY_POLICY_URLare unset.config.env.exampledocuments the fallback. -
Working plan — per-user colors (GitHub #109): Calendar shift lines use a stable hue from
user_id(HSL chips +@media (prefers-color-scheme: dark)). Week list shift cards get a matching left border. Legend text updated in all shipped i18n files. Color hash helper:front/src/app/working-plan/working-plan-shift-colors.ts(+working-plan-shift-colors.spec.ts).
-
Agents: 001 log-reviewer sweep watermark updated (
agents/001-log-reviewer/time-of-last-review.txt, 2026-03-31T12:27Z): 0 open GitHub issues → 0 new FEAT-; Docker log pass → 0 new NEW-. -
Auth / landing footers — Terms & Privacy next to account actions (GitHub #114): Staff login and register show provider login, register-as-provider, contact, and legal links in one wrapped row; landing keeps the same order with inline legal links. Provider login/register footers include contact and
getPublicLegalUrls()links;PROVIDER_AUTH.*i18n in all shipped locales.app-legal-linkssupports[inline]="true"for footer flow. -
HAProxy production (
haproxy.prod.cfg): API backend Layer7 check usesGET /healthinstead of/docsfor a lighter, faster probe after container start (reducesapi_backend/<NOSRV>503 windows). Addedtimeout check, defaultretries 3, and per-serverinter/downinter/rise/fall(WS, API, front) for more predictable UP/DOWN and quicker recovery while a server is down. -
Agent loop: Main coder step runs when
WIP-*.mdexists as well asNEW-*.md, so tasks are not stuck after NEW → WIP (agents/pos-agent-loop.sh,agents/002-coder/CODER.md,docs/agent-loop.md). -
Agent loop — 001 GitHub: When
gh issue listfails, 001 preflight usesgh api repos/…/issues(open issues, PRs excluded) to build the digest andG001_UNTRACKED_ISSUES, so the gate can still open for new issues (agents/pos-agent-loop.sh,docs/agent-loop.md). On 401 / bad credentials, the loop prints a stderr banner and recordsghstderr in the digest (G001_GH_AUTH_FAILEDin the preflight summary). -
Agent loop — tester:
pos-agent-loop.sh testertreats in-progressTESTING-*.mdlikeUNTESTED-*.mdfor gating andcursor-agent, so tasks renamed to TESTING- but not yet CLOSED-/ WIP- are not skipped by the loop (agents/pos-agent-loop.sh,agents/003-tester/TESTER.md,docs/agent-loop.md). -
Agent loop — token use: 001 builds a shell preflight digest (GitHub open issues + Docker log heuristics) under
$AGENT_LOOP_TMP/001-latest-context.txtand callscursor-agentonly when there is likely work (open issue not yet linked fromagents/tasks/*.md, or log incident signals). FEAT batch stops early when noFEAT-*.md. 002–004 / 006–007 skipgit-sync-developmentas well ascursor-agentwhen their queue is empty (or no git diff for 007). Env:AGENT_LOG_REVIEWER_ALWAYS,AGENT_001_SKIP_PREFLIGHT,AGENT_001_RUN_WHEN_GH_UNKNOWN,AGENT_GH_REPO,AGENT_LOOP_TMP(docs/agent-loop.md). -
Agent loop — Ollama log triage:
scripts/agent-ollama-log-triage.shruns automatically whenollama listworks and lists ≥1 model (unlessAGENT_001_OLLAMA_LOG_TRIAGE=0), only for log-only 001 signals (no untracked issues). DefaultOLLAMA_MODEL=qwen2.5:1.5b. Documented indocs/agent-loop.md. -
Agent loop — run logging:
run_agent()inagents/pos-agent-loop.shprints thecursor-agentprompt and message before each invocation to simplify orchestrator debugging.
-
Public feedback tab title with browser locale (GitHub #67):
FeedbackPublicComponentsetsdocument.titleafter first render (afterNextRender) and usestranslate.stream()for the title string so Spanish (and other) auto-detected locales do not leave the defaultindex.htmltitle on first load. -
Tables floor plan for waiters (GitHub #65):
/tables/canvasusedadminGuard, so waiters were redirected to the dashboard. The route now usestableAccessGuardlike/tables, andPermissionService.ROUTE_ROLES['/tables/canvas']includes waiter and receptionist.test:tables-waiter-assignmentasserts floor-plan access whenWAITER_LOGIN_EMAIL/WAITER_LOGIN_PASSWORDare set. -
Staff SPA —
ApiServicecircular dependency (GitHub #99):PermissionServiceno longer eagerly injectsApiService; it resolvesApiServiceviaInjectorso Angular does not report NG0200 (Circular dependency detected for _ApiService) when the staff app boots andHttpClientruns interceptors. -
Migration
20260326133000on existingstaff_contract_template_preset(GitHub #112): If the preset table already existed withoutuq_staff_contract_template_preset_region_locale_key,CREATE TABLE IF NOT EXISTSskipped DDL and the seededINSERT … ON CONFLICT (region_code, locale, template_key)failed. Added an idempotentDOblock that creates the unique constraint when missing. Ifcreated_at/updated_atare NOT NULL without server defaults, the seed insert received NULLs; addedALTER COLUMN … SET DEFAULT NOW()for those columns before the insert so production migrate can complete and the app tier can run againsttenant.country_code. -
Settings → Data & privacy i18n (GitHub #108): Added missing
SETTINGS.*export/purge strings for es, fr, ca, zh-CN, and hi; corrected leftover EnglishPURGE_CONFIRM_LABELin bg. UI already usedtranslatepipes; missing keys fell back to English. -
Bulgarian staff dashboard subtitle (GitHub #107):
DASHBOARD.WELCOME_TEXTinfront/public/i18n/bg.jsonwas still English; translated to match the rest of the Bulgarian staff UI. -
Landing footer version vs
package.json(GitHub #70):test:landing-versionon localhost requires the landing bar semver to matchfront/package.jsonso bumps are not “green” while the UI still shows an old version (stalecommit-hash.ts). OptionalSKIP_LANDING_PACKAGE_VERSION_CHECK=1when probing a remote host. Refreshedfront/src/environments/commit-hash.tsviaget-commit-hash.js.
- Terms of service & privacy URLs (GitHub #110): Optional per-tenant URLs in settings; global fallbacks in
config.env;GET /public/legal-urls; links on landing, auth, book, and public feedback. Migration20260327100000_public_terms_privacy_urls.sql.
- Docs — working plan:
docs/0021-working-plan-implementation-plan.mddescribes goals, scope, BetterShift evaluation, and an in-house direction for kitchen/bar/waiter shift scheduling.
- Agent / git workflow: Documented and automated sync before edits for multi-agent work on
development: newscripts/git-sync-development.sh,agents/pos-agent-loop.shruns it at each step (disable withAGENT_GIT_SYNC=0), updates to.cursor/rules/git-development-branch-workflow.mdc,AGENTS.md,docs/agent-loop.md,docs/agent-cursor-rules.md, and agent prompts underagents/.
-
Staff contract PDFs:
GET /uploads/{tenant_id}/contracts/{filename}now returns 403 so signed contract files are not served by the publicStaticFilesmount; use authenticatedGET /staff-contracts/{id}/documentonly. Regression test:tests/test_uploads_security.py. Security review notes:docs/SECURITY-REVIEW.md. -
Tenant IDOR sample:
tests/test_security_tenant_idor_orders.pyasserts another tenant’s order cannot be soft-deleted by ID.
- Contract template catalog & locale (GitHub #106): Per-tenant templates gain optional locale (BCP 47). New table
staff_contract_template_presetseeded by migration with regional outlines (ES/es, IN/en, global en).GET /staff-contract-templates/presetsreturns presets ranked by tenantcountry_code,default_language, and fallbacks (currency CIF/timezone heuristics when country unset).POST /staff-contract-templates/import-presetcopies a preset into the tenant (same RBAC as template CRUD). Tenantcountry_code(ISO 3166-1 alpha-2) in Business settings. Settings → Contract templates: catalog table with Import / preview; i18n in all shipped locales. Migration20260326133000_contract_template_locale_presets.sql. Tests:tests/test_staff_contract_templates.py.
-
Bulgarian locale (
bg): Full UI coverage inpublic/i18n/bg.jsonand backend message catalog alignment with other shipped locales. -
Landing footer Contact us (GitHub #104): Mailto sales@satisfecho.de with i18n label
LANDING.CONTACT_USin allpublic/i18nlocales;test-landing-provider-linksasserts the mailto href. -
User management password re-auth (GitHub #105):
PUT /users/{id}with a newpasswordrequiresactor_current_password(verified against the signed-in user)./usersedit modal collects Your current password above new/confirm when changing a password. API messages inmessages.py; UI strings in allpublic/i18nlocales. Tests:tests/test_user_password_update.py. -
OpenStreetMap link for tenants (GitHub #102): Optional
public_openstreetmap_urlin contact settings (same validation as other public http(s) links). Shown on public book (including post-submit), reservation-by-token view, and feedback pages alongside Google Maps when configured. Reservation confirmation and reminder emails includegoogle_maps_link_block_htmlandopenstreetmap_link_block_htmlplaceholders (default template updated). Migration20260326104500_tenant_public_openstreetmap_url.sql. Tests:tests/test_reservation_email_template.py,tests/test_reservation_reminder_email.py,tests/test_guest_feedback.py. -
Agent Cursor rules (GitHub #98): Stack-focused
.cursor/rules/*.mdcfor Angular/ngx-translate (allpublic/i18nlocales), FastAPI/SQLModel/migrations, Docker Compose + HAProxy, and security/tenant boundaries; catalog indocs/agent-cursor-rules.mdwith pointers fromAGENTS.mdanddocs/agent-loop.md. -
Tenant data export & purge (GitHub #96): Owner-only
GET /tenant/data-exportreturns a ZIP withtenant-export.json(tenant settings with payment/SMTP secrets redacted, staff without password hashes, products, orders, reservations, i18n, inventory, etc.). Owner-onlyPOST /tenant/purgewithconfirm_tenant_namematching the tenant name irreversibly deletes tenant data and schedules upload cleanup; logs a warning with operator id/email. Settings → Data & privacy (owners): download export and danger-zone delete. Users: owners editing another user can assign the Owner role (co-owner). Scriptpython -m app.seeds.purge_demo_tenantsremoves tenants 2–7 whenDEMO_PURGE_CONFIRM=1. Tests:tests/test_tenant_lifecycle.py. -
Password reset (GitHub #93): Self-service recovery for staff (optional
tenant_id, same as login) and provider accounts. New tablepassword_reset_token,POST /password-reset/request(generic JSON message; rate-limited),POST /password-reset/confirm(one-time token, min. 8-char password, bumpstoken_version). Email uses existing SMTP (tenant SMTP when set). Frontend:/forgot-password,/reset-password,/provider/forgot-password; link from staff and provider login. RequiresPUBLIC_APP_BASE_URLfor emailed links. Env:PASSWORD_RESET_TOKEN_EXPIRE_MINUTES,RATE_LIMIT_PASSWORD_RESET_PER_HOUR. Tests:tests/test_password_reset.py. -
Working plan bulk month (GitHub #88): Apply to month on the working plan creates the same shift on selected weekdays for the target calendar month (aligned with export month rules). Optional skip days that already have a shift preserves per-day edits and exceptions. New
POST /schedule/bulk;tests/test_schedule_bulk.py; Puppeteertest:working-planchecks the new control. -
Working plan Excel export (GitHub #89): Staff with schedule access can choose a worker and download that person’s shifts for the visible calendar month (calendar view) or the month containing the Monday of the displayed week (week view) as
.xlsx. NewGET /schedule/export(user_id,year,month, optionallang) uses openpyxl; tenant- and role-scoped like the schedule API. Puppeteertest:working-planchecks export UI; backendtests/test_schedule_export.pycovers the endpoint. -
My shift overtime alert (GitHub #87): Open clock-in sessions expose
open_duration_minutes,contract_threshold_minutes(default 8h), andover_contracton work-session APIs and reports. My shift shows a warning banner and elapsed time while clocked in past the threshold; dashboard My shift card shows a short notice. Backend tests cover threshold logic. -
Tenant
reservation_slot_minutes: Migration adds nullable column; staff Settings → Reservations can set the interval between public booking start times (5–120 minutes, or empty/0 for legacy 15-minute steps). Publicbook-week-slots,next-available, and staff overbooking default grid use this value.
-
Docs — PostgreSQL username: Clarified in
README.md,config.env.example, anddocker-compose.ymlthat the container superuser isPOSTGRES_USER(defaultpos), notpostgres, so IDE/psqldefaults do not causeFATAL: role "postgres" does not existconfusion. -
Password reset email i18n (GitHub #97): Reset email subject/body use
messages.pytranslations for all backend-supported locales; language matchesPOST /password-reset/request(?lang/Accept-Language, same as API message). Forgot/reset flows sendlangfrom the in-app language picker (ApiService). Tests:tests/test_password_reset.py. -
Staff reservations week grid (GitHub #94): Create/edit on
/reservationsuses the same Mon–Sun availability grid as public/book(GET /reservations/book-week-slots), tenant timezone, party size, and public lead-time rules. SharedReservationWeekSlotGridComponent; optionalexclude_reservation_idon book-week-slots excludes the edited booking from demand. Saving without changing date/time still allowed (e.g. past slots). -
Reservation emails (GitHub #91): Default confirmation template and scheduled/staff-triggered reminder emails include a Contact us block with the tenant’s public phone and email (
tel:/mailto:in HTML, plain lines in text) when those fields are set. New template placeholderrestaurant_contact_block_html; Settings → Reservations hint text updated in all i18n files. -
Public table menu (GitHub #85): Product cards and featured items use a light primary tint and border while the product is in the cart; adding or incrementing shows a short highlight on the card and the matching cart line. Respects reduced-motion for the pulse animation.
-
Staff reservations modal (GitHub #84): Create/edit dialog field order and labels match the public
/bookflow (date, time, party size, then name, phone, email, reservation notes, customer notes); staff-only notes remain at the end. Uses global form styling, optional email placeholder like the book page, and the same phone/email validation rules as the book form. Short hint explains calendar/time inputs vs. the public week grid. -
Settings → Reservations (GitHub #82): Pre-payment amount uses whole amount and minor units derived from the tenant currency via
Intl(e.g. euros + cents); zero-decimal currencies show a single whole-unit field. Still stored as smallest-currency-unit integer (reservation_prepayment_cents). -
Agents: 001 log-reviewer review stamps (2026-03-25–26),
LOG-REVIEWER-PROMPT.mddata-deletion scope, and task queue updates for contact-us (#104).
-
Angular NG0200 ApiService circular dependency (GitHub #100):
authInterceptorno longer callsinject(ApiService)whileHttpClientis constructed forApiService; it resolvesApiServicelazily viaInjectorinside the 401 error path (front/src/app/auth/auth.interceptor.ts). -
Settings → logo remove (GitHub #95): Removing the business logo now calls
DELETE /tenant/logo(unlink file, clearlogo_filename), matching header-background removal. Previously only the local preview was cleared. -
Working plan Excel export hidden for non-admin staff (GitHub #90): The worker dropdown and export button were omitted when
GET /usersfailed (403 for roles withschedule:readbut notuser:read). The app now loads schedulable staff viaGET /schedule/plan-users; export controls stay visible with a disabled state and hint when no plan users exist. -
Tables → Open menu / table PIN (GitHub #86): Staff “Open menu” from the tables list and tile view now uses the same short-lived
staff_accesslink as staff orders, so placing an order no longer forces the public table PIN modal. QR codes and “Copy” still use the customer URL. The PIN modal still shows which table (Table: …) when a PIN is required (e.g. wrong PIN retry). -
Settings → Email (SMTP) (GitHub #81): Added missing
SETTINGS.*translation keys for SMTP and reservation confirmation copy in Catalan, Spanish, German, French, Hindi, and Chinese (public/i18n). SMTP port and username placeholders use i18n like other fields. -
Settings Security / 2FA (GitHub #83): Spacing between OTP description hint and the enable action (and setup hint before the secret row) so the control is not flush against the copy.
- Staff contract templates & print view (GitHub #101): Per-tenant contract document templates (
staff_contract_templatemigration20260326103000), CRUD API/staff-contract-templates(STAFF_CONTRACT_MANAGE), safe delete when nostaff_contractreferences the template key.GET /staff-contracts/{id}/printreturns print-styled HTML with{{placeholder}}merge (employer/worker/role/dates/etc.) and signature block; falls back to a field summary when no template matches. Settings → Contract templates; Contracts links templates when creating a contract and adds Print view. Tests:tests/test_staff_contract_templates.py.
- Staff contracts (GitHub #99): Tenant-scoped employee and freelancer agreements with statuses, versioning (
POST /staff-contracts/{id}/new-version), payroll vs invoice payment structure, optional tax-id and jurisdiction notes, internal management notes, and signed PDF upload stored underuploads/{tenant_id}/contracts/(served only via authenticatedGET /staff-contracts/{id}/document, not public/uploads). RBAC:staff_contract:readfor all tenant staff (list filtered to own contracts unless owner/admin);staff_contract:managefor owner/admin (create/update/upload). Staff UI: sidebar Contracts, route/contracts. Migration20260325180000_staff_contract.sql. Tests:tests/test_staff_contracts.py.
- Tables floor plan (GitHub #75): Selected-table panel links to Staff orders —
?focusOrder=when the table hasactive_order_id, else?focusTableId=to jump to the right order/tab (scroll or open edit for history). Same roles as/staff/orders. New Puppeteertest:tables-canvas-open-orders.
- Reservation reminders (GitHub #74): Reminder email HTML/text use the same manage-reservation link label as confirmation (“View or change your reservation online”); the href is HTML-escaped. URL remains
/reservation?token=…whenPUBLIC_APP_BASE_URLis set. - Deploy (amvara9, GitHub #73): After successful back and front image builds,
scripts/deploy-amvara9.shrunsdocker buildx prune -fto trim unused BuildKit cache (non-interactive).SKIP_BUILDX_PRUNE=1skips; prune failure logs a warning and does not abort deploy. Documented indocs/0001-ci-cd-amvara9.md. - Agents: Bump-version task for issue #70 moved from
UNTESTED-20260324-2131-bump-versiontoWIP-20260324-2131-bump-version(tester notes: package/changelog at 2.0.54; refresh generatedcommit-hash/ runget-commit-hash.jsso the landing footer matches the package version).
- Spanish register placeholders (GitHub #78): Register page placeholders now follow the selected UI language (e.g. Spanish) instead of falling back to English.
- Tax of iva dropdown (GitHub #79): Settings → Contact information “Tax of iva” dropdown no longer renders empty; backend seeds default Spanish IVA taxes for tenants without tax rows and the UI loads all taxes to avoid validity edge cases.
- Tenant UI modules (GitHub #72): Per-tenant toggles for staff areas (
tables,working_plan,providers,reservations,kitchen_bar,inventory) stored intenant.ui_modules(JSONB, compact), exposed onGET/PUT/tenant/settingsas a resolvedui_modulesmap. Settings Navigation tab; sidebar and dashboard respect flags; direct URLs to disabled routes redirect to/dashboard. i18n en/es/de for new strings.
- Smoke test:
npm run test:tables-waiter-assignment— waiter Table view has read-only assignment cells (optionalWAITER_LOGIN_EMAIL/WAITER_LOGIN_PASSWORD; skips if unset). - i18n (zh-CN, hi): Kitchen prep stations UI strings (Settings, Products, KDS filter) aligned with en/de/es/ca.
- Public feedback (browser tab title): Document title also refreshes when locale JSON finishes loading (
onTranslationChange), not only on language change, reducing stale or key-like titles on slow networks (GitHub #67). - Public feedback (error view): Invalid or missing tenant now shows
FEEDBACKstrings via the translate pipe and includes the language picker so title and error lines follow the selected locale without a prior visit to/feedback/1(GitHub #67). - i18n (de): Repaired invalid
de.json(missing comma inSETTINGSafterRESERVATION_REMINDER_2H_HINT) so the German locale file parses and loads; public feedback and all DE strings work again (GitHub #67). - Public feedback i18n: French, Catalan, Hindi, and Simplified Chinese now include full
FEEDBACK.*strings (form, errors, staff table columns); public language pickeraria-labelusesSETTINGS.SELECT_LANGUAGE(GitHub #67). - Public feedback (loading): Language picker is shown while the tenant loads; rating “required” marker uses
FEEDBACK.FIELD_REQUIRED_MARKin all locales (GitHub #67). - Public feedback (submit errors): HTTP 429 and 422 responses map to
FEEDBACK.RATE_LIMIT/FEEDBACK.VALIDATION_ERRORin all supported locales instead of English rate-limit or validation payloads (GitHub #67). - Catalog menu rows: Prevent
tenantproduct.price_centsfrom being cleared to NULL (database NOT NULL): flush-time coalesce from supplier or linked product price, safer product backfill from catalog-only items, and PUT ignores explicit null price. - Tables floor plan: Selected-table panel shows read-only assigned waiter from table API for roles without
table:write, consistent with Tiles/Table list (GitHub #65).
- Docs: Removed the trailing “Reference paths (local)” section from
docs/agent-loop.md(redundant mac-stats-reviewer paths; upstream context remains earlier in the doc). - Landing (
/): Hero, value props, and a two-column guest vs staff layout (table code / register CTA) using existing design tokens; section heading for restaurant list; copy and newLANDING.*keys in all locales (GitHub #69). - Agents: Closed task
CLOSED-20260324-1558-feedback-page-needs-translationmoved toagents/tasks/done/2026/03/24/. - Repository: Ignore
time-of-last-review.txtat repo root (001 log-reviewer output),.factory/(local IDE settings), and vim*.swpswap files. - API (public tenant):
GET /public/tenants/{tenant_id}404detailuses the same localized catalog as other public endpoints (Accept-Language/?lang=), not a hardcoded English string (GitHub #67). - Smoke test:
test:feedback-public-i18nchecks de, fr, es, ca, zh-CN, hi,?token=URL, invalid/feedback/0(with en), missing tenant/feedback/999999999(API 404), and first-load locale via a fresh Chromium profile withnavigator.languagestubbed to es-ES before navigation (document title + visible copy, no rawFEEDBACK.*in the DOM) (GitHub #67). - Agents: 001-log-reviewer
time-of-last-review.txt— GitHub/issue sweep and Docker log pass lines appended (2026-03-23, through 22:55Z UTC).
- Public feedback (browser tab title, production): Tab title uses synchronous
translate.instantplusget()and omitstakeUntilDestroyedon the inner subscription soproduction-staticbuilds (e.g. satisfecho.de) updatedocument.titleinstead of leaving the index default while the page copy is translated (GitHub #67).
- Smoke test:
npm run test:feedback-public-i18n— public/feedback/:tenantresolves translations (en + de); fails if rawFEEDBACK.*keys appear in the DOM (GitHub #67).
- i18n bootstrap (Accept-Language interceptor): Inject
LanguageServiceonly for API requests (environment.apiUrl). Loading/i18n/*.jsonduring bootstrap no longer hits a circular dependency, so ngx-translate loads locale files and public routes (/feedback/*,/book/*, etc.) show translated copy on first paint (GitHub #67). - Public feedback (browser tab title): Tab title uses
FEEDBACK.LOADING/FEEDBACK.TITLE/FEEDBACK.THANK_YOUplus tenant name when available (GitHub #67). - i18n (ca, fr, hi, zh-CN):
NAV.GUEST_FEEDBACKandRESERVATIONS.VIEW_FEEDBACK_PAGEtranslated (GitHub #67).
- Week view for online reservations: Guests can pick a day and time using a clear week layout: open slots look green, full ones red, and closed or past times are greyed out (GitHub #64).
- Smarter suggested times: The booking flow suggests a time a few minutes after "now" instead of always defaulting to the same evening hour; staff get the same idea when creating reservations (GitHub #62).
- Extras on each order line: Staff can note swaps, add-ons, and removals (for example pizza-style changes). Kitchen tickets and printed bills show a short, readable summary (GitHub #50).
- Kitchen and bar prep stations: Owners can name prep stations (kitchen, bar, grill, etc.), choose whether each station shows on the kitchen or bar screen, map products to stations, and set defaults so drinks and dishes go to the right place (GitHub #66).
- "My shift" on the dashboard: Team members see at a glance if they are clocked in and can open their shift page in one tap (GitHub #57).
- Edit your own suppliers: Restaurants can update contact details for providers that belong to them under Settings (GitHub #25).
- Tips and Revolut: Short owner-facing note on how tip buttons relate to card payments with Revolut, plus small tests around edge cases (GitHub #58 follow-up).
- Planning docs: Extra roadmap material for larger future themes (GitHub #52 planning).
- Pay earlier in the flow: When your rules allow it, staff see a clearer "Pay now" path and short explanation that the kitchen can still be finishing dishes until you mark the order complete (GitHub #23).
- Catalog cards: Long descriptions can expand and collapse; prices line up neatly across the grid (GitHub #34).
- Safer server updates: Production deploy builds new images before stopping the app, keeps the database running during routine updates, and waits until the site answers health checks again. Overlapping deploy jobs are avoided (GitHub #49).
- Automated browser checks: Default test runs no longer pop up a browser window unless you ask for one; optional deeper navigation checks when credentials are configured.
- How we ship code: Day-to-day work lands on a development branch first; production still follows the main branch when you promote a release.
- Who waits which table: Waiters and reception can read table and floor assignments without needing extra admin rights (GitHub #65).
- New staff accounts: Resolved a database mismatch that could block creating some users.
- Order list layout: Action buttons on order cards stay aligned when extra lines appear (GitHub #59).
- Product list layout: Removed odd empty space below the products table (GitHub #33).
- Agent playbooks, task folders, and a small pos-agent-loop helper script were added so automated assistants can follow the same steps as the team. Issue tracking hooks are documented for those workflows.
- GitHub #62 — Public book page: Month calendar next to the date field (Mon–Sun grid, prev/next month, legend). Closed days use opening hours (
GET /reservations/book-calendar); past days and outside the 12‑month window are disabled. Tenanttimezoneis included onGET /public/tenants/{id}so “today” and time filtering match the restaurant. Public bookings require at least 10 minutes lead time (RESERVATION_PUBLIC_MIN_LEAD_MINUTES);GET /reservations/next-availablegainsmin_lead_minutes(default 10; staff uses 0). Time dropdown for today hides slots before that cutoff. i18n:BOOK.CAL_*. API messagereservation_min_lead_time(en, es).
- GitHub #58 — POS tips: Settings → Payments — up to four tip percentages (default 5/10/15/20), optional tip VAT rate for invoice breakdown.
PUT /orders/{id}/mark-paidandPUT /orders/{id}/finishaccept optionaltip_percent; order storestip_percent_appliedandtip_amount_cents.GET /ordersreturnssubtotal_cents, tip fields, andtotal_centsincluding tip. Unmark paid clears tip. Printed invoice shows subtotal, tip line, and VAT split when configured. Migration20260323140000_tenant_tip_presets_and_order_tip.sql. Tests:back/tests/test_order_tip.py. i18n:ORDERS.*tip keys,SETTINGS.TIP_*. - GitHub #57 — staff attendance (clock in/out): Table
work_sessionfor recorded on-site times (separate from working planshiftscheduling). API:GET /users/me/work-session,POST .../work-session/start,POST .../work-session/end,GET /users/me/work-sessions; start/end IP from the server’s view (proxy headers) for audit. Staff page/my-shift+ nav My shift; Reports adds an attendance table andGET /reports/work-sessions. Migration20260323150000_work_session.sql. Testback/tests/test_work_session.py. i18n:MY_SHIFT.*,REPORTS.WORK_SESSIONS_*,NAV.MY_SHIFT.
- GitHub #52 (follow-up):
GET /billing-customerslist now includesbirth_date(aligned with single-customer and order payloads). Customers (/customers) — table column and add/edit birth date field with i18n. Migration20260323120500_billing_customer_birth_date.sqladded to the repo (idempotentIF NOT EXISTS).docs/0032-github-issues-roadmap.md— per-theme implementation status for all #52 bullets;docs/0017-billing-customers-factura.mdandROADMAP.mdupdated accordingly. - GitHub #56: Kitchen / bar — wait-time progress bar (fill toward red threshold; color matches timer tier). Staff urgent —
order.staff_urgent,PUT /orders/{id}/staff-urgent, waiter toggle on Orders, kitchen badge + sort. Reports CSV/Excel — exportlangfromLanguageService; localized reservation source/status in Excel; products CSV includes category;report_export_labelsfallback for language tags. Migration20260323130000_order_staff_urgent.sql. Tests:back/tests/test_report_export_i18n.py.
- GitHub #50 — order customizations (pizza-style): Multi-select choice questions: staff save options as
{ choices: string[], multi: true }(or a plain list for single select). Public menu shows checkboxes;customization_answersmay storestring[]per question id.POST /menu/.../ordervalidates answers server-side and merges lines using normalized equality.OrderItem.customization_summarysnapshots “Label: value · …” for kitchen, staff orders, order history, current order, and printed invoice. Migration20260323121000_orderitem_customization_summary.sql. Moduleback/app/product_customization.py. Docs:docs/0031-order-customizations-plan.md.
- GitHub #24 (fast checkout): Staff Orders — Finish marks all active line items as delivered and marks the order paid in one API call (
PUT /orders/{id}/finish). Green Finish button on order cards, Finish (serve all & pay) in the status menu, and in Edit order; payment method matches Mark as paid. Requiresorder:update_statusandorder:mark_paid. i18n:ORDERS.FINISH_*in all locale files.
- Customers / Angular build:
createBillingCustomerTypeScript payload allowsbirth_date: nullso the billing form matches the API type andng servecompiles.
- Birth date on billing customers (#52 — CRM): Optional
birth_dateonBillingCustomer;POST/PUT/GET/billing-customers/{id}and nestedbilling_customeron orders return it. API + model + i18n keys; seedocs/0017-billing-customers-factura.md. (List endpoint, Customers UI, migration file, and roadmap status table completed in 2.0.48.)
- Google Maps on public pages (GitHub #54): Settings → Contact includes an optional Google Maps link (
public_google_maps_url). Public API also exposesaddressso guests see the street address on book, reservation view, and feedback pages, with an Open in Google Maps button when the maps URL is set. Note: Google does not allow auto-posting reviews; the existing Google review field remains the supported way to deep-link guests to leave a public review after private feedback.
- Per-restaurant currency (GitHub #41): Settings → Payments uses an ISO 4217 dropdown (EUR, USD, GBP, JPY, MXN, and other common codes).
currency_codeis the source of truth; the legacy symbol column stays aligned on save.
- Currency display (GitHub #41):
GET /tenant/settingsandPUT /tenant/settingsresponses, and the public menu payload, now expose a consistentcurrency_code+ symbol (no more EUR with a $ label from an old legacy field). Staff Products, Catalog, Orders, Reports, and customer menu formatting use the ISO code first; defaults remain EUR when unset. - Landing / table code (GitHub #38): The homepage field expected the menu URL token (from the QR code), while guests type the printed table name (e.g.
T01), which showed a confusing menu error.GET /public/table-lookupresolves token or printed name (case-insensitive, trimmed); the landing page calls it before opening/menu/.... If several tenants share the same table name, the user picks a restaurant.slowapi:public_table_lookuptakesresponse: Responseso rate limiting withkey_funcdoes not return 500. Tests:back/tests/test_public_table_lookup.py. Register page test matchertest-register-page.mjsextended for the updated guest hint.
- Guest feedback (staff):
/guest-feedbackshows a QR code for the public form (/feedback/{tenantId}), copy link, and Print QR — print stylesheet hides sidebar and the feedback table so owners get a clean page for table tents or the register. - i18n:
FEEDBACK.QR_*/COPY_FEEDBACK_URL/PRINT_QR/URL_COPIEDin all locale files.
- Products / menu customizations (GitHub #50, Phase 1): Staff
/productsedit form includes customer menu customizations for saved products (list, add, edit, delete, reorder). Backend:PATCH/DELETE/products/{id}/questions/{question_id},PUT/products/{id}/questions/reorder, stricteroptionsvalidation on create/update (choicelist,scalemin/max,text).ProductQuestion.optionstyping allows JSON arrays. - i18n: New
PRODUCTS.*strings for the customization editor (en,es,de,fr,ca,zh-CN,hi).
docs/0031-order-customizations-plan.md: Phase 1 marked done; API / staff UI rows updated.
- UX (GitHub #32): Form dialogs move keyboard focus to the first
input/select/textareawhen opened. The sharedappFocusFirstInputdirective now usesafterNextRenderplus short deferred passes so focus reliably wins over the opening click and late-rendered fields. Wired on billing Customers, inventory suppliers, items (create/edit + adjust stock), purchase orders (create + receive-goods), and the menu payment message step; existing modals (reservations, orders, settings, etc.) use the same directive.
- Tables (#48): Floor plan links to tile grid and list now open
/tables?view=tiles/?view=tableso the correct view shows; localStorage restore no longer overrides that choice on first paint. - Products / tenant settings (#41):
GET /tenant/settingsfills missingcurrency_code/currencywithEUR/€so staff product prices format in euros when the tenant never set currency.
- i18n (
en,es): Tables view labels clarify tile grid (mosaic) vs list view. - Deploy (GitHub Actions #49):
deploy-amvara9usesconcurrencyper branch, checks out the pushed branch (github.ref_name) instead of hard-codingmaster, and retries landing + health smoke checks with backoff.
- i18n: Register and settings email/website placeholders no longer use
example.com/ “example”-style domains (en,es,ca,fr,de,zh-CN,hi). - Seeds:
wine_importandpizza_importimage downloads send the sameUser-Agentasbeer_import(GitHub repo contact URL).
- Settings (SMTP user): Input placeholder uses
you@your-mail.cominstead ofuser@example.com. - Beer import seed: Wikimedia
User-Agentcontact URL points at the GitHub repo instead ofexample.com.
back/app/settings.py:PUBLIC_APP_BASE_URLfield description examples usesatisfecho.de/localhost:4202instead ofexample.com.- README: Backend tests blurb mentions
tests/test_settings_defaults.py. commit-hash.ts: Synced to match post-2.0.35 git short hash.
- Backend test
tests/test_settings_defaults.py: AssertsEMAIL_FROMmodel default isnoreply@satisfecho.de(guards against regressing toexample.com). Mentioned indocs/testing.md.
- Mail-related tests & docs: Replaced
@example.complaceholders in Puppeteer scripts (defaults and comments),AGENTS.md,docs/testing.md,docs/screenshots/README.md, anddocs/0001-ci-cd-amvara9.mdwith@amvara.de(or project-consistent demo addresses) per no-example.com rules for paths that register, log in, or rate-limit with real addresses. back/tests/test_contact_validation.py: Normalization case usesamvara.deinstead ofexample.com.- Backend default
EMAIL_FROM: Fallback isnoreply@satisfecho.deinstead ofnoreply@example.comwhen SMTP sends without a tenant-specific from address.
- Dev
environment.ts: Importversionfrompackage.jsonas a named binding (instead of the whole JSON object) for a slightly leaner dev bundle.
- Dev
environment.ts: Version fallback whencommit-hash.tsis0.0.0now comes frompackage.json(viaresolveJsonModule) instead of a manually maintainedDEV_VERSION_FALLBACKconstant.
- Dev
environment.ts:DEV_VERSION_FALLBACKmatchedpackage.jsonagain (was 2.0.21; used only whencommit-hash.tsstill has version0.0.0, e.g.ng servewithout runningget-commit-hash.jsfirst).
- Docker dev — landing footer hash:
docker-compose.ymlpasses optionalCOMMIT_HASHinto thefrontservice (bind mount has no.git)../run.shexportsCOMMIT_HASHfromgit rev-parse --short HEADwhen unset sodocker-entrypoint.sh/get-commit-hash.jscan show the correct short hash without committingcommit-hash.tsafter every commit. - Docs:
README.md,AGENTS.md, andconfig.env.exampledescribeCOMMIT_HASHfor manualdocker composeusage.
- Dev landing footer: Regenerated
front/src/environments/commit-hash.tswithnode scripts/get-commit-hash.jsso the displayed version matchespackage.json(it had stayed at 2.0.21 when only the bind-mounted front tree was present). The committed git short hash is refreshed the same way (anddocker-entrypoint.shruns this script on container start when/app/scripts/get-commit-hash.jsexists).
- README: Under Development, added Backend tests with the
pytest /app/testsDocker one-liner and a pointer todocs/testing.md. - Docs / comments:
get_current_orderfallback comment now says open orders (not paid/cancelled) instead of “unpaid” only.
GET /menu/{token}/order: Fallback order lookup now excludes paid and cancelled rows in the SQL filter (same behaviour as before, less redundant filtering in Python).
- Docs:
docs/testing.md— reservation capacity tests: note they run underpytest /app/tests, add a-Tdocker compose execexample, and clarify why SQLite uses a minimal table set.
- Comments:
create_ordershared-order branch comment now mentions cancelled as well as paid when describing when a new order row is created.
- Backend test
tests/test_public_menu_order_response.py: Asserts the firstPOST /menu/{token}/orderon a table returnsstatus: "created"and a follow-up post returns"updated"with the sameorder_id(regression guard for shared-orderis_new_order).
- Public menu
POST /menu/{token}/order: Removed a line that always forcedis_new_order = False, so the first order on a table again returnsstatus: "created"and publishesnew_orderto Redis (was incorrectly alwaysupdated/items_added).
- Backend: Replaced
printdebug noise inget_menuandcreate_orderwithlogger.debug(andlogger.exceptionon menu query failure). Dropped a duplicated CORS comment line.
- Backend: Replaced deprecated FastAPI
@app.on_event("startup" | "shutdown")with a singlelifespancontext manager (same DB init, migrations, and reservation reminder heartbeat). RemovesDeprecationWarningnoise duringpytestand matches current FastAPI guidance.
- Public menu — customer cancel: Clearing the table’s
active_order_idwhen the shared order is cancelled so the nextPOST /menu/{token}/ordercreates a new order (matches “new order after cancel” behaviour and avoids reusing a cancelled row). GET /menu/{token}/order: Ignores cancelled shared orders the same way as paid ones so customers do not see a cancelled cart as current.
- Backend tests:
RATE_LIMIT_ENABLED=falsefor pytest /PgClientTestCase(tests/conftest.py,tests/pg_client_mixin.py) so SlowAPI does not break or flake onTestClientruns. Session-isolation test bindsOrder.session_idin the DB to assert PUT/DELETE session checks. Guest feedback, payment security, and public-tenant WhatsApp tests use the same Postgres rollback mixin instead of SQLitecreate_all.
- Backend dev/test deps:
httpxandpytestinback/requirements.txtsoTestClienttests run inside the back Docker image after rebuild (docker compose build back).
- Docs:
docs/testing.md— run backendpytestin the back container (afterdocker compose build back). tests/test_users_me_anonymous.py: UsesTestClientonly (no in-memory SQLitecreate_all; full schema uses Postgres-only types such as JSONB). Importsappfrom the package root so it runs both on the host (back/) and in Docker (/app).
- API —
GET /users/me: Returns 200 with JSONnullwhen there is no valid session (instead of 401). Same as before when logged in (user JSON). Lets the SPA check auth without a failed-network / console noise for guests. Breaking for any client that relied on 401 to detect “not logged in”; use status 200 + null body instead.
- Backend test:
tests/test_users_me_anonymous.pyasserts anonymousGET /users/me→ 200 + null (uses FastAPITestClient;httpxis now inrequirements.txt).
- Landing page: Stopped calling
checkAuth()a second time on load.ApiServicealready requests/users/meonce at bootstrap; the landing component now waits for that to finish viawaitForInitialAuthCheck(), then redirects or loads public tenants. Cuts duplicate requests and avoids an extra 401 in the network log for guests.
- Docker dev — version footer:
docker-entrypoint.shrunsget-commit-hash.jsbeforeng servesocommit-hash.tstrackspackage.jsonversion on each container start. The script keeps the previous short hash when git is unavailable (e.g. only./frontis bind-mounted), instead of overwriting withgit hash.
- Angular NG0505 (hydration): Removed
provideClientHydrationfrom the browserapp.config.ts. Shipped dev and prod builds use CSR only (development-no-ssr,production-static), so there was no serialized SSR payload and the client logged NG0505 on every load. SSR builds still get hydration viaapp.config.server.ts. - Dev landing version string: Regenerated
commit-hash.tssoversionmatchespackage.json(was0.0.0, which forced the staleDEV_VERSION_FALLBACK). AlignedDEV_VERSION_FALLBACKwith the current package version for when the script has not run.
- Reports (NG0955):
@forover “by product” now tracks product id + name (matches backend aggregation key), so duplicateproduct_idvalues no longer break change detection. Revenue chart Y-axis ticks track by Y position so max revenue 1¢ does not duplicate thetick.valuekey.
- Reports export (#35): CSV/Excel column and sheet titles follow the UI language.
GET /reports/exportaccepts optionallang(e.g. en, es, de, ca, fr, zh-CN, hi). The app sends the current ngx-translate language when downloading exports.
- Reports & Products — locale for numbers (#41): Shared
intlLocaleFromTranslatemaps the UI language to a BCP 47 locale forIntl— currency and short dates on Reports match the selected language, and changing language refreshes formatted values. Products: tenant currency settings load before the product list (avoids a flash of the wrong symbol); price formatting uses the same locale mapping; when settings omit currency, the default prefix is € instead of $.
- Customers / orders (#30): English string Print invoice instead of “Print Factura” for
CUSTOMERS.PRINT_FACTURA. - Catalog (#43, #42, #44): “Set your price” field uses a currency prefix + input row so the symbol no longer overlaps the amount; catalog card image area has padding and a neutral background; Remove from menu is an outline button so it reads clearly vs solid Add to menu.
- Reports (#45): Data table cells use vertical-align: middle; Share (%) column cells get a minimum height so the last row aligns better with the bar + percentage.
- Guest feedback (issue #54 – MVP): Public page
/feedback/:tenantId(optional?token=reservation magic link) for star rating, optional comment, and optional contact fields; thank-you step with button to the tenant’s Google review URL when configured. Staff list at/guest-feedback(same access as reservations). Settings: Google review link (public_google_review_url, http/https only). API:POST /public/tenants/{id}/guest-feedback(rate-limited per IP/hour,RATE_LIMIT_GUEST_FEEDBACK_PER_HOUR),GET /tenant/guest-feedback; public tenant payloads includepublic_google_review_url. Migration20260322190000_guest_feedback_and_google_review_url.sql. i18n:FEEDBACK.*,NAV.GUEST_FEEDBACK,SETTINGS.PUBLIC_GOOGLE_REVIEW_*,RESERVATIONS.VIEW_FEEDBACK_PAGE(en, de, es + en strings for fr, ca, hi, zh-CN). Reservation view links to the feedback form with token when present.
- Cancel reservation modal: The footer no longer shows the left dismiss button—only Yes, cancel reservation remains (header × and clicking the backdrop still close without cancelling). Applies to staff reservations list and public reservation view. Other confirmation modals keep both footer buttons via
showSecondaryButton(defaulttrue).
- Public booking – “View or cancel” link: Reservations created via
/book/...while a staff session cookie was present had notoken, so the success-page link and token-based flow failed. Every new reservation now receives a magic-link token (staff-created bookings included). - Cancel reservation dialog: Confirm and dismiss actions no longer both read as “Cancel” in English; primary action is Yes, cancel reservation, secondary is Close (staff list and public reservation view).
- Contact on confirmation: After a successful public booking, the success card shows the restaurant’s phone, WhatsApp, and email when configured.
- Email validation:
email-validatoronPOST /register,POST /register/provider, adminPOST/PUT /users, and reservation create/update when an email is provided; registration and booking forms use stricter client checks. - Phone validation:
phonenumbers(libphonenumber) on reservation create/update and optional provider registration phone; numbers stored in E.164 when valid. - 12-month booking horizon: Reservation date cannot be more than ~12 months ahead (366 days, tenant-local “today”) on create and staff update.
- Delay notice:
PUT /reservations/{id}/publicrate-limits non-empty delay updates per client IP per reservation (rolling 1h, Redis; configurable viaRATE_LIMIT_RESERVATION_DELAY_PER_HOUR). UImaxlength="500"and APImax_length=500on delay text.
- POST /register and POST /register/provider 500 (SlowAPI): With
headers_enabled, SlowAPI’s sync wrapper injects rate-limit headers into aResponseinstance. Endpoints that returned a plaindictpassedNonethere and raised, producing500 Internal Server Erroron account creation. Both routes now returnJSONResponsewith201 Created(same approach as public menu and table mutations).
- Reservations – turn time and walk-in buffer: Tenant settings
reservation_average_table_turn_minutes(optional) andreservation_walk_in_tables_reserved(default 0). When turn is set, capacity treats each table as busy only for[start, start + turn)from the earliest ofreservation.seated_at(set when staff seats),order.created_at, ortable.activated_at/ now for active sessions. When turn is unset, busy tables remain excluded for the full calendar day (tenant TZ) as before. Walk-in buffer removes the smallest tables (byseat_count, thenid) from the reservation pool. Migration20260322120000_reservation_turn_walk_in_seated_at.sql(reservation.seated_at, tenant columns, backfill seated rows). Settings UI (en, es, de, ca, fr) anddocs/0025-reservation-overbooking-detection.md. Unit tests:back/tests/test_reservable_capacity_turn_walkin.py.
- Reservations – opening hours and per-slot capacity:
POST/PUT /reservationsreject times outside opening hours; slot capacity endpoints evaluate per requested time (not only once per day). Overbooking report top-leveltotal_seats/total_tablesare physical counts; each slot row uses reservable capacity for that time. Public book time dropdown respects opening hours.GET /tables/with-statusand seating a reservation treatis_activeandpartially_deliveredorders as occupied; seating is blocked if the table is still activated for ordering.
- Catalog (GitHub #42–#44): Card image area uses normal card inset (no full-bleed negative margin), extra inner padding, centered
object-fit. “Set price” row: wider currency cell and input padding so values don’t overlap the € symbol (#43). Remove from menu uses light red fill + dashed border vs solid primary Add (#44). - Reports (GitHub #45): Share column cells (
td.share-with-bar) use a tallermin-heightandbox-sizingso the % row aligns with bar height. - Product image upload (GitHub #40):
POST /products/{id}/image(and provider product image upload) inferContent-Typefrom filename or Pillow when the client omits it; deleting the previous file supports bothtenant_id/products/…andproviders/…catalog paths. - Scripts:
go-ahead-loop.shonly pull + pytest + landing smoke (no auto-commit-hash.tscommits; syncing that file each cycle would always lagHEADand create endless chore commits). - API service: WebSocket
console.logdiagnostics (open/close/reconnect) run only when!environment.production;console.warn/console.errorunchanged. - Menu (public order): Geolocation errors are ignored silently when building the order payload (no
console.log). - Purchase orders: Remove debug
console.logafter loading inventory items for the create modal. - Seeds:
update_wine_detailsImportError message refers towine_importTusumiller helpers (not onlyfetch_wine_detail_page). - Seeds:
update_wine_pricesandupdate_wine_detailsreuseFORM_DATA_BASEfromwine_importfor Tusumiller searchPOSTbodies (withupdate_wine_pricesstill reusingAPI_ENDPOINT/HEADERS/COOKIES). - Seeds:
productsCLI hoistsargparseto module level. - Seeds:
beer_import,pizza_import, andwine_importhoisttracebackto module level (CLI error handler). - Seeds:
migrate_provider_tokenshoistsshutilanduuid4imports to module level. - Seeds:
update_wine_detailsimportsrequestsandAPI_ENDPOINT/HEADERS/COOKIESat module level instead of inside the per-product loop. - Seeds:
wine_importdrops a redundant innerimport re(module already importsreat top level). - Docs:
0004-deployment.mdHTTPSconfig.envsnippet usesyourdomain.cominstead ofexample.comfor API / WS / CORS origins. - Env examples:
.env.examplelogin placeholders use@your-restaurant.com;config.env.exampleCORS /PUBLIC_APP_BASE_URLcomments avoidexample.com. - Docs:
0005-email-sending-options.mdSendGrid/Resend example snippets useguest@yourrestaurant.cominstead ofcustomer@example.com. - Docs:
AGENTS.mddocuments optionalssh amvara9access from the configured dev machine for production diagnostics; reservation-email troubleshooting doc updated accordingly.
- Scripts:
scripts/start-go-ahead-loop-background.sh— startsgo-ahead-loop.shwithnohup, records.go-ahead-loop.pid(gitignored); same env vars as the main loop. - Scripts:
scripts/go-ahead-loop.sh— opt-in long run (default ~8h):git pull --rebase --autostash, Docker pytest +npm run test:landing-versionon an interval (no auto-commits;commit-hash.tsstays manual/agent). RequiresGO_AHEAD_LOOP=1; log defaults to.go-ahead-loop.log(gitignored). Documented indocs/testing.md;AGENTS.mdpoints to it for long-running smokes. - Orders – unmark paid: Staff can revert a paid order to unpaid. "Unmark paid" in the status popover clears the paid mark only; order status is restored from item statuses. Backend:
PUT /orders/{id}/unmark-paid; permissionorder:mark_paid. i18n:ORDERS.UNMARK_PAID(en, es, ca, de, fr, zh-CN, hi). - Orders – soft-delete: Orders can be marked as deleted (excluded from list and book-keeping) for test/cleanup. Backend:
Order.deleted_at,deleted_by_user_id; migration20260320100000_add_order_deleted_at.sql;DELETE /orders/{id}(soft-delete, clears table active_order_id); permissionorder:delete(owner, admin); list, reports, and public order history exclude deleted orders. Frontend: "Delete order" button on order cards and in history grid (with confirmation); i18n:ORDERS.DELETE_ORDER,DELETE_ORDER_CONFIRM, etc.
- Orders – status popover: Popover opens directly below the status button, has no animation, and uses a higher z-index so it appears above adjacent order cards.
- Tables – canvas header:
/tables/canvasnow shows the same header options as/tables: title "Tables", Floor Plan (active), List View link, Tiles/Table view links, Add Table button (focuses shape palette), and Save Layout when there are unsaved changes.
-
Orders – pre-pay (mark as paid before delivery): Staff can mark an order as paid on
/staff/orderseven when not all items are delivered (e.g. customer pays while kitchen is still preparing). Backend:PUT /orders/{id}/mark-paidno longer requires order status “completed”; it only rejects already-paid or cancelled orders. Frontend: “Mark as paid” appears in the order status dropdown for any non-paid, non-cancelled order (pending, preparing, ready, partially_delivered, completed). -
Docs – printing when backend is outside restaurant WiFi:
docs/PRINTING.mddescribes the print-agent-on-LAN approach, options (WebSocket bridge, browser extension + native host, Node/Python agent), and references (WebApp Hardware Bridge, Ninja Printer, node-thermal-printer, etc.). -
Tax system (IVA): Tax-inclusive pricing with per-line and per-rate totals on invoices. Backend:
Taxmodel (name, rate_percent, valid_from, valid_to), tenant default tax, product-level tax override; order items store applied tax snapshot (tax_id, tax_rate_percent, tax_amount_cents). Spanish IVA: 10% (food/drinks immediate consumption), 21% (services), 0% (exempt). Settings: default tax dropdown, Taxes tab with CRUD; Products: tax override dropdown. Seed:back/app/seeds/seed_spanish_taxes.py. Migrations:20260318100000_add_tax_system.sql, product availability dates, provider tenant_id,20260318130000_ensure_provider_tenant_id.sql(idempotent repair). -
Smoke test – sidebar navigation:
test-settings-logo-upload.mjsnow visits every sidebar link after the logo flow and asserts no 5xx responses (catalog, tenant-products, tables, etc.).
- Orders – history tab: "Historial de Pedidos" now includes paid orders (
completedOrdersfilter includes statuspaid) so paid invoices appear in the history list. - Kitchen/bar timer and frontend build: Timer settings button and per-order “Waiting” timer now appear after fixing Angular template errors: use component methods instead of
Object.keys/Array.isArrayin templates (kitchen-display, orders, menu); optional chaining fortenant()in book and reservation-view. Added Puppeteer testtest-kitchen-timer.mjsand npm scripttest:kitchen-timer. - Backend 500 (slowapi): Endpoints that return dict/list under global rate limiting now inject
response: Responseand/or returnJSONResponseso slowapi can set rate-limit headers. Fixed for/catalog,/catalog/categories,/catalog/{id},/tenant-products,/tables/with-status, tenant settings, tenant logo, and tax CRUD. Public menu endpoints now returnJSONResponse:GET /menu/{token},GET /menu/{token}/order,GET /menu/{token}/order-history,POST /menu/{token}/order,POST /menu/{token}/order/{id}/request-payment,POST /menu/{token}/call-waiter,DELETE/PUTorder items,DELETEorder (fixes 500 when opening table menu or requesting payment). Repair migration20260318130000_ensure_provider_tenant_id.sqlensuresprovider.tenant_idexists when schema version was applied without the column.
- Toasts – dismiss only on action: Success and error toasts no longer auto-dismiss; they stay until the user closes them. Applies to Orders, Working plan, Tables, Provider dashboard, and Settings (Settings success/error toasts now have a dismiss button).
- Orders – waiter alert shows customer message: When a customer sends a message with “Call waiter” or “Request payment”, the staff alert banner now displays that message (in quotes) below the table name.
- Docs – consolidation: Removed
docs/0006-gmail-setup-instructions.md(redundant withdocs/0018-gmail-setup.md); all references (README, ROADMAP, config.env.example) now point to 0018. Mergeddocs/0003-deploy-server.mdintodocs/0004-deployment.md: single deployment guide with configuration (API_URL, WS_URL, CORS) and deploy steps (git pull, compose, migrations, seeds). Deleted 0003; docs index and CHANGELOG updated.
-
Rate limiting (extended): Public menu endpoints 30/min per IP; file uploads (tenant logo, product image, provider product image) 10/hour per user; admin/management endpoints (tenant settings, tables, providers) 30/min per user; per-order payment attempts 3/hour per IP. See
docs/0020-rate-limiting-production.mdand ROADMAP.md. -
HAProxy SSL (amvara9) – durable certificate path: SSL cert loaded from
./certbot/haproxy-certs(same path used for certbot on amvara9). Compose mounts this dir into HAProxy; deploy createscertbot/wwwandcertbot/haproxy-certsand does not overwrite certs. Workflow:certbot certonly --webroot -w .../certbot/www, thencat fullchain+privkey > certbot/haproxy-certs/satisfecho.de.pem, thendocker exec pos-haproxy kill -HUP 1. Seecertbot/README.mdanddocs/0026-haproxy-ssl-amvara9.md. -
Deploy: Script run from server repo (not stdin); version inject via
front/scripts/inject-version-into-index.js; force-remove front image before build; smoke test cache-bust and 5s delay; deploy step timeout 20 min. -
Docs:
docs/0026-haproxy-ssl-amvara9.md(SSL durable setup and restore);certbot/README.md,certs/README.md(point to certbot path).
- HAProxy: Bind 443 with
ssl crt /etc/haproxy/certs; redirect HTTP to HTTPS except/.well-known/acme-challenge; removeddaemonfor Docker. - Working plan – Owner and Administrator as workers: Owner and Administrator can be selected as the worker when adding/editing shifts. Backend:
create_shift/update_shiftacceptownerandadmin; frontend:getUsersForSchedule()includes owner/admin in dropdown.
- Angular hydration (NG0505): Added
provideClientHydration(withEventReplay())to the server app config (app.config.server.ts) so the server includes serialized hydration data in the response when using SSR. This removes the console warning "Angular hydration was requested on the client, but there was no serialized information present in the server response." - Deploy (amvara9) – front CSS / stale build: Deploy script now builds the front image with
--no-cacheso each deploy serves assets from the current code (fixes wrong styling e.g. Settings > Opening hours). Nginx in the front container sendsCache-Control: no-cachefor the HTML document so clients get new hashed asset URLs after deploy. Seedocs/0024-deploy-css-fix-amvara9.md. - Deploy (amvara9) – version not updating: Fixed
docker compose buildflag order (build --no-cache front); addedup -d --force-recreateso the front container is recreated with the new image; added a post-deploy step that prints the version served by the front container for verification.
- Public reservation – confirmation button: Button on the confirmation page ("View or Cancel") now uses high-contrast styling (white background, dark text) so it remains readable when the tenant sets a blue or other colored public background.
- Tenant branding (public theme): Public background color (hex) and header background image for customer-facing pages. Backend:
Tenant.public_background_color,Tenant.header_background_filename; migrations20260319100000_add_tenant_public_background_color.sql,20260319110000_add_tenant_header_background.sql; GET/POST/DELETE for header image (/tenant/header-background); public tenant and menu endpoints expose theme. Frontend: Settings > Business profile: color picker with RAL5002 (Azul) preset, header background upload and remove; book, menu, and reservation-view use tenant background color and header image (dark overlay when image set). i18n:SETTINGS.PUBLIC_BACKGROUND_COLOR,HEADER_BACKGROUND, etc. (en, es, ca, fr). Seedocs/0028-tenant-public-branding.md. - HAProxy self-signed certificate: Base
docker-compose.ymlmounts./haproxy/certs(containsdefault.pem) so HAProxy can bind 443 without a real certificate. Fixes "unable to stat SSL certificate" whencertbot/haproxy-certsis missing. Production override still usescertbot/haproxy-certs. Seehaproxy/certs/README.md. - Cursor rule – commit/changelog/version:
.cursor/rules/commit-changelog-version.mdcreminds to review CHANGELOG, README, and docs on "commit" and to bump version when changelog has substantial changes. - Smoke test – amvara9:
front/scripts/test-amvara9-smoke.mjsfor amvara9 smoke runs.
- Cost price and profit: Optional cost price per product for profit calculation. Backend:
cost_centson Product, TenantProduct, and OrderItem (snapshot when item is added); migration20260318150000_add_product_cost_cents.sql; product/tenant-product CRUD and order item creation accept and store cost; when adding from catalog, cost defaults from provider price; sales reports and CSV/Excel export include total cost, total profit, and per-product/category/table/waiter cost and profit. Frontend: Products form and table: cost price field and column; Reports: Total cost and Total profit summary cards, Cost and Profit columns in by-product/category/table/waiter when cost data exists; i18n for cost/profit labels (en, es, ca, de, fr, hi, zh-CN).
- Tables view – 500 when assigning waiter or activating table: Rate-limited table endpoints (
PUT /tables/{id}/assign-waiter,POST /tables/{id}/activate,POST /tables/{id}/close,POST /tables/{id}/regenerate-pin) now returnJSONResponseso slowapi can inject rate-limit headers (fixes "parameter response must be an instance of starlette.responses.Response").
- Production build (TS2367): Cast
versiontostringinenvironment.tsso the comparison with'0.0.0'is valid when the build injects the real version from package.json (fixes "This comparison appears to be unintentional because the types have no overlap" duringproduction-staticbuild).
- Reservations – overbooking detection and seating warnings (0025): Per-slot capacity checks and warnings. Backend: Capacity/demand helpers;
GET /reservations/overbooking-report(per-slot metrics: over_seats, over_tables);GET /reservations/slot-capacity(seats/tables left for create/edit);GET /reservations/upcoming-no-table-count(for seat modal); create/update reservation returns 400 when slot would be over capacity;GET /reservations/next-availableuses capacity (party_size param) instead of "first empty slot";GET /tables/with-statusincludesupcoming_reservationper reserved table; reports payload addsreservations.overbooking_slots_count. Frontend: Reservations list shows "Overbooked" badge for over-capacity slots; create/edit shows seats left and tables left, keeps form open on 400; seat modal shows "You have N upcoming reservation(s) today with no table assigned" and per-table "Table X has an upcoming reservation at HH:MM (Name). Seat here anyway?"; reports show Overbooked slots card when count > 0. i18n:RESERVATIONS.OVERBOOKED,SEATS_LEFT,TABLES_LEFT,UPCOMING_NO_TABLE,TABLE_UPCOMING;REPORTS.OVERBOOKING_SLOTS(en, es). Seedocs/0025-reservation-overbooking-detection.md.
- Dashboard – Working plan card: Quick-action card for Working plan (shift schedule) on the dashboard, with title and description from i18n.
- Reports – revenue graph over time: On
/reports, a Revenue over time chart shows daily revenue as an SVG line chart with gradient area fill. Uses existingsummary.dailydata; Y-axis shows formatted currency (max, mid, zero), X-axis shows first/middle/last date. i18n:REPORTS.REVENUE_OVER_TIME(en, de, es, fr, ca, zh-CN, hi). - Tables – reassign orders and reservations when deleting: Deleting a table that has orders no longer only blocks with an error. The UI offers to reassign its orders and reservations to another table, then delete. List and canvas table views: when delete returns 400 ("has orders"), a modal opens to choose the target table and confirm Reassign and delete. i18n:
TABLES.REASSIGN_AND_DELETE_TITLE,REASSIGN_AND_DELETE_MESSAGE,REASSIGN_TO_TABLE,REASSIGN_AND_DELETE,VIEW_TILES,VIEW_TABLE(en, de, es, fr, ca, zh-CN, hi). - Tables – view mode persisted: Tiles/table view preference is stored in
localStorageand restored on load; view toggle buttons have icons and titles. - Tables – inline edit: Table list inline edit supports floor dropdown and seat count in separate cells; layout adjusted for clarity.
- App – dev favicon: In development, the app uses a white favicon (
favicon-dev.svg) to distinguish from production. - Working plan and opening hours – i18n (all locales):
WORKING_PLANsection andSETTINGS.PERSONNEL_PER_SHIFT,STAFF_*added in ca, es, fr, hi, zh-CN;NAV.WORKING_PLAN,DASHBOARD.WORKING_PLAN_TITLE/WORKING_PLAN_DESCwhere missing. - .env.example: Comments for test credentials (
LOGIN_EMAIL,LOGIN_PASSWORD,TENANT_ID) for Puppeteer and other scripts. - Docs:
docs/0022-oauth-social-login-notes.md(OAuth/social login design notes). - Puppeteer:
front/scripts/test-settings-logo-upload.mjsfor settings logo upload;test-tables-page.mjsimprovements.
- Working plan (shift schedule) – full feature (0021): Shift CRUD with opening-hours alignment, personnel-per-shift in Settings (bar, waiter, kitchen, receptionist), owner notification (* in sidebar when staff update the plan), time step (30 min / 1 h), and "use any hour" option for cleaning. Schedule access for owner, admin, kitchen, bartender, waiter, receptionist. Backend:
Shiftmodel, GET/POST/PUT/DELETE/schedule, GET/schedule/notification; tenant fieldsworking_plan_updated_at/working_plan_owner_seen_at. Frontend:/working-planweek view, add/edit/delete modal. Seedocs/0021-working-plan.md. - Working plan – suggested date: When adding a shift, the form suggests the next open day with a free slot for the current user's role; closed days (e.g. Monday) are skipped. Owner/admin see the first day with any role gap; fallback is the first open day of the week. Current user is pre-selected when in the schedule list.
- Working plan – toast notifications: Success toasts on create ("Shift saved."), update ("Shift updated."), delete ("Shift removed."); error toasts with API message (e.g. role restriction). i18n:
WORKING_PLAN.SAVED,UPDATED,SAVE_FAILED,DELETED,DELETE_FAILED(en, de). - AGENTS.md – compilation errors rule: MUST ALWAYS DO rule: when working on the frontend, always check
docker compose logs --tail=80 frontfor TypeScript/Angular build errors before concluding a change is done.
- Working plan – owner seen: The asterisk (*) next to "Working plan" in the sidebar is cleared when the owner visits the page; backend now commits the "seen" timestamp after
_mark_working_plan_seen_by_ownerin GET/schedule. - Backend – shift assignment: Receptionist can be assigned to shifts;
create_shiftallows kitchen, bartender, waiter, and receptionist (error message updated). - Docs:
docs/0021-working-plan.mdadded (implementation plan and status);docs/0023-prioritisation-019-022.mdmarks 0021 as Done;docs/testing.mdWorking plan test described as schedule roles (owner, admin, kitchen, bartender, waiter, receptionist).
- Working plan – confirmation modal: Fixed NG8002 (removed invalid
[open]and other bindings onapp-confirmation-modal; use conditional render and correct inputs). - Settings – setStaffRequired: Method accepts
stringfor role key to fix template type error; cast internally. - Working plan – DayHours: Interface moved above
@Componentso the class is correctly decorated (fixes "Decorators are not valid here" build error). - Working plan – getApiErrorMessage: Centralised API error extraction (string or validation array) for toast and form error.
-
Testing docs – known issues and follow-up:
docs/testing.mdnow has a Known issues and follow-up (to address later) section: test-provider-register (unknown state), debug-reservations-public (422/time validation), login tests hitting 429 when run in quick succession, and no test data cleanup for provider/restaurant registration. Coverage summary table includes Kitchen display; cross-reference §5 → §4 fixed. -
Rate limiting: API rate limits per the security roadmap: global 100 req/min per IP; login
POST /token5 per 15 minutes; registerPOST /registerandPOST /register/provider3 per hour; payment endpoints 10/min. Uses slowapi + Redis (in-memory fallback when Redis is down). Client IP fromX-Forwarded-Forwhen behind proxy. Each 429 is logged (path, method, client). Login page shows "Too many login attempts. Please try again later." on 429. Env:RATE_LIMIT_ENABLED,RATE_LIMIT_REDIS_URL,RATE_LIMIT_*(seedocs/0020-rate-limiting-production.md). Tests:test:rate-limit(API, 429 after limit),test:rate-limit-puppeteer(Puppeteer, login 6 wrong attempts → error banner). -
Reservations – no-show feature (standing out): Because “they didn’t show” deserved its own status. Staff can mark as no-show (with confirmation), freeing the table and recording the outcome instead of pretending it was a cancellation. Filter and badge for no-shows; optional Send reminder for booked reservations with an email (SMTP required). Backend:
ReservationStatus.no_show, status handler clearstable_id;POST /reservations/{id}/send-reminder;send_reservation_reminderin email service. i18n: STATUS_NO_SHOW, NO_SHOW, SEND_REMINDER, etc. (en, de, es, fr, ca, zh-CN, hi). The one feature that admits your guests are human. -
No-show – implementation plan:
docs/0019-no-show-implementation-plan.mddocuments goals, what was implemented, step-by-step implementation plan (backend model/API/email, frontend, i18n), optional extensions (reports, view link in reminder, scheduled reminders), and a checklist. README Documentation table updated with link to the new doc. -
Reports – reservations by status: Sales report now includes reservation counts by status (booked, seated, finished, cancelled, no_show). Backend:
reservations_summary.by_statusin_build_report_payload; Excel export Reservations sheet includes Status/Count rows. Frontend: Reports page shows “Reservations – By status” block with translated labels;SalesReport.reservations.by_statusandgetReservationStatusLabel(). i18n:REPORTS.RESERVATIONS_BY_STATUS(en, de, es, fr, ca, zh-CN, hi). -
Products page – search: On
/products, a search input above the category filters lets you filter the product list by name, ingredients, description, category, or subcategory (case-insensitive, live as you type). Works together with the existing category/subcategory ribbons. i18n:PRODUCTS.SEARCH_PLACEHOLDER(en, de, es, fr, ca, zh-CN, hi). -
Per-tenant SMTP / email settings: Restaurant owners can configure their own SMTP (host, port, TLS, user, password, from address/name) in Settings → Email (SMTP). If left empty, the app uses the global SMTP from
config.env. Backend: new optional fields onTenantandTenantUpdate; GET/PUT/tenant/settingsread and update them (password masked in responses). Email service uses tenant config when bothsmtp_userandsmtp_passwordare set, otherwise falls back to global. Migration20260316150000_add_tenant_smtp_email.sql. i18n:SETTINGS.EMAIL_SETTINGS,SMTP_*,EMAIL_FROM*(en). -
Gmail setup guide:
docs/0018-gmail-setup.md– step-by-step: create Gmail account, enable 2FA, create App Password at myaccount.google.com/apppasswords, then enter Gmail and password in POS → Settings → Email (SMTP). -
SMTP debug script:
back/scripts/debug_smtp.py– minimal script that parsesconfig.env(handles passwords with apostrophes when wrapped in double quotes), tests SMTP connection, and optionally sends a test email. Run:PYTHONPATH=back python back/scripts/debug_smtp.py [to_email]from repo root, or pipeconfig.envinto the back container. -
Products – category labels from i18n: On
/products, the main category names (Starters, Main Course, Desserts, Beverages, Sides) are now translated. AddedPRODUCTS.CATEGORY_STARTERS,CATEGORY_MAIN_COURSE,CATEGORY_DESSERTS,CATEGORY_BEVERAGES,CATEGORY_SIDESin en, de, es, ca, fr, zh-CN, hi. Category filter pills, dropdowns, table column, and Categories tab use the translated labels; stored values remain in English. -
Supply chain hardening (npm): Pinned all dependency versions in
front/package.jsonand the lockfile root to exact versions (no^/~). Addedfront/.npmrcwithsave-exact=trueandignore-scripts=trueso new deps are pinned and install lifecycle scripts never run. Dockerfiles (front/Dockerfile,front/Dockerfile.prod) now copy.npmrcand usenpm ci --ignore-scripts. -
French (Français) locale: New language for Morocco and Francophone users. Added
frto supported languages (label "Français", locale fr-FR) and full translation filefront/public/i18n/fr.json. Language picker and app UI available in French. -
Translations: Review of remaining locales (de, es, ca, hi, zh-CN): removed English-only strings (e.g. Dashboard → Übersicht, Website → Webseite in German; Balance → Saldo, Stock → Existencias in Spanish), added missing keys (COMMON, MENU, ORDERS, REPORTS, RESERVATIONS, KITCHEN_DISPLAY, SETTINGS where needed), and localized placeholders (e.g. yourbusiness → example/udaharan where appropriate).
-
Invoice – open source footer: Printed invoices (Print invoice / Print Factura) now show a small grey line at the bottom: "Open source · Made with ♥ in Barcelona and Mexico", GitHub repo URL (plain text for print), app version, and commit hash. A separator line above this block matches the total-row line; spacing is symmetric. Styled in 9px grey. i18n:
ORDERS.INVOICE_FOOTER,ORDERS.INVOICE_OSS_PREFIX(en, es, ca, de, hi, zh-CN). -
Tables – confirm before closing (#18): Clicking "Close Table" now opens a confirmation modal ("Close table "…"? This will end the current session.") with Confirm and Cancel. On confirm, the table is closed and a success snackbar ("Table closed.") is shown. i18n:
TABLES.CLOSE_TABLE_CONFIRM,TABLES.TABLE_CLOSED(en, de, es, fr, ca, zh-CN, hi). -
Login and register – language selector (#16): The login and create-account (register) pages now include the same language dropdown as the rest of the app, in the top-right of the auth card header, so users can switch language before signing in or creating an account.
-
Password confirmation and show/hide (#17): Registration (tenant and provider) and user create/edit now have a "Confirm password" field; both fields must match before submit. All password inputs (login, register, provider login/register, Users modal) have an eye icon to toggle visibility. i18n:
AUTH.CONFIRM_PASSWORD,AUTH.PASSWORDS_DO_NOT_MATCH,AUTH.SHOW_PASSWORD,AUTH.HIDE_PASSWORD;USERS.CONFIRM_PASSWORD,USERS.PASSWORDS_DO_NOT_MATCH,USERS.SHOW_PASSWORD,USERS.HIDE_PASSWORD(en, de, es, ca, fr, zh-CN, hi). -
Reservations – email field: The table reservation (book a table) page and staff reservations now include an optional Email field. Public booking form, success confirmation, and view-by-token page show it when provided; staff list and create/edit modal display and persist it. Backend:
customer_emailonReservation,ReservationCreate,ReservationUpdate; migration20260316160000_add_reservation_customer_email.sql. i18n:RESERVATIONS.CUSTOMER_EMAIL(en, de, es, ca, fr, zh-CN, hi). -
Opening hours – copy to other days and summary: In Settings → Opening hours, a "Copy from [day]" dropdown and Copy to other days button copy one day’s hours to all others. A formatted summary (e.g. "Mon–Fri 09:00–22:00, Sat 10:00–20:00, Sun closed") is shown above the grid. Public tenant API now returns
opening_hoursso the book page can display them. i18n:SETTINGS.COPY_FROM_DAY,SETTINGS.COPY_TO_OTHER_DAYS,BOOK.OPENING_HOURS(en, de, es, ca, fr, zh-CN, hi).
- ROADMAP and README – customer accounts: Customer accounts (end-customer registration, login, email verification, MFA, order history, customer-facing invoices) are now listed under Completed in ROADMAP.md. README planned list and Roadmap section no longer list them as planned; docs table entry for
docs/0002-customer-features-plan.mddescribes the implemented scope. - Opening hours summary – localized: The opening hours summary in Settings → Opening hours and the opening hours text on the public book page now use the current UI language: short day names (e.g. Lun, Mar, Mié in Spanish; Mon, Tue, Wed in English) via
Intl.DateTimeFormat, and the word "closed" fromSETTINGS.CLOSED(e.g. Cerrado, Closed, Geschlossen). - Opening hours and reservation time – 15-minute steps, 24h format: Settings opening hours and the book (reservation) page now use time selectors with minutes 0, 15, 30, 45 only, in European 24h format (e.g. 20:00). Settings: time inputs replaced by dropdowns; existing values are rounded to the nearest quarter hour. Book page: time is a dropdown, default 20:00; opening hours are shown in the hero when set. Next-available reservation slot API returns 15-minute slots.
- Kitchen display: On
/kitchen, only orders that have at least one item in pending or preparing are shown; within each order only those items are listed (ready/delivered/cancelled lines are hidden). Status badge and dropdown buttons use the same size as on the Orders page (min-height 44px / 48px) for thumb-friendly tapping. - Documentation: Merged
GEMINI.mdintoAGENTS.md. Agent instructions now include project overview, architecture, setup & development (quick start, manual commands), development conventions, and key URLs in a single file.
- Logout – single click and land on login view (#19): Clicking "Close session" (logout) now logs out and navigates to the landing/login view in one click. Previously the app cleared auth state only after the logout request completed, while navigation ran immediately, so the landing page still saw a valid session and redirected back to the dashboard; a second click was needed. Now local state is cleared immediately and navigation runs after the server has processed logout. Same behaviour for provider dashboard logout.
- Products – waiter cannot add/edit (#20): Users without
product:write(e.g. waiter) no longer see the "Add product" button. When they open a product (Edit), the form is read-only with the message "Only owners can edit products."; all inputs and the Save button are disabled, image upload is hidden, and Delete is not shown. Inline category/subcategory editing is disabled for non-editors. i18n:PRODUCTS.ONLY_OWNERS_CAN_EDIT(en, de, es, fr, ca, zh-CN, hi). - Products form – validation feedback (#15): When adding or editing a product, submitting without a name or without a valid price now shows clear feedback: required fields are marked with an asterisk, a banner message asks to fill name and price, and inline errors appear under the name and price fields. Errors clear when the user corrects each field. i18n:
PRODUCTS.FILL_REQUIRED_FIELDS,PRODUCTS.NAME_REQUIRED,PRODUCTS.PRICE_REQUIRED(en, de, es, fr, ca, zh-CN, hi). - Backend – config.env loading on reload: Settings now load env files only from absolute paths under the project root, and only include paths that exist. This prevents
FileNotFoundError: config.envwhen the backend reloads (e.g. after file changes) and the subprocess has a different working directory.
- GEMINI.md: Removed; content merged into
AGENTS.md.
- Billing customers (Factura): Register customers that require a tax invoice (Factura) with company details. New Customers section at
/customers: add, edit, search by name, company, tax ID, or email. From Orders (active, not paid, or history): Print Factura opens a modal to select a billing customer; the printed invoice includes a "Bill to" block with company name, tax ID, address, and email. Optionally save the selected customer on the order for future reference. Backend:BillingCustomermodel,GET/POST/PUT/DELETE /billing-customerswith search,PUT /orders/:id/billing-customer; migration20260316140000_add_billing_customer.sql. Permissions:billing_customer:read/billing_customer:write. i18n:CUSTOMERS.*,NAV.CUSTOMERS(en, es, ca, de, zh-CN, hi). Seedocs/0017-billing-customers-factura.md.
- Sidebar and dashboard order: Most-used options first: Orders, Reservations, Tables, Kitchen display, Beverages display (bar view, same route as kitchen), then Customers, Products, Catalog, Reports, etc. Dashboard quick-action cards follow the same order.
- Dashboard Help section: Friendlier, inviting copy (“Need help?”, “We’re here for you…”) and new line encouraging users to start a discussion for enhancements or open an issue. Light gradient background and clearer call-to-action. i18n:
DASHBOARD.HELP_TITLE,HELP_DESC,HELP_INVITE(en, es, ca, de, zh-CN, hi). - Kitchen display – clickable item status: On
/kitchen, the item status badge (e.g. "Preparando") is now clickable for users withorder:item_status. Clicking it opens the same status dropdown as on the Orders page (Move forward / Go back), so kitchen staff can advance items to "Ready" (Listo) or move them back without leaving the kitchen view. Uses the same transition logic and API as the Orders page.
- Settings – Tax ID and CIF: In Settings → Contact, tenants can set Tax ID / VAT and CIF / NIF (e.g. for Spanish CIF). Values are stored in the database (migration
20260316120000_add_tenant_tax_id_cif.sql) and included on printed invoices. - Orders – Print invoice: Each order card on
/ordershas a Print invoice button. Clicking it opens a new window with a print-optimized invoice (business name, logo, address, Tax ID, CIF, order number, date, table, customer, line items, total) and triggers the browser print dialog so staff can print or save as PDF for customer handover. i18n:ORDERS.PRINT_INVOICE,ORDERS.INVOICE,ORDERS.INVOICE_FOOTER(en, es).
- 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/0004-deployment.mdfor configuration and 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.