Skip to content

Online Settings

Overview

Online Settings is the location-specific configuration page for the online ordering channel. It controls operational parameters including order capacity management (how many orders and items are accepted per time slot), delivery and pickup day availability, tip collection settings, checkout note options, and menu assignments. Each location stores its own online settings on the Location document's online_settings field, allowing multi-location businesses to tailor their online ordering experience per venue.

The settings are managed by OnlineSettingsService, which validates menu eligibility and schedule overlaps and saves the validated payload to the location. The validation rules live in StoreOnlineSettingsRequest, and the defaults are sourced from Constants::getDefaultOnlineSettings().

Purpose

This page lets you configure how your online ordering channel operates for a specific location, including order capacity limits, available delivery and pickup days, tip collection preferences, checkout notes, and which menus are displayed to online customers.

Key Concepts

  • Order Capacity: A system for controlling order volume per time slot. The base (global) configuration includes a master enabled toggle, time_slot_duration_min (the interval in minutes), limit_orders_per_time_slot (boolean toggle), number_of_orders_per_time_slot (maximum orders), limit_items_per_time_slot (boolean toggle), and number_of_items_per_time_slot (maximum items). These global settings apply by default to all time slots unless overridden by time-specific rules.
  • Flexible Capacity: A capacity mode that allows exceeding the base order limit when order value meets a threshold. Enabled via enable_flexible_limit_order_per_time_slot, configured with value_threshold_per_time_slot (minimum order value to qualify) and allow_up_to (number of extra orders allowed). NOTE: the back office defines and validates these fields; the storefront enforcement behavior is not verified here.
  • Time-Specific Rules: Named override rules stored under order_capacity.time_specific_rules. Each rule has a unique name, a time_range (with from and to times), an active_days array (full day names, e.g. "Monday"), optional number_of_orders_per_time_slot and number_of_items_per_time_slot, optional flexible capacity settings, and optional category-specific limits. The hasOverlappingTimeRanges validation from SchedulingTrait rejects rules whose time ranges overlap on the same day.
  • Menu Assignment: Online settings define a default_menu_id (the primary menu) and optional extra_menu_ids (additional menus with schedule-based availability). Extra menus are validated for eligibility, non-duplication, not matching the default, internal schedule self-overlap (isSingleScheduleOverlap), and cross-menu schedule non-overlap (isScheduleOverlap). Schedules are resolved per location via Menu::getSchedule($location).
  • Collect Tips: A tipping object with enabled (boolean), options (array of up to 3 percentage values, each 1-100, default [10, 15, 20]), and calculate_tips (a CalculateTipsOptions enum value: "After taxes" or "Before taxes").
  • Checkout Notes (two independent settings): The checkout notes UI (CheckoutNotes.vue, shared with In-House settings) exposes two separate note toggles:
    • allow_notes -- per-item notes (UI label "Allow Item Notes"). Lets customers add a note to an individual line item.
    • allow_order_notes -- a whole-order note (UI label "Allow Order Notes"). Lets customers add a single instruction for the entire order (e.g. "Please ring the doorbell"). Both share the same shape ({enabled, placeholder}) and the same required/nullable validation pattern. For backward compatibility with locations saved before allow_order_notes existed, the backend and the back-office store both fall back to allow_notes when allow_order_notes is absent (see Technical Details).

Route

  • Backoffice Route: /online-settings (page at src/pages/online-settings/index.vue)
  • Backend Controller: app/Http/Controllers/Api/BackOffice/OnlineSettingsController.php
  • Service: app/Services/BackOffice/OnlineSettingsService.php
  • Request Validator: app/Http/Requests/BackOffice/OnlineSettings/StoreOnlineSettingsRequest.php
  • Resource: app/Http/Resources/BackOffice/OnlineSettings/ShowOnlineSettingsResource.php
  • Route File: routes/api/backoffice/online-settings.php (mounted under the /back-office prefix)
  • Constants: app/Constants.php -- getDefaultOnlineSettings() and the $DEFAULT_ORDER_CAPACITY_SETTINGS static property
  • Vue Component: src/views/online-settings/OnlineSettingsForm.vue
  • Store Module: src/store/modules/onlineSettings.ts
  • Types: src/types/onlineSettings.ts
  • Related Components (shared with In-House settings): src/views/in-house/components/forms/OrderCapacityManagement.vue, src/views/in-house/components/forms/CollectTips.vue, src/views/in-house/components/forms/CheckoutNotes.vue, src/components/DefaultMenusSection.vue (all rendered with mode="online-settings")

