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
name(max 25 chars) and atable_name_typeof either Automatic or Custom; the section's tables are stored as a list of table names, not as individual table records. - 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. - 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.
Manage Table Sections and Tables
Create table sections (e.g., "Terrace", "Bar Area"). For each section choose a Table Name Type: Automatic (set a label and number of tables) or Custom (enter each table name). Tables are stored as a list of names; there is no per-table active/inactive toggle.
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).
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 |
| Label | Accept Orders Until |
| Type | Select (enum AcceptOrdersUntil) |
| Options | Closing Time Minus Prep Time, Closing Time |
| Required | Yes |
| Default | Closing Time |
Description: How late QR orders are accepted relative to closing.
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.
Table Section Fields
Sections and their tables are created/updated via StoreAndUpdateTableSectionRequest.
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.
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.
"How do I add tables? Do I have to name each one manually?" When creating a table 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).
"What's the maximum length for a section name?" A table section name is required and limited to 25 characters. Automatic table labels are max 10 characters; custom table names are max 20.
"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. Two options: "Closing Time Minus Prep Time" and "Closing Time".
"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",
"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] │
└─────────────────────────────────────┘