Refactoring - Version OK

This commit is contained in:
dahoud
2025-11-17 16:02:04 +00:00
parent 3f00a26308
commit 3b9ffac8cd
198 changed files with 18010 additions and 11383 deletions

View File

@@ -0,0 +1,597 @@
/// BLoC pour la gestion des contributions
library contributions_bloc;
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/utils/logger.dart';
import '../data/models/contribution_model.dart';
import 'contributions_event.dart';
import 'contributions_state.dart';
/// BLoC pour gérer l'état des contributions
class ContributionsBloc extends Bloc<ContributionsEvent, ContributionsState> {
ContributionsBloc() : super(const ContributionsInitial()) {
on<LoadContributions>(_onLoadContributions);
on<LoadContributionById>(_onLoadContributionById);
on<CreateContribution>(_onCreateContribution);
on<UpdateContribution>(_onUpdateContribution);
on<DeleteContribution>(_onDeleteContribution);
on<SearchContributions>(_onSearchContributions);
on<LoadContributionsByMembre>(_onLoadContributionsByMembre);
on<LoadContributionsPayees>(_onLoadContributionsPayees);
on<LoadContributionsNonPayees>(_onLoadContributionsNonPayees);
on<LoadContributionsEnRetard>(_onLoadContributionsEnRetard);
on<RecordPayment>(_onRecordPayment);
on<LoadContributionsStats>(_onLoadContributionsStats);
on<GenerateAnnualContributions>(_onGenerateAnnualContributions);
on<SendPaymentReminder>(_onSendPaymentReminder);
}
/// Charger la liste des contributions
Future<void> _onLoadContributions(
LoadContributions event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'LoadContributions', data: {
'page': event.page,
'size': event.size,
});
emit(const ContributionsLoading(message: 'Chargement des contributions...'));
// Simuler un délai réseau
await Future.delayed(const Duration(milliseconds: 500));
// Données mock
final contributions = _getMockContributions();
final total = contributions.length;
final totalPages = (total / event.size).ceil();
// Pagination
final start = event.page * event.size;
final end = (start + event.size).clamp(0, total);
final paginatedContributions = contributions.sublist(
start.clamp(0, total),
end,
);
emit(ContributionsLoaded(
contributions: paginatedContributions,
total: total,
page: event.page,
size: event.size,
totalPages: totalPages,
));
AppLogger.blocState('ContributionsBloc', 'ContributionsLoaded', data: {
'count': paginatedContributions.length,
'total': total,
});
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors du chargement des contributions',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Erreur lors du chargement des contributions',
error: e,
));
}
}
/// Charger une contribution par ID
Future<void> _onLoadContributionById(
LoadContributionById event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'LoadContributionById', data: {
'id': event.id,
});
emit(const ContributionsLoading(message: 'Chargement de la contribution...'));
await Future.delayed(const Duration(milliseconds: 300));
final contributions = _getMockContributions();
final contribution = contributions.firstWhere(
(c) => c.id == event.id,
orElse: () => throw Exception('Contribution non trouvée'),
);
emit(ContributionDetailLoaded(contribution: contribution));
AppLogger.blocState('ContributionsBloc', 'ContributionDetailLoaded');
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors du chargement de la contribution',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Contribution non trouvée',
error: e,
));
}
}
/// Créer une nouvelle contribution
Future<void> _onCreateContribution(
CreateContribution event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'CreateContribution');
emit(const ContributionsLoading(message: 'Création de la contribution...'));
await Future.delayed(const Duration(milliseconds: 500));
final newContribution = event.contribution.copyWith(
id: 'cont_${DateTime.now().millisecondsSinceEpoch}',
dateCreation: DateTime.now(),
);
emit(ContributionCreated(contribution: newContribution));
AppLogger.blocState('ContributionsBloc', 'ContributionCreated');
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors de la création de la contribution',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Erreur lors de la création de la contribution',
error: e,
));
}
}
/// Mettre à jour une contribution
Future<void> _onUpdateContribution(
UpdateContribution event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'UpdateContribution', data: {
'id': event.id,
});
emit(const ContributionsLoading(message: 'Mise à jour de la contribution...'));
await Future.delayed(const Duration(milliseconds: 500));
final updatedContribution = event.contribution.copyWith(
id: event.id,
dateModification: DateTime.now(),
);
emit(ContributionUpdated(contribution: updatedContribution));
AppLogger.blocState('ContributionsBloc', 'ContributionUpdated');
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors de la mise à jour de la contribution',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Erreur lors de la mise à jour de la contribution',
error: e,
));
}
}
/// Supprimer une contribution
Future<void> _onDeleteContribution(
DeleteContribution event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'DeleteContribution', data: {
'id': event.id,
});
emit(const ContributionsLoading(message: 'Suppression de la contribution...'));
await Future.delayed(const Duration(milliseconds: 500));
emit(ContributionDeleted(id: event.id));
AppLogger.blocState('ContributionsBloc', 'ContributionDeleted');
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors de la suppression de la contribution',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Erreur lors de la suppression de la contribution',
error: e,
));
}
}
/// Rechercher des contributions
Future<void> _onSearchContributions(
SearchContributions event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'SearchContributions');
emit(const ContributionsLoading(message: 'Recherche en cours...'));
await Future.delayed(const Duration(milliseconds: 500));
var contributions = _getMockContributions();
// Filtrer par membre
if (event.membreId != null) {
contributions = contributions
.where((c) => c.membreId == event.membreId)
.toList();
}
// Filtrer par statut
if (event.statut != null) {
contributions = contributions
.where((c) => c.statut == event.statut)
.toList();
}
// Filtrer par type
if (event.type != null) {
contributions = contributions
.where((c) => c.type == event.type)
.toList();
}
// Filtrer par année
if (event.annee != null) {
contributions = contributions
.where((c) => c.annee == event.annee)
.toList();
}
final total = contributions.length;
final totalPages = (total / event.size).ceil();
// Pagination
final start = event.page * event.size;
final end = (start + event.size).clamp(0, total);
final paginatedContributions = contributions.sublist(
start.clamp(0, total),
end,
);
emit(ContributionsLoaded(
contributions: paginatedContributions,
total: total,
page: event.page,
size: event.size,
totalPages: totalPages,
));
AppLogger.blocState('ContributionsBloc', 'ContributionsLoaded (search)');
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors de la recherche de contributions',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Erreur lors de la recherche',
error: e,
));
}
}
/// Charger les contributions d'un membre
Future<void> _onLoadContributionsByMembre(
LoadContributionsByMembre event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'LoadContributionsByMembre', data: {
'membreId': event.membreId,
});
emit(const ContributionsLoading(message: 'Chargement des contributions du membre...'));
await Future.delayed(const Duration(milliseconds: 500));
final contributions = _getMockContributions()
.where((c) => c.membreId == event.membreId)
.toList();
final total = contributions.length;
final totalPages = (total / event.size).ceil();
emit(ContributionsLoaded(
contributions: contributions,
total: total,
page: event.page,
size: event.size,
totalPages: totalPages,
));
AppLogger.blocState('ContributionsBloc', 'ContributionsLoaded (by membre)');
} catch (e, stackTrace) {
AppLogger.error(
'Erreur lors du chargement des contributions du membre',
error: e,
stackTrace: stackTrace,
);
emit(ContributionsError(
message: 'Erreur lors du chargement',
error: e,
));
}
}
/// Charger les contributions payées
Future<void> _onLoadContributionsPayees(
LoadContributionsPayees event,
Emitter<ContributionsState> emit,
) async {
try {
emit(const ContributionsLoading(message: 'Chargement des contributions payées...'));
await Future.delayed(const Duration(milliseconds: 500));
final contributions = _getMockContributions()
.where((c) => c.statut == ContributionStatus.payee)
.toList();
final total = contributions.length;
final totalPages = (total / event.size).ceil();
emit(ContributionsLoaded(
contributions: contributions,
total: total,
page: event.page,
size: event.size,
totalPages: totalPages,
));
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur', error: e));
}
}
/// Charger les contributions non payées
Future<void> _onLoadContributionsNonPayees(
LoadContributionsNonPayees event,
Emitter<ContributionsState> emit,
) async {
try {
emit(const ContributionsLoading(message: 'Chargement des contributions non payées...'));
await Future.delayed(const Duration(milliseconds: 500));
final contributions = _getMockContributions()
.where((c) => c.statut == ContributionStatus.nonPayee)
.toList();
final total = contributions.length;
final totalPages = (total / event.size).ceil();
emit(ContributionsLoaded(
contributions: contributions,
total: total,
page: event.page,
size: event.size,
totalPages: totalPages,
));
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur', error: e));
}
}
/// Charger les contributions en retard
Future<void> _onLoadContributionsEnRetard(
LoadContributionsEnRetard event,
Emitter<ContributionsState> emit,
) async {
try {
emit(const ContributionsLoading(message: 'Chargement des contributions en retard...'));
await Future.delayed(const Duration(milliseconds: 500));
final contributions = _getMockContributions()
.where((c) => c.statut == ContributionStatus.enRetard)
.toList();
final total = contributions.length;
final totalPages = (total / event.size).ceil();
emit(ContributionsLoaded(
contributions: contributions,
total: total,
page: event.page,
size: event.size,
totalPages: totalPages,
));
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur', error: e));
}
}
/// Enregistrer un paiement
Future<void> _onRecordPayment(
RecordPayment event,
Emitter<ContributionsState> emit,
) async {
try {
AppLogger.blocEvent('ContributionsBloc', 'RecordPayment');
emit(const ContributionsLoading(message: 'Enregistrement du paiement...'));
await Future.delayed(const Duration(milliseconds: 500));
final contributions = _getMockContributions();
final contribution = contributions.firstWhere((c) => c.id == event.contributionId);
final updatedContribution = contribution.copyWith(
montantPaye: event.montant,
datePaiement: event.datePaiement,
methodePaiement: event.methodePaiement,
numeroPaiement: event.numeroPaiement,
referencePaiement: event.referencePaiement,
statut: event.montant >= contribution.montant
? ContributionStatus.payee
: ContributionStatus.partielle,
dateModification: DateTime.now(),
);
emit(PaymentRecorded(contribution: updatedContribution));
AppLogger.blocState('ContributionsBloc', 'PaymentRecorded');
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur lors de l\'enregistrement du paiement', error: e));
}
}
/// Charger les statistiques
Future<void> _onLoadContributionsStats(
LoadContributionsStats event,
Emitter<ContributionsState> emit,
) async {
try {
emit(const ContributionsLoading(message: 'Chargement des statistiques...'));
await Future.delayed(const Duration(milliseconds: 500));
final contributions = _getMockContributions();
final stats = {
'total': contributions.length,
'payees': contributions.where((c) => c.statut == ContributionStatus.payee).length,
'nonPayees': contributions.where((c) => c.statut == ContributionStatus.nonPayee).length,
'enRetard': contributions.where((c) => c.statut == ContributionStatus.enRetard).length,
'partielles': contributions.where((c) => c.statut == ContributionStatus.partielle).length,
'montantTotal': contributions.fold<double>(0, (sum, c) => sum + c.montant),
'montantPaye': contributions.fold<double>(0, (sum, c) => sum + (c.montantPaye ?? 0)),
'montantRestant': contributions.fold<double>(0, (sum, c) => sum + c.montantRestant),
'tauxRecouvrement': 0.0,
};
if (stats['montantTotal']! > 0) {
stats['tauxRecouvrement'] = (stats['montantPaye']! / stats['montantTotal']!) * 100;
}
emit(ContributionsStatsLoaded(stats: stats));
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur', error: e));
}
}
/// Générer les contributions annuelles
Future<void> _onGenerateAnnualContributions(
GenerateAnnualContributions event,
Emitter<ContributionsState> emit,
) async {
try {
emit(const ContributionsLoading(message: 'Génération des contributions...'));
await Future.delayed(const Duration(seconds: 1));
// Simuler la génération de 50 contributions
emit(const ContributionsGenerated(nombreGenere: 50));
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur', error: e));
}
}
/// Envoyer un rappel de paiement
Future<void> _onSendPaymentReminder(
SendPaymentReminder event,
Emitter<ContributionsState> emit,
) async {
try {
emit(const ContributionsLoading(message: 'Envoi du rappel...'));
await Future.delayed(const Duration(milliseconds: 500));
emit(ReminderSent(contributionId: event.contributionId));
} catch (e, stackTrace) {
AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
emit(ContributionsError(message: 'Erreur', error: e));
}
}
/// Données mock pour les tests
List<ContributionModel> _getMockContributions() {
final now = DateTime.now();
return [
ContributionModel(
id: 'cont_001',
membreId: 'mbr_001',
membreNom: 'Dupont',
membrePrenom: 'Jean',
montant: 50000,
dateEcheance: DateTime(now.year, 12, 31),
annee: now.year,
statut: ContributionStatus.payee,
montantPaye: 50000,
datePaiement: DateTime(now.year, 1, 15),
methodePaiement: PaymentMethod.virement,
),
ContributionModel(
id: 'cont_002',
membreId: 'mbr_002',
membreNom: 'Martin',
membrePrenom: 'Marie',
montant: 50000,
dateEcheance: DateTime(now.year, 12, 31),
annee: now.year,
statut: ContributionStatus.nonPayee,
),
ContributionModel(
id: 'cont_003',
membreId: 'mbr_003',
membreNom: 'Bernard',
membrePrenom: 'Pierre',
montant: 50000,
dateEcheance: DateTime(now.year - 1, 12, 31),
annee: now.year - 1,
statut: ContributionStatus.enRetard,
),
ContributionModel(
id: 'cont_004',
membreId: 'mbr_004',
membreNom: 'Dubois',
membrePrenom: 'Sophie',
montant: 50000,
dateEcheance: DateTime(now.year, 12, 31),
annee: now.year,
statut: ContributionStatus.partielle,
montantPaye: 25000,
datePaiement: DateTime(now.year, 2, 10),
methodePaiement: PaymentMethod.especes,
),
ContributionModel(
id: 'cont_005',
membreId: 'mbr_005',
membreNom: 'Petit',
membrePrenom: 'Luc',
montant: 50000,
dateEcheance: DateTime(now.year, 12, 31),
annee: now.year,
statut: ContributionStatus.payee,
montantPaye: 50000,
datePaiement: DateTime(now.year, 3, 5),
methodePaiement: PaymentMethod.mobileMoney,
),
];
}
}

