Shipping calculation
Technical Information
Highlights
-
Freely definable shipping cost calculation via TWIG
-
Debug output via acris_dump()
-
Insert additional fields into an order using TWIG code
-
Extended access to variables
Features
-
Individual calculation for individual shipping methods via TWIG
-
Debug output via acris_dump() for analysing variables and intermediate steps
-
Extended access to variables
-
Rule-based application
-
Add custom fields to an order during TWIG shipping calculation with acris_set_order_custom_field(“custom_field_one”, “My value 1”)
Shipping cost calculation in Shopware is very limited. This makes price calculation difficult for many products.
We have therefore developed a plugin that offers the following functions:
Customizable shipping cost calculation using TWIG
With this plugin, any logic for shipping cost calculation can be created, greatly expanding the flexibility in designing shipping costs. Example TWIG code snippets are provided in the configuration guide.
Debug output via acris_dump()
With the new acris_dump() function, any variables or intermediate steps can be output directly in the TWIG code during shipping cost calculation. The debug values appear in the JavaScript console of the storefront, making it easier to analyse and test complex shipping logic.
Access to more variables
By using TWIG, it is possible to access more variables and their properties. These include currency, context, customer, shopping cart, lineItem, and much more. The available variables are explained in detail in the configuration guide.
Add custom fields to an order using TWIG code (from 4.2.0 for Shopware 6.6 and 5.2.0 for Shopware 6.7)
The following code allows you to add a custom field to the order when it is created. Of course, this requires that the shipping method and calculation of the shipping method with the individual TWIG code are applied to the order.
acris_set_order_custom_field(“custom_field_one”, “My value 1”)
The first parameter (custom_field_one) corresponds to the technical name of the additional field. This can match the technical name of the custom field created in the admin (Settings > Custom fields) for later display in the administration when the order is placed. However, there does not have to be a match. In this case, the value would be stored in the database, but it would not be displayed in the administration when the order is placed.
The second parameter corresponds to the value of the custom field. The following types are possible:
* Text (string)
* Number (int, float)
* Array
Code example:
{% set number_of_parcels = 0 %}
{% set shipping = 10 %}
{% for lineItem in lineItems %}
{% if lineItem.good %}
{% set number_of_parcels = number_of_parcels + 1 %}
{% endif %}
{% endfor %}
{{ acris_set_order_custom_field('custom_number_of_parcels', number_of_parcels) }}
{{ shipping }}
Dynamic modification of shipping method name (from plugin version 5.3.0)
The name of the shipping method can now be dynamically adjusted directly within the Twig template used for shipping cost calculation.
Example 1: Replace shipping method name
{{ acris_set_shipping_name('ACRIS Test Name') }}Example 2: Extend shipping method name
{{ acris_set_shipping_name(shippingMethod.translated.name ~ ' (my suffix)') }}Important note: The customization of the shipping method name only applies to the currently calculated or selected shipping method. In the selection (e.g. in the cart or on the checkout page), it is not possible to display different names for non-selected shipping methods.
The reason for this is that the inserted Twig code is only executed when the shipping cost calculation is actually performed.
Installation
- Open Plugin Manager via Settings > System > Plugins
- Upload, install and activate the plugin
Calculate shipping costs:
6.7: Settings > Commerce > Shipping > Edit or create shipping method > Price matrix > Add price matrix > Calculate shipping manually
Configuration options:Select/create rule
As with the standard shipping calculation, a rule must be defined so the calculation can take effect.
Twig input field
This is where the TWIG code is entered. In addition, the acris_dump() function can be used to generate debug output that is visible in the JavaScript console of the storefront.
TWIG - Templates
-
Shipping costs fixed at 10 without further rules
{% set shipping = 10 %} {{ shipping }} -
Shipping costs increase by a value if an item with the CustomField is in the cart
{% set shipping = 10 %} {% for lineItem in lineItems %} {% if lineItem.payload.customFields.custom_sw4_attributes_attr7 == "1" %} {% set shipping = shipping + 1 %} {% endif %} {% endfor %} {{ shipping }}
Important: When configuring the custom fields in the admin under Settings > System > Custom Fields, the option “Available in carts” must be activated for the respective fields. -
Shipping costs depending on which categories the products in the cart belong to
{% set numberProductsInsideCategory1 = 0 %} {% set numberProductsInsideCategory2 = 0 %} {% for lineItem in lineItems %} {% if lineItem.payload.categoryIds %} {% for categoryId in lineItem.payload.categoryIds %} {# Is product inside category 1 - we check it by the UUID #} {% if categoryId == '21b199946c884aa294b409f135963880' %} {% set numberProductsInsideCategory1 = numberProductsInsideCategory1 + 1 %} {% endif %} {# Is product inside category 2 - we check it by the UUID #} {% if categoryId == '0b5e8204e7034c2ca17a3899274874a4' %} {% set numberProductsInsideCategory2 = numberProductsInsideCategory2 + 1 %} {% endif %} {% endfor %} {% endif %} {% endfor %} {# Now we can do something with the variables numberProductsInsideCategory1 and numberProductsInsideCategory2 #} {% if numberProductsInsideCategory1 > 0 and numberProductsInsideCategory2 > 0 %} 19.90 {% elseif numberProductsInsideCategory1 > 0 %} 14.90 {% elseif numberProductsInsideCategory2 > 0 %} 12.90 {% else %} 9.90 {% endif %}
Note: In the cart only the UUIDs of the categories (unique IDs in the database) are available. You can easily read and copy the UUID using the plugin https://store.shopware.com/acris28622190382f/acris-kategorie-id-anzeigen.html . -
Quantity calculation
{% set quantity = 0 %} {% for lineItem in lineItems %} {% if lineItem.quantity %} {% set quantity = quantity + lineItem.quantity %} {% endif %} {% endfor %} {{ quantity }} -
Customer custom field free shipping limit
{% set customerShippingFreeLimit = false %} {% if customer.customFields is not empty and customer.customFields.custom_customer_shipping_free_limit > 0 %} {% set customerShippingFreeLimit = customer.customFields.custom_customer_shipping_free_limit %} {% endif %} {% set shippingCosts = 9.95 %} {% if customerShippingFreeLimit > 0 and cart.price.totalPrice >= customerShippingFreeLimit %} {% set shippingCosts = 0 %} {% endif %} {{ shippingCosts }} -
Add VAT depending on delivery country and cart contents to the net price of shipping costs
{% set shippingNet = 100 %} {% if context.taxState == 'gross' and matchingTaxRules and matchingTaxRules.highestRate() %} {% set shippingNet = shippingNet / 100 * matchingTaxRules.highestRate().getPercentage() %} {% set shipping = shippingNet * (1 + (matchingTaxRules.highestRate().getTaxRate() / 100)) %} {% else %} {% set shipping = shippingNet %} {% endif %} {{ shipping }} -
Shipping cost calculation depending on gross / net in the cart
{% if context.taxState == 'gross' %} {# do smth if tax state is gross #} {% else %} {# do smth if tax state is net #} {% endif %} -
If at least one product is out of stock, shipping costs = €10, otherwise €5
{% set oneProductOutOfStock = false %} {% for lineItem in lineItems %} {% if lineItem.deliveryInformation and lineItem.deliveryInformation.stock <= 0 %} {% set oneProductOutOfStock = true %} {% endif %} {% endfor %} {% if oneProductOutOfStock %} 10 {% else %} 5 {% endif %}
Available variables
Currency- currency.isoCode
- currency.factor
- currency.symbol
- currency.shortName
- currency.name
- currency.position
- currency.translations
- currency.orders
- currency.salesChannels
- currency.salesChannelDefaultAssignments
- currency.salesChannelDomains
- currency.shippingMethodPrices
- currency.promotionDiscountPrices
- currency.isSystemDefault
- currency.productExports
- currency.countryRoundings
- currency.itemRounding
- currency.totalRounding
- currency.taxFreeFrom
- currency.translated.customFields
- context.languageIdChain
- context.versionId
- context.currencyId
- context.currencyFactor
- context.scope
- context.ruleIds
- context.source
- context.considerInheritance
- context.taxState
- context.rounding
An array with the active IDs of the rules.
Customer
- customer.groupId
- customer.defaultPaymentMethodId
- customer.salesChannelId
- customer.languageId
- customer.lastPaymentMethodId
- customer.defaultBillingAddressId
- customer.defaultShippingAddressId
- customer.customerNumber
- customer.salutationId
- customer.firstName
- customer.lastName
- customer.company
- customer.password
- customer.email
- customer.title
- customer.vatIds
- customer.affiliateCode
- customer.campaignCode
- customer.active
- customer.doubleOptInRegistration
- customer.doubleOptInEmailSentDate
- customer.doubleOptInConfirmDate
- customer.hash
- customer.guest
- customer.firstLogin
- customer.lastLogin
- customer.newsletter
- customer.birthday
- customer.lastOrderDate
- customer.orderCount
- customer.orderTotalAmount
- customer.createdAt
- customer.updatedAt
- customer.legacyEncoder
- customer.legacyPassword
- customer.group
- customer.defaultPaymentMethod
- customer.salesChannel
- customer.language
- customer.lastPaymentMethod
- customer.salutation
- customer.defaultBillingAddress
- customer.defaultShippingAddress
- customer.activeBillingAddress
- customer.activeShippingAddress
- customer.addresses
- customer.orderCustomers
- customer.autoIncrement
- customer.tags
- customer.tagIds
- customer.promotions
- customer.recoveryCustomer
- customer.productReviews
- customer.remoteAddress
- customer.requestedGroupId
- customer.requestedGroup
- customer.boundSalesChannelId
- customer.boundSalesChannel
- customer.wishlists
- customer.customFields
- cart.name
- cart.token
- cart.price.netPrice
- cart.price.totalPrice
- cart.price.calculatedTaxes
- cart.price.taxRules
- cart.price.positionPrice
- cart.price.taxStatus
- cart.price.rawTotal
- cart.lineItems
- cart.errors
- cart.deliveries
- cart.transactions
- cart.modified
- cart.customerComment
- cart.affiliateCode
- cart.campaignCode
- cart.data
- cart.ruleIds
An array with the line items.
lineItem
A lineItem contains information about the listed products
- lineItem.id
- lineItem.referencedId
- lineItem.label
- lineItem.quantity
- lineItem.type
- lineItem.priceDefinition
- lineItem.price
- lineItem.good
- lineItem.description
- lineItem.cover
- lineItem.deliveryInformation
- lineItem.deliveryInformation.stock
- lineItem.deliveryInformation.weight
- lineItem.deliveryInformation.freeDelivery
- lineItem.deliveryInformation.restockTime
- lineItem.deliveryInformation.deliveryTime
- lineItem.deliveryInformation.height
- lineItem.deliveryInformation.width
- lineItem.deliveryInformation.length
- lineItem.children
- lineItem.requirement
- lineItem.removable
- lineItem.stackable
- lineItem.quantityInformation
- lineItem.modified
- lineItem.dataTimestamp
- lineItem.dataContextHash
- lineItem.payload
- lineItem.payload.isCloseout
- lineItem.payload.customFields
- lineItem.payload.createdAt
- lineItem.payload.releaseDate
- lineItem.payload.isNew
- lineItem.payload.markAsTopseller
- lineItem.payload.purchasePrices
- lineItem.payload.productNumber
- lineItem.payload.manufacturerId
- lineItem.payload.taxId
- lineItem.payload.tagIds
- lineItem.payload.categoryIds
- lineItem.payload.propertyIds
- lineItem.payload.optionIds
- lineItem.payload.options
- lineItem.payload.length
- lineItem.payload.height
- lineItem.payload.width
- lineItem.payload.deliveryTime
- lineItem.payload.features
- salesChannel.typeId
- salesChannel.languageId
- salesChannel.currencyId
- salesChannel.paymentMethodId
- salesChannel.shippingMethodId
- salesChannel.countryId
- salesChannel.navigationCategoryId
- salesChannel.navigationCategoryDepth
- salesChannel.homeSlotConfig
- salesChannel.homeCmsPageId
- salesChannel.homeCmsPage
- salesChannel.homeEnabled
- salesChannel.homeName
- salesChannel.homeMetaTitle
- salesChannel.homeMetaDescription
- salesChannel.homeKeywords
- salesChannel.footerCategoryId
- salesChannel.serviceCategoryId
- salesChannel.name
- salesChannel.shortName
- salesChannel.accessKey
- salesChannel.currencies
- salesChannel.languages
- salesChannel.configuration
- salesChannel.active
- salesChannel.maintenance
- salesChannel.maintenanceIpWhitelist
- salesChannel.taxCalculationType
- salesChannel.type
- salesChannel.currency
- salesChannel.language
- salesChannel.paymentMethod
- salesChannel.shippingMethod
- salesChannel.country
- salesChannel.orders
- salesChannel.customers
- salesChannel.countries
- salesChannel.paymentMethods
- salesChannel.shippingMethods
- salesChannel.translations
- salesChannel.domains
- salesChannel.systemConfigs
- salesChannel.navigationCategory
- salesChannel.footerCategory
- salesChannel.serviceCategory
- salesChannel.productVisibilities
- salesChannel.mailHeaderFooterId
- salesChannel.numberRangeSalesChannels
- salesChannel.mailHeaderFooter
- salesChannel.customerGroupId
- salesChannel.customerGroup
- salesChannel.newsletterRecipients
- salesChannel.promotionSalesChannels
- salesChannel.documentBaseConfigSalesChannels
- salesChannel.productReviews
- salesChannel.seoUrls
- salesChannel.seoUrlTemplates
- salesChannel.mainCategories
- salesChannel.paymentMethodIds
- salesChannel.productExports
- salesChannel.hreflangActive
- salesChannel.hreflangDefaultDomainId
- salesChannel.hreflangDefaultDomain
- salesChannel.analyticsId
- salesChannel.analytics
- salesChannel.customerGroupsRegistrations
- salesChannel.eventActions
- salesChannel.boundCustomers
- salesChannel.wishlists
- salesChannel.landingPages
- salesChannel.translated.customFields
- shippingMethod.translated.name
- shippingMethod.active
- shippingMethod.description
- shippingMethod.trackingUrl
- shippingMethod.deliveryTimeId
- shippingMethod.deliveryTime
- shippingMethod.translations
- shippingMethod.orderDeliveries
- shippingMethod.salesChannelDefaultAssignments
- shippingMethod.salesChannels
- shippingMethod.availabilityRule
- shippingMethod.availabilityRuleId
- shippingMethod.prices
- shippingMethod.mediaId
- shippingMethod.taxId
- shippingMethod.media
- shippingMethod.tags
- shippingMethod.taxType
- shippingMethod.tax
- shippingMethod.translated.customFields
- paymentMethod.pluginId
- paymentMethod.handlerIdentifier
- paymentMethod.translated.name
- paymentMethod.distinguishableName
- paymentMethod.description
- paymentMethod.position
- paymentMethod.active
- paymentMethod.afterOrderEnabled
- paymentMethod.plugin
- paymentMethod.translations
- paymentMethod.orderTransactions
- paymentMethod.customers
- paymentMethod.salesChannelDefaultAssignments
- paymentMethod.salesChannels
- paymentMethod.availabilityRule
- paymentMethod.availabilityRuleId
- paymentMethod.mediaId
- paymentMethod.media
- paymentMethod.formattedHandlerIdentifier
- paymentMethod.shortName
- paymentMethod.appPaymentMethod
- paymentMethod.translated.customFields
- customerGroup.name
- customerGroup.translated.name
- customerGroup.displayGross
- customerGroup.translations
- customerGroup.customers
- customerGroup.salesChannels
- customerGroup.registrationActive
- customerGroup.registrationTitle
- customerGroup.registrationIntroduction
- customerGroup.registrationOnlyCompanyRegistration
- customerGroup.registrationSeoMetaDescription
- customerGroup.registrationSalesChannels
- customerGroup.translated.customFields
List of taxes available in the shop – PHP class TaxCollection
- taxRules.ids
- taxRules.elements
The tax rate relevant for the shipping method in Shopware is determined by the “Tax type” (Auto, Highest rate or Fixed rate). The result is now stored in the variable “matchingTaxRules”, which corresponds to the PHP class https://github.com/shopware/platform/blob/trunk/src/Core/Checkout/Cart/Tax/Struct/TaxRuleCollection.php You can therefore read out the currently highest tax rate in the cart using the following Twig variable: {{ matchingTaxRules.highestRate().getTaxRate() }} With this tax rate you can now calculate. Taxes relevant for the shipping method are determined via Tax type – PHP class TaxRuleCollection
Frequently Asked Questions
Yes. This can be done using the following code:
acris_set_order_custom_field(“custom_field_one”, “My value 1”)
Detailed information is documented in the description of the extension.
| Version | Date | Compatibility | Changelog |
|---|---|---|---|
| 5.3.0 | 15 April 2026 | >=6.7.0.0 <6.8.0.0 |
|
| 5.2.2 | 16 February 2026 | >=6.7.0.0 <6.8.0.0 |
|
| 5.2.1 | 30 December 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.2.0 | 14 November 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.1.0 | 29 October 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.0.5 | 8 October 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.0.4 | 7 October 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.0.3 | 11 July 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.0.2 | 1 July 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.0.1 | 25 June 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 5.0.0 | 19 May 2025 | >=6.7.0.0 <6.8.0.0 |
|
| 4.3.0 | 15 April 2026 | >=6.6.0.0 <6.7.0.0 |
|
| 4.2.2 | 16 February 2026 | >=6.6.0.0 <6.7.0.0 |
|
| 4.2.1 | 30 December 2025 | >=6.6.0.0 <6.7.0.0 |
|
| 4.2.0 | 14 November 2025 | >=6.6.0.0 <6.7.0.0 |
|
| 4.1.0 | 29 October 2025 | >=6.6.0.0 <6.7.0.0 |
|
| 4.0.4 | 20 May 2025 | >=6.6.0.0 <6.7.0.0 |
|
| 4.0.3 | 16 August 2024 | >=6.6.0.0 <6.7.0.0 |
|
| 4.0.2 | 24 June 2024 | >=6.6.0.0 <6.7.0.0 |
|
| 4.0.1 | 25 March 2024 | >=6.6.0.0 |
|
| 4.0.0 | 20 March 2024 | >=6.6.0.0 |
|
| 3.2.1 | 16 February 2026 | >=6.5.0.0 <6.6.0.0 |
|
| 3.2.0 | 12 February 2026 | >=6.5.0.0 <6.6.0.0 |
|
| 3.1.3 | 14 October 2025 | >=6.5.0.0 <6.6.0.0 |
|
| 3.1.2 | 26 May 2025 | >=6.5.0.0 <6.6.0.0 |
|
| 3.1.1 | 15 January 2024 | >=6.5.0.0 |
|
| 3.1.0 | 3 November 2023 | >=6.5.0.0 |
|
| 3.0.2 | 28 August 2023 | >=6.5.0.0 |
|
| 3.0.1 | 5 July 2023 | >=6.5.0.0 |
|
| 3.0.0 | 7 March 2023 | >=6.4.0.0 |
|
| 2.3.4 | 15 February 2023 | >=6.4.0.0 |
|
| 2.3.3 | 26 November 2022 | >=6.4.0.0 |
|
| 2.3.2 | 26 November 2022 | >=6.4.0.0 |
|
| 2.3.1 | 26 November 2022 | >=6.4.0.0 |
|
| 2.3.0 | 26 November 2022 | >=6.4.0.0 |
|
| 2.2.2 | 26 November 2022 | >=6.4.0.0 |
|
| 2.2.1 | 26 November 2022 | >=6.4.0.0 |
|
| 2.2.0 | 26 November 2022 | >=6.4.0.0 |
|
| 2.1.0 | 26 November 2022 | >=6.4.0.0 |
|
| 2.0.2 | 26 November 2022 | >=6.4.0.0 |
|
| 2.0.1 | 26 November 2022 | >=6.4.0.0 |
|
| 2.0.0 | 26 November 2022 | >=6.4.0.0 |
|
| 1.0.4 | 26 November 2022 | >=6.3.0.0 |
|
| 1.0.3 | 26 November 2022 | >=6.3.0.0 |
|
Login
Perfekt für komplexere Anforderungen geeignet
Das Plugin ist wirklich super, wenn man etwas mehr Freiraum bei der Berechnung der Versandkosten benötigt. Besonders wenn man auf Zusatzfelder oder andere Plugins eingehen möchte, lässt sich hier wirklich viel umsetzen! Der Support ist bei Fragen auch wirklich 1A, definitive Empfehlung.
Extrem flexibel
Das Plugin ermöglicht sehr flexible Versandkostenberechnungen. Hier gibt es kaum Grenzen. Auch auf die Variablen anderer Plugins kann zugegriffen werden. Auf meine Anregung wurde sogar eine Debug Möglichkeit eingebaut.
Der Support war dafür Top und sehr hilfreich und schnell, hier ein wirklich großes Dankeschön!
Alles in allem sehr zu empfehlen bei komplexen Versandkostenstrukturen.
Ein muss für jeden Shop mit komplexeren Versandlogiken. Top!
Wir nutzen das Plugin zur komplexeren Versandkalkulation (Volumengewicht, Bandmaße, verschiedene Logiken) und es tut genau das, was man eigentlich von Bord vermisst. Der Support hat mir innerhalb kürzester Zeit geholfen, sehr lobenswert!
Exemplary support
Due to Shopware being quite basic in terms of shipping, we looked for a plugin to meet our needs and this one was the only one that accepted complex rules. Of course we had no idea how to create the code for the rules, but we asked for help from the plugin developers to create the script for us based on our request. Julian was a massive help and replied all day long to our requests, thank you for this!
Mächtiges, gut funktionierendes Werkzeug
Die Erweiterung stellt ein mächtiges Werkzeug zur Darstellung auch komplexerer Versandkostenanforderungen zur Verfügung.
Der Acris-Support, wenn man ihn mal benötigt, ist schnell und kompetent und hilft ersichtlich gerne.
Leider ist die Erweiterung überhaupt erst nötig, weil Shopware in SW6 keine Auf- und Abschlagsversandarten mehr bietet und eigene Regeln und Bedingungen ebenfalls nicht mehr möglich sind.
In SW5 konnte man unsere Anforderungen noch mit Hausmittel darstellen!