Actions

View Online Settings

Retrieve the current online settings for a specific location. OnlineSettingsService::show() resolves the Location, reads its online_settings, and enriches it by fetching the full Menu objects for the default menu (default_menu) and any extra menus (extra_menus). The resource (ShowOnlineSettingsResource) reads getOnlineSettings(alsoRetrieveCategoryName: true) (so category names are resolved for any category-specific limits), then strips raw default_menu_id and extra_menu_ids and replaces them with expanded default_menu / extra_menus objects (each with id, name, description, schedule).

  • Endpoint: GET /api/back-office/online-settings/{locationId}
  • Response: JSON ShowOnlineSettingsResource containing id, the spread online_settings (order_capacity, delivery_available_days, pickup_available_days, collect_tips, allow_notes), an explicitly computed allow_order_notes (see below), plus default_menu (or null) and extra_menus (array, defaults to [])
  • Backward compatibility for allow_order_notes: ShowOnlineSettingsResource always returns allow_order_notes explicitly: it uses the stored allow_order_notes if present, else falls back to the stored allow_notes, else falls back to {enabled: false, placeholder: 'Add a note for your whole order'}. This lets locations saved before order notes were split into their own setting keep working without a migration.

Update Online Settings

Save updated online settings for a specific location. The request is validated through StoreOnlineSettingsRequest. In prepareForValidation() the request:

  • Merges Constants::getDefaultOnlineSettings() defaults into order_capacity, collect_tips, allow_notes, and allow_order_notes (and defaults delivery_available_days / pickup_available_days to [] if absent). For allow_order_notes specifically, the merge order is: defaults, then the request's allow_notes value (backward-compat fallback for callers that only send allow_notes), then the request's own allow_order_notes value (if sent, it wins).
  • Applies "Guardrail A" normalization: if enable_limit_specific_categories is true but limit_specific_categories is empty, the toggle is forced off; if enable_flexible_limit_order_per_time_slot is true but value_threshold_per_time_slot/allow_up_to are not valid positive numbers, the flexible toggle is forced off (same normalization is applied per time-specific rule).

After validation, OnlineSettingsService::update() performs business-logic validation:

  1. Validates the default menu via isMenuEligible($settings['default_menu_id']).
  2. For each extra menu: aborts 400 if it equals the default menu; resolves it via getEligibleMenu($extraMenuId, locationId: ...); aborts 400 if its own schedule self-overlaps (isSingleScheduleOverlap); within a nested loop aborts 400 on duplicate extra menus and on cross-menu schedule overlap (isScheduleOverlap).
  3. Saves the validated payload to the location's online_settings field via LocationRepository::save().
  4. Clears both timeslot caches for the location -- OrderCapacityService::clearAvailableTimeslotsCache($locationId) and OrderCapacityService::clearAllTimeSlotStatsCacheForLocation($locationId) -- so a capacity change is visible on the storefront immediately instead of after the cache TTL. (Verified: app/Services/BackOffice/OnlineSettingsService.php lines 106-113.)
  5. Dispatches a ReloadMenu($locationId) event.
  • Endpoint: PUT /api/back-office/online-settings/{locationId}
  • Request Body: Full settings object (see Fields section)
  • Response: Standard success response (sendSuccess())

Fields

Order Capacity - Global Settings

FieldIDTypeRequiredValidation
Order Capacity Enabled (master toggle)order_capacity.enabledBooleanNonullable|boolean
Time Slot Duration (min)order_capacity.time_slot_duration_minNumberYesrequired|numeric|gt:0
Limit Orders per Slotorder_capacity.limit_orders_per_time_slotBooleanYesrequired|boolean
Max Orders per Slotorder_capacity.number_of_orders_per_time_slotNumberConditionalnullable by default; becomes required|numeric|gt:0 when limit_orders_per_time_slot is true
Limit Items per Slotorder_capacity.limit_items_per_time_slotBooleanYesrequired|boolean
Max Items per Slotorder_capacity.number_of_items_per_time_slotNumberConditionalnullable by default; becomes required|numeric|gt:0 when limit_items_per_time_slot is true

