Skip to content

Display Groups

Overview

Display Groups are the grouping unit inside a menu. A menu contains an ordered list of display_group_ids, and each display group contains an ordered list of item_ids (regular items and variant groups). Display groups define the sections that customers see when browsing a menu on the kiosk, online ordering, and table QR ordering.

Display groups are managed from within the Menu Builder, not from a standalone top-level page. You open a menu in the Menu Builder, then add, edit, reorder, and fill display groups with items.

Display groups are stored in the menus MongoDB collection with model type display_group and use soft deletion (SOFT_DELETE = true). Each display group holds multilingual details, an optional content image (content_id), an ordered item_ids array, a menu_ids array linking it to its menu(s), a location_id, a pos_name for POS integration, and external_ids/external_data for third-party platform mapping. There is also an optional published_item_ids snapshot used for draft/discard support.

NOTE

Channel visibility (Kiosk, POS, Online Ordering, etc.) is configured on the menu, not on the display group. The display group itself has no visibility field in the backend. Items are filtered onto a channel by the menu's visibility plus each item's own platforms setting and status.

Purpose

This page lets you create, edit, reorder, and fill display groups that organize a menu's items into the sections customers see on kiosks, online ordering, and table QR ordering. Display groups are managed inside the Menu Builder after a menu is selected.

