Files
unionflow-server-api/unionflow-mobile-apps/lib/features/cotisations/presentation/bloc/cotisations_bloc.dart
2025-09-13 19:05:06 +00:00

510 lines
16 KiB
Dart

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../../../core/models/cotisation_model.dart';
import '../../domain/repositories/cotisation_repository.dart';
import 'cotisations_event.dart';
import 'cotisations_state.dart';
/// BLoC pour la gestion des cotisations
/// Gère l'état et les événements liés aux cotisations
@injectable
class CotisationsBloc extends Bloc<CotisationsEvent, CotisationsState> {
final CotisationRepository _cotisationRepository;
CotisationsBloc(this._cotisationRepository) : super(const CotisationsInitial()) {
// Enregistrement des handlers d'événements
on<LoadCotisations>(_onLoadCotisations);
on<LoadCotisationById>(_onLoadCotisationById);
on<LoadCotisationByReference>(_onLoadCotisationByReference);
on<CreateCotisation>(_onCreateCotisation);
on<UpdateCotisation>(_onUpdateCotisation);
on<DeleteCotisation>(_onDeleteCotisation);
on<LoadCotisationsByMembre>(_onLoadCotisationsByMembre);
on<LoadCotisationsByStatut>(_onLoadCotisationsByStatut);
on<LoadCotisationsEnRetard>(_onLoadCotisationsEnRetard);
on<SearchCotisations>(_onSearchCotisations);
on<LoadCotisationsStats>(_onLoadCotisationsStats);
on<RefreshCotisations>(_onRefreshCotisations);
on<ResetCotisationsState>(_onResetCotisationsState);
on<FilterCotisations>(_onFilterCotisations);
on<SortCotisations>(_onSortCotisations);
}
/// Handler pour charger la liste des cotisations
Future<void> _onLoadCotisations(
LoadCotisations event,
Emitter<CotisationsState> emit,
) async {
try {
if (event.refresh || state is CotisationsInitial) {
emit(CotisationsLoading(isRefreshing: event.refresh));
}
final cotisations = await _cotisationRepository.getCotisations(
page: event.page,
size: event.size,
);
List<CotisationModel> allCotisations = [];
// Si c'est un refresh ou la première page, remplacer la liste
if (event.refresh || event.page == 0) {
allCotisations = cotisations;
} else {
// Sinon, ajouter à la liste existante (pagination)
if (state is CotisationsLoaded) {
final currentState = state as CotisationsLoaded;
allCotisations = [...currentState.cotisations, ...cotisations];
} else {
allCotisations = cotisations;
}
}
emit(CotisationsLoaded(
cotisations: allCotisations,
filteredCotisations: allCotisations,
hasReachedMax: cotisations.length < event.size,
currentPage: event.page,
));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement des cotisations: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour charger une cotisation par ID
Future<void> _onLoadCotisationById(
LoadCotisationById event,
Emitter<CotisationsState> emit,
) async {
try {
emit(const CotisationsLoading());
final cotisation = await _cotisationRepository.getCotisationById(event.id);
emit(CotisationDetailLoaded(cotisation));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement de la cotisation: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour charger une cotisation par référence
Future<void> _onLoadCotisationByReference(
LoadCotisationByReference event,
Emitter<CotisationsState> emit,
) async {
try {
emit(const CotisationsLoading());
final cotisation = await _cotisationRepository.getCotisationByReference(event.numeroReference);
emit(CotisationDetailLoaded(cotisation));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement de la cotisation: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour créer une nouvelle cotisation
Future<void> _onCreateCotisation(
CreateCotisation event,
Emitter<CotisationsState> emit,
) async {
try {
emit(const CotisationOperationLoading('create'));
final nouvelleCotisation = await _cotisationRepository.createCotisation(event.cotisation);
emit(CotisationCreated(nouvelleCotisation));
// Recharger la liste des cotisations
add(const LoadCotisations(refresh: true));
} catch (error) {
emit(CotisationsError(
'Erreur lors de la création de la cotisation: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour mettre à jour une cotisation
Future<void> _onUpdateCotisation(
UpdateCotisation event,
Emitter<CotisationsState> emit,
) async {
try {
emit(CotisationOperationLoading('update', cotisationId: event.id));
final cotisationMiseAJour = await _cotisationRepository.updateCotisation(
event.id,
event.cotisation,
);
emit(CotisationUpdated(cotisationMiseAJour));
// Mettre à jour la liste si elle est chargée
if (state is CotisationsLoaded) {
final currentState = state as CotisationsLoaded;
final updatedList = currentState.cotisations.map((c) {
return c.id == event.id ? cotisationMiseAJour : c;
}).toList();
emit(currentState.copyWith(
cotisations: updatedList,
filteredCotisations: updatedList,
));
}
} catch (error) {
emit(CotisationsError(
'Erreur lors de la mise à jour de la cotisation: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour supprimer une cotisation
Future<void> _onDeleteCotisation(
DeleteCotisation event,
Emitter<CotisationsState> emit,
) async {
try {
emit(CotisationOperationLoading('delete', cotisationId: event.id));
await _cotisationRepository.deleteCotisation(event.id);
emit(CotisationDeleted(event.id));
// Retirer de la liste si elle est chargée
if (state is CotisationsLoaded) {
final currentState = state as CotisationsLoaded;
final updatedList = currentState.cotisations
.where((c) => c.id != event.id)
.toList();
emit(currentState.copyWith(
cotisations: updatedList,
filteredCotisations: updatedList,
));
}
} catch (error) {
emit(CotisationsError(
'Erreur lors de la suppression de la cotisation: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour charger les cotisations d'un membre
Future<void> _onLoadCotisationsByMembre(
LoadCotisationsByMembre event,
Emitter<CotisationsState> emit,
) async {
try {
if (event.refresh || event.page == 0) {
emit(CotisationsLoading(isRefreshing: event.refresh));
}
final cotisations = await _cotisationRepository.getCotisationsByMembre(
event.membreId,
page: event.page,
size: event.size,
);
List<CotisationModel> allCotisations = [];
if (event.refresh || event.page == 0) {
allCotisations = cotisations;
} else {
if (state is CotisationsByMembreLoaded) {
final currentState = state as CotisationsByMembreLoaded;
allCotisations = [...currentState.cotisations, ...cotisations];
} else {
allCotisations = cotisations;
}
}
emit(CotisationsByMembreLoaded(
membreId: event.membreId,
cotisations: allCotisations,
hasReachedMax: cotisations.length < event.size,
currentPage: event.page,
));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement des cotisations du membre: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour charger les cotisations par statut
Future<void> _onLoadCotisationsByStatut(
LoadCotisationsByStatut event,
Emitter<CotisationsState> emit,
) async {
try {
if (event.refresh || event.page == 0) {
emit(CotisationsLoading(isRefreshing: event.refresh));
}
final cotisations = await _cotisationRepository.getCotisationsByStatut(
event.statut,
page: event.page,
size: event.size,
);
List<CotisationModel> allCotisations = [];
if (event.refresh || event.page == 0) {
allCotisations = cotisations;
} else {
if (state is CotisationsLoaded) {
final currentState = state as CotisationsLoaded;
allCotisations = [...currentState.cotisations, ...cotisations];
} else {
allCotisations = cotisations;
}
}
emit(CotisationsLoaded(
cotisations: allCotisations,
filteredCotisations: allCotisations,
hasReachedMax: cotisations.length < event.size,
currentPage: event.page,
currentFilter: event.statut,
));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement des cotisations par statut: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour charger les cotisations en retard
Future<void> _onLoadCotisationsEnRetard(
LoadCotisationsEnRetard event,
Emitter<CotisationsState> emit,
) async {
try {
if (event.refresh || event.page == 0) {
emit(CotisationsLoading(isRefreshing: event.refresh));
}
final cotisations = await _cotisationRepository.getCotisationsEnRetard(
page: event.page,
size: event.size,
);
List<CotisationModel> allCotisations = [];
if (event.refresh || event.page == 0) {
allCotisations = cotisations;
} else {
if (state is CotisationsEnRetardLoaded) {
final currentState = state as CotisationsEnRetardLoaded;
allCotisations = [...currentState.cotisations, ...cotisations];
} else {
allCotisations = cotisations;
}
}
emit(CotisationsEnRetardLoaded(
cotisations: allCotisations,
hasReachedMax: cotisations.length < event.size,
currentPage: event.page,
));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement des cotisations en retard: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour la recherche de cotisations
Future<void> _onSearchCotisations(
SearchCotisations event,
Emitter<CotisationsState> emit,
) async {
try {
if (event.refresh || event.page == 0) {
emit(CotisationsLoading(isRefreshing: event.refresh));
}
final cotisations = await _cotisationRepository.rechercherCotisations(
membreId: event.membreId,
statut: event.statut,
typeCotisation: event.typeCotisation,
annee: event.annee,
mois: event.mois,
page: event.page,
size: event.size,
);
final searchCriteria = <String, dynamic>{
if (event.membreId != null) 'membreId': event.membreId,
if (event.statut != null) 'statut': event.statut,
if (event.typeCotisation != null) 'typeCotisation': event.typeCotisation,
if (event.annee != null) 'annee': event.annee,
if (event.mois != null) 'mois': event.mois,
};
List<CotisationModel> allCotisations = [];
if (event.refresh || event.page == 0) {
allCotisations = cotisations;
} else {
if (state is CotisationsSearchResults) {
final currentState = state as CotisationsSearchResults;
allCotisations = [...currentState.cotisations, ...cotisations];
} else {
allCotisations = cotisations;
}
}
emit(CotisationsSearchResults(
cotisations: allCotisations,
searchCriteria: searchCriteria,
hasReachedMax: cotisations.length < event.size,
currentPage: event.page,
));
} catch (error) {
emit(CotisationsError(
'Erreur lors de la recherche de cotisations: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour charger les statistiques
Future<void> _onLoadCotisationsStats(
LoadCotisationsStats event,
Emitter<CotisationsState> emit,
) async {
try {
emit(const CotisationsLoading());
final statistics = await _cotisationRepository.getCotisationsStats();
emit(CotisationsStatsLoaded(statistics));
} catch (error) {
emit(CotisationsError(
'Erreur lors du chargement des statistiques: ${error.toString()}',
originalError: error,
));
}
}
/// Handler pour rafraîchir les données
Future<void> _onRefreshCotisations(
RefreshCotisations event,
Emitter<CotisationsState> emit,
) async {
add(const LoadCotisations(refresh: true));
}
/// Handler pour réinitialiser l'état
Future<void> _onResetCotisationsState(
ResetCotisationsState event,
Emitter<CotisationsState> emit,
) async {
emit(const CotisationsInitial());
}
/// Handler pour filtrer les cotisations localement
Future<void> _onFilterCotisations(
FilterCotisations event,
Emitter<CotisationsState> emit,
) async {
if (state is CotisationsLoaded) {
final currentState = state as CotisationsLoaded;
List<CotisationModel> filteredList = currentState.cotisations;
// Filtrage par recherche textuelle
if (event.searchQuery != null && event.searchQuery!.isNotEmpty) {
final query = event.searchQuery!.toLowerCase();
filteredList = filteredList.where((cotisation) {
return cotisation.numeroReference.toLowerCase().contains(query) ||
(cotisation.nomMembre?.toLowerCase().contains(query) ?? false) ||
cotisation.typeCotisation.toLowerCase().contains(query) ||
(cotisation.description?.toLowerCase().contains(query) ?? false);
}).toList();
}
// Filtrage par statut
if (event.statutFilter != null && event.statutFilter!.isNotEmpty) {
filteredList = filteredList.where((cotisation) {
return cotisation.statut == event.statutFilter;
}).toList();
}
// Filtrage par type
if (event.typeFilter != null && event.typeFilter!.isNotEmpty) {
filteredList = filteredList.where((cotisation) {
return cotisation.typeCotisation == event.typeFilter;
}).toList();
}
emit(currentState.copyWith(
filteredCotisations: filteredList,
searchQuery: event.searchQuery,
currentFilter: event.statutFilter ?? event.typeFilter,
));
}
}
/// Handler pour trier les cotisations
Future<void> _onSortCotisations(
SortCotisations event,
Emitter<CotisationsState> emit,
) async {
if (state is CotisationsLoaded) {
final currentState = state as CotisationsLoaded;
List<CotisationModel> sortedList = [...currentState.filteredCotisations];
switch (event.sortBy) {
case 'dateEcheance':
sortedList.sort((a, b) => event.ascending
? a.dateEcheance.compareTo(b.dateEcheance)
: b.dateEcheance.compareTo(a.dateEcheance));
break;
case 'montantDu':
sortedList.sort((a, b) => event.ascending
? a.montantDu.compareTo(b.montantDu)
: b.montantDu.compareTo(a.montantDu));
break;
case 'statut':
sortedList.sort((a, b) => event.ascending
? a.statut.compareTo(b.statut)
: b.statut.compareTo(a.statut));
break;
case 'nomMembre':
sortedList.sort((a, b) => event.ascending
? (a.nomMembre ?? '').compareTo(b.nomMembre ?? '')
: (b.nomMembre ?? '').compareTo(a.nomMembre ?? ''));
break;
case 'typeCotisation':
sortedList.sort((a, b) => event.ascending
? a.typeCotisation.compareTo(b.typeCotisation)
: b.typeCotisation.compareTo(a.typeCotisation));
break;
default:
// Tri par défaut par date d'échéance
sortedList.sort((a, b) => b.dateEcheance.compareTo(a.dateEcheance));
}
emit(currentState.copyWith(filteredCotisations: sortedList));
}
}
}