# What the Planner Decides

Most planning stacks split one decision over many tools: a route optimiser, a driving-time check, an ADR spreadsheet, a load planner, a dock booking screen, an empties desk, a carrier portal and an intermodal planner. Each one is right on its own, and the combined plan still fails at the dock, at the terminal or at the trailer door.

Transportial decides all of it together, in one search, so the plan it hands over can actually run. The same engine plans inside the product (the optimizer and map planning) and on the API (`POST /vrp/solve`).

This page lists what that one search decides, which rule guards each decision, and where you set it on the platform and in the API. The request fields are in the [Multi-Modal VRP example](/examples/multimodal-vrp).

## Every resource per tour

A refrigerated consignment leaves Venlo at 06:00. The tractor is in Venlo, the only free reefer trailer stands in Tilburg, and the driver who is free has already driven 41 hours this week. One option is a carrier who serves Venlo to Lyon, but only if the order is in before 14:00.

**What the planner decides.** Each tour gets a vehicle, a driver and, when it needs one, a trailer or other equipment. Instead of your own fleet, a tour can also run on a carrier's capacity contract.

- **Drivers.** Each driver has one EU 561/2006 driving-time clock across all their tours. The clock starts from what they have already driven, so breaks, daily and weekly rests and weekly limits are planned over the whole plan, not per tour. Availability, ADR certificate, licence, tacho card, certificates, bans and languages count as well.
- **Trailers.** A trailer is picked from the pool by type, attributes, temperature and room, and the tour fetches it from where it stands. The drive to it counts in the tour's time and cost.
- **Carriers.** A capacity contract is priced with your own pricing modules. It is limited by its lanes, service areas, operating hours, lead time and order cut-off, and by the goods it accepts.


| Rule codes | Default (API / platform) |
|  --- | --- |
| `availability`, `operational`, `driving_time`, `chauffeur_ban`, `chauffeur_attribute` | block / block |
| `adr_chauffeur`, `certificate`, `licence` | block / warn |
| `equipment_type`, `temperature`, `vehicle_type`, `vehicle_attribute` | block / block |
| `contract_lane`, `contract_validity`, `contract_capability`, `carrier_is_customer` | block / block |


