Appearance
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
nameand atable_name_typeof 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 Namesgenerates names from atable_label(max 10 chars) and a count (table_numbers);Custom Tables Namelets 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_pathandqr_code_pdf_path. - Snooze: A temporary pause on accepting table QR orders, independent of the ordering schedule. It is stored as
snoozed_untilon the location'sqr_ordering_setting— a unix timestamp, the stringdont_accept, ornullwhen accepting normally. The Table QR channel has its ownsnoozed_until, separate from the Online Ordering channel's. (Verified:QrOrderingService::snooze, upvendo-backendapp/Services/BackOffice/QrOrderingService.phplines 111-149;Constants::$DEFAULT_QR_ORDERING_SETTINGapp/Constants.phplines 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:
| Choice | Sent as | Effect |
|---|---|---|
| Accept orders | accept | Clears the snooze and resumes normal availability |
| Snooze for 20 minutes | 20 | Snoozed until now + 20 minutes (location timezone) |
| Snooze for 40 minutes | 40 | Snoozed until now + 40 minutes |
| Snooze for 60 minutes | 60 | Snoozed until now + 60 minutes |
| Snooze for the rest of the day | rest_of_the_day | Snoozed until the start of the next day |
| Don't accept orders | dont_accept | Stores 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}/snoozewith 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
| Property | Value |
|---|---|
| Field ID | sections |
| Label | Sections & Tables |
| Type | Array of section IDs (strings) |
| Required | No (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
| Property | Value |
|---|---|
| Field ID | custom_availability |
| Label | Customize availability for QR Code Ordering |
| Type | Toggle (boolean) |
| Required | Yes |
| Default | false |
Description: When on, QR ordering follows the schedule below instead of the location's normal business hours.
Schedule
| Property | Value |
|---|---|
| Field ID | schedule |
| Label | Ordering Hours |
| Type | Per-day object (schedule.{day}.is_available, schedule.{day}.times.*.from/.to as H:i) |
| Required | Only when custom_availability is on |
| Default | null |
Description: Per-day availability windows. Overlapping time ranges within a day are rejected server-side.
Accept Orders Until
| Property | Value |
|---|---|
| Field ID | accept_orders_until (API / stored key; the back-office form holds it as accept_orders_until_type) |
| Label | Accept Orders Until |
| Type | Toggle (single switch), stored as enum AcceptOrdersUntil |
| Options | ON → Closing Time; OFF → Closing Time Minus Prep Time |
| Required | Yes |
| Default | Closing 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:
- 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. - "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
| Property | Value |
|---|---|
| Field ID | custom_default_menu |
| Label | Use a custom menu for QR ordering |
| Type | Toggle (boolean) |
| Required | No (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
| Property | Value |
|---|---|
| Field ID | default_menu_id |
| Label | Default Menu |
| Type | String (menu ID) |
| Required | Required when custom_default_menu is on |
| Default | null |
Description: The primary menu shown for QR ordering. (Note: the field is default_menu_id, not menu_id.)
Extra Menus
| Property | Value |
|---|---|
| Field ID | extra_menu_ids |
| Label | Extra Menus |
| Type | Array of menu IDs (strings) |
| Required | No (only relevant when custom_default_menu is on) |
| Default | [] |
Description: Additional menus offered alongside the default menu for QR ordering.
Require Name
| Property | Value |
|---|---|
| Field ID | require_name |
| Label | Require name |
| Type | Toggle (boolean) |
| Required | Yes |
| Default | false |
Description: Whether a customer name is required at checkout. When off, the table name is used as the customer name.
Require Email
| Property | Value |
|---|---|
| Field ID | require_email |
| Label | Require email |
| Type | Toggle (boolean) |
| Required | Yes |
| Default | false |
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)
| Property | Value |
|---|---|
| Field ID | snoozed_until |
| Label | (set from the Snooze dropdown, not the settings form) |
| Type | Unix timestamp (integer), the string dont_accept, or null |
| Required | No |
| Default | null |
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
| Property | Value |
|---|---|
| Field ID | name |
| Type | Text |
| Required | Yes |
| Validation | max: 25 characters |
Table Name Type
| Property | Value |
|---|---|
| Field ID | table_name_type |
| Type | Select (enum TableNameType) |
| Options | Automatic Tables Names, Custom Tables Name |
| Required | Yes |
Table Label (Automatic only)
| Property | Value |
|---|---|
| Field ID | table_label |
| Type | Text |
| Required | No (Automatic mode) |
| Validation | max: 10 characters |
Description: Used to build automatic names as "{section} {label} {n}" (or "{section} {n}" when blank).
Table Numbers (Automatic only)
| Property | Value |
|---|---|
| Field ID | table_numbers |
| Type | Integer |
| Required | Yes in Automatic mode |
| Validation | min: 1 |
Description: How many tables to auto-generate.
Table Names (Custom only)
| Property | Value |
|---|---|
| Field ID | table_names |
| Type | Array of strings |
| Required | Yes in Custom mode |
| Validation | each 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)
| Property | Value |
|---|---|
| Field IDs | qr_code_zip_path, qr_code_pdf_path |
| Type | String (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 tableTable 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 rejectedCustomer Impact
Customer Experience
- Scan: Scan QR code with phone camera
- Open: Link opens ordering page
- Browse: View menu, see item details
- Order: Add items, customize with modifiers
- Checkout: Provide name/email if required, then pay online
- Submit: Place order
- Wait: Order prepared and delivered
- 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
Related Features
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
GenerateQrCodesForSectionjob 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::isPastQrOrderingAcceptCutoffapp/RawModels/Location.phplines 693-697;LocationFactoryapp/RawFactories/LocationFactory.php line 32.)
FAQs
Can I use different menus for QR ordering and online ordering? Yes. Enable
custom_default_menuand set adefault_menu_id(plus optionalextra_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) enforcesmax: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-backendapp/Http/Requests/BackOffice/TableSection/StoreAndUpdateTableSectionRequest.php line 38 vs app/Http/Requests/BackOffice/InHouseSettings/StoreInHouseSettingsRequest.php line 126;upvendo-backofficesrc/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.vuelines 23-36 and 355-364; defaultsConstants.phpline 306 (QRaccept_orders_until) vs line 640 (online orderingaccept_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-backendapp/RawFactories/LocationFactory.php line 32; app/Constants.php lines 390-419;Location::isPastQrOrderingAcceptCutoffapp/RawModels/Location.php lines 693-696;RestaurantSuggestionService::getNearbyRestaurantsline 74 →zestidoo-online-orderingsrc/components/list/NearbyRestaurants.vue line 138 andformatPrepTimelines 247-258; fieldupvendo-backofficesrc/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.vuelines 76-138;QrOrderingService::snoozewritesqr_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:
- QR code damaged/dirty
- Poor lighting
- Camera issue
- QR code too small
Solutions:
- Replace QR code
- Improve lighting
- Try different phone
- Print larger QR code
Problem: "Table not found" error
Causes:
- Table/section deleted
- Section removed from this location's QR settings
- Wrong location
- QR code outdated
Solutions:
- Check the section and table still exist
- Re-add the section to the location's QR settings
- Verify location
- Regenerate QR code
Problem: Orders not appearing on KDS
Causes:
- KDS not configured
- Order not submitted
- Payment pending
- Network issue
Solutions:
- Configure KDS
- Verify order submitted
- Check payment status
- Check network
Problem: Order not attaching to the right table
Causes:
- QR code moved to a different table
- Order dining option is not "For Here"
- Payment not Complete, or order already Complete
- Kassanet section already has an open POS bill (total > 0)
Solutions:
- Ensure QR codes are fixed to their tables
- Confirm the order is "For Here" and paid
- For Kassanet sections, settle/clear the existing POS bill first
- 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=abc123xyzTable 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] │
└─────────────────────────────────────┘