Cost Model
Fuel resolution chain (CollectAPI → FuelPriceCache → default), toll resolution (Google Routes → TollGuru → estimate), the CostBreakdown structure, and RSE compliance staffing costs.
The cost model feeds TripAnalysis.costBreakdown — a per-segment breakdown used to compute the internal cost, margin, and ProfitabilityIndicator displayed in the back-office. None of the cost components are directly added to the client-facing price; they exist for profitability analysis only.
CostBreakdown Structure
interface CostBreakdown {
fuel: FuelCostComponent;
tolls: TollCostComponent;
wear: WearCostComponent;
driver: DriverCostComponent;
parking: ParkingCostComponent;
zoneSurcharges?: ZoneSurcharges;
tco?: TcoCostComponent; // TCO if vehicle data available
total: number;
}total = fuel + tolls + wear + driver + parking. Zone surcharges and TCO are additive when present.
Fuel Cost
Fuel price resolution chain
The engine resolves the price per liter in this order, stopping at the first available result:
| Priority | Source | Mechanism |
|---|---|---|
| 1 | CollectAPI (real-time) | HTTP request to CollectAPI with GPS coordinates; returns prices per country/region |
| 2 | FuelPriceCache (database) | Most recent FuelPriceCache row for the resolved country; stale after 48 h |
| 3 | Organization override | OrganizationPricingSettings.fuelPricePerLiter |
| 4 | Built-in defaults | DEFAULT_FUEL_PRICES in constants.ts |
Default fuel prices:
| Fuel type | Default (€/L) |
|---|---|
DIESEL | 1.789 |
GASOLINE | 1.899 |
LPG | 0.999 |
ELECTRIC | 0.25 |
CollectAPI timeout: 4 000 ms (REALTIME_API_TIMEOUT_MS). A cache hit with less than 48 hours of age satisfies the request without an API call. The per-request in-memory cache (TTL 60 s) prevents duplicate calls within a single pricing calculation for multi-waypoint excursions.
Fuel consumption resolution chain
The liters-per-100-km figure resolves via a separate four-level chain:
| Priority | Source |
|---|---|
| 1 | Vehicle.fuelConsumption (specific vehicle, if assigned) |
| 2 | VehicleCategory.fuelConsumption |
| 3 | OrganizationPricingSettings.fuelConsumptionL100km |
| 4 | Built-in default: 8.0 L/100 km |
Fuel cost formula
litersUsed = (distanceKm / 100) × consumptionL100km
fuelCost = litersUsed × pricePerLiterToll Cost
Toll resolution chain
| Priority | Source | Notes |
|---|---|---|
| 1 | Google Routes API | Parses travelAdvisory.tollInfo.estimatedPrice from the v2 response |
| 2 | TollGuru API | Fallback when Google returns no toll data; uses vehicle-category toll class, height, and axle metadata |
| 3 | Flat rate estimate | distanceKm × tollCostPerKm (default 0.15 €/km) |
Toll results are cached in the TollCalculationCache table for 24 hours. Cache keys must include route coordinates plus the vehicle-class metadata (tollClass, vehicleHeightCm, axleCount) so a light sedan never reuses a coach toll estimate. TollCostComponent.isFromCache flags cache hits.
For ELECTRIC fuel type, the engine remaps to GASOLINE for the Google Routes API (which may not return toll data for electric vehicles).
Approach and empty-return legs are priced with the same toll pipeline as the client service leg when those legs have route geometry. If Google/HERE/TollGuru cannot return a route, the engine falls back to the Haversine estimate rather than dropping the cost component.
Toll cost formula (fallback only)
tollCost = distanceKm × tollCostPerKmWear Cost
Vehicle mechanical wear modeled as a flat rate per km:
wearCost = distanceKm × wearCostPerKmDefault: 0.10 €/km (DEFAULT_COST_PARAMETERS.wearCostPerKm). Overridden by OrganizationPricingSettings.wearCostPerKm.
Driver Cost
Driver time valued at an hourly rate:
driverCost = (durationMinutes / 60) × driverHourlyCostDefault: 25.00 €/h (DEFAULT_COST_PARAMETERS.driverHourlyCost). Overridden by OrganizationPricingSettings.driverHourlyCost.
For HEAVY vehicles with RSE mandatory breaks, the extra break minutes are included in the totalDurationMinutes fed into the driver cost calculation.
TCO (Total Cost of Ownership)
When full vehicle and category TCO data is available, a TcoCostComponent is added:
interface TcoCostComponent {
amount: number;
distanceKm: number;
depreciation: { amount: number; ratePerKm: number; method: "LINEAR" | "DECLINING_BALANCE" };
maintenance: { amount: number; ratePerKm: number };
insurance: { amount: number; ratePerKm: number };
totalRatePerKm: number;
source: "VEHICLE" | "CATEGORY";
}TCO complements the wear cost; when TCO is present, tco.total provides a more accurate amortisation figure than the flat wearCostPerKm rate.
Zone Surcharges
Fixed surcharges defined per zone are collected after route resolution:
interface ZoneSurcharges {
pickup: ZoneSurchargeComponent | null;
dropoff: ZoneSurchargeComponent | null;
total: number;
}Each ZoneSurchargeComponent aggregates parkingSurcharge + accessFee for the zone. If pickup and dropoff resolve to the same zone, the surcharge is counted only once.
RSE Compliance Staffing Costs
When integrateComplianceIntoPricing() detects a long-distance, same-day, relay, double-crew, or multi-day requirement, it attaches a CompliancePlan to TripAnalysis:
| Plan type | Trigger | Cost impact |
|---|---|---|
DOUBLE_CREW | Trip exceeds single-driver daily hours limit | Second driver cost added to totalInternalCost |
RELAY_DRIVER | Long-haul trip requiring driver relay | Relay positioning cost added |
MULTI_DAY | Trip spans multiple calendar days | Driver accommodation + per-diem costs added |
NONE | No compliance issue | No extra cost |
The engine now resolves staffing through a single computeStaffing() path parameterized by an RSE mode profile:
| Mode profile | Typical trip shape | Notes |
|---|---|---|
TRANSFER | One-way service | Default one-way aggregation |
ROUND_TRIP | Same quote with return leg | Uses round-trip decomposition and waiting time |
MULTI_STOP | Waypoints / multi-stop routing | Aggregates cumulated service segments |
MAD | Mise à disposition | Continuous amplitude profile |
SEJOUR | Multi-day stay | Counts day-by-day RSE, feeds real counters, and never offers relay/double choice cards |
The staffing plan selection policy is configurable (OrganizationPricingSettings.staffingSelectionPolicy) and operator choices can override the automatic plan when the cockpit sends a staffingOverride:
| Policy | Behaviour |
|---|---|
CHEAPEST | Selects the staffing arrangement with the lowest total cost |
FASTEST | Minimises total trip duration |
PREFER_INTERNAL | Prefers internal drivers over external relay drivers |
Staffing costs inflate TripAnalysis.totalInternalCost and are also folded into the recommended quote price when the returned plan carries an additionalCost. The cost breakdown itself remains operator-internal; agency responses receive only reduced feasibility/crew information.
Relay plans use a frozen sharePercent split. The default comes from OrganizationLicenseRule.defaultRelaySharePercent, and explicit operator overrides are clamped to an exclusive 1–99 percent range. Accepted quotes persist the split into StaffingPlan / StaffingAssignment so dispatch does not recompute it.
Profitability Indicator
type ProfitabilityIndicator = "green" | "orange" | "red";
// Thresholds (configurable):
// green: marginPercent ≥ greenMarginThreshold (default 20 %)
// orange: marginPercent ≥ orangeMarginThreshold (default 0 %)
// red: marginPercent < orangeMarginThresholdmarginPercent = ((price - internalCost) / price) × 100Both greenMarginThreshold and orangeMarginThreshold are configurable in OrganizationPricingSettings.
Short-trip pricing
Below shortTripThresholdKm (configurable), the engine applies a shortTripMultiplier to offset the fixed overhead costs per trip. A minimumTripPriceHt floor can also be set to guarantee a minimum revenue.
| Field | Effect |
|---|---|
minimumTripPriceHt | Price floor (HT) regardless of formula result |
shortTripThresholdKm | Distance below which the short-trip multiplier applies |
shortTripMultiplier | Factor applied to base price for short trips |
See also
- Modes — FIXED_GRID vs DYNAMIC and bidirectional pricing
- Hierarchical Algorithm — Zone resolution and grid matching
- Shadow Calculation — Segments A / B / C and availability overlap
- Configuration → Behaviour —
OrganizationPricingSettingsfield map
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.
Configuration → Behaviour
Complete field-to-behaviour mapping for OrganizationPricingSettings — every field, its default, and exactly where the engine reads it.