Skip to main content

Order Grouping

Order Grouping is a constraint that allows the optimization engine to bundle multiple orders heading to the same physical location into a single, consolidated stop.

This feature ensures that drop-offs for orders going to the exact same location are fulfilled by the same vehicle simultaneously, creating more realistic and efficient route plans while satisfying other constraints.

Value in the Planning Process​

In real-world logistics, if several orders are placed by the same customer or are destined for the same location, it is highly inefficient for multiple vehicles to fulfill them separately or for one vehicle to visit the location multiple times in a single trip.

Activating the Order Grouping constraint provides immense value during the planning process by:

  • Reducing service time overhead: The time spent parking or at the delivery point is not duplicated for each individual order.
  • Ensuring compact routes: By forcing the optimizer to treat multiple shipments to the same location as one consolidated delivery, the algorithm generates highly efficient schedules.

To enable this feature during optimization, set node_grouping_enabled to true in logistics_api_settings.

tip

When manually editing routes, you can enable the manual_edit_allow_cvrptw_conversion parameter to trigger a similar grouping logic. This streamlines manual planning by converting grouped orders into single nodes (ignoring capacity constraints to force the drop-off combination) to ensure trips have a single consolidated drop-off, visiting each location only once per trip.

How Order Grouping Works​

Grouping runs before the solver sees the request. Orders that qualify are merged into one synthetic node, the solver routes that node like any other, and the result is split back into the original orders afterwards. The solver never sees the individual orders inside a group.

Step 1: Which mode is used​

The engine picks the mode from the shape of the request:

  • CVRPTW: chosen when every order shares one pickup location (a depot run) or every order shares one drop-off location, with the same opening time. Orders already assigned to a vehicle count toward this check and do not force PDP: their stops at the shared location are merged into the depot stop, and their other stops keep their assigned sequence. The shared side becomes a fixed point and is never grouped. Grouping applies to the stops on the side that varies, which for a typical depot run means the drop-offs.
  • PDP (Pickup and Delivery Problem): used in every other case, and always when there is only one order, when any vehicle has completed stops, when a live (GPS-tracked) vehicle has assigned orders, or when a vehicle carries assigned stops that are neither orders nor its start/end locations. Each order is treated as a pickup-and-drop-off pair, and pairs are grouped as a whole.

Step 2: Candidate buckets​

Nodes are bucketed by exact coordinates. There is no distance tolerance: two stops 1 metre apart are different locations and are never grouped.

ModeTwo orders share a bucket when…
CVRPTWtheir lat/lon are identical
PDPtheir pickup lat/lon and drop-off lat/lon are both identical

