Sixième Étoile — Documentation

Shadow Calculation

Segments A / B / C (approach, service, return), estimated end time, availability overlap detection, round-trip modes, and how positioning costs feed the margin model.

The shadow calculation produces a full operational loop for every trip — including deadhead legs the client never pays for. It is the foundation for internal cost analysis, driver availability detection, and dispatch optimisation.

The Three Segments

SegmentNameRouteBilled to client?
AApproachBase → PickupNo (internal cost only)
BServicePickup → DropoffYes
CReturnDropoff → BaseNo (internal cost only)

calculateShadowSegments() assembles these three SegmentAnalysis objects inside TripAnalysis.segments. Each segment carries its own CostBreakdown (fuel, tolls, wear, driver time).

When no vehicle has been assigned yet (quote creation stage), segments A and C may be absent or estimated — the approach distance depends on which base the eventually-assigned vehicle departs from.

Routing source

TripAnalysis.routingSource records where segment distances came from:

ValueMeaning
GOOGLE_APIGoogle Routes API called for real distances
HAVERSINE_ESTIMATEStraight-line distance × correction factor
VEHICLE_SELECTIONDistances computed during vehicle dispatch selection

Time Analysis

calculateTimeAnalysis() takes the raw Google Routes duration and applies three sequential adjustments, all recorded in TimeAnalysis.

1. Vehicle speed adjustment (HEAVY only)

Coaches and minibuses average ~70 km/h vs a car's ~100 km/h. The engine adds +40 % of the base duration for vehicles with regulatoryCategory = "HEAVY".

adjustedMinutes = baseGoogleDurationMinutes × 1.40

2. Traffic adjustment (pickup time)

The engine evaluates the pickup hour against a set of traffic rules. Only the first matching rule applies:

Rule nameHours (local)Adjustment
RUSH_HOUR_MORNING07:00 – 09:00+15 %
RUSH_HOUR_EVENING17:00 – 19:00+15 %
NIGHT22:00 – 06:00−10 %

The percentage is applied to the base Google duration (before vehicle adjustment), then the resulting minutes are added to the running total.

3. RSE mandatory breaks (HEAVY only)

French RSE regulation (Art. 561-2) limits continuous driving. The engine computes the number of required breaks:

breakCount = floor(drivingMinutes / 270)     // 4.5 h max continuous
totalBreakMinutes = breakCount × 45          // 45 min per break

Constants:

ConstantValue
Max continuous driving270 min (4.5 h)
Break duration45 min
Regulation referenceRSE Art. 561-2

mandatoryBreaks is null for LIGHT vehicles, and for HEAVY vehicles where the trip is short enough that no break is triggered.


Estimated End Time

calculateEstimatedEndAt(pickupAt, tripAnalysis) computes the mission end timestamp:

estimatedEndAt = pickupAt + totalDurationMinutes

Special case — MULTI_DAY compliance plan:

estimatedEndAt = pickupAt + (daysRequired × 24 × 60)

This timestamp is used by the dispatch engine to detect driver availability overlap: a driver cannot be assigned to a new mission whose window overlaps [scheduledAt, estimatedEndAt] of an existing confirmed mission.


Positioning Costs

calculatePositioningCosts() breaks out the segments A and C as billable items for margin analysis.

Approach fee (Segment A)

When a vehicle is selected, the approach cost equals segments.approach.cost.total — the full internal cost of driving from the base to the pickup point.

When no vehicle has been assigned yet, approachFee.cost = 0 and the reason records that the cost will be computed at dispatch.

Empty return (Segment C)

When a vehicle is selected, the empty return cost is:

adjustedCost = segments.return.cost.total × (emptyReturnCostPercent / 100)

emptyReturnCostPercent is configured in OrganizationPricingSettings (default 100 %). Setting it to 50 % means only half the return operating cost is counted against margin.

When no vehicle is selected, emptyReturn.cost = 0 — the engine cannot estimate the return distance without knowing the vehicle's base.

Neither the approach fee nor the empty return is added to the client-facing price. They are internal cost items used solely for profitability analysis and ProfitabilityIndicator calculation.


Round-Trip Segments

When a quote is marked as a round trip, calculateRoundTripSegments() replaces the simple ×2 shortcut with accurate per-segment pricing.

The six segments for a round trip:

SegmentRoutePresent in
ABase → PickupBoth modes
BPickup → Dropoff (outbound)Both modes
CDropoff → Base (empty return)RETURN_BETWEEN_LEGS only
DBase → Pickup (repositioning)RETURN_BETWEEN_LEGS only
EDropoff → Pickup (return service)Both modes
FPickup → Base (final empty return)Both modes

Round-trip mode selection

ModeConditionCost
WAIT_ON_SITENo waiting time, or waitingTimeMinutes < thresholdA + B + E + F ≈ 2× single leg
RETURN_BETWEEN_LEGSwaitingTimeMinutes ≥ threshold (default 120 min)A + B + C + D + E + F

waitOnSiteThresholdMinutes defaults to 120 minutes and can be overridden per call.


TripAnalysis Structure

interface TripAnalysis {
  segments: {
    approach: SegmentAnalysis | null;   // Segment A
    service: SegmentAnalysis;           // Segment B
    return: SegmentAnalysis | null;     // Segment C
  };
  totalDistanceKm: number;
  totalDurationMinutes: number;
  totalInternalCost: number;
  costBreakdown: CostBreakdown;         // Combined A + B + C
  routingSource: "GOOGLE_API" | "HAVERSINE_ESTIMATE" | "VEHICLE_SELECTION";
  vehicleSelection: VehicleSelectionInfo | undefined;
  compliancePlan: CompliancePlan | undefined;
  calculatedAt: string;                 // ISO 8601
}

Each SegmentAnalysis contains distanceKm, durationMinutes, cost (a CostBreakdown), and isEstimated.


Relationship to the Cost Model

The shadow calculation feeds directly into the cost model. See Cost Model for:

  • How CostBreakdown is built (fuel, tolls, wear, driver).
  • Fuel resolution chain (vehicle → category → organization → default).
  • Toll resolution (Google Routes → TollGuru → tollCostPerKm estimate).
  • RSE compliance staffing costs and how they inflate totalInternalCost.

See also

Was this page helpful?

On this page