Markus Michalski FAQ

Variant Price Surcharge: Technical Details

Technical details of Variant Price Surcharge for Sylius: price calculation flow, decorated calculators, data model with per-channel overrides, saving in the form, plugin structure and routes.

Deutsche Version

Back to overview

Variant Price Surcharge: Technical Details

Price calculation flow

graph TD
    A["calculate() / calculateOriginal()"] --> B["Own price via the Sylius calculator"]
    B --> C{"Global switch on?"}
    C -->|No| Z["Own price"]
    C -->|Yes| D{"Surcharge stored and not base?"}
    D -->|No| Z
    D -->|Yes| H1{"Enabled, channel value before global value?"}
    H1 -->|No| Z
    H1 -->|Yes| E{"Product has a base variant?"}
    E -->|No| Z
    E -->|Yes| F["Base variant price via the Sylius calculator"]
    F --> G["(base price + fixed surcharge) x percentage / 100"]
    G --> H["Round, minimum 0"]

Decorated calculators

Service Applies to Decorator
sylius.calculator.product_variant_price Sylius 2.2 and 2.3, cart and order SurchargeAwareProductVariantPriceCalculator
sylius.calculator.product_variant_catalog_price from Sylius 2.3, product page, listings, variant selection, API SurchargeAwareCatalogPricesCalculator, registered by the compiler pass DecorateCatalogPricesCalculatorPass

Data model

Table Column Type Note
mmd_product_variant_price_surcharge id INT Primary key
product_variant_id INT Unique, foreign key to sylius_product_variant, ON DELETE CASCADE
is_base Boolean Base variant
is_enabled Boolean Surcharge enabled
percentage Decimal(10,2), nullable Default 100.00
fixed_surcharge INT, nullable Smallest currency unit, default 0
mmd_product_variant_price_surcharge_channel id INT Primary key
surcharge_id INT Foreign key to mmd_product_variant_price_surcharge, ON DELETE CASCADE
channel_code VARCHAR(255) Channel code, no foreign key to sylius_channel
is_enabled Boolean, nullable NULL means inherit the global value
percentage Decimal(10,2), nullable NULL means inherit the global value
fixed_surcharge INT, nullable NULL means inherit the global value
mmd_variant_price_surcharge_settings id INT Always 1
active Boolean Global switch, default on

The combination of surcharge_id and channel_code is unique (index mmd_surcharge_channel_idx). The surcharge is attached to the variant through a foreign key. The plugin does not modify the Sylius ProductVariant class.

Three migrations create the tables: Version20260905080038 (surcharges), Version20260905124014 (settings) and Version20261015135354 (per-channel overrides).

Saving in the variant form

ProductVariantTypeExtension extends the form type ProductVariantType with an unmapped sub-form. On load it fetches the variant's stored row or creates an empty object. The form always shows one entry per Sylius channel, even if no row exists for it yet. On submit:

Plugin structure

Excerpt of the main directories:

src/
  Calculator/     SurchargeAwareProductVariantPriceCalculator, SurchargeAwareCatalogPricesCalculator
  Command/        SetupCommand
  Controller/     Admin/VariantPriceSurchargeSettingsController
  DependencyInjection/   Extension, Configuration, Compiler/DecorateCatalogPricesCalculatorPass
  Entity/         ProductVariantPriceSurcharge, Channel/ProductVariantPriceSurchargeChannel, Settings/VariantPriceSurchargeSettings
  Form/           Extension/ProductVariantTypeExtension, Type/...
  Menu/           AdminMenuListener, MenuGroup
  Migrations/
  Repository/
  Twig/           PluginVersionExtension
config/
  doctrine/       Mapping of the surcharge entity
  grids/          Overview grid
  routes/         admin.yaml, shop.yaml (no routes)
  services/
  twig_hooks/     Form section and settings page
templates/admin/
translations/     messages and flashes, de and en

Routes

Route Path Purpose
mmd_variant_price_surcharge_admin_settings /<admin path>/variant-price-surcharge/settings Settings page, GET and POST
Resource routes of the overview /<admin path>/variant-price-surcharges List and delete

<admin path> is the parameter sylius_admin.path_name, admin by default. Both routes sit behind the Sylius admin firewall.

Further notes