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; supports LIGHT/HEAVY weight classes |
| 3 | Flat rate estimate | distanceKm × tollCostPerKm (default 0.15 €/km) |
Toll results are cached in the TollCalculationCache table for 24 hours (keyed on coordinate hash to 4 decimal places ≈ 11 m precision). 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).
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 or multi-day mission, 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 staffing plan selection policy is configurable (OrganizationPricingSettings.staffingSelectionPolicy):
| 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 but are not added to the client price. They affect the profitability indicator.
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.