Skip to content

QR Table Ordering

Overview

QR Table Ordering allows dine-in customers to scan a QR code at their table to view the menu and place orders directly from their phone.

Key Purpose: Enable contactless table ordering via QR codes.

Purpose

This page lets you configure QR code table ordering so dine-in customers can scan, browse the menu, and place orders from their phone without waiting for staff.

Key Concepts

  • Table Section: A grouping of tables (e.g., Terrace, Bar) defined by a name and a table_name_type of either Automatic or Custom; the section's tables are stored as a list of table names, not as individual table records. Sections are created and edited in In-House Settings, not on this page — see Table Sections.
  • Table Name Type: Automatic Tables Names generates names from a table_label (max 10 chars) and a count (table_numbers); Custom Tables Name lets you enter each table name yourself (table_names, each max 20 chars).
  • Order Session: A session linking orders placed at a table; an order binds to it only when its dining option is "For Here", payment is Complete, and the order is not already Complete.
  • Custom Availability: An optional per-day schedule (custom_availability + schedule) that restricts when QR ordering is available, validated to prevent overlapping time ranges.
  • QR Code Generation: QR codes are generated asynchronously via a background job and stored in Cloudflare R2 as downloadable ZIP (image) or PDF (ordering card) files; section resources expose qr_code_zip_path and qr_code_pdf_path.
  • Snooze: A temporary pause on accepting table QR orders, independent of the ordering schedule. It is stored as snoozed_until on the location's qr_ordering_setting — a unix timestamp, the string dont_accept, or null when accepting normally. The Table QR channel has its own snoozed_until, separate from the Online Ordering channel's. (Verified: QrOrderingService::snooze, upvendo-backend app/Services/BackOffice/QrOrderingService.php lines 111-149; Constants::$DEFAULT_QR_ORDERING_SETTING app/Constants.php lines 303-312.)
  • Kassanet Integration: When a Hendrickx or Vanhoutte POS is connected and the table section is a matching Kassanet section, table QR orders can be forwarded to the POS bill for that table automatically.

Actions

Get Started

On a location that hasn't been set up yet (first_time_setup), a "Get Started" screen is shown. Clicking Get Started opens the setup form with sections for QR Code Ordering Hours, Sections & Tables, Menus, and Checkout.

Select Table Sections

Pick which of your existing table sections QR ordering applies to, and reorder or remove them from that selection. This page only selects sections — it cannot create or edit them. Sections (and their tables) are created and edited in In-House → Settings → Sections & Tables; see Table Sections. (Verified: upvendo-backoffice src/views/in-house/components/qr-ordering/QROrderingForm.vue line 354 renders SectionsAndTablesSelected, while the create/edit dialog is reached only from src/views/in-house/InHouseSettingsForm.vue line 174.)

Download QR Codes

Download generated QR codes for one or more table sections. Choose "Ordering Card with QR Code" (a pre-designed instruction card, PDF) or "QR Code Only" (codes for your own design, ZIP). If codes have not been generated yet, the system dispatches a generation job and returns a 400 error prompting you to retry.

Configure Ordering Settings

Set the ordering hours / custom availability, when orders are accepted relative to closing (accept_orders_until), the default and extra menus, and the checkout requirements (require_name, require_email).

Snooze Table QR Ordering

Pause table QR ordering temporarily from the Snooze dropdown in the page header, without editing the schedule. The six choices are:

ChoiceSent asEffect
Accept ordersacceptClears the snooze and resumes normal availability
Snooze for 20 minutes20Snoozed until now + 20 minutes (location timezone)
Snooze for 40 minutes40Snoozed until now + 40 minutes
Snooze for 60 minutes60Snoozed until now + 60 minutes
Snooze for the rest of the dayrest_of_the_daySnoozed until the start of the next day
Don't accept ordersdont_acceptStores the dont_accept sentinel — paused indefinitely until you resume

Saving a snooze fires a ReloadMenu event for the location so the customer-facing channel picks it up. The button requires the manage integration-settings ability in the back office, and the endpoint requires the edit-online-ordering permission (the Table QR routes reuse the online-ordering permission).

