> For the complete documentation index, see [llms.txt](https://docs.fulfillmenttools.com/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fulfillmenttools.com/documentation/by-pillar/advanced-order-routing/complex-routing-with-combinatorics/ratings.md).

# Ratings

{% hint style="info" %}
If you would like to use these functionalities, [contact our Support team](https://ocfulfillment.atlassian.net/servicedesk/customer/portal/1) for more information and activation support.
{% endhint %}

Ratings determine the preferred routing option when multiple options can fulfill an order after [fences](/documentation/by-pillar/advanced-order-routing/complex-routing-with-combinatorics/fences.md) filter out facilities. Ratings never exclude a routing option. Instead, they only change the ranking order of the routing options.&#x20;

There are two types of ratings:

* [**Priority ratings**](#priority-ratings) apply first and take absolute precedence.
* [**Weighted ratings**](#weighted-ratings) score the remaining routing options and break any ties.

## Rating types

You can configure every rating as either a weighted rating or a priority rating. The choice reflects your business priorities. For example, fulfilling orders as cheaply as possible without unnecessary delays is a trade-off and should be a weighted rating. Whereas, if you never want a store to fulfill an order if a warehouse can, that is a rule and should be a priority rating.

The [Available ratings section](#available-ratings) describes the ratings themselves. A routing strategy node can combine both rating types. A rating can only be one type per routing strategy node.

### Priority ratings

Priority ratings are rules of precedence. A priority rating records only whether a routing option meets the rating. If a routing option doesn't meet the rating, it's removed from the options (if at least one option meets the rating). Use priority ratings for criteria that must always outweigh everything else.

Priority ratings are evaluated in a fixed order of importance. The routing option that performs best on the first priority rating wins, regardless of any ratings below it. A lower rating takes effect only when the higher rating ends in a tie. For example, rank by delivery speed first, and only fall back to a second criterion when two routing options are equally fast.

### Weighted ratings

Weighted ratings are trade-offs. Each weighted rating turns its result into a penalty, and the penalties are added up. A higher weighting counts for more in the total rating score. The routing option with the lowest total penalty wins.&#x20;

A routing option can still be chosen even if it has a higher score on shipping cost because it has a lower score on faster delivery which results in the lowest total penalty score.

Weighted ratings are evaluated only when priority ratings tie or aren't set.

#### Normalization of weighted ratings

Normalization ensures comparability across weighted ratings by mapping raw metric values to a normalized penalty range. It applies only to weighted ratings, because priority ratings don't produce a penalty score.

* **Parameters**: `minThreshold`, `maxThreshold`, `maxPenalty`.
* **Behavior:**
  * If the computed metric value of a routing option exactly equals `maxThreshold`, the rating assigns `maxPenalty`.
  * All ratings are uncapped, so values above `maxThreshold` produce penalties greater than `maxPenalty`, increasing proportionally with the metric value.
  * Intermediate values are mapped proportionally between the thresholds.

`minThreshold` and `maxThreshold` are fixed per rating implementation and are documented with each [available rating](#available-ratings). Only `maxPenalty` is configurable.

## Decision order

When a routing strategy node defines fences and both rating types, fulfillmenttools narrows the candidate set in three stages:

1. [**Fences**](/documentation/by-pillar/advanced-order-routing/complex-routing-with-combinatorics/fences.md) remove routing options that fail to meet a hard criterion. Routing options that pass are all valid options.
2. [**Priority ratings**](#priority-ratings) compare the remaining routing options in order of `priority`. Routing options that fail a priority rating drop out, provided at least one routing option meets it.
3. [**Weighted ratings**](#weighted-ratings) decide between the routing options that remain equivalent across all priority ratings. The routing option with the lowest total penalty is selected.

A priority rating never makes an order unroutable. If no routing option meets a priority rating, that rating is skipped, and no candidate drops out. The comparison continues with the next rating. This differs from a fence, which removes routing options even if an alternative exists.

### Use case

A retailer fulfills online orders from two central warehouses and from stores. Two business objectives are non-negotiable:

1. Orders must be fulfilled completely whenever a routing option can, because partial deliveries generate support contacts.&#x20;
2. Warehouses are preferred over stores, because store staff shouldn't be pulled away from walk-in customers.&#x20;

Cost and delivery speed are flexible objectives, the retailer accepts one additional delivery day for savings of 4.00 EUR.

These business objectives translate into the following configuration.

| Rating                                                                | Category        | Weighting        | Objective                                   |
| --------------------------------------------------------------------- | --------------- | ---------------- | ------------------------------------------- |
| [Order completeness](#order-completeness-rating)                      | Priority rating | `priority`: 1    | Fulfill the entire order from the candidate |
| [Binary toolkit rating ](#binary-toolkit-rating)for the facility type | Priority rating | `priority`: 2    | Prefer warehouses over stores               |
| [Total cost](#total-cost-cost-component-rating)                       | Weighted rating | `maxPenalty`: 50 | Reduce purchase and shipping cost           |
| [Delivery time](#delivery-time-rating)                                | Weighted rating | `maxPenalty`: 40 | Shorten the time until the order arrives    |

#### Calibrating the weighted ratings

The two weighted ratings must be weighted against each other to express the intended trade-off. Both ratings scale their metric against a fixed `maxThreshold` (see the [Normalization section](#normalization-of-weighted-ratings) for more information): 50.00 EUR for total cost and 10 days for delivery time. So, the penalty per unit follows from `maxPenalty`:

* Total cost with a `maxPenalty` of 50 produces `50 / 50.00 EUR` = 1 penalty point per Euro.
* Delivery time with a `maxPenalty` of 40 produces `40 / 10 days` = 4 penalty points per day.

One delivery day and 4.00 EUR both amount to 4 penalty points, which is exactly the trade-off the retailer intends. Adjusting the ratio means changing one `maxPenalty`: a `maxPenalty` of 20 for delivery time would halve the value of a day, so savings of 2.00 EUR would justify an additional day.

#### Routing options decision

An order is placed for 10 items. After going through the fences, four routing options are available:

<table><thead><tr><th>Routing option</th><th>Facilities</th><th>Complete order</th><th width="133.5234375">Warehouses only</th><th>Cost</th><th>Delivery time</th></tr></thead><tbody><tr><td>A</td><td>Warehouse Cologne</td><td>Yes</td><td>Yes</td><td>34.00 EUR</td><td>2.0 days</td></tr><tr><td>B</td><td>Store Munich</td><td>No, 7 of 10 items</td><td>No</td><td>15.00 EUR</td><td>1.0 day</td></tr><tr><td>C</td><td>Warehouse Erfurt</td><td>Yes</td><td>Yes</td><td>25.00 EUR</td><td>4.0 days</td></tr><tr><td>D</td><td>Warehouse Cologne, Store Bonn</td><td>Yes</td><td>No</td><td>20.00 EUR</td><td>1.5 days</td></tr></tbody></table>

The weighted ratings for total cost and delivery time produce these penalties:

| Routing option | Total cost | Delivery time | Total penalty |
| -------------- | ---------- | ------------- | ------------- |
| A              | 34.0       | 8.0           | 42.0          |
| B              | 15.0       | 4.0           | 19.0          |
| C              | 25.0       | 16.0          | 41.0          |
| D              | 20.0       | 6.0           | 26.0          |

Looking at the total penalty score alone, routing option B would win as it's the lowest. But our priority ratings change the outcome:

1. The order completeness rating is evaluated first, as it has a priority of 1.&#x20;
   * Routing options A, C, and D can fulfill the complete order, but option B can't.&#x20;
   * Since at least one option fulfills the order completely, candidate B drops out despite its low total penalty score.
2. The facility type rating is evaluated next, as it has a priority of 2.&#x20;
   * Routing options A and C are warehouses, but option D isn't.
   * Since at least one option is a warehouse, option D drops out.
3. The weighted ratings total penalty scores are now taken into account, as both routing options A and C are tied across the priority ratings.
   * Routing option C wins with a total penalty of 41.0 against 42.0.

So, even though routing option C is neither the cheapest nor the fastest, and has the second-highest total penalty, it is selected as the best routing option. This shows how the priority rating can affect the decision. Had the business type rating been configured as a weighted rating instead, routing option D would have been selected (depending on the penalties given).

The decision between routing options A and C also shows the calibration at work. Routing option C is 9.00 EUR cheaper and two days slower than routing option A. Two days are worth 8.00 EUR under the configuration we've set. So routing option C wins by 1 penalty point. If routing option C cost 26.00 EUR for delivery, then it would have the same penalty rating as routing option A. In this case, the decision would be random between the two options as they are the same.

#### Ranking of the priority ratings

In this example, both priority ratings could be satisfied by the same candidates, so their ranking doesn't affect the result. Ranking becomes decisive when no candidate satisfies all priority ratings. If the warehouses held only part of the order and complete fulfillment required a store, the configuration above would select the complete store-based candidate, because order completeness outranks the business type. Reversing the two priority values would select an incomplete warehouse-based candidate instead.

### Configuration reference

In the routing strategy node, configure weighted ratings in the `ratings` array and priority ratings in the `priorityRatings` array. Both accept a standard variant and a toolkit variant.

{% hint style="info" %}
These can also be set in the [Routing strategy section of Backoffice](/documentation/backoffice/network-view/routing.md).
{% endhint %}

| Category | Type                     | Weighting field | Rating logic                                                       |
| -------- | ------------------------ | --------------- | ------------------------------------------------------------------ |
| Weighted | `StandardRating`         | `maxPenalty`    | Selected through `implementation`, refined through `configuration` |
| Weighted | `ToolkitRating`          | `maxPenalty`    | Defined through `rule` and `comparisonRule`                        |
| Priority | `StandardPriorityRating` | `priority`      | Selected through `implementation`, refined through `configuration` |
| Priority | `ToolkitPriorityRating`  | `priority`      | Defined through `rule` and `comparisonRule`                        |

`priority` establishes the order in which ratings are evaluated. The lower the value (for example, 1), the higher in the sequence.

`maxPenalty` scales the effect of a weighted rating. A rating configured with a `maxPenalty` of 40 can shift the decision four times as much as a rating configured with a `maxPenalty` of 10. We recommend keeping `maxPenalty` at 1000 or below. A rating that requires a higher value to win isn't being weighted against the other ratings but is intended to override them, and should be configured as a priority rating instead.

#### Reading the result

Every rating result is recorded on the [sourcing option](/documentation/by-pillar/advanced-order-routing/complex-routing-with-combinatorics/sourcing-options-endpoint.md) and is available in the [decision logs](/documentation/by-pillar/advanced-order-routing/decision-logs.md).

* `ratingResults` contains one entry per weighted rating, including the `penalty` it contributed, the rating name, and the `routingStrategyNodeId` of the node that applied it.
* `totalPenalty` contains the sum across all weighted ratings, which is the value compared in stage three of the decision order.
* `priorityRatingResults` contains one entry per priority rating with `matchesPriorityMinimum`, the rating `name`, its `type` (`STANDARD` or `TOOLKIT`), and the `routingStrategyNodeId`.
* `totalRatingResults` in the execution results reports `minPenalty`, `maxPenalty`, and `avgPenalty` per weighted rating across all candidates of the run, which helps to identify ratings that never differentiate.

## Available ratings

Below are the ratings that can be used as either a priority rating or a weighted rating in a routing strategy node.

### **Geo distance rating**

* **Definition**: Prefers routing options with shorter average distance from fulfillment locations to the final recipient.
* **Thresholds:**
  * `minThreshold` = 0 km
  * `maxThreshold` = 1000 km
* **Calculation**:
  * Consider only transfers that deliver to the final receiver
  * Compute the average distance of those transfers
  * Penalty = `(averageDistance / maxThreshold) × maxPenalty`
* **Notes**: Number of items per transfer doesn't affect the distance calculation

### **Order completeness rating**

* **Definition**: Prefers options that assign a higher share of order lines within the candidate.
* **Thresholds:**
  * `minThreshold` = 0%
  * `maxThreshold` = 100%
* **Calculation**:
  * assignmentRate = `assignedItems / totalItems`
  * Penalty = `(1 − assignmentRate) × maxPenalty`
* **Notes**: This rating reflects order completeness and reduces partial fulfillment where possible.

### **Number of deliveries rating**

* **Definition**: Prefers options that minimize the number of partial deliveries to the final receiver.
* **Thresholds:**
  * `minThreshold` = 1 partial delivery
  * `maxThreshold` = 3 partial deliveries
* **Calculation**:
  * Count transfers that result in separate deliveries to the final receiver
  * Map the count to a penalty using `minThreshold` and `maxThreshold`
  * Penalty = `maxPenalty × (number of partialDeliveries − minThreshold) / (maxThreshold − minThreshold)`
* **Notes**: Fewer deliveries reduce customer complexity and operational overhead

### **Binary toolkit rating**

* **Definition**: Applies binary conditions that must be met or penalized.
* **Thresholds:**
  * `minThreshold` = condition met
  * `maxThreshold` = condition not met
* **Examples**:
  * Facility tag matches order tag (for example, `SalesChannel: Amazon`)
  * Carrier capability present for a required service
* **Calculation**:
  * For each facility in the considered sourcing path that doesn't meet the condition, the defined penalty is applied once. If the condition is not met by more than one location along the sourcing path, the defined penalty is applied multiple times to the sourcing path.

### **Total cost (cost component) rating**

* **Definition**: Evaluates the aggregated monetary cost of a sourcing option.
* **Thresholds:**
  * `minThreshold` = 0
  * `maxThreshold` = 50
* **Components**: purchase prices, shipping costs (optional components)
* **Calculation**:
  * `totalCost = sum(purchasePrices) + sum(shippingCosts)`
  * Penalty = `(totalCost / maxThreshold) × maxPenalty`
* **Notes**: All cost components must use the same currency. Missing or inconsistent cost data triggers a failure mode that applies the maximum penalty.

### **Delivery time rating**

* **Definition**: Evaluates the estimated delivery time for the final receiver to receive all partial shipments.
* **Thresholds:**
  * `minThreshold` = 0 days
  * `maxThreshold` = 10 days
* **Calculation:**

  1. **Calculate delivery time per transfer:**
     * For each transfer that delivers to the final receiver:

  ```
     deliveryTime_i = earliestPossibleDeliveryTime_i − {today}
  ```

  2. **Calculate weighted average delivery time:**
     * Weight = number of assigned items per transfer

  ```
     weightedAverageDeliveryTime = Σ(assignedItems_i × deliveryTime_i) / Σ(assignedItems_i)
  ```

  3. **Calculate penalty:**

  ```
     Penalty = (weightedAverageDeliveryTime / maxThreshold) × maxPenalty
  ```
* **Example:**

  A routing option has 3 transfers to the final receiver. Today = Day 0.

  | Transfer | Assigned items | Delivery time (days) |
  | -------- | -------------- | -------------------- |
  | T1       | 5              | 3                    |
  | T2       | 8              | 5                    |
  | T3       | 2              | 7                    |

  **Step 1: Delivery time per transfer calculation:**

  ```
  T1: 3 − 0 = 3 days
  T2: 5 − 0 = 5 days
  T3: 7 − 0 = 7 days
  ```

  **Step 2:** **Weighted average delivery time calculation:**

  ```
  weightedAverageDeliveryTime = (5×3 + 8×5 + 2×7) / (5+8+2)
                               = (15 + 40 + 14) / 15
                               = 69 / 15
                               = 4.6 days
  ```

  **Step 3: Penalty calculation:**

  ```
  Penalty = (4.6 / 10) × maxPenalty = 0.46 × maxPenalty
  ```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.fulfillmenttools.com/documentation/by-pillar/advanced-order-routing/complex-routing-with-combinatorics/ratings.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
