Versione OK Pour l'onglet événements.

This commit is contained in:
DahoudG
2025-09-15 20:15:34 +00:00
parent 8a619ee1bf
commit 12d514d866
73 changed files with 11508 additions and 674 deletions

View File

@@ -0,0 +1,326 @@
import 'package:json_annotation/json_annotation.dart';
part 'cotisation_filter_model.g.dart';
/// Modèle pour les filtres de recherche des cotisations
/// Permet de filtrer les cotisations selon différents critères
@JsonSerializable()
class CotisationFilterModel {
final String? membreId;
final String? nomMembre;
final String? numeroMembre;
final List<String>? statuts;
final List<String>? typesCotisation;
final DateTime? dateEcheanceMin;
final DateTime? dateEcheanceMax;
final DateTime? datePaiementMin;
final DateTime? datePaiementMax;
final double? montantMin;
final double? montantMax;
final int? annee;
final int? mois;
final String? periode;
final bool? recurrente;
final bool? enRetard;
final bool? echeanceProche;
final String? methodePaiement;
final String? recherche;
final String? triPar;
final String? ordretri;
final int page;
final int size;
const CotisationFilterModel({
this.membreId,
this.nomMembre,
this.numeroMembre,
this.statuts,
this.typesCotisation,
this.dateEcheanceMin,
this.dateEcheanceMax,
this.datePaiementMin,
this.datePaiementMax,
this.montantMin,
this.montantMax,
this.annee,
this.mois,
this.periode,
this.recurrente,
this.enRetard,
this.echeanceProche,
this.methodePaiement,
this.recherche,
this.triPar,
this.ordreTriPar,
this.page = 0,
this.size = 20,
});
/// Factory pour créer depuis JSON
factory CotisationFilterModel.fromJson(Map<String, dynamic> json) =>
_$CotisationFilterModelFromJson(json);
/// Convertit vers JSON
Map<String, dynamic> toJson() => _$CotisationFilterModelToJson(this);
/// Crée un filtre vide
factory CotisationFilterModel.empty() {
return const CotisationFilterModel();
}
/// Crée un filtre pour les cotisations en retard
factory CotisationFilterModel.enRetard() {
return const CotisationFilterModel(
enRetard: true,
triPar: 'dateEcheance',
ordreTriPar: 'ASC',
);
}
/// Crée un filtre pour les cotisations avec échéance proche
factory CotisationFilterModel.echeanceProche() {
return const CotisationFilterModel(
echeanceProche: true,
triPar: 'dateEcheance',
ordreTriPar: 'ASC',
);
}
/// Crée un filtre pour un membre spécifique
factory CotisationFilterModel.parMembre(String membreId) {
return CotisationFilterModel(
membreId: membreId,
triPar: 'dateEcheance',
ordreTriPar: 'DESC',
);
}
/// Crée un filtre pour un statut spécifique
factory CotisationFilterModel.parStatut(String statut) {
return CotisationFilterModel(
statuts: [statut],
triPar: 'dateEcheance',
ordreTriPar: 'DESC',
);
}
/// Crée un filtre pour une période spécifique
factory CotisationFilterModel.parPeriode(int annee, [int? mois]) {
return CotisationFilterModel(
annee: annee,
mois: mois,
triPar: 'dateEcheance',
ordreTriPar: 'DESC',
);
}
/// Crée un filtre pour une recherche textuelle
factory CotisationFilterModel.recherche(String terme) {
return CotisationFilterModel(
recherche: terme,
triPar: 'dateCreation',
ordreTriPar: 'DESC',
);
}
/// Vérifie si le filtre est vide
bool get isEmpty {
return membreId == null &&
nomMembre == null &&
numeroMembre == null &&
(statuts == null || statuts!.isEmpty) &&
(typesCotisation == null || typesCotisation!.isEmpty) &&
dateEcheanceMin == null &&
dateEcheanceMax == null &&
datePaiementMin == null &&
datePaiementMax == null &&
montantMin == null &&
montantMax == null &&
annee == null &&
mois == null &&
periode == null &&
recurrente == null &&
enRetard == null &&
echeanceProche == null &&
methodePaiement == null &&
(recherche == null || recherche!.isEmpty);
}
/// Vérifie si le filtre a des critères actifs
bool get hasActiveFilters => !isEmpty;
/// Compte le nombre de filtres actifs
int get nombreFiltresActifs {
int count = 0;
if (membreId != null) count++;
if (nomMembre != null) count++;
if (numeroMembre != null) count++;
if (statuts != null && statuts!.isNotEmpty) count++;
if (typesCotisation != null && typesCotisation!.isNotEmpty) count++;
if (dateEcheanceMin != null || dateEcheanceMax != null) count++;
if (datePaiementMin != null || datePaiementMax != null) count++;
if (montantMin != null || montantMax != null) count++;
if (annee != null) count++;
if (mois != null) count++;
if (periode != null) count++;
if (recurrente != null) count++;
if (enRetard == true) count++;
if (echeanceProche == true) count++;
if (methodePaiement != null) count++;
if (recherche != null && recherche!.isNotEmpty) count++;
return count;
}
/// Retourne une description textuelle des filtres actifs
String get descriptionFiltres {
List<String> descriptions = [];
if (statuts != null && statuts!.isNotEmpty) {
descriptions.add('Statut: ${statuts!.join(', ')}');
}
if (typesCotisation != null && typesCotisation!.isNotEmpty) {
descriptions.add('Type: ${typesCotisation!.join(', ')}');
}
if (annee != null) {
String periodeDesc = 'Année: $annee';
if (mois != null) {
periodeDesc += ', Mois: $mois';
}
descriptions.add(periodeDesc);
}
if (enRetard == true) {
descriptions.add('En retard');
}
if (echeanceProche == true) {
descriptions.add('Échéance proche');
}
if (montantMin != null || montantMax != null) {
String montantDesc = 'Montant: ';
if (montantMin != null && montantMax != null) {
montantDesc += '${montantMin!.toStringAsFixed(0)} - ${montantMax!.toStringAsFixed(0)} XOF';
} else if (montantMin != null) {
montantDesc += '${montantMin!.toStringAsFixed(0)} XOF';
} else {
montantDesc += '${montantMax!.toStringAsFixed(0)} XOF';
}
descriptions.add(montantDesc);
}
if (recherche != null && recherche!.isNotEmpty) {
descriptions.add('Recherche: "$recherche"');
}
return descriptions.join('');
}
/// Convertit vers Map pour les paramètres de requête
Map<String, dynamic> toQueryParameters() {
Map<String, dynamic> params = {};
if (membreId != null) params['membreId'] = membreId;
if (statuts != null && statuts!.isNotEmpty) {
params['statut'] = statuts!.length == 1 ? statuts!.first : statuts!.join(',');
}
if (typesCotisation != null && typesCotisation!.isNotEmpty) {
params['typeCotisation'] = typesCotisation!.length == 1 ? typesCotisation!.first : typesCotisation!.join(',');
}
if (annee != null) params['annee'] = annee.toString();
if (mois != null) params['mois'] = mois.toString();
if (periode != null) params['periode'] = periode;
if (recurrente != null) params['recurrente'] = recurrente.toString();
if (enRetard == true) params['enRetard'] = 'true';
if (echeanceProche == true) params['echeanceProche'] = 'true';
if (methodePaiement != null) params['methodePaiement'] = methodePaiement;
if (recherche != null && recherche!.isNotEmpty) params['q'] = recherche;
if (triPar != null) params['sortBy'] = triPar;
if (ordreTriPar != null) params['sortOrder'] = ordreTriPar;
params['page'] = page.toString();
params['size'] = size.toString();
return params;
}
/// Copie avec modifications
CotisationFilterModel copyWith({
String? membreId,
String? nomMembre,
String? numeroMembre,
List<String>? statuts,
List<String>? typesCotisation,
DateTime? dateEcheanceMin,
DateTime? dateEcheanceMax,
DateTime? datePaiementMin,
DateTime? datePaiementMax,
double? montantMin,
double? montantMax,
int? annee,
int? mois,
String? periode,
bool? recurrente,
bool? enRetard,
bool? echeanceProche,
String? methodePaiement,
String? recherche,
String? triPar,
String? ordreTriPar,
int? page,
int? size,
}) {
return CotisationFilterModel(
membreId: membreId ?? this.membreId,
nomMembre: nomMembre ?? this.nomMembre,
numeroMembre: numeroMembre ?? this.numeroMembre,
statuts: statuts ?? this.statuts,
typesCotisation: typesCotisation ?? this.typesCotisation,
dateEcheanceMin: dateEcheanceMin ?? this.dateEcheanceMin,
dateEcheanceMax: dateEcheanceMax ?? this.dateEcheanceMax,
datePaiementMin: datePaiementMin ?? this.datePaiementMin,
datePaiementMax: datePaiementMax ?? this.datePaiementMax,
montantMin: montantMin ?? this.montantMin,
montantMax: montantMax ?? this.montantMax,
annee: annee ?? this.annee,
mois: mois ?? this.mois,
periode: periode ?? this.periode,
recurrente: recurrente ?? this.recurrente,
enRetard: enRetard ?? this.enRetard,
echeanceProche: echeanceProche ?? this.echeanceProche,
methodePaiement: methodePaiement ?? this.methodePaiement,
recherche: recherche ?? this.recherche,
triPar: triPar ?? this.triPar,
ordreTriPar: ordreTriPar ?? this.ordreTriPar,
page: page ?? this.page,
size: size ?? this.size,
);
}
/// Réinitialise tous les filtres
CotisationFilterModel clear() {
return const CotisationFilterModel();
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CotisationFilterModel &&
other.membreId == membreId &&
other.statuts == statuts &&
other.typesCotisation == typesCotisation &&
other.annee == annee &&
other.mois == mois &&
other.recherche == recherche;
}
@override
int get hashCode => Object.hash(membreId, statuts, typesCotisation, annee, mois, recherche);
@override
String toString() {
return 'CotisationFilterModel(filtres actifs: $nombreFiltresActifs)';
}
}