Order Capacity - Flexible Capacity

FieldIDTypeRequiredValidation
Enable Flexible Capacityorder_capacity.enable_flexible_limit_order_per_time_slotBooleanYesrequired|boolean
Value Threshold per Slotorder_capacity.value_threshold_per_time_slotNumberConditionalnullable|numeric|gt:0 by default; becomes required|numeric|gt:0 when flexible enabled
Allow Up To (extra orders)order_capacity.allow_up_toIntegerConditionalnullable|integer|gt:0 by default; becomes required|integer|gt:0 when flexible enabled

Order Capacity - Category-Specific Limits

FieldIDTypeRequiredValidation
Enable Category Limitsorder_capacity.enable_limit_specific_categoriesBooleanYesrequired|boolean
Category Limitsorder_capacity.limit_specific_categoriesArrayConditionalarray; required only when both enable_limit_specific_categories AND a non-empty limit_specific_categories are present, else nullable. Each entry's category_id must exist via ExistsInConnectionArrayWithModel (Category, tenant/settings collection)
Category IDorder_capacity.limit_specific_categories.*.category_idStringYesrequired|distinct|string
Items per Slot per Categoryorder_capacity.limit_specific_categories.*.number_of_items_per_time_slotNumberYesrequired|numeric|gt:0

Order Capacity - Time-Specific Rules

All paths below are nested under order_capacity.time_specific_rules.*. The array itself is nullable|array with a closure that rejects overlapping ranges (hasOverlappingTimeRanges).

FieldIDTypeRequiredValidation
Rule Name...time_specific_rules.*.nameStringYesrequired|string|distinct -- unique among rules
Time Range...time_specific_rules.*.time_rangeObjectYesrequired|array
Time Range From...time_specific_rules.*.time_range.fromStringYesrequired|string
Time Range To...time_specific_rules.*.time_range.toStringYesrequired|string
Active Days...time_specific_rules.*.active_daysArrayYesrequired|array; each entry required|Rule::in(Carbon::getDays()) (full day names, e.g. "Monday")
Orders per Slot...time_specific_rules.*.number_of_orders_per_time_slotNumberNonullable|numeric|gt:0
Items per Slot...time_specific_rules.*.number_of_items_per_time_slotNumberNonullable|numeric|gt:0
Enable Flexible (per rule)...time_specific_rules.*.enable_flexible_limit_order_per_time_slotBooleanNonullable|boolean
Value Threshold (per rule)...time_specific_rules.*.value_threshold_per_time_slotNumberConditionalbecomes required|numeric|gt:0 when that rule's flexible toggle is true
Allow Up To (per rule)...time_specific_rules.*.allow_up_toIntegerConditionalbecomes required|integer|gt:0 when that rule's flexible toggle is true
Category Limits (per rule)...time_specific_rules.*.limit_specific_categoriesArrayNonullable|array + ExistsInConnectionArrayWithModel; entries *.category_id required|distinct|string, *.number_of_items_per_time_slot required|numeric|gt:0

Delivery and Pickup

FieldIDTypeRequiredValidation
Delivery Available Daysdelivery_available_daysArrayNonullable|array; each entry required|Rule::in(Carbon::getDays()) (full day names)
Pickup Available Dayspickup_available_daysArrayNonullable|array; each entry required|Rule::in(Carbon::getDays()) (full day names)

Tips

FieldIDTypeRequiredValidation
Collect Tips (object)collect_tipsObjectYesrequired|array
Enable Tipscollect_tips.enabledBooleanYesrequired|boolean
Tip Optionscollect_tips.optionsArrayConditionalnullable|array (becomes required when collect_tips.enabled is true); each value numeric|min:1|max:100. Front office sends exactly 3 values
Calculate Tips Methodcollect_tips.calculate_tipsEnumConditionalnullable (becomes required when enabled) + Rule::enum(CalculateTipsOptions::class); valid values are the enum's string values "After taxes" / "Before taxes"

Notes

There are two independent note settings, both rendered by the shared CheckoutNotes.vue component: per-item notes (allow_notes, UI label "Allow Item Notes") and a whole-order note (allow_order_notes, UI label "Allow Order Notes").

