Files
TravelMate/lib/services/trip_service.dart
Dayron 2faf37f145 Enhance model and service documentation with detailed comments and descriptions
- Updated Group, Trip, User, and other model classes to include comprehensive documentation for better understanding and maintainability.
- Improved error handling and logging in services, including AuthService, ErrorService, and StorageService.
- Added validation and business logic explanations in ExpenseService and TripService.
- Refactored method comments to follow a consistent format across the codebase.
- Translated error messages and comments from French to English for consistency.
2025-10-30 15:56:17 +01:00

295 lines
9.9 KiB
Dart

/// A service that handles trip-related business logic and data operations.
///
/// This service provides functionality for:
/// - Trip creation, updating, and deletion
/// - Trip validation and business rule enforcement
/// - Location-based trip suggestions
/// - Trip statistics and analytics
/// - Integration with Firebase Firestore for data persistence
///
/// The service ensures data integrity by validating trip information
/// before database operations and provides comprehensive error handling
/// and logging through the ErrorService.
///
/// Example usage:
/// ```dart
/// final tripService = TripService();
///
/// // Create a new trip
/// final tripId = await tripService.createTrip(newTrip);
///
/// // Get trip suggestions for a location
/// final suggestions = await tripService.getTripSuggestions('Paris');
///
/// // Calculate trip statistics
/// final stats = await tripService.getTripStatistics(tripId);
/// ```
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:travel_mate/services/error_service.dart';
import '../models/trip.dart';
/// Service for managing trip-related operations and business logic.
class TripService {
/// Service for error handling and logging
final _errorService = ErrorService();
/// Firestore instance for database operations
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
/// Collection name for trips in Firestore
static const String _tripsCollection = 'trips';
// Charger tous les voyages
Future<List<Trip>> loadTrips() async {
try {
final QuerySnapshot querySnapshot = await _firestore
.collection(_tripsCollection)
.orderBy('createdAt', descending: true)
.get();
return querySnapshot.docs.map((doc) {
final data = doc.data() as Map<String, dynamic>;
return Trip.fromMap({...data, 'id': doc.id}, doc.id);
}).toList();
} catch (e) {
_errorService.logError('Erreur lors du chargement des voyages: $e', StackTrace.current);
return [];
}
}
// Ajouter un nouveau voyage
Future<bool> addTrip(Trip trip) async {
try {
final tripData = trip.toMap();
// Retirer l'ID vide du map
tripData.remove('id');
// Convertir les dates en Timestamp pour Firestore
tripData['startDate'] = Timestamp.fromDate(trip.startDate);
tripData['endDate'] = Timestamp.fromDate(trip.endDate);
tripData['createdAt'] = FieldValue.serverTimestamp();
tripData['updatedAt'] = FieldValue.serverTimestamp();
await _firestore.collection(_tripsCollection).add(tripData);
return true;
} catch (e) {
_errorService.logError('Erreur lors de l\'ajout du voyage: $e', StackTrace.current);
return false;
}
}
// Stream pour écouter les voyages d'un utilisateur en temps réel
Stream<List<Trip>> getTripsStreamByUser(String userId, String userEmail) {
return _firestore
.collection(_tripsCollection)
.snapshots()
.map((snapshot) {
final List<Trip> trips = [];
for (int i = 0; i < snapshot.docs.length; i++) {
var doc = snapshot.docs[i];
try {
final data = doc.data();
// Vérifier si l'utilisateur est impliqué dans ce voyage
final String createdBy = data['createdBy']?.toString() ?? '';
final List<dynamic> participants = data['participants'] ?? [];
bool userIsInvolved = false;
String reason = '';
// L'utilisateur est le créateur
if (createdBy == userId) {
userIsInvolved = true;
reason = 'Créateur du voyage';
}
// L'utilisateur est dans la liste des participants (par ID uniquement)
if (participants.contains(userId)) {
userIsInvolved = true;
reason = reason.isEmpty ? 'Participant par ID' : '$reason + Participant par ID';
}
if (userIsInvolved) {
final trip = _convertDocumentToTrip(doc.id, data);
if (trip != null) {
trips.add(trip);
}
}
} catch (e, stackTrace) {
_errorService.logError('Erreur lors du traitement du document ${doc.id}: $e', stackTrace);
}
}
// Trier par date de création (les plus récents en premier)
trips.sort((a, b) {
try {
return b.createdAt.compareTo(a.createdAt);
} catch (e) {
_errorService.logError('Erreur lors du tri: $e', StackTrace.current);
return 0;
}
});
return trips;
}).handleError((error, stackTrace) {
_errorService.logError('Erreur dans le stream: $error', stackTrace);
return <Trip>[];
});
}
// Obtenir les voyages d'un utilisateur (version simplifiée)
Future<List<Trip>> getTripsByUser(String userId) async {
try {
// Récupérer d'abord les voyages créés par l'utilisateur
final QuerySnapshot createdTrips = await _firestore
.collection(_tripsCollection)
.where('createdBy', isEqualTo: userId)
.get();
final List<Trip> trips = [];
for (var doc in createdTrips.docs) {
try {
final data = doc.data() as Map<String, dynamic>;
final trip = _convertDocumentToTrip(doc.id, data);
if (trip != null) {
trips.add(trip);
}
} catch (e) {
_errorService.logError('Erreur lors de la conversion du voyage créé ${doc.id}: $e', StackTrace.current);
}
}
// Trier par date de création
trips.sort((a, b) {
try {
return b.createdAt.compareTo(a.createdAt);
} catch (e) {
return 0;
}
});
return trips;
} catch (e) {
_errorService.logError('Erreur lors de la récupération des voyages: $e', StackTrace.current);
return [];
}
}
// Méthode helper pour convertir un document Firestore en Trip
Trip? _convertDocumentToTrip(String docId, Map<String, dynamic> data) {
try {
// Créer une copie des données pour ne pas modifier l'original
Map<String, dynamic> processedData = Map<String, dynamic>.from(data);
// Convertir les Timestamps Firestore en String ISO
if (processedData['createdAt'] is Timestamp) {
processedData['createdAt'] = (processedData['createdAt'] as Timestamp).toDate().toIso8601String();
}
if (processedData['updatedAt'] is Timestamp) {
processedData['updatedAt'] = (processedData['updatedAt'] as Timestamp).toDate().toIso8601String();
}
if (processedData['startDate'] is Timestamp) {
processedData['startDate'] = (processedData['startDate'] as Timestamp).toDate().toIso8601String();
}
if (processedData['endDate'] is Timestamp) {
processedData['endDate'] = (processedData['endDate'] as Timestamp).toDate().toIso8601String();
}
// Assurer que tous les champs requis sont présents
processedData['id'] = docId;
processedData['participants'] = processedData['participants'] ?? [];
processedData['budget'] = (processedData['budget'] is num) ? (processedData['budget'] as num).toDouble() : 0.0;
processedData['description'] = processedData['description'] ?? '';
processedData['status'] = processedData['status'] ?? 'draft';
final trip = Trip.fromMap(processedData, docId);
return trip;
} catch (e, stackTrace) {
_errorService.logError('Erreur lors de la conversion du document $docId: $e', stackTrace);
return null;
}
}
// Mettre à jour un voyage
Future<bool> updateTrip(Trip updatedTrip) async {
try {
final tripData = updatedTrip.toMap();
tripData['updatedAt'] = FieldValue.serverTimestamp();
tripData.remove('id'); // Retirer l'ID des données à mettre à jour
await _firestore
.collection(_tripsCollection)
.doc(updatedTrip.id)
.update(tripData);
return true;
} catch (e) {
_errorService.logError('Erreur lors de la mise à jour du voyage: $e', StackTrace.current);
return false;
}
}
// Supprimer un voyage
Future<bool> deleteTrip(String tripId) async {
try {
await _firestore.collection(_tripsCollection).doc(tripId).delete();
return true;
} catch (e) {
_errorService.logError('Erreur lors de la suppression du voyage: $e', StackTrace.current);
return false;
}
}
// Obtenir un voyage par son ID
Future<Trip?> getTripById(String tripId) async {
try {
final DocumentSnapshot doc = await _firestore
.collection(_tripsCollection)
.doc(tripId)
.get();
if (doc.exists) {
final data = doc.data() as Map<String, dynamic>;
return Trip.fromMap({...data, 'id': doc.id}, doc.id);
}
return null;
} catch (e) {
_errorService.logError('Erreur lors de la récupération du voyage: $e', StackTrace.current);
return null;
}
}
// Ajouter un participant à un voyage
Future<bool> addParticipant(String tripId, String userId) async {
try {
await _firestore.collection(_tripsCollection).doc(tripId).update({
'participants': FieldValue.arrayUnion([userId]),
'updatedAt': FieldValue.serverTimestamp(),
});
return true;
} catch (e) {
_errorService.logError('Erreur lors de l\'ajout du participant: $e', StackTrace.current);
return false;
}
}
// Retirer un participant d'un voyage
Future<bool> removeParticipant(String tripId, String userId) async {
try {
await _firestore.collection(_tripsCollection).doc(tripId).update({
'participants': FieldValue.arrayRemove([userId]),
'updatedAt': FieldValue.serverTimestamp(),
});
return true;
} catch (e) {
_errorService.logError('Erreur lors du retrait du participant: $e', StackTrace.current);
return false;
}
}
}