Appearance
Business Hours
Overview
Business Hours (labelled Operating hours in the back office) define which days and times a location is open. They are part of the Location configuration, not a standalone screen, and control when customers can place orders.
Key Purpose: Define the weekly operating schedule for each location.
Purpose
The Operating hours section of the location form lets you tick which days the location is open and set one or more time ranges per open day. These hours are stored on the location as business_hours and are used to decide whether a location is open at a given time.
Key Concepts
- Per-day schedule:
business_hoursis keyed by full, capitalized weekday names (MondaythroughSunday). Each day has anis_availableflag and atimesarray. - Time ranges: Each day's
timesis an array of{ "from": "HH:MM", "to": "HH:MM" }pairs. A day can have more than one range (split shifts, e.g. lunch and dinner). - 24-hour time: Times are stored and validated in 24-hour
H:iformat (e.g.09:00,17:30,22:00). - Day toggle: Unticking a day sets
is_availableto false and clears that day'stimes. - Timezone awareness: Open/closed checks run in the location's stored
timezone, falling back toEurope/Brusselswhen that field is empty. The stored value is derived at save time from the map pin (or, if that lookup fails, from the postal code) — it is not resolved again at check time. - Channel availability is separate: Online ordering and QR ordering each have their own optional "custom availability" schedule. When custom availability is off, they fall back to these business hours (see Channel Availability below).
Actions
Set the Weekly Schedule
Tick the days the location is open and add one or more time ranges per open day under Operating hours.
Apply Hours to All Days (Select All)
Use the Select All control to copy one schedule (availability + time ranges) to every day at once.
Add Split Shifts
Use Add Times on a day to add another time range within that day (e.g. 11:00–14:30 and 17:00–22:00) to represent a break between service periods.
Location
- Backoffice Route:
/settings/locations/:id— Operating hours is a section within the location form (usenewas the id when adding a location). There is no dedicated business-hours URL. - Backend Model:
app/RawModels/Location.php(getBusinessHours()) - Validation:
app/Http/Requests/BackOffice/Settings/Location/StoreLocationRequest.php - Vue Component:
src/views/settings/Locations/forms/LocationBusinessHours.vue, which wraps the sharedsrc/components/forms/OperatingHours.vue
Concepts
Regular Business Hours
A weekly schedule (Monday–Sunday). Each day is either open (is_available: true) with one or more time ranges, or closed (is_available: false).
Channel Availability
Online ordering and QR ordering can run on a different schedule from the main business hours, but this is configured on their own settings, not in the Operating hours section:
- Online ordering (pickup and delivery): uses the online-ordering "custom availability" schedule when enabled, otherwise falls back to business hours.
- QR ordering (for-here / table ordering): uses the QR-ordering "custom availability" schedule when enabled, otherwise falls back to business hours.
There are no separate per-day "pickup hours" or "delivery hours" fields — pickup and delivery both use the online-ordering schedule.
Restricted Dates
Restricted dates close the location on specific dates regardless of the weekly schedule. They are configured in their own section of the location form (see Locations / Restricted Dates).
Fields
The business_hours object is keyed by day name. Each day looks like:
json
{
"Monday": {
"is_available": true,
"times": [
{ "from": "11:00", "to": "14:30" },
{ "from": "17:00", "to": "22:00" }
]
}
}Day Availability
| Property | Value |
|---|---|
| Field ID | business_hours.{Day}.is_available |
| Label | Day checkbox (Monday … Sunday) |
| Type | Boolean (checkbox) |
| Required | Yes (per day) |
Description: Whether the location is open on this day. Days are the capitalized full names Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday.
Notes:
- Unticking a day clears its
times. - The form requires at least one open day with a valid time range before the location can be saved.
Time Ranges
| Property | Value |
|---|---|
| Field ID | business_hours.{Day}.times |
| Label | Time ranges (per day) |
| Type | Array of { from, to } objects |
| Required | Required when the day is is_available: true |
Description: One or more opening periods for the day. Each entry has a from and a to. A day may have multiple ranges to represent split shifts.
Notes:
- Both
fromandtoare required when the day is open. - There is no configured limit on the number of ranges per day.
From / To
| Property | Value |
|---|---|
| Field ID | business_hours.{Day}.times[].from / business_hours.{Day}.times[].to |
| Label | (from) … to … (to) |
| Type | Time |
| Required | Required when the day is open |
| Validation | date_format:H:i (24-hour, e.g. 08:00) |
Description: Opening (from) and closing (to) time for a range.
Examples:
09:0017:3022:00
Note: The field-level validation only checks that each value is a valid 24-hour H:i time, but the save path adds two further rules on top of it:
tomust be strictly afterfrom. Equal or reversed times are rejected with "There is an invalid time range".- Ranges within the same day must not overlap. An overlap is rejected with "There is a time overlap in the business hours".
Both run on create and update, for every day that is ticked open. There is still no limit on the number of ranges per day, and the open/closed check compares the times as plain strings, so a range whose to wraps past midnight is not handled specially.
Channel Availability
These are configured outside the Operating hours section and are documented here only to clarify how business hours interact with ordering channels. The exact UI for these schedules is part of the Online Ordering / QR ordering settings.
- Online ordering schedule: If online-ordering custom availability is enabled, online ordering uses that schedule; otherwise it uses the location's business hours.
- Pickup and delivery: Both follow the online-ordering schedule above. There are no independent pickup-only or delivery-only weekly schedules.
- Eat-in (For Here, online): The open/close gate always uses the location's business hours — it never uses a QR or custom-availability schedule.
- QR ordering schedule: A QR custom-availability schedule affects only which hours are displayed to customers; QR/table ordering itself is immediate and is not gated by this schedule (it never reaches the open/close check).
- Show location hours only (
show_location_hours_only, default off, in Online Ordering settings): a display-only toggle. When enabled, the storefront hides the separate online-ordering / QR-ordering hours column and shows only the location's business hours. It does not change actual ordering availability.
Business Logic
Open / Closed Check
When checking whether a location is open at a requested time (checkForBusinessHours):
Resolve the requested time in the location's timezone
│
▼
Pick the schedule for the dining option:
- Pickup / Delivery → online-ordering custom-availability schedule (or business hours if it's off)
- Eat-in / For Here (online) → the location's business hours (never a QR schedule)
- Otherwise → business hours
(QR table ordering is immediate and never reaches this gate.)
│
▼
Reject if the location is temporarily closed until a later date
Reject if the date is a restricted date
Reject if a same-day / next-day cutoff has passed (pickup / delivery only)
Reject if the date is beyond that channel's "schedule for future days" window
(delivery / takeout / eat-in) — when that channel's future scheduling is off,
the window collapses to 0 days and today is the only orderable date
Reject if the time falls within a merchant-configured blocked time period (pickup / delivery only)
│
▼
Is that weekday is_available?
├── No → CLOSED
└── Yes → Is the requested time within any "from"–"to" range?
├── Yes → OPEN
└── No → CLOSEDThe time comparison is time >= from && time <= to on the H:i strings.
Preparation Time
Preparation time is a separate Location setting (average_prep_time, default 20 minutes), not part of the business-hours schedule. The effective prep time per dining option is:
prep time = average_prep_time
+ delivery delay (delivery orders)
+ takeout delay (pickup orders)
+ eat_in delay (online eat-in orders)Those three dining options are the only ones that get a prep time, and only on the online-ordering channel. Every other channel or flow — kiosk, table-QR ordering — gets 0. Per-item prep times are not part of this either: that behaviour is gated off in every environment (see Menu Items). The delay may be negative, and only the final result is clamped, to 0-1440 minutes. (Verified: app/RawModels/Location.php:2001-2018 — getDiningOptionPrepTime(), the one place this is computed, with the 1440 ceiling at :60; the channel and table-QR exclusions at app/Services/OrderCapacity/OrderCapacityService.php:126-131.)
This affects the earliest time a customer can be served; it is not subtracted from the closing time as part of business hours.
Customer Impact
Online Ordering
| Scenario | Effect |
|---|---|
| Day not available | The location is treated as closed for that day |
| Time outside all ranges | The location is treated as closed at that time |
| Restricted date | The location is closed for that date |
Online Ordering — what the customer actually sees
| State | Condition | What the customer sees |
|---|---|---|
| No orderable date at all | Neither pickup nor delivery has an orderable day inside its booking window (today + days-in-advance, honouring a temporary-closure reopening date) — typically today is closed and "schedule for future days" is off | The "Choose" date/time prompt is replaced by the closed banner "Not accepting orders" |
| Today unavailable, a later day is available | Today is closed (restricted date or a non-open weekday) but the future-days window reaches an open day | The date auto-advances to the first orderable day, with the hint "Today is unavailable. The earliest order date is {date}." under the date field |
| Selected date past its cut-off | The same-day or next-day cut-off for the selected option has passed | The informational deadline hint is replaced by an amber alert "Same-day {option} ordering has closed (cut-off was {time}). Please choose another date." (or "Next-day …"), and Save is disabled |
| Table-QR past the accept-orders-until cut-off | The QR channel's Accept Orders Until is "Closing Time Minus Prep Time" and now falls inside (close − preparation time, close] | QR checkout is blocked and the reason reads "QR ordering has closed for today". Past the actual closing time the ordinary closed handling takes over |
Kiosk
Kiosk availability depends on kiosk/device configuration and is not driven solely by these business hours.
Relations
Depends On
- Locations: Business hours are stored on the location (
business_hours). - Timezone: Open/closed checks use the location's timezone.
Affects
- Online Ordering: Falls back to business hours when no custom availability schedule is set.
- QR Ordering: Falls back to business hours when no custom availability schedule is set.
- Online-ordering readiness: A location counts as having business hours set when at least one day has a non-empty
timesarray (part of the Ready / Partial / Incomplete badge on the Locations list).
Related Features
Business Rules
- Business hours are stored on the location as
business_hours, keyed by capitalized day namesMonday–Sunday. - Each day has a required
is_availableboolean. When a day is available, itstimesarray (and each entry'sfromandto) is required. fromandtomust be valid 24-hourH:itimes, andtomust be strictly afterfrom— equal or reversed times are rejected on save with "There is an invalid time range". There is no limit on the number of ranges per day.- Time ranges within one day must not overlap; an overlap is rejected on save with "There is a time overlap in the business hours". The overlap test is strict, so back-to-back ranges are fine (
11:00–14:00followed by14:00–22:00) while11:00–15:00together with14:00–22:00is rejected. - A requested date beyond the dining option's "schedule for future days" window (delivery / takeout / eat-in) is treated as closed. When that channel's future scheduling is off the window is 0 days, so only today is orderable.
- Unticking a day clears that day's time ranges.
- The location form requires at least one open day with a valid time range before saving.
- Online ordering and QR ordering use their own custom-availability schedules when enabled, and otherwise fall back to these business hours. Pickup and delivery both follow the online-ordering schedule.
- Open/closed checks are evaluated in the location's timezone and also respect restricted dates, the temporarily-closed-until date, and same-day / next-day cutoffs.
FAQs
- "Where do I set my opening hours?" In Settings → Locations, open the location and go to the Operating hours section.
- "Can I have a lunch break (two ranges in one day)?" Yes. Use Add Times to add a second range for that day, leaving a gap in between.
- "Can I copy the same hours to every day?" Yes. Use Select All to apply one schedule to all days at once.
- "What time format do I enter?" 24-hour
HH:MM(e.g.17:30), shown in the location's timezone. - "Do online ordering and delivery follow these hours?" By default yes. If you set a custom availability schedule for online ordering or QR ordering, that schedule is used instead. Pickup and delivery both follow the online-ordering schedule.
- "How do I close for a holiday?" Use Restricted Dates on the location, not the weekly hours.
Troubleshooting
Problem: Location shows as closed during expected open hours
Causes:
- Wrong timezone on the location
- The day is not ticked (
is_availablefalse) - The current time falls in a gap between time ranges
- A restricted date or temporarily-closed-until date applies
- A same-day / next-day cutoff has passed (pickup / delivery)
- A blocked time period is active for that time (pickup / delivery) — configured in Online Ordering settings, separate from the weekly business hours
- The date is beyond the dining option's "schedule for future days" window — with future scheduling off for that channel, only today is orderable
Solutions:
- Verify the location's timezone
- Tick the day in Operating hours
- Check the day's time ranges
- Check Restricted Dates and any temporary closure
- Check the online-ordering cutoff settings
- Check "Can customers schedule pickups & delivery for future days?" and its days-in-advance value for that channel
Problem: Can't save the location — operating hours error
Cause: No open day has a complete time range.
Solution: Tick at least one day and add a range with both a from and a to time.
Cause: Two ranges on the same day overlap, or a range's to is not later than its from (the error reads "There is a time overlap in the business hours" or "There is an invalid time range").
Solution: Make the ranges on that day disjoint, and make each to strictly later than its from.
Problem: Online ordering hours differ from what was set here
Cause: A custom availability schedule is enabled for online ordering or QR ordering, which overrides business hours for that channel.
Solution: Review the online-ordering / QR-ordering availability settings, or disable custom availability to fall back to business hours.
Examples
Standard Restaurant (Lunch & Dinner, closed Sunday)
json
{
"Monday": { "is_available": true, "times": [{ "from": "11:30", "to": "14:30" }, { "from": "17:30", "to": "22:00" }] },
"Tuesday": { "is_available": true, "times": [{ "from": "11:30", "to": "14:30" }, { "from": "17:30", "to": "22:00" }] },
"Wednesday": { "is_available": true, "times": [{ "from": "11:30", "to": "14:30" }, { "from": "17:30", "to": "22:00" }] },
"Thursday": { "is_available": true, "times": [{ "from": "11:30", "to": "14:30" }, { "from": "17:30", "to": "22:00" }] },
"Friday": { "is_available": true, "times": [{ "from": "11:30", "to": "14:30" }, { "from": "17:30", "to": "23:00" }] },
"Saturday": { "is_available": true, "times": [{ "from": "17:00", "to": "23:00" }] },
"Sunday": { "is_available": false, "times": [] }
}Continuous Hours
json
{
"Monday": { "is_available": true, "times": [{ "from": "10:00", "to": "22:00" }] },
"Tuesday": { "is_available": true, "times": [{ "from": "10:00", "to": "22:00" }] },
"Wednesday": { "is_available": true, "times": [{ "from": "10:00", "to": "22:00" }] },
"Thursday": { "is_available": true, "times": [{ "from": "10:00", "to": "22:00" }] },
"Friday": { "is_available": true, "times": [{ "from": "10:00", "to": "23:00" }] },
"Saturday": { "is_available": true, "times": [{ "from": "10:00", "to": "23:00" }] },
"Sunday": { "is_available": true, "times": [{ "from": "11:00", "to": "21:00" }] }
}Café (Breakfast & Lunch)
json
{
"Monday": { "is_available": true, "times": [{ "from": "07:00", "to": "16:00" }] },
"Tuesday": { "is_available": true, "times": [{ "from": "07:00", "to": "16:00" }] },
"Wednesday": { "is_available": true, "times": [{ "from": "07:00", "to": "16:00" }] },
"Thursday": { "is_available": true, "times": [{ "from": "07:00", "to": "16:00" }] },
"Friday": { "is_available": true, "times": [{ "from": "07:00", "to": "16:00" }] },
"Saturday": { "is_available": true, "times": [{ "from": "08:00", "to": "15:00" }] },
"Sunday": { "is_available": true, "times": [{ "from": "09:00", "to": "14:00" }] }
}