FieldIDTypeRequiredValidation
Allow Notes (object) -- per-itemallow_notesObjectYesrequired|array
Enable Item Notesallow_notes.enabledBooleanYesrequired|boolean
Item Notes Placeholderallow_notes.placeholderStringConditionalnullable|string (becomes required|string when allow_notes.enabled is true)
Allow Order Notes (object) -- whole orderallow_order_notesObjectYesrequired|array
Enable Order Notesallow_order_notes.enabledBooleanYesrequired|boolean
Order Notes Placeholderallow_order_notes.placeholderStringConditionalnullable|string (becomes required|string when allow_order_notes.enabled is true)
FieldIDTypeRequiredValidation
Default Menu IDdefault_menu_idStringYesrequired|string (request-level); the service then enforces eligibility via isMenuEligible
Extra Menu IDsextra_menu_idsArrayNonullable|array, each entry required|string. Uniqueness, not-equal-to-default, eligibility, and schedule-overlap checks are enforced in the service (not in the request rules)

Business Rules

  • The default menu must pass eligibility validation via isMenuEligible from the MenuTrait, ensuring it is a valid, active menu that can serve the online ordering channel.
  • Extra menus cannot be the same as the default menu. The service checks $extraMenuId === $settings['default_menu_id'] and aborts with a 400 error and message "Extra menu cannot be the same as default menu" if they match.
  • Extra menus cannot be duplicated. The service uses a nested loop comparing $extraMenuId === $extraMenuId2 (skipping self-comparison) and aborts with "Extra menu cannot be duplicated" if a duplicate is found.
  • Extra menu schedules must not overlap with each other. The service calls isScheduleOverlap for each pair of extra menus, passing their schedules resolved via getSchedule($location). A descriptive error naming both conflicting menus is returned: "There is a schedule overlap between :extraMenuName and :extraMenu2Name".
  • Each extra menu's schedule is also checked for internal self-overlap via isSingleScheduleOverlap, returning "There is a time overlap in the schedule" if the menu's own time windows conflict.
  • Time-specific rules within order capacity must not have overlapping time ranges for the same day. The hasOverlappingTimeRanges validation (from SchedulingTrait) is applied as a custom validation closure on the time_specific_rules array.
  • StoreOnlineSettingsRequest::prepareForValidation() merges default values from Constants::getDefaultOnlineSettings() into order_capacity, collect_tips, allow_notes, and allow_order_notes before validation. Defaults include all seven days (Sunday through Saturday) for delivery and pickup, tips disabled with [10, 15, 20] options and calculate_tips set to the AFTER_TAXES enum value (string "After taxes"), item notes (allow_notes) disabled with a placeholder string, and order notes (allow_order_notes) disabled with the placeholder "Add a note for your whole order".
  • Guardrail A normalization runs in prepareForValidation() before the rules: an enabled-but-empty enable_limit_specific_categories is forced off, and an enabled-but-invalid enable_flexible_limit_order_per_time_slot (missing/invalid threshold or allow_up_to) is forced off -- both globally and per time-specific rule.
  • Conditional validation rules are dynamically computed: number_of_orders_per_time_slot becomes required only when limit_orders_per_time_slot is true; number_of_items_per_time_slot only when limit_items_per_time_slot is true; value_threshold_per_time_slot/allow_up_to only when enable_flexible_limit_order_per_time_slot is true (global, and re-evaluated per time-specific rule); limit_specific_categories only when both the toggle and a non-empty list are present; tip options/calculate_tips only when collect_tips.enabled is true; allow_notes.placeholder only when allow_notes.enabled is true; and allow_order_notes.placeholder only when allow_order_notes.enabled is true.

Customer Impact

  • Online Ordering: These settings configure the online ordering experience for the location: order capacity limits, available delivery/pickup days, tip options, and checkout notes. (Storefront/proxy enforcement of these settings is not verified here.)
  • Delivery: The delivery_available_days array configures which days delivery is offered. Defaults to all seven days (Sunday through Saturday).
  • Pickup: The pickup_available_days array configures which days pickup is available. Same shape as delivery availability. Defaults to all seven days.
  • Kitchen Operations: Order capacity limits are intended to bound the orders/items accepted per time slot; flexible capacity allows configured value-based exceptions, and category-specific limits target specific categories. (Actual order-time enforcement is handled by OrderCapacityService, not verified in detail here.)
  • Revenue: Tip collection settings (enabled, options, before/after-taxes calculation) feed the storefront checkout tip display. The precise checkout effect is not verified here.

