Skip to content

Modifiers & Options

Overview

Modifiers allow customers to customize menu items. They are organized into Modifier Groups (e.g., "Size", "Toppings") containing individual options (e.g., "Small", "Medium", "Large").

Key Purpose: Enable product customization with extras, sizes, or removals.

Purpose

This page lets you create and manage modifier groups and their individual options so customers can customize menu items with extras, sizes, or removals.

Key Concepts

  • Modifier Group: A named collection of related options (e.g., "Size", "Toppings") with a pricing mode, channel visibility, and settings for Optional/Required, allow-multiple-selection, allow-same-modifier-more-than-once, and a maximum number selectable.
  • Modifier (Option): An individual choice within a group with a customer-facing name, an optional kitchen_name, an absolute price (Money — not a +/- adjustment), and an optional image.
  • Pricing Mode: One of No charge (all modifiers free), Individual (each modifier has its own price, stored in a modifier_prices map on the group), or Group (all modifiers share a single group price). A tax rate can be set for Individual and Group pricing.
  • Reordering: Modifier groups have an order field used for display sequence; it is set to the current count of groups on creation. (This field is marked @deprecated in the model.)

Actions

Create Modifier Group

Add a new modifier group with a name (and optional description and POS name), pricing mode, tax rate (if not "No charge"), channel visibility, settings, and a list of modifier options. Each option is created and synced, and the group is synced to Square if the integration is active.

Edit Modifier Group

Update the group's name, settings, pricing, visibility, or its list of modifier options. Existing modifiers are updated in place; new ones are created. A ReloadMenu event fires so ordering channels reflect changes immediately.

Delete Modifier Group

Remove a modifier group permanently. The deletion syncs to Square if integrated and triggers a menu reload event.

Reorder Modifier Groups

Change the display order of modifier groups by submitting a new ordering array. Each group's order field is updated accordingly.

Location

  • Backoffice Route: /menus/modifiers (Navigation: Menus → Modifier Groups)
  • Backend Controller: app/Http/Controllers/Api/ModifierGroupController.php
  • Vue Component: src/views/menus/modifiers/ModifierGroups.vue (mounted by src/pages/menus/modifiers/index.vue)

Concepts

Modifier Group

A collection of related options. Examples:

  • "Size" (Small, Medium, Large)
  • "Toppings" (Cheese, Bacon, Mushrooms)
  • "Sauce" (Ketchup, Mayo, BBQ)

Modifier Option

An individual choice within a group. Examples:

  • "Large" (€2.00)
  • "Extra Cheese" (€1.50)
  • "No Onions" (€0.00)

Modifier Group Fields

Group Name

PropertyValue
Field IDdetails.name
LabelModifier Group Name
TypeText
RequiredYes
Validationrequired, string

Description: Name of the modifier group, used internally and shown to customers (kiosk, online ordering, etc.) unless a POS Name overrides it.

Examples:

  • "Size"
  • "Choose Your Toppings"
  • "Select a Sauce"

Description

PropertyValue
Field IDdetails.description
LabelDescription
TypeText
RequiredNo
Validationnullable, string (UI enforces max 300 characters)

Description: Optional descriptive text for the modifier group.


POS Name

PropertyValue
Field IDpos_name
LabelPOS Name
TypeText
RequiredNo
Validationnullable, string, unique per location

Description: Optional internal name (e.g., a shorter label for the kitchen printer). Must be unique within the location. If left blank, the regular name is used everywhere.


Pricing

PropertyValue
Field IDpricing
LabelPricing
TypeEnum
RequiredYes
Allowed Valuesno-charge, individual, group

Description: How modifiers in the group are priced.

