feat: Add logger service and improve expense dialog with enhanced receipt management and calculation logic.
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
/// - [Settlement] for individual payment recommendations
|
||||
/// - [UserBalance] for per-user balance information
|
||||
library;
|
||||
|
||||
import '../models/group_balance.dart';
|
||||
import '../models/expense.dart';
|
||||
import '../models/group_statistics.dart';
|
||||
@@ -59,7 +60,10 @@ class BalanceService {
|
||||
try {
|
||||
return await _balanceRepository.calculateGroupBalance(groupId);
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceService', 'Erreur calcul balance groupe: $e');
|
||||
_errorService.logError(
|
||||
'BalanceService',
|
||||
'Erreur calcul balance groupe: $e',
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +84,9 @@ class BalanceService {
|
||||
|
||||
/// Stream de la balance en temps réel
|
||||
Stream<GroupBalance> getGroupBalanceStream(String groupId) {
|
||||
return _expenseRepository.getExpensesStream(groupId).asyncMap((expenses) async {
|
||||
return _expenseRepository.getExpensesStream(groupId).asyncMap((
|
||||
expenses,
|
||||
) async {
|
||||
try {
|
||||
final userBalances = calculateUserBalances(expenses);
|
||||
final settlements = optimizeSettlements(userBalances);
|
||||
@@ -145,7 +151,7 @@ class BalanceService {
|
||||
/// Optimiser les règlements (algorithme avancé)
|
||||
List<Settlement> optimizeSettlements(List<UserBalance> balances) {
|
||||
final settlements = <Settlement>[];
|
||||
|
||||
|
||||
// Filtrer les utilisateurs avec une balance significative (> 0.01€)
|
||||
final creditors = balances
|
||||
.where((b) => b.shouldReceive && b.balance > 0.01)
|
||||
@@ -164,10 +170,10 @@ class BalanceService {
|
||||
|
||||
// Utiliser des copies mutables pour les calculs
|
||||
final creditorsRemaining = Map<String, double>.fromEntries(
|
||||
creditors.map((c) => MapEntry(c.userId, c.balance))
|
||||
creditors.map((c) => MapEntry(c.userId, c.balance)),
|
||||
);
|
||||
final debtorsRemaining = Map<String, double>.fromEntries(
|
||||
debtors.map((d) => MapEntry(d.userId, -d.balance))
|
||||
debtors.map((d) => MapEntry(d.userId, -d.balance)),
|
||||
);
|
||||
|
||||
// Algorithme glouton optimisé
|
||||
@@ -185,16 +191,19 @@ class BalanceService {
|
||||
);
|
||||
|
||||
if (settlementAmount > 0.01) {
|
||||
settlements.add(Settlement(
|
||||
fromUserId: debtor.userId,
|
||||
fromUserName: debtor.userName,
|
||||
toUserId: creditor.userId,
|
||||
toUserName: creditor.userName,
|
||||
amount: settlementAmount,
|
||||
));
|
||||
settlements.add(
|
||||
Settlement(
|
||||
fromUserId: debtor.userId,
|
||||
fromUserName: debtor.userName,
|
||||
toUserId: creditor.userId,
|
||||
toUserName: creditor.userName,
|
||||
amount: settlementAmount,
|
||||
),
|
||||
);
|
||||
|
||||
// Mettre à jour les montants restants
|
||||
creditorsRemaining[creditor.userId] = creditRemaining - settlementAmount;
|
||||
creditorsRemaining[creditor.userId] =
|
||||
creditRemaining - settlementAmount;
|
||||
debtorsRemaining[debtor.userId] = debtRemaining - settlementAmount;
|
||||
}
|
||||
}
|
||||
@@ -204,7 +213,10 @@ class BalanceService {
|
||||
}
|
||||
|
||||
/// Calculer le montant optimal pour un règlement
|
||||
double _calculateOptimalSettlementAmount(double creditAmount, double debtAmount) {
|
||||
double _calculateOptimalSettlementAmount(
|
||||
double creditAmount,
|
||||
double debtAmount,
|
||||
) {
|
||||
final amount = [creditAmount, debtAmount].reduce((a, b) => a < b ? a : b);
|
||||
// Arrondir à 2 décimales
|
||||
return (amount * 100).round() / 100;
|
||||
@@ -213,36 +225,50 @@ class BalanceService {
|
||||
/// Valider les règlements calculés
|
||||
List<Settlement> _validateSettlements(List<Settlement> settlements) {
|
||||
// Supprimer les règlements trop petits
|
||||
final validSettlements = settlements
|
||||
.where((s) => s.amount > 0.01)
|
||||
.toList();
|
||||
final validSettlements = settlements.where((s) => s.amount > 0.01).toList();
|
||||
|
||||
// Log pour debug en cas de problème
|
||||
final totalSettlements = validSettlements.fold(0.0, (sum, s) => sum + s.amount);
|
||||
_errorService.logInfo('BalanceService',
|
||||
'Règlements calculés: ${validSettlements.length}, Total: ${totalSettlements.toStringAsFixed(2)}€');
|
||||
final totalSettlements = validSettlements.fold(
|
||||
0.0,
|
||||
(sum, s) => sum + s.amount,
|
||||
);
|
||||
_errorService.logInfo(
|
||||
'BalanceService',
|
||||
'Règlements calculés: ${validSettlements.length}, Total: ${totalSettlements.toStringAsFixed(2)}€',
|
||||
);
|
||||
|
||||
return validSettlements;
|
||||
}
|
||||
|
||||
/// Calculer la dette entre deux utilisateurs spécifiques
|
||||
double calculateDebtBetweenUsers(String groupId, String userId1, String userId2) {
|
||||
double calculateDebtBetweenUsers(
|
||||
String groupId,
|
||||
String userId1,
|
||||
String userId2,
|
||||
) {
|
||||
// Cette méthode pourrait être utile pour des fonctionnalités avancées
|
||||
// comme "Combien me doit X ?" ou "Combien je dois à Y ?"
|
||||
return 0.0; // TODO: Implémenter si nécessaire
|
||||
|
||||
// On peut utiliser optimizeSettlements pour avoir la réponse précise
|
||||
// Cependant, cela nécessite d'avoir les dépenses.
|
||||
// Comme cette méthode est synchrone et ne prend pas les dépenses en entrée,
|
||||
// elle est difficile à implémenter correctement sans changer sa signature.
|
||||
// Pour l'instant, on retourne 0.0 car elle n'est pas utilisée.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/// Analyser les tendances de dépenses par catégorie
|
||||
Map<String, double> analyzeCategorySpending(List<Expense> expenses) {
|
||||
final categoryTotals = <String, double>{};
|
||||
|
||||
|
||||
for (final expense in expenses) {
|
||||
if (expense.isArchived) continue;
|
||||
|
||||
|
||||
final categoryName = expense.category.displayName;
|
||||
categoryTotals[categoryName] = (categoryTotals[categoryName] ?? 0) + expense.amountInEur;
|
||||
categoryTotals[categoryName] =
|
||||
(categoryTotals[categoryName] ?? 0) + expense.amountInEur;
|
||||
}
|
||||
|
||||
|
||||
return categoryTotals;
|
||||
}
|
||||
|
||||
@@ -253,9 +279,12 @@ class BalanceService {
|
||||
}
|
||||
|
||||
final nonArchivedExpenses = expenses.where((e) => !e.isArchived).toList();
|
||||
final totalAmount = nonArchivedExpenses.fold(0.0, (sum, e) => sum + e.amountInEur);
|
||||
final totalAmount = nonArchivedExpenses.fold(
|
||||
0.0,
|
||||
(sum, e) => sum + e.amountInEur,
|
||||
);
|
||||
final averageAmount = totalAmount / nonArchivedExpenses.length;
|
||||
|
||||
|
||||
final categorySpending = analyzeCategorySpending(nonArchivedExpenses);
|
||||
final topCategory = categorySpending.entries
|
||||
.reduce((a, b) => a.value > b.value ? a : b)
|
||||
@@ -279,11 +308,14 @@ class BalanceService {
|
||||
try {
|
||||
// Ici vous pourriez enregistrer le règlement en base
|
||||
// ou créer une transaction de règlement
|
||||
|
||||
|
||||
// Pour l'instant, on pourrait juste recalculer
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
|
||||
_errorService.logSuccess('BalanceService', 'Règlement marqué comme effectué');
|
||||
|
||||
_errorService.logSuccess(
|
||||
'BalanceService',
|
||||
'Règlement marqué comme effectué',
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceService', 'Erreur mark settlement: $e');
|
||||
rethrow;
|
||||
@@ -312,4 +344,4 @@ class _UserBalanceCalculator {
|
||||
balance: _totalPaid - _totalOwed,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user