Files
TravelMate/lib/repositories/expense_repository.dart
Dayron e3dad39c4f feat: Add TripImageService for automatic trip image management
- Implemented TripImageService to load missing images for trips, reload images, and clean up unused images.
- Added functionality to get image statistics and clean up duplicate images.
- Created utility scripts for manual image cleanup and diagnostics in Firebase Storage.
- Introduced tests for image loading optimization and photo quality algorithms.
- Updated dependencies in pubspec.yaml and pubspec.lock for image handling.
2025-11-03 14:33:58 +01:00

117 lines
3.7 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/expense.dart';
import '../services/error_service.dart';
class ExpenseRepository {
final FirebaseFirestore _firestore;
final ErrorService _errorService;
ExpenseRepository({
FirebaseFirestore? firestore,
ErrorService? errorService,
}) : _firestore = firestore ?? FirebaseFirestore.instance,
_errorService = errorService ?? ErrorService();
CollectionReference get _expensesCollection => _firestore.collection('expenses');
// Stream des dépenses d'un groupe
Stream<List<Expense>> getExpensesStream(String groupId) {
// Utiliser une requête simple pour éviter les problèmes d'index
return _expensesCollection
.where('groupId', isEqualTo: groupId)
.snapshots()
.map((snapshot) {
final expenses = snapshot.docs
.map((doc) => Expense.fromMap(doc.data() as Map<String, dynamic>, doc.id))
.where((expense) => !expense.isArchived) // Filtrer côté client
.toList();
// Trier côté client par date de création (plus récent en premier)
expenses.sort((a, b) => b.createdAt.compareTo(a.createdAt));
return expenses;
}).handleError((error) {
_errorService.logError('ExpenseRepository', 'Erreur stream expenses: $error');
return <Expense>[];
});
}
// Créer une dépense
Future<String> createExpense(Expense expense) async {
try {
final docRef = await _expensesCollection.add(expense.toMap());
return docRef.id;
} catch (e) {
_errorService.logError('ExpenseRepository', 'Erreur création expense: $e');
rethrow;
}
}
// Mettre à jour une dépense
Future<void> updateExpense(Expense expense) async {
try {
final updateData = expense.toMap();
updateData['editedAt'] = FieldValue.serverTimestamp();
updateData['isEdited'] = true;
await _expensesCollection.doc(expense.id).update(updateData);
} catch (e) {
_errorService.logError('ExpenseRepository', 'Erreur update expense: $e');
rethrow;
}
}
// Supprimer une dépense
Future<void> deleteExpense(String expenseId) async {
try {
await _expensesCollection.doc(expenseId).delete();
} catch (e) {
_errorService.logError('ExpenseRepository', 'Erreur delete expense: $e');
rethrow;
}
}
// Marquer un split comme payé
Future<void> markSplitAsPaid(String expenseId, String userId) async {
try {
await _firestore.runTransaction((transaction) async {
final expenseDoc = await transaction.get(_expensesCollection.doc(expenseId));
if (!expenseDoc.exists) {
throw Exception('Dépense non trouvée');
}
final expense = Expense.fromMap(
expenseDoc.data() as Map<String, dynamic>,
expenseDoc.id
);
final updatedSplits = expense.splits.map((split) {
if (split.userId == userId) {
return split.copyWith(isPaid: true, paidAt: DateTime.now());
}
return split;
}).toList();
transaction.update(expenseDoc.reference, {
'splits': updatedSplits.map((s) => s.toMap()).toList(),
});
});
} catch (e) {
_errorService.logError('ExpenseRepository', 'Erreur mark split paid: $e');
rethrow;
}
}
// Archiver une dépense
Future<void> archiveExpense(String expenseId) async {
try {
await _expensesCollection.doc(expenseId).update({
'isArchived': true,
'archivedAt': FieldValue.serverTimestamp(),
});
} catch (e) {
_errorService.logError('ExpenseRepository', 'Erreur archive expense: $e');
rethrow;
}
}
}