Business Logic:

  • No charge (no-charge): All modifiers are free (every modifier price is set to 0).
  • Individual (individual): Each modifier has its own price (stored in the group's modifier_prices map).
  • Group (group): All modifiers share the single group price.

Group Price

PropertyValue
Field IDprice
LabelPrice
TypeCurrency (Money)
RequiredOnly when pricing is group
Validationrequired, numeric (when pricing = group)

Description: The single shared price applied to every modifier when Group pricing is selected.


Tax Rate Code

PropertyValue
Field IDtax_rate_code
LabelTax Rate
TypeString
RequiredNo
Validationnullable, string (only accepted when pricing is not no-charge)

Description: Tax rate applied to the group's modifiers. Not required, and not applicable for No charge pricing.


Visibility (Channels)

PropertyValue
Field IDvisibility
LabelChannels
TypeArray of channel enums
RequiredYes
Allowed ValuesKiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, Shopify

Description: Sales channels on which the modifier group is visible. At least one channel must be selected.


Settings — Selection Type

PropertyValue
Field IDsettings.is_mandatory
LabelSelection Type (Optional / Required)
TypeBoolean
RequiredYes
Defaultfalse (Optional)

Description: When true (Required), the customer must pick a modifier from this group. When false (Optional), selection is not forced.


Settings — Allow Multiple Selection

PropertyValue
Field IDsettings.allow_select_more_than_one
LabelAllow customers to select more than one modifier
TypeBoolean
RequiredYes
Defaultfalse

Description: When true, customers can select multiple modifiers from the group, and a maximum-selectable dropdown appears.


Settings — Maximum Selected

PropertyValue
Field IDsettings.max_selected
LabelMaximum of modifiers a customer can select
Type"No Maximum" or a number (2..N)
RequiredRequired only when allow_select_more_than_one is true

Description: The maximum number of modifiers a customer can choose. Options are "No Maximum" or a specific number up to the count of modifiers in the group. Only shown/required when multiple selection is enabled.


Settings — Allow Same Modifier More Than Once

PropertyValue
Field IDsettings.allow_same_modifier_more_than_one
LabelAllow customers to select the same modifier more than once
TypeBoolean
RequiredYes
Defaultfalse

Description: When true, a customer can add the same modifier multiple times (e.g., double sugar, double sauce).


Order (deprecated)

PropertyValue
Field IDorder
Label(internal)
TypeNumber
RequiredNo (system-managed)

Description: Display-ordering value, set to the current count of groups on creation. Marked @deprecated in the model; reordering still writes to it via the reorder endpoint.


Modifier Option Fields

Option Name

PropertyValue
Field IDname
LabelName
TypeText
RequiredYes
Validationrequired, string

Description: Customer-facing name of the option.

Examples:

  • "Small"
  • "Extra Cheese"
  • "No Pickles"

Kitchen Name

PropertyValue
Field IDkitchen_name
LabelKitchen Name
TypeText
RequiredNo
Validationnullable, string

Description: Optional clearer/shorter label for kitchen and printer staff.


Price

PropertyValue
Field IDprice
LabelPrice
TypeCurrency (Money — absolute amount, not a +/- adjustment)
RequiredYes
Validationrequired, numeric

Description: The modifier's price as an absolute amount. With Group pricing every modifier is forced to the group price; with No charge every modifier is set to 0; with Individual pricing each modifier keeps its own value.

Examples:

  • "Large": 2.00
  • "Extra Cheese": 1.50
  • "Ketchup": 0.00

Image

PropertyValue
Field IDimage (image.source, image.file)
LabelImage
TypeFile upload
RequiredNo
Validationimage.source is a valid image source; image.file max 10240 KB

Description: Optional image for the modifier option, stored as a content entity (its content_id is saved on the modifier record). Used where modifier images are shown (e.g., kiosk).


Business Logic

Price Calculation with Modifiers

Item Base Price: €10.00

Selected Modifiers (absolute prices):
- Size: Large (€2.00)
- Extra Cheese (€1.50)
- Bacon (€2.00)
- No Onions (€0.00)

Item Total: €10.00 + €2.00 + €1.50 + €2.00 + €0.00 = €15.50

Quantity: 2

Line Total: €15.50 × 2 = €31.00

Pricing Modes

No charge  → every modifier price is forced to 0
Individual → each modifier keeps its own price (stored in group's modifier_prices map)
Group      → every modifier is forced to the single group price

Validation Flow

Customer selects item


For each modifier group attached to item:

        ├── Is group Required (settings.is_mandatory)?
        │   └── Yes → Must have a selection

        ├── Is multiple selection allowed (allow_select_more_than_one)?
        │   └── Yes → Enforce settings.max_selected ("No Maximum" or a number)

        └── Validate all rules pass


        Allow "Add to Cart"

Customer Impact

Online Ordering Flow

  1. Customer clicks item
  2. Item detail modal opens
  3. Modifier groups displayed in order
  4. Required groups marked with badge
  5. Customer makes selections
  6. Price updates in real-time
  7. "Add to Cart" enabled when valid

Kiosk Flow

  1. Customer taps item
  2. Full-screen customization
  3. Step through each modifier group
  4. Large touch targets for options
  5. Running total displayed
  6. Confirm and add to order

KDS Display

Cheeseburger
  - Size: Large
  - Extra Cheese
  - No Pickles
  - Add Bacon

Receipt Format

Cheeseburger                    €8.95
  Large                         €2.00
  Extra Cheese                  €1.50
  Bacon                         €2.00
                              --------
                               €14.45

Relations

Depends On

  • Nothing (standalone feature)

Affects

  • Menu Items: Items reference modifier groups
  • Order Total: Modifier prices affect total
  • KDS: Modifiers shown for preparation
  • Reports: Modifier popularity tracked

Business Rules

  • Pricing must be one of No charge (no-charge), Individual (individual), or Group (group). With Individual pricing each modifier's price is stored separately in a modifier_prices map on the group; with Group pricing every modifier is forced to the single group price; with No charge every modifier price is 0.
  • A tax_rate_code is only accepted when pricing is not No charge, and it is validated only when provided.
  • pos_name is optional and must be unique within the location; if blank, the group's name is used everywhere.
  • visibility is required and must contain at least one channel from: Kiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, Shopify.
  • Modifier groups are synced to external integrations (Square) on create, update, and delete operations.
  • Deleting a modifier group does not automatically remove it from items that reference it; items will simply no longer display the removed group.
  • The order field on a newly created modifier group is set to the count of existing groups, placing it last by default. The field is marked @deprecated in the model.
  • Modifier option images are uploaded and stored as content entities; the content_id is saved on the modifier record after media processing.

FAQs

  • What is the difference between modifiers and variants? Modifiers are add-on customizations (toppings, sauces) sharing the item's inventory. Variants are distinct product variations (sizes) with their own SKU and inventory tracking.

  • How is a modifier priced? Each modifier has an absolute price (not a +/- adjustment). The price actually applied depends on the group's pricing mode: No charge forces every price to 0, Group forces every modifier to the single group price, and Individual lets each modifier keep its own price.

  • Can I share one modifier group across multiple items? Yes. The same modifier group can be attached to many items; you attach it from each item's own editor.

  • "Where do I create and manage modifier groups?" Go to Menus → Modifier Groups (/menus/modifiers). Click "Create Modifier Group" to add one, or use the row actions to Edit or Delete an existing group.

  • "What's the difference between the Name and the POS Name fields?" "Modifier Group Name" is the customer-facing/internal name and is required. "POS Name" is optional, used for internal purposes only (e.g. a shorter name on the kitchen printer), and must be unique within the location. If left blank, the regular name is used everywhere.

  • "What pricing options does a modifier group have?" Three: No charge (modifiers are free), Individual (each modifier has its own price), and Group (all modifiers share one price set on the group). A tax rate can be set for Individual and Group pricing; it isn't required for No charge.

  • "If I pick Group pricing, can each modifier have a different price?" No. With Group pricing every modifier is forced to the single group price, and with No charge every modifier is set to 0. Choose Individual pricing if each option needs its own price.

  • "How do I let customers choose more than one modifier from a group?" In Settings, turn on "Allow customers to select more than one modifier." A "Maximum of modifiers a customer can select?" dropdown then appears (No Maximum or a specific number). Leaving the toggle off makes it single-choice.

  • "How do I make a group mandatory?" In Settings, set the selection type to Required (the default is Optional). Required means the customer must pick a modifier from this group.

  • "What is 'Allow customers to select the same modifier more than once'?" A Settings toggle that lets a customer add the same option multiple times (e.g. double sugar, double sauce) rather than just once.

  • "What's the 'Kitchen Name' on a modifier for?" Each modifier has an optional Kitchen Name alongside its customer-facing Name — use it to give kitchen/printer staff a clearer or shorter label.

  • "Which sales channels can a modifier group appear on?" In the Channels section you pick visibility per channel: Kiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, and Shopify. At least one channel must be selected.

  • "I attached a modifier group but it doesn't show on my item — where do I attach it?" On the modifier group page the "Items" section is read-only and just lists items already using the group. You attach a group to a product from the item's own editor, not from the modifier group screen.

  • "Why are my MplusKassa/ShopCaisse/Lightspeed modifier group's fields greyed out?" Modifier groups synced from an external POS are partially or fully read-only. Name, settings, pricing, and prices come from the source system and would be overwritten on the next sync, so they're locked.

  • "Will my changes to a modifier group show up in online ordering right away?" Yes. Saving an edit fires a menu-reload so ordering channels pick up the change immediately, and create/update/delete are synced to active integrations such as Square.

  • "Can I add a photo to a modifier?" Yes. Each modifier option supports an image upload, used where modifier images are shown (e.g. kiosk).


Troubleshooting

Problem: Modifier group not showing on item

Causes:

  1. Group not attached to item
  2. Group has no modifiers
  3. Group's channel visibility excludes that channel

Solutions:

  1. Attach the group from the item's own editor
  2. Add modifiers to the group
  3. Check the group's Channels (visibility) selection

Problem: Customer can't add item to cart

Causes:

  1. A Required group (settings.is_mandatory) has no selection
  2. Validation error

Solutions:

  1. Check which group is set to Required
  2. Review the group's settings

Problem: Price not calculating correctly

Causes:

  1. Wrong pricing mode for the group
  2. A modifier's price is incorrect

Solutions:

  1. Confirm the pricing mode (No charge / Individual / Group) is what you intend
  2. Review each modifier's price (remember Group forces all to the group price, No charge forces all to 0)

Examples

The JSON below mirrors the create/update request payload (details, pos_name, pricing, price, tax_rate_code, visibility, settings, modifiers[]). location_id is also required on the request but omitted here for brevity.

Size Selection (Required, single, individual pricing)

json
{
  "details": { "name": "Size", "description": "Choose your size" },
  "pos_name": "Size",
  "pricing": "individual",
  "tax_rate_code": "standard",
  "visibility": ["Kiosk", "POS", "Online Ordering"],
  "settings": {
    "is_mandatory": true,
    "allow_select_more_than_one": false,
    "allow_same_modifier_more_than_one": false
  },
  "modifiers": [
    { "name": "Small", "kitchen_name": "S", "price": 0 },
    { "name": "Medium", "kitchen_name": "M", "price": 1.50 },
    { "name": "Large", "kitchen_name": "L", "price": 3.00 }
  ]
}

Extra Toppings (Optional, multiple selection up to 3, individual pricing)

json
{
  "details": { "name": "Extra Toppings" },
  "pricing": "individual",
  "tax_rate_code": "standard",
  "visibility": ["Kiosk", "POS", "Online Ordering", "Takeaway"],
  "settings": {
    "is_mandatory": false,
    "allow_select_more_than_one": true,
    "max_selected": 3,
    "allow_same_modifier_more_than_one": false
  },
  "modifiers": [
    { "name": "Extra Cheese", "kitchen_name": "Cheese", "price": 1.50 },
    { "name": "Bacon", "kitchen_name": "Bacon", "price": 2.00 },
    { "name": "Mushrooms", "kitchen_name": "Mushr", "price": 1.00 },
    { "name": "Jalapeños", "kitchen_name": "Jala", "price": 0.75 }
  ]
}

Choose Your Sauce (Required, single, no charge)

json
{
  "details": { "name": "Choose Your Sauce" },
  "pricing": "no-charge",
  "visibility": ["Kiosk", "POS", "Online Ordering"],
  "settings": {
    "is_mandatory": true,
    "allow_select_more_than_one": false,
    "allow_same_modifier_more_than_one": false
  },
  "modifiers": [
    { "name": "Ketchup", "price": 0 },
    { "name": "Mayonnaise", "price": 0 },
    { "name": "BBQ Sauce", "price": 0 },
    { "name": "Hot Sauce", "price": 0 },
    { "name": "No Sauce", "price": 0 }
  ]
}

Add Extra Shot (Optional, group pricing — every modifier €0.80)

json
{
  "details": { "name": "Add Extra Shot" },
  "pricing": "group",
  "price": 0.80,
  "tax_rate_code": "standard",
  "visibility": ["Kiosk", "POS"],
  "settings": {
    "is_mandatory": false,
    "allow_select_more_than_one": true,
    "max_selected": 2,
    "allow_same_modifier_more_than_one": true
  },
  "modifiers": [
    { "name": "Espresso Shot", "kitchen_name": "Shot", "price": 0.80 },
    { "name": "Decaf Shot", "kitchen_name": "Decaf", "price": 0.80 }
  ]
}

Remove Ingredients (Optional, multiple selection, no charge)

json
{
  "details": { "name": "Remove Ingredients" },
  "pricing": "no-charge",
  "visibility": ["Kiosk", "POS", "Online Ordering"],
  "settings": {
    "is_mandatory": false,
    "allow_select_more_than_one": true,
    "max_selected": "No Maximum",
    "allow_same_modifier_more_than_one": false
  },
  "modifiers": [
    { "name": "No Lettuce", "price": 0 },
    { "name": "No Tomato", "price": 0 },
    { "name": "No Onion", "price": 0 },
    { "name": "No Pickles", "price": 0 }
  ]
}