Skip to main content
Not every fleet can serve its full daily demand in a single loop. When total demand across a wave of orders exceeds the fleet’s total capacity, resources need to return to the depot, reload, and go out again — potentially several times — inside their workingTimeWindow. Nothing about this requires a dedicated field: it’s a modeling pattern built from capacities and stop kind, the same primitives covered in the data model.
Before modeling multi-trip tours, check whether you actually need them, using the exact test from the agent modeling checklist (§2, “Check capacity feasibility before modeling orders”): sum your stops’ capacities per dimension and compare against your fleet’s total capacity in a single loop. That test is a strict binary, not a margin call — total demand either fits within total fleet capacity (single-loop, no reload needed) or exceeds it (single-loop is infeasible, not merely suboptimal; the shortfall will show up as unplanned stops, not an error). This page assumes that check already came out on the “exceeds” side. It isn’t a separate, softer threshold of its own, and a ratio close to but under 1 doesn’t independently justify reaching for this pattern — per the checklist, don’t resolve a capacity shortfall by defaulting to multi-trip on your own; surface it as an open question, and only model this pattern once the client’s own data or context confirms the fleet genuinely reloads at a depot mid-shift.

The core mechanic: pair each delivery with its own depot pickup

Rather than pre-computing a fixed number of “reload rounds” per vehicle, pair every delivery with a pickup of the same cargo at the depot, inside the same order:
Since an order’s stops all run on the same resource, in array order, and the pickup adds exactly what the delivery then removes, each pair is capacity-neutral by construction — it never touches the resource’s baseline load. That has two consequences that make it a better default than sizing a handful of large reload stops to a resource’s full capacity:
  • No fixed round count. The engine is free to chain any number of these pairs on a single resource, going back to the depot as many times as the schedule and capacity allow — you don’t need to guess in advance how many rounds each vehicle will need, or leave unused "optional": true reload orders on the table.
  • No per-vehicle sizing. A pickup sized to one delivery’s own weight/piles fits any resource with that much spare capacity, heterogeneous fleet or not. There’s no need for a skills / requiredSkills pair to reserve a reload for a specific vehicle — whichever resource ends up serving the delivery automatically picks up its own matching cargo first, because both stops belong to the same order.
If you also want to force or bias which stops happen before versus after each other (for example, a fixed morning sector followed by a fixed afternoon sector), combine this with tagged phases and the maximizePrecedences objective. That’s a separate concern from the capacity mechanic above.

Charging depot time once per visit, not once per pickup

With one pickup stop per delivery, a vehicle loading five deliveries’ worth of cargo before a round would otherwise pay operationDuration five times over for what is physically a single dock visit. Keep each pickup’s operationDuration negligible (as in the example above) and instead charge the real access time once per visit with the plan-level accessDurationsByStopTag field:
This adds the duration once, before the first of a run of consecutive stops sharing the tag — matching a single loading operation at the dock, regardless of how many individual pickups happen during that visit. See Data model for the full field.

Limiting simultaneous depot visits

A physical depot usually has a limited number of loading docks or bays, and can’t serve every vehicle at once. Tag every pickup stop with a shared depot tag (as in the example above) and cap simultaneous presence with overlappingCapacitiesByStopTag at the plan level:
This caps the number of resources simultaneously present at any stop tagged depot:main to 2 — matching, for example, a depot with two loading docks. Because every load in this pattern is an explicit tagged stop that’s part of an order — including the first load of the day, not just later reloads — the limit applies uniformly from the very first pickup, with nothing left uncovered. departure and arrival (see Depot vs. position) are only used for the resource’s idle start/end-of-day position; they carry no cargo and no tag, so keep the actual loading out of them entirely.
Without a fairness objective, the engine can load one or two resources up to their limit on repeated depot rounds while others in the fleet sit comparatively idle — all while still satisfying maximizeMandatoryStops and the delay/cost objectives ahead of it. Include minimizeLargestTourDuration (see How the optimization engine works) to cap how unbalanced the longest single tour can get relative to the rest of the fleet — it’s placed after minimizeResources above so fleet size is still minimized first, but tour lengths are then balanced across whatever fleet size that settles on.
If resources are arriving back at the depot and then waiting idle for their next reload window, consider setting the plan-level lateDeparture: true (see Plan-level fields) so the engine compacts departure timing to reduce that idle wait, instead of always having resources leave as early as possible.

See also

  • Data modelcapacities, stop kind, accessDurationsByStopTag, and the rest of the constraints catalog referenced above.
  • Modeling advanced constraints — heterogeneous capacities, skills, breaks, and multiple time windows.
  • Handling infeasibility — diagnosing a plan where demand still doesn’t fit even with multiple rounds.