Appearance
Online Ordering Setup
Overview
Online Ordering allows customers to place pickup, delivery, and eat-in (dine-in) orders through a web-based ordering system. This is the primary channel for remote orders.
Key Purpose: Enable customers to order food online for pickup, delivery, or eat-in.
Purpose
This page lets you enable and configure the online ordering channel for a location, including pickup/delivery/eat-in toggles, preparation times, scheduling options, and order acceptance controls.
Key Concepts
- Online Ordering Setting: A nested settings object stored on the location record that controls all online ordering behavior including enabled state, prep time, delays, scheduling, and snooze status.
- Eat In: An opt-in online-ordering option (
eat_in, disabled by default) that lets customers order ahead online and eat in at the restaurant at a chosen time. It behaves like online pickup ordering (timeslot picker, future-day scheduling, shared order capacity) but the order is recorded as For Here / dine-in for correct POS category and VAT. Availability follows the location's business hours; unlike pickup, eat-in has no same-day/next-day cutoff toggles and its last available slot is always closing time minus prep time (theaccept_orders_untilsetting does not apply to eat-in). - Snooze: A temporary pause mechanism with options to snooze for 20, 40, or 60 minutes, for the rest of the business day, or indefinitely ("don't accept"). The
snoozed_untilfield stores either a Unix timestamp or the string value for permanent pause. - Subscription: Enabling online ordering triggers a Stripe subscription for the location. The system handles new subscriptions, retries for incomplete payments, and cancellations automatically.
- Landing Page Image: An optional image uploaded for the online ordering landing page, processed through Cloudflare Images and stored as both a content ID and a Cloudflare image ID.
- Custom Scheduled Orders: When enabled, orders placed far in advance (beyond
hours_in_advancethreshold) are held and sent to the kitchen onlyminutes_before_pickup_delivery_timebefore the scheduled time. - Storefront display settings: A few online-ordering settings only change what the storefront shows, never what customers can order — the menu grid width (
menu_item_columns), whether the separate online-ordering hours are shown alongside the location's business hours (show_location_hours_only), and whether remaining stock counts are visible (show_item_stock). The one stock setting that does change ordering behavior issell_without_stock.
Actions
Configure Online Ordering
Update the full online ordering settings for a location, including enabling/disabling pickup, delivery, and eat-in, setting preparation times, configuring scheduling options, and uploading a landing page image. Saving triggers a subscription if not already active and fires a ReloadMenu event.
Snooze Orders
Temporarily pause order acceptance using preset durations (20, 40, or 60 minutes), pause for the rest of the business day, resume accepting orders, or stop accepting indefinitely. The snooze state is stored on the location and propagated to the ordering frontend.
View Settings
Retrieve the current online ordering configuration for a location. If no settings exist yet, default settings are automatically initialized.
Location
- Backoffice Route:
/online/online-ordering(nav: Online → Online Ordering) - Backend Controller:
app/Http/Controllers/Api/BackOffice/OnlineOrderingController.php - Vue Component:
src/views/online/online-ordering/OnlineOrdering.vue - Online Ordering App:
zestidoo-online-ordering(customer-facing storefront repo)
Fields
How online ordering is turned on / paused (no settings toggle for either):
- Enabling online ordering is not a settings field — there is no
enabledflag. A location enables it by subscribing to the Online Ordering channel (Add Channel → Stripe subscription); the subscription is created on save. While unsubscribed, the storefront is unavailable.- To temporarily pause orders without unsubscribing, use Snooze (see the Snooze section). There is no
accepting_ordersfield — pausing is driven bysnoozed_until/ theSnoozeOptionsenum.
Enable Pickup
| Property | Value |
|---|---|
| Field ID | takeout.enabled |
| Label | Enable Pickup |
| Type | Toggle |
| Default | true |
| Required | No |
Description: Allow customers to place pickup orders. Stored as takeout.enabled in the online ordering settings object.
Customer Impact:
- Enabled: "Pickup" option shown at checkout
- Disabled: Only delivery available (if enabled)
Enable Delivery
| Property | Value |
|---|---|
| Field ID | delivery.enabled |
| Label | Enable Delivery |
| Type | Toggle |
| Default | false |
| Required | No |
| Depends On | A delivery_region is always part of the settings; enabling delivery additionally requires a delivery.delay |
Description: Allow customers to place delivery orders. Stored as delivery.enabled in the online ordering settings object. Default is false (unlike pickup which defaults to true).
Customer Impact:
- Enabled: "Delivery" option shown at checkout
- Disabled: Only pickup available (if enabled)
Note: Even if enabled, delivery only works if the customer's address falls within the configured delivery_region (Radius, Distance, or matching postal code).
Enable Eat In
| Property | Value |
|---|---|
| Field ID | eat_in.enabled |
| Label | Eat in |
| Type | Toggle |
| Default | false |
| Required | No |
Description: Opt-in toggle that lets customers order ahead online and eat in at the restaurant ("Let customers order ahead online and eat in at the restaurant. Uses your location business hours."). Stored as eat_in.enabled in the online ordering settings object. Disabled by default. eat_in is nullable/optional on save, so existing integrations that never send this block keep working and fall back to the disabled default.
Customer Impact:
- Enabled: "Eat in" (For Here) option shown at checkout alongside Pickup/Delivery
- Disabled: Only pickup/delivery available (as configured)
Eat In Delay
| Property | Value |
|---|---|
| Field ID | eat_in.delay |
| Label | Add Delay (shown under the Eat In toggle) |
| Type | Number |
| Unit | Minutes |
| Default | 0 |
| Required | No (required only when eat_in.enabled is true, in the back office form) |
Description: Additional time added to preparation time for eat-in orders, mirroring the Pickup/Delivery delay fields. Gives the kitchen extra buffer before the customer's eat-in slot.
Business Logic:
- Eat-in earliest = Now + Prep Time + Eat In Delay
- Eat-in shares order capacity with pickup and delivery
- Eat-in's last available slot is always closing time minus prep time; it does not use the
accept_orders_untilsetting or same-day/next-day cutoffs that pickup and delivery have
Delivery Minimum Order Amount
| Property | Value |
|---|---|
| Field ID | minimum_order_amount.enabled (toggle) + minimum_order_amount.amount (currency) |
| Label | Delivery minimum order amount (on the Delivery Settings card) |
| Type | Toggle + Currency |
| Default | enabled: false, amount: 10 |
| Required | No |
| Validation | min: 0 |
Description: Minimum order value required for delivery orders. It is not applied to pickup — pickup has its own pickup_minimum_order_amount on the Pickup Settings card — and eat-in / For Here orders are exempt from both minimums. Configured on the Delivery Settings card of the Online Ordering page.
Customer Impact:
- Below minimum, the storefront shows "Minimum order amount (€X.XX) not met" — it names the required minimum, not the shortfall.
Business Logic:
- Checked at checkout, not during browsing
- Applies to subtotal after discounts, before delivery fees
- Enforcement is strictly per dining option: Delivery uses
minimum_order_amount, Pickup usespickup_minimum_order_amount, and eat-in (For Here) returns without any check - Eat-in (
For Here) orders are also exempt from the pickup transaction fee, which only applies to Pickup and Takeout orders
Preparation Time
| Property | Value |
|---|---|
| Field ID | average_prep_time (Location setting) |
| Label | Average Preparation Time |
| Type | Number |
| Unit | Minutes |
| Source | Location record (read-only on the Online Ordering page) |
Description: Average time needed to prepare an order, as quoted to guests. This is a location-level setting (the average_prep_time property on the Location record), not an Online Ordering field — it is not part of the online ordering settings object or StoreOnlineOrderingRequest. It is shown read-only on the Online Ordering page; set it under Settings → Locations. It affects the earliest available pickup, delivery and online eat-in time, and each channel can add a delay on top of it (see Pickup Delay / Delivery Delay).
Menu items also carry their own prep time, but it does not currently reach this calculation: it is gated behind ONLINE_ORDERING_INCLUDE_ITEM_PREP_TIME, which defaults to off and is not set in production, staging or testing. Today the quote is always the location's preparation time plus the channel delay. (Verified: upvendo-backend config/upvendo.php line 26; app/Services/OrderCapacity/OrderCapacityService.php lines 103 and 172, which return 0 for the item term while the flag is off.)
The number a customer sees in the nearby-restaurants list before they order and the one the time-slot picker uses at checkout are the same number: both call Location::getDiningOptionPrepTime(), the single definition of the rule (app/RawModels/Location.php:2001-2018, reached from :2072-2087 for the list and from app/Services/OrderCapacity/OrderCapacityService.php:86-89 at checkout). They used to be computed separately and could disagree — a negative pickup or delivery delay was ignored in the list but applied at checkout. That half is fixed.
Eat-in is still absent from the browse number, and that is by construction, not a leftover bug. The list maps the rule over getOnlineOrderingDiningOptions() (:1956-1968), which returns Pickup and Delivery only — ForHere is never in it, whatever eat_in.enabled says. So a location that offers only online eat-in advertises {from: 0, to: 0} while browsing and still quotes a real prep time once the customer reaches the picker. The two agree for pickup and delivery; they do not agree for eat-in.
Business Logic:
- Earliest time = Now + Preparation Time + Channel Delay
- For pickup: Earliest = Now + Prep Time + Pickup Delay
- For delivery: Earliest = Now + Prep Time + Delivery Delay
Customer Impact:
- If prep time is 20 min and it's 12:00, earliest pickup is 12:20
- Longer prep time = later earliest available slot
Examples:
- Fast food: 10-15 minutes
- Casual dining: 20-30 minutes
- Fine dining: 45-60 minutes
Pickup Delay
| Property | Value |
|---|---|
| Field ID | takeout.delay |
| Label | Additional Pickup Time |
| Type | Number |
| Unit | Minutes |
| Default | 0 |
| Required | No |
| Validation | min: 0 |
Description: Additional time added to preparation time for pickup orders. Use for buffer time.
Business Logic:
- Pickup earliest = Now + Prep Time + Pickup Delay
- Gives kitchen extra buffer for pickup orders
Delivery Delay
| Property | Value |
|---|---|
| Field ID | delivery.delay |
| Label | Additional Delivery Time |
| Type | Number |
| Unit | Minutes |
| Default | 0 |
| Required | No |
| Validation | min: 0 |
Description: Additional time added to preparation time for delivery orders. Accounts for delivery travel time.
Business Logic:
- Delivery earliest = Now + Prep Time + Delivery Delay
- Should account for average delivery distance
Scheduled Orders (Pickup)
| Property | Value |
|---|---|
| Field ID | schedule_order_for_future_days.takeout.enabled |
| Label | Allow Scheduled Pickup Orders |
| Type | Toggle |
| Default | true |
| Required | No |
Description: Allow customers to schedule pickup orders for future times/dates.
Scheduled Pickup Days Ahead
| Property | Value |
|---|---|
| Field ID | schedule_order_for_future_days.takeout.days_in_advance |
| Label | Pickup Days in Advance |
| Type | Number |
| Unit | Days |
| Default | 30 |
| Required | Yes (if scheduled pickup enabled) |
| Validation | max: 365 |
Description: How many days in advance customers can schedule pickup orders.
Scheduled Orders (Delivery)
| Property | Value |
|---|---|
| Field ID | schedule_order_for_future_days.delivery.enabled |
| Label | Allow Scheduled Delivery Orders |
| Type | Toggle |
| Default | false |
| Required | No |
Description: Allow customers to schedule delivery orders for future times/dates.
Scheduled Delivery Days Ahead
| Property | Value |
|---|---|
| Field ID | schedule_order_for_future_days.delivery.days_in_advance |
| Label | Delivery Days in Advance |
| Type | Number |
| Unit | Days |
| Default | 30 |
| Required | Yes (if scheduled delivery enabled) |
| Validation | max: 365 |
Description: How many days in advance customers can schedule delivery orders.
Scheduled Orders (Eat In)
| Property | Value |
|---|---|
| Field ID | schedule_order_for_future_days.eat_in.enabled |
| Label | Eat in (future-days scheduling section) |
| Type | Toggle |
| Default | false |
| Required | No |
| Depends On | Only shown/relevant when eat_in.enabled is true |
Description: Allow customers to schedule eat-in orders for future days, mirroring the pickup/delivery future-days scheduling. This block is optional on save (nullable) so clients that don't send it keep working and fall back to the disabled default.
Scheduled Eat In Days Ahead
| Property | Value |
|---|---|
| Field ID | schedule_order_for_future_days.eat_in.days_in_advance |
| Label | Eat In Days in Advance |
| Type | Number |
| Unit | Days |
| Default | 30 |
| Required | Yes (if scheduled eat-in enabled) |
| Validation | max: 365 |
Description: How many days in advance customers can schedule eat-in orders.
Custom Scheduled Orders
| Property | Value |
|---|---|
| Field ID | custom_scheduled_orders |
| Label | Custom Scheduled Order Handling |
| Type | Toggle |
| Default | false |
| Required | No |
Description: Enable custom timing for when scheduled orders are sent to the kitchen.
Business Logic:
- Disabled: Orders sent to kitchen immediately
- Enabled: Orders sent based on
hours_in_advanceandminutes_before_pickup_delivery_time
Hours in Advance
| Property | Value |
|---|---|
| Field ID | hours_in_advance |
| Label | Hours in Advance Threshold |
| Type | Number |
| Unit | Hours |
| Default | None (no default; only present/required when custom_scheduled_orders is enabled) |
| Required | Yes (if custom scheduled enabled), min: 1 |
| Depends On | custom_scheduled_orders must be true |
Description: If order is placed more than X hours before pickup/delivery time, use delayed sending.
Business Logic:
- Order placed > X hours ahead → Send to kitchen at (pickup time - minutes_before)
- Order placed < X hours ahead → Send to kitchen immediately
Minutes Before Pickup/Delivery
| Property | Value |
|---|---|
| Field ID | minutes_before_pickup_delivery_time |
| Label | Send to Kitchen Before |
| Type | Number |
| Unit | Minutes |
| Default | None (no default; only present/required when custom_scheduled_orders is enabled) |
| Required | Yes (if custom scheduled enabled), min: 1 |
| Depends On | custom_scheduled_orders must be true |
Description: For orders placed far in advance, send to kitchen this many minutes before pickup/delivery time.
Example:
- Customer orders at 8:00 AM for 6:00 PM pickup (10 hours ahead)
hours_in_advance= 6 hoursminutes_before_pickup_delivery_time= 60 minutes- Order sent to kitchen at 5:00 PM (60 min before pickup)
Menu Layout (Storefront Grid Columns)
| Property | Value |
|---|---|
| Field ID | menu_item_columns |
| Label | Menu Layout |
| Type | Select |
| Options | 2 Columns / 3 Columns / 4 Columns (stored as the integers 2, 3, 4) |
| Default | 2 |
| Required | No (nullable) |
| Validation | Integer; must be 2, 3 or 4 |
Description: How many columns the online-ordering storefront uses for the menu-item grid. Set on the main Online Ordering card under Menu Layout ("Choose between a 2, 3 or 4 column layout for menu items"). (Verified: upvendo-backend app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php line 218 — nullable|integer|in:2,3,4 — with the default 2 at app/Constants.php lines 686-689; upvendo-backoffice src/views/online/online-ordering/forms/OnlineOrdering.vue lines 155-166, the 2/3/4 options at lines 776-780, form default at src/store/modules/onlineOrdering.ts line 122.)
This is the storefront setting — it is not the kiosk one. The kiosk device profile has its own, separate
menu_item_columnsthat still allows only 2 or 3 columns (Verified: upvendo-backendapp/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.phpline 211 —nullable|integer|in:2,3). The two settings live on different records (the location'sonline_ordering_settingvs. the device profile), are edited on different back-office pages, and diverged deliberately: 4 columns exists for online ordering only. Do not "reconcile" the two — see Device Profiles and Kiosk for the kiosk field.
Customer Impact: the storefront reads the value and falls back to 2 for anything else. The grid is always single-column on the narrowest screens; the merchant's choice applies from the small breakpoint up:
- 2 — two across from the small breakpoint up
- 3 — three across from the small breakpoint up
- 4 — a responsive ladder rather than a hard 4-up: two across at small, three at medium, four only at extra-large. A "4 column" menu therefore shows fewer than four columns on smaller screens.
(Verified: zestidoo-online-ordering src/stores/app.ts lines 259-262 and src/components/menu/MenuSection.vue lines 11, 57-65.)
Show Location Hours Only
| Property | Value |
|---|---|
| Field ID | show_location_hours_only |
| Label | Show location hours only |
| Type | Toggle |
| Default | false |
| Required | No (nullable) |
Description: Display-only toggle in Fulfillment and Scheduling, directly under Custom Availability: "Hide the separate online ordering hours from customers and show only your location's opening hours." (Verified: upvendo-backoffice src/views/online/online-ordering/forms/FullfilmentAndScheduling.vue lines 50-61; default false at upvendo-backend app/Constants.php lines 655-658 and 'show_location_hours_only' => 'nullable|boolean' at app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php line 214.)
Business Logic:
- The channel payload always carries the location's business hours (
hours.location). The separate online-ordering schedule (hours.online_ordering) is included only when Custom Availability is on and this toggle is off; otherwise that key isnull. (Verified: upvendo-backendapp/RawModels/Location.phplines 1449 and 1540.) - Because of that condition, the toggle has no visible effect while Custom Availability is off — there is no separate schedule to hide.
- It changes display only. Ordering availability, the custom-availability schedule itself, restricted dates, and cut-offs are unaffected.
- The value is read from
online_ordering_settingfor the QR-ordering channel too, so it also suppresses thehours.qr_orderingkey for table QR. (Verified: same lines —$hoursKeyisonline_orderingorqr_ordering, and both are gated by the same flag.)
Continue Selling Items When Out of Stock
| Property | Value |
|---|---|
| Field ID | sell_without_stock |
| Label | Continue selling items when out of stock (card: Inventory Settings) |
| Type | Toggle |
| Default | false |
| Required | No (nullable) |
| Visibility | The Inventory Settings card only appears when the location has at least one inventory-tracked item |
Description: When enabled, items stay orderable online even when their tracked stock is zero or negative. Back-office subtitle: "Control how items with zero or negative stock are handled for online orders." (Verified: upvendo-backoffice src/views/online/online-ordering/forms/FullfilmentAndScheduling.vue lines 343-380 — the card is rendered v-if="hasInventoryTracked", which is fed by GET /back-office/inventories/has-tracked at lines 449-460 (route: upvendo-backend routes/api/backoffice/inventories.php line 9); validation nullable|boolean at app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php line 213, default false at app/Constants.php line 654.)
Business Logic:
- The setting is passed into stock management when an online order is placed (
manageLocationStocks(..., sellWithoutStock: ...)). (Verified:app/Services/Orchestrators/OnlineOrderingOrchestrator.phplines 462-463.) - It is resolved per channel at payment capture: kiosk orders use the device profile's own
sell_without_stock, online-ordering orders use this location setting. (Verified:app/Services/Payment/PaymentCaptureService.phplines 647-660.) - On the storefront, enabling it makes available stock effectively unlimited, so items are never gated by stock. Items are still blocked by their own status (Inactive / Unavailable / Hidden). (Verified: zestidoo-online-ordering
src/stores/app.tslines 292-297 and 339-344.)
Show Remaining Item Stock
| Property | Value |
|---|---|
| Field ID | show_item_stock |
| Label | Show remaining item stock to customers (card: Stock Visibility) |
| Type | Toggle |
| Default | false |
| Required | No (nullable) |
| Visibility | Shown in the same inventory-tracked-only card group as sell_without_stock |
Description: Display-only toggle that reveals the remaining stock count on inventory-tracked items while the customer is ordering. Back-office subtitle: "Control whether remaining stock counts are shown to customers on the storefront."; tooltip: "When enabled, customers see the remaining stock count on inventory-tracked items while ordering. This does not change ordering availability." (Verified: upvendo-backoffice src/views/online/online-ordering/forms/FullfilmentAndScheduling.vue lines 384-419; validation nullable|boolean at upvendo-backend app/Http/Requests/BackOffice/OnlineOrdering/StoreOnlineOrderingRequest.php line 215, default false at app/Constants.php lines 659-662.)
Business Logic:
- Display only — it does not change ordering availability or how stock is enforced.
- The count is shown on the item detail modal only. The equivalent pill on the menu grid card exists in code but is commented out in production (zestidoo-online-ordering
src/components/FoodCard.vuelines 60-68), so customers do not see counts while browsing the grid. (Verified:src/components/ItemDetail.vuelines 37-46 and 173-181.) - Two states: at or below 10 remaining the pill reads "Only {count} left" (amber); above 10 it reads "{count} in stock" (neutral grey). Threshold
LOW_STOCK_THRESHOLD = 10insrc/utils/constants.tsline 142. - A count is only shown for items with a real tracked quantity. Items that don't track quantity have no count to show, and turning on
sell_without_stocksuppresses the count too, because stock is then treated as unlimited (the storefront gate isshowItemStock && availableStock > 0 && availableStock < 99999, where 99999 is the untracked / sell-without-stock sentinel). (Verified: zestidoo-online-orderingsrc/stores/app.tsline 266 and lines 292-297.)
Payment Methods (Checkout)
Online-ordering payment methods are configured under Online Ordering settings → Checkout → Payment methods. Card / Bancontact are the standard online payment methods. One additional, deferred-payment method can be enabled:
Invoice (Pay by Invoice)
| Property | Value |
|---|---|
| Field ID | payment_methods.invoice_enabled |
| Label | Invoice |
| Type | Toggle |
| Default | Off |
| Availability | Mplus locations only (the option is hidden for non-Mplus tenants) |
Description: Lets business (B2B) customers choose Invoice at checkout instead of paying online. Those orders are submitted to the POS as unpaid/open and are billed later from the Invoice Collection page (see the Invoice Collection feature).
Business Logic:
- When enabled, an Invoice option appears at online-ordering checkout for the location.
- Invoice orders are not charged online; the merchant bills the customer afterwards via Invoice Collection (issued through Stripe).
- Only shown for Mplus tenants.
Note: Cash on Delivery (COD) is not currently available in production, so it is not offered at checkout.
Business Logic
Order Flow
Customer places order
│
▼
Validate order:
├── Check business hours
├── Check capacity
├── Check minimum order
├── Check delivery region (if delivery)
├── Check item availability
└── Check payment
│
▼
Calculate times:
├── order_prep_time = earliest slot available
├── order_date = order_prep_time + prep_time
└── sent_at = when to send to kitchen
│
▼
Process payment
│
▼
Create transaction
│
▼
Send to KDS (based on sent_at)Time Calculation
Current Time: 12:00
Prep Time: 20 min
Pickup Delay: 5 min
Delivery Delay: 15 min
Pickup earliest: 12:00 + 20 + 5 = 12:25
Delivery earliest: 12:00 + 20 + 15 = 12:35
With 15-min slots:
Pickup slots: 12:30, 12:45, 13:00...
Delivery slots: 12:45, 13:00, 13:15...Per-Person Items (Party Size)
Some MplusKassa items are sold per party rather than per plate (a "bestelbeperking" — e.g. a sharing dish only orderable for 2, 4 or 6 people). The allowed party sizes are synced from MplusKassa into the item's order_quantities list and sent to the storefront with the item. An empty list means unrestricted, and the customer sees no party-size control at all. (Verified: upvendo-backend app/RawModels/Item.php lines 187-194; app/Http/Resources/OnlineOrdering/ShowItemResource.php line 126.)
On the item detail modal — two separate controls:
- A party-size selector listing the allowed values, defaulting to the first one. It only renders when the item has allowed party sizes. (zestidoo-online-ordering
src/components/item-detail/ItemDetailActions.vuelines 12-18;src/components/ItemDetail.vuelines 198-205.) - The usual +/- quantity stepper, which still counts dishes — 2× a dish for 6 people is a valid order.
What the bag stores: the line's quantity is article units = party size × dishes (this is what gets priced), plus a persons field holding the party size. Re-opening the line to edit it backs the dish count out of the stored units. (src/components/ItemDetail.vue lines 207, 209, 489-494.)
In the shopping bag: when persons > 1 the row displays the dish count (units ÷ persons), the +/- buttons step a whole party at a time, and the quantity field is read-only so a partial party can't be typed in. A "Voor {count} personen" subtitle appears whenever the line carries a party size. (src/components/ShoppingBagItem.vue lines 15-16, 152-163.)
In account order history: the order-detail row divides back to dishes only when persons > 1 and the stored units divide evenly by the party size; otherwise it prints the raw units. The same "Voor {count} personen" subtitle is shown. (src/components/sidebar/user-account/content/account-menu/orders/OrderDetail.vue lines 46-47, 228-232.)
Order Again (re-order): the saved party size is replayed onto the rebuilt line. The line is dropped only when the item still restricts party sizes and the saved one is no longer in that set. If the bestelbeperking was cleared in MplusKassa entirely (order_quantities now empty = unrestricted), the line is kept and the saved party size is still carried onto it. (src/utils/reorder.ts lines 262-266: if (allowed.length && !allowed.includes(persons)) return null;)
Customer Impact
Online Ordering Experience
- Landing Page: Shows location info, menu preview
- Menu: Browse categories and items
- Item Detail: Select modifiers, add to cart
- Cart: Review order, apply promo codes
- Checkout: Choose pickup/delivery, select time, enter details
- Payment: Complete payment
- Confirmation: Order number, estimated time
What Settings Affect
| Setting | Customer Sees |
|---|---|
| No active subscription | Online ordering not available (enablement is subscription-driven, not an enabled flag) |
Snoozed (snoozed_until) | Timed snooze → "Available in {duration}" countdown; permanent ("don't accept") → "Currently not accepting orders" |
| Pickup disabled | No pickup option |
| Delivery disabled | No delivery option |
| Eat In disabled (default) | No "Eat in" option |
| Eat In enabled | "Eat in" option shown alongside Pickup/Delivery; order recorded as For Here/dine-in |
| Minimum order not met | "Minimum order amount (€X.XX) not met" |
| Prep time | Earliest available time |
| Scheduled days in advance | Calendar date range |
Menu Layout (menu_item_columns) | Menu items shown 2, 3 or 4 across (single column on the narrowest screens; 4 only reaches four across on wide screens) |
Show location hours only (show_location_hours_only) | Only the location's business hours are shown; the separate online-ordering hours are hidden (no effect unless Custom Availability is on) |
Continue selling out of stock (sell_without_stock) | Out-of-stock items stay orderable |
Show remaining item stock (show_item_stock) | Remaining stock count shown on the item detail modal for inventory-tracked items (not on the menu grid) |
Legal identity on the storefront
The storefront's general terms & conditions page identifies the trader (mandatory under Belgian law) from the merchant's default billing profile, not from anything on the Online Ordering page or the location settings. The ordering data the storefront receives carries a legal block with:
- Company name — the default billing profile's company name, falling back to the merchant's business name when there is no billing profile or its company name is blank.
- VAT number — the default billing profile's VAT, omitted entirely when it is not set (never sent empty).
- Website — resolved on the server, preferring a verified custom domain over the standard ordering URL. It is resolved server-side because the app builds cannot derive their own public address.
To change the company name or VAT shown on your storefront's terms page, edit the default billing profile under Settings → Billing — there is no separate field for it on the Online Ordering page. See Billing.
Support note: the
legalblock was added on 2026-07-25 and the ordering data is refreshed per location on save (or by a manual reconciliation run), not on a schedule. A location that has not been saved since then still serves ordering data with nolegalblock; re-saving the location fills it in.
Relations
Depends On
- Locations: Online ordering is per-location
- Business Hours: Available times within hours
- Menus: What items are available
- Delivery Region: A single
delivery_region(Radius / Distance / Postal Code) defines where delivery is available
Affects
- Order Capacity: Orders consume capacity
- Transactions: Orders create transactions
- KDS: Orders sent to kitchen
- Inventory: Stock decremented
Related Features
Business Rules
- Saving online ordering settings automatically creates a Stripe subscription for the location if one does not already exist; incomplete subscriptions are retried before creating new ones.
- The snooze feature uses the location's timezone to calculate expiry; "rest of business day" sets the snooze until the start of the next day, while "don't accept" stores a permanent flag rather than a timestamp.
- Delivery is bounded by a single
delivery_region(type Radius, Distance, or Postal Code), not by multiple "zone" entities; delivery fees can be customised per postal code viadelivery_fee.custom_fees[]. - The earliest available time slot is calculated as: current time + average preparation time + channel-specific delay (pickup delay, delivery delay, or eat-in delay).
- Updating online ordering settings fires a
ReloadMenuevent scoped to the Online Ordering channel, ensuring the customer-facing ordering app reflects changes immediately. - Eat In (
eat_in.enabled) is opt-in and disabled by default. It behaves like online pickup ordering (timeslot picker, future-day scheduling viaschedule_order_for_future_days.eat_in, shared order capacity) but the order is recorded as For Here/dine-in for correct POS category and VAT handling. Unlike pickup/delivery, eat-in has no same-day/next-day cutoff toggles, and its last available slot is always closing time minus prep time (theaccept_orders_untilsetting does not apply to eat-in). Availability otherwise follows the location's business hours. - Known limitation — online eat-in tax display. An online eat-in (
For Here) order is charged the item's dine-in rate, but the storefront's displayed tax breakdown currently shows the take-out rate: the online-ordering menu payload only carries Delivery and Pickup rate keys, and the storefront maps For Here to Pickup. This only produces a visible mismatch when the location's dine-in and take-out rates differ and the tax method is Exclusive. The Kiosk had the same issue and was fixed on 2026-06-24. Do not tell merchants the displayed eat-in VAT is authoritative — the recorded and charged amount is. eat_inandschedule_order_for_future_days.eat_inare both nullable/optional on save — clients that never send these blocks keep working and fall back to the disabled defaults.- The storefront menu grid (
menu_item_columns) accepts 2, 3 or 4 columns and defaults to 2. The kiosk device profile has a separate field of the same name that accepts only 2 or 3; the two are independent, and changing one does not affect the other. menu_item_columns,show_location_hours_onlyandshow_item_stockare display-only — none of them changes ordering availability, the schedule, or how stock is enforced.sell_without_stockis the only one of the four that changes what customers can order.show_location_hours_onlyonly has a visible effect when Custom Availability is enabled: the separate online-ordering (or QR-ordering) hours are sent to the ordering app only when custom availability is on and this toggle is off.sell_without_stockis resolved per channel at payment capture — kiosk orders use the kiosk device profile's ownsell_without_stock, online orders use the location's online-ordering setting.- The Inventory Settings / Stock Visibility card (
sell_without_stock+show_item_stock) is only rendered on the Online Ordering page when the location has at least one inventory-tracked item.
FAQs
How do I enable or pause online ordering? There is no
enabledmaster switch and no "accepting orders" toggle. Online ordering is enabled by subscribing to the Online Ordering channel (a Stripe subscription is created when you save settings), and it is paused via Snooze (snoozed_until) — not by an on/off field.How does snoozing work? Snoozing temporarily pauses order acceptance for a chosen duration (20, 40, or 60 minutes), the rest of the business day, or indefinitely. The snooze automatically expires when its timer runs out.
Do I need a payment provider before enabling online ordering? Yes. A connected payment provider and a Stripe subscription are required. The system attempts to create a subscription automatically when you save settings.
Can I have different preparation times for pickup and delivery? The base preparation time is a location-level setting (
average_prep_time) shared by both channels, but you can add separate pickup delay (takeout.delay) and delivery delay (delivery.delay) values that extend the earliest available time slot for each channel independently. Setting a longer prep time on individual menu items does not work for this — item prep time is switched off platform-wide (see Menu Items)."Where do I find Online Ordering settings in the back office?" Go to Online → Online Ordering. You must first select a location; settings are configured per location.
"How do I set my preparation time? I don't see it on the Online Ordering page." The base preparation time is a location-level setting, not an Online Ordering field. Set it under Settings → Locations ("Average preparation time"); the Online Ordering page shows it read-only and lets you add per-channel delays on top. On the Online Ordering / Pickup / Delivery cards you can choose "Use default preparation time" or "Add Delay" to extend the earliest slot for that channel.
"What are the snooze options and how long do they last?" Snooze choices are 20, 40, or 60 minutes, "For the rest of the day," "Do not accept" (indefinite pause), or "Accept online orders" (resume). Timed snoozes expire automatically; "rest of the day" lasts until the next day; "Do not accept" stays paused until you resume. Snooze uses the location's timezone.
"Can I have separate same-day and next-day cut-off times for pickup and delivery?" Yes. Pickup and delivery each have independent Same-day cut-off and Next-day cut-off toggles with a time. After the same-day cut-off, same-day ordering closes and customers must pick a later day; the next-day cut-off works the same for next-day ordering.
"When are pickup/delivery orders sent to my POS?" Under Receiving Orders → Send to POS you choose "Receive orders based on pickup and delivery time" (default — tickets sent based on prep time before the order is due) or "Receive orders when the order is placed" (sent immediately, even for future-scheduled orders).
"How is the order reference shown to staff and customers?" Under Receiving Orders → Order reference, pick Order number (unique tracking ID), Queue number (simple pickup number that resets daily), or Both. The default is Both.
"Can I require a different minimum order for pickup vs delivery?" Yes — they are two separate fields. Delivery uses
minimum_order_amount("Delivery minimum order amount", on the Delivery Settings card); Pickup usespickup_minimum_order_amount(Pickup Settings card). There is no general minimum that covers both, and eat-in orders are exempt from both."What delivery fee models are available?" Three types: Free delivery, Charge a delivery fee for all orders, or Free delivery for orders over a certain amount. You can add custom fees by postal code, and optional route-distance pricing (tiered or flat rate per km).
"How do I define my delivery area?" Under Delivery Settings → Delivery Region, choose Radius (e.g. 10 km), Distance (driving-route distance), or Postal Code (list the exact postal codes you deliver to). Customers outside the region can't place delivery orders.
"How do I connect my own custom domain for ordering?" Add a subdomain (e.g.
order.yourdomain.com— apex/root domains aren't supported), then add the CNAME record (routing) and the TXT record at_upvendo-verify.<domain>(ownership), and click Verify Domain. DNS can take 5-15 minutes; if you use Cloudflare DNS, set the CNAME proxy to OFF (grey cloud)."How do I temporarily close a location but still take future orders?" Use Temporary Closure and set a reopening date ("Closed until"). Customers can still order, but only for dates on or after the reopening date. This is separate from snooze, which fully pauses acceptance.
"How do I let business customers pay by invoice instead of paying online?" Under Checkout → Payment methods, enable Invoice payment. This option only appears for Mplus tenants, and only customers whose account email is registered as a business in your POS can use it; those orders are billed later rather than charged online.
"What customer details can I require at checkout?" Under Checkout → Customer Information: Full name (require first and last, or only first) and Contact details (require email and phone, or only email). Defaults are first+last name and email+phone.
"Can customers order online and eat in at the restaurant?" Yes, if you enable Eat in (
eat_in.enabled) on the Online Ordering page. It's off by default. Customers pick a time slot like pickup, but the order is recorded as For Here/dine-in on the POS."How do I turn on eat-in / 'For Here' online ordering?" Go to Online → Online Ordering and toggle Eat in on (next to Pickup and Delivery). You can optionally add a delay (
eat_in.delay, in minutes) on top of the location's preparation time, same as Pickup/Delivery delay."Can customers schedule an eat-in order for a future day?" Yes, if you also enable the eat-in future-days toggle (
schedule_order_for_future_days.eat_in.enabled) and set Eat In Days in Advance (schedule_order_for_future_days.eat_in.days_in_advance, default 30, max 365) in the Fulfillment and Scheduling section — this only appears once Eat In itself is enabled."Why can't I set a same-day/next-day cutoff for eat-in orders?" Eat-in doesn't have same-day/next-day cutoff toggles like pickup and delivery. Its last available slot is always closing time minus the location's preparation time, and it doesn't use the
accept_orders_untilsetting."Does eat-in use its own order capacity, or share it with pickup and delivery?" Eat-in shares the same order capacity pool as pickup and delivery orders for the location.
"Can I change how many menu items show per row on my online ordering page?" Yes. On Online → Online Ordering, use Menu Layout to pick a 2, 3 or 4 column layout (
menu_item_columns, default 2 columns)."I set Menu Layout to 4 columns but I only see two or three — is it broken?" No. The storefront is always a single column on the narrowest screens and steps up with screen width: the 4-column layout shows two across on small screens, three on medium, and four only on extra-large screens. The 2- and 3-column options apply from the small breakpoint up.
"Is the online ordering Menu Layout the same setting as the kiosk's grid columns?" No. They're separate settings on separate records. The kiosk grid lives on the device profile and still only offers 2 or 3 columns; the online-ordering Menu Layout offers 2, 3 or 4. Changing one does not change the other.
"How do I stop showing separate online ordering hours to my customers?" Turn on Show location hours only in Fulfillment and Scheduling. Customers then see only your location's opening hours; the separate online-ordering hours are hidden. It's display-only — it does not change when customers can actually order. It also only makes a visible difference when Custom Availability is on, since that's the only time separate online-ordering hours exist.
"Can customers still order items that are out of stock?" Only if you enable Continue selling items when out of stock (
sell_without_stock) under Inventory Settings on the Online Ordering page. Off by default. Note this is the online-ordering setting; kiosks have their own equivalent on the device profile."Can I show customers how many of an item are left?" Yes — turn on Show remaining item stock to customers (
show_item_stock) under Stock Visibility. It's off by default and display-only; it doesn't change availability. The count appears when the customer opens the item (the item detail view), not on the menu grid while browsing. At 10 or fewer remaining it reads "Only {count} left"; above that, "{count} in stock". Counts only appear for items you actually track inventory for, and they're suppressed when "Continue selling items when out of stock" is on (stock is treated as unlimited then)."Why don't I see the Inventory Settings / Stock Visibility options on the Online Ordering page?" That card only appears when the location has at least one inventory-tracked item. Start tracking inventory on a menu item and the toggles appear.
Troubleshooting
Problem: "Online ordering not available"
Causes:
- Online ordering disabled
- Location disabled
- No menu assigned
Solutions:
- Enable online ordering toggle
- Enable location in Settings
- Assign menu to location
Problem: No time slots available
Causes:
- Outside business hours
- Capacity limits reached
- All slots booked
Solutions:
- Check business hours configuration
- Increase capacity limits
- Wait for slots to free up
Problem: Delivery not showing
Causes:
- Delivery disabled
delivery_regionnot configured- Customer address outside the configured delivery region
Solutions:
- Enable delivery toggle
- Configure the
delivery_region(Radius, Distance, or Postal Code) - For Postal Code regions, add the customer's postal code to the region
Problem: Stock counts not showing to customers
Causes:
show_item_stockis off (it's off by default)sell_without_stockis on — stock is treated as unlimited, so there is no finite count to display- The item does not track quantity, so it has no stock count
- The merchant is looking at the menu grid — counts are only rendered on the item detail view in production
Solutions:
- Turn on Show remaining item stock to customers under Stock Visibility (the card only appears once the location has at least one inventory-tracked item)
- Turn off Continue selling items when out of stock if you want real counts shown
- Enable inventory tracking on the item
- Open an item on the storefront to see its count; the menu-grid pill is not rendered in production
Problem: "Eat in" option not showing
Causes:
eat_in.enabledis off (it's off by default)- Outside business hours (eat-in follows location business hours, with the last slot at closing time minus prep time)
- No time slots available (see "No time slots available" above)
Solutions:
- Go to Online → Online Ordering and toggle Eat in on
- Check the location's business hours configuration
- Increase capacity limits if slots are fully booked (eat-in shares capacity with pickup/delivery)
Assistant Guidance
When answering questions about this feature:
- Online ordering has three fulfillment channels: Pickup (
takeout), Delivery (delivery), and Eat In (eat_in). Don't assume it's pickup/delivery only — eat-in is opt-in and off by default, but merchants do ask about it. - Eat In orders behave like pickup (timeslot picker, future-day scheduling, shared order capacity) but are recorded as For Here/dine-in on the POS, not takeout. Mention this distinction when a merchant asks about "eat in", "dine in", or "For Here" online ordering.
- Eat In does not have same-day/next-day cutoff toggles like pickup and delivery do — its last slot is always closing time minus prep time. Don't tell a merchant to look for an eat-in cutoff setting; it doesn't exist.
- There is no
enabledmaster switch for online ordering itself — it's enabled via Stripe subscription and paused via Snooze. This is separate from the per-channeltakeout.enabled/delivery.enabled/eat_in.enabledtoggles. - If a merchant says a channel (pickup/delivery/eat-in) option isn't showing at checkout, check in this order: channel toggle enabled → business hours → capacity/slots available → (delivery only) delivery region configured and address in range.
menu_item_columnsexists twice in the product and the two are different. Online ordering's Menu Layout allows 2, 3 or 4; the kiosk device profile's field of the same name allows only 2 or 3. Always confirm which channel the merchant means before quoting the options, and never tell a kiosk merchant they can pick 4 columns.- A merchant who picks the 4-column layout and still sees two or three columns is not looking at a bug — the storefront steps the grid up with screen width and only reaches four columns on extra-large screens.
show_location_hours_onlyandshow_item_stockare display-only. If a merchant reports that customers can't order (or can order) something, neither of these is the cause — check channel toggles, hours/cut-offs, capacity, andsell_without_stockinstead.show_location_hours_onlydoes nothing visible unless Custom Availability is enabled — there's no separate online-ordering hours block to hide otherwise.- The Inventory Settings / Stock Visibility toggles are hidden entirely until the location tracks inventory on at least one item; that's the usual reason a merchant "can't find" them.
Note: These examples show the relevant fields of the
online_ordering_settingobject only (not the full object). The base preparation time is the location-levelaverage_prep_time, not part of this object.
Quick Service Restaurant
json
{
"takeout": { "enabled": true, "delay": 0 },
"delivery": { "enabled": true, "delay": 10 },
"eat_in": { "enabled": false, "delay": 0 },
"minimum_order_amount": { "enabled": true, "amount": 10 },
"schedule_order_for_future_days": {
"takeout": { "enabled": true, "days_in_advance": 3 },
"delivery": { "enabled": true, "days_in_advance": 3 },
"eat_in": { "enabled": false, "days_in_advance": 30 }
},
"custom_scheduled_orders": false
}Fine Dining (Pickup Only)
json
{
"takeout": { "enabled": true, "delay": 15 },
"delivery": { "enabled": false, "delay": 0 },
"eat_in": { "enabled": false, "delay": 0 },
"minimum_order_amount": { "enabled": true, "amount": 50 },
"schedule_order_for_future_days": {
"takeout": { "enabled": true, "days_in_advance": 7 },
"delivery": { "enabled": false, "days_in_advance": 30 },
"eat_in": { "enabled": false, "days_in_advance": 30 }
},
"custom_scheduled_orders": true,
"hours_in_advance": 24,
"minutes_before_pickup_delivery_time": 60
}Restaurant with Eat In Enabled
json
{
"takeout": { "enabled": true, "delay": 0 },
"delivery": { "enabled": false, "delay": 0 },
"eat_in": { "enabled": true, "delay": 5 },
"minimum_order_amount": { "enabled": false, "amount": 10 },
"schedule_order_for_future_days": {
"takeout": { "enabled": true, "days_in_advance": 30 },
"delivery": { "enabled": false, "days_in_advance": 30 },
"eat_in": { "enabled": true, "days_in_advance": 14 }
},
"custom_scheduled_orders": false
}