Refactoring
This commit is contained in:
@@ -0,0 +1,481 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Entité représentant une demande d'aide dans le système de solidarité
|
||||
///
|
||||
/// Cette entité encapsule toutes les informations relatives à une demande d'aide,
|
||||
/// incluant les détails du demandeur, le type d'aide, les montants et le statut.
|
||||
class DemandeAide extends Equatable {
|
||||
/// Identifiant unique de la demande
|
||||
final String id;
|
||||
|
||||
/// Numéro de référence unique (format: DA-YYYY-NNNNNN)
|
||||
final String numeroReference;
|
||||
|
||||
/// Titre de la demande d'aide
|
||||
final String titre;
|
||||
|
||||
/// Description détaillée de la demande
|
||||
final String description;
|
||||
|
||||
/// Type d'aide demandée
|
||||
final TypeAide typeAide;
|
||||
|
||||
/// Statut actuel de la demande
|
||||
final StatutAide statut;
|
||||
|
||||
/// Priorité de la demande
|
||||
final PrioriteAide priorite;
|
||||
|
||||
/// Identifiant du demandeur
|
||||
final String demandeurId;
|
||||
|
||||
/// Nom complet du demandeur
|
||||
final String nomDemandeur;
|
||||
|
||||
/// Identifiant de l'organisation
|
||||
final String organisationId;
|
||||
|
||||
/// Montant demandé (si applicable)
|
||||
final double? montantDemande;
|
||||
|
||||
/// Montant approuvé (si applicable)
|
||||
final double? montantApprouve;
|
||||
|
||||
/// Montant versé (si applicable)
|
||||
final double? montantVerse;
|
||||
|
||||
/// Date de création de la demande
|
||||
final DateTime dateCreation;
|
||||
|
||||
/// Date de modification
|
||||
final DateTime dateModification;
|
||||
|
||||
/// Date de soumission
|
||||
final DateTime? dateSoumission;
|
||||
|
||||
/// Date d'évaluation
|
||||
final DateTime? dateEvaluation;
|
||||
|
||||
/// Date d'approbation
|
||||
final DateTime? dateApprobation;
|
||||
|
||||
/// Date limite de traitement
|
||||
final DateTime? dateLimiteTraitement;
|
||||
|
||||
/// Identifiant de l'évaluateur assigné
|
||||
final String? evaluateurId;
|
||||
|
||||
/// Commentaires de l'évaluateur
|
||||
final String? commentairesEvaluateur;
|
||||
|
||||
/// Motif de rejet (si applicable)
|
||||
final String? motifRejet;
|
||||
|
||||
/// Informations complémentaires requises
|
||||
final String? informationsRequises;
|
||||
|
||||
/// Justification de l'urgence
|
||||
final String? justificationUrgence;
|
||||
|
||||
/// Contact d'urgence
|
||||
final ContactUrgence? contactUrgence;
|
||||
|
||||
/// Localisation du demandeur
|
||||
final Localisation? localisation;
|
||||
|
||||
/// Liste des bénéficiaires
|
||||
final List<BeneficiaireAide> beneficiaires;
|
||||
|
||||
/// Liste des pièces justificatives
|
||||
final List<PieceJustificative> piecesJustificatives;
|
||||
|
||||
/// Historique des changements de statut
|
||||
final List<HistoriqueStatut> historiqueStatuts;
|
||||
|
||||
/// Commentaires et échanges
|
||||
final List<CommentaireAide> commentaires;
|
||||
|
||||
/// Données personnalisées
|
||||
final Map<String, dynamic> donneesPersonnalisees;
|
||||
|
||||
/// Indique si la demande est modifiable
|
||||
final bool estModifiable;
|
||||
|
||||
/// Indique si la demande est urgente
|
||||
final bool estUrgente;
|
||||
|
||||
/// Indique si le délai est dépassé
|
||||
final bool delaiDepasse;
|
||||
|
||||
/// Indique si la demande est terminée
|
||||
final bool estTerminee;
|
||||
|
||||
const DemandeAide({
|
||||
required this.id,
|
||||
required this.numeroReference,
|
||||
required this.titre,
|
||||
required this.description,
|
||||
required this.typeAide,
|
||||
required this.statut,
|
||||
required this.priorite,
|
||||
required this.demandeurId,
|
||||
required this.nomDemandeur,
|
||||
required this.organisationId,
|
||||
this.montantDemande,
|
||||
this.montantApprouve,
|
||||
this.montantVerse,
|
||||
required this.dateCreation,
|
||||
required this.dateModification,
|
||||
this.dateSoumission,
|
||||
this.dateEvaluation,
|
||||
this.dateApprobation,
|
||||
this.dateLimiteTraitement,
|
||||
this.evaluateurId,
|
||||
this.commentairesEvaluateur,
|
||||
this.motifRejet,
|
||||
this.informationsRequises,
|
||||
this.justificationUrgence,
|
||||
this.contactUrgence,
|
||||
this.localisation,
|
||||
this.beneficiaires = const [],
|
||||
this.piecesJustificatives = const [],
|
||||
this.historiqueStatuts = const [],
|
||||
this.commentaires = const [],
|
||||
this.donneesPersonnalisees = const {},
|
||||
this.estModifiable = false,
|
||||
this.estUrgente = false,
|
||||
this.delaiDepasse = false,
|
||||
this.estTerminee = false,
|
||||
});
|
||||
|
||||
/// Calcule le pourcentage d'avancement de la demande
|
||||
double get pourcentageAvancement {
|
||||
return statut.pourcentageAvancement;
|
||||
}
|
||||
|
||||
/// Calcule le délai restant en heures
|
||||
int? get delaiRestantHeures {
|
||||
if (dateLimiteTraitement == null) return null;
|
||||
|
||||
final maintenant = DateTime.now();
|
||||
if (maintenant.isAfter(dateLimiteTraitement!)) return 0;
|
||||
|
||||
return dateLimiteTraitement!.difference(maintenant).inHours;
|
||||
}
|
||||
|
||||
/// Calcule la durée de traitement en jours
|
||||
int get dureeTraitementJours {
|
||||
if (dateSoumission == null) return 0;
|
||||
|
||||
final dateFin = dateEvaluation ?? DateTime.now();
|
||||
return dateFin.difference(dateSoumission!).inDays;
|
||||
}
|
||||
|
||||
/// Indique si la demande nécessite une action urgente
|
||||
bool get necessiteActionUrgente {
|
||||
return estUrgente || delaiDepasse || priorite == PrioriteAide.critique;
|
||||
}
|
||||
|
||||
/// Obtient la couleur associée au statut
|
||||
String get couleurStatut => statut.couleur;
|
||||
|
||||
/// Obtient l'icône associée au type d'aide
|
||||
String get iconeTypeAide => typeAide.icone;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
numeroReference,
|
||||
titre,
|
||||
description,
|
||||
typeAide,
|
||||
statut,
|
||||
priorite,
|
||||
demandeurId,
|
||||
nomDemandeur,
|
||||
organisationId,
|
||||
montantDemande,
|
||||
montantApprouve,
|
||||
montantVerse,
|
||||
dateCreation,
|
||||
dateModification,
|
||||
dateSoumission,
|
||||
dateEvaluation,
|
||||
dateApprobation,
|
||||
dateLimiteTraitement,
|
||||
evaluateurId,
|
||||
commentairesEvaluateur,
|
||||
motifRejet,
|
||||
informationsRequises,
|
||||
justificationUrgence,
|
||||
contactUrgence,
|
||||
localisation,
|
||||
beneficiaires,
|
||||
piecesJustificatives,
|
||||
historiqueStatuts,
|
||||
commentaires,
|
||||
donneesPersonnalisees,
|
||||
estModifiable,
|
||||
estUrgente,
|
||||
delaiDepasse,
|
||||
estTerminee,
|
||||
];
|
||||
|
||||
DemandeAide copyWith({
|
||||
String? id,
|
||||
String? numeroReference,
|
||||
String? titre,
|
||||
String? description,
|
||||
TypeAide? typeAide,
|
||||
StatutAide? statut,
|
||||
PrioriteAide? priorite,
|
||||
String? demandeurId,
|
||||
String? nomDemandeur,
|
||||
String? organisationId,
|
||||
double? montantDemande,
|
||||
double? montantApprouve,
|
||||
double? montantVerse,
|
||||
DateTime? dateCreation,
|
||||
DateTime? dateModification,
|
||||
DateTime? dateSoumission,
|
||||
DateTime? dateEvaluation,
|
||||
DateTime? dateApprobation,
|
||||
DateTime? dateLimiteTraitement,
|
||||
String? evaluateurId,
|
||||
String? commentairesEvaluateur,
|
||||
String? motifRejet,
|
||||
String? informationsRequises,
|
||||
String? justificationUrgence,
|
||||
ContactUrgence? contactUrgence,
|
||||
Localisation? localisation,
|
||||
List<BeneficiaireAide>? beneficiaires,
|
||||
List<PieceJustificative>? piecesJustificatives,
|
||||
List<HistoriqueStatut>? historiqueStatuts,
|
||||
List<CommentaireAide>? commentaires,
|
||||
Map<String, dynamic>? donneesPersonnalisees,
|
||||
bool? estModifiable,
|
||||
bool? estUrgente,
|
||||
bool? delaiDepasse,
|
||||
bool? estTerminee,
|
||||
}) {
|
||||
return DemandeAide(
|
||||
id: id ?? this.id,
|
||||
numeroReference: numeroReference ?? this.numeroReference,
|
||||
titre: titre ?? this.titre,
|
||||
description: description ?? this.description,
|
||||
typeAide: typeAide ?? this.typeAide,
|
||||
statut: statut ?? this.statut,
|
||||
priorite: priorite ?? this.priorite,
|
||||
demandeurId: demandeurId ?? this.demandeurId,
|
||||
nomDemandeur: nomDemandeur ?? this.nomDemandeur,
|
||||
organisationId: organisationId ?? this.organisationId,
|
||||
montantDemande: montantDemande ?? this.montantDemande,
|
||||
montantApprouve: montantApprouve ?? this.montantApprouve,
|
||||
montantVerse: montantVerse ?? this.montantVerse,
|
||||
dateCreation: dateCreation ?? this.dateCreation,
|
||||
dateModification: dateModification ?? this.dateModification,
|
||||
dateSoumission: dateSoumission ?? this.dateSoumission,
|
||||
dateEvaluation: dateEvaluation ?? this.dateEvaluation,
|
||||
dateApprobation: dateApprobation ?? this.dateApprobation,
|
||||
dateLimiteTraitement: dateLimiteTraitement ?? this.dateLimiteTraitement,
|
||||
evaluateurId: evaluateurId ?? this.evaluateurId,
|
||||
commentairesEvaluateur: commentairesEvaluateur ?? this.commentairesEvaluateur,
|
||||
motifRejet: motifRejet ?? this.motifRejet,
|
||||
informationsRequises: informationsRequises ?? this.informationsRequises,
|
||||
justificationUrgence: justificationUrgence ?? this.justificationUrgence,
|
||||
contactUrgence: contactUrgence ?? this.contactUrgence,
|
||||
localisation: localisation ?? this.localisation,
|
||||
beneficiaires: beneficiaires ?? this.beneficiaires,
|
||||
piecesJustificatives: piecesJustificatives ?? this.piecesJustificatives,
|
||||
historiqueStatuts: historiqueStatuts ?? this.historiqueStatuts,
|
||||
commentaires: commentaires ?? this.commentaires,
|
||||
donneesPersonnalisees: donneesPersonnalisees ?? this.donneesPersonnalisees,
|
||||
estModifiable: estModifiable ?? this.estModifiable,
|
||||
estUrgente: estUrgente ?? this.estUrgente,
|
||||
delaiDepasse: delaiDepasse ?? this.delaiDepasse,
|
||||
estTerminee: estTerminee ?? this.estTerminee,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Énumération des types d'aide disponibles
|
||||
enum TypeAide {
|
||||
aideFinanciereUrgente('Aide financière urgente', 'emergency_fund', '#F44336'),
|
||||
aideFinanciereMedicale('Aide financière médicale', 'medical_services', '#2196F3'),
|
||||
aideFinanciereEducation('Aide financière éducation', 'school', '#4CAF50'),
|
||||
aideMaterielleVetements('Aide matérielle vêtements', 'checkroom', '#FF9800'),
|
||||
aideMaterielleNourriture('Aide matérielle nourriture', 'restaurant', '#795548'),
|
||||
aideProfessionnelleFormation('Aide professionnelle formation', 'work', '#9C27B0'),
|
||||
aideSocialeAccompagnement('Aide sociale accompagnement', 'support', '#607D8B'),
|
||||
autre('Autre', 'help', '#9E9E9E');
|
||||
|
||||
const TypeAide(this.libelle, this.icone, this.couleur);
|
||||
|
||||
final String libelle;
|
||||
final String icone;
|
||||
final String couleur;
|
||||
}
|
||||
|
||||
/// Énumération des statuts de demande d'aide
|
||||
enum StatutAide {
|
||||
brouillon('Brouillon', 'draft', '#9E9E9E', 5.0),
|
||||
soumise('Soumise', 'send', '#2196F3', 10.0),
|
||||
enAttente('En attente', 'schedule', '#FF9800', 20.0),
|
||||
enCoursEvaluation('En cours d\'évaluation', 'assessment', '#9C27B0', 40.0),
|
||||
approuvee('Approuvée', 'check_circle', '#4CAF50', 70.0),
|
||||
approuveePartiellement('Approuvée partiellement', 'check_circle_outline', '#8BC34A', 70.0),
|
||||
rejetee('Rejetée', 'cancel', '#F44336', 100.0),
|
||||
informationsRequises('Informations requises', 'info', '#FF5722', 30.0),
|
||||
enCoursVersement('En cours de versement', 'payment', '#00BCD4', 85.0),
|
||||
versee('Versée', 'paid', '#4CAF50', 100.0),
|
||||
livree('Livrée', 'local_shipping', '#4CAF50', 100.0),
|
||||
terminee('Terminée', 'done_all', '#4CAF50', 100.0),
|
||||
cloturee('Clôturée', 'archive', '#607D8B', 100.0);
|
||||
|
||||
const StatutAide(this.libelle, this.icone, this.couleur, this.pourcentageAvancement);
|
||||
|
||||
final String libelle;
|
||||
final String icone;
|
||||
final String couleur;
|
||||
final double pourcentageAvancement;
|
||||
}
|
||||
|
||||
/// Énumération des priorités de demande d'aide
|
||||
enum PrioriteAide {
|
||||
critique('Critique', '#F44336', 1, 24),
|
||||
urgente('Urgente', '#FF5722', 2, 72),
|
||||
elevee('Élevée', '#FF9800', 3, 168),
|
||||
normale('Normale', '#4CAF50', 4, 336),
|
||||
faible('Faible', '#9E9E9E', 5, 720);
|
||||
|
||||
const PrioriteAide(this.libelle, this.couleur, this.niveau, this.delaiTraitementHeures);
|
||||
|
||||
final String libelle;
|
||||
final String couleur;
|
||||
final int niveau;
|
||||
final int delaiTraitementHeures;
|
||||
}
|
||||
|
||||
/// Classe représentant un contact d'urgence
|
||||
class ContactUrgence extends Equatable {
|
||||
final String nom;
|
||||
final String telephone;
|
||||
final String? email;
|
||||
final String relation;
|
||||
|
||||
const ContactUrgence({
|
||||
required this.nom,
|
||||
required this.telephone,
|
||||
this.email,
|
||||
required this.relation,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [nom, telephone, email, relation];
|
||||
}
|
||||
|
||||
/// Classe représentant une localisation
|
||||
class Localisation extends Equatable {
|
||||
final String adresse;
|
||||
final String ville;
|
||||
final String? codePostal;
|
||||
final String? pays;
|
||||
final double? latitude;
|
||||
final double? longitude;
|
||||
|
||||
const Localisation({
|
||||
required this.adresse,
|
||||
required this.ville,
|
||||
this.codePostal,
|
||||
this.pays,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [adresse, ville, codePostal, pays, latitude, longitude];
|
||||
}
|
||||
|
||||
/// Classe représentant un bénéficiaire d'aide
|
||||
class BeneficiaireAide extends Equatable {
|
||||
final String nom;
|
||||
final String prenom;
|
||||
final int age;
|
||||
final String relation;
|
||||
final String? telephone;
|
||||
|
||||
const BeneficiaireAide({
|
||||
required this.nom,
|
||||
required this.prenom,
|
||||
required this.age,
|
||||
required this.relation,
|
||||
this.telephone,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [nom, prenom, age, relation, telephone];
|
||||
}
|
||||
|
||||
/// Classe représentant une pièce justificative
|
||||
class PieceJustificative extends Equatable {
|
||||
final String id;
|
||||
final String nom;
|
||||
final String type;
|
||||
final String url;
|
||||
final int taille;
|
||||
final DateTime dateAjout;
|
||||
|
||||
const PieceJustificative({
|
||||
required this.id,
|
||||
required this.nom,
|
||||
required this.type,
|
||||
required this.url,
|
||||
required this.taille,
|
||||
required this.dateAjout,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, nom, type, url, taille, dateAjout];
|
||||
}
|
||||
|
||||
/// Classe représentant l'historique des statuts
|
||||
class HistoriqueStatut extends Equatable {
|
||||
final StatutAide ancienStatut;
|
||||
final StatutAide nouveauStatut;
|
||||
final DateTime dateChangement;
|
||||
final String? commentaire;
|
||||
final String? utilisateurId;
|
||||
|
||||
const HistoriqueStatut({
|
||||
required this.ancienStatut,
|
||||
required this.nouveauStatut,
|
||||
required this.dateChangement,
|
||||
this.commentaire,
|
||||
this.utilisateurId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [ancienStatut, nouveauStatut, dateChangement, commentaire, utilisateurId];
|
||||
}
|
||||
|
||||
/// Classe représentant un commentaire sur une demande
|
||||
class CommentaireAide extends Equatable {
|
||||
final String id;
|
||||
final String contenu;
|
||||
final String auteurId;
|
||||
final String nomAuteur;
|
||||
final DateTime dateCreation;
|
||||
final bool estPrive;
|
||||
|
||||
const CommentaireAide({
|
||||
required this.id,
|
||||
required this.contenu,
|
||||
required this.auteurId,
|
||||
required this.nomAuteur,
|
||||
required this.dateCreation,
|
||||
this.estPrive = false,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, contenu, auteurId, nomAuteur, dateCreation, estPrive];
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Entité représentant une évaluation d'aide dans le système de solidarité
|
||||
///
|
||||
/// Cette entité encapsule toutes les informations relatives à l'évaluation
|
||||
/// d'une demande d'aide ou d'une proposition d'aide.
|
||||
class EvaluationAide extends Equatable {
|
||||
/// Identifiant unique de l'évaluation
|
||||
final String id;
|
||||
|
||||
/// Identifiant de la demande d'aide évaluée
|
||||
final String demandeAideId;
|
||||
|
||||
/// Identifiant de la proposition d'aide (si applicable)
|
||||
final String? propositionAideId;
|
||||
|
||||
/// Identifiant de l'évaluateur
|
||||
final String evaluateurId;
|
||||
|
||||
/// Nom de l'évaluateur
|
||||
final String nomEvaluateur;
|
||||
|
||||
/// Type d'évaluateur
|
||||
final TypeEvaluateur typeEvaluateur;
|
||||
|
||||
/// Note globale (1 à 5)
|
||||
final double noteGlobale;
|
||||
|
||||
/// Note pour le délai de réponse
|
||||
final double? noteDelaiReponse;
|
||||
|
||||
/// Note pour la communication
|
||||
final double? noteCommunication;
|
||||
|
||||
/// Note pour le professionnalisme
|
||||
final double? noteProfessionnalisme;
|
||||
|
||||
/// Note pour le respect des engagements
|
||||
final double? noteRespectEngagements;
|
||||
|
||||
/// Notes détaillées par critère
|
||||
final Map<String, double> notesDetaillees;
|
||||
|
||||
/// Commentaire principal
|
||||
final String commentairePrincipal;
|
||||
|
||||
/// Points positifs
|
||||
final String? pointsPositifs;
|
||||
|
||||
/// Points d'amélioration
|
||||
final String? pointsAmelioration;
|
||||
|
||||
/// Recommandations
|
||||
final String? recommandations;
|
||||
|
||||
/// Indique si l'évaluateur recommande cette aide
|
||||
final bool? recommande;
|
||||
|
||||
/// Date de création de l'évaluation
|
||||
final DateTime dateCreation;
|
||||
|
||||
/// Date de modification
|
||||
final DateTime dateModification;
|
||||
|
||||
/// Date de vérification (si applicable)
|
||||
final DateTime? dateVerification;
|
||||
|
||||
/// Identifiant du vérificateur
|
||||
final String? verificateurId;
|
||||
|
||||
/// Statut de l'évaluation
|
||||
final StatutEvaluation statut;
|
||||
|
||||
/// Nombre de signalements reçus
|
||||
final int nombreSignalements;
|
||||
|
||||
/// Score de qualité calculé automatiquement
|
||||
final double scoreQualite;
|
||||
|
||||
/// Indique si l'évaluation a été modifiée
|
||||
final bool estModifie;
|
||||
|
||||
/// Indique si l'évaluation est vérifiée
|
||||
final bool estVerifiee;
|
||||
|
||||
/// Données personnalisées
|
||||
final Map<String, dynamic> donneesPersonnalisees;
|
||||
|
||||
const EvaluationAide({
|
||||
required this.id,
|
||||
required this.demandeAideId,
|
||||
this.propositionAideId,
|
||||
required this.evaluateurId,
|
||||
required this.nomEvaluateur,
|
||||
required this.typeEvaluateur,
|
||||
required this.noteGlobale,
|
||||
this.noteDelaiReponse,
|
||||
this.noteCommunication,
|
||||
this.noteProfessionnalisme,
|
||||
this.noteRespectEngagements,
|
||||
this.notesDetaillees = const {},
|
||||
required this.commentairePrincipal,
|
||||
this.pointsPositifs,
|
||||
this.pointsAmelioration,
|
||||
this.recommandations,
|
||||
this.recommande,
|
||||
required this.dateCreation,
|
||||
required this.dateModification,
|
||||
this.dateVerification,
|
||||
this.verificateurId,
|
||||
this.statut = StatutEvaluation.active,
|
||||
this.nombreSignalements = 0,
|
||||
required this.scoreQualite,
|
||||
this.estModifie = false,
|
||||
this.estVerifiee = false,
|
||||
this.donneesPersonnalisees = const {},
|
||||
});
|
||||
|
||||
/// Calcule la note moyenne des critères détaillés
|
||||
double get noteMoyenneDetaillees {
|
||||
if (notesDetaillees.isEmpty) return noteGlobale;
|
||||
|
||||
double somme = notesDetaillees.values.fold(0.0, (a, b) => a + b);
|
||||
return somme / notesDetaillees.length;
|
||||
}
|
||||
|
||||
/// Indique si l'évaluation est positive (note >= 4)
|
||||
bool get estPositive => noteGlobale >= 4.0;
|
||||
|
||||
/// Indique si l'évaluation est négative (note <= 2)
|
||||
bool get estNegative => noteGlobale <= 2.0;
|
||||
|
||||
/// Obtient le niveau de satisfaction textuel
|
||||
String get niveauSatisfaction {
|
||||
if (noteGlobale >= 4.5) return 'Excellent';
|
||||
if (noteGlobale >= 4.0) return 'Très bien';
|
||||
if (noteGlobale >= 3.0) return 'Bien';
|
||||
if (noteGlobale >= 2.0) return 'Moyen';
|
||||
return 'Insuffisant';
|
||||
}
|
||||
|
||||
/// Obtient la couleur associée à la note
|
||||
String get couleurNote {
|
||||
if (noteGlobale >= 4.0) return '#4CAF50'; // Vert
|
||||
if (noteGlobale >= 3.0) return '#FF9800'; // Orange
|
||||
return '#F44336'; // Rouge
|
||||
}
|
||||
|
||||
/// Indique si l'évaluation peut être modifiée
|
||||
bool get peutEtreModifiee {
|
||||
return statut == StatutEvaluation.active &&
|
||||
!estVerifiee &&
|
||||
nombreSignalements < 3;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
demandeAideId,
|
||||
propositionAideId,
|
||||
evaluateurId,
|
||||
nomEvaluateur,
|
||||
typeEvaluateur,
|
||||
noteGlobale,
|
||||
noteDelaiReponse,
|
||||
noteCommunication,
|
||||
noteProfessionnalisme,
|
||||
noteRespectEngagements,
|
||||
notesDetaillees,
|
||||
commentairePrincipal,
|
||||
pointsPositifs,
|
||||
pointsAmelioration,
|
||||
recommandations,
|
||||
recommande,
|
||||
dateCreation,
|
||||
dateModification,
|
||||
dateVerification,
|
||||
verificateurId,
|
||||
statut,
|
||||
nombreSignalements,
|
||||
scoreQualite,
|
||||
estModifie,
|
||||
estVerifiee,
|
||||
donneesPersonnalisees,
|
||||
];
|
||||
|
||||
EvaluationAide copyWith({
|
||||
String? id,
|
||||
String? demandeAideId,
|
||||
String? propositionAideId,
|
||||
String? evaluateurId,
|
||||
String? nomEvaluateur,
|
||||
TypeEvaluateur? typeEvaluateur,
|
||||
double? noteGlobale,
|
||||
double? noteDelaiReponse,
|
||||
double? noteCommunication,
|
||||
double? noteProfessionnalisme,
|
||||
double? noteRespectEngagements,
|
||||
Map<String, double>? notesDetaillees,
|
||||
String? commentairePrincipal,
|
||||
String? pointsPositifs,
|
||||
String? pointsAmelioration,
|
||||
String? recommandations,
|
||||
bool? recommande,
|
||||
DateTime? dateCreation,
|
||||
DateTime? dateModification,
|
||||
DateTime? dateVerification,
|
||||
String? verificateurId,
|
||||
StatutEvaluation? statut,
|
||||
int? nombreSignalements,
|
||||
double? scoreQualite,
|
||||
bool? estModifie,
|
||||
bool? estVerifiee,
|
||||
Map<String, dynamic>? donneesPersonnalisees,
|
||||
}) {
|
||||
return EvaluationAide(
|
||||
id: id ?? this.id,
|
||||
demandeAideId: demandeAideId ?? this.demandeAideId,
|
||||
propositionAideId: propositionAideId ?? this.propositionAideId,
|
||||
evaluateurId: evaluateurId ?? this.evaluateurId,
|
||||
nomEvaluateur: nomEvaluateur ?? this.nomEvaluateur,
|
||||
typeEvaluateur: typeEvaluateur ?? this.typeEvaluateur,
|
||||
noteGlobale: noteGlobale ?? this.noteGlobale,
|
||||
noteDelaiReponse: noteDelaiReponse ?? this.noteDelaiReponse,
|
||||
noteCommunication: noteCommunication ?? this.noteCommunication,
|
||||
noteProfessionnalisme: noteProfessionnalisme ?? this.noteProfessionnalisme,
|
||||
noteRespectEngagements: noteRespectEngagements ?? this.noteRespectEngagements,
|
||||
notesDetaillees: notesDetaillees ?? this.notesDetaillees,
|
||||
commentairePrincipal: commentairePrincipal ?? this.commentairePrincipal,
|
||||
pointsPositifs: pointsPositifs ?? this.pointsPositifs,
|
||||
pointsAmelioration: pointsAmelioration ?? this.pointsAmelioration,
|
||||
recommandations: recommandations ?? this.recommandations,
|
||||
recommande: recommande ?? this.recommande,
|
||||
dateCreation: dateCreation ?? this.dateCreation,
|
||||
dateModification: dateModification ?? this.dateModification,
|
||||
dateVerification: dateVerification ?? this.dateVerification,
|
||||
verificateurId: verificateurId ?? this.verificateurId,
|
||||
statut: statut ?? this.statut,
|
||||
nombreSignalements: nombreSignalements ?? this.nombreSignalements,
|
||||
scoreQualite: scoreQualite ?? this.scoreQualite,
|
||||
estModifie: estModifie ?? this.estModifie,
|
||||
estVerifiee: estVerifiee ?? this.estVerifiee,
|
||||
donneesPersonnalisees: donneesPersonnalisees ?? this.donneesPersonnalisees,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Énumération des types d'évaluateur
|
||||
enum TypeEvaluateur {
|
||||
beneficiaire('Bénéficiaire', 'person', '#2196F3'),
|
||||
proposant('Proposant', 'volunteer_activism', '#4CAF50'),
|
||||
evaluateurOfficial('Évaluateur officiel', 'verified_user', '#9C27B0'),
|
||||
administrateur('Administrateur', 'admin_panel_settings', '#FF5722');
|
||||
|
||||
const TypeEvaluateur(this.libelle, this.icone, this.couleur);
|
||||
|
||||
final String libelle;
|
||||
final String icone;
|
||||
final String couleur;
|
||||
}
|
||||
|
||||
/// Énumération des statuts d'évaluation
|
||||
enum StatutEvaluation {
|
||||
active('Active', 'check_circle', '#4CAF50'),
|
||||
signalee('Signalée', 'flag', '#FF9800'),
|
||||
masquee('Masquée', 'visibility_off', '#F44336'),
|
||||
supprimee('Supprimée', 'delete', '#9E9E9E');
|
||||
|
||||
const StatutEvaluation(this.libelle, this.icone, this.couleur);
|
||||
|
||||
final String libelle;
|
||||
final String icone;
|
||||
final String couleur;
|
||||
}
|
||||
|
||||
/// Classe représentant les statistiques d'évaluations
|
||||
class StatistiquesEvaluation extends Equatable {
|
||||
final double noteMoyenne;
|
||||
final int nombreEvaluations;
|
||||
final Map<int, int> repartitionNotes;
|
||||
final double pourcentagePositives;
|
||||
final double pourcentageRecommandations;
|
||||
final DateTime derniereMiseAJour;
|
||||
|
||||
const StatistiquesEvaluation({
|
||||
required this.noteMoyenne,
|
||||
required this.nombreEvaluations,
|
||||
required this.repartitionNotes,
|
||||
required this.pourcentagePositives,
|
||||
required this.pourcentageRecommandations,
|
||||
required this.derniereMiseAJour,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
noteMoyenne,
|
||||
nombreEvaluations,
|
||||
repartitionNotes,
|
||||
pourcentagePositives,
|
||||
pourcentageRecommandations,
|
||||
derniereMiseAJour,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'demande_aide.dart';
|
||||
|
||||
/// Entité représentant une proposition d'aide dans le système de solidarité
|
||||
///
|
||||
/// Cette entité encapsule toutes les informations relatives à une proposition d'aide,
|
||||
/// incluant les détails du proposant, les capacités et les conditions.
|
||||
class PropositionAide extends Equatable {
|
||||
/// Identifiant unique de la proposition
|
||||
final String id;
|
||||
|
||||
/// Numéro de référence unique (format: PA-YYYY-NNNNNN)
|
||||
final String numeroReference;
|
||||
|
||||
/// Titre de la proposition d'aide
|
||||
final String titre;
|
||||
|
||||
/// Description détaillée de la proposition
|
||||
final String description;
|
||||
|
||||
/// Type d'aide proposée
|
||||
final TypeAide typeAide;
|
||||
|
||||
/// Statut actuel de la proposition
|
||||
final StatutProposition statut;
|
||||
|
||||
/// Identifiant du proposant
|
||||
final String proposantId;
|
||||
|
||||
/// Nom complet du proposant
|
||||
final String nomProposant;
|
||||
|
||||
/// Identifiant de l'organisation
|
||||
final String organisationId;
|
||||
|
||||
/// Montant maximum proposé (si applicable)
|
||||
final double? montantMaximum;
|
||||
|
||||
/// Montant minimum proposé (si applicable)
|
||||
final double? montantMinimum;
|
||||
|
||||
/// Nombre maximum de bénéficiaires
|
||||
final int nombreMaxBeneficiaires;
|
||||
|
||||
/// Nombre de bénéficiaires déjà aidés
|
||||
final int nombreBeneficiairesAides;
|
||||
|
||||
/// Nombre de demandes traitées
|
||||
final int nombreDemandesTraitees;
|
||||
|
||||
/// Montant total versé
|
||||
final double montantTotalVerse;
|
||||
|
||||
/// Date de création de la proposition
|
||||
final DateTime dateCreation;
|
||||
|
||||
/// Date de modification
|
||||
final DateTime dateModification;
|
||||
|
||||
/// Date d'expiration
|
||||
final DateTime? dateExpiration;
|
||||
|
||||
/// Délai de réponse en heures
|
||||
final int delaiReponseHeures;
|
||||
|
||||
/// Zones géographiques couvertes
|
||||
final List<String> zonesGeographiques;
|
||||
|
||||
/// Créneaux de disponibilité
|
||||
final List<CreneauDisponibilite> creneauxDisponibilite;
|
||||
|
||||
/// Critères de sélection
|
||||
final List<CritereSelection> criteresSelection;
|
||||
|
||||
/// Contact du proposant
|
||||
final ContactProposant contactProposant;
|
||||
|
||||
/// Conditions particulières
|
||||
final String? conditionsParticulieres;
|
||||
|
||||
/// Instructions spéciales
|
||||
final String? instructionsSpeciales;
|
||||
|
||||
/// Note moyenne des évaluations
|
||||
final double? noteMoyenne;
|
||||
|
||||
/// Nombre d'évaluations reçues
|
||||
final int nombreEvaluations;
|
||||
|
||||
/// Nombre de vues de la proposition
|
||||
final int nombreVues;
|
||||
|
||||
/// Nombre de candidatures reçues
|
||||
final int nombreCandidatures;
|
||||
|
||||
/// Score de pertinence calculé
|
||||
final double scorePertinence;
|
||||
|
||||
/// Données personnalisées
|
||||
final Map<String, dynamic> donneesPersonnalisees;
|
||||
|
||||
/// Indique si la proposition est disponible
|
||||
final bool estDisponible;
|
||||
|
||||
/// Indique si la proposition est vérifiée
|
||||
final bool estVerifiee;
|
||||
|
||||
/// Indique si la proposition est expirée
|
||||
final bool estExpiree;
|
||||
|
||||
const PropositionAide({
|
||||
required this.id,
|
||||
required this.numeroReference,
|
||||
required this.titre,
|
||||
required this.description,
|
||||
required this.typeAide,
|
||||
required this.statut,
|
||||
required this.proposantId,
|
||||
required this.nomProposant,
|
||||
required this.organisationId,
|
||||
this.montantMaximum,
|
||||
this.montantMinimum,
|
||||
required this.nombreMaxBeneficiaires,
|
||||
this.nombreBeneficiairesAides = 0,
|
||||
this.nombreDemandesTraitees = 0,
|
||||
this.montantTotalVerse = 0.0,
|
||||
required this.dateCreation,
|
||||
required this.dateModification,
|
||||
this.dateExpiration,
|
||||
this.delaiReponseHeures = 48,
|
||||
this.zonesGeographiques = const [],
|
||||
this.creneauxDisponibilite = const [],
|
||||
this.criteresSelection = const [],
|
||||
required this.contactProposant,
|
||||
this.conditionsParticulieres,
|
||||
this.instructionsSpeciales,
|
||||
this.noteMoyenne,
|
||||
this.nombreEvaluations = 0,
|
||||
this.nombreVues = 0,
|
||||
this.nombreCandidatures = 0,
|
||||
this.scorePertinence = 50.0,
|
||||
this.donneesPersonnalisees = const {},
|
||||
this.estDisponible = true,
|
||||
this.estVerifiee = false,
|
||||
this.estExpiree = false,
|
||||
});
|
||||
|
||||
/// Calcule le nombre de places restantes
|
||||
int get placesRestantes {
|
||||
return nombreMaxBeneficiaires - nombreBeneficiairesAides;
|
||||
}
|
||||
|
||||
/// Calcule le pourcentage de capacité utilisée
|
||||
double get pourcentageCapaciteUtilisee {
|
||||
if (nombreMaxBeneficiaires == 0) return 0.0;
|
||||
return (nombreBeneficiairesAides / nombreMaxBeneficiaires) * 100;
|
||||
}
|
||||
|
||||
/// Indique si la proposition peut accepter de nouveaux bénéficiaires
|
||||
bool get peutAccepterBeneficiaires {
|
||||
return estDisponible && !estExpiree && placesRestantes > 0;
|
||||
}
|
||||
|
||||
/// Indique si la proposition est active et disponible
|
||||
bool get isActiveEtDisponible {
|
||||
return statut == StatutProposition.active && estDisponible && !estExpiree;
|
||||
}
|
||||
|
||||
/// Calcule un score de compatibilité avec une demande
|
||||
double calculerScoreCompatibilite(DemandeAide demande) {
|
||||
double score = 0.0;
|
||||
|
||||
// Correspondance du type d'aide (40 points max)
|
||||
if (demande.typeAide == typeAide) {
|
||||
score += 40.0;
|
||||
} else {
|
||||
// Bonus partiel pour les types similaires
|
||||
score += 20.0;
|
||||
}
|
||||
|
||||
// Compatibilité financière (25 points max)
|
||||
if (demande.montantDemande != null && montantMaximum != null) {
|
||||
if (demande.montantDemande! <= montantMaximum!) {
|
||||
score += 25.0;
|
||||
} else {
|
||||
// Pénalité proportionnelle
|
||||
double ratio = montantMaximum! / demande.montantDemande!;
|
||||
score += 25.0 * ratio;
|
||||
}
|
||||
} else if (demande.montantDemande == null) {
|
||||
score += 25.0; // Pas de contrainte financière
|
||||
}
|
||||
|
||||
// Expérience du proposant (15 points max)
|
||||
if (nombreBeneficiairesAides > 0) {
|
||||
score += (nombreBeneficiairesAides * 2.0).clamp(0.0, 15.0);
|
||||
}
|
||||
|
||||
// Réputation (10 points max)
|
||||
if (noteMoyenne != null && nombreEvaluations >= 3) {
|
||||
score += (noteMoyenne! - 3.0) * 3.33;
|
||||
}
|
||||
|
||||
// Disponibilité (10 points max)
|
||||
if (peutAccepterBeneficiaires) {
|
||||
double ratioCapacite = placesRestantes / nombreMaxBeneficiaires;
|
||||
score += 10.0 * ratioCapacite;
|
||||
}
|
||||
|
||||
return score.clamp(0.0, 100.0);
|
||||
}
|
||||
|
||||
/// Obtient la couleur associée au statut
|
||||
String get couleurStatut => statut.couleur;
|
||||
|
||||
/// Obtient l'icône associée au type d'aide
|
||||
String get iconeTypeAide => typeAide.icone;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
numeroReference,
|
||||
titre,
|
||||
description,
|
||||
typeAide,
|
||||
statut,
|
||||
proposantId,
|
||||
nomProposant,
|
||||
organisationId,
|
||||
montantMaximum,
|
||||
montantMinimum,
|
||||
nombreMaxBeneficiaires,
|
||||
nombreBeneficiairesAides,
|
||||
nombreDemandesTraitees,
|
||||
montantTotalVerse,
|
||||
dateCreation,
|
||||
dateModification,
|
||||
dateExpiration,
|
||||
delaiReponseHeures,
|
||||
zonesGeographiques,
|
||||
creneauxDisponibilite,
|
||||
criteresSelection,
|
||||
contactProposant,
|
||||
conditionsParticulieres,
|
||||
instructionsSpeciales,
|
||||
noteMoyenne,
|
||||
nombreEvaluations,
|
||||
nombreVues,
|
||||
nombreCandidatures,
|
||||
scorePertinence,
|
||||
donneesPersonnalisees,
|
||||
estDisponible,
|
||||
estVerifiee,
|
||||
estExpiree,
|
||||
];
|
||||
|
||||
PropositionAide copyWith({
|
||||
String? id,
|
||||
String? numeroReference,
|
||||
String? titre,
|
||||
String? description,
|
||||
TypeAide? typeAide,
|
||||
StatutProposition? statut,
|
||||
String? proposantId,
|
||||
String? nomProposant,
|
||||
String? organisationId,
|
||||
double? montantMaximum,
|
||||
double? montantMinimum,
|
||||
int? nombreMaxBeneficiaires,
|
||||
int? nombreBeneficiairesAides,
|
||||
int? nombreDemandesTraitees,
|
||||
double? montantTotalVerse,
|
||||
DateTime? dateCreation,
|
||||
DateTime? dateModification,
|
||||
DateTime? dateExpiration,
|
||||
int? delaiReponseHeures,
|
||||
List<String>? zonesGeographiques,
|
||||
List<CreneauDisponibilite>? creneauxDisponibilite,
|
||||
List<CritereSelection>? criteresSelection,
|
||||
ContactProposant? contactProposant,
|
||||
String? conditionsParticulieres,
|
||||
String? instructionsSpeciales,
|
||||
double? noteMoyenne,
|
||||
int? nombreEvaluations,
|
||||
int? nombreVues,
|
||||
int? nombreCandidatures,
|
||||
double? scorePertinence,
|
||||
Map<String, dynamic>? donneesPersonnalisees,
|
||||
bool? estDisponible,
|
||||
bool? estVerifiee,
|
||||
bool? estExpiree,
|
||||
}) {
|
||||
return PropositionAide(
|
||||
id: id ?? this.id,
|
||||
numeroReference: numeroReference ?? this.numeroReference,
|
||||
titre: titre ?? this.titre,
|
||||
description: description ?? this.description,
|
||||
typeAide: typeAide ?? this.typeAide,
|
||||
statut: statut ?? this.statut,
|
||||
proposantId: proposantId ?? this.proposantId,
|
||||
nomProposant: nomProposant ?? this.nomProposant,
|
||||
organisationId: organisationId ?? this.organisationId,
|
||||
montantMaximum: montantMaximum ?? this.montantMaximum,
|
||||
montantMinimum: montantMinimum ?? this.montantMinimum,
|
||||
nombreMaxBeneficiaires: nombreMaxBeneficiaires ?? this.nombreMaxBeneficiaires,
|
||||
nombreBeneficiairesAides: nombreBeneficiairesAides ?? this.nombreBeneficiairesAides,
|
||||
nombreDemandesTraitees: nombreDemandesTraitees ?? this.nombreDemandesTraitees,
|
||||
montantTotalVerse: montantTotalVerse ?? this.montantTotalVerse,
|
||||
dateCreation: dateCreation ?? this.dateCreation,
|
||||
dateModification: dateModification ?? this.dateModification,
|
||||
dateExpiration: dateExpiration ?? this.dateExpiration,
|
||||
delaiReponseHeures: delaiReponseHeures ?? this.delaiReponseHeures,
|
||||
zonesGeographiques: zonesGeographiques ?? this.zonesGeographiques,
|
||||
creneauxDisponibilite: creneauxDisponibilite ?? this.creneauxDisponibilite,
|
||||
criteresSelection: criteresSelection ?? this.criteresSelection,
|
||||
contactProposant: contactProposant ?? this.contactProposant,
|
||||
conditionsParticulieres: conditionsParticulieres ?? this.conditionsParticulieres,
|
||||
instructionsSpeciales: instructionsSpeciales ?? this.instructionsSpeciales,
|
||||
noteMoyenne: noteMoyenne ?? this.noteMoyenne,
|
||||
nombreEvaluations: nombreEvaluations ?? this.nombreEvaluations,
|
||||
nombreVues: nombreVues ?? this.nombreVues,
|
||||
nombreCandidatures: nombreCandidatures ?? this.nombreCandidatures,
|
||||
scorePertinence: scorePertinence ?? this.scorePertinence,
|
||||
donneesPersonnalisees: donneesPersonnalisees ?? this.donneesPersonnalisees,
|
||||
estDisponible: estDisponible ?? this.estDisponible,
|
||||
estVerifiee: estVerifiee ?? this.estVerifiee,
|
||||
estExpiree: estExpiree ?? this.estExpiree,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Énumération des statuts de proposition d'aide
|
||||
enum StatutProposition {
|
||||
active('Active', 'check_circle', '#4CAF50'),
|
||||
suspendue('Suspendue', 'pause_circle', '#FF9800'),
|
||||
terminee('Terminée', 'done_all', '#607D8B'),
|
||||
expiree('Expirée', 'schedule', '#9E9E9E'),
|
||||
supprimee('Supprimée', 'delete', '#F44336');
|
||||
|
||||
const StatutProposition(this.libelle, this.icone, this.couleur);
|
||||
|
||||
final String libelle;
|
||||
final String icone;
|
||||
final String couleur;
|
||||
}
|
||||
|
||||
/// Classe représentant un créneau de disponibilité
|
||||
class CreneauDisponibilite extends Equatable {
|
||||
final String jourSemaine;
|
||||
final String heureDebut;
|
||||
final String heureFin;
|
||||
final String? commentaire;
|
||||
|
||||
const CreneauDisponibilite({
|
||||
required this.jourSemaine,
|
||||
required this.heureDebut,
|
||||
required this.heureFin,
|
||||
this.commentaire,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [jourSemaine, heureDebut, heureFin, commentaire];
|
||||
}
|
||||
|
||||
/// Classe représentant un critère de sélection
|
||||
class CritereSelection extends Equatable {
|
||||
final String nom;
|
||||
final String valeur;
|
||||
final bool estObligatoire;
|
||||
final String? description;
|
||||
|
||||
const CritereSelection({
|
||||
required this.nom,
|
||||
required this.valeur,
|
||||
this.estObligatoire = false,
|
||||
this.description,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [nom, valeur, estObligatoire, description];
|
||||
}
|
||||
|
||||
/// Classe représentant le contact d'un proposant
|
||||
class ContactProposant extends Equatable {
|
||||
final String nom;
|
||||
final String telephone;
|
||||
final String? email;
|
||||
final String? adresse;
|
||||
final String? methodePrefereee;
|
||||
|
||||
const ContactProposant({
|
||||
required this.nom,
|
||||
required this.telephone,
|
||||
this.email,
|
||||
this.adresse,
|
||||
this.methodePrefereee,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [nom, telephone, email, adresse, methodePrefereee];
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../entities/demande_aide.dart';
|
||||
import '../entities/proposition_aide.dart';
|
||||
import '../entities/evaluation_aide.dart';
|
||||
|
||||
/// Repository abstrait pour la gestion de la solidarité
|
||||
///
|
||||
/// Ce repository définit les contrats pour toutes les opérations
|
||||
/// liées au système de solidarité : demandes, propositions, évaluations.
|
||||
abstract class SolidariteRepository {
|
||||
|
||||
// === GESTION DES DEMANDES D'AIDE ===
|
||||
|
||||
/// Crée une nouvelle demande d'aide
|
||||
///
|
||||
/// [demande] La demande d'aide à créer
|
||||
/// Retourne [Right(DemandeAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, DemandeAide>> creerDemandeAide(DemandeAide demande);
|
||||
|
||||
/// Met à jour une demande d'aide existante
|
||||
///
|
||||
/// [demande] La demande d'aide à mettre à jour
|
||||
/// Retourne [Right(DemandeAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, DemandeAide>> mettreAJourDemandeAide(DemandeAide demande);
|
||||
|
||||
/// Obtient une demande d'aide par son ID
|
||||
///
|
||||
/// [id] Identifiant de la demande
|
||||
/// Retourne [Right(DemandeAide)] si trouvée
|
||||
/// Retourne [Left(Failure)] si non trouvée ou erreur
|
||||
Future<Either<Failure, DemandeAide>> obtenirDemandeAide(String id);
|
||||
|
||||
/// Soumet une demande d'aide pour évaluation
|
||||
///
|
||||
/// [demandeId] Identifiant de la demande
|
||||
/// Retourne [Right(DemandeAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, DemandeAide>> soumettreDemande(String demandeId);
|
||||
|
||||
/// Évalue une demande d'aide
|
||||
///
|
||||
/// [demandeId] Identifiant de la demande
|
||||
/// [evaluateurId] Identifiant de l'évaluateur
|
||||
/// [decision] Décision d'évaluation
|
||||
/// [commentaire] Commentaire de l'évaluateur
|
||||
/// [montantApprouve] Montant approuvé (optionnel)
|
||||
/// Retourne [Right(DemandeAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, DemandeAide>> evaluerDemande({
|
||||
required String demandeId,
|
||||
required String evaluateurId,
|
||||
required StatutAide decision,
|
||||
String? commentaire,
|
||||
double? montantApprouve,
|
||||
});
|
||||
|
||||
/// Recherche des demandes d'aide avec filtres
|
||||
///
|
||||
/// [filtres] Critères de recherche
|
||||
/// Retourne [Right(List<DemandeAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<DemandeAide>>> rechercherDemandes({
|
||||
String? organisationId,
|
||||
TypeAide? typeAide,
|
||||
StatutAide? statut,
|
||||
String? demandeurId,
|
||||
bool? urgente,
|
||||
int page = 0,
|
||||
int taille = 20,
|
||||
});
|
||||
|
||||
/// Obtient les demandes urgentes
|
||||
///
|
||||
/// [organisationId] Identifiant de l'organisation
|
||||
/// Retourne [Right(List<DemandeAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<DemandeAide>>> obtenirDemandesUrgentes(String organisationId);
|
||||
|
||||
/// Obtient les demandes de l'utilisateur connecté
|
||||
///
|
||||
/// [utilisateurId] Identifiant de l'utilisateur
|
||||
/// Retourne [Right(List<DemandeAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<DemandeAide>>> obtenirMesdemandes(String utilisateurId);
|
||||
|
||||
// === GESTION DES PROPOSITIONS D'AIDE ===
|
||||
|
||||
/// Crée une nouvelle proposition d'aide
|
||||
///
|
||||
/// [proposition] La proposition d'aide à créer
|
||||
/// Retourne [Right(PropositionAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, PropositionAide>> creerPropositionAide(PropositionAide proposition);
|
||||
|
||||
/// Met à jour une proposition d'aide existante
|
||||
///
|
||||
/// [proposition] La proposition d'aide à mettre à jour
|
||||
/// Retourne [Right(PropositionAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, PropositionAide>> mettreAJourPropositionAide(PropositionAide proposition);
|
||||
|
||||
/// Obtient une proposition d'aide par son ID
|
||||
///
|
||||
/// [id] Identifiant de la proposition
|
||||
/// Retourne [Right(PropositionAide)] si trouvée
|
||||
/// Retourne [Left(Failure)] si non trouvée ou erreur
|
||||
Future<Either<Failure, PropositionAide>> obtenirPropositionAide(String id);
|
||||
|
||||
/// Active ou désactive une proposition d'aide
|
||||
///
|
||||
/// [propositionId] Identifiant de la proposition
|
||||
/// [activer] true pour activer, false pour désactiver
|
||||
/// Retourne [Right(PropositionAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, PropositionAide>> changerStatutProposition({
|
||||
required String propositionId,
|
||||
required bool activer,
|
||||
});
|
||||
|
||||
/// Recherche des propositions d'aide avec filtres
|
||||
///
|
||||
/// [filtres] Critères de recherche
|
||||
/// Retourne [Right(List<PropositionAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<PropositionAide>>> rechercherPropositions({
|
||||
String? organisationId,
|
||||
TypeAide? typeAide,
|
||||
String? proposantId,
|
||||
bool? actives,
|
||||
int page = 0,
|
||||
int taille = 20,
|
||||
});
|
||||
|
||||
/// Obtient les propositions actives pour un type d'aide
|
||||
///
|
||||
/// [typeAide] Type d'aide recherché
|
||||
/// Retourne [Right(List<PropositionAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<PropositionAide>>> obtenirPropositionsActives(TypeAide typeAide);
|
||||
|
||||
/// Obtient les meilleures propositions (top performers)
|
||||
///
|
||||
/// [limite] Nombre maximum de propositions à retourner
|
||||
/// Retourne [Right(List<PropositionAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<PropositionAide>>> obtenirMeilleuresPropositions(int limite);
|
||||
|
||||
/// Obtient les propositions de l'utilisateur connecté
|
||||
///
|
||||
/// [utilisateurId] Identifiant de l'utilisateur
|
||||
/// Retourne [Right(List<PropositionAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<PropositionAide>>> obtenirMesPropositions(String utilisateurId);
|
||||
|
||||
// === MATCHING ET COMPATIBILITÉ ===
|
||||
|
||||
/// Trouve les propositions compatibles avec une demande
|
||||
///
|
||||
/// [demandeId] Identifiant de la demande
|
||||
/// Retourne [Right(List<PropositionAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<PropositionAide>>> trouverPropositionsCompatibles(String demandeId);
|
||||
|
||||
/// Trouve les demandes compatibles avec une proposition
|
||||
///
|
||||
/// [propositionId] Identifiant de la proposition
|
||||
/// Retourne [Right(List<DemandeAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<DemandeAide>>> trouverDemandesCompatibles(String propositionId);
|
||||
|
||||
/// Recherche des proposants financiers pour une demande approuvée
|
||||
///
|
||||
/// [demandeId] Identifiant de la demande
|
||||
/// Retourne [Right(List<PropositionAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<PropositionAide>>> rechercherProposantsFinanciers(String demandeId);
|
||||
|
||||
// === GESTION DES ÉVALUATIONS ===
|
||||
|
||||
/// Crée une nouvelle évaluation
|
||||
///
|
||||
/// [evaluation] L'évaluation à créer
|
||||
/// Retourne [Right(EvaluationAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, EvaluationAide>> creerEvaluation(EvaluationAide evaluation);
|
||||
|
||||
/// Met à jour une évaluation existante
|
||||
///
|
||||
/// [evaluation] L'évaluation à mettre à jour
|
||||
/// Retourne [Right(EvaluationAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, EvaluationAide>> mettreAJourEvaluation(EvaluationAide evaluation);
|
||||
|
||||
/// Obtient une évaluation par son ID
|
||||
///
|
||||
/// [id] Identifiant de l'évaluation
|
||||
/// Retourne [Right(EvaluationAide)] si trouvée
|
||||
/// Retourne [Left(Failure)] si non trouvée ou erreur
|
||||
Future<Either<Failure, EvaluationAide>> obtenirEvaluation(String id);
|
||||
|
||||
/// Obtient les évaluations d'une demande d'aide
|
||||
///
|
||||
/// [demandeId] Identifiant de la demande
|
||||
/// Retourne [Right(List<EvaluationAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<EvaluationAide>>> obtenirEvaluationsDemande(String demandeId);
|
||||
|
||||
/// Obtient les évaluations d'une proposition d'aide
|
||||
///
|
||||
/// [propositionId] Identifiant de la proposition
|
||||
/// Retourne [Right(List<EvaluationAide>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, List<EvaluationAide>>> obtenirEvaluationsProposition(String propositionId);
|
||||
|
||||
/// Signale une évaluation comme inappropriée
|
||||
///
|
||||
/// [evaluationId] Identifiant de l'évaluation
|
||||
/// [motif] Motif du signalement
|
||||
/// Retourne [Right(EvaluationAide)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, EvaluationAide>> signalerEvaluation({
|
||||
required String evaluationId,
|
||||
required String motif,
|
||||
});
|
||||
|
||||
// === STATISTIQUES ET ANALYTICS ===
|
||||
|
||||
/// Obtient les statistiques de solidarité pour une organisation
|
||||
///
|
||||
/// [organisationId] Identifiant de l'organisation
|
||||
/// Retourne [Right(Map<String, dynamic>)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, Map<String, dynamic>>> obtenirStatistiquesSolidarite(String organisationId);
|
||||
|
||||
/// Calcule la note moyenne d'une demande d'aide
|
||||
///
|
||||
/// [demandeId] Identifiant de la demande
|
||||
/// Retourne [Right(StatistiquesEvaluation)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, StatistiquesEvaluation>> calculerMoyenneDemande(String demandeId);
|
||||
|
||||
/// Calcule la note moyenne d'une proposition d'aide
|
||||
///
|
||||
/// [propositionId] Identifiant de la proposition
|
||||
/// Retourne [Right(StatistiquesEvaluation)] en cas de succès
|
||||
/// Retourne [Left(Failure)] en cas d'erreur
|
||||
Future<Either<Failure, StatistiquesEvaluation>> calculerMoyenneProposition(String propositionId);
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/usecases/usecase.dart';
|
||||
import '../entities/demande_aide.dart';
|
||||
import '../repositories/solidarite_repository.dart';
|
||||
|
||||
/// Cas d'usage pour créer une nouvelle demande d'aide
|
||||
class CreerDemandeAideUseCase implements UseCase<DemandeAide, CreerDemandeAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
CreerDemandeAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, DemandeAide>> call(CreerDemandeAideParams params) async {
|
||||
return await repository.creerDemandeAide(params.demande);
|
||||
}
|
||||
}
|
||||
|
||||
class CreerDemandeAideParams {
|
||||
final DemandeAide demande;
|
||||
|
||||
CreerDemandeAideParams({required this.demande});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour mettre à jour une demande d'aide
|
||||
class MettreAJourDemandeAideUseCase implements UseCase<DemandeAide, MettreAJourDemandeAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
MettreAJourDemandeAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, DemandeAide>> call(MettreAJourDemandeAideParams params) async {
|
||||
return await repository.mettreAJourDemandeAide(params.demande);
|
||||
}
|
||||
}
|
||||
|
||||
class MettreAJourDemandeAideParams {
|
||||
final DemandeAide demande;
|
||||
|
||||
MettreAJourDemandeAideParams({required this.demande});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir une demande d'aide par ID
|
||||
class ObtenirDemandeAideUseCase implements UseCase<DemandeAide, ObtenirDemandeAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirDemandeAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, DemandeAide>> call(ObtenirDemandeAideParams params) async {
|
||||
return await repository.obtenirDemandeAide(params.id);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirDemandeAideParams {
|
||||
final String id;
|
||||
|
||||
ObtenirDemandeAideParams({required this.id});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour soumettre une demande d'aide
|
||||
class SoumettreDemandeAideUseCase implements UseCase<DemandeAide, SoumettreDemandeAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
SoumettreDemandeAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, DemandeAide>> call(SoumettreDemandeAideParams params) async {
|
||||
return await repository.soumettreDemande(params.demandeId);
|
||||
}
|
||||
}
|
||||
|
||||
class SoumettreDemandeAideParams {
|
||||
final String demandeId;
|
||||
|
||||
SoumettreDemandeAideParams({required this.demandeId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour évaluer une demande d'aide
|
||||
class EvaluerDemandeAideUseCase implements UseCase<DemandeAide, EvaluerDemandeAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
EvaluerDemandeAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, DemandeAide>> call(EvaluerDemandeAideParams params) async {
|
||||
return await repository.evaluerDemande(
|
||||
demandeId: params.demandeId,
|
||||
evaluateurId: params.evaluateurId,
|
||||
decision: params.decision,
|
||||
commentaire: params.commentaire,
|
||||
montantApprouve: params.montantApprouve,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EvaluerDemandeAideParams {
|
||||
final String demandeId;
|
||||
final String evaluateurId;
|
||||
final StatutAide decision;
|
||||
final String? commentaire;
|
||||
final double? montantApprouve;
|
||||
|
||||
EvaluerDemandeAideParams({
|
||||
required this.demandeId,
|
||||
required this.evaluateurId,
|
||||
required this.decision,
|
||||
this.commentaire,
|
||||
this.montantApprouve,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour rechercher des demandes d'aide
|
||||
class RechercherDemandesAideUseCase implements UseCase<List<DemandeAide>, RechercherDemandesAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
RechercherDemandesAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<DemandeAide>>> call(RechercherDemandesAideParams params) async {
|
||||
return await repository.rechercherDemandes(
|
||||
organisationId: params.organisationId,
|
||||
typeAide: params.typeAide,
|
||||
statut: params.statut,
|
||||
demandeurId: params.demandeurId,
|
||||
urgente: params.urgente,
|
||||
page: params.page,
|
||||
taille: params.taille,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RechercherDemandesAideParams {
|
||||
final String? organisationId;
|
||||
final TypeAide? typeAide;
|
||||
final StatutAide? statut;
|
||||
final String? demandeurId;
|
||||
final bool? urgente;
|
||||
final int page;
|
||||
final int taille;
|
||||
|
||||
RechercherDemandesAideParams({
|
||||
this.organisationId,
|
||||
this.typeAide,
|
||||
this.statut,
|
||||
this.demandeurId,
|
||||
this.urgente,
|
||||
this.page = 0,
|
||||
this.taille = 20,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les demandes urgentes
|
||||
class ObtenirDemandesUrgentesUseCase implements UseCase<List<DemandeAide>, ObtenirDemandesUrgentesParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirDemandesUrgentesUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<DemandeAide>>> call(ObtenirDemandesUrgentesParams params) async {
|
||||
return await repository.obtenirDemandesUrgentes(params.organisationId);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirDemandesUrgentesParams {
|
||||
final String organisationId;
|
||||
|
||||
ObtenirDemandesUrgentesParams({required this.organisationId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les demandes de l'utilisateur connecté
|
||||
class ObtenirMesDemandesUseCase implements UseCase<List<DemandeAide>, ObtenirMesDemandesParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirMesDemandesUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<DemandeAide>>> call(ObtenirMesDemandesParams params) async {
|
||||
return await repository.obtenirMesdemandes(params.utilisateurId);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirMesDemandesParams {
|
||||
final String utilisateurId;
|
||||
|
||||
ObtenirMesDemandesParams({required this.utilisateurId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour valider une demande d'aide avant soumission
|
||||
class ValiderDemandeAideUseCase implements UseCase<bool, ValiderDemandeAideParams> {
|
||||
ValiderDemandeAideUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> call(ValiderDemandeAideParams params) async {
|
||||
try {
|
||||
final demande = params.demande;
|
||||
final erreurs = <String>[];
|
||||
|
||||
// Validation du titre
|
||||
if (demande.titre.trim().isEmpty) {
|
||||
erreurs.add('Le titre est obligatoire');
|
||||
} else if (demande.titre.length < 10) {
|
||||
erreurs.add('Le titre doit contenir au moins 10 caractères');
|
||||
} else if (demande.titre.length > 100) {
|
||||
erreurs.add('Le titre ne peut pas dépasser 100 caractères');
|
||||
}
|
||||
|
||||
// Validation de la description
|
||||
if (demande.description.trim().isEmpty) {
|
||||
erreurs.add('La description est obligatoire');
|
||||
} else if (demande.description.length < 50) {
|
||||
erreurs.add('La description doit contenir au moins 50 caractères');
|
||||
} else if (demande.description.length > 1000) {
|
||||
erreurs.add('La description ne peut pas dépasser 1000 caractères');
|
||||
}
|
||||
|
||||
// Validation du montant pour les aides financières
|
||||
if (_necessiteMontant(demande.typeAide)) {
|
||||
if (demande.montantDemande == null) {
|
||||
erreurs.add('Le montant est obligatoire pour ce type d\'aide');
|
||||
} else if (demande.montantDemande! <= 0) {
|
||||
erreurs.add('Le montant doit être supérieur à zéro');
|
||||
} else if (!_isMontantValide(demande.typeAide, demande.montantDemande!)) {
|
||||
erreurs.add('Le montant demandé n\'est pas dans la fourchette autorisée');
|
||||
}
|
||||
}
|
||||
|
||||
// Validation des bénéficiaires
|
||||
if (demande.beneficiaires.isEmpty) {
|
||||
erreurs.add('Au moins un bénéficiaire doit être spécifié');
|
||||
} else {
|
||||
for (int i = 0; i < demande.beneficiaires.length; i++) {
|
||||
final beneficiaire = demande.beneficiaires[i];
|
||||
if (beneficiaire.nom.trim().isEmpty) {
|
||||
erreurs.add('Le nom du bénéficiaire ${i + 1} est obligatoire');
|
||||
}
|
||||
if (beneficiaire.prenom.trim().isEmpty) {
|
||||
erreurs.add('Le prénom du bénéficiaire ${i + 1} est obligatoire');
|
||||
}
|
||||
if (beneficiaire.age < 0 || beneficiaire.age > 120) {
|
||||
erreurs.add('L\'âge du bénéficiaire ${i + 1} n\'est pas valide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validation de la justification d'urgence si priorité critique ou urgente
|
||||
if (demande.priorite == PrioriteAide.critique || demande.priorite == PrioriteAide.urgente) {
|
||||
if (demande.justificationUrgence == null || demande.justificationUrgence!.trim().isEmpty) {
|
||||
erreurs.add('Une justification d\'urgence est requise pour cette priorité');
|
||||
} else if (demande.justificationUrgence!.length < 20) {
|
||||
erreurs.add('La justification d\'urgence doit contenir au moins 20 caractères');
|
||||
}
|
||||
}
|
||||
|
||||
// Validation du contact d'urgence si priorité critique
|
||||
if (demande.priorite == PrioriteAide.critique) {
|
||||
if (demande.contactUrgence == null) {
|
||||
erreurs.add('Un contact d\'urgence est obligatoire pour les demandes critiques');
|
||||
} else {
|
||||
final contact = demande.contactUrgence!;
|
||||
if (contact.nom.trim().isEmpty) {
|
||||
erreurs.add('Le nom du contact d\'urgence est obligatoire');
|
||||
}
|
||||
if (contact.telephone.trim().isEmpty) {
|
||||
erreurs.add('Le téléphone du contact d\'urgence est obligatoire');
|
||||
} else if (!_isValidPhoneNumber(contact.telephone)) {
|
||||
erreurs.add('Le numéro de téléphone du contact d\'urgence n\'est pas valide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (erreurs.isNotEmpty) {
|
||||
return Left(ValidationFailure(erreurs.join(', ')));
|
||||
}
|
||||
|
||||
return const Right(true);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors de la validation: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
bool _necessiteMontant(TypeAide typeAide) {
|
||||
return [
|
||||
TypeAide.aideFinanciereUrgente,
|
||||
TypeAide.aideFinanciereMedicale,
|
||||
TypeAide.aideFinanciereEducation,
|
||||
].contains(typeAide);
|
||||
}
|
||||
|
||||
bool _isMontantValide(TypeAide typeAide, double montant) {
|
||||
switch (typeAide) {
|
||||
case TypeAide.aideFinanciereUrgente:
|
||||
return montant >= 5000 && montant <= 50000;
|
||||
case TypeAide.aideFinanciereMedicale:
|
||||
return montant >= 10000 && montant <= 100000;
|
||||
case TypeAide.aideFinanciereEducation:
|
||||
return montant >= 5000 && montant <= 200000;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isValidPhoneNumber(String phone) {
|
||||
// Validation simple pour les numéros de téléphone ivoiriens
|
||||
final phoneRegex = RegExp(r'^(\+225)?[0-9]{8,10}$');
|
||||
return phoneRegex.hasMatch(phone.replaceAll(RegExp(r'[\s\-\(\)]'), ''));
|
||||
}
|
||||
}
|
||||
|
||||
class ValiderDemandeAideParams {
|
||||
final DemandeAide demande;
|
||||
|
||||
ValiderDemandeAideParams({required this.demande});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer la priorité automatique d'une demande
|
||||
class CalculerPrioriteDemandeUseCase implements UseCase<PrioriteAide, CalculerPrioriteDemandeParams> {
|
||||
CalculerPrioriteDemandeUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PrioriteAide>> call(CalculerPrioriteDemandeParams params) async {
|
||||
try {
|
||||
final demande = params.demande;
|
||||
|
||||
// Priorité critique si justification d'urgence et contact d'urgence
|
||||
if (demande.justificationUrgence != null &&
|
||||
demande.justificationUrgence!.isNotEmpty &&
|
||||
demande.contactUrgence != null) {
|
||||
return const Right(PrioriteAide.critique);
|
||||
}
|
||||
|
||||
// Priorité urgente pour certains types d'aide
|
||||
if ([TypeAide.aideFinanciereUrgente, TypeAide.aideFinanciereMedicale].contains(demande.typeAide)) {
|
||||
return const Right(PrioriteAide.urgente);
|
||||
}
|
||||
|
||||
// Priorité élevée pour les montants importants
|
||||
if (demande.montantDemande != null && demande.montantDemande! > 50000) {
|
||||
return const Right(PrioriteAide.elevee);
|
||||
}
|
||||
|
||||
// Priorité normale par défaut
|
||||
return const Right(PrioriteAide.normale);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du calcul de priorité: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerPrioriteDemandeParams {
|
||||
final DemandeAide demande;
|
||||
|
||||
CalculerPrioriteDemandeParams({required this.demande});
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/usecases/usecase.dart';
|
||||
import '../entities/evaluation_aide.dart';
|
||||
import '../repositories/solidarite_repository.dart';
|
||||
|
||||
/// Cas d'usage pour créer une nouvelle évaluation
|
||||
class CreerEvaluationUseCase implements UseCase<EvaluationAide, CreerEvaluationParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
CreerEvaluationUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EvaluationAide>> call(CreerEvaluationParams params) async {
|
||||
return await repository.creerEvaluation(params.evaluation);
|
||||
}
|
||||
}
|
||||
|
||||
class CreerEvaluationParams {
|
||||
final EvaluationAide evaluation;
|
||||
|
||||
CreerEvaluationParams({required this.evaluation});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour mettre à jour une évaluation
|
||||
class MettreAJourEvaluationUseCase implements UseCase<EvaluationAide, MettreAJourEvaluationParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
MettreAJourEvaluationUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EvaluationAide>> call(MettreAJourEvaluationParams params) async {
|
||||
return await repository.mettreAJourEvaluation(params.evaluation);
|
||||
}
|
||||
}
|
||||
|
||||
class MettreAJourEvaluationParams {
|
||||
final EvaluationAide evaluation;
|
||||
|
||||
MettreAJourEvaluationParams({required this.evaluation});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir une évaluation par ID
|
||||
class ObtenirEvaluationUseCase implements UseCase<EvaluationAide, ObtenirEvaluationParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirEvaluationUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EvaluationAide>> call(ObtenirEvaluationParams params) async {
|
||||
return await repository.obtenirEvaluation(params.id);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirEvaluationParams {
|
||||
final String id;
|
||||
|
||||
ObtenirEvaluationParams({required this.id});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les évaluations d'une demande
|
||||
class ObtenirEvaluationsDemandeUseCase implements UseCase<List<EvaluationAide>, ObtenirEvaluationsDemandeParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirEvaluationsDemandeUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<EvaluationAide>>> call(ObtenirEvaluationsDemandeParams params) async {
|
||||
return await repository.obtenirEvaluationsDemande(params.demandeId);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirEvaluationsDemandeParams {
|
||||
final String demandeId;
|
||||
|
||||
ObtenirEvaluationsDemandeParams({required this.demandeId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les évaluations d'une proposition
|
||||
class ObtenirEvaluationsPropositionUseCase implements UseCase<List<EvaluationAide>, ObtenirEvaluationsPropositionParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirEvaluationsPropositionUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<EvaluationAide>>> call(ObtenirEvaluationsPropositionParams params) async {
|
||||
return await repository.obtenirEvaluationsProposition(params.propositionId);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirEvaluationsPropositionParams {
|
||||
final String propositionId;
|
||||
|
||||
ObtenirEvaluationsPropositionParams({required this.propositionId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour signaler une évaluation
|
||||
class SignalerEvaluationUseCase implements UseCase<EvaluationAide, SignalerEvaluationParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
SignalerEvaluationUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EvaluationAide>> call(SignalerEvaluationParams params) async {
|
||||
return await repository.signalerEvaluation(
|
||||
evaluationId: params.evaluationId,
|
||||
motif: params.motif,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignalerEvaluationParams {
|
||||
final String evaluationId;
|
||||
final String motif;
|
||||
|
||||
SignalerEvaluationParams({
|
||||
required this.evaluationId,
|
||||
required this.motif,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer la note moyenne d'une demande
|
||||
class CalculerMoyenneDemandeUseCase implements UseCase<StatistiquesEvaluation, CalculerMoyenneDemandeParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
CalculerMoyenneDemandeUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, StatistiquesEvaluation>> call(CalculerMoyenneDemandeParams params) async {
|
||||
return await repository.calculerMoyenneDemande(params.demandeId);
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerMoyenneDemandeParams {
|
||||
final String demandeId;
|
||||
|
||||
CalculerMoyenneDemandeParams({required this.demandeId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer la note moyenne d'une proposition
|
||||
class CalculerMoyennePropositionUseCase implements UseCase<StatistiquesEvaluation, CalculerMoyennePropositionParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
CalculerMoyennePropositionUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, StatistiquesEvaluation>> call(CalculerMoyennePropositionParams params) async {
|
||||
return await repository.calculerMoyenneProposition(params.propositionId);
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerMoyennePropositionParams {
|
||||
final String propositionId;
|
||||
|
||||
CalculerMoyennePropositionParams({required this.propositionId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour valider une évaluation avant création
|
||||
class ValiderEvaluationUseCase implements UseCase<bool, ValiderEvaluationParams> {
|
||||
ValiderEvaluationUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> call(ValiderEvaluationParams params) async {
|
||||
try {
|
||||
final evaluation = params.evaluation;
|
||||
final erreurs = <String>[];
|
||||
|
||||
// Validation de la note globale
|
||||
if (evaluation.noteGlobale < 1.0 || evaluation.noteGlobale > 5.0) {
|
||||
erreurs.add('La note globale doit être comprise entre 1 et 5');
|
||||
}
|
||||
|
||||
// Validation des notes détaillées
|
||||
final notesDetaillees = [
|
||||
evaluation.noteDelaiReponse,
|
||||
evaluation.noteCommunication,
|
||||
evaluation.noteProfessionnalisme,
|
||||
evaluation.noteRespectEngagements,
|
||||
];
|
||||
|
||||
for (final note in notesDetaillees) {
|
||||
if (note != null && (note < 1.0 || note > 5.0)) {
|
||||
erreurs.add('Toutes les notes détaillées doivent être comprises entre 1 et 5');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation du commentaire principal
|
||||
if (evaluation.commentairePrincipal.trim().isEmpty) {
|
||||
erreurs.add('Le commentaire principal est obligatoire');
|
||||
} else if (evaluation.commentairePrincipal.length < 20) {
|
||||
erreurs.add('Le commentaire principal doit contenir au moins 20 caractères');
|
||||
} else if (evaluation.commentairePrincipal.length > 1000) {
|
||||
erreurs.add('Le commentaire principal ne peut pas dépasser 1000 caractères');
|
||||
}
|
||||
|
||||
// Validation de la cohérence entre note et commentaire
|
||||
if (evaluation.noteGlobale <= 2.0 && evaluation.commentairePrincipal.length < 50) {
|
||||
erreurs.add('Un commentaire détaillé est requis pour les notes faibles');
|
||||
}
|
||||
|
||||
// Validation des points positifs et d'amélioration
|
||||
if (evaluation.pointsPositifs != null && evaluation.pointsPositifs!.length > 500) {
|
||||
erreurs.add('Les points positifs ne peuvent pas dépasser 500 caractères');
|
||||
}
|
||||
|
||||
if (evaluation.pointsAmelioration != null && evaluation.pointsAmelioration!.length > 500) {
|
||||
erreurs.add('Les points d\'amélioration ne peuvent pas dépasser 500 caractères');
|
||||
}
|
||||
|
||||
// Validation des recommandations
|
||||
if (evaluation.recommandations != null && evaluation.recommandations!.length > 500) {
|
||||
erreurs.add('Les recommandations ne peuvent pas dépasser 500 caractères');
|
||||
}
|
||||
|
||||
// Validation de la cohérence de la recommandation
|
||||
if (evaluation.recommande == true && evaluation.noteGlobale < 3.0) {
|
||||
erreurs.add('Impossible de recommander avec une note inférieure à 3');
|
||||
}
|
||||
|
||||
if (evaluation.recommande == false && evaluation.noteGlobale >= 4.0) {
|
||||
erreurs.add('Une note de 4 ou plus devrait normalement être recommandée');
|
||||
}
|
||||
|
||||
// Détection de contenu inapproprié
|
||||
if (_contientContenuInapproprie(evaluation.commentairePrincipal)) {
|
||||
erreurs.add('Le commentaire contient du contenu inapproprié');
|
||||
}
|
||||
|
||||
if (erreurs.isNotEmpty) {
|
||||
return Left(ValidationFailure(erreurs.join(', ')));
|
||||
}
|
||||
|
||||
return const Right(true);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors de la validation: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
bool _contientContenuInapproprie(String texte) {
|
||||
// Liste simple de mots inappropriés (à étendre selon les besoins)
|
||||
final motsInappropries = [
|
||||
'spam', 'arnaque', 'escroquerie', 'fraude',
|
||||
// Ajouter d'autres mots selon le contexte
|
||||
];
|
||||
|
||||
final texteMinuscule = texte.toLowerCase();
|
||||
return motsInappropries.any((mot) => texteMinuscule.contains(mot));
|
||||
}
|
||||
}
|
||||
|
||||
class ValiderEvaluationParams {
|
||||
final EvaluationAide evaluation;
|
||||
|
||||
ValiderEvaluationParams({required this.evaluation});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer le score de qualité d'une évaluation
|
||||
class CalculerScoreQualiteEvaluationUseCase implements UseCase<double, CalculerScoreQualiteEvaluationParams> {
|
||||
CalculerScoreQualiteEvaluationUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, double>> call(CalculerScoreQualiteEvaluationParams params) async {
|
||||
try {
|
||||
final evaluation = params.evaluation;
|
||||
double score = 50.0; // Score de base
|
||||
|
||||
// Bonus pour la longueur du commentaire
|
||||
final longueurCommentaire = evaluation.commentairePrincipal.length;
|
||||
if (longueurCommentaire >= 100) {
|
||||
score += 15.0;
|
||||
} else if (longueurCommentaire >= 50) {
|
||||
score += 10.0;
|
||||
} else if (longueurCommentaire >= 20) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
// Bonus pour les notes détaillées
|
||||
final notesDetaillees = [
|
||||
evaluation.noteDelaiReponse,
|
||||
evaluation.noteCommunication,
|
||||
evaluation.noteProfessionnalisme,
|
||||
evaluation.noteRespectEngagements,
|
||||
];
|
||||
|
||||
final nombreNotesDetaillees = notesDetaillees.where((note) => note != null).length;
|
||||
score += nombreNotesDetaillees * 5.0; // 5 points par note détaillée
|
||||
|
||||
// Bonus pour les sections optionnelles remplies
|
||||
if (evaluation.pointsPositifs != null && evaluation.pointsPositifs!.isNotEmpty) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
if (evaluation.pointsAmelioration != null && evaluation.pointsAmelioration!.isNotEmpty) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
if (evaluation.recommandations != null && evaluation.recommandations!.isNotEmpty) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
// Bonus pour la cohérence
|
||||
if (_estCoherente(evaluation)) {
|
||||
score += 10.0;
|
||||
}
|
||||
|
||||
// Malus pour les évaluations extrêmes sans justification
|
||||
if ((evaluation.noteGlobale <= 1.5 || evaluation.noteGlobale >= 4.5) &&
|
||||
longueurCommentaire < 50) {
|
||||
score -= 15.0;
|
||||
}
|
||||
|
||||
// Malus pour les signalements
|
||||
score -= evaluation.nombreSignalements * 10.0;
|
||||
|
||||
return Right(score.clamp(0.0, 100.0));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du calcul du score de qualité: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
bool _estCoherente(EvaluationAide evaluation) {
|
||||
// Vérifier la cohérence entre la note globale et les notes détaillées
|
||||
final notesDetaillees = [
|
||||
evaluation.noteDelaiReponse,
|
||||
evaluation.noteCommunication,
|
||||
evaluation.noteProfessionnalisme,
|
||||
evaluation.noteRespectEngagements,
|
||||
].where((note) => note != null).cast<double>().toList();
|
||||
|
||||
if (notesDetaillees.isEmpty) return true;
|
||||
|
||||
final moyenneDetaillees = notesDetaillees.reduce((a, b) => a + b) / notesDetaillees.length;
|
||||
final ecart = (evaluation.noteGlobale - moyenneDetaillees).abs();
|
||||
|
||||
// Cohérent si l'écart est inférieur à 1 point
|
||||
return ecart < 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerScoreQualiteEvaluationParams {
|
||||
final EvaluationAide evaluation;
|
||||
|
||||
CalculerScoreQualiteEvaluationParams({required this.evaluation});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour analyser les tendances d'évaluation
|
||||
class AnalyserTendancesEvaluationUseCase implements UseCase<AnalyseTendancesEvaluation, AnalyserTendancesEvaluationParams> {
|
||||
AnalyserTendancesEvaluationUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, AnalyseTendancesEvaluation>> call(AnalyserTendancesEvaluationParams params) async {
|
||||
try {
|
||||
// Simulation d'analyse des tendances d'évaluation
|
||||
// Dans une vraie implémentation, on analyserait les données historiques
|
||||
|
||||
final analyse = AnalyseTendancesEvaluation(
|
||||
noteMoyenneGlobale: 4.2,
|
||||
nombreTotalEvaluations: 1247,
|
||||
repartitionNotes: {
|
||||
5: 456,
|
||||
4: 523,
|
||||
3: 189,
|
||||
2: 58,
|
||||
1: 21,
|
||||
},
|
||||
pourcentageRecommandations: 78.5,
|
||||
tempsReponseEvaluationMoyen: const Duration(days: 3),
|
||||
criteresLesMieuxNotes: [
|
||||
CritereNote('Respect des engagements', 4.6),
|
||||
CritereNote('Communication', 4.3),
|
||||
CritereNote('Professionnalisme', 4.1),
|
||||
CritereNote('Délai de réponse', 3.9),
|
||||
],
|
||||
typeEvaluateursPlusActifs: [
|
||||
TypeEvaluateurActivite(TypeEvaluateur.beneficiaire, 67.2),
|
||||
TypeEvaluateurActivite(TypeEvaluateur.proposant, 23.8),
|
||||
TypeEvaluateurActivite(TypeEvaluateur.evaluateurOfficial, 6.5),
|
||||
TypeEvaluateurActivite(TypeEvaluateur.administrateur, 2.5),
|
||||
],
|
||||
evolutionSatisfaction: EvolutionSatisfaction(
|
||||
dernierMois: 4.2,
|
||||
moisPrecedent: 4.0,
|
||||
tendance: TendanceSatisfaction.hausse,
|
||||
),
|
||||
recommandationsAmelioration: [
|
||||
'Améliorer les délais de réponse des proposants',
|
||||
'Encourager plus d\'évaluations détaillées',
|
||||
'Former les proposants à la communication',
|
||||
],
|
||||
);
|
||||
|
||||
return Right(analyse);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors de l\'analyse des tendances: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnalyserTendancesEvaluationParams {
|
||||
final String organisationId;
|
||||
final DateTime? dateDebut;
|
||||
final DateTime? dateFin;
|
||||
|
||||
AnalyserTendancesEvaluationParams({
|
||||
required this.organisationId,
|
||||
this.dateDebut,
|
||||
this.dateFin,
|
||||
});
|
||||
}
|
||||
|
||||
/// Classes pour l'analyse des tendances d'évaluation
|
||||
class AnalyseTendancesEvaluation {
|
||||
final double noteMoyenneGlobale;
|
||||
final int nombreTotalEvaluations;
|
||||
final Map<int, int> repartitionNotes;
|
||||
final double pourcentageRecommandations;
|
||||
final Duration tempsReponseEvaluationMoyen;
|
||||
final List<CritereNote> criteresLesMieuxNotes;
|
||||
final List<TypeEvaluateurActivite> typeEvaluateursPlusActifs;
|
||||
final EvolutionSatisfaction evolutionSatisfaction;
|
||||
final List<String> recommandationsAmelioration;
|
||||
|
||||
const AnalyseTendancesEvaluation({
|
||||
required this.noteMoyenneGlobale,
|
||||
required this.nombreTotalEvaluations,
|
||||
required this.repartitionNotes,
|
||||
required this.pourcentageRecommandations,
|
||||
required this.tempsReponseEvaluationMoyen,
|
||||
required this.criteresLesMieuxNotes,
|
||||
required this.typeEvaluateursPlusActifs,
|
||||
required this.evolutionSatisfaction,
|
||||
required this.recommandationsAmelioration,
|
||||
});
|
||||
}
|
||||
|
||||
class CritereNote {
|
||||
final String nom;
|
||||
final double noteMoyenne;
|
||||
|
||||
const CritereNote(this.nom, this.noteMoyenne);
|
||||
}
|
||||
|
||||
class TypeEvaluateurActivite {
|
||||
final TypeEvaluateur type;
|
||||
final double pourcentage;
|
||||
|
||||
const TypeEvaluateurActivite(this.type, this.pourcentage);
|
||||
}
|
||||
|
||||
class EvolutionSatisfaction {
|
||||
final double dernierMois;
|
||||
final double moisPrecedent;
|
||||
final TendanceSatisfaction tendance;
|
||||
|
||||
const EvolutionSatisfaction({
|
||||
required this.dernierMois,
|
||||
required this.moisPrecedent,
|
||||
required this.tendance,
|
||||
});
|
||||
}
|
||||
|
||||
enum TendanceSatisfaction { hausse, baisse, stable }
|
||||
@@ -0,0 +1,391 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/usecases/usecase.dart';
|
||||
import '../entities/demande_aide.dart';
|
||||
import '../entities/proposition_aide.dart';
|
||||
import '../repositories/solidarite_repository.dart';
|
||||
|
||||
/// Cas d'usage pour trouver les propositions compatibles avec une demande
|
||||
class TrouverPropositionsCompatiblesUseCase implements UseCase<List<PropositionAide>, TrouverPropositionsCompatiblesParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
TrouverPropositionsCompatiblesUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<PropositionAide>>> call(TrouverPropositionsCompatiblesParams params) async {
|
||||
return await repository.trouverPropositionsCompatibles(params.demandeId);
|
||||
}
|
||||
}
|
||||
|
||||
class TrouverPropositionsCompatiblesParams {
|
||||
final String demandeId;
|
||||
|
||||
TrouverPropositionsCompatiblesParams({required this.demandeId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour trouver les demandes compatibles avec une proposition
|
||||
class TrouverDemandesCompatiblesUseCase implements UseCase<List<DemandeAide>, TrouverDemandesCompatiblesParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
TrouverDemandesCompatiblesUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<DemandeAide>>> call(TrouverDemandesCompatiblesParams params) async {
|
||||
return await repository.trouverDemandesCompatibles(params.propositionId);
|
||||
}
|
||||
}
|
||||
|
||||
class TrouverDemandesCompatiblesParams {
|
||||
final String propositionId;
|
||||
|
||||
TrouverDemandesCompatiblesParams({required this.propositionId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour rechercher des proposants financiers
|
||||
class RechercherProposantsFinanciersUseCase implements UseCase<List<PropositionAide>, RechercherProposantsFinanciersParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
RechercherProposantsFinanciersUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<PropositionAide>>> call(RechercherProposantsFinanciersParams params) async {
|
||||
return await repository.rechercherProposantsFinanciers(params.demandeId);
|
||||
}
|
||||
}
|
||||
|
||||
class RechercherProposantsFinanciersParams {
|
||||
final String demandeId;
|
||||
|
||||
RechercherProposantsFinanciersParams({required this.demandeId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer le score de compatibilité entre une demande et une proposition
|
||||
class CalculerScoreCompatibiliteUseCase implements UseCase<double, CalculerScoreCompatibiliteParams> {
|
||||
CalculerScoreCompatibiliteUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, double>> call(CalculerScoreCompatibiliteParams params) async {
|
||||
try {
|
||||
final demande = params.demande;
|
||||
final proposition = params.proposition;
|
||||
|
||||
double score = 0.0;
|
||||
|
||||
// 1. Correspondance du type d'aide (40 points max)
|
||||
if (demande.typeAide == proposition.typeAide) {
|
||||
score += 40.0;
|
||||
} else if (_sontTypesCompatibles(demande.typeAide, proposition.typeAide)) {
|
||||
score += 25.0;
|
||||
} else if (proposition.typeAide == TypeAide.autre) {
|
||||
score += 15.0;
|
||||
}
|
||||
|
||||
// 2. Compatibilité financière (25 points max)
|
||||
if (_necessiteMontant(demande.typeAide) && proposition.montantMaximum != null) {
|
||||
final montantDemande = demande.montantDemande;
|
||||
if (montantDemande != null) {
|
||||
if (montantDemande <= proposition.montantMaximum!) {
|
||||
score += 25.0;
|
||||
} else {
|
||||
// Pénalité proportionnelle au dépassement
|
||||
double ratio = proposition.montantMaximum! / montantDemande;
|
||||
score += 25.0 * ratio;
|
||||
}
|
||||
}
|
||||
} else if (!_necessiteMontant(demande.typeAide)) {
|
||||
score += 25.0; // Pas de contrainte financière
|
||||
}
|
||||
|
||||
// 3. Expérience du proposant (15 points max)
|
||||
if (proposition.nombreBeneficiairesAides > 0) {
|
||||
score += (proposition.nombreBeneficiairesAides * 2.0).clamp(0.0, 15.0);
|
||||
}
|
||||
|
||||
// 4. Réputation (10 points max)
|
||||
if (proposition.noteMoyenne != null && proposition.nombreEvaluations >= 3) {
|
||||
score += (proposition.noteMoyenne! - 3.0) * 3.33;
|
||||
}
|
||||
|
||||
// 5. Disponibilité et capacité (10 points max)
|
||||
if (proposition.peutAccepterBeneficiaires) {
|
||||
double ratioCapacite = proposition.placesRestantes / proposition.nombreMaxBeneficiaires;
|
||||
score += 10.0 * ratioCapacite;
|
||||
}
|
||||
|
||||
// Bonus et malus additionnels
|
||||
score += _calculerBonusGeographique(demande, proposition);
|
||||
score += _calculerBonusTemporel(demande, proposition);
|
||||
score -= _calculerMalusDelai(demande, proposition);
|
||||
|
||||
return Right(score.clamp(0.0, 100.0));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du calcul de compatibilité: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
bool _sontTypesCompatibles(TypeAide typeAide1, TypeAide typeAide2) {
|
||||
// Définir les groupes de types compatibles
|
||||
final groupesCompatibles = [
|
||||
[TypeAide.aideFinanciereUrgente, TypeAide.aideFinanciereMedicale, TypeAide.aideFinanciereEducation],
|
||||
[TypeAide.aideMaterielleVetements, TypeAide.aideMaterielleNourriture],
|
||||
[TypeAide.aideProfessionnelleFormation, TypeAide.aideSocialeAccompagnement],
|
||||
];
|
||||
|
||||
for (final groupe in groupesCompatibles) {
|
||||
if (groupe.contains(typeAide1) && groupe.contains(typeAide2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _necessiteMontant(TypeAide typeAide) {
|
||||
return [
|
||||
TypeAide.aideFinanciereUrgente,
|
||||
TypeAide.aideFinanciereMedicale,
|
||||
TypeAide.aideFinanciereEducation,
|
||||
].contains(typeAide);
|
||||
}
|
||||
|
||||
double _calculerBonusGeographique(DemandeAide demande, PropositionAide proposition) {
|
||||
// Simulation - dans une vraie implémentation, on utiliserait les données de localisation
|
||||
if (demande.localisation != null && proposition.zonesGeographiques.isNotEmpty) {
|
||||
// Logique de proximité géographique
|
||||
return 5.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double _calculerBonusTemporel(DemandeAide demande, PropositionAide proposition) {
|
||||
double bonus = 0.0;
|
||||
|
||||
// Bonus pour demande urgente
|
||||
if (demande.estUrgente) {
|
||||
bonus += 5.0;
|
||||
}
|
||||
|
||||
// Bonus pour proposition récente
|
||||
final joursDepuisCreation = DateTime.now().difference(proposition.dateCreation).inDays;
|
||||
if (joursDepuisCreation <= 30) {
|
||||
bonus += 3.0;
|
||||
}
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
double _calculerMalusDelai(DemandeAide demande, PropositionAide proposition) {
|
||||
double malus = 0.0;
|
||||
|
||||
// Malus si la demande est en retard
|
||||
if (demande.delaiDepasse) {
|
||||
malus += 5.0;
|
||||
}
|
||||
|
||||
// Malus si la proposition a un délai de réponse long
|
||||
if (proposition.delaiReponseHeures > 168) { // Plus d'une semaine
|
||||
malus += 3.0;
|
||||
}
|
||||
|
||||
return malus;
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerScoreCompatibiliteParams {
|
||||
final DemandeAide demande;
|
||||
final PropositionAide proposition;
|
||||
|
||||
CalculerScoreCompatibiliteParams({
|
||||
required this.demande,
|
||||
required this.proposition,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour effectuer un matching intelligent
|
||||
class EffectuerMatchingIntelligentUseCase implements UseCase<List<ResultatMatching>, EffectuerMatchingIntelligentParams> {
|
||||
final TrouverPropositionsCompatiblesUseCase trouverPropositionsCompatibles;
|
||||
final CalculerScoreCompatibiliteUseCase calculerScoreCompatibilite;
|
||||
|
||||
EffectuerMatchingIntelligentUseCase({
|
||||
required this.trouverPropositionsCompatibles,
|
||||
required this.calculerScoreCompatibilite,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<ResultatMatching>>> call(EffectuerMatchingIntelligentParams params) async {
|
||||
try {
|
||||
// 1. Trouver les propositions compatibles
|
||||
final propositionsResult = await trouverPropositionsCompatibles(
|
||||
TrouverPropositionsCompatiblesParams(demandeId: params.demande.id)
|
||||
);
|
||||
|
||||
return propositionsResult.fold(
|
||||
(failure) => Left(failure),
|
||||
(propositions) async {
|
||||
// 2. Calculer les scores de compatibilité
|
||||
final resultats = <ResultatMatching>[];
|
||||
|
||||
for (final proposition in propositions) {
|
||||
final scoreResult = await calculerScoreCompatibilite(
|
||||
CalculerScoreCompatibiliteParams(
|
||||
demande: params.demande,
|
||||
proposition: proposition,
|
||||
)
|
||||
);
|
||||
|
||||
scoreResult.fold(
|
||||
(failure) {
|
||||
// Ignorer les erreurs de calcul de score individuel
|
||||
},
|
||||
(score) {
|
||||
if (score >= params.scoreMinimum) {
|
||||
resultats.add(ResultatMatching(
|
||||
proposition: proposition,
|
||||
score: score,
|
||||
raisonCompatibilite: _genererRaisonCompatibilite(params.demande, proposition, score),
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Trier par score décroissant
|
||||
resultats.sort((a, b) => b.score.compareTo(a.score));
|
||||
|
||||
// 4. Limiter le nombre de résultats
|
||||
final resultatsLimites = resultats.take(params.limiteResultats).toList();
|
||||
|
||||
return Right(resultatsLimites);
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du matching intelligent: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
String _genererRaisonCompatibilite(DemandeAide demande, PropositionAide proposition, double score) {
|
||||
final raisons = <String>[];
|
||||
|
||||
// Type d'aide
|
||||
if (demande.typeAide == proposition.typeAide) {
|
||||
raisons.add('Type d\'aide identique');
|
||||
}
|
||||
|
||||
// Compatibilité financière
|
||||
if (demande.montantDemande != null && proposition.montantMaximum != null) {
|
||||
if (demande.montantDemande! <= proposition.montantMaximum!) {
|
||||
raisons.add('Montant compatible');
|
||||
}
|
||||
}
|
||||
|
||||
// Expérience
|
||||
if (proposition.nombreBeneficiairesAides > 5) {
|
||||
raisons.add('Proposant expérimenté');
|
||||
}
|
||||
|
||||
// Réputation
|
||||
if (proposition.noteMoyenne != null && proposition.noteMoyenne! >= 4.0) {
|
||||
raisons.add('Excellente réputation');
|
||||
}
|
||||
|
||||
// Disponibilité
|
||||
if (proposition.peutAccepterBeneficiaires) {
|
||||
raisons.add('Places disponibles');
|
||||
}
|
||||
|
||||
return raisons.isEmpty ? 'Compatible' : raisons.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
class EffectuerMatchingIntelligentParams {
|
||||
final DemandeAide demande;
|
||||
final double scoreMinimum;
|
||||
final int limiteResultats;
|
||||
|
||||
EffectuerMatchingIntelligentParams({
|
||||
required this.demande,
|
||||
this.scoreMinimum = 30.0,
|
||||
this.limiteResultats = 10,
|
||||
});
|
||||
}
|
||||
|
||||
/// Classe représentant un résultat de matching
|
||||
class ResultatMatching {
|
||||
final PropositionAide proposition;
|
||||
final double score;
|
||||
final String raisonCompatibilite;
|
||||
|
||||
const ResultatMatching({
|
||||
required this.proposition,
|
||||
required this.score,
|
||||
required this.raisonCompatibilite,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour analyser les tendances de matching
|
||||
class AnalyserTendancesMatchingUseCase implements UseCase<AnalyseTendances, AnalyserTendancesMatchingParams> {
|
||||
AnalyserTendancesMatchingUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, AnalyseTendances>> call(AnalyserTendancesMatchingParams params) async {
|
||||
try {
|
||||
// Simulation d'analyse des tendances
|
||||
// Dans une vraie implémentation, on analyserait les données historiques
|
||||
|
||||
final analyse = AnalyseTendances(
|
||||
tauxMatchingMoyen: 78.5,
|
||||
tempsMatchingMoyen: const Duration(hours: 6),
|
||||
typesAidePlusDemandesMap: {
|
||||
TypeAide.aideFinanciereUrgente: 45,
|
||||
TypeAide.aideFinanciereMedicale: 32,
|
||||
TypeAide.aideMaterielleNourriture: 28,
|
||||
},
|
||||
typesAidePlusProposesMap: {
|
||||
TypeAide.aideFinanciereEducation: 38,
|
||||
TypeAide.aideProfessionnelleFormation: 25,
|
||||
TypeAide.aideSocialeAccompagnement: 22,
|
||||
},
|
||||
heuresOptimalesMatching: ['09:00', '14:00', '18:00'],
|
||||
recommandations: [
|
||||
'Augmenter les propositions d\'aide financière urgente',
|
||||
'Promouvoir les aides matérielles auprès des proposants',
|
||||
'Optimiser les notifications entre 9h et 18h',
|
||||
],
|
||||
);
|
||||
|
||||
return Right(analyse);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors de l\'analyse des tendances: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnalyserTendancesMatchingParams {
|
||||
final String organisationId;
|
||||
final DateTime? dateDebut;
|
||||
final DateTime? dateFin;
|
||||
|
||||
AnalyserTendancesMatchingParams({
|
||||
required this.organisationId,
|
||||
this.dateDebut,
|
||||
this.dateFin,
|
||||
});
|
||||
}
|
||||
|
||||
/// Classe représentant une analyse des tendances de matching
|
||||
class AnalyseTendances {
|
||||
final double tauxMatchingMoyen;
|
||||
final Duration tempsMatchingMoyen;
|
||||
final Map<TypeAide, int> typesAidePlusDemandesMap;
|
||||
final Map<TypeAide, int> typesAidePlusProposesMap;
|
||||
final List<String> heuresOptimalesMatching;
|
||||
final List<String> recommandations;
|
||||
|
||||
const AnalyseTendances({
|
||||
required this.tauxMatchingMoyen,
|
||||
required this.tempsMatchingMoyen,
|
||||
required this.typesAidePlusDemandesMap,
|
||||
required this.typesAidePlusProposesMap,
|
||||
required this.heuresOptimalesMatching,
|
||||
required this.recommandations,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/usecases/usecase.dart';
|
||||
import '../entities/proposition_aide.dart';
|
||||
import '../entities/demande_aide.dart';
|
||||
import '../repositories/solidarite_repository.dart';
|
||||
|
||||
/// Cas d'usage pour créer une nouvelle proposition d'aide
|
||||
class CreerPropositionAideUseCase implements UseCase<PropositionAide, CreerPropositionAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
CreerPropositionAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PropositionAide>> call(CreerPropositionAideParams params) async {
|
||||
return await repository.creerPropositionAide(params.proposition);
|
||||
}
|
||||
}
|
||||
|
||||
class CreerPropositionAideParams {
|
||||
final PropositionAide proposition;
|
||||
|
||||
CreerPropositionAideParams({required this.proposition});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour mettre à jour une proposition d'aide
|
||||
class MettreAJourPropositionAideUseCase implements UseCase<PropositionAide, MettreAJourPropositionAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
MettreAJourPropositionAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PropositionAide>> call(MettreAJourPropositionAideParams params) async {
|
||||
return await repository.mettreAJourPropositionAide(params.proposition);
|
||||
}
|
||||
}
|
||||
|
||||
class MettreAJourPropositionAideParams {
|
||||
final PropositionAide proposition;
|
||||
|
||||
MettreAJourPropositionAideParams({required this.proposition});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir une proposition d'aide par ID
|
||||
class ObtenirPropositionAideUseCase implements UseCase<PropositionAide, ObtenirPropositionAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirPropositionAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PropositionAide>> call(ObtenirPropositionAideParams params) async {
|
||||
return await repository.obtenirPropositionAide(params.id);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirPropositionAideParams {
|
||||
final String id;
|
||||
|
||||
ObtenirPropositionAideParams({required this.id});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour changer le statut d'une proposition d'aide
|
||||
class ChangerStatutPropositionUseCase implements UseCase<PropositionAide, ChangerStatutPropositionParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ChangerStatutPropositionUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PropositionAide>> call(ChangerStatutPropositionParams params) async {
|
||||
return await repository.changerStatutProposition(
|
||||
propositionId: params.propositionId,
|
||||
activer: params.activer,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChangerStatutPropositionParams {
|
||||
final String propositionId;
|
||||
final bool activer;
|
||||
|
||||
ChangerStatutPropositionParams({
|
||||
required this.propositionId,
|
||||
required this.activer,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour rechercher des propositions d'aide
|
||||
class RechercherPropositionsAideUseCase implements UseCase<List<PropositionAide>, RechercherPropositionsAideParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
RechercherPropositionsAideUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<PropositionAide>>> call(RechercherPropositionsAideParams params) async {
|
||||
return await repository.rechercherPropositions(
|
||||
organisationId: params.organisationId,
|
||||
typeAide: params.typeAide,
|
||||
proposantId: params.proposantId,
|
||||
actives: params.actives,
|
||||
page: params.page,
|
||||
taille: params.taille,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RechercherPropositionsAideParams {
|
||||
final String? organisationId;
|
||||
final TypeAide? typeAide;
|
||||
final String? proposantId;
|
||||
final bool? actives;
|
||||
final int page;
|
||||
final int taille;
|
||||
|
||||
RechercherPropositionsAideParams({
|
||||
this.organisationId,
|
||||
this.typeAide,
|
||||
this.proposantId,
|
||||
this.actives,
|
||||
this.page = 0,
|
||||
this.taille = 20,
|
||||
});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les propositions actives pour un type d'aide
|
||||
class ObtenirPropositionsActivesUseCase implements UseCase<List<PropositionAide>, ObtenirPropositionsActivesParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirPropositionsActivesUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<PropositionAide>>> call(ObtenirPropositionsActivesParams params) async {
|
||||
return await repository.obtenirPropositionsActives(params.typeAide);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirPropositionsActivesParams {
|
||||
final TypeAide typeAide;
|
||||
|
||||
ObtenirPropositionsActivesParams({required this.typeAide});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les meilleures propositions
|
||||
class ObtenirMeilleuresPropositionsUseCase implements UseCase<List<PropositionAide>, ObtenirMeilleuresPropositionsParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirMeilleuresPropositionsUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<PropositionAide>>> call(ObtenirMeilleuresPropositionsParams params) async {
|
||||
return await repository.obtenirMeilleuresPropositions(params.limite);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirMeilleuresPropositionsParams {
|
||||
final int limite;
|
||||
|
||||
ObtenirMeilleuresPropositionsParams({this.limite = 10});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour obtenir les propositions de l'utilisateur connecté
|
||||
class ObtenirMesPropositionsUseCase implements UseCase<List<PropositionAide>, ObtenirMesPropositionsParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirMesPropositionsUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<PropositionAide>>> call(ObtenirMesPropositionsParams params) async {
|
||||
return await repository.obtenirMesPropositions(params.utilisateurId);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirMesPropositionsParams {
|
||||
final String utilisateurId;
|
||||
|
||||
ObtenirMesPropositionsParams({required this.utilisateurId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour valider une proposition d'aide avant création
|
||||
class ValiderPropositionAideUseCase implements UseCase<bool, ValiderPropositionAideParams> {
|
||||
ValiderPropositionAideUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> call(ValiderPropositionAideParams params) async {
|
||||
try {
|
||||
final proposition = params.proposition;
|
||||
final erreurs = <String>[];
|
||||
|
||||
// Validation du titre
|
||||
if (proposition.titre.trim().isEmpty) {
|
||||
erreurs.add('Le titre est obligatoire');
|
||||
} else if (proposition.titre.length < 10) {
|
||||
erreurs.add('Le titre doit contenir au moins 10 caractères');
|
||||
} else if (proposition.titre.length > 100) {
|
||||
erreurs.add('Le titre ne peut pas dépasser 100 caractères');
|
||||
}
|
||||
|
||||
// Validation de la description
|
||||
if (proposition.description.trim().isEmpty) {
|
||||
erreurs.add('La description est obligatoire');
|
||||
} else if (proposition.description.length < 50) {
|
||||
erreurs.add('La description doit contenir au moins 50 caractères');
|
||||
} else if (proposition.description.length > 1000) {
|
||||
erreurs.add('La description ne peut pas dépasser 1000 caractères');
|
||||
}
|
||||
|
||||
// Validation du nombre maximum de bénéficiaires
|
||||
if (proposition.nombreMaxBeneficiaires <= 0) {
|
||||
erreurs.add('Le nombre maximum de bénéficiaires doit être supérieur à zéro');
|
||||
} else if (proposition.nombreMaxBeneficiaires > 100) {
|
||||
erreurs.add('Le nombre maximum de bénéficiaires ne peut pas dépasser 100');
|
||||
}
|
||||
|
||||
// Validation des montants pour les aides financières
|
||||
if (_estAideFinanciere(proposition.typeAide)) {
|
||||
if (proposition.montantMaximum == null) {
|
||||
erreurs.add('Le montant maximum est obligatoire pour les aides financières');
|
||||
} else if (proposition.montantMaximum! <= 0) {
|
||||
erreurs.add('Le montant maximum doit être supérieur à zéro');
|
||||
} else if (proposition.montantMaximum! > 1000000) {
|
||||
erreurs.add('Le montant maximum ne peut pas dépasser 1 000 000 FCFA');
|
||||
}
|
||||
|
||||
if (proposition.montantMinimum != null) {
|
||||
if (proposition.montantMinimum! <= 0) {
|
||||
erreurs.add('Le montant minimum doit être supérieur à zéro');
|
||||
} else if (proposition.montantMaximum != null &&
|
||||
proposition.montantMinimum! >= proposition.montantMaximum!) {
|
||||
erreurs.add('Le montant minimum doit être inférieur au montant maximum');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validation du délai de réponse
|
||||
if (proposition.delaiReponseHeures <= 0) {
|
||||
erreurs.add('Le délai de réponse doit être supérieur à zéro');
|
||||
} else if (proposition.delaiReponseHeures > 720) { // 30 jours max
|
||||
erreurs.add('Le délai de réponse ne peut pas dépasser 30 jours');
|
||||
}
|
||||
|
||||
// Validation du contact proposant
|
||||
final contact = proposition.contactProposant;
|
||||
if (contact.nom.trim().isEmpty) {
|
||||
erreurs.add('Le nom du contact est obligatoire');
|
||||
}
|
||||
if (contact.telephone.trim().isEmpty) {
|
||||
erreurs.add('Le téléphone du contact est obligatoire');
|
||||
} else if (!_isValidPhoneNumber(contact.telephone)) {
|
||||
erreurs.add('Le numéro de téléphone n\'est pas valide');
|
||||
}
|
||||
|
||||
// Validation de l'email si fourni
|
||||
if (contact.email != null && contact.email!.isNotEmpty) {
|
||||
if (!_isValidEmail(contact.email!)) {
|
||||
erreurs.add('L\'adresse email n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
// Validation des zones géographiques
|
||||
if (proposition.zonesGeographiques.isEmpty) {
|
||||
erreurs.add('Au moins une zone géographique doit être spécifiée');
|
||||
}
|
||||
|
||||
// Validation des créneaux de disponibilité
|
||||
if (proposition.creneauxDisponibilite.isEmpty) {
|
||||
erreurs.add('Au moins un créneau de disponibilité doit être spécifié');
|
||||
} else {
|
||||
for (int i = 0; i < proposition.creneauxDisponibilite.length; i++) {
|
||||
final creneau = proposition.creneauxDisponibilite[i];
|
||||
if (!_isValidTimeFormat(creneau.heureDebut)) {
|
||||
erreurs.add('L\'heure de début du créneau ${i + 1} n\'est pas valide (format HH:MM)');
|
||||
}
|
||||
if (!_isValidTimeFormat(creneau.heureFin)) {
|
||||
erreurs.add('L\'heure de fin du créneau ${i + 1} n\'est pas valide (format HH:MM)');
|
||||
}
|
||||
if (_isValidTimeFormat(creneau.heureDebut) &&
|
||||
_isValidTimeFormat(creneau.heureFin) &&
|
||||
_compareTime(creneau.heureDebut, creneau.heureFin) >= 0) {
|
||||
erreurs.add('L\'heure de fin du créneau ${i + 1} doit être après l\'heure de début');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validation de la date d'expiration
|
||||
if (proposition.dateExpiration != null) {
|
||||
if (proposition.dateExpiration!.isBefore(DateTime.now())) {
|
||||
erreurs.add('La date d\'expiration ne peut pas être dans le passé');
|
||||
} else if (proposition.dateExpiration!.isAfter(DateTime.now().add(const Duration(days: 365)))) {
|
||||
erreurs.add('La date d\'expiration ne peut pas dépasser un an');
|
||||
}
|
||||
}
|
||||
|
||||
if (erreurs.isNotEmpty) {
|
||||
return Left(ValidationFailure(erreurs.join(', ')));
|
||||
}
|
||||
|
||||
return const Right(true);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors de la validation: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
bool _estAideFinanciere(TypeAide typeAide) {
|
||||
return [
|
||||
TypeAide.aideFinanciereUrgente,
|
||||
TypeAide.aideFinanciereMedicale,
|
||||
TypeAide.aideFinanciereEducation,
|
||||
].contains(typeAide);
|
||||
}
|
||||
|
||||
bool _isValidPhoneNumber(String phone) {
|
||||
final phoneRegex = RegExp(r'^(\+225)?[0-9]{8,10}$');
|
||||
return phoneRegex.hasMatch(phone.replaceAll(RegExp(r'[\s\-\(\)]'), ''));
|
||||
}
|
||||
|
||||
bool _isValidEmail(String email) {
|
||||
final emailRegex = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');
|
||||
return emailRegex.hasMatch(email);
|
||||
}
|
||||
|
||||
bool _isValidTimeFormat(String time) {
|
||||
final timeRegex = RegExp(r'^([01]?[0-9]|2[0-3]):[0-5][0-9]$');
|
||||
return timeRegex.hasMatch(time);
|
||||
}
|
||||
|
||||
int _compareTime(String time1, String time2) {
|
||||
final parts1 = time1.split(':');
|
||||
final parts2 = time2.split(':');
|
||||
|
||||
final minutes1 = int.parse(parts1[0]) * 60 + int.parse(parts1[1]);
|
||||
final minutes2 = int.parse(parts2[0]) * 60 + int.parse(parts2[1]);
|
||||
|
||||
return minutes1.compareTo(minutes2);
|
||||
}
|
||||
}
|
||||
|
||||
class ValiderPropositionAideParams {
|
||||
final PropositionAide proposition;
|
||||
|
||||
ValiderPropositionAideParams({required this.proposition});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer le score de pertinence d'une proposition
|
||||
class CalculerScorePropositionUseCase implements UseCase<double, CalculerScorePropositionParams> {
|
||||
CalculerScorePropositionUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, double>> call(CalculerScorePropositionParams params) async {
|
||||
try {
|
||||
final proposition = params.proposition;
|
||||
double score = 50.0; // Score de base
|
||||
|
||||
// Bonus pour l'expérience (nombre d'aides réalisées)
|
||||
score += (proposition.nombreBeneficiairesAides * 2.0).clamp(0.0, 20.0);
|
||||
|
||||
// Bonus pour la note moyenne
|
||||
if (proposition.noteMoyenne != null && proposition.nombreEvaluations >= 3) {
|
||||
score += (proposition.noteMoyenne! - 3.0) * 10.0;
|
||||
}
|
||||
|
||||
// Bonus pour la récence (proposition créée récemment)
|
||||
final joursDepuisCreation = DateTime.now().difference(proposition.dateCreation).inDays;
|
||||
if (joursDepuisCreation <= 30) {
|
||||
score += 10.0;
|
||||
} else if (joursDepuisCreation <= 90) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
// Bonus pour la disponibilité
|
||||
if (proposition.isActiveEtDisponible) {
|
||||
score += 15.0;
|
||||
}
|
||||
|
||||
// Malus pour l'inactivité (pas de vues)
|
||||
if (proposition.nombreVues == 0) {
|
||||
score -= 10.0;
|
||||
}
|
||||
|
||||
// Bonus pour la vérification
|
||||
if (proposition.estVerifiee) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
return Right(score.clamp(0.0, 100.0));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du calcul du score: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerScorePropositionParams {
|
||||
final PropositionAide proposition;
|
||||
|
||||
CalculerScorePropositionParams({required this.proposition});
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/usecases/usecase.dart';
|
||||
import '../entities/demande_aide.dart';
|
||||
import '../entities/proposition_aide.dart';
|
||||
import '../repositories/solidarite_repository.dart';
|
||||
|
||||
/// Cas d'usage pour obtenir les statistiques complètes de solidarité
|
||||
class ObtenirStatistiquesSolidariteUseCase implements UseCase<StatistiquesSolidarite, ObtenirStatistiquesSolidariteParams> {
|
||||
final SolidariteRepository repository;
|
||||
|
||||
ObtenirStatistiquesSolidariteUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, StatistiquesSolidarite>> call(ObtenirStatistiquesSolidariteParams params) async {
|
||||
final result = await repository.obtenirStatistiquesSolidarite(params.organisationId);
|
||||
|
||||
return result.fold(
|
||||
(failure) => Left(failure),
|
||||
(data) {
|
||||
try {
|
||||
final statistiques = StatistiquesSolidarite.fromMap(data);
|
||||
return Right(statistiques);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du parsing des statistiques: ${e.toString()}'));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ObtenirStatistiquesSolidariteParams {
|
||||
final String organisationId;
|
||||
|
||||
ObtenirStatistiquesSolidariteParams({required this.organisationId});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour calculer les KPIs de performance
|
||||
class CalculerKPIsPerformanceUseCase implements UseCase<KPIsPerformance, CalculerKPIsPerformanceParams> {
|
||||
CalculerKPIsPerformanceUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, KPIsPerformance>> call(CalculerKPIsPerformanceParams params) async {
|
||||
try {
|
||||
// Simulation de calculs KPI - dans une vraie implémentation,
|
||||
// ces calculs seraient basés sur des données réelles
|
||||
|
||||
final kpis = KPIsPerformance(
|
||||
efficaciteMatching: _calculerEfficaciteMatching(params.statistiques),
|
||||
tempsReponseMoyen: _calculerTempsReponseMoyen(params.statistiques),
|
||||
satisfactionGlobale: _calculerSatisfactionGlobale(params.statistiques),
|
||||
tauxResolution: _calculerTauxResolution(params.statistiques),
|
||||
impactSocial: _calculerImpactSocial(params.statistiques),
|
||||
engagementCommunautaire: _calculerEngagementCommunautaire(params.statistiques),
|
||||
evolutionMensuelle: _calculerEvolutionMensuelle(params.statistiques),
|
||||
objectifsAtteints: _verifierObjectifsAtteints(params.statistiques),
|
||||
);
|
||||
|
||||
return Right(kpis);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors du calcul des KPIs: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
double _calculerEfficaciteMatching(StatistiquesSolidarite stats) {
|
||||
if (stats.demandes.total == 0) return 0.0;
|
||||
|
||||
final demandesMatchees = stats.demandes.parStatut[StatutAide.approuvee] ?? 0;
|
||||
return (demandesMatchees / stats.demandes.total) * 100;
|
||||
}
|
||||
|
||||
Duration _calculerTempsReponseMoyen(StatistiquesSolidarite stats) {
|
||||
return Duration(hours: stats.demandes.delaiMoyenTraitementHeures.toInt());
|
||||
}
|
||||
|
||||
double _calculerSatisfactionGlobale(StatistiquesSolidarite stats) {
|
||||
// Simulation basée sur le taux d'approbation
|
||||
return (stats.demandes.tauxApprobation / 100) * 5.0;
|
||||
}
|
||||
|
||||
double _calculerTauxResolution(StatistiquesSolidarite stats) {
|
||||
if (stats.demandes.total == 0) return 0.0;
|
||||
|
||||
final demandesResolues = (stats.demandes.parStatut[StatutAide.terminee] ?? 0) +
|
||||
(stats.demandes.parStatut[StatutAide.versee] ?? 0) +
|
||||
(stats.demandes.parStatut[StatutAide.livree] ?? 0);
|
||||
|
||||
return (demandesResolues / stats.demandes.total) * 100;
|
||||
}
|
||||
|
||||
int _calculerImpactSocial(StatistiquesSolidarite stats) {
|
||||
// Estimation du nombre de personnes aidées
|
||||
return (stats.demandes.total * 2.3).round(); // Moyenne de 2.3 personnes par demande
|
||||
}
|
||||
|
||||
double _calculerEngagementCommunautaire(StatistiquesSolidarite stats) {
|
||||
if (stats.propositions.total == 0) return 0.0;
|
||||
|
||||
return (stats.propositions.actives / stats.propositions.total) * 100;
|
||||
}
|
||||
|
||||
EvolutionMensuelle _calculerEvolutionMensuelle(StatistiquesSolidarite stats) {
|
||||
// Simulation d'évolution - dans une vraie implémentation,
|
||||
// on comparerait avec les données du mois précédent
|
||||
return const EvolutionMensuelle(
|
||||
demandes: 12.5,
|
||||
propositions: 8.3,
|
||||
montants: 15.7,
|
||||
satisfaction: 2.1,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, bool> _verifierObjectifsAtteints(StatistiquesSolidarite stats) {
|
||||
return {
|
||||
'tauxApprobation': stats.demandes.tauxApprobation >= 80.0,
|
||||
'delaiTraitement': stats.demandes.delaiMoyenTraitementHeures <= 48.0,
|
||||
'satisfactionMinimum': true, // Simulation
|
||||
'propositionsActives': stats.propositions.actives >= 10,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class CalculerKPIsPerformanceParams {
|
||||
final StatistiquesSolidarite statistiques;
|
||||
|
||||
CalculerKPIsPerformanceParams({required this.statistiques});
|
||||
}
|
||||
|
||||
/// Cas d'usage pour générer un rapport d'activité
|
||||
class GenererRapportActiviteUseCase implements UseCase<RapportActivite, GenererRapportActiviteParams> {
|
||||
GenererRapportActiviteUseCase();
|
||||
|
||||
@override
|
||||
Future<Either<Failure, RapportActivite>> call(GenererRapportActiviteParams params) async {
|
||||
try {
|
||||
final rapport = RapportActivite(
|
||||
periode: params.periode,
|
||||
dateGeneration: DateTime.now(),
|
||||
resumeExecutif: _genererResumeExecutif(params.statistiques),
|
||||
metriquesClees: _extraireMetriquesClees(params.statistiques),
|
||||
analyseTendances: _analyserTendances(params.statistiques),
|
||||
recommandations: _genererRecommandations(params.statistiques),
|
||||
annexes: _genererAnnexes(params.statistiques),
|
||||
);
|
||||
|
||||
return Right(rapport);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur lors de la génération du rapport: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
String _genererResumeExecutif(StatistiquesSolidarite stats) {
|
||||
return '''
|
||||
Durant cette période, ${stats.demandes.total} demandes d'aide ont été traitées avec un taux d'approbation de ${stats.demandes.tauxApprobation.toStringAsFixed(1)}%.
|
||||
|
||||
${stats.propositions.total} propositions d'aide ont été créées, dont ${stats.propositions.actives} sont actuellement actives.
|
||||
|
||||
Le montant total versé s'élève à ${stats.financier.montantTotalVerse.toStringAsFixed(0)} FCFA, représentant ${stats.financier.tauxVersement.toStringAsFixed(1)}% des montants approuvés.
|
||||
|
||||
Le délai moyen de traitement des demandes est de ${stats.demandes.delaiMoyenTraitementHeures.toStringAsFixed(1)} heures.
|
||||
''';
|
||||
}
|
||||
|
||||
Map<String, dynamic> _extraireMetriquesClees(StatistiquesSolidarite stats) {
|
||||
return {
|
||||
'totalDemandes': stats.demandes.total,
|
||||
'tauxApprobation': stats.demandes.tauxApprobation,
|
||||
'montantVerse': stats.financier.montantTotalVerse,
|
||||
'propositionsActives': stats.propositions.actives,
|
||||
'delaiMoyenTraitement': stats.demandes.delaiMoyenTraitementHeures,
|
||||
};
|
||||
}
|
||||
|
||||
String _analyserTendances(StatistiquesSolidarite stats) {
|
||||
return '''
|
||||
Tendances observées :
|
||||
- Augmentation de 12.5% des demandes par rapport au mois précédent
|
||||
- Amélioration du taux d'approbation (+3.2%)
|
||||
- Réduction du délai moyen de traitement (-8 heures)
|
||||
- Croissance de l'engagement communautaire (+5.7%)
|
||||
''';
|
||||
}
|
||||
|
||||
List<String> _genererRecommandations(StatistiquesSolidarite stats) {
|
||||
final recommandations = <String>[];
|
||||
|
||||
if (stats.demandes.tauxApprobation < 80.0) {
|
||||
recommandations.add('Améliorer le processus d\'évaluation pour augmenter le taux d\'approbation');
|
||||
}
|
||||
|
||||
if (stats.demandes.delaiMoyenTraitementHeures > 48.0) {
|
||||
recommandations.add('Optimiser les délais de traitement des demandes');
|
||||
}
|
||||
|
||||
if (stats.propositions.actives < 10) {
|
||||
recommandations.add('Encourager plus de propositions d\'aide de la part des membres');
|
||||
}
|
||||
|
||||
if (stats.financier.tauxVersement < 90.0) {
|
||||
recommandations.add('Améliorer le suivi des versements approuvés');
|
||||
}
|
||||
|
||||
if (recommandations.isEmpty) {
|
||||
recommandations.add('Maintenir l\'excellent niveau de performance actuel');
|
||||
}
|
||||
|
||||
return recommandations;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _genererAnnexes(StatistiquesSolidarite stats) {
|
||||
return {
|
||||
'repartitionParType': stats.demandes.parType,
|
||||
'repartitionParStatut': stats.demandes.parStatut,
|
||||
'repartitionParPriorite': stats.demandes.parPriorite,
|
||||
'statistiquesFinancieres': {
|
||||
'montantTotalDemande': stats.financier.montantTotalDemande,
|
||||
'montantTotalApprouve': stats.financier.montantTotalApprouve,
|
||||
'montantTotalVerse': stats.financier.montantTotalVerse,
|
||||
'capaciteFinanciereDisponible': stats.financier.capaciteFinanciereDisponible,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class GenererRapportActiviteParams {
|
||||
final StatistiquesSolidarite statistiques;
|
||||
final PeriodeRapport periode;
|
||||
|
||||
GenererRapportActiviteParams({
|
||||
required this.statistiques,
|
||||
required this.periode,
|
||||
});
|
||||
}
|
||||
|
||||
/// Classes de données pour les statistiques
|
||||
|
||||
class StatistiquesSolidarite {
|
||||
final StatistiquesDemandes demandes;
|
||||
final StatistiquesPropositions propositions;
|
||||
final StatistiquesFinancieres financier;
|
||||
final Map<String, dynamic> kpis;
|
||||
final Map<String, dynamic> tendances;
|
||||
final DateTime dateCalcul;
|
||||
final String organisationId;
|
||||
|
||||
const StatistiquesSolidarite({
|
||||
required this.demandes,
|
||||
required this.propositions,
|
||||
required this.financier,
|
||||
required this.kpis,
|
||||
required this.tendances,
|
||||
required this.dateCalcul,
|
||||
required this.organisationId,
|
||||
});
|
||||
|
||||
factory StatistiquesSolidarite.fromMap(Map<String, dynamic> map) {
|
||||
return StatistiquesSolidarite(
|
||||
demandes: StatistiquesDemandes.fromMap(map['demandes']),
|
||||
propositions: StatistiquesPropositions.fromMap(map['propositions']),
|
||||
financier: StatistiquesFinancieres.fromMap(map['financier']),
|
||||
kpis: Map<String, dynamic>.from(map['kpis']),
|
||||
tendances: Map<String, dynamic>.from(map['tendances']),
|
||||
dateCalcul: DateTime.parse(map['dateCalcul']),
|
||||
organisationId: map['organisationId'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StatistiquesDemandes {
|
||||
final int total;
|
||||
final Map<StatutAide, int> parStatut;
|
||||
final Map<TypeAide, int> parType;
|
||||
final Map<PrioriteAide, int> parPriorite;
|
||||
final int urgentes;
|
||||
final int enRetard;
|
||||
final double tauxApprobation;
|
||||
final double delaiMoyenTraitementHeures;
|
||||
|
||||
const StatistiquesDemandes({
|
||||
required this.total,
|
||||
required this.parStatut,
|
||||
required this.parType,
|
||||
required this.parPriorite,
|
||||
required this.urgentes,
|
||||
required this.enRetard,
|
||||
required this.tauxApprobation,
|
||||
required this.delaiMoyenTraitementHeures,
|
||||
});
|
||||
|
||||
factory StatistiquesDemandes.fromMap(Map<String, dynamic> map) {
|
||||
return StatistiquesDemandes(
|
||||
total: map['total'],
|
||||
parStatut: Map<StatutAide, int>.from(map['parStatut']),
|
||||
parType: Map<TypeAide, int>.from(map['parType']),
|
||||
parPriorite: Map<PrioriteAide, int>.from(map['parPriorite']),
|
||||
urgentes: map['urgentes'],
|
||||
enRetard: map['enRetard'],
|
||||
tauxApprobation: map['tauxApprobation'].toDouble(),
|
||||
delaiMoyenTraitementHeures: map['delaiMoyenTraitementHeures'].toDouble(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StatistiquesPropositions {
|
||||
final int total;
|
||||
final int actives;
|
||||
final Map<TypeAide, int> parType;
|
||||
final int capaciteDisponible;
|
||||
final double tauxUtilisationMoyen;
|
||||
final double noteMoyenne;
|
||||
|
||||
const StatistiquesPropositions({
|
||||
required this.total,
|
||||
required this.actives,
|
||||
required this.parType,
|
||||
required this.capaciteDisponible,
|
||||
required this.tauxUtilisationMoyen,
|
||||
required this.noteMoyenne,
|
||||
});
|
||||
|
||||
factory StatistiquesPropositions.fromMap(Map<String, dynamic> map) {
|
||||
return StatistiquesPropositions(
|
||||
total: map['total'],
|
||||
actives: map['actives'],
|
||||
parType: Map<TypeAide, int>.from(map['parType']),
|
||||
capaciteDisponible: map['capaciteDisponible'],
|
||||
tauxUtilisationMoyen: map['tauxUtilisationMoyen'].toDouble(),
|
||||
noteMoyenne: map['noteMoyenne'].toDouble(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StatistiquesFinancieres {
|
||||
final double montantTotalDemande;
|
||||
final double montantTotalApprouve;
|
||||
final double montantTotalVerse;
|
||||
final double capaciteFinanciereDisponible;
|
||||
final double montantMoyenDemande;
|
||||
final double tauxVersement;
|
||||
|
||||
const StatistiquesFinancieres({
|
||||
required this.montantTotalDemande,
|
||||
required this.montantTotalApprouve,
|
||||
required this.montantTotalVerse,
|
||||
required this.capaciteFinanciereDisponible,
|
||||
required this.montantMoyenDemande,
|
||||
required this.tauxVersement,
|
||||
});
|
||||
|
||||
factory StatistiquesFinancieres.fromMap(Map<String, dynamic> map) {
|
||||
return StatistiquesFinancieres(
|
||||
montantTotalDemande: map['montantTotalDemande'].toDouble(),
|
||||
montantTotalApprouve: map['montantTotalApprouve'].toDouble(),
|
||||
montantTotalVerse: map['montantTotalVerse'].toDouble(),
|
||||
capaciteFinanciereDisponible: map['capaciteFinanciereDisponible'].toDouble(),
|
||||
montantMoyenDemande: map['montantMoyenDemande'].toDouble(),
|
||||
tauxVersement: map['tauxVersement'].toDouble(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KPIsPerformance {
|
||||
final double efficaciteMatching;
|
||||
final Duration tempsReponseMoyen;
|
||||
final double satisfactionGlobale;
|
||||
final double tauxResolution;
|
||||
final int impactSocial;
|
||||
final double engagementCommunautaire;
|
||||
final EvolutionMensuelle evolutionMensuelle;
|
||||
final Map<String, bool> objectifsAtteints;
|
||||
|
||||
const KPIsPerformance({
|
||||
required this.efficaciteMatching,
|
||||
required this.tempsReponseMoyen,
|
||||
required this.satisfactionGlobale,
|
||||
required this.tauxResolution,
|
||||
required this.impactSocial,
|
||||
required this.engagementCommunautaire,
|
||||
required this.evolutionMensuelle,
|
||||
required this.objectifsAtteints,
|
||||
});
|
||||
}
|
||||
|
||||
class EvolutionMensuelle {
|
||||
final double demandes;
|
||||
final double propositions;
|
||||
final double montants;
|
||||
final double satisfaction;
|
||||
|
||||
const EvolutionMensuelle({
|
||||
required this.demandes,
|
||||
required this.propositions,
|
||||
required this.montants,
|
||||
required this.satisfaction,
|
||||
});
|
||||
}
|
||||
|
||||
class RapportActivite {
|
||||
final PeriodeRapport periode;
|
||||
final DateTime dateGeneration;
|
||||
final String resumeExecutif;
|
||||
final Map<String, dynamic> metriquesClees;
|
||||
final String analyseTendances;
|
||||
final List<String> recommandations;
|
||||
final Map<String, dynamic> annexes;
|
||||
|
||||
const RapportActivite({
|
||||
required this.periode,
|
||||
required this.dateGeneration,
|
||||
required this.resumeExecutif,
|
||||
required this.metriquesClees,
|
||||
required this.analyseTendances,
|
||||
required this.recommandations,
|
||||
required this.annexes,
|
||||
});
|
||||
}
|
||||
|
||||
class PeriodeRapport {
|
||||
final DateTime debut;
|
||||
final DateTime fin;
|
||||
final String libelle;
|
||||
|
||||
const PeriodeRapport({
|
||||
required this.debut,
|
||||
required this.fin,
|
||||
required this.libelle,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user