FAQs

What are the default online settings for a new location?

New locations use defaults from Constants::getDefaultOnlineSettings(): all seven days available for both delivery and pickup; tips disabled with [10, 15, 20] option presets and calculate_tips = "After taxes"; item notes (allow_notes) disabled with a sample placeholder string; order notes (allow_order_notes) disabled with placeholder "Add a note for your whole order"; default_menu_id null; and extra_menu_ids empty. Order capacity also has defaults from $DEFAULT_ORDER_CAPACITY_SETTINGS: enabled false, time_slot_duration_min 15, all limit/flexible/category toggles false, numeric fields null, and empty limit_specific_categories / time_specific_rules.

What is the difference between "Allow Item Notes" and "Allow Order Notes"?

These are two independent checkout note settings, both configured in the same Notes section of Online Settings:

  • Allow Item Notes (allow_notes) lets customers add a note to an individual line item (e.g. "no onions" on a specific burger).
  • Allow Order Notes (allow_order_notes) lets customers add one note that applies to the whole order (e.g. "please ring the doorbell").

Each has its own enabled toggle and placeholder text, and both can be turned on independently. If a location was saved before allow_order_notes existed, the back office shows the old allow_notes value for both fields until the merchant configures order notes separately.

How do time-specific rules work?

Time-specific rules override the global order capacity settings during their defined time windows on their specified active days. For example, you can create a "Lunch Rush" rule for 11:00-14:00 with a higher order limit, and a "Dinner" rule for 17:00-21:00 with different limits. Each rule requires a unique name, a time_range (from/to), and at least one active_days entry (full day names like "Monday"); the per-rule number_of_orders_per_time_slot and number_of_items_per_time_slot are optional (nullable). Time ranges must not overlap for the same day (enforced by hasOverlappingTimeRanges). Each rule can also enable its own flexible capacity (then value_threshold_per_time_slot and allow_up_to become required) and define category-specific limits. The exact runtime enforcement of these rules is handled by OrderCapacityService and is not detailed here.

Can I have different online settings per location?

Yes. Online settings are stored on each Location document independently. Each location can have completely different order capacity limits, available days, tip settings, menu assignments, and all other configurations. Changes to one location do not affect others.

What is flexible capacity?

Flexible capacity allows exceeding the base order limit when the total order value meets a minimum threshold. For example, if the limit is 10 orders per slot and flexible capacity is enabled with a threshold of 50 and "allow up to" 5, then up to 5 additional orders can be accepted if each meets the value threshold. This is useful for not turning away high-value orders during busy periods.

How do extra menus with schedules work?

The default menu is always available during operating hours. Extra menus have their own schedules (configured on the menu itself, resolved per location) and are only shown during their configured time windows. This allows offering different menus for breakfast, lunch, and dinner on the online ordering channel. Schedules across extra menus must not overlap with each other.

What does the "calculate tips" option control?

The calculate_tips field determines how suggested tip amounts are calculated: either before taxes or after taxes. The CalculateTipsOptions enum defines the two valid values, whose stored string values are "After taxes" and "Before taxes" (the default is "After taxes"). NOTE: exactly how this affects the amounts shown at the storefront checkout is not verified here.

Troubleshooting

"Extra menu cannot be the same as default menu" error

You have added the same menu as both the default and an extra menu. Remove the duplicate from the extra menus list, or choose a different menu as the default. The service compares IDs directly.

"There is a schedule overlap between X and Y" error

Two extra menus have overlapping availability schedules at the current location. The error message names both conflicting menus. Review their schedules (configured on each menu, resolved per location via getSchedule) and adjust time windows so they do not conflict.

"There is a time overlap in the schedule" error for an extra menu

A single extra menu has overlapping time ranges within its own schedule at this location. The isSingleScheduleOverlap check found conflicting time windows. Review and fix the menu's schedule configuration.

Order capacity limits are not being enforced

Verify that limit_orders_per_time_slot or limit_items_per_time_slot is set to true and the corresponding numeric value is greater than zero. Check that time_slot_duration_min is configured with a sensible value. If using time-specific rules, verify the rule's active days and time range cover the period in question. The OrderCapacityService reads these settings from the location's online_settings at order time.

Time-specific rules validation fails