**Where you set it.** On the platform, these come from your vehicles, chauffeurs, transport equipment and capacity contracts, and their rule policy from the planning settings (`planningSettings.optimizerRules`). On the API, send `vehicles`, `chauffeurs`, `transportEquipments` and `capacityContracts`. See [Add drivers](/examples/multimodal-vrp#add-drivers), [Add trailers and equipment](/examples/multimodal-vrp#add-trailers-and-equipment) and [Add capacity contracts](/examples/multimodal-vrp#add-capacity-contracts).

## Every mode

Forty containers must go from Rotterdam to Milan by Thursday. A train leaves Rotterdam on Tuesday at 18:00 with 28 free slots and a cut-off three hours before departure. Trucks can do the whole distance, or bring the containers to the terminal and collect them in Milan.

**What the planner decides.** Road, rail, inland waterway, sea and air are planned in one plan. A consignment can also ride a scheduled service door to door: by truck to the terminal, by train, barge, ship or aircraft, and by truck to the door. Cut-offs and capacity per departure count. The road legs at both ends follow the same rules as every other tour.

| Rule | Default |
|  --- | --- |
| Transport mode of the vehicle and the location | always enforced |
| Cut-off and capacity per departure | always enforced |


**Where you set it.** On the platform, the optimizer plans over your stored schedule routes (`useScheduleRoutes`, on by default). On the API, send `scheduleRoutes`, or set `options.chains.useStoredScheduleRoutes`. See [Add scheduled services](/examples/multimodal-vrp#add-scheduled-services).

## The load

A reefer leaves the depot with frozen fish in the front and chilled dairy at the rear. A drum of pesticide (ADR class 6.1) is on the same order list for the same customer.

**What the planner decides.**

- **Load plans.** Every proposed trip gets a 3D load plan that the planner can open before saving, in the optimizer and in map planning, whatever engine planned the trip. It shows the stop-by-stop loading order. When the trip is saved, the plan stays with it.
- **Compartments.** Multi-temperature reefers, tank compartments and food-grade holds are modelled. A consignment only goes into a compartment that fits its temperature, its dangerous goods and its foodstuff status. Each compartment's capacity is checked against what is on board at the same moment. In the 3D load plan, a bulkhead is a wall.
- **ADR.** ADR 1.1.3.6 points add up per tour, over everything on board at the same moment. Incompatible classes are kept apart. Foodstuffs never share a compartment with toxic or infectious classes (6.1 and 6.2 by default; ADR 7.5.4).


| Rule codes | Default (API / platform) |
|  --- | --- |
| `compartment` | block / block |
| `adr_vehicle`, `adr_equipment`, `adr_mixed_loading` | block / warn |
| 3D packing | set by load planning: `off`, `verify` or `enforce` |


**Where you set it.** On the platform, compartments are set on vehicles and transport equipment. Foodstuff and the required temperature are set on the goods. The Load planning setting (Off, Verify, Enforce) needs the `stacking` module. On the API, send `vehicles[].compartments`, `transportEquipments[].compartments`, `consignments[].foodstuff`, `rules.adr` and `options.loadPlanning`. See [Add compartments](/examples/multimodal-vrp#add-compartments), [Add dangerous goods](/examples/multimodal-vrp#add-dangerous-goods) and [Calculating a 3D Load Plan](/examples/3d-load-planning).

## The places

A distribution centre has two loading docks. Between 07:00 and 08:30 one of them is booked by a supplier's timeslot, and three of your trucks are planned to arrive around 07:30.

**What the planner decides.** Docks, ramps, berths and rail sidings have a capacity. The planner never plans more vehicles at once than a site can handle. Other trips already at the site and booked dock timeslots count as well. A vehicle that finds every dock taken waits for a free one, and that wait is real waiting time: it counts in the tour time and in the driver's day. When a tour with slack can wait at less cost, it gives way to a tour that cannot. The method is after Bombelli & Fazi (2022), see [Research it builds on](#research-it-builds-on).

| Rule code | Default (API / platform) |
|  --- | --- |
| `dock_capacity` | block / block |


**Where you set it.** On the platform, the capacity of a location is the sum of `maxConcurrentOperations` over its operational loading docks. Stops of other trips and requested or confirmed timeslot bookings count. The optimizer response shows each wait in `dockWaitByStop`. On the API, send `locations[].docks` and read `dockWaitSeconds` and `dockStart` per stop. See [Add docks and berths](/examples/multimodal-vrp#add-docks-and-berths).

## The equipment between tours

An import container is delivered to a warehouse in Tilburg and is empty once unloaded. Ten kilometres away, an exporter in Waalwijk needs an empty container of the same type that afternoon. The nearest container depot is in Moerdijk.

**What the planner decides.** Empty containers, swap bodies and pool trailers are shared by all tours. A consignment can need a unit at its pickup, as an export needs an empty container. It can also free one after its delivery, as an import's container is empty once unloaded.

- **Sources.** The planner reuses a freed unit on the same tour (a street-turn), or takes one from stock or a depot. A fetch from stock or a depot is priced as a detour on the leg into the pickup.
- **Stock.** Stock is tracked over time across all tours, and is never planned below zero.
- **Method.** The method is after Fazi, Choudhary & Dong (2023), see [Research it builds on](#research-it-builds-on).


| Rule code | Default (API / platform) |
|  --- | --- |
| `equipment_stock` | block / block |


**Where you set it.** On the platform, a location's equipment stock is set in its site details. A consignment's need or release is its `equipmentFlow`. The optimizer response shows each source in `equipmentByTrip` and the stock over time in `equipmentStock`. On the API, send the top-level `equipmentStock` and `consignments[].equipmentFlow`. See [Add empty containers and pools](/examples/multimodal-vrp#add-empty-containers-and-pools).

## Your definition of "best"

One planner wants every consignment planned first, then the fewest tours, and only then the lowest cost. Another weighs CO2e and cost together, because a customer pays for the lower emissions.

**What the planner decides.** You set a custom objective: a weight on each of distance, driving time, tour time, waiting time, money, CO2e, tours, resources, lateness and unplanned consignments. Priority levels can rank them, for example first plan everything, then the fewest tours, then cost. A higher level always outweighs everything below it. The named objectives (distance, time, CO2, cost, fewest assets) are presets of the same weights.

The result explains itself: the objective per component, per tour and in total.

**Where you set it.** On the platform, choose Custom as the optimisation objective in the optimizer settings and set the default weights. A run can override them in the optimizer. The response holds `objective` and `objectiveByTrip`. On the API, send `options.objective: "custom"` with `options.objectiveWeights` and `options.objectivePriorities`, and read `routes[].objective` and `objective`. See [Choose what "best" means](/examples/multimodal-vrp#choose-what-best-means).

## Transparency and control

A planner sees that one consignment stayed unplanned and wants to know why. A tenant that has not entered all driver licences yet still wants a plan.

**What the planner decides.**

- Every rule can be `block`, `warn` or `off`.
- A warned rule costs something in the plan, and each tour lists what it breaks in `softViolations`.
- Every consignment left out gets a `reason`, and `details` counts how many resources each rule refused.


**Where you set it.** On the platform, set the policy per rule in the planning settings (`planningSettings.optimizerRules`). On the API, send `rules.policy` and `rules.softWeights`. See [How strict each rule is](/examples/multimodal-vrp#how-strict-each-rule-is).

## Rule codes

Every rule below can be set to `block` (a violation makes the plan infeasible), `warn` (it costs a penalty and is reported on the tour) or `off`. The platform defaults to `warn` for data that most tenants have not filled in yet, so an upgrade never empties a plan.

| Rule code | Default API | Default platform |
|  --- | --- | --- |
| `operational` | block | block |
| `availability` | block | block |
| `adr_chauffeur` | block | warn |
| `adr_vehicle` | block | warn |
| `adr_equipment` | block | warn |
| `adr_mixed_loading` | block | warn |
| `certificate` | block | warn |
| `licence` | block | warn |
| `chauffeur_ban` | block | block |
| `chauffeur_attribute` | block | block |
| `vehicle_attribute` | block | block |
| `vehicle_type` | block | block |
| `fuel_type` | block | block |
| `emission_standard` | block | block |
| `equipment_type` | block | block |
| `location_access` | block | warn |
| `contract_lane` | block | block |
| `contract_validity` | block | block |
| `contract_capability` | block | block |
| `carrier_is_customer` | block | block |
| `driving_time` | block | block |
| `tour_limit` | block | block |
| `temperature` | block | block |
| `compartment` | block | block |
| `equipment_stock` | block | block |
| `dock_capacity` | block | block |


`capacity`, `trip_status` and `existing_trip` always block and cannot be set. The API rejects them as policy keys, and the platform settings ignore them.

## Where it runs

| Where | What it does |
|  --- | --- |
| Optimizer (`POST /trip/optimizer`) | Plans your consignments with your vehicles, chauffeurs, equipment and contracts from the platform. It proposes trips and saves nothing, unless `planningSettings.autoApplyOptimizerPlans` is on. See [Trips & Planning](/guides/trips-and-planning). |
| Map planning | Plans from the map with the same engine. Each proposed trip has a Load plan button. |
| `POST /vrp/solve` | Takes a complete problem in the request and stores nothing. Nothing in it has to exist in Transportial. Permission `calculate:vrp`. See [Multi-Modal VRP](/examples/multimodal-vrp). |
| `POST /vrp/solve/async` | The same request as a job, for searches of up to 120 seconds. Poll `GET /worker-job/{id}`. |


Everything on this page is decided by the built-in Transportial engine. When your platform uses another optimizer engine, that engine plans the routes, and its proposals still get load plans.

## Research it builds on

Two parts of the planner build on published research. The results below are the papers' own, from their experiments, not measurements of Transportial.

- **Docks.** A. Bombelli & S. Fazi (2022), "The ground handler dock capacitated pickup and delivery problem with time windows: A collaborative framework for air cargo operations", *Transportation Research Part E* 159, 102603. In the paper's experiments, uncoordinated plans queued for docks up to about 3 hours, and some became infeasible. Coordination cut travel time by 2–19%. Our adaptation keeps every plan the search holds dock-feasible. Instead of the paper's penalised overbooking, a tour waits, or the tour that can shift gives way.
- **Empties.** S. Fazi, S.K. Choudhary & J.X. Dong (2023), "The multi-trip container drayage problem with synchronization for efficient empty containers re-usage", *European Journal of Operational Research* 310(1), 343–359. In the paper's real cases near Tilburg and 's-Hertogenbosch, its plans were 25–43% cheaper than the planners' own, with fewer trips. Our adaptation:
  - runs inside our large neighbourhood search instead of branch-and-price;
  - checks stock only when a unit is taken (the paper's "critical times");
  - prices depot fetches as detours ("bypass").


## What it does not do yet

- A consignment is not split over vehicles. It travels on one vehicle, or on one chain of legs.
- Compartment lengths are fixed. There is no movable bulkhead yet.
- `/vrp/solve` checks that every consignment has a matching compartment, but does not report which compartment it got. A load plan reports it per unit in `placements[].compartmentId`.
- Separate loading and unloading docks are not modelled. All operational docks of a site count together.
- Equipment types match exactly. There is no equivalence between ISO size-type groups.
- Yard capacity is not modelled. A return is never refused because a yard is full.
- Stock and depot visits do not become explicit stops on a saved trip. They count in the trip's times and distance only.
- `equipmentStock` and `scheduleRoutes` cannot be combined in one request.
- A trailer from the pool stays with one vehicle for the whole plan. Handing it over to another vehicle is not modelled, and neither is a chassis pool shared between vehicles.
- Without your own distance matrix, distances are straight-line estimates, and `warnings` says so.