Expression Language for Shipping and Payment Rules
Expression language for payment and shipping rules expressions is a user-friendly and business oriented extension of the Symfony Expression Language. It is easy to use and, on top of usual comparison and logical operators, it allows iterating through the collections of items using (collection).all and (collection).any operations. Keep in mind that out-of-the-box, you can add any custom fields to the required entity in addition to the attributes listed below.
Note
Note that the float values require using a number with a fractional part (a floating-point number), for example, lineItem.quantity != 1.0 instead of lineItem.quantity != 1.
Attributes Supported in Shipping and Payment Rule Expressions
Attributes
Shipping
shippingMethod string
Financial
paymentMethod string
currency string
subtotal.value float
subtotal.currency string
Geographies
billingAddress
billingAddress.street string
billingAddress.street2 string
billingAddress.city string
billingAddress.regionName string
billingAddress.regionCode string
billingAddress.postalCode string
billingAddress.countryName string
billingAddress.countryIso3 string
billingAddress.countryIso2 string
To find the whole list of fields available for billingAddress, navigate to System > Entities > Entity Management > Order Address. More details on the entity fields management are described here.
shippingAddress
shippingAddress.street string
shippingAddress.street2 string
shippingAddress.city string
shippingAddress.regionName string
shippingAddress.regionCode string
shippingAddress.postalCode string
shippingAddress.countryName string
shippingAddress.countryIso3 string
shippingAddress.countryIso2 string
shippingOrigin
shippingOrigin.street string
shippingOrigin.street2 string
shippingOrigin.city string
shippingOrigin.regionName string
shippingOrigin.regionCode string
shippingOrigin.postalCode string
shippingOrigin.countryName string
shippingOrigin.countryIso3 string
shippingOrigin.countryIso2 string
To find the whole list of fields available for shippingOrigin, navigate to System > Entities > Entity Management > Order Address. More details on the entity fields management are described here.
Business
Customer
customer.id int
customer.name string
customer.group.id int
customer.group.name string
customer.payment_term_7c4f1e8e.label string (Payment Term)
customer.payment_term_7c4f1e8e.id int (Payment Term)
To find the whole list of fields available for customer, navigate to System > Entities > Entity Management > Customer. More details on the entity fields management are described here.
Customer User
customerUser.id int
customerUser.email string
customerUser.firstName string
customerUser.middleName string
customerUser.lastName string
customerUser.fullName string (which is a customerUser.firstName ~ ‘ ‘ ~ customerUser.lastName, e.g. ‘Amanda Cole’)
To find the whole list of fields available for customerUser, navigate to System > Entities > Entity Management > Customer User. More details on the entity fields management are described here.
Note
Please note that if you enable guest checkout on your website, customerUser will be empty on the first two checkout steps. To prevent shipping and payment calculation errors, you can add an additional condition, “customerUser and”, before the first usage of the customerUser variable, for example, subtotal > 500.0 and customerUser and customerUser.email = “”
Collections
lineItems is a collection of line items (products and their quantity, units, price, weight, and dimensions) that are being ordered.
lineItems[X].product.unitPrecisions is a collection of unit precisions that is available in OroCommerce for the lineItem product in the order.
lineItems[X].product.inventoryLevels is a collection of inventory levels - available quantities of the particular product in various units in warehouses that are available in OroCommerce.
customer.users is a collection of customer users that belong to the customer the order is submitted for.
lineItems[X].kitItemLineItems is a collection of product kit item line items (products kit items, products and their quantity, units, price) that are being ordered.
lineItems Collection
Attributes
Note
Use the items with LineItem prefix when processing the lineItems collection using .any() and .all() expressions. Alternatively, address the item in the collection directly, e.g. lineItems[1].product.sku.
lineItem.product.id int
lineItem.product.type string
lineItem.product.sku string
lineItem.product.primaryUnitPrecision.id int
lineItem.product.primaryUnitPrecision.precision int
lineItem.product.primaryUnitPrecision.sell bool
lineItem.product.category.id int
lineItem.product.inventoryLevels collection
lineItem.productUnit.code string
lineItem.quantity float
lineItem.price.value float
lineItem.price.currency string
lineItem.weight.value float
lineItem.weight.unit.code string
lineItem.dimensions.value.length float
lineItem.dimensions.value.width float
lineItem.dimensions.value.height float
lineItem.dimensions.unit.code string
lineItem.product.unitPrecisions collection
lineItem.kitItemLineItems collection
lineItem.checksum string
To find the whole list of fields available for:
lineItem — navigate to System > Entities > Entity Management > Order Line Item
lineItem.product — navigate to System > Entities > Entity Management > Product
lineItem.product.productUnit — navigate to System > Entities > Entity Management > Product Unit
More details on the entity fields management are described here.
lineItems[X].product.unitPrecisions Collection
Attributes
Note
Use the items with unitPrecision prefix when processing the unitPrecisions collection using lineItem.product.unitPrecisions.any() and lineItem.product.unitPrecisions.all() expressions. Alternatively, address the item in the collection directly, e.g. lineItem.product.unitPrecisions[1].unit.code.
unitPrecision.unit.code string
unitPrecision.precision int
unitPrecision.sell bool
lineItems[X].product.inventoryLevels Collection
Attributes
Note
Use the items with inventoryLevel prefix when processing the inventoryLevels collection using lineItem.product.inventoryLevels.any() and lineItem.product.inventoryLevels.all() expressions. Alternatively, address the item in the collection directly, e.g. lineItem.product.inventoryLevels[1].warehouse.id.
inventoryLevel.id int
inventoryLevel.quantity int
inventoryLevel.productUnitPrecision.unit.code string
inventoryLevel.productUnitPrecision.precision int
inventoryLevel.productUnitPrecision.sell bool
inventoryLevel.warehouse.id int
inventoryLevel.warehouse.name string
lineItems[X].kitItemLineItems Collection
Note
Use the items with kitItemLineItem prefix when processing the kitItemLineItems collection using lineItem.kitItemLineItems.any() and lineItem.kitItemLineItems.all() expressions. Alternatively, address the item in the collection directly, e.g. lineItem.kitItemLineItems[1].product.sku.
kitItemLineItem.kitItem.id int
kitItemLineItem.kitItem.defaultLabel string
kitItemLineItem.kitItem.optional bool
kitItemLineItem.kitItem.sortOrder int
kitItemLineItem.kitItem.minimumQuantity float
kitItemLineItem.kitItem.maximumQuantity float
kitItemLineItem.product.id int
kitItemLineItem.product.sku string
kitItemLineItem.product.primaryUnitPrecision.id int
kitItemLineItem.product.primaryUnitPrecision.precision int
kitItemLineItem.product.primaryUnitPrecision.sell bool
kitItemLineItem.product.category.id int
kitItemLineItem.product.inventoryLevels collection
kitItemLineItem.product.unitPrecisions collection
kitItemLineItem.productUnit.code string
kitItemLineItem.quantity float
kitItemLineItem.price.value float
kitItemLineItem.price.currency string
To find the whole list of fields available for kitItem, navigate to System > Entities > Entity Management > Product Kit Item. For more details on the entity fields management, see the Manage Entity Fields topic.
customer.users Collection
Attributes
Note
Use the items with user prefix when processing the customer.users collection using customer.users.any() and customer.users.all() expressions. Alternatively, address the item in the collection directly, e.g. customer.users[1].email.
user.id int
user.email string
user.firstName string
user.middleName string
user.lastName string
Expression Syntax
You can use the following elements to build the expression that identifies the cases when shipping or payment rule should be applied.
Supported Data
Text enclosed in quotes (’) or double quotes (“)
Numbers (e.g. 32)
Arrays (e.g. [1, 5], and [“Option A”, “Option B”])
Boolean values (true and false)
null
Attributes and data structures listed in the Attributes Supported in Shipping and Payment Rule Expressions, e.g. subtotal > 100000 or lineItems.all(lineItem.quantity > 1000).
Use lineItems.all(expression) and lineItems.any(expression) to assess the collection of line items (products and their quantity, units, price, weight, and dimensions) in the order, quote or request for quote. Inside the expression, use lineItem.product.<fieldname> phrase to access the product field value. Separate the field from the item with a period.
Use lineItems.sum(expression) to sum up results of complex calculations that use the collection items and their properties as parameters. For example, you can get a total weight of the order using the following expression: lineItems.sum(lineItem.weight.value *lineItem.quantity).
Outside the collection operations, you can assess an element of the array using item[id].fieldname phrase (e.g. lineItems[1].product.price > 1000.00). Separate the field from the item with a period.
See more information about using collections in the Collection Validation section below.
Supported operators
Arithmetic:
add: +
subtract: -
multiply: *
divide: /
mod (a remainder of division): %
power: **
Operations with text:
concatenate: ~
Comparison:
equal: =
not equal: !=
less: <
more: >
less or equal: <=
more or equal: >=
matches (regexp)
in
not in
Logical:
and
or
not
.. (range, like in 1..10)
Collection Validation with any (OR) and all (AND) Operations
To validate all items in the collection (e.g. products in the order being submitted), or ensure that at least one value has a particular quality (e.g. it meets bulk quantity requirements), use items.all(sub-condition) and items.any(sub-condition) expression phrases. The sub-condition is an expression that applies to every item. Note that it is enclosed in brackets, and no single/double quotes (‘/”) are used as they are reserved for the text values.
When you are using all or any method, you provide the named collection of elements (e.g. products) and Oro automatically guesses the name of the single element (e.g. product). It is produced by stripping the trailing ‘s’ for countable nouns and by adding a leading ‘Item’ the uncountable ones, like in: milk.all(milkItem.isfresh).
The items.all(nested_expression) expression is true when the nested condition is satisfied for every item in the collection. When an item evaluation results in false, the items.all() immediately returns false without processing the remaining items.
Vise versa, items.any(nested_expression) is true if a nested expression evaluates to true for at least one item. Remaining items are not processed either.
Sample Expression
For example, you need to ensure that all products are available in the requested quantity in the particular warehouse (inventory levels in the warehouse A is greater than the line item quantity in the order).
You can refer to the Attributes Supported in Shipping and Payment Rule Expressions to build the expression.
For expression evaluation, OroCommerce walks through the lineItems collection and for every item in the collection it checks that this product is available in the warehouse A in the units that were ordered, that it is enabled for sale from the warehouse A, and that it is in stock in the required quantity.
lineItems.all(
lineItem.product.inventoryLevels.any(
inventoryLevel.warehouse.name = 'Additional Warehouse'
and
inventoryLevel.quantity >= lineItem.quantity
and
inventoryLevel.productUnitPrecision.unit.code = lineItem.productUnit.code
and
inventoryLevel.productUnitPrecision.sell
)
)
The lineItems.all(…) expression is a loop through the elements of lineItems collection. It exposes every element of the collection inside the loop (in round brackets) as a lineItem.
In the example, for every line item, the following condition is verified to be true:
...
lineItem.product.inventoryLevels.any(
inventoryLevel.warehouse.name = 'Additional Warehouse'
and
inventoryLevel.quantity >= lineItem.quantity
and
inventoryLevel.productUnitPrecision.unit.code = lineItem.productUnit.code
and
inventoryLevel.productUnitPrecision.sell
)
...
inventoryLevels is another collection being decomposed in the nested loop: lineItem.product.inventoryLevels.any(..)
Inside the loop, OroCommerce checks every inventory level to find the one that is related to the warehouse A and verify the remaining conditions to evaluate the quantity is enough, like the following:
inventoryLevel.productUnitPrecision.unit.code = lineItem.productUnit.code
Here’s another example that may be helpful for those who want to work with product kit line items. This expression does the same thing as the previous one but also checks if a product line item is a kit. If it is, it verifies that all the products in the kit are available in the requested quantity in the designated warehouse.
...
lineItems.all(
lineItem.product.inventoryLevels.any(
inventoryLevel.warehouse.name = 'Additional Warehouse'
and
inventoryLevel.quantity >= lineItem.quantity
and
inventoryLevel.productUnitPrecision.unit.code = lineItem.productUnit.code
and
inventoryLevel.productUnitPrecision.sell
)
and
(
lineItem.product.type != 'kit'
or
lineItem.kitItemLineItems.all(
kitItemLineItem.product.inventoryLevels.any(
inventoryLevel.warehouse.name = 'Product Kits Warehouse'
and
inventoryLevel.quantity >= kitItemLineItem.quantity
and
inventoryLevel.productUnitPrecision.unit.code = kitItemLineItem.productUnit.code
and
inventoryLevel.productUnitPrecision.sell
)
)
)
)
...