View File

@@ -0,0 +1,72 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cotisation_filter_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CotisationFilterModel _$CotisationFilterModelFromJson(
Map<String, dynamic> json) =>
CotisationFilterModel(
membreId: json['membreId'] as String?,
nomMembre: json['nomMembre'] as String?,
numeroMembre: json['numeroMembre'] as String?,
statuts:
(json['statuts'] as List<dynamic>?)?.map((e) => e as String).toList(),
typesCotisation: (json['typesCotisation'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
dateEcheanceMin: json['dateEcheanceMin'] == null
? null
: DateTime.parse(json['dateEcheanceMin'] as String),
dateEcheanceMax: json['dateEcheanceMax'] == null
? null
: DateTime.parse(json['dateEcheanceMax'] as String),
datePaiementMin: json['datePaiementMin'] == null
? null
: DateTime.parse(json['datePaiementMin'] as String),
datePaiementMax: json['datePaiementMax'] == null
? null
: DateTime.parse(json['datePaiementMax'] as String),
montantMin: (json['montantMin'] as num?)?.toDouble(),
montantMax: (json['montantMax'] as num?)?.toDouble(),
annee: (json['annee'] as num?)?.toInt(),
mois: (json['mois'] as num?)?.toInt(),
periode: json['periode'] as String?,
recurrente: json['recurrente'] as bool?,
enRetard: json['enRetard'] as bool?,
echeanceProche: json['echeanceProche'] as bool?,
methodePaiement: json['methodePaiement'] as String?,
recherche: json['recherche'] as String?,
triPar: json['triPar'] as String?,
page: (json['page'] as num?)?.toInt() ?? 0,
size: (json['size'] as num?)?.toInt() ?? 20,
);
Map<String, dynamic> _$CotisationFilterModelToJson(
CotisationFilterModel instance) =>
<String, dynamic>{
'membreId': instance.membreId,
'nomMembre': instance.nomMembre,
'numeroMembre': instance.numeroMembre,
'statuts': instance.statuts,
'typesCotisation': instance.typesCotisation,
'dateEcheanceMin': instance.dateEcheanceMin?.toIso8601String(),
'dateEcheanceMax': instance.dateEcheanceMax?.toIso8601String(),
'datePaiementMin': instance.datePaiementMin?.toIso8601String(),
'datePaiementMax': instance.datePaiementMax?.toIso8601String(),
'montantMin': instance.montantMin,
'montantMax': instance.montantMax,
'annee': instance.annee,
'mois': instance.mois,
'periode': instance.periode,
'recurrente': instance.recurrente,
'enRetard': instance.enRetard,
'echeanceProche': instance.echeanceProche,
'methodePaiement': instance.methodePaiement,
'recherche': instance.recherche,
'triPar': instance.triPar,
'page': instance.page,
'size': instance.size,
};

View File

@@ -88,6 +88,12 @@ class CotisationModel {
return (montantPaye / montantDu * 100).clamp(0, 100);
}
/// Calcule le nombre de jours de retard
int get joursRetard {
if (!isEnRetard) return 0;
return DateTime.now().difference(dateEcheance).inDays;
}
/// Retourne la couleur associée au statut
String get couleurStatut {
switch (statut) {

View File

@@ -0,0 +1,295 @@
import 'package:json_annotation/json_annotation.dart';
part 'cotisation_statistics_model.g.dart';
/// Modèle de données pour les statistiques des cotisations
/// Représente les métriques et analyses des cotisations
@JsonSerializable()
class CotisationStatisticsModel {
final int totalCotisations;
final double montantTotal;
final double montantPaye;
final double montantRestant;
final int cotisationsPayees;
final int cotisationsEnAttente;
final int cotisationsEnRetard;
final int cotisationsAnnulees;
final double tauxPaiement;
final double tauxRetard;
final double montantMoyenCotisation;
final double montantMoyenPaiement;
final Map<String, int>? repartitionParType;
final Map<String, double>? montantParType;
final Map<String, int>? repartitionParStatut;
final Map<String, double>? montantParStatut;
final Map<String, int>? evolutionMensuelle;
final Map<String, double>? chiffreAffaireMensuel;
final List<CotisationTrendModel>? tendances;
final DateTime dateCalcul;
final String? periode;
final int? annee;
final int? mois;
const CotisationStatisticsModel({
required this.totalCotisations,
required this.montantTotal,
required this.montantPaye,
required this.montantRestant,
required this.cotisationsPayees,
required this.cotisationsEnAttente,
required this.cotisationsEnRetard,
required this.cotisationsAnnulees,
required this.tauxPaiement,
required this.tauxRetard,
required this.montantMoyenCotisation,
required this.montantMoyenPaiement,
this.repartitionParType,
this.montantParType,
this.repartitionParStatut,
this.montantParStatut,
this.evolutionMensuelle,
this.chiffreAffaireMensuel,
this.tendances,
required this.dateCalcul,
this.periode,
this.annee,
this.mois,
});
/// Factory pour créer depuis JSON
factory CotisationStatisticsModel.fromJson(Map<String, dynamic> json) =>
_$CotisationStatisticsModelFromJson(json);
/// Convertit vers JSON
Map<String, dynamic> toJson() => _$CotisationStatisticsModelToJson(this);
/// Calcule le pourcentage de cotisations payées
double get pourcentageCotisationsPayees {
if (totalCotisations == 0) return 0;
return (cotisationsPayees / totalCotisations * 100);
}
/// Calcule le pourcentage de cotisations en retard
double get pourcentageCotisationsEnRetard {
if (totalCotisations == 0) return 0;
return (cotisationsEnRetard / totalCotisations * 100);
}
/// Calcule le pourcentage de cotisations en attente
double get pourcentageCotisationsEnAttente {
if (totalCotisations == 0) return 0;
return (cotisationsEnAttente / totalCotisations * 100);
}
/// Retourne le statut de santé financière
String get statutSanteFinanciere {
if (tauxPaiement >= 90) return 'EXCELLENT';
if (tauxPaiement >= 75) return 'BON';
if (tauxPaiement >= 60) return 'MOYEN';
if (tauxPaiement >= 40) return 'FAIBLE';
return 'CRITIQUE';
}
/// Retourne la couleur associée au statut de santé
String get couleurSanteFinanciere {
switch (statutSanteFinanciere) {
case 'EXCELLENT':
return '#4CAF50'; // Vert
case 'BON':
return '#8BC34A'; // Vert clair
case 'MOYEN':
return '#FF9800'; // Orange
case 'FAIBLE':
return '#FF5722'; // Orange foncé
case 'CRITIQUE':
return '#F44336'; // Rouge
default:
return '#757575'; // Gris
}
}
/// Retourne le libellé du statut de santé
String get libelleSanteFinanciere {
switch (statutSanteFinanciere) {
case 'EXCELLENT':
return 'Excellente santé financière';
case 'BON':
return 'Bonne santé financière';
case 'MOYEN':
return 'Santé financière moyenne';
case 'FAIBLE':
return 'Santé financière faible';
case 'CRITIQUE':
return 'Situation critique';
default:
return 'Statut inconnu';
}
}
/// Calcule la progression par rapport à la période précédente
double? calculerProgression(CotisationStatisticsModel? precedent) {
if (precedent == null || precedent.montantPaye == 0) return null;
return ((montantPaye - precedent.montantPaye) / precedent.montantPaye * 100);
}
/// Retourne les indicateurs clés de performance
Map<String, dynamic> get kpis {
return {
'tauxRecouvrement': tauxPaiement,
'tauxRetard': tauxRetard,
'montantMoyenCotisation': montantMoyenCotisation,
'montantMoyenPaiement': montantMoyenPaiement,
'efficaciteRecouvrement': montantPaye / montantTotal * 100,
'risqueImpaye': montantRestant / montantTotal * 100,
};
}
/// Retourne les alertes basées sur les seuils
List<String> get alertes {
List<String> alertes = [];
if (tauxRetard > 20) {
alertes.add('Taux de retard élevé (${tauxRetard.toStringAsFixed(1)}%)');
}
if (tauxPaiement < 60) {
alertes.add('Taux de paiement faible (${tauxPaiement.toStringAsFixed(1)}%)');
}
if (cotisationsEnRetard > totalCotisations * 0.3) {
alertes.add('Trop de cotisations en retard ($cotisationsEnRetard)');
}
if (montantRestant > montantTotal * 0.4) {
alertes.add('Montant impayé important (${montantRestant.toStringAsFixed(0)} XOF)');
}
return alertes;
}
/// Vérifie si des actions sont nécessaires
bool get actionRequise => alertes.isNotEmpty;
/// Retourne les recommandations d'amélioration
List<String> get recommandations {
List<String> recommandations = [];
if (tauxRetard > 15) {
recommandations.add('Mettre en place des rappels automatiques');
recommandations.add('Contacter les membres en retard');
}
if (tauxPaiement < 70) {
recommandations.add('Faciliter les moyens de paiement');
recommandations.add('Proposer des échéanciers personnalisés');
}
if (cotisationsEnRetard > 10) {
recommandations.add('Organiser une campagne de recouvrement');
}
return recommandations;
}
/// Copie avec modifications
CotisationStatisticsModel copyWith({
int? totalCotisations,
double? montantTotal,
double? montantPaye,
double? montantRestant,
int? cotisationsPayees,
int? cotisationsEnAttente,
int? cotisationsEnRetard,
int? cotisationsAnnulees,
double? tauxPaiement,
double? tauxRetard,
double? montantMoyenCotisation,
double? montantMoyenPaiement,
Map<String, int>? repartitionParType,
Map<String, double>? montantParType,
Map<String, int>? repartitionParStatut,
Map<String, double>? montantParStatut,
Map<String, int>? evolutionMensuelle,
Map<String, double>? chiffreAffaireMensuel,
List<CotisationTrendModel>? tendances,
DateTime? dateCalcul,
String? periode,
int? annee,
int? mois,
}) {
return CotisationStatisticsModel(
totalCotisations: totalCotisations ?? this.totalCotisations,
montantTotal: montantTotal ?? this.montantTotal,
montantPaye: montantPaye ?? this.montantPaye,
montantRestant: montantRestant ?? this.montantRestant,
cotisationsPayees: cotisationsPayees ?? this.cotisationsPayees,
cotisationsEnAttente: cotisationsEnAttente ?? this.cotisationsEnAttente,
cotisationsEnRetard: cotisationsEnRetard ?? this.cotisationsEnRetard,
cotisationsAnnulees: cotisationsAnnulees ?? this.cotisationsAnnulees,
tauxPaiement: tauxPaiement ?? this.tauxPaiement,
tauxRetard: tauxRetard ?? this.tauxRetard,
montantMoyenCotisation: montantMoyenCotisation ?? this.montantMoyenCotisation,
montantMoyenPaiement: montantMoyenPaiement ?? this.montantMoyenPaiement,
repartitionParType: repartitionParType ?? this.repartitionParType,
montantParType: montantParType ?? this.montantParType,
repartitionParStatut: repartitionParStatut ?? this.repartitionParStatut,
montantParStatut: montantParStatut ?? this.montantParStatut,
evolutionMensuelle: evolutionMensuelle ?? this.evolutionMensuelle,
chiffreAffaireMensuel: chiffreAffaireMensuel ?? this.chiffreAffaireMensuel,
tendances: tendances ?? this.tendances,
dateCalcul: dateCalcul ?? this.dateCalcul,
periode: periode ?? this.periode,
annee: annee ?? this.annee,
mois: mois ?? this.mois,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CotisationStatisticsModel &&
other.dateCalcul == dateCalcul &&
other.periode == periode &&
other.annee == annee &&
other.mois == mois;
}
@override
int get hashCode => Object.hash(dateCalcul, periode, annee, mois);
@override
String toString() {
return 'CotisationStatisticsModel(totalCotisations: $totalCotisations, '
'montantTotal: $montantTotal, tauxPaiement: $tauxPaiement%)';
}
}
/// Modèle pour les tendances des cotisations
@JsonSerializable()
class CotisationTrendModel {
final String periode;
final int totalCotisations;
final double montantTotal;
final double montantPaye;
final double tauxPaiement;
final DateTime date;
const CotisationTrendModel({
required this.periode,
required this.totalCotisations,
required this.montantTotal,
required this.montantPaye,
required this.tauxPaiement,
required this.date,
});
factory CotisationTrendModel.fromJson(Map<String, dynamic> json) =>
_$CotisationTrendModelFromJson(json);
Map<String, dynamic> toJson() => _$CotisationTrendModelToJson(this);
@override
String toString() {
return 'CotisationTrendModel(periode: $periode, tauxPaiement: $tauxPaiement%)';
}
}

View File

@@ -0,0 +1,105 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cotisation_statistics_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CotisationStatisticsModel _$CotisationStatisticsModelFromJson(
Map<String, dynamic> json) =>
CotisationStatisticsModel(
totalCotisations: (json['totalCotisations'] as num).toInt(),
montantTotal: (json['montantTotal'] as num).toDouble(),
montantPaye: (json['montantPaye'] as num).toDouble(),
montantRestant: (json['montantRestant'] as num).toDouble(),
cotisationsPayees: (json['cotisationsPayees'] as num).toInt(),
cotisationsEnAttente: (json['cotisationsEnAttente'] as num).toInt(),
cotisationsEnRetard: (json['cotisationsEnRetard'] as num).toInt(),
cotisationsAnnulees: (json['cotisationsAnnulees'] as num).toInt(),
tauxPaiement: (json['tauxPaiement'] as num).toDouble(),
tauxRetard: (json['tauxRetard'] as num).toDouble(),
montantMoyenCotisation:
(json['montantMoyenCotisation'] as num).toDouble(),
montantMoyenPaiement: (json['montantMoyenPaiement'] as num).toDouble(),
repartitionParType:
(json['repartitionParType'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toInt()),
),
montantParType: (json['montantParType'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toDouble()),
),
repartitionParStatut:
(json['repartitionParStatut'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toInt()),
),
montantParStatut:
(json['montantParStatut'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toDouble()),
),
evolutionMensuelle:
(json['evolutionMensuelle'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toInt()),
),
chiffreAffaireMensuel:
(json['chiffreAffaireMensuel'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toDouble()),
),
tendances: (json['tendances'] as List<dynamic>?)
?.map((e) => CotisationTrendModel.fromJson(e as Map<String, dynamic>))
.toList(),
dateCalcul: DateTime.parse(json['dateCalcul'] as String),
periode: json['periode'] as String?,
annee: (json['annee'] as num?)?.toInt(),
mois: (json['mois'] as num?)?.toInt(),
);
Map<String, dynamic> _$CotisationStatisticsModelToJson(
CotisationStatisticsModel instance) =>
<String, dynamic>{
'totalCotisations': instance.totalCotisations,
'montantTotal': instance.montantTotal,
'montantPaye': instance.montantPaye,
'montantRestant': instance.montantRestant,
'cotisationsPayees': instance.cotisationsPayees,
'cotisationsEnAttente': instance.cotisationsEnAttente,
'cotisationsEnRetard': instance.cotisationsEnRetard,
'cotisationsAnnulees': instance.cotisationsAnnulees,
'tauxPaiement': instance.tauxPaiement,
'tauxRetard': instance.tauxRetard,
'montantMoyenCotisation': instance.montantMoyenCotisation,
'montantMoyenPaiement': instance.montantMoyenPaiement,
'repartitionParType': instance.repartitionParType,
'montantParType': instance.montantParType,
'repartitionParStatut': instance.repartitionParStatut,
'montantParStatut': instance.montantParStatut,
'evolutionMensuelle': instance.evolutionMensuelle,
'chiffreAffaireMensuel': instance.chiffreAffaireMensuel,
'tendances': instance.tendances,
'dateCalcul': instance.dateCalcul.toIso8601String(),
'periode': instance.periode,
'annee': instance.annee,
'mois': instance.mois,
};
CotisationTrendModel _$CotisationTrendModelFromJson(
Map<String, dynamic> json) =>
CotisationTrendModel(
periode: json['periode'] as String,
totalCotisations: (json['totalCotisations'] as num).toInt(),
montantTotal: (json['montantTotal'] as num).toDouble(),
montantPaye: (json['montantPaye'] as num).toDouble(),
tauxPaiement: (json['tauxPaiement'] as num).toDouble(),
date: DateTime.parse(json['date'] as String),
);
Map<String, dynamic> _$CotisationTrendModelToJson(
CotisationTrendModel instance) =>
<String, dynamic>{
'periode': instance.periode,
'totalCotisations': instance.totalCotisations,
'montantTotal': instance.montantTotal,
'montantPaye': instance.montantPaye,
'tauxPaiement': instance.tauxPaiement,
'date': instance.date.toIso8601String(),
};

View File

@@ -0,0 +1,279 @@
import 'package:json_annotation/json_annotation.dart';
part 'payment_model.g.dart';
/// Modèle de données pour les paiements
/// Représente une transaction de paiement de cotisation
@JsonSerializable()
class PaymentModel {
final String id;
final String cotisationId;
final String numeroReference;
final double montant;
final String codeDevise;
final String methodePaiement;
final String statut;
final DateTime dateTransaction;
final String? numeroTransaction;
final String? referencePaiement;
final String? description;
final Map<String, dynamic>? metadonnees;
final String? operateurMobileMoney;
final String? numeroTelephone;
final String? nomPayeur;
final String? emailPayeur;
final double? fraisTransaction;
final String? codeAutorisation;
final String? messageErreur;
final int? nombreTentatives;
final DateTime? dateEcheance;
final DateTime dateCreation;
final DateTime? dateModification;
const PaymentModel({
required this.id,
required this.cotisationId,
required this.numeroReference,
required this.montant,
required this.codeDevise,
required this.methodePaiement,
required this.statut,
required this.dateTransaction,
this.numeroTransaction,
this.referencePaiement,
this.description,
this.metadonnees,
this.operateurMobileMoney,
this.numeroTelephone,
this.nomPayeur,
this.emailPayeur,
this.fraisTransaction,
this.codeAutorisation,
this.messageErreur,
this.nombreTentatives,
this.dateEcheance,
required this.dateCreation,
this.dateModification,
});
/// Factory pour créer depuis JSON
factory PaymentModel.fromJson(Map<String, dynamic> json) =>
_$PaymentModelFromJson(json);
/// Convertit vers JSON
Map<String, dynamic> toJson() => _$PaymentModelToJson(this);
/// Vérifie si le paiement est réussi
bool get isSuccessful => statut == 'COMPLETED' || statut == 'SUCCESS';
/// Vérifie si le paiement est en cours
bool get isPending => statut == 'PENDING' || statut == 'PROCESSING';
/// Vérifie si le paiement a échoué
bool get isFailed => statut == 'FAILED' || statut == 'ERROR' || statut == 'CANCELLED';
/// Retourne la couleur associée au statut
String get couleurStatut {
switch (statut) {
case 'COMPLETED':
case 'SUCCESS':
return '#4CAF50'; // Vert
case 'PENDING':
case 'PROCESSING':
return '#FF9800'; // Orange
case 'FAILED':
case 'ERROR':
return '#F44336'; // Rouge
case 'CANCELLED':
return '#9E9E9E'; // Gris
default:
return '#757575'; // Gris foncé
}
}
/// Retourne le libellé du statut en français
String get libelleStatut {
switch (statut) {
case 'COMPLETED':
case 'SUCCESS':
return 'Réussi';
case 'PENDING':
return 'En attente';
case 'PROCESSING':
return 'En cours';
case 'FAILED':
return 'Échoué';
case 'ERROR':
return 'Erreur';
case 'CANCELLED':
return 'Annulé';
default:
return statut;
}
}
/// Retourne le libellé de la méthode de paiement
String get libelleMethodePaiement {
switch (methodePaiement) {
case 'MOBILE_MONEY':
return 'Mobile Money';
case 'ORANGE_MONEY':
return 'Orange Money';
case 'WAVE':
return 'Wave';
case 'MOOV_MONEY':
return 'Moov Money';
case 'CARTE_BANCAIRE':
return 'Carte bancaire';
case 'VIREMENT':
return 'Virement bancaire';
case 'ESPECES':
return 'Espèces';
case 'CHEQUE':
return 'Chèque';
default:
return methodePaiement;
}
}
/// Retourne l'icône associée à la méthode de paiement
String get iconeMethodePaiement {
switch (methodePaiement) {
case 'MOBILE_MONEY':
case 'ORANGE_MONEY':
case 'WAVE':
case 'MOOV_MONEY':
return '📱';
case 'CARTE_BANCAIRE':
return '💳';
case 'VIREMENT':
return '🏦';
case 'ESPECES':
return '💵';
case 'CHEQUE':
return '📝';
default:
return '💰';
}
}
/// Calcule le montant net (montant - frais)
double get montantNet {
return montant - (fraisTransaction ?? 0);
}
/// Vérifie si des frais sont appliqués
bool get hasFrais => fraisTransaction != null && fraisTransaction! > 0;
/// Retourne le pourcentage de frais
double get pourcentageFrais {
if (montant == 0 || fraisTransaction == null) return 0;
return (fraisTransaction! / montant * 100);
}
/// Vérifie si le paiement est expiré
bool get isExpired {
if (dateEcheance == null) return false;
return DateTime.now().isAfter(dateEcheance!) && !isSuccessful;
}
/// Retourne le temps restant avant expiration
Duration? get tempsRestant {
if (dateEcheance == null || isExpired) return null;
return dateEcheance!.difference(DateTime.now());
}
/// Retourne un message d'état détaillé
String get messageStatut {
switch (statut) {
case 'COMPLETED':
case 'SUCCESS':
return 'Paiement effectué avec succès';
case 'PENDING':
return 'Paiement en attente de confirmation';
case 'PROCESSING':
return 'Traitement du paiement en cours';
case 'FAILED':
return messageErreur ?? 'Le paiement a échoué';
case 'ERROR':
return messageErreur ?? 'Erreur lors du paiement';
case 'CANCELLED':
return 'Paiement annulé par l\'utilisateur';
default:
return 'Statut inconnu';
}
}
/// Vérifie si le paiement peut être retenté
bool get canRetry {
return isFailed && (nombreTentatives ?? 0) < 3 && !isExpired;
}
/// Copie avec modifications
PaymentModel copyWith({
String? id,
String? cotisationId,
String? numeroReference,
double? montant,
String? codeDevise,
String? methodePaiement,
String? statut,
DateTime? dateTransaction,
String? numeroTransaction,
String? referencePaiement,
String? description,
Map<String, dynamic>? metadonnees,
String? operateurMobileMoney,
String? numeroTelephone,
String? nomPayeur,
String? emailPayeur,
double? fraisTransaction,
String? codeAutorisation,
String? messageErreur,
int? nombreTentatives,
DateTime? dateEcheance,
DateTime? dateCreation,
DateTime? dateModification,
}) {
return PaymentModel(
id: id ?? this.id,
cotisationId: cotisationId ?? this.cotisationId,
numeroReference: numeroReference ?? this.numeroReference,
montant: montant ?? this.montant,
codeDevise: codeDevise ?? this.codeDevise,
methodePaiement: methodePaiement ?? this.methodePaiement,
statut: statut ?? this.statut,
dateTransaction: dateTransaction ?? this.dateTransaction,
numeroTransaction: numeroTransaction ?? this.numeroTransaction,
referencePaiement: referencePaiement ?? this.referencePaiement,
description: description ?? this.description,
metadonnees: metadonnees ?? this.metadonnees,
operateurMobileMoney: operateurMobileMoney ?? this.operateurMobileMoney,
numeroTelephone: numeroTelephone ?? this.numeroTelephone,
nomPayeur: nomPayeur ?? this.nomPayeur,
emailPayeur: emailPayeur ?? this.emailPayeur,
fraisTransaction: fraisTransaction ?? this.fraisTransaction,
codeAutorisation: codeAutorisation ?? this.codeAutorisation,
messageErreur: messageErreur ?? this.messageErreur,
nombreTentatives: nombreTentatives ?? this.nombreTentatives,
dateEcheance: dateEcheance ?? this.dateEcheance,
dateCreation: dateCreation ?? this.dateCreation,
dateModification: dateModification ?? this.dateModification,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is PaymentModel && other.id == id;
}
@override
int get hashCode => id.hashCode;
@override
String toString() {
return 'PaymentModel(id: $id, numeroReference: $numeroReference, '
'montant: $montant, methodePaiement: $methodePaiement, statut: $statut)';
}
}

View File

@@ -0,0 +1,64 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'payment_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
PaymentModel _$PaymentModelFromJson(Map<String, dynamic> json) => PaymentModel(
id: json['id'] as String,
cotisationId: json['cotisationId'] as String,
numeroReference: json['numeroReference'] as String,
montant: (json['montant'] as num).toDouble(),
codeDevise: json['codeDevise'] as String,
methodePaiement: json['methodePaiement'] as String,
statut: json['statut'] as String,
dateTransaction: DateTime.parse(json['dateTransaction'] as String),
numeroTransaction: json['numeroTransaction'] as String?,
referencePaiement: json['referencePaiement'] as String?,
description: json['description'] as String?,
metadonnees: json['metadonnees'] as Map<String, dynamic>?,
operateurMobileMoney: json['operateurMobileMoney'] as String?,
numeroTelephone: json['numeroTelephone'] as String?,
nomPayeur: json['nomPayeur'] as String?,
emailPayeur: json['emailPayeur'] as String?,
fraisTransaction: (json['fraisTransaction'] as num?)?.toDouble(),
codeAutorisation: json['codeAutorisation'] as String?,
messageErreur: json['messageErreur'] as String?,
nombreTentatives: (json['nombreTentatives'] as num?)?.toInt(),
dateEcheance: json['dateEcheance'] == null
? null
: DateTime.parse(json['dateEcheance'] as String),
dateCreation: DateTime.parse(json['dateCreation'] as String),
dateModification: json['dateModification'] == null
? null
: DateTime.parse(json['dateModification'] as String),
);
Map<String, dynamic> _$PaymentModelToJson(PaymentModel instance) =>
<String, dynamic>{
'id': instance.id,
'cotisationId': instance.cotisationId,
'numeroReference': instance.numeroReference,
'montant': instance.montant,
'codeDevise': instance.codeDevise,
'methodePaiement': instance.methodePaiement,
'statut': instance.statut,
'dateTransaction': instance.dateTransaction.toIso8601String(),
'numeroTransaction': instance.numeroTransaction,
'referencePaiement': instance.referencePaiement,
'description': instance.description,
'metadonnees': instance.metadonnees,
'operateurMobileMoney': instance.operateurMobileMoney,
'numeroTelephone': instance.numeroTelephone,
'nomPayeur': instance.nomPayeur,
'emailPayeur': instance.emailPayeur,
'fraisTransaction': instance.fraisTransaction,
'codeAutorisation': instance.codeAutorisation,
'messageErreur': instance.messageErreur,
'nombreTentatives': instance.nombreTentatives,
'dateEcheance': instance.dateEcheance?.toIso8601String(),
'dateCreation': instance.dateCreation.toIso8601String(),
'dateModification': instance.dateModification?.toIso8601String(),
};