Each bucket is then split further:

  • Same groups: only orders whose groups lists hold the same labels (order doesn't matter) can be grouped. Orders carrying a priority label from booking-order prioritization are kept apart this way.
  • Same eligible vehicles: every order in the bucket must be servable by exactly the same set of vehicles (after vehicle_labels filtering), and that set must not be empty. If even one order in the bucket has different label requirements, nothing in that bucket is grouped. Every order in it goes to the solver on its own.

A bucket with only one order is left as is.

Step 3: Chaining time windows​

Within a bucket, orders are sorted by open time (ties broken by service time, then max_slack; if orders at the same coordinates carry different location_names, each name's orders are kept together first) and walked through once, in that order. The engine keeps a running group and tries to add each next order to it:

  1. The group starts with the first order. Its window is that order's window, and its service time is that order's service time.
  2. The next order joins the group only if:
    • Its window still overlaps after the group's service. Service is back to back: the next order starts once the earlier orders in the group are finished. The group's window shifted by its accumulated service time must overlap the next order's window.
    • The total demand stays within the capacity threshold (see Grouping Capacity Strategies).
  3. If the order joins, the group's window narrows to the times that still work for every member, and the order's service time is added to the group's.
  4. If the order fails either check, the current group is closed and the failing order starts a new group. Orders already placed in a group are never revisited.

Because the walk only ever compares an order with the group directly before it, one location can produce several groups, or a mix of groups and single orders.

Example: one location, several windows​

Five drop-offs at the same coordinates, each with 10 minutes of service time:

OrderWindow
A09:00 – 10:00
B09:00 – 10:00
C09:30 – 11:00
D14:00 – 15:00
E14:00 – 15:00
  • A + B: identical windows, so B joins.
  • A + B + C: after A and B (20 minutes of service), C's window still overlaps, so C joins. The group's window narrows to 09:10 – 09:50 so that A, B and C can each start inside their own window when served in that order.
  • D: after A, B and C, the group's latest start is 09:50, so its service ends by 10:20. D cannot open until 14:00, so the windows no longer overlap. The group closes and D starts a new one.
  • D + E: identical windows, so E joins.

The solver therefore sees two stops at this location instead of five: {A, B, C} in the morning and {D, E} in the afternoon. The two groups can go to different vehicles, or to the same vehicle on two separate visits.

Order matters

Because the walk is single-pass, a tight window in the middle of the sorted list can split orders that would otherwise group. With 10-minute service times:

  • A 09:00 – 10:00 and C 10:00 – 11:00 on their own form one group.
  • Add X 09:20 – 09:25 at the same location. The sorted order becomes A, X, C. A + X group, but their narrowed window ends at 09:15. With 20 minutes of service already queued, the group is finished by 09:35 at the latest, before C opens at 10:00. C is left on its own.

Step 4: The grouped node​

Each group becomes one node with:

FieldValue
open_time_ts / close_time_tsThe narrowed window: the range of start times that lets every member begin service inside its own window, served back to back in sorted order.
service_timeSum of the members' service times.
demandSum of the members' demands, per capacity type.
penaltySum of the members' rejection penalties, so rejecting the group costs as much as rejecting each order.
Everything elseCopied from the first order in the bucket (in request order, not sorted order), including vehicle_labels and groups.

In PDP mode the pickup and the drop-off of the pair are each merged this way, giving one grouped pickup and one grouped drop-off.

Step 5: Ungrouping the result​

After the solver finishes, every grouped node is split back into its original orders, in the sorted order from Step 3:

  • The first order gets the group's scheduled_ts. Each following order is scheduled when the previous one's service time ends.
  • All members get the same vehicle.
  • Breaks and slack attach to the last member. The others get a slack of 0.
  • If the group was rejected, every member is reported in rejected_bookings with the same reason.
Late arrivals inside a group

The narrowed window guarantees that each member starts on time only if the solver starts the group inside that window. If lateness is allowed (for example, through soft time windows or allow_late), the solver may start the group late. Every member after the first then inherits that delay plus the accumulated service time ahead of it, so later members can be noticeably later than the group's start.

Assigned nodes​

By default only unassigned orders are grouped. Stops already assigned to a vehicle are left alone.

Setting assigned_node_grouping_enabled to true (PDP only) also groups each vehicle's own assigned stops with one another, using the same rules with the capacity check turned off (the vehicle already carries them). It does not merge a new, unassigned order into a stop a vehicle is already carrying. In CVRPTW runs, including depot runs with assigned orders, the setting has no effect.

Grouping Capacity Strategies​

When multiple nodes are grouped, their demands are aggregated together. Because this consolidated demand could easily surpass the capacity of smaller vehicles, you must provide a strategy for the engine to evaluate whether the group is allowed to be formed in the first place based on vehicle capacity constraints.

You can configure this using the node_grouping_capacity_strategy parameter. The available strategies are:

  • min: Grouping fails if the combined total demand exceeds the minimum capacity across all eligible vehicle types. This is the safest approach to guarantee any eligible vehicle can pick it up.
  • max: Grouping fails if the combined total demand exceeds the maximum capacity of eligible vehicle types.
  • median: Grouping fails if the combined total demand exceeds the median capacity across all eligible vehicle types.
  • mean: Grouping fails if the combined total demand is greater than the average (mean) capacity of eligible vehicle types.
  • ignore: Bypasses the capacity checks entirely during the grouping phase. Use this if you want maximum consolidation regardless of vehicle capacity, but note that if the grouped demand exceeds all your largest vehicles, the orders might be entirely rejected during routing.

Implementation in API​

All three toggles live in logistics_api_settings, in the Grouping & Sequence Rules section of the Logistics API Settings page:

SettingDefaultEffect
node_grouping_enabledfalseTurns order grouping on.
node_grouping_capacity_strategyminCapacity threshold a group must fit (see Grouping Capacity Strategies).
assigned_node_grouping_enabledfalseAlso groups each vehicle's own assigned stops (PDP only; see Assigned nodes).

Example Configuration​

{
"data": {
"logistics_api_settings": {
"node_grouping_enabled": true,
"node_grouping_capacity_strategy": "min"
}
}
}