Ensure each time-specific rule has: a unique name (enforced by distinct validation), a time_range with valid from/to strings, and at least one valid active_days entry (full day names like "Monday", validated against Carbon::getDays()). The per-rule number_of_orders_per_time_slot and number_of_items_per_time_slot are optional, but when present must be > 0. Also ensure no two rules' time ranges overlap on the same day (hasOverlappingTimeRanges). If flexible capacity is enabled per rule, value_threshold_per_time_slot and allow_up_to become required and must be positive.

Saved settings do not appear on the online ordering frontend

After saving, a ReloadMenu event is dispatched and both timeslot caches for the location are cleared, so order-capacity changes should be reflected on the very next storefront request -- there is no propagation delay to wait out for settings saved on this page. If changes still do not appear, verify the menu cache has been refreshed for the location. Check that the default menu is correctly assigned and that its display groups have Online Ordering in their visibility arrays.

Note that this does not cover the scheduling settings on the separate Online Ordering page (blocked times, custom availability, days-in-advance, last pickup slot). That save path does not clear the timeslot caches, so those changes can take up to 60 seconds to appear. (Verified: app/Services/BackOffice/OnlineOrderingService.php::update() lines 223-280 has no cache-clear call; contrast OnlineSettingsService.php lines 106-113.)

Technical Details

Default Settings Initialization

The Constants::getDefaultOnlineSettings() method returns the default online settings structure:

  • order_capacity: the $DEFAULT_ORDER_CAPACITY_SETTINGS static property -- {enabled: false, time_slot_duration_min: 15, limit_orders_per_time_slot: false, number_of_orders_per_time_slot: null, limit_items_per_time_slot: false, number_of_items_per_time_slot: null, enable_flexible_limit_order_per_time_slot: false, value_threshold_per_time_slot: null, allow_up_to: null, enable_limit_specific_categories: false, limit_specific_categories: [], time_specific_rules: []}
  • delivery_available_days: All seven days (Sunday through Saturday)
  • pickup_available_days: All seven days (Sunday through Saturday)
  • collect_tips: {enabled: false, options: [10, 15, 20], calculate_tips: "After taxes"} (the value of CalculateTipsOptions::AFTER_TAXES)
  • allow_notes (per-item notes): {enabled: false, placeholder: "E.g. \"For Oliver\" or \"I'm allergic to mushrooms"}
  • allow_order_notes (whole-order note): {enabled: false, placeholder: "Add a note for your whole order"}
  • default_menu_id: null
  • extra_menu_ids: empty array

StoreOnlineSettingsRequest::prepareForValidation() merges these defaults into order_capacity, collect_tips, allow_notes, and allow_order_notes, defaults delivery_available_days/pickup_available_days to [] if absent, and applies Guardrail A normalization (see Update Online Settings). Note: it does NOT merge a default_menu_id/extra_menu_ids default -- default_menu_id is required and supplied by the caller.

allow_order_notes backward compatibility: allow_order_notes was split out of allow_notes as its own setting. Both the backend (ShowOnlineSettingsResource, StoreOnlineSettingsRequest::prepareForValidation()) and the back-office store (onlineSettings.ts) fall back to the value of allow_notes whenever allow_order_notes is missing, so locations saved before the split continue to behave as before (item notes and order notes both reflect the old single allow_notes value) until the merchant explicitly configures the two settings independently.

Backoffice Store Module

The Vue store module (src/store/modules/onlineSettings.ts) transforms the API response (SET_ONLINE_SETTINGS_FORM) into a form-friendly structure:

  • collect_tips.options[0..2] -> tip_percentage_1, tip_percentage_2, tip_percentage_3
  • collect_tips.enabled -> is_collect_tips
  • collect_tips.calculate_tips -> calculate_tips_type (only kept if it is one of the known InHouseSettingsSectionCalculateTipsType values, else null)
  • allow_notes.enabled -> is_allow_notes; allow_notes.placeholder -> notes
  • allow_order_notes.enabled -> is_allow_order_notes; allow_order_notes.placeholder -> order_notes. If the API response has no allow_order_notes, the store first falls back to allow_notes before reading enabled/placeholder (backward compatibility, see above).
  • default_menu + extra_menus -> a unified menus array, each entry {is_default: boolean, data: menu}
  • order_capacity is spread over the default form shape, then locally normalized (category toggle off if no entries; flexible toggle off if no threshold/allow_up_to)

