Enhance model and service documentation with detailed comments and descriptions

- Updated Group, Trip, User, and other model classes to include comprehensive documentation for better understanding and maintainability.
- Improved error handling and logging in services, including AuthService, ErrorService, and StorageService.
- Added validation and business logic explanations in ExpenseService and TripService.
- Refactored method comments to follow a consistent format across the codebase.
- Translated error messages and comments from French to English for consistency.
This commit is contained in:
Dayron
2025-10-30 15:56:17 +01:00
parent 1eeea6997e
commit 2faf37f145
46 changed files with 2656 additions and 220 deletions

View File

@@ -6,13 +6,29 @@ import '../../services/error_service.dart';
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
/// service to provide business logic and data persistence.
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({
required ExpenseRepository expenseRepository,
ExpenseService? expenseService,
@@ -31,6 +47,10 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
on<ArchiveExpense>(_onArchiveExpense);
}
/// 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(
LoadExpensesByGroup event,
Emitter<ExpenseState> emit,
@@ -47,11 +67,19 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
onError: (error) => add(ExpensesUpdated([], error: error.toString())),
);
} catch (e) {
_errorService.logError('ExpenseBloc', 'Erreur chargement expenses: $e');
_errorService.logError('ExpenseBloc', 'Error loading expenses: $e');
emit(ExpenseError(e.toString()));
}
}
/// 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
Future<void> _onExpensesUpdated(
ExpensesUpdated event,
Emitter<ExpenseState> emit,
@@ -63,71 +91,119 @@ 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
Future<void> _onCreateExpense(
CreateExpense event,
Emitter<ExpenseState> emit,
) async {
try {
await _expenseService.createExpenseWithValidation(event.expense, event.receiptImage);
emit(const ExpenseOperationSuccess('pense créée avec succès'));
emit(const ExpenseOperationSuccess('Expense created successfully'));
} catch (e) {
_errorService.logError('ExpenseBloc', 'Erreur création expense: $e');
_errorService.logError('ExpenseBloc', 'Error creating expense: $e');
emit(ExpenseError(e.toString()));
}
}
/// 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
Future<void> _onUpdateExpense(
UpdateExpense event,
Emitter<ExpenseState> emit,
) async {
try {
await _expenseService.updateExpenseWithValidation(event.expense, event.newReceiptImage);
emit(const ExpenseOperationSuccess('pense modifiée avec succès'));
emit(const ExpenseOperationSuccess('Expense updated successfully'));
} catch (e) {
_errorService.logError('ExpenseBloc', 'Erreur mise à jour expense: $e');
_errorService.logError('ExpenseBloc', 'Error updating expense: $e');
emit(ExpenseError(e.toString()));
}
}
/// 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
Future<void> _onDeleteExpense(
DeleteExpense event,
Emitter<ExpenseState> emit,
) async {
try {
await _expenseRepository.deleteExpense(event.expenseId);
emit(const ExpenseOperationSuccess('pense supprimée avec succès'));
emit(const ExpenseOperationSuccess('Expense deleted successfully'));
} catch (e) {
_errorService.logError('ExpenseBloc', 'Erreur suppression expense: $e');
_errorService.logError('ExpenseBloc', 'Error deleting expense: $e');
emit(ExpenseError(e.toString()));
}
}
/// 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
Future<void> _onMarkSplitAsPaid(
MarkSplitAsPaid event,
Emitter<ExpenseState> emit,
) async {
try {
await _expenseRepository.markSplitAsPaid(event.expenseId, event.userId);
emit(const ExpenseOperationSuccess('Paiement marqué comme effectué'));
emit(const ExpenseOperationSuccess('Payment marked as completed'));
} catch (e) {
_errorService.logError('ExpenseBloc', 'Erreur mark split paid: $e');
_errorService.logError('ExpenseBloc', 'Error marking split as paid: $e');
emit(ExpenseError(e.toString()));
}
}
/// 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
Future<void> _onArchiveExpense(
ArchiveExpense event,
Emitter<ExpenseState> emit,
) async {
try {
await _expenseRepository.archiveExpense(event.expenseId);
emit(const ExpenseOperationSuccess('pense archivée avec succès'));
emit(const ExpenseOperationSuccess('Expense archived successfully'));
} catch (e) {
_errorService.logError('ExpenseBloc', 'Erreur archivage expense: $e');
_errorService.logError('ExpenseBloc', 'Error archiving expense: $e');
emit(ExpenseError(e.toString()));
}
}
/// Cleans up resources when the bloc is closed.
///
/// Cancels the expense stream subscription to prevent memory leaks
/// and ensure proper disposal of resources.
@override
Future<void> close() {
_expensesSubscription?.cancel();

View File

@@ -2,26 +2,45 @@ import 'package:equatable/equatable.dart';
import '../../models/expense.dart';
import 'dart:io';
/// Abstract base class for all expense-related events.
///
/// This class extends [Equatable] to enable value equality for event comparison.
/// All expense events in the application should inherit from this class.
abstract class ExpenseEvent extends Equatable {
/// Creates a new [ExpenseEvent].
const ExpenseEvent();
@override
List<Object?> get props => [];
}
/// Event to load all expenses for a specific group.
///
/// This event triggers the loading of all expenses associated with
/// the specified group, setting up a stream to receive real-time updates.
class LoadExpensesByGroup extends ExpenseEvent {
/// The ID of the group to load expenses for.
final String groupId;
/// Creates a [LoadExpensesByGroup] event for the specified [groupId].
const LoadExpensesByGroup(this.groupId);
@override
List<Object?> get props => [groupId];
}
/// Event to create a new expense.
///
/// This event is dispatched when a user wants to add a new expense
/// to a group, optionally including a receipt image.
class CreateExpense extends ExpenseEvent {
/// The expense data to create.
final Expense expense;
/// Optional receipt image file to upload with the expense.
final File? receiptImage;
/// Creates a [CreateExpense] event with the expense data and optional receipt.
const CreateExpense({
required this.expense,
this.receiptImage,
@@ -31,10 +50,18 @@ class CreateExpense extends ExpenseEvent {
List<Object?> get props => [expense, receiptImage];
}
/// Event to update an existing expense.
///
/// This event is dispatched when a user modifies an existing expense,
/// optionally changing the receipt image.
class UpdateExpense extends ExpenseEvent {
/// The updated expense data.
final Expense expense;
/// Optional new receipt image file to replace the existing one.
final File? newReceiptImage;
/// Creates an [UpdateExpense] event with updated expense data.
const UpdateExpense({
required this.expense,
this.newReceiptImage,
@@ -44,19 +71,33 @@ class UpdateExpense extends ExpenseEvent {
List<Object?> get props => [expense, newReceiptImage];
}
/// Event to delete an expense.
///
/// This event is dispatched when a user wants to permanently
/// remove an expense from the group.
class DeleteExpense extends ExpenseEvent {
/// The ID of the expense to delete.
final String expenseId;
/// Creates a [DeleteExpense] event for the specified [expenseId].
const DeleteExpense(this.expenseId);
@override
List<Object?> get props => [expenseId];
}
/// Event to mark a user's split of an expense as paid.
///
/// This event is used when a user has paid their portion of
/// a shared expense to the person who originally paid for it.
class MarkSplitAsPaid extends ExpenseEvent {
/// The ID of the expense containing the split.
final String expenseId;
/// The ID of the user whose split is being marked as paid.
final String userId;
/// Creates a [MarkSplitAsPaid] event for the specified expense and user.
const MarkSplitAsPaid({
required this.expenseId,
required this.userId,
@@ -66,20 +107,33 @@ class MarkSplitAsPaid extends ExpenseEvent {
List<Object?> get props => [expenseId, userId];
}
/// Event to archive an expense.
///
/// This event moves an expense to an archived state, hiding it
/// from the main expense list while preserving it for history.
class ArchiveExpense extends ExpenseEvent {
/// The ID of the expense to archive.
final String expenseId;
/// Creates an [ArchiveExpense] event for the specified [expenseId].
const ArchiveExpense(this.expenseId);
@override
List<Object?> get props => [expenseId];
}
// Événement privé pour les mises à jour du stream
/// Internal event for handling expense stream updates.
///
/// This is a private event used internally by the bloc to handle
/// real-time updates from the Firestore stream.
class ExpensesUpdated extends ExpenseEvent {
/// The updated list of expenses from the stream.
final List<Expense> expenses;
/// Optional error message if the stream encountered an error.
final String? error;
/// Creates an [ExpensesUpdated] event with the expense list and optional error.
const ExpensesUpdated(this.expenses, {this.error});
@override

View File

@@ -1,21 +1,47 @@
import 'package:equatable/equatable.dart';
import '../../models/expense.dart';
/// Abstract base class for all expense-related states.
///
/// This class extends [Equatable] to enable value equality for state comparison.
/// All expense states in the application should inherit from this class.
abstract class ExpenseState extends Equatable {
/// Creates a new [ExpenseState].
const ExpenseState();
@override
List<Object?> get props => [];
}
/// Initial state of the expense bloc.
///
/// This state represents the initial state before any expense
/// operations have been performed.
class ExpenseInitial extends ExpenseState {}
/// State indicating that an expense operation is in progress.
///
/// This state is used to show loading indicators during expense
/// operations like loading, creating, updating, or deleting expenses.
class ExpenseLoading extends ExpenseState {}
/// State indicating that expenses have been successfully loaded.
///
/// This state contains the list of expenses for a group and
/// exchange rates for currency conversion calculations.
class ExpensesLoaded extends ExpenseState {
/// List of expenses for the current group.
final List<Expense> expenses;
/// Exchange rates for currency conversion.
///
/// Maps currency codes to their exchange rates relative to EUR.
/// Used for converting different currencies to a common base for calculations.
final Map<String, double> exchangeRates;
/// Creates an [ExpensesLoaded] state with expenses and exchange rates.
///
/// [exchangeRates] defaults to common rates if not provided.
const ExpensesLoaded({
required this.expenses,
this.exchangeRates = const {'EUR': 1.0, 'USD': 0.85, 'GBP': 1.15},
@@ -25,18 +51,30 @@ class ExpensesLoaded extends ExpenseState {
List<Object?> get props => [expenses, exchangeRates];
}
/// State indicating that an expense operation has completed successfully.
///
/// This state is used to show success messages after operations like
/// creating, updating, deleting, or archiving expenses.
class ExpenseOperationSuccess extends ExpenseState {
/// Success message to display to the user.
final String message;
/// Creates an [ExpenseOperationSuccess] state with the given [message].
const ExpenseOperationSuccess(this.message);
@override
List<Object?> get props => [message];
}
/// State indicating that an expense operation has failed.
///
/// This state contains an error message that can be displayed to the user
/// when expense operations fail.
class ExpenseError extends ExpenseState {
/// The error message describing what went wrong.
final String message;
/// Creates an [ExpenseError] state with the given error [message].
const ExpenseError(this.message);
@override