e14b333a67
- Cloud Function travel.js : autocomplete Google Places + calcul itinéraires via Google Routes API avec péages Ulys /legs (precision=6) + /rate - Modèles : VehicleModel, DepotModel, RouteResultModel + FuelPrices - Services : VehicleService, TravelService (Firestore CRUD + API calls) - Gestion des données : 3 nouveaux onglets (Dépôts, Véhicules, Prix carburants) - Autocomplétion adresse dans le formulaire événement - Dialog calcul frais : config + carte flutter_map OSM + sélection itinéraire - Injection option FRAIS_KM dans l'événement à la sélection - flutter_map 7.0.2 + latlong2 0.9.1 ajoutés - npm: csv-parser + @mapbox/polyline installés dans functions
159 lines
4.5 KiB
Dart
159 lines
4.5 KiB
Dart
/// Résultat d'un itinéraire calculé par Google Maps + Ulys.
|
|
class RouteResult {
|
|
/// 'TOLL' ou 'TOLL_FREE'
|
|
final String routeType;
|
|
final int distanceMeters;
|
|
final int durationSeconds;
|
|
final String encodedPolyline;
|
|
final double tollCost;
|
|
|
|
const RouteResult({
|
|
required this.routeType,
|
|
required this.distanceMeters,
|
|
required this.durationSeconds,
|
|
required this.encodedPolyline,
|
|
required this.tollCost,
|
|
});
|
|
|
|
factory RouteResult.fromMap(Map<String, dynamic> map) {
|
|
return RouteResult(
|
|
routeType: (map['routeType'] ?? 'TOLL').toString(),
|
|
distanceMeters: _parseInt(map['distanceMeters'] ?? 0),
|
|
durationSeconds: _parseInt(map['durationSeconds'] ?? 0),
|
|
encodedPolyline: (map['encodedPolyline'] ?? '').toString(),
|
|
tollCost: _parseDouble(map['tollCost'] ?? 0),
|
|
);
|
|
}
|
|
|
|
bool get isTollFree => routeType == 'TOLL_FREE';
|
|
|
|
double get distanceKm => distanceMeters / 1000.0;
|
|
double get durationMinutes => durationSeconds / 60.0;
|
|
double get durationHours => durationSeconds / 3600.0;
|
|
|
|
/// Calcule le coût carburant.
|
|
/// [consumptionPer100km] : L/100km (ou kWh/100km si électrique)
|
|
/// [fuelPricePerLiter] : €/L ou €/kWh
|
|
/// [freeZoneKm] : km gratuits à déduire (zone de gratuité)
|
|
double fuelCost({
|
|
required double consumptionPer100km,
|
|
required double fuelPricePerLiter,
|
|
double freeZoneKm = 0,
|
|
}) {
|
|
final effectiveKm = (distanceKm - freeZoneKm).clamp(0, double.infinity);
|
|
return (effectiveKm / 100.0) * consumptionPer100km * fuelPricePerLiter;
|
|
}
|
|
|
|
/// Calcule le coût de maintenance.
|
|
double maintenanceCost({
|
|
required double costPerKm,
|
|
double freeZoneKm = 0,
|
|
}) {
|
|
final effectiveKm = (distanceKm - freeZoneKm).clamp(0, double.infinity);
|
|
return effectiveKm * costPerKm;
|
|
}
|
|
|
|
/// Calcule le coût de main-d'œuvre (techniciens).
|
|
/// [freeZoneMinutes] : minutes gratuites à déduire (zone de gratuité)
|
|
double laborCost({
|
|
required int nbTechnicians,
|
|
required double hourlyRate,
|
|
double freeZoneMinutes = 0,
|
|
}) {
|
|
final effectiveMinutes =
|
|
(durationMinutes - freeZoneMinutes).clamp(0, double.infinity);
|
|
return (effectiveMinutes / 60.0) * nbTechnicians * hourlyRate;
|
|
}
|
|
|
|
/// Calcule le coût total pour un aller simple.
|
|
double totalCost({
|
|
required double consumptionPer100km,
|
|
required double fuelPricePerLiter,
|
|
required double maintenanceCostPerKm,
|
|
required int nbTechnicians,
|
|
required double hourlyRate,
|
|
bool applyFreeZone = false,
|
|
}) {
|
|
const freeKm = 20.0;
|
|
const freeMinutes = 20.0;
|
|
|
|
return fuelCost(
|
|
consumptionPer100km: consumptionPer100km,
|
|
fuelPricePerLiter: fuelPricePerLiter,
|
|
freeZoneKm: applyFreeZone ? freeKm : 0,
|
|
) +
|
|
maintenanceCost(
|
|
costPerKm: maintenanceCostPerKm,
|
|
freeZoneKm: applyFreeZone ? freeKm : 0,
|
|
) +
|
|
laborCost(
|
|
nbTechnicians: nbTechnicians,
|
|
hourlyRate: hourlyRate,
|
|
freeZoneMinutes: applyFreeZone ? freeMinutes : 0,
|
|
) +
|
|
tollCost;
|
|
}
|
|
|
|
static double _parseDouble(dynamic v) {
|
|
if (v is double) return v;
|
|
if (v is int) return v.toDouble();
|
|
if (v is String) return double.tryParse(v) ?? 0.0;
|
|
return 0.0;
|
|
}
|
|
|
|
static int _parseInt(dynamic v) {
|
|
if (v is int) return v;
|
|
if (v is double) return v.toInt();
|
|
if (v is String) return int.tryParse(v) ?? 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// Prix des carburants (stocké dans Firestore app_config/fuel_prices)
|
|
class FuelPrices {
|
|
final double diesel; // €/L
|
|
final double essence; // €/L
|
|
final double electricite; // €/kWh
|
|
|
|
const FuelPrices({
|
|
this.diesel = 1.60,
|
|
this.essence = 1.75,
|
|
this.electricite = 0.22,
|
|
});
|
|
|
|
factory FuelPrices.fromMap(Map<String, dynamic> map) {
|
|
return FuelPrices(
|
|
diesel: _parseDouble(map['diesel'] ?? 1.60),
|
|
essence: _parseDouble(map['essence'] ?? 1.75),
|
|
electricite: _parseDouble(map['electricite'] ?? 0.22),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'diesel': diesel,
|
|
'essence': essence,
|
|
'electricite': electricite,
|
|
};
|
|
|
|
double priceForFuelType(String fuelType) {
|
|
switch (fuelType.toLowerCase()) {
|
|
case 'diesel':
|
|
return diesel;
|
|
case 'essence':
|
|
return essence;
|
|
case 'electrique':
|
|
case 'électrique':
|
|
return electricite;
|
|
default:
|
|
return diesel;
|
|
}
|
|
}
|
|
|
|
static double _parseDouble(dynamic v) {
|
|
if (v is double) return v;
|
|
if (v is int) return v.toDouble();
|
|
if (v is String) return double.tryParse(v) ?? 0.0;
|
|
return 0.0;
|
|
}
|
|
}
|