Courier shifts and routes
Vehicle or courier shifts
You can define one or more work shifts for a vehicle. Use the vehicle.shifts
field to define the shifts.
A shift is a time range when the vehicle is allowed to operate. Shift time includes the loading time at the depot, travel time, and handling time at order locations, as well as unloading time at the depot (if the vehicle returns to the depot).
To use one or more shifts in Excel, define the following fields:
-
shifts.0.time_window
: A soft time window corresponding to the vehicle's operating hours. -
shifts.0.hard_window
: Flag indicating a hard time window. If the time window is hard, the algorithm can't go outside the specified interval. -
shifts.0.service_duration_s
: Time between shifts (in seconds). For example, it can be the time needed to replace the driver or exchange documents.
You can also restrict the maximum shift duration (shifts.N.max_duration_s
and shifts.N.hard_max_duration_s
).
In the case of a soft time window, you can set additional penalties:
-
For time window violation:
shifts.0.penalty.out_of_time.fixed
: A fixed penalty for one-time violation of the shift's time window.shifts.0.penalty.out_of_time.minute
: A penalty per minute of the shift's time window violation.
-
For early departure:
shifts.0.penalty.early.fixed
: A fixed penalty for one-time start of work before the shift's time window.shifts.0.penalty.early.minute
: A penalty per minute of work start before the shift's time window.
-
For late arrival:
shifts.0.penalty.late.fixed
: A fixed penalty for one-time work completion later than the shift's time window.shifts.0.penalty.late.minute
: A penalty per minute of work end after the shift's time window.
To limit permissible violations of the shift's time window, you can set a hard window around a soft one.
Start, end, and duration of shift
Let's say a shift's time_window
is from 9:00 to 18:00. The properties used are hard time window (hard_window
), maximum shift duration (max_duration_s
), and flexible start time from the depot (depot.flexible_start_time
).
How to plan courier's work |
Parameters |
Set a work interval that will preferably last no longer than 6 hours, strictly between 9:00 and 18:00 |
|
Start the work strictly at 9:00, preferably end it before 15:00 and strictly not later than 18:00 (this parameter combination corresponds to |
|
Set a work interval that will preferably last no longer than 6 hours, preferably between 9:00 and 18:00 |
|
Start the work strictly at 9:00, preferably end it before 15:00 and preferably not later than 18:00 (this parameter combination corresponds to |
|
You may start work later than 9:00, finish preferably before 18:00, preferably work no longer than 10 hours |
|
Start the work strictly at 9:00, finish preferably before 18:00, but preferably no later than 19:00 (this combination of parameters corresponds to |
|
With this combination of parameters, you won't be able to plan the solution |
|
Note
Note that to define multiple shifts, you need multiple Excel fields with an increasing numeric index.
For example. shifts.0.time_window
refers to the first shift, and shifts.1.time_window
refers to the second one.
Example
The request to RouteQ includes one vehicle with three shifts (morning, afternoon, and evening) and three orders, of which one order has a delivery time in the morning, and two others in the evening.
Note
Note that the RouteQ response will only use the morning and evening shifts, since there are no delivery orders for the day shift.
API request (JSON) ⋅ API response ⋅ View on map
Multiple courier runs per day
By default, a courier starts and ends their route at a depot, that is they complete exactly one route during a working day (shift).
If a courier can return to the depot anytime during the work shift to reload additional orders, you can limit the number of runs using one of the parameters:
-
vehicle.shifts.N.max_runs
: The maximum number of runs per shift. The default is 1. -
vehicle.max_runs
: The maximum number of runs for all the courier's shifts summed up (for example, the courier can perform all the runs in one shift). The number of courier runs can be lower than the number of shifts. The default is 1. -
If none of the parameters are specified, the courier can make as many runs as they have shifts (one run per shift).
Note
You can use vehicle.max_runs
or vehicle.shifts.N.max_runs
in the planning task, but not both parameters at the same time.
At least one shift should be set for the courier.
Example
The example below uses one vehicle with the limited carrying capacity of 2 tons and three orders with a weight of 0.8 tons. The vehicle is allowed to do two runs. This means that after completing two orders, the vehicle can return to the depot to pick up and deliver another order.
API request (JSON) ⋅ API response ⋅ View on map
Break before the last run
The break is set using the vehicle.extra_wait_before_last_run_s
object. Mind the following:
-
The break is scheduled before the last run in the shift.
-
The start of the last run is shifted by the specified break time.
Parameter |
Value |
Example |
|
Break duration in seconds. |
Break time is 45 minutes (2700 seconds). |
Example
A courier has two scheduled runs. A one-hour break and a 45-minute break are scheduled between these runs (extra_wait_before_last_run_s
= 2700).
As a result, two breaks are scheduled:
- 60 minutes in the first run.
- 45 minutes before the start of the second run. As a result, the second run starts 45 minutes later.
API request (JSON) ⋅ API response ⋅ View on map
Limit the number of stops per shift
When planning a route, you can specify the minimum and maximum number of stops in the route. This can be useful, for example, to limit the maximum courier workload per day or, on the contrary, make sure that the courier gets on the route with a certain minimum number of stops.
For this, RouteQ uses the minimal_stops
and maximal_stops
shift parameters. You can set the number of stops for each courier.
The restriction is not strict and may be violated. If the limit on the minimum number of stops is violated, a penalty applies. The penalty is specified in the following shift fields: penalty.stop_lack.fixed
(for making fewer stops than the minimum number) and penalty.stop_lack.per_stop
(for each stop missing under the minimum number).
In the same way, if the limit on the maximum number of stops is violated, a penalty applies. The penalty is specified in the following shift fields: penalty.stop_excess.fixed
(for making more stops than the maximum number) and penalty.stop_excess.per_stop
(for each stop above the maximum number).
Note
Usually when you restrict the number of stops, the solution metrics degrade since the solution favors restrictions at the cost of the optimal route.
Unique stops
When using the minimal_stops
parameter, orders with matching coordinates are considered different stops if they aren't consecutive in the route. For the minimal_stops
condition to be fulfilled, the algorithm may plan the route to have the courier visit the same address multiple times.
To avoid repeat visits, use the minimal_unique_stops
parameter and penalty.unique_stop_lack.fixed
and penalty.unique_stop_lack.per_stop
penalties instead of minimal_stops
.
The minimal_unique_stops
parameter considers only orders with unique coordinates, so there won't be any repeat visits planned for the courier. However, orders to the same address may be assigned to different couriers, because such stops are considered different in this case, and the algorithm can use this to enforce the minimal_unique_stops
limit.
It makes sense to use the minimal_unique_stops
limit if the route has a lot of multi-orders.
Stops for non-active couriers
By default, the minimal_stops
and minimal_unique_stops
limits apply to all assigned couriers. When you specify more couriers than necessary, and the penalty.stop_lack
or penalty.unique_stop_lack
penalty value is too high, this can result in suboptimal planning.
To set the minimum number of stops in a shift and build an effective route at the same time, use the option ignore_min_stops_for_unused
(see Counting the minimum number of stops for active vehicles only).
Example 1
In the example, the route is built for 3 couriers who deliver to 15 points. Since there are no restrictions for the route, the route is built the usual way.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
This example has the same conditions as example 1, but sets the limit of 4 minimum stops per courier. As a result, the routes changed so that each courier visited at least 4 delivery locations.
API request (JSON) ⋅ API response ⋅ View on map
Example 3
This example has the same conditions as example 1, but sets the limit of 5 maximum stops per courier. As a result, orders are distributed evenly among all couriers, even though several orders are located next to each other and can be completed by one courier.
API request (JSON) ⋅ API response ⋅ View on map
Maximum shift duration
Sometimes you may need to extend the courier's working hours. But you also need to limit the maximum shift length and set the window you want to fit into. For example, there's a specific 14-hour time window during which couriers can make deliveries: from 8:00 to 22:00. However, the duration of each courier's shift must not exceed 10 hours (max_duration_s
), and there should be no more than 8 working hours per shift (max_working_duration_s
).
RouteQ helps you schedule shifts with hard time windows and preferred lengths. In this case, the courier's shift length doesn't affect the work start time. It only affects the time that they spend completing orders.
The maximum shift length can be set as a soft restriction shifts.N.max_duration_s
that can be violated for a penalty or as a hard restriction shifts.N.hard_max_duration_s
that can't be violated:
-
When planning with a soft restriction, the workload that doesn't fit into one courier's preferred shift is distributed among others or to the same courier, but with penalties for violating the shift length. All orders that can't be completed in
max_duration_s
are added to the route with the penalty specified in theshift.penalty.late
field (if applicable) or in theshift.penalty.out_of_time
field all other times. The total route penalty for violating the maximum shift length is shown in the API response in theovertime_penalty
field. If the estimated order cost including penalties is higher than the cost of completing the order by another courier, the order is handed over to another courier. -
When planning with a hard restriction, the courier's shift never exceeds the
hard_max_duration_s
value.
The maximum working time per shift can also be defined by a soft restriction shifts.N.max_working_duration_s
or hard restriction shifts.N.hard_max_working_duration_s
.
-
If the courier violates a soft restriction, the workload is redistributed to other couriers or given back to the same courier, but with penalties. Orders that are not completed within the shift's working time
max_working_duration_s
will be added to the route with a penaltyworking_overtime
:-
shifts.N.penalty.working_overtime.fixed
: Penalty for exceeding the maximum working time per shift. -
shifts.N.penalty.working_overtime.minute
: Penalty for each minute of such overtime.
If the penalty value isn't specified, the penalty for violating the time window
shift.penalty.out_of_time
will be applied to the orders. You can find the total penalty amount in theworking_overtime_penalty
field of the API response. The order will be transferred to another courier if the estimated cost of the order (including penalties) exceeds the cost of completing the order with another courier. -
-
When planning with a hard restriction, the maximum duration of working time per shift never exceeds the value specified in
hard_max_working_duration_s
.
If you use both soft and hard restrictions for max_duration_s
and max_working_duration_s
, the hard one can't be lower than the soft one. By default, max_duration_s
and max_working_duration_s
are set to 2 days, while hard_max_duration_s
and hard_max_working_duration_s
are set to 30 days.
Example 1
Let's say the shift window shifts.N.time_window
is from 8:00 to 23:00. By default, routes will be built to use the minimum number of couriers.
If you specify max_duration_s
= 14400, the load is distributed between couriers, and each of them works for approximately 4 hours (the shift length is set in seconds). Some couriers may still work more than 4 hours, because it's less costly to pay for extra hours than to hand over the order to another courier.
As a result of planning, orders are distributed among three couriers. One courier's shift went over 4 hours. The penalty amount is shown in the API response in the overtime_penalty
field.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
The same as in Example 1, but with hard restrictions on the maximum shift length: hard_max_duration_s
= 14400. When planning, all couriers are given shifts that are 4 hours or less. As a result, the number of couriers increased to 4.
API request (JSON) ⋅ API response ⋅ View on map
Example 3
Three couriers are delivering 26 orders. The shift window shifts.N.time_window
is 8:00 to 23:00. Penalties are set for exceeding the maximum working time per shift: shifts.N.penalty.working_overtime.fixed
for overtime, and shifts.N.penalty.working_overtime.minute
for overtime per minute.
If you specify shifts.0.max_working_duration_s
= 14,400, each courier will work for approximately 4 hours. The restriction is soft, and one courier worked overtime.
As a result of the planning, all orders are distributed among couriers, and the total penalty amount for overtime is indicated in the field working_overtime_penalty
of the API response.
API request (JSON) ⋅ API response ⋅ View on map
Example 4
Same as in Example 3, but with a hard restriction on the maximum working time per shift shifts.N.hard_max_working_duration_s
= 14,400.
As a result of planning, all couriers are busy and work for a maximum of 4 hours. However, one of the orders remains unassigned.
API request (JSON) ⋅ API response ⋅ View on map
Maximum mileage per shift
In some cases, you need to limit the mileage per vehicle. For example:
-
The planning includes hired cars, and their rate implies limited mileage.
-
You can assign specific vehicles for far-away points. Then you can limit the mileage for other vehicles.
The shifts.max_mileage_km
parameter determines the maximum vehicle mileage per shift. The entire mileage is taken into account, including:
-
Traveling from the depot or starting point to the first point from the order list.
-
Returning to the depot or end point of the route from the last point in the order list.
The restriction is soft: the algorithm may violate it. Penalties are set in the fields:
-
shifts.penalty.max_mileage.fixed
: A fixed penalty for exceeding the maximum mileage (1000 by default). -
shifts.penalty.max_mileage.km
: A penalty per each kilometer in excess of the maximum mileage (100 by default).
If the maximum mileage per run is critical to you, and the vehicle can do several runs per shift, you can create multiple identical shifts (see Example 2).
If the courier uses public transit, only the walking part of the route is returned as the mileage.
Example 1
The mileage limit per courier is 50 km, with a big penalty for violating this restriction. In the result, the planned mileage on any route doesn't exceed 50 km.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
There's one courier and each of their runs should be no more than 50 km. To achieve this, the courier has several shifts with the same time windows and mileage restrictions. The result includes 3 routes, each one less than 50 km long.
API request (JSON) ⋅ API response ⋅ View on map
Limit the mileage and number of stops per run
Sometimes you might need to restrict the mileage and number of stops for a specific run in the route.
To set a mileage limit, go to the Vehicles sheet and specify the maximum allowed mileage per run in kilometers in the shifts.N.run_restrictions.max_mileage_km.value
field. If necessary, add penalties for exceeding the limit using the following fields:
-
shifts.N.run_restrictions.max_mileage_km.penalty.fixed
: The penalty for exceeding the maximum mileage (defaults to 1000). -
shifts.N.run_restrictions.max_mileage_km.penalty.km
: The penalty for each kilometer in excess of the maximum mileage (defaults to 100).
To limit the number of stops in the run, specify values for the following parameters on the Vehicles sheet:
-
shifts.N.run_restrictions.maximal_stops.value
: The maximum number of stops. -
shifts.N.run_restrictions.minimal_stops.value
: The minimum number of stops.
Penalties can be imposed for violating the limit on the number of stops. If necessary, specify penalty values in the following fields:
-
shifts.N.run_restrictions.maximal_stops.penalty.fixed
: The penalty for exceeding the maximum number of stops (defaults to 1000). -
shifts.N.run_restrictions.maximal_stops.penalty.per_stop
: The penalty for each extra stop (defaults to 100). -
shifts.N.run_restrictions.minimal_stops.penalty.fixed
: The penalty for violating the minimum number of stops (defaults to 1000). -
shifts.N.run_restrictions.minimal_stops.penalty.per_stop
: The penalty for each missing stop (defaults to 100).
Example 1
A courier must deliver 10 orders. There are no restrictions on the mileage and number of stops in the run.
As a result, the courier delivers all the orders in 1 run.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
Same as example 1 except for the first shift runs, where the following restrictions on the maximum number of stops are set:
shifts.0.run_restrictions.maximal_stops.value
= 1shifts.0.run_restrictions.maximal_stops.penalty.fixed
= 1 000 000shifts.0.run_restrictions.maximal_stops.penalty.per_stop
= 1 000
The penalties are so high that it's unprofitable to violate the stop restrictions. As a result, the courier delivers the orders in 2 runs. In the first run they deliver order 8, and in the second run they deliver all the remaining orders.
API request (JSON) ⋅ API response ⋅ View on map
Example 3
Same as example 2 except for the first shift runs, where the penalties for exceeding the maximum number of stops (shifts.0.run_restrictions.maximal_stops.penalty.fixed
and shifts.0.run_restrictions.maximal_stops.penalty.per_stop
) are no longer used. The run has the following restriction: shifts.0.run_restrictions.maximal_stops.value
= 1.
Plus, the cost of one run (3000) is added to the cost.run
field.
As a result, the courier delivers all the orders in 1 run. Under the given conditions, it's more profitable to violate the restriction and get a penalty than to split the orders into several runs. The penalty amount is 1100, where 1000 is the default value for shifts.0.run_restrictions.maximal_stops.penalty.fixed
and 100 is the default value for shifts.0.run_restrictions.maximal_stops.penalty.per_stop
.
API request (JSON) ⋅ API response ⋅ View on map
Route density
To set the preferred area of work for a vehicle so that the route includes orders near a certain location, use the global_proximity_attraction_point
parameter. In the parameter value, specify the location id
with the garage
type. This is used as a hotspot: the algorithm aims to reduce the total distance between orders on the route and this location.
The density of routes around the hotspot is determined by the options.global_proximity_factor
option: the greater its value, the denser the routes are. For more information about this option, see Dense routes.
The maximum distance from the route locations to the hotspot is stored in the max_distance_to_attraction_point_m
parameter, which can be used to calculate the cost of the route. The total sum of distances from all orders to the hotspot on the route affects the penalty for inadequate route grouping.
Other ways to group orders geographically
- You can use the
global_proximity_factor
option to build dense routes without setting a “hotspot”. This option only works for vehicles that don't have theglobal_proximity_attraction_point
parameter set. - By using geofences for vehicles, you can set restrictions on delivery zones. While geofence restriction can't be violated, route density settings are a more flexible tool.
Example 1
Three vehicles have to deliver 15 orders. The route is calculated based on the order weight and vehicle capacity. There are no other restrictions on the route.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
The same as in example 1, but each courier has a set hotspot that shows their preferred area of work. As a result, the routes are more dense. The maximum distance from the order to the hotspot on the route is 20,663 m.
API request (JSON) ⋅ API response ⋅ View on map
Example 3
The same as in example 2, but one of the couriers has a different hotspot. Because orders are regrouped, the location of routes on the map has changed. The maximum distance from the order to the hotspot increased to 28,528 m.
API request (JSON) ⋅ API response ⋅ View on map
Planned route
The user can specify which courier should deliver particular orders. This is useful:
-
For additional planning when you have pre-planned routes.
-
When the order requires a specific courier.
To specify pre-planned orders for the courier, use the vehicle.planned_route
option. This option has the following properties:
-
When planning, all orders that you specify in
vehicle.planned_route
can't become unassigned, even if strict restrictions are violated, such as vehicle capacity or time windows for whichhard_window
=true
. -
By default, the order sequence set in
planned_route
isn't fixed. When re-planning, orders can be arranged in a new sequence. -
If you need to save the route that's specified in
planned_route
without saving changes (sequence changes and adding new orders), usevehicle.fixed_planned_route
=true
.
For each order in planned_route
, be sure to specify the vehicle shift (even if the vehicle only has 1 shift).
If in planned_route
several shifts are specified, you can lock orders for shifts using the fix_planned_shifts
= true
option. By default, the value is false
.
If a courier arrives too early, delivery time of the order is determined by the vehicle.wait_if_early
parameter:
true
: The courier waits for the order time window to start (default value).false
: The courier handles the order as soon as they arrive.
Alert
If there is only a hard time window location.hard_window
= true
for an order, the vehicle.wait_if_early
parameter with the false
value won't affect the order delivery.
If the courier needs to visit depots for reloading while following the route, specify them in the middle_depot_id
field. They may be the same as the depots the courier visits at the start or at the end of the route.
If the courier needs to visit depots for reloading in planned_route
, set is_middle_depot
= true
(it's false
by default). If you set is_middle_depot
= true
for all depots, the algorithm will plan the start and end depots based on the courier settings (see sections Start from one of several depots and Returning to a depot at the end of the route or run).
Example 1
Both couriers had pre-planned orders in planned_route
. As a result, these orders were given to these two couriers.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
This example involves 1 vehicle that has orders linked to it via planned_route
. The total weight of the orders exceeds the vehicle capacity, but the route is still being planned (because orders specified in this option can't be unallocated). But the ask status is UNFEASIBLE
.
API request (JSON) ⋅ API response ⋅ View on map
Example 3
Theoretically, 2 couriers could deliver all 20 orders without overload and without being late. But a fixed sequence is specified for courier 1 using the vehicle.fixed_planned_route
= true
parameter, so they'll only deliver orders from this sequence. Courier 2 isn't limited by a strict plan, so they'll take as many orders as they can carry. Several orders will remain in the depot, despite the fact that courier 1 has the space and time.
API request (JSON) ⋅ API response ⋅ View on map
Example 4.1
2 couriers need to deliver 15 orders in 2 shifts. Using the planned_route
option, orders 1-8 are assigned to courier 1, and orders 9-15 to courier 2. At the same time, it's indicated that orders 1-4 and 9-12 must be delivered during the first shift, and orders 5-8 and 13-15 during the second shift. Since fix_planned_shifts
= false
and couriers manage to deliver all orders within one shift, the distribution of orders by shifts is not taken into account.
API request (JSON) ⋅ API response ⋅ View on map
Example 4.2
The conditions are the same as in example 4.1, but the option fix_planned_shifts
= true
. This means the shifts during which orders have to be delivered are taken into account.
API request (JSON) ⋅ API response ⋅ View on map
Loaded orders
If the order is already in the vehicle, the courier can immediately start delivery. To run this scenario, specify the parameters in the planned_route
field:
Parameter |
Description |
Value |
|
List of order |
|
|
All orders loaded to the vehicle should be serviced during the same run before the vehicle returns to the depot. |
|
|
The vehicle will visit all depots in Use if:
|
|
* Specified for depots and orders of the garage
type.
Example
Several orders were loaded in the courier's vehicle last night. The courier doesn't have to pick them up at the depot. Therefore, the point of the garage
type is specified in the place where the courier will go.
The loaded_orders
parameter shows that orders 1 and 2 have been loaded. The courier can immediately deliver them to the destination.
The following parameters are also set in the conditions:
-
planned_runs_first
=true
. -
delivery_in_current_run
=true
for depot and garage.
The courier starts from the garage and immediately delivers orders 1 and 2. They then go to the depot to pick up orders 3 and 4, which they must deliver immediately after loading. After these two deliveries, the courier returns to the depot to pick up orders 5 and 6 and follows the route further.
API request (JSON) ⋅ API response ⋅ View on map
Fixed part of the route
The visited_locations
feature is useful if:
-
The beginning of route must follow a particular path.
-
You need to change the start point when doing additional planning.
The route must be fixed for each courier individually.
To do this, specify the visited_locations
array in vehicles
and describe the points of the fixed route using the following parameters.
Parameter |
Description |
|
Order ID from |
|
ID of the courier shift from |
|
The time of departure from the point. If the first point of a fixed part of the route:
You can specify this parameter for several orders, so that the visit time is calculated according to the time specified in |
|
The need to wait for the start of the time window in case of early arrival.
|
* Required parameter
Example 1
There are 5 orders in this example. The sequence of orders 1, 2, 3 is fixed in visited_locations
. Orders 4 and 5 can be delivered in any sequence.
API request (JSON) ⋅ API response ⋅ View on map
Example 2
In visited_locations
, it's specified that the route must start with order 2 at 10:00.
API request (JSON) ⋅ API response ⋅ View on map
Example 3
The order sequence 1, 2, 3 is set as in example 1. It's also specified that departure from order 1 must be at 08:20 and from order 3 at 11:00.
API request (JSON) ⋅ API response ⋅ View on map
Example 4
There are 8 orders in this example. The specified sequence is orders 1, 2, 3, then the depot, then order 4. The courier may deliver the remaining orders in any sequence.
API request (JSON) ⋅ API response ⋅ View on map