Files
TravelMate/lib/services/expense_service.dart
Dayron 4edbd1cf34 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
2025-10-21 16:02:58 +02:00

91 lines
2.8 KiB
Dart

import 'dart:io';
import '../models/expense.dart';
import '../repositories/expense_repository.dart';
import 'error_service.dart';
import 'storage_service.dart'; // Pour upload des reçus
class ExpenseService {
final ExpenseRepository _expenseRepository;
final ErrorService _errorService;
final StorageService _storageService;
ExpenseService({
required ExpenseRepository expenseRepository,
ErrorService? errorService,
StorageService? storageService,
}) : _expenseRepository = expenseRepository,
_errorService = errorService ?? ErrorService(),
_storageService = storageService ?? StorageService();
// Création avec validation et upload d'image
Future<String> createExpenseWithValidation(Expense expense, File? receiptImage) async {
try {
// Validation métier
_validateExpenseData(expense);
// Upload du reçu si présent
String? receiptUrl;
if (receiptImage != null) {
receiptUrl = await _storageService.uploadReceiptImage(
expense.groupId,
receiptImage,
);
}
// Créer l'expense avec l'URL du reçu
final expenseWithReceipt = expense.copyWith(receiptUrl: receiptUrl);
return await _expenseRepository.createExpense(expenseWithReceipt);
} catch (e) {
_errorService.logError('ExpenseService', 'Erreur création expense: $e');
rethrow;
}
}
// Mise à jour avec validation
Future<void> updateExpenseWithValidation(Expense expense, File? newReceiptImage) async {
try {
_validateExpenseData(expense);
// Gérer le nouvel upload si nécessaire
String? receiptUrl = expense.receiptUrl;
if (newReceiptImage != null) {
receiptUrl = await _storageService.uploadReceiptImage(
expense.groupId,
newReceiptImage,
);
}
final expenseWithReceipt = expense.copyWith(
receiptUrl: receiptUrl,
editedAt: DateTime.now(),
isEdited: true,
);
await _expenseRepository.updateExpense(expenseWithReceipt);
} catch (e) {
_errorService.logError('ExpenseService', 'Erreur update expense: $e');
rethrow;
}
}
// Validation des données
void _validateExpenseData(Expense expense) {
if (expense.description.trim().isEmpty) {
throw Exception('La description est requise');
}
if (expense.amount <= 0) {
throw Exception('Le montant doit être positif');
}
if (expense.splits.isEmpty) {
throw Exception('Au moins un participant est requis');
}
final totalSplits = expense.splits.fold(0.0, (sum, split) => sum + split.amount);
if ((totalSplits - expense.amountInEur).abs() > 0.01) {
throw Exception('La somme des répartitions ne correspond pas au montant total');
}
}
}