View File

@@ -0,0 +1,225 @@
/// Événements pour le BLoC des contributions
library contributions_event;
import 'package:equatable/equatable.dart';
import '../data/models/contribution_model.dart';
/// Classe de base pour tous les événements de contributions
abstract class ContributionsEvent extends Equatable {
const ContributionsEvent();
@override
List<Object?> get props => [];
}
/// Charger la liste des contributions
class LoadContributions extends ContributionsEvent {
final int page;
final int size;
const LoadContributions({
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [page, size];
}
/// Charger une contribution par ID
class LoadContributionById extends ContributionsEvent {
final String id;
const LoadContributionById({required this.id});
@override
List<Object?> get props => [id];
}
/// Créer une nouvelle contribution
class CreateContribution extends ContributionsEvent {
final ContributionModel contribution;
const CreateContribution({required this.contribution});
@override
List<Object?> get props => [contribution];
}
/// Mettre à jour une contribution
class UpdateContribution extends ContributionsEvent {
final String id;
final ContributionModel contribution;
const UpdateContribution({
required this.id,
required this.contribution,
});
@override
List<Object?> get props => [id, contribution];
}
/// Supprimer une contribution
class DeleteContribution extends ContributionsEvent {
final String id;
const DeleteContribution({required this.id});
@override
List<Object?> get props => [id];
}
/// Rechercher des contributions
class SearchContributions extends ContributionsEvent {
final String? membreId;
final ContributionStatus? statut;
final ContributionType? type;
final int? annee;
final String? query;
final int page;
final int size;
const SearchContributions({
this.membreId,
this.statut,
this.type,
this.annee,
this.query,
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [membreId, statut, type, annee, query, page, size];
}
/// Charger les contributions d'un membre
class LoadContributionsByMembre extends ContributionsEvent {
final String membreId;
final int page;
final int size;
const LoadContributionsByMembre({
required this.membreId,
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [membreId, page, size];
}
/// Charger les contributions payées
class LoadContributionsPayees extends ContributionsEvent {
final int page;
final int size;
const LoadContributionsPayees({
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [page, size];
}
/// Charger les contributions non payées
class LoadContributionsNonPayees extends ContributionsEvent {
final int page;
final int size;
const LoadContributionsNonPayees({
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [page, size];
}
/// Charger les contributions en retard
class LoadContributionsEnRetard extends ContributionsEvent {
final int page;
final int size;
const LoadContributionsEnRetard({
this.page = 0,
this.size = 20,
});
@override
List<Object?> get props => [page, size];
}
/// Enregistrer un paiement
class RecordPayment extends ContributionsEvent {
final String contributionId;
final double montant;
final PaymentMethod methodePaiement;
final String? numeroPaiement;
final String? referencePaiement;
final DateTime datePaiement;
final String? notes;
final String? reference;
const RecordPayment({
required this.contributionId,
required this.montant,
required this.methodePaiement,
this.numeroPaiement,
this.referencePaiement,
required this.datePaiement,
this.notes,
this.reference,
});
@override
List<Object?> get props => [
contributionId,
montant,
methodePaiement,
numeroPaiement,
referencePaiement,
datePaiement,
notes,
reference,
];
}
/// Charger les statistiques des contributions
class LoadContributionsStats extends ContributionsEvent {
final int? annee;
const LoadContributionsStats({this.annee});
@override
List<Object?> get props => [annee];
}
/// Générer les contributions annuelles
class GenerateAnnualContributions extends ContributionsEvent {
final int annee;
final double montant;
final DateTime dateEcheance;
const GenerateAnnualContributions({
required this.annee,
required this.montant,
required this.dateEcheance,
});
@override
List<Object?> get props => [annee, montant, dateEcheance];
}
/// Envoyer un rappel de paiement
class SendPaymentReminder extends ContributionsEvent {
final String contributionId;
const SendPaymentReminder({required this.contributionId});
@override
List<Object?> get props => [contributionId];
}

View File

@@ -0,0 +1,172 @@
/// États pour le BLoC des contributions
library contributions_state;
import 'package:equatable/equatable.dart';
import '../data/models/contribution_model.dart';
/// Classe de base pour tous les états de contributions
abstract class ContributionsState extends Equatable {
const ContributionsState();
@override
List<Object?> get props => [];
}
/// État initial
class ContributionsInitial extends ContributionsState {
const ContributionsInitial();
}
/// État de chargement
class ContributionsLoading extends ContributionsState {
final String? message;
const ContributionsLoading({this.message});
@override
List<Object?> get props => [message];
}
/// État de rafraîchissement
class ContributionsRefreshing extends ContributionsState {
const ContributionsRefreshing();
}
/// État chargé avec succès
class ContributionsLoaded extends ContributionsState {
final List<ContributionModel> contributions;
final int total;
final int page;
final int size;
final int totalPages;
const ContributionsLoaded({
required this.contributions,
required this.total,
required this.page,
required this.size,
required this.totalPages,
});
@override
List<Object?> get props => [contributions, total, page, size, totalPages];
}
/// État détail d'une contribution chargé
class ContributionDetailLoaded extends ContributionsState {
final ContributionModel contribution;
const ContributionDetailLoaded({required this.contribution});
@override
List<Object?> get props => [contribution];
}
/// État contribution créée
class ContributionCreated extends ContributionsState {
final ContributionModel contribution;
const ContributionCreated({required this.contribution});
@override
List<Object?> get props => [contribution];
}
/// État contribution mise à jour
class ContributionUpdated extends ContributionsState {
final ContributionModel contribution;
const ContributionUpdated({required this.contribution});
@override
List<Object?> get props => [contribution];
}
/// État contribution supprimée
class ContributionDeleted extends ContributionsState {
final String id;
const ContributionDeleted({required this.id});
@override
List<Object?> get props => [id];
}
/// État paiement enregistré
class PaymentRecorded extends ContributionsState {
final ContributionModel contribution;
const PaymentRecorded({required this.contribution});
@override
List<Object?> get props => [contribution];
}
/// État statistiques chargées
class ContributionsStatsLoaded extends ContributionsState {
final Map<String, dynamic> stats;
const ContributionsStatsLoaded({required this.stats});
@override
List<Object?> get props => [stats];
}
/// État contributions générées
class ContributionsGenerated extends ContributionsState {
final int nombreGenere;
const ContributionsGenerated({required this.nombreGenere});
@override
List<Object?> get props => [nombreGenere];
}
/// État rappel envoyé
class ReminderSent extends ContributionsState {
final String contributionId;
const ReminderSent({required this.contributionId});
@override
List<Object?> get props => [contributionId];
}
/// État d'erreur générique
class ContributionsError extends ContributionsState {
final String message;
final dynamic error;
const ContributionsError({
required this.message,
this.error,
});
@override
List<Object?> get props => [message, error];
}
/// État d'erreur réseau
class ContributionsNetworkError extends ContributionsState {
final String message;
const ContributionsNetworkError({required this.message});
@override
List<Object?> get props => [message];
}
/// État d'erreur de validation
class ContributionsValidationError extends ContributionsState {
final String message;
final Map<String, String>? fieldErrors;
const ContributionsValidationError({
required this.message,
this.fieldErrors,
});
@override
List<Object?> get props => [message, fieldErrors];
}