Key Concepts

  • Display Group: A named container of menu items that defines a section within a menu. Fields verified on the model: multilingual details (a nested structure with a default key holding name and description), content_id (optional image), an ordered item_ids array, a menu_ids array, location_id, pos_name (for POS integration), external_ids/external_data (for third-party systems), and an optional published_item_ids draft snapshot. Stored in the menus collection with SOFT_DELETE = true. (Verified: app/RawModels/DisplayGroup.php.)
  • Menu Association: Display groups are linked to menus via a bidirectional relationship. The display group stores menu_ids; the menu stores display_group_ids. When a display group is created, syncRelations adds its ID to the target menu's display_group_ids array and calls syncMenu on the repository to keep the menu-to-display-group mapping in sync. (Verified: DisplayGroupService::syncRelations, DisplayGroupRepository::syncMenu.)
  • Multi-Group Membership: The item-to-display-group relation is many-to-many. One item can sit in several display groups at once, including several groups within the same menu — "Add to group" appends without removing, while "Move" relocates. (Verified: MenuOrchestrator::addItemToGroups; upvendo-backoffice src/utils/menuItemGroups.ts.)
  • Item Ordering: Items within a display group are displayed in the order of the item_ids array. This order is preserved when serializing for kiosk (kioskSerialize) and online ordering (onlineOrderingSerialize), via the forMenu path of getItems. The array can contain both regular item IDs and variant group IDs, resolved at render time. (Verified: DisplayGroup::getItems, kioskSerialize, onlineOrderingSerialize.)
  • Label Consistency: All items within a single display group must share the same label. validateItems compares each item's label against the first item's label and throws "Group {name}: All items must have the same label" if they differ. (Verified: DisplayGroupService::validateItems.)
  • Draft / Publish for Items: Display group item changes can be saved as a draft (no ReloadMenu event) or published (clears the draft snapshot, recomputes the menu's published hash, and dispatches ReloadMenu). A published_item_ids snapshot is taken before the first draft edit so the change can be discarded back to the published state. (Verified: MenuOrchestrator::saveDraftDisplayGroupItems, saveDisplayGroupItems, discardDraftDisplayGroupItems; DisplayGroupRepository::savePublishedItems/clearPublishedSnapshot/discardDraft.)

Route

Display groups do not have a dedicated back-office page. They are created and managed inside the Menu Builder at /menus/menu-builder (the menu builder UI), after selecting a menu.

  • Back-office Page: /menus/menu-builder (Menu Builder). The page file is src/pages/menus/menu-builder/index.vue; there is no /menus/display-groups page.
  • Display Group CRUD Controller: app/Http/Controllers/Api/DisplayGroupController.php (injects DisplayGroupService directly — there is no orchestrator layer for display groups)
  • Display Group Service: app/Services/BackOffice/DisplayGroupService.php
  • Model: app/RawModels/DisplayGroup.php
  • Repository: app/Repositories/DisplayGroupRepository.php
  • Request Validators: app/Http/Requests/BackOffice/DisplayGroup/StoreDisplayGroupRequest.php, app/Http/Requests/BackOffice/DisplayGroup/UpdateDisplayGroupRequest.php, app/Http/Requests/BackOffice/Menu/SaveDisplayGroupItemsRequest.php, app/Http/Requests/BackOffice/Menu/ReorderDisplayGroupsRequest.php
  • Resources: app/Http/Resources/BackOffice/Menu/DisplayGroupResource.php, app/Http/Resources/BackOffice/Menu/ShowDisplayGroupResource.php, app/Http/Resources/BackOffice/Menu/ShowDisplayGroupItemsResource.php
  • Item-assignment endpoints (handled by MenuController, not DisplayGroupController): app/Http/Controllers/Api/MenuController.php + app/Services/Orchestrators/BackOffice/MenuOrchestrator.php
  • Vue Components: src/views/menus/dialogs/DisplayGroupDialog.vue, src/views/menus/forms/DisplayGroupForm.vue, src/views/menus/components/MenuDisplayGroups.vue, src/components/dialogs/DisplayGroupListDialog.vue
  • Store Module: src/store/modules/displayGroup.ts

Actions

List Display Groups

Retrieve all display groups for the current vendor. The service first fetches all menus for the vendor, then queries display groups whose menu_ids intersect those menu IDs, merged with the validated LocationScopedRequest filters. Returns a collection of DisplayGroupResource objects (id, name, description, image). (Verified: DisplayGroupService::all, DisplayGroupResource.)

  • Endpoint: GET /api/back-office/display-groups
  • Response: JSON array of DisplayGroupResource (id, name, description, image)

Create Display Group

Create a new display group. The flow:

  1. Validates the request via StoreDisplayGroupRequest.
  2. Checks name uniqueness via isFieldValueUsed on DisplayGroupRepository (scoped to the menu only when editing an existing group — on create the moreFilter is empty).
  3. Wraps the submitted details (name/description) under a details.default structure for multi-language support.
  4. If an image is provided, uploads it via handleMediaUpload under the display-groups directory and stores the resulting content_id; otherwise content_id is set to null.
  5. Saves the display group document via the repository.
  6. Calls syncRelations, which validates item labels, syncs item_ids, and adds the display group ID to the target menu's display_group_ids.

(Verified: DisplayGroupService::store, genDisplayGroup, syncRelations.) The store flow does not dispatch a guided-setup event.

  • Endpoint: POST /api/back-office/display-groups
  • Response: JSON DisplayGroupResource of the created display group

View Display Group Details

Retrieve a specific display group by ID. Returns a ShowDisplayGroupResource with id, details (name/description), all_details (full multilingual structure), pos_name, image, location_id, label, item_ids, uneditable_fields, is_mounted, and has_unpublished_changes. The items key is returned empty here; the expanded item list is served by the separate display-group-items endpoint. (Verified: ShowDisplayGroupResource.)

  • Endpoint: GET /api/back-office/display-groups/{id}
  • Response: JSON ShowDisplayGroupResource

Update Display Group

Modify a display group's name, description, image, or POS name. The flow:

  1. Validates via UpdateDisplayGroupRequest.
  2. Retrieves the existing display group.
  3. Checks name uniqueness against other display groups in the same menu (scoped by the existing group's first menu_id, excluding the current group by ID).
  4. Merges the new details under the default key, preserving other language entries.
  5. If a new image is provided, uploads it and updates content_id; otherwise sets content_id to null.
  6. Saves the document and dispatches a ReloadMenu event for the display group's location.

(Verified: DisplayGroupService::update, genDisplayGroup.)

  • Endpoint: PUT /api/back-office/display-groups/{id}
  • Response: JSON DisplayGroupResource of the updated display group

Delete Display Group

Soft-delete a display group and dispatch a ReloadMenu event for its location. The deleteByMenuId method bulk-deletes all display groups attached to a given menu ID and dispatches ReloadMenu per affected location. (Verified: DisplayGroupService::delete, deleteByMenuId.)

NOTE

There are two delete routes. DELETE /api/back-office/display-groups/{id} (the standalone controller) and DELETE /api/back-office/menus/display-groups/{id} (handled by MenuController::deleteDisplayGroup, which delegates to the same service). The Menu Builder UI uses the menus-prefixed route. (Verified: routes/api/backoffice/display-groups.php, routes/api/backoffice/menus.php.)

  • Endpoint: DELETE /api/back-office/display-groups/{id} (or DELETE /api/back-office/menus/display-groups/{id})
  • Response: Success response

Assign / Reorder Items Within a Display Group

Item assignment and ordering inside a display group are handled by menu-scoped endpoints on MenuController, not by the display group CRUD controller:

  • View items: GET /api/back-office/menus/display-group-items/{id} returns the expanded, ordered items for a display group (ShowDisplayGroupItemsResource).
  • Publish items: POST /api/back-office/menus/display-group-items/{id} saves the items (via syncRelations), clears the draft snapshot, recomputes the menu's published hash, and dispatches ReloadMenu.
  • Save draft items: POST /api/back-office/menus/display-group-items/{id}/draft snapshots the current item_ids (if not already snapshotted), saves the new items, marks the menu as having a draft, and does not dispatch ReloadMenu.
  • Discard draft items: POST /api/back-office/menus/display-group-items/{id}/discard restores item_ids from the published snapshot and clears the menu's draft flag when no other group is still dirty.
  • Move item to another group: POST /api/back-office/menus/move-item-to-other-group/{id} removes an item from its previous group within the menu and adds it to the target group.
  • Add item to further groups: POST /api/back-office/menus/add-item-to-groups/{itemId} appends one item to one or more display groups without removing it from any group it already belongs to. Payload is { menu_id, item_id (from the route), display_group_ids }; display_group_ids is required|array|min:1 and every entry must be an existing document with the display_group model type. The append is computed server-side from a fresh getItemIds() read under the menu save lock, is idempotent per group, and dispatches ReloadMenu once for the affected location. (Verified: MenuOrchestrator::addItemToGroups lines 274-315, AddItemToGroupsRequest, routes/api/backoffice/menus.php line 18, permission edit-menus.)
  • Reorder display groups within a menu: POST /api/back-office/menus/{id}/display-groups/reorder reorders the menu's display_group_ids.

(Verified: routes/api/backoffice/menus.php, MenuController, MenuOrchestrator.) The item-save payload uses items (array of {id}) plus a menu_id; reorder uses a display_group_ids array. (Verified: SaveDisplayGroupItemsRequest, ReorderDisplayGroupsRequest.)

Fields

FieldIDTypeRequiredValidation
Namedetails.nameStringYesrequired|string -- must be unique within the same menu (see Business Rules)
Descriptiondetails.descriptionStringNonullable|string
POS Namepos_nameStringNonullable|string -- unique per connection, scoped by location_id, via UniqueInConnectionWithModel
Image Sourceimage.sourceEnumNonullable, one of ImageSources: direct_upload, existing, url, photo_studio, external, square, mpluskassa, shopcaisse, suggestions_api, ai_photo
Image Fileimage.fileFileNonullable|max:10240 (10 MB maximum)
Location IDlocation_idStringYesrequired|string|exists:locations,_id
Menu IDmenu_idStringYes (create only)required|string -- must exist in the menus collection; not present on the update validator

(Verified: StoreDisplayGroupRequest, UpdateDisplayGroupRequest, app/Enums/ImageSources.php.)

NOTE

There is no visibility field in the create/update validators, no visibility property on the DisplayGroup model, and no visibility in the display group resources. The Menu Builder form holds a visibility value in client state, but the Visibility component is imported and never rendered, and the backend ignores any visibility sent. Channel visibility is a menu field (validated against ChannelOptions in StoreMenuRequest). The label is derived from a group's items, not entered directly. (Verified: request validators, DisplayGroup.php, DisplayGroupForm.vue, StoreMenuRequest.)

Business Rules

  • Display group names must be unique within the same menu. isFieldValueUsed queries DisplayGroupRepository with the name value; on update the uniqueness is scoped by the existing group's first menu_id via moreFilter, while on create the filter is empty. A 400 "Name should be unique" is returned for duplicates. (Verified: DisplayGroupService::genDisplayGroup.)
  • All items within a display group must share the same label. validateItems throws "Group {name}: All items must have the same label" on a mismatch. (Verified: DisplayGroupService::validateItems.)
  • Display groups use soft delete (SOFT_DELETE = true); deleted records are flagged rather than physically removed from the menus collection. (Verified: DisplayGroup::SOFT_DELETE, DisplayGroupRepository constructor.)
  • Images are uploaded to the display-groups directory via ContentTrait::handleMediaUpload. If no image is provided on update, content_id is set to null. (Verified: DisplayGroupService::genDisplayGroup, ContentTrait::handleMediaUpload.)
  • Deleting a menu deletes its display groups via deleteByMenuId, which iterates display groups matching the menu_ids filter, deletes each, and dispatches ReloadMenu per affected location. (Verified: DisplayGroupService::deleteByMenuId.)
  • syncRelations handles both item assignment (syncItems, which also syncs display_group_ids onto the items and variant groups) and menu linkage (adding the display group ID to the menu's display_group_ids, plus syncMenu). (Verified: DisplayGroupService::syncRelations, DisplayGroupRepository::syncItems/syncMenu.)
  • Display group content images default to the kioskgroup Cloudflare Images variant when resolving URLs via content/getContentUrl. (Verified: DisplayGroup::content, getContentUrl.)
  • At render time, items are filtered by effective status (Active/Unavailable) and, per channel, by the item's platforms list, so an item in item_ids will not appear on a channel if it is hidden/inactive or lacks that platform. (Verified: DisplayGroup::genDisplayGroupItems, genKioskDisplayGroupItems.)

Customer Impact

  • Kiosk: Display groups define the sections customers browse on the kiosk. Items are rendered in item_ids order via kioskSerialize / genKioskDisplayGroupItems, filtered to Active/Unavailable status and to items whose platforms include Kiosk. (Verified: DisplayGroup::kioskSerialize.)
  • Online Ordering & QR Ordering: Display groups structure the menu into sections via onlineOrderingSerialize. Each group renders with its id, multilingual details, image_url, and ordered items, filtered by the requested channel (visibility argument, e.g. Online Ordering or Table Qr Ordering) and Active/Unavailable status. (Verified: DisplayGroup::onlineOrderingSerialize, genDisplayGroupItems.)
  • Channel selection happens at the menu level: which channels a menu (and therefore its display groups) appears on is governed by the menu's visibility. A display group appears wherever its parent menu is published for that channel; individual items are then filtered by their own platforms. (Verified: menu visibility in StoreMenuRequest; item filtering in the serialize methods.)

FAQs

What is the difference between a display group and a category?

Categories are a backend classification system for items (used for organization, reporting, and related rules). Display groups are the in-menu grouping unit that defines the sections customers see. An item is added to a display group via the menu builder; the same item can appear in several display groups, both across different menus and within a single menu. (Verified: MenuOrchestrator::addItemToGroups; src/utils/menuItemGroups.ts.)

Can an item appear in multiple display groups?

Yes — including within the same menu. The item-to-display-group relation is many-to-many in storage, and the Menu Builder exposes two distinct row actions on an item:

  • Add to group opens the Add to Groups dialog, which posts POST /api/back-office/menus/add-item-to-groups/{itemId} with { menu_id, display_group_ids }. The item is appended to each target group and stays in the group it is already in. Adding an item a group already holds is a no-op, so a double-submit cannot duplicate it. (Verified: MenuOrchestrator::addItemToGroups, upvendo-backend app/Services/Orchestrators/BackOffice/MenuOrchestrator.php lines 274-315; route routes/api/backoffice/menus.php line 18; AddItemToGroupsRequest.)
  • Move still posts POST /api/back-office/menus/move-item-to-other-group/{id}, which removes the item from its previous group before adding it to the target.

Both actions are shown only when the menu has more than one display group. (Verified: upvendo-backoffice src/views/menus/components/MenuItems.vue lines 132-142; src/views/menus/dialogs/AddToGroupsDialog.vue lines 31-42; store action src/store/modules/menu.ts lines 519-530; src/utils/menuItemGroups.ts lines 1-6 -- "An item may belong to several display groups.")

The Add to Groups dialog only offers groups the item is not already in, since re-adding would do nothing. (Verified: AddToGroupsDialog.vue lines 25-29.)

The Add Items picker hides items already used elsewhere in the same menu. An item that already belongs to another display group of the same menu is not shown in the picker's main list — it is moved to a second Hidden tab, and each hidden row carries an "In <group names>" chip naming the other groups using it. The Browse / Hidden tab strip only renders when the Hidden bucket is non-empty. Items already in the group you are editing — whether saved or just staged in the Selected panel — appear in neither tab. If the Hidden tab empties (you tick every hidden item, or switch category) the picker falls back to Browse rather than stranding you on an empty list, and the tab resets to Browse each time the dialog reopens. (Verified: src/utils/menuItemGroups.ts lines 14-30, 39-51, 60-69; AddItemsDialog.vue lines 187-209, 630-646.)

How do I control the order of items within a display group?

Items render in the order of the item_ids array. In the Menu Builder you reorder items by dragging them; saving sends the new order to the display-group-items endpoint, which updates item_ids via syncRelations. The backend preserves this order when serializing for kiosk and online ordering. (Verified: DisplayGroupService::syncRelations, DisplayGroup::getItems forMenu path.)

There is also a sort control in the group's card header offering Off (manual, the default), Alphabetical (A–Z by item name) and PLU. It appears only when the group holds more than one item and you have edit menus with the menu's item field editable. Picking a sort rewrites the item order in memory and flows through the same dirty-check / save / publish path as a drag. While any mode other than Off is active the drag handles are hidden and drag reordering is disabled, and the list re-sorts automatically as you add items. PLU sort compares numerically and pushes items with no PLU to the bottom. The mode resets to Off when you switch display group. (Verified: MenuItems.vue lines 202-205, 219-228, 233-248, 250-257.)

How do drafts and publishing work for display group items?

You can save item changes as a draft (no customer-facing refresh) or publish them. Saving a draft snapshots the previously published item_ids into published_item_ids and marks the menu as having a draft; publishing clears that snapshot, recomputes the menu's published hash, and dispatches ReloadMenu. Discarding restores item_ids from the snapshot. (Verified: MenuOrchestrator::saveDraftDisplayGroupItems/saveDisplayGroupItems/discardDraftDisplayGroupItems.)

Why can't I find an item in the Add Items list?

Because it is already in another display group of this menu. Switch to the Hidden tab in the Add Items dialog — it lists exactly those items, each chipped with the group it currently sits in. If the item is already in the group you are editing, it will not appear in either tab.

Why can't I set channel visibility on a display group?

Channel visibility is configured on the menu, not the display group. The display group has no visibility field in the backend. Whether a group's items reach a given channel depends on the menu's visibility plus each item's own platforms setting and status. (Verified: request validators and model have no visibility; StoreMenuRequest validates menu visibility.)

What happens to items when I delete a display group?

Deleting a display group does not delete the items within it. The items remain in the system and can be assigned to other display groups. Only the grouping container is removed (soft-deleted). (Verified: DisplayGroupService::delete.)

What are the supported image sources for display groups?

The ImageSources enum allows: direct_upload, existing, url, photo_studio, external, square, mpluskassa, shopcaisse, suggestions_api, and ai_photo. (Verified: app/Enums/ImageSources.php.)

Can I translate display group names?

Yes. Names and descriptions are stored in a multilingual details structure. The default key holds the primary language; additional language keys are managed through the translations system, which references display groups via the TranslationEntities::DisplayGroup enum case (value display_groups). (Verified: DisplayGroup::getDetails, app/Enums/TranslationEntities.php.)

Troubleshooting

Display group does not appear on the kiosk or online ordering

Display group visibility follows its parent menu. Verify the parent menu is published and visible on the target channel, and that the items inside the group are set to Active/Unavailable and have the matching channel in their platforms list. There is no per-display-group visibility toggle to check. (Verified: serialize methods, menu visibility.)

"Name should be unique" error when creating or editing a display group

A display group with the same name already exists within the same menu. Use a different name. The uniqueness check is scoped to the menu when editing. (Verified: DisplayGroupService::genDisplayGroup.)

"All items must have the same label" error

This occurs when items with different labels (e.g. mixing regular items with externally imported items) are assigned to the same display group. Ensure all items in the group share the same label. The error message includes the display group name. (Verified: DisplayGroupService::validateItems.)

Display group image is not showing

Verify the image uploaded successfully (the content_id is set on the document) and that the referenced Content document exists. The image file must be 10 MB or less per the max:10240 validation rule. (Verified: StoreDisplayGroupRequest/UpdateDisplayGroupRequest.)

Changes to a display group are not reflected on kiosk/ordering

Publishing display group items and editing a display group dispatch a ReloadMenu event for the location; cached menus may take a moment to refresh. Note that saving item changes as a draft deliberately does not dispatch ReloadMenu — publish the draft to push changes to customers. (Verified: MenuOrchestrator::saveDraftDisplayGroupItems vs saveDisplayGroupItems, DisplayGroupService::update.)

Items appear in the wrong order

Item order is the sequence of the item_ids array. Reorder items in the Menu Builder and save; this updates item_ids through syncRelations. (Verified: DisplayGroupService::syncRelations.)

Technical Details

Serialization for Customer Channels

  • Online Ordering / QR Ordering (onlineOrderingSerialize): Returns id, details, image_url, and items. Items come from genDisplayGroupItems, filtered by the requested channel (visibility argument) against each item's platforms and by status (Active/Unavailable). Each item is serialized via its own onlineOrderingSerialize. (Verified: DisplayGroup::onlineOrderingSerialize.)
  • Kiosk (kioskSerialize): Returns id, details, image_url, and items. Items are filtered by platforms containing Kiosk and by status, then serialized via each item's kioskSerialize. The image URL uses the kioskgroup variant. (Verified: DisplayGroup::kioskSerialize, genKioskDisplayGroupItems.)
  • Both serialize paths support an optional MenuLoadContext; when present, items come from getPublishedOrCurrentItemIds (the published snapshot when a draft is pending) and category visibility cascades onto item status. (Verified: genDisplayGroupItems/genKioskDisplayGroupItems $ctx branch.)

Variant Group Handling

The item_ids array can contain both regular item IDs and variant group IDs. getItems queries both ItemRepository and VariantGroupRepository. When withVariantGroup is true, variant groups are returned as-is; when false, they are expanded into their member items. For menu serialization (forMenu: true), variant groups are expanded into individual items in item_ids order. (Verified: DisplayGroup::getItems.)

External Integration Data

Display groups carry external_ids and external_data for third-party platform mapping. toSnapshot (marked TODO for offers) includes these fields only for display groups whose label is not the regular label. (Verified: DisplayGroup::toSnapshot, getExternalIds/getExternalData.)

Multi-Language Support

details is a nested structure with a default key containing the primary-language name and description. On update, new details are merged under the default key while preserving other language entries. The translation system references display groups via TranslationEntities::DisplayGroup (value display_groups). (Verified: DisplayGroupService::genDisplayGroup, DisplayGroup::getNameDefaultLanguage, TranslationEntities.)

Import and Sync Mechanisms

  • Kassanet Import: ImportKassanetDisplayGroupsJob imports display groups from the Kassanet POS, creating display group documents with external ID/data mapping. (Verified: app/Jobs/ImportKassanetDisplayGroupsJob.php.)
  • Third-Party Integrations: AbstractThirdPartyIntegrationService and its implementations (UberEatsService, DeliverooService, ShopifyIntegrationService, SquareUpService) handle syncing between Upvendo's menu/display-group structure and each platform's category/section format. (Verified: those service files exist under app/Services/BackOffice.)

Assistant Guidance

When users ask about display groups, explain that they are the grouping unit inside a menu: a menu contains display groups, and each display group contains items. They are managed in the Menu Builder (/menus/menu-builder) after selecting a menu — there is no standalone display-groups page. Distinguish display groups from categories: display groups organize a menu into customer-facing sections, while categories are a backend classification. If a user wants items to show on specific channels only, do not point them at a display-group visibility setting (it does not exist) — direct them to the menu's channel visibility and the item's platform settings. When troubleshooting missing items on kiosk or online ordering, check three things: (1) the parent menu is published and visible on the target channel, (2) the item's platforms include that channel, and (3) the item's status is Active/Unavailable. For multi-language setups, explain that names/descriptions live under details.default and translations are added per language. If item changes aren't reaching customers, check whether they were saved as a draft (which does not trigger a reload) rather than published.

Relations

Depends On

  • Menus: A display group must belong to a menu. The menu must exist before a display group can be created within it, enforced by the menu_id validation rule. (Verified: StoreDisplayGroupRequest.)
  • Items: Display groups reference items (and variant groups) via item_ids; items must exist before being added. (Verified: SaveDisplayGroupItemsRequest, DisplayGroup::getItems.)
  • Locations: Display groups are scoped to a location via location_id, which must reference a valid location document. (Verified: request validators.)
  • Content Service: Images are stored as Content entities (Cloudflare Images), referenced by content_id. (Verified: ContentTrait, DisplayGroup::content.)
  • Variant Groups: Display groups can contain variant group IDs in item_ids, resolved to individual items at render time. (Verified: DisplayGroup::getItems.)

Affects

  • Kiosk App: Display groups define the section structure and item grouping on kiosk screens via kioskSerialize. (Verified: DisplayGroup::kioskSerialize.)
  • Online & QR Ordering: Display groups structure the menu layout for customers via onlineOrderingSerialize. (Verified: DisplayGroup::onlineOrderingSerialize.)
  • Menu Cache: Editing a display group, publishing its items, or deleting it dispatches ReloadMenu to refresh cached menus for the location. Saving items as a draft does not. (Verified: DisplayGroupService::update/delete, MenuOrchestrator::saveDisplayGroupItems.)
  • Third-Party Integrations: Display group external IDs/data feed Uber Eats, Deliveroo, Shopify, and Square syncs via the integration services. (Verified: integration service files; DisplayGroup::getExternalIds/getExternalData.)
  • Translations: Display group names and descriptions can be translated, keyed by TranslationEntities::DisplayGroup. (Verified: TranslationEntities.)