Beside the button a status chip shows the current state: available (green) while ordering is open, snoozed for N minutes / snoozed for the rest of the day (amber) while a snooze is running, and off / do not accept (red) when paused or outside the day's ordering hours.

  • Endpoint: POST /back-office/qr-ordering/{locationId}/snooze with body { "snooze": "<option>" }
  • Response: { "snoozed_until": <timestamp | "dont_accept" | null> }

(Verified: snoozeMenuOptions in upvendo-backoffice src/views/in-house/components/qr-ordering/QROrderingForm.vue lines 76-87, status chip onUntil/onUntilColor lines 89-138, template lines 229-266; dispatch handleSnoozeSelect lines 177-186; store action snoozeQROrdering src/store/modules/inHouse.ts lines 398-404. Backend: route routes/api/backoffice/qr-ordering.php line 11, QrOrderingController::snooze, SnoozeQrOrderingRequest, app/Enums/SnoozeOptions.php, QrOrderingService::snooze lines 111-150.)

Location

  • Backoffice Route: /in-house/qr-ordering
  • Backend Controller: app/Http/Controllers/Api/BackOffice/QrOrderingController.php (table sections: app/Http/Controllers/Api/BackOffice/TableSectionController.php)
  • Backoffice Page: src/pages/in-house/qr-ordering/index.vue
  • Settings Form: src/views/in-house/components/qr-ordering/QROrderingForm.vue
  • Customer App: Table QR web application (controller app/Http/Controllers/Api/TableQrOrderingController.php)

Fields

These are the QR ordering settings persisted on the location (validated by StoreQrOrderingRequest, defaulted by Constants::$DEFAULT_QR_ORDERING_SETTING). There is no master enabled toggle — the feature is gated by setup state (first_time_setup).

Sections

PropertyValue
Field IDsections
LabelSections & Tables
TypeArray of section IDs (strings)
RequiredNo (nullable)
Default[]

Description: The table sections selected for this location's QR ordering. Sections themselves are created/edited via the Table Section endpoints (see Table Section Fields below).


Custom Availability

PropertyValue
Field IDcustom_availability
LabelCustomize availability for QR Code Ordering
TypeToggle (boolean)
RequiredYes
Defaultfalse

Description: When on, QR ordering follows the schedule below instead of the location's normal business hours.


Schedule

PropertyValue
Field IDschedule
LabelOrdering Hours
TypePer-day object (schedule.{day}.is_available, schedule.{day}.times.*.from/.to as H:i)
RequiredOnly when custom_availability is on
Defaultnull

Description: Per-day availability windows. Overlapping time ranges within a day are rejected server-side.


Accept Orders Until

PropertyValue
Field IDaccept_orders_until (API / stored key; the back-office form holds it as accept_orders_until_type)
LabelAccept Orders Until
TypeToggle (single switch), stored as enum AcceptOrdersUntil
OptionsON → Closing Time; OFF → Closing Time Minus Prep Time
RequiredYes
DefaultClosing Time Minus Prep Time (toggle off)

Description: How late QR orders are accepted relative to closing. The UI is a single switch, not a dropdown: turn it on to keep taking table orders right up to closing time; leave it off (the default) to stop one average-prep-time before closing so the kitchen can finish. Anything that is not explicitly "Closing Time Minus Prep Time" reads as on.

When the setting is "Closing Time Minus Prep Time", the cutoff is the day's latest closing time minus the location's customer prep time; with a prep time of 0 there is no pull-back. When it is "Closing Time" there is no pull-back at all and the ordinary business-hours boundary applies.

IMPORTANT

If a location has no customer prep time stored, it reads as 0 — and that single 0 causes two symptoms that look unrelated.

The prep time lives on the location itself (Settings → Locations → the location → "Customer prep time", in minutes), not on this page. The "Default: 20 minutes" caption under that field describes what is written when a location is created — it is not applied when a location is read. A location document that never got the field at all is therefore read back as 0, permanently, and:

  1. Your restaurant may advertise "0 min". The prep promise in the customer-facing nearby-restaurants list is max(base, item prep) + channel delay, clamped. With a base of 0 it collapses to the channel delay alone — so it renders "0 min" only when that channel's delay is also 0. A location carrying, say, a 15-minute pickup delay still advertises "15 min" and shows no symptom here, while still being broken for the cutoff below.
  2. "Closing Time Minus Prep Time" silently does nothing. This half is unconditional: the cutoff reads the base prep time only, and a 0 short-circuits it before any delay is considered — so table QR orders keep being accepted right up to closing, exactly as if the toggle were off. A location can therefore look fine to customers and still have a dead cutoff.

Square-imported locations created before the Square import began stamping the default are one known population. Do not assume they are the only one — the repository shows what each create path writes today, not what it wrote historically, and the platform-side repair command exists precisely because the affected set was not known to be that narrow. The fix a merchant can apply themselves: open the location in Settings → Locations, enter a Customer prep time and Save — the save always writes the field. (The field is disabled when the location's uneditable_fields includes it.) There is also a platform-side repair for the affected legacy locations, but running it is a product decision — see Locations.

(Verified: read-side hydration upvendo-backend app/RawFactories/LocationFactory.php line 32 — (int) ($data['average_prep_time'] ?? 0); the write-side-only nature of the 20 is spelled out in the docblock at app/Constants.php lines 390-418, constant at line 419. Cutoff short-circuit Location::isPastQrOrderingAcceptCutoff app/RawModels/Location.php lines 693-696. Discovery promise Location::getOnlineOrderingPrepTime lines 2072-2087 via getDiningOptionPrepTime line 2014, surfaced as prep_time by RestaurantSuggestionService::getNearbyRestaurants app/Services/RestaurantSuggestionService.php line 74 and rendered by zestidoo-online-ordering src/components/list/NearbyRestaurants.vue line 138 → formatPrepTime lines 247-258 → restaurants.prepTime.single = '{minutes} min' src/plugins/i18n/locales/en.ts line 826. The storefront's own copy of the cutoff also requires a non-zero prep: src/stores/app.ts lines 779-780. Square create default added in backend commit 912e3d668, app/Services/BackOffice/Square/SquareLocationSync.php lines 265-269. Merchant field: upvendo-backoffice src/views/settings/Locations/forms/LocationOrderBehaviour.vue lines 5-23; save path StoreLocationRequest lines 56-58 and 137, used by both store and update — app/Http/Controllers/Api/LocationController.php lines 58 and 80.)

NOTE

The Table QR default is the opposite of the Online Ordering default. QR ordering defaults to Closing Time Minus Prep Time; online ordering defaults to Closing Time. (Verified: upvendo-backend app/Constants.php$DEFAULT_QR_ORDERING_SETTING starts at line 303, its accept_orders_until at line 306; $DEFAULT_ONLINE_ORDERING_SETTING starts at line 591, its accept_orders_until at line 640.)

(Verified: toggle and mapping in upvendo-backoffice src/views/in-house/components/forms/OrderingHours.vue lines 23-36 and 355-364; enum InHouseQROrderingAcceptOrdersUntilType in src/types/inHouse.ts lines 11-14; form default src/store/modules/inHouse.ts line 63 and load fallback line 232; sent to the API as accept_orders_until at line 383. Backend: StoreQrOrderingRequest line 27 (required, Rule::enum(AcceptOrdersUntil::class)), Constants::$DEFAULT_QR_ORDERING_SETTING lines 303-312, cutoff logic Location::isPastQrOrderingAcceptCutoff app/RawModels/Location.php lines 684-726.)


Custom Default Menu

PropertyValue
Field IDcustom_default_menu
LabelUse a custom menu for QR ordering
TypeToggle (boolean)
RequiredNo (nullable)

Description: When on, QR ordering uses a dedicated menu instead of the location's normal menu, enabling default_menu_id and extra_menu_ids below.


Default Menu

PropertyValue
Field IDdefault_menu_id
LabelDefault Menu
TypeString (menu ID)
RequiredRequired when custom_default_menu is on
Defaultnull

Description: The primary menu shown for QR ordering. (Note: the field is default_menu_id, not menu_id.)


Extra Menus

PropertyValue
Field IDextra_menu_ids
LabelExtra Menus
TypeArray of menu IDs (strings)
RequiredNo (only relevant when custom_default_menu is on)
Default[]

Description: Additional menus offered alongside the default menu for QR ordering.


Require Name

PropertyValue
Field IDrequire_name
LabelRequire name
TypeToggle (boolean)
RequiredYes
Defaultfalse

Description: Whether a customer name is required at checkout. When off, the table name is used as the customer name.


Require Email

PropertyValue
Field IDrequire_email
LabelRequire email
TypeToggle (boolean)
RequiredYes
Defaultfalse

Description: Whether a customer email is required at checkout. When off, the location's contact email is used.


Snoozed Until (read-only on the settings form)

PropertyValue
Field IDsnoozed_until
Label(set from the Snooze dropdown, not the settings form)
TypeUnix timestamp (integer), the string dont_accept, or null
RequiredNo
Defaultnull

Description: The current table-QR snooze state. It is not part of the settings PUT payload — it is written only by POST /back-office/qr-ordering/{locationId}/snooze and read back with the settings. null means "not snoozed". (Verified: Constants::$DEFAULT_QR_ORDERING_SETTING app/Constants.php line 311, array at lines 303-312; QrOrderingService::snooze; back-office type src/types/inHouse.ts line 155; the settings save payload in src/store/modules/inHouse.ts lines 381-392 does not include it.)


Table Section Fields

The fields below are validated by StoreAndUpdateTableSectionRequest, which backs the standalone /back-office/table-sections API. The In-House Settings form saves sections through StoreInHouseSettingsRequest instead, whose only rule on a section name is distinct — so the 25-character cap is not enforced on that path.

Section Name

PropertyValue
Field IDname
TypeText
RequiredYes
Validationmax: 25 characters

Table Name Type

PropertyValue
Field IDtable_name_type
TypeSelect (enum TableNameType)
OptionsAutomatic Tables Names, Custom Tables Name
RequiredYes

Table Label (Automatic only)

PropertyValue
Field IDtable_label
TypeText
RequiredNo (Automatic mode)
Validationmax: 10 characters

Description: Used to build automatic names as "{section} {label} {n}" (or "{section} {n}" when blank).


Table Numbers (Automatic only)

PropertyValue
Field IDtable_numbers
TypeInteger
RequiredYes in Automatic mode
Validationmin: 1

Description: How many tables to auto-generate.


Table Names (Custom only)

PropertyValue
Field IDtable_names
TypeArray of strings
RequiredYes in Custom mode
Validationeach name max: 20 characters

Description: The explicit list of table names. (There is no per-table qr_code or active field.)


Generated QR Paths (read-only)

PropertyValue
Field IDsqr_code_zip_path, qr_code_pdf_path
TypeString (R2 path)

Description: Paths to the generated downloadable QR files for the section, populated by the background generation job.


Business Logic

QR Ordering Flow

Customer scans QR code


Open table ordering page


Table identified from QR


Browse menu


Add items to cart


Review order


Checkout (name/email per require_name & require_email)


Pay online → Order confirmed


Order sent to KDS


Staff prepares and delivers to table

Table Session Binding

Order placed via QR at a table


Bind to table session if ALL true:
├── Dining option = "For Here"
├── Payment status = Complete
└── Order status ≠ Complete


If Kassanet section (Hendrickx/Vanhoutte):
├── POS bill total = 0  → bind to POS bill
└── POS bill total > 0  → binding rejected

Customer Impact

Customer Experience

  1. Scan: Scan QR code with phone camera
  2. Open: Link opens ordering page
  3. Browse: View menu, see item details
  4. Order: Add items, customize with modifiers
  5. Checkout: Provide name/email if required, then pay online
  6. Submit: Place order
  7. Wait: Order prepared and delivered
  8. Repeat: Scan again to order more items

Benefits

  • No waiting for waiter to take order
  • Browse menu at own pace
  • See full item descriptions and images
  • Easy reordering
  • Contactless experience

Relations

Depends On

  • Locations: QR ordering per location
  • Menus: Menu displayed
  • Payments: If payment required

Affects

  • Transactions: Orders created
  • KDS: Orders sent to kitchen
  • Reports: QR ordering analytics

Business Rules

  • Custom availability schedules are validated server-side; overlapping time ranges within a single schedule are rejected with an "invalid time range" error.
  • QR codes are stored in Cloudflare R2 storage; if the file is missing or not yet generated, the system dispatches a GenerateQrCodesForSection job and returns a 400 error asking the user to retry.
  • An order can only be bound to a table session if the order's dining option is "For Here", the payment status is "Complete", and the order status is not already "Complete".
  • When a Kassanet POS (Hendrickx/Vanhoutte) is integrated and the table section is linked to it, the system checks the POS bill total before binding; if the bill total is greater than zero, binding is rejected.
  • After updating QR ordering settings, the system automatically triggers a D1 constants update and a location update push so changes are reflected in real time.
  • "Closing Time Minus Prep Time" only pulls the cutoff back when the location actually has a customer prep time. A location whose record has no prep time stored reads as 0 minutes, and the cutoff is skipped entirely. The same 0 also drops the location's advertised prep in the customer-facing nearby-restaurants list to that channel's delay alone — which shows as "0 min" only when the delay is itself 0, so a location can advertise a normal-looking time and still have a dead cutoff. (Verified: Location::isPastQrOrderingAcceptCutoff app/RawModels/Location.php lines 693-697; LocationFactory app/RawFactories/LocationFactory.php line 32.)

FAQs

  • Can I use different menus for QR ordering and online ordering? Yes. Enable custom_default_menu and set a default_menu_id (plus optional extra_menu_ids); this menu is independent from the online ordering menu.

  • What happens if a customer scans a QR code for a table that no longer exists? The system returns a "Table not found" error because the table is not present in the location's active table sections lookup.

  • Are QR codes regenerated when I rename or change a section? No, QR codes are generated on demand via the background job; you need to trigger a new download/regeneration after changing table names or sections.

  • Can QR ordering work with a Hendrickx POS? Yes, when Hendrickx integration is active and the table section is linked to it, orders placed via QR are automatically forwarded to the POS for that table.

  • What file formats are available for QR code downloads? QR codes can be downloaded as either a ZIP archive of individual images or a single PDF file, selectable at download time.

  • "How do I get started with Table QR Code Ordering for the first time?" Go to In-House → Table QR Code Ordering. On a location that hasn't been set up you'll see a "Get Started" screen; click Get Started to open the setup form with QR Code Ordering Hours, Sections & Tables, Menus, and Checkout. Note that the Sections & Tables step only selects sections that already exist — create them first in In-House → Settings → Sections & Tables.

  • "How do I add tables? Do I have to name each one manually?" Tables belong to a table section, and sections are created in In-House → Settings → Sections & Tables (not on the QR ordering page). When creating a section you choose a Table Name Type: Automatic (set a table label and a number of tables; names are generated as "{section} {label} {n}") or Custom (enter each table name yourself). See Table Sections.

  • "What's the maximum length for a section name?" The section-name field shows a "x/25" character counter, and the standalone table-section API (/back-office/table-sections) enforces max:25. The In-House Settings save path that the back office actually uses does not enforce a maximum, so a longer name typed into the section form will still save. Automatic table labels are max 10 characters; custom table names are max 20. (Verified: upvendo-backend app/Http/Requests/BackOffice/TableSection/StoreAndUpdateTableSectionRequest.php line 38 vs app/Http/Requests/BackOffice/InHouseSettings/StoreInHouseSettingsRequest.php line 126; upvendo-backoffice src/views/in-house/components/settings/SectionFormDialog.vue line 166.)

  • "A dish on the QR menu asks 'for how many people' — what is that?" It is a per-person item: the article has a bestelbeperking in MplusKassa, so the guest picks a party size as well as how many of the dish, and the line is priced per head. See Menu Items → Per-Person Items.

  • "How do I restrict the hours when QR ordering is available?" In the QR Code Ordering Hours section, turn on "Customize availability for QR Code Ordering" and set per-day from/to times. If the toggle is off, QR ordering follows the location's normal availability. Overlapping time ranges in a day are rejected.

  • "What does the 'Accept Orders Until' setting do?" It controls how late QR orders are accepted relative to closing, and it is a toggle, not a dropdown. Off (the default) means "Closing Time Minus Prep Time" — QR ordering stops one average-prep-time before the day's closing time. On means "Closing Time" — orders are taken right up to closing. Note this default is the opposite of Online Ordering, which defaults to Closing Time. If your location has no customer prep time stored, "Closing Time Minus Prep Time" has nothing to subtract and behaves exactly like "Closing Time" — see the next question. (Verified: OrderingHours.vue lines 23-36 and 355-364; defaults Constants.php line 306 (QR accept_orders_until) vs line 640 (online ordering accept_orders_until).)

  • "My restaurant shows '0 min' to customers, and 'Closing Time Minus Prep Time' never stops orders before closing. Are these two separate problems?" No — it is one cause. Both come from the location having no customer prep time stored, which is read back as 0 minutes. That 0 leaves the table-QR cutoff nothing to subtract, so orders keep coming in right up to closing; and it drops your advertised prep to the channel's delay on its own, which shows as "0 min" when that delay is 0. If you see the dead cutoff but not "0 min", it is still the same missing value — a non-zero channel delay is just masking the visible half. The "Default: 20 minutes" text under the field describes what gets written when a location is created, not what is assumed when one is read — so a location that never received the value stays at 0. What to do: open Settings → Locations → the location, set Customer prep time (minutes) and Save; the save always writes the value, and both symptoms clear together. This mostly affects Square-imported locations created before the Square import started stamping the default. There is also a platform-side repair for the affected legacy locations, but running it is a product decision — see Locations. (Verified: upvendo-backend app/RawFactories/LocationFactory.php line 32; app/Constants.php lines 390-419; Location::isPastQrOrderingAcceptCutoff app/RawModels/Location.php lines 693-696; RestaurantSuggestionService::getNearbyRestaurants line 74 → zestidoo-online-ordering src/components/list/NearbyRestaurants.vue line 138 and formatPrepTime lines 247-258; field upvendo-backoffice src/views/settings/Locations/forms/LocationOrderBehaviour.vue lines 5-23.)

  • "How do I stop taking table QR orders right now without changing my hours?" Use the Snooze dropdown at the top of the Table QR Code Ordering page. You can snooze for 20, 40, or 60 minutes, for the rest of the day, or choose "Don't accept orders" to pause indefinitely; "Accept orders" resumes. The chip next to the button shows whether you are currently available, snoozed (and for how long), or off. Table QR snooze is separate from the Online Ordering snooze — pausing one does not pause the other. (Verified: QROrderingForm.vue lines 76-138; QrOrderingService::snooze writes qr_ordering_setting.snoozed_until, not the online-ordering setting.)

  • "Can I show a different menu for QR ordering than my main menu?" Yes. In the Menus section you can enable a custom default menu and pick a default menu, plus optionally add extra menus, specifically for QR ordering.

  • "What information can I require from customers at checkout?" In Checkout you can toggle "Require name" and "Require email". When "Require name" is off, the table name is used as the customer name; when "Require email" is off, the location's contact email is used.

  • "How do I download the printable QR codes, and what designs are available?" Click Get QR Codes (you must Save first, or the button is disabled). Pick a design — "Ordering Card with QR Code" (a pre-designed instruction card, downloaded as PDF) or "QR Code Only" (codes for your own design, downloaded as ZIP) — then choose which sections to download.

  • "I clicked Get QR Codes but it says the files are still generating. What's happening?" QR generation runs in the background. If a section's codes aren't ready, the system starts generating them and asks you to retry in a few minutes. A progress bar shows status, and PDFs can only be generated after the QR (image) generation finishes.

  • "Why is the 'Get QR Codes' button greyed out?" You need to save your settings first. Until the location's QR codes have been generated, the button stays disabled with a tooltip telling you to click Save, then wait a few minutes.

  • "An order placed via QR isn't attaching to the right table — what conditions must be met?" An order only binds to a table session when its dining option is "For Here", its payment is Complete (paid), and the order isn't already marked Complete. Unpaid or already-completed orders are rejected.

  • "We use a Hendrickx/Vanhoutte (Kassanet) POS — will QR orders go onto the table's POS bill?" Yes, but only when the table section is a Kassanet section matching the order's POS provider. Before binding, the system checks the table's POS bill total; if that bill total is greater than zero, binding is rejected to avoid mixing with an existing open bill.

Troubleshooting

Problem: QR code not scanning

Causes:

  1. QR code damaged/dirty
  2. Poor lighting
  3. Camera issue
  4. QR code too small

Solutions:

  1. Replace QR code
  2. Improve lighting
  3. Try different phone
  4. Print larger QR code

Problem: "Table not found" error

Causes:

  1. Table/section deleted
  2. Section removed from this location's QR settings
  3. Wrong location
  4. QR code outdated

Solutions:

  1. Check the section and table still exist
  2. Re-add the section to the location's QR settings
  3. Verify location
  4. Regenerate QR code

Problem: Orders not appearing on KDS

Causes:

  1. KDS not configured
  2. Order not submitted
  3. Payment pending
  4. Network issue

Solutions:

  1. Configure KDS
  2. Verify order submitted
  3. Check payment status
  4. Check network

Problem: Order not attaching to the right table

Causes:

  1. QR code moved to a different table
  2. Order dining option is not "For Here"
  3. Payment not Complete, or order already Complete
  4. Kassanet section already has an open POS bill (total > 0)

Solutions:

  1. Ensure QR codes are fixed to their tables
  2. Confirm the order is "For Here" and paid
  3. For Kassanet sections, settle/clear the existing POS bill first
  4. Verify the table with the customer

Examples

Table Section Setup (Automatic)

json
{
  "name": "Terrace",
  "table_name_type": "Automatic Tables Names",
  "table_label": "T",
  "table_numbers": 5,
  "location_id": "loc-123"
}

This auto-generates table names: "Terrace T 1" … "Terrace T 5".

Table Section Setup (Custom)

json
{
  "name": "Bar Area",
  "table_name_type": "Custom Tables Name",
  "table_names": ["Bar 1", "Bar 2", "Window Seat"],
  "location_id": "loc-123"
}

QR Ordering Settings

json
{
  "sections": ["section-terrace", "section-bar"],
  "custom_availability": false,
  "schedule": null,
  "accept_orders_until": "Closing Time Minus Prep Time",
  "custom_default_menu": true,
  "default_menu_id": "menu-dine-in",
  "extra_menu_ids": ["menu-drinks"],
  "require_name": false,
  "require_email": false
}

QR Code URL Structure

https://table.restaurant.com/order?
  location=loc-123
  &table=t1
  &token=abc123xyz

Table Tent Design

╔═══════════════════════════════════════╗
║                                       ║
║         SCAN TO ORDER                 ║
║                                       ║
║         ┌─────────────┐               ║
║         │             │               ║
║         │   [QR CODE] │               ║
║         │             │               ║
║         └─────────────┘               ║
║                                       ║
║         TABLE 5                       ║
║                                       ║
║   1. Scan with your phone camera      ║
║   2. Browse our menu                  ║
║   3. Place your order                 ║
║   4. We'll bring it to you!           ║
║                                       ║
║         MARIO'S RESTAURANT            ║
╚═══════════════════════════════════════╝

Customer App Interface

┌─────────────────────────────────────┐
│ MARIO'S RESTAURANT          Table 5 │
├─────────────────────────────────────┤
│                                     │
│ [Starters] [Mains] [Drinks] [Desserts]
│                                     │
│ ┌─────────────────────────────────┐ │
│ │ 🍕 Margherita Pizza             │ │
│ │ Fresh mozzarella, tomato, basil │ │
│ │                          €12.50 │ │
│ │                      [Add to Cart]│
│ └─────────────────────────────────┘ │
│                                     │
│ ┌─────────────────────────────────┐ │
│ │ 🍝 Spaghetti Carbonara          │ │
│ │ Creamy pasta with pancetta      │ │
│ │                          €14.50 │ │
│ │                      [Add to Cart]│
│ └─────────────────────────────────┘ │
│                                     │
├─────────────────────────────────────┤
│ 🛒 Cart (2 items)           €27.00  │
│ [View Cart]                         │
└─────────────────────────────────────┘