feat: Add User and UserBalance models with serialization methods

feat: Implement BalanceRepository for group balance calculations

feat: Create ExpenseRepository for managing expenses

feat: Add services for handling expenses and storage operations

fix: Update import paths for models in repositories and services

refactor: Rename CountContent to AccountContent in HomePage

chore: Add StorageService for image upload and management
This commit is contained in:
Dayron
2025-10-21 16:02:58 +02:00
parent 62eb434548
commit 4edbd1cf34
60 changed files with 1973 additions and 342 deletions

View File

@@ -0,0 +1,113 @@
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) {
return _expensesCollection
.where('groupId', isEqualTo: groupId)
.where('isArchived', isEqualTo: false)
.orderBy('createdAt', descending: true)
.snapshots()
.map((snapshot) {
return snapshot.docs
.map((doc) => Expense.fromMap(doc.data() as Map<String, dynamic>, doc.id))
.toList();
}).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;
}
}
}