The DOMS Toolkit allows you to create customized routing rules by providing you with tools to evaluate the content of the orders and the network of affiliates, and make decisions accordingly.
How to define a toolkit fence
In the following we will explain in detail what each field does and how to use it. Almost all of these steps will apply to building a toolkit rating so if you're interested in how a toolkit rating works, please don't skip this section!
ToolkitFence fields in detail
Click to see what each field does
entity1
This describes the entity type for the left-hand side. In case of conditional rules this is the entity that gets evaluated first.
entity2
This describes the entity type for the right-hand side. In case of conditional rules this is the entity that gets evaluated second (or never), but more to that later..
active
Whether this fence is active or not
name
The name, has to be unique
nameLocalized
An object containing the localized names for different languages
description - [optional]
An optional description to say what the fence has to do. Although optional, it is strongly recommended that you do give it a meaningful description.
descriptionLocalized - [optional]
An object containing the localized descriptions for different languages
order
A number determining whether the fence should be executed before or after different fences. Lower numbers have precedence over higher numbers.
Defining the actual rule
To define a rule you have to pick exactly one of the following.
rule
The setup for a conditional rule. Incompatible with using the comparisonRule field. Use this if you want to compare each side against a fixed value (e.g. order line item quantity > 3)
comparisonRule
The setup for a comparison rule. Incompatible with using the rule field. Use this if you want to compare the left side directly with the right side. (e.g. order brand == facility brand)
How to define a conditional rule
Conditional rules test a certain property of an entity (Order, Facility) against a pre-defined static value.
Show how to do it!
operator
What the positive evaluation of the left hand side means for the execution of the fence as a whole. For now, there is only the equality-operator.
leftPart - ToolkitRulePart
The first part that is evaluated. Its evaluation result determines whether the right part gets evaluated.
rightPart - ToolkitRulePart
The second part that is evaluated if the first evaluation passes. Its evaluation result determines the final outcome of the fence.
How to define a ToolkitRulePart
A ToolkitRulePart consists of an array of predicates and a connector that determines how they are connected together.
predicateConnector
OR will make the rule part true if any of the predicates evaluate to true. AND will only make the expression true if all predicates evaluate to true.
predicates
An array of one or more ToolkitPredicates. A predicate consists of the following fields:
propertyPath
entityOperator
expectedValue
transformation (optional)
transformationArgs (optional, depends on transformation used)
How to define a ToolkitPredicate
You have to define at least one predicate. In the following we'll explain what each field is used for.
The operators determine how a property of an entity should be compared against a given value. They can be divided up into two groups: Single value entity operators and array entity operators.
Single value entity operators
These operators can only be used for single values, not for arrays, such as VALUE_EQUALS
VALUE_EQUALS -> actual value equals expected value (e.g. 2 == 2)
VALUE_NOT_EQUALS -> actual value unequals expected value (e.g. 2 != 3)
VALUE_CONTAINS -> actual value contains expected value (e.g. "HELLO WORLD" contains "HELLO")
VALUE_NOT_CONTAINS -> actual value does not contain expected value (e.g. "HELLO WORLD" does not contain "HI")
LESS_THAN -> actual value less than expected value (e.g. 2 < 3)
LESS_EQUALS -> actual value less than or equals expected value (e.g. 2 <= 3, 3 <= 3)
GREATER_THAN -> actual value greater than expected value (e.g. 3 > 2)
GREATER_EQUALS -> actual value greater than or equals expected value (e.g. 3 >= 2, 2 >= 2)
Array entity operators
These operators can only be used for arrays, not for single values. They can be grouped into three categories: ANY, EVERY, NONE. Within each category, either any, every or no value has meet the criteria. The condition may be anything of the aforementioned single value entity operators. Here are some examples on how this may look:
ANY_VALUE_EQUALS
EVERY_VALUE_GREATER_EQUALS (which is equivalent to NO_VALUE_LESS_THAN)
NO_VALUE_CONTAINS (which is equivalent to EVERY_VALUE_NOT_CONTAINS)
ANY, EVERY and NONE..
ANY
Requires that at least one element of the array satisfies the following condition. It is not required that more than one element match but if the array is empty this condition will NOT be fulfilled.
EVERY
Requires that every element of the array satisfies the following condition. If the array is empty, this condition will be true because there is no element that does not fulfill the condition.
NONE
Requires that no element satisfies the following condition, meaning that every element does not satisfy the condition. This condition will always be fulfilled, unless there is an element that satisfies the condition, including for empty arrays.
Single value operators vs array operators
If your actual value is an array, you have to use an array operator. When is that the case? - If you use a wildcard operator (* or ?()) you get an array of elements (such as $.orderLineItems[*]). However, if you do use a transformer (such as COUNT to get the number of elements or SUM to add the quantities of each OrderLineItem), then you will get a single value and must use a single value operator.
The expected value the property should match Examples:
2 - a number
"WAREHOUSE" - a string
"2024-02-19T16:16:38.107Z" - a date-string
How to define a comparison rule
Comparison rules are very different to conditional rules in that a certain property of the left-hand side (Order) can be compared to a certain property of the right-hand side (Facility).
An example of this could be an order with articles of a certain brand demanding a facility tagged with the same brand.
Show how to do it!
predicateConnector
OR will make the rule part true if any of the predicates evaluate to true. AND will only make the expression true if all predicates evaluate to true.
$.tenantOrderId -> $ refers to the order entity while tenantOrderId refers to the tenantOrderId. The . indicates that tenantOrderId is a nested property of Order
$.orderLineItems[*].article -> Gets the articles from all orderLineItems, resulting in an array
$.orderLineItems[?(@.quantity > 3)].article -> Same as last time but only gets the articles from OrderLineItems with a quantity greater than 3
To sum up we can say the following: nested properties can be accessed using the . while nested array properties such as orderLineItems, tags or stickers can be accessed using the array brackets [ and ] with an asterisk * to indicate a wildcard.
If you want to be more specific, instead of a wildcard * you can use the ?() syntax. Inside the parentheses (, ) you can then define an arbitrary expression using the @ symbol to refer to the entity being tested.
If you are for instance on an OrderLineItem such as in our example, the @ refers to the OrderLineItem that is just being evaluated. You can then access all of its nested attributes and test for an arbitrary condition. This could be the quantity as in our previous example but it could also be more complex such as the following expression:
This gets us the articles of all OrderLineItems that have at least one tag of type color with the value red.
Note: It is recommended though to keep your expressions as simple as they can possibly be without compromising on their functionality to avoid mistakes leading to misbehaviour of the fence/rating.
Intro to transformers
Transformers allow to modify the input of a ToolkitPredicate before it gets evaluated. This can serve multiple purposes, maybe you only want to look at the number of elements, get the total price of all OrderLineItems or just want to consider the beginning of the tenantOrderId.
These are the transformations currently available in the platform.
SUM : sums the number values of each element - requires an array of elements
COUNT : counts the number of elements - requires an array of elements
SUBSTRING : gets a substring of a value, requires start and end parameters
LAST: gets the last n chars of a string - required length parameter
Applies to orders where any OrderLineItem has a tenantArticleId that ends with "Christmas Special"
Examples:
Scenario 1, routing orders to a facility located in the same area as the customer.
In this scenario we received the task of routing orders based on the user's location. Our customer has a very specific use case, where the order from customers located in the zip codes 50xxx and 51xxx should be routed to facilities located in the same areas (both areas are valid regardless of which zip is used).
The first step would be to create a Fence in our system by using the API. We use the API endpoint /api/configurations/routing/toolkit/fences like in the following example:
Orders from zip code areas beginning with 50 or 51 must be routed to facilities in zip code areas 50 or 51.
Some details in the example:
The Fence rule consists of two parts, the left part processes the information contained in the order (entity1), and the right part processes the information contained in the facility (entity2). Left part and right part are connected with the operator EQUALS, which means that if the result of evaluating the left part is true then the result of evaluating the right part has to evaluate to true, and analogously when the left side evaluates to false
The left part consists of two predicates connected with the OR predicate evaluator. The predicates are similar, as they take the value of the postalCode property of the consumer's address.
Both predicates have a transformation, which means that after getting the value of the postalCode, and before evaluating the predicate, this postal code is transformed by using the SUBSTRING transformation. In this case, the first two digits are extracted.
Then, for this extracted values, they are compared by using the entity operator, in this case VALUE_EQUALS. This would be interpreted as, "if the extracted value equals the expected value, then this predicate is evalued to true".
After evaluating the two left-side predicates individually, they are evaluated as a whole using the predicate operator OR.
Analogously, the right-hand side of the rule is evaluated. The result of evaluating the right side and the left side are evaluated again by using the rule operator EQUALS.
Suppose we have an incoming order for an user that is located in an area with postal code 51379, and the fence is evaluating a Facility located in an area with postal code 51355. When evaluating this combination from order and facility with the fence specified above would be:
and the facility will be retained as candidate for the routing, because the fence evaluated to true.
Scenario 2, directly compare values of the entity on the left with the entity on the right.
In this scenario we will ignore the evaluation of the left side and the right side of the rule, to perform a direct comparison of values of the entity on the left with the right entity, to handle the following scenario:
Orders with order line items containing a tag with id "CATEGORY" should be routed to facilities that have the same category. Available categories are "DANGEROUS_GOODS" and "SAFE_GOODS".
We create the fence in our system by using the following request:
Suppose we have an incoming order with an order line item that has the tag {id:"CATEGORY",value:"DANGEROUS_GOODS"}, and we have to facilities, (1) the first which has the tag SAFE_GOODS and (2) the second which has the tag DANGEROUS_GOODS
When evaluating the Fence for the (1) first facility, the following will happen:
the fence will consider the order and take the value of the orderLineItems.tags[*].value, in this case, the value "[DANGEROUS_GOODS]".
the fence will consider the facility take the value of the tag "tags[*].value", in this case for facility (1) it is "[SAFE_GOODS]".
then it will apply the entity operator "RIGHT_CONTAINS_LEFT". Because the value "[SAFE_GOODS]" does not contains "[DANGEROUS GOODS]", the fence will evaluate to false, and this facility will be discarded.
When evaluating the Fence for the (2) second facility, the following will happen:
the fence will consider the order and take the value of the orderLineItems.tags[*].value, in this case, the value "[DANGEROUS_GOODS]".
the fence will consider the facility take the value of the tag "tags[*].value", in this case for facility (1) it is "[DANGEROUS_GOODS]".
then it will apply the entity operator "RIGHT_CONTAINS_LEFT". Because the value "[DANGEROUS_GOODS]" is contained in "[DANGEROUS GOODS]", the fence will evaluate to true, and this facility will be retained as candidate.
Note:In case the order has many order line items containing many different tags, then all of them will be evaluated and compared with the values from the right part.
Reference
Find the full specification of the toolkit configuration endpoints in our fulfillmenttools API