The saveOnlineSettingsForm action reverses these: it rebuilds collect_tips ({enabled, options: [tip_percentage_1, tip_percentage_2, tip_percentage_3], calculate_tips: calculate_tips_type}), allow_notes ({enabled, placeholder: notes}), allow_order_notes ({enabled: is_allow_order_notes, placeholder: order_notes}), default_menu_id/extra_menu_ids from menus, plus order_capacity, delivery_available_days, and pickup_available_days, and PUTs them to the endpoint.

Schedule Overlap Detection

Two traits provide the validation methods used during online settings update:

  • MenuTrait::isMenuEligible($menuId, ...) and MenuTrait::getEligibleMenu($id, locationId, ...): validate that a menu exists and is eligible (and resolve the Menu for an extra menu).
  • SchedulingTrait::isSingleScheduleOverlap($schedule): checks a single schedule for internal time-range conflicts (and aborts 400 on an invalid time range where from >= to).
  • SchedulingTrait::isScheduleOverlap($schedule1, $schedule2): compares two schedules for cross-schedule time-range conflicts (an all-day availability always overlaps).
  • SchedulingTrait::hasOverlappingTimeRanges($rules): used as a closure on order_capacity.time_specific_rules to reject rules whose ranges overlap on the same day.

Assistant Guidance

When users ask about online settings, first determine which aspect they need help with: order capacity, delivery/pickup days, tips, notes, or menu assignment. For order capacity questions, explain the three-tier hierarchy: global settings apply by default, time-specific rules override during their configured periods, and flexible capacity provides value-based exceptions. Emphasize that online settings are per-location and changes only affect the selected location. If users report issues with menu display in online ordering, check both the online settings menu assignment and the individual display group visibility settings. For tip configuration, explain the difference between calculating tips before versus after taxes (calculate_tips enum values "Before taxes" / "After taxes") and that up to three percentage options are configured; how they render at customer checkout is a storefront concern not detailed here. For notes questions, always clarify which of the two independent settings the user means: "Allow Item Notes" (allow_notes, a note per line item) versus "Allow Order Notes" (allow_order_notes, one note for the whole order) -- they have separate toggles and placeholders. If a merchant reports their order notes toggle unexpectedly matches their item notes value, mention the backward-compatibility fallback: locations saved before allow_order_notes existed show the allow_notes value for both until the merchant saves the order notes setting explicitly.

Relations

Depends On

  • Locations: Online settings are stored on the Location document's online_settings field. A valid location must exist.
  • Menus: Default and extra menus must exist and pass eligibility checks. Menu schedules are resolved per location for overlap validation.
  • Categories: Category-specific order limits reference category IDs that must exist in the settings collection.
  • Constants: Default values are sourced from Constants::getDefaultOnlineSettings().
  • SchedulingTrait: Provides isScheduleOverlap, isSingleScheduleOverlap, and hasOverlappingTimeRanges validation methods.
  • MenuTrait: Provides isMenuEligible and getEligibleMenu methods for menu validation.

Affects

  • Online Ordering Channel: Directly controls order capacity, available days, tip options, note collection, and menu display for the customer-facing online ordering experience.
  • Order Processing: Order capacity limits (managed by OrderCapacityService) determine whether new orders are accepted or rejected during specific time slots.
  • Menu Cache: Updating online settings dispatches a ReloadMenu event, triggering a refresh of cached menus for the affected location.
  • Timeslot Availability Cache: Updating online settings also clears the location's available-timeslots cache (60-second TTL) and slot-stats cache (30-second TTL), so changed capacity limits apply to the very next storefront availability request. (Verified: app/Services/BackOffice/OnlineSettingsService.php lines 106-113; TTLs at app/Services/OrderCapacity/OrderCapacityService.php lines 32-35 and 1789-1800.)
  • Kitchen Operations: Order and item limits per time slot directly impact kitchen workload, order throughput, and preparation scheduling.
  • Transaction Processing: Tip settings (enabled/options/calculate_tips) feed downstream tip handling at order time. (The exact runtime tip calculation/application is outside this settings page and not verified here.)
  • Revenue: Tip collection settings and calculation method affect the tip revenue collected per order (storefront behavior not verified here).