feat: Add logger service and improve expense dialog with enhanced receipt management and calculation logic.
This commit is contained in:
@@ -19,10 +19,10 @@
|
||||
/// balanceRepository: balanceRepository,
|
||||
/// expenseRepository: expenseRepository,
|
||||
/// );
|
||||
///
|
||||
///
|
||||
/// // Load balances for a group
|
||||
/// balanceBloc.add(LoadGroupBalances('groupId123'));
|
||||
///
|
||||
///
|
||||
/// // Mark a settlement as completed
|
||||
/// balanceBloc.add(MarkSettlementAsCompleted(
|
||||
/// groupId: 'groupId123',
|
||||
@@ -32,6 +32,7 @@
|
||||
/// ));
|
||||
/// ```
|
||||
library;
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../repositories/balance_repository.dart';
|
||||
import '../../repositories/expense_repository.dart';
|
||||
@@ -46,10 +47,10 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
final ErrorService _errorService;
|
||||
|
||||
/// Constructor for BalanceBloc.
|
||||
///
|
||||
///
|
||||
/// Initializes the bloc with required repositories and optional services.
|
||||
/// Sets up event handlers for balance-related operations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [balanceRepository]: Repository for balance data operations
|
||||
/// [expenseRepository]: Repository for expense data operations
|
||||
@@ -61,7 +62,12 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
BalanceService? balanceService,
|
||||
ErrorService? errorService,
|
||||
}) : _balanceRepository = balanceRepository,
|
||||
_balanceService = balanceService ?? BalanceService(balanceRepository: balanceRepository, expenseRepository: expenseRepository),
|
||||
_balanceService =
|
||||
balanceService ??
|
||||
BalanceService(
|
||||
balanceRepository: balanceRepository,
|
||||
expenseRepository: expenseRepository,
|
||||
),
|
||||
_errorService = errorService ?? ErrorService(),
|
||||
super(BalanceInitial()) {
|
||||
on<LoadGroupBalances>(_onLoadGroupBalance);
|
||||
@@ -70,11 +76,11 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
}
|
||||
|
||||
/// Handles [LoadGroupBalances] events.
|
||||
///
|
||||
///
|
||||
/// Loads and calculates user balances for a specific group along with
|
||||
/// optimal settlement recommendations. This provides a complete overview
|
||||
/// of who owes money to whom and the most efficient payment strategy.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The LoadGroupBalances event containing the group ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -83,18 +89,22 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
Emitter<BalanceState> emit,
|
||||
) async {
|
||||
try {
|
||||
emit(BalanceLoading());
|
||||
|
||||
// Emit empty state initially to avoid infinite spinner
|
||||
emit(const GroupBalancesLoaded(balances: [], settlements: []));
|
||||
|
||||
// Calculate group user balances
|
||||
final userBalances = await _balanceRepository.calculateGroupUserBalances(event.groupId);
|
||||
|
||||
final userBalances = await _balanceRepository.calculateGroupUserBalances(
|
||||
event.groupId,
|
||||
);
|
||||
|
||||
// Calculate optimal settlements
|
||||
final settlements = await _balanceService.calculateOptimalSettlements(event.groupId);
|
||||
|
||||
emit(GroupBalancesLoaded(
|
||||
balances: userBalances,
|
||||
settlements: settlements,
|
||||
));
|
||||
final settlements = await _balanceService.calculateOptimalSettlements(
|
||||
event.groupId,
|
||||
);
|
||||
|
||||
emit(
|
||||
GroupBalancesLoaded(balances: userBalances, settlements: settlements),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceBloc', 'Error loading balance: $e');
|
||||
emit(BalanceError(e.toString()));
|
||||
@@ -102,11 +112,11 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
}
|
||||
|
||||
/// Handles [RefreshBalance] events.
|
||||
///
|
||||
///
|
||||
/// Refreshes the balance data for a group while trying to maintain the current
|
||||
/// state when possible to provide a smoother user experience. Only shows loading
|
||||
/// state if there's no existing balance data.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The RefreshBalance event containing the group ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -119,17 +129,20 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
if (state is! GroupBalancesLoaded) {
|
||||
emit(BalanceLoading());
|
||||
}
|
||||
|
||||
|
||||
// Calculate group user balances
|
||||
final userBalances = await _balanceRepository.calculateGroupUserBalances(event.groupId);
|
||||
|
||||
final userBalances = await _balanceRepository.calculateGroupUserBalances(
|
||||
event.groupId,
|
||||
);
|
||||
|
||||
// Calculate optimal settlements
|
||||
final settlements = await _balanceService.calculateOptimalSettlements(event.groupId);
|
||||
|
||||
emit(GroupBalancesLoaded(
|
||||
balances: userBalances,
|
||||
settlements: settlements,
|
||||
));
|
||||
final settlements = await _balanceService.calculateOptimalSettlements(
|
||||
event.groupId,
|
||||
);
|
||||
|
||||
emit(
|
||||
GroupBalancesLoaded(balances: userBalances, settlements: settlements),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceBloc', 'Error refreshing balance: $e');
|
||||
emit(BalanceError(e.toString()));
|
||||
@@ -137,11 +150,11 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
}
|
||||
|
||||
/// Handles [MarkSettlementAsCompleted] events.
|
||||
///
|
||||
///
|
||||
/// Records a settlement transaction between two users, marking that
|
||||
/// a debt has been paid. This updates the balance calculations and
|
||||
/// automatically refreshes the group balance data to reflect the change.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The MarkSettlementAsCompleted event containing settlement details
|
||||
/// [emit]: State emitter function
|
||||
@@ -156,9 +169,9 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
toUserId: event.toUserId,
|
||||
amount: event.amount,
|
||||
);
|
||||
|
||||
|
||||
emit(const BalanceOperationSuccess('Settlement marked as completed'));
|
||||
|
||||
|
||||
// Reload balance after settlement
|
||||
add(RefreshBalance(event.groupId));
|
||||
} catch (e) {
|
||||
@@ -166,4 +179,4 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
emit(BalanceError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'expense_event.dart';
|
||||
import 'expense_state.dart';
|
||||
|
||||
/// BLoC for managing expense operations and state.
|
||||
///
|
||||
///
|
||||
/// This BLoC handles expense-related operations including loading expenses,
|
||||
/// creating new expenses, updating existing ones, deleting expenses, and
|
||||
/// managing expense splits. It coordinates with the expense repository and
|
||||
@@ -15,18 +15,18 @@ import 'expense_state.dart';
|
||||
class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
/// Repository for expense data operations.
|
||||
final ExpenseRepository _expenseRepository;
|
||||
|
||||
|
||||
/// Service for expense business logic and validation.
|
||||
final ExpenseService _expenseService;
|
||||
|
||||
|
||||
/// Service for error handling and logging.
|
||||
final ErrorService _errorService;
|
||||
|
||||
|
||||
/// Subscription to the expenses stream for real-time updates.
|
||||
StreamSubscription? _expensesSubscription;
|
||||
|
||||
/// Creates a new [ExpenseBloc] with required dependencies.
|
||||
///
|
||||
///
|
||||
/// [expenseRepository] is required for data operations.
|
||||
/// [expenseService] and [errorService] have default implementations if not provided.
|
||||
ExpenseBloc({
|
||||
@@ -34,10 +34,11 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
ExpenseService? expenseService,
|
||||
ErrorService? errorService,
|
||||
}) : _expenseRepository = expenseRepository,
|
||||
_expenseService = expenseService ?? ExpenseService(expenseRepository: expenseRepository),
|
||||
_expenseService =
|
||||
expenseService ??
|
||||
ExpenseService(expenseRepository: expenseRepository),
|
||||
_errorService = errorService ?? ErrorService(),
|
||||
super(ExpenseInitial()) {
|
||||
|
||||
on<LoadExpensesByGroup>(_onLoadExpensesByGroup);
|
||||
on<ExpensesUpdated>(_onExpensesUpdated);
|
||||
on<CreateExpense>(_onCreateExpense);
|
||||
@@ -48,7 +49,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [LoadExpensesByGroup] events.
|
||||
///
|
||||
///
|
||||
/// Sets up a stream subscription to receive real-time updates for expenses
|
||||
/// in the specified group. Cancels any existing subscription before creating a new one.
|
||||
Future<void> _onLoadExpensesByGroup(
|
||||
@@ -56,15 +57,18 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
Emitter<ExpenseState> emit,
|
||||
) async {
|
||||
try {
|
||||
emit(ExpenseLoading());
|
||||
|
||||
// Emit empty state initially to avoid infinite spinner
|
||||
// The stream will update with actual data when available
|
||||
emit(const ExpensesLoaded(expenses: []));
|
||||
|
||||
await _expensesSubscription?.cancel();
|
||||
|
||||
|
||||
_expensesSubscription = _expenseRepository
|
||||
.getExpensesStream(event.groupId)
|
||||
.listen(
|
||||
(expenses) => add(ExpensesUpdated(expenses)),
|
||||
onError: (error) => add(ExpensesUpdated([], error: error.toString())),
|
||||
onError: (error) =>
|
||||
add(ExpensesUpdated([], error: error.toString())),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error loading expenses: $e');
|
||||
@@ -73,10 +77,10 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [ExpensesUpdated] events.
|
||||
///
|
||||
///
|
||||
/// Processes real-time updates from the expense stream, either emitting
|
||||
/// the updated expense list or an error state if the stream encountered an error.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The ExpensesUpdated event containing expenses or error information
|
||||
/// [emit]: State emitter function
|
||||
@@ -92,11 +96,11 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [CreateExpense] events.
|
||||
///
|
||||
///
|
||||
/// Creates a new expense with validation and optional receipt image upload.
|
||||
/// Uses the expense service to handle business logic and validation,
|
||||
/// including currency conversion and split calculations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The CreateExpense event containing expense data and optional receipt
|
||||
/// [emit]: State emitter function
|
||||
@@ -105,7 +109,10 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
Emitter<ExpenseState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _expenseService.createExpenseWithValidation(event.expense, event.receiptImage);
|
||||
await _expenseService.createExpenseWithValidation(
|
||||
event.expense,
|
||||
event.receiptImage,
|
||||
);
|
||||
emit(const ExpenseOperationSuccess('Expense created successfully'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error creating expense: $e');
|
||||
@@ -114,11 +121,11 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [UpdateExpense] events.
|
||||
///
|
||||
///
|
||||
/// Updates an existing expense with validation and optional new receipt image.
|
||||
/// Uses the expense service to handle business logic, validation, and
|
||||
/// recalculation of splits if expense details change.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The UpdateExpense event containing updated expense data and optional new receipt
|
||||
/// [emit]: State emitter function
|
||||
@@ -127,7 +134,10 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
Emitter<ExpenseState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _expenseService.updateExpenseWithValidation(event.expense, event.newReceiptImage);
|
||||
await _expenseService.updateExpenseWithValidation(
|
||||
event.expense,
|
||||
event.newReceiptImage,
|
||||
);
|
||||
emit(const ExpenseOperationSuccess('Expense updated successfully'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error updating expense: $e');
|
||||
@@ -136,10 +146,10 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [DeleteExpense] events.
|
||||
///
|
||||
///
|
||||
/// Permanently deletes an expense from the database. This action
|
||||
/// cannot be undone and will affect group balance calculations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The DeleteExpense event containing the expense ID to delete
|
||||
/// [emit]: State emitter function
|
||||
@@ -157,11 +167,11 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [MarkSplitAsPaid] events.
|
||||
///
|
||||
///
|
||||
/// Marks a user's portion of an expense split as paid, updating the
|
||||
/// expense's split information and affecting balance calculations.
|
||||
/// This helps track who has settled their portion of shared expenses.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The MarkSplitAsPaid event containing expense ID and user ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -179,11 +189,11 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Handles [ArchiveExpense] events.
|
||||
///
|
||||
///
|
||||
/// Archives an expense, moving it out of the active expense list
|
||||
/// while preserving it for historical records and audit purposes.
|
||||
/// Archived expenses are not included in current balance calculations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The ArchiveExpense event containing the expense ID to archive
|
||||
/// [emit]: State emitter function
|
||||
@@ -201,7 +211,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
}
|
||||
|
||||
/// Cleans up resources when the bloc is closed.
|
||||
///
|
||||
///
|
||||
/// Cancels the expense stream subscription to prevent memory leaks
|
||||
/// and ensure proper disposal of resources.
|
||||
@override
|
||||
@@ -209,4 +219,4 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
_expensesSubscription?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user