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
      )
    )
  )
)

...