feat(sprint-9 mobile 2026-04-25): feature compliance dashboard + Devise enum + selector + tests

Apporte aux compliance officers et controleurs internes l'accès mobile au tableau de bord
de conformité backend (P1-NEW-7). Et prépare la diaspora avec Devise enum + sélecteur
préférence persisté.

Feature Compliance (Clean Architecture)
- Domain : ComplianceSnapshot + ConformiteIndicateur (Equatable), helpers scoreSeverite + hasAlertesCritiques
- Data : ComplianceSnapshotModel.fromJson (parsing tolerant aux nullables), ComplianceRemoteDataSourceImpl Dio (GET /api/compliance/dashboard), ComplianceRepositoryImpl @Injectable
- Presentation : ComplianceBloc (Load/Refresh events, Initial/Loading/Loaded/Error states), ConformiteDashboardPage (Material 3, ScoreCard 0-100 colorée, 9 IndicateurTile, AlertesCard rouge si critiques)

Feature Devise
- Devise enum (10 valeurs miroirs backend, code/libelle/zone)
- fromCode tolérant casse + null/vide → XOF
- estInternationale pour AML
- DeviseSelector widget DropdownButtonFormField + readPreferred/writePreferred via FlutterSecureStorage (clé unionflow.devise.preferee)

Tests (17/17 verts)
- ComplianceSnapshot : 7 tests (scoreSeverite × 3, hasAlertesCritiques × 4)
- ComplianceSnapshotModel.fromJson : 4 tests (complet, fallbacks, string→double, indicateur invalide)
- Devise enum : 6 tests (reference, fromCode parse, fromCode null/inconnu, estInternationale × 2, intégrité valeurs)

Note : feature reporting trimestriel mobile (PDF viewer + bloc liste) reportée à un sprint
dédié — nécessite intégration pdf viewer + cache local non triviale.
This commit is contained in:
dahoud
2026-04-25 11:11:03 +00:00
parent 8356ccc0b0
commit 8c1a254e80
12 changed files with 808 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
import 'package:equatable/equatable.dart';
/// Indicateur de conformité avec statut + message explicatif.
class ConformiteIndicateur extends Equatable {
final String statut; // OK, EN_ATTENTE, RETARD, OPTIONNEL, OBLIGATOIRE, EN_VEILLE
final String message;
const ConformiteIndicateur({required this.statut, required this.message});
@override
List<Object?> get props => [statut, message];
}
/// Snapshot du tableau de bord de conformité (miroir mobile du record backend).
///
/// Couvre AG annuelle, rapports AIRMS, CMU dirigeants, KYC, formation LBC/FT, UBO,
/// commissaire aux comptes, FOMUS-CI, et le score global agrégé (0-100).
class ComplianceSnapshot extends Equatable {
final String organisationId;
final String organisationNom;
final String referentielComptable;
final bool complianceOfficerDesigne;
final ConformiteIndicateur agAnnuelle;
final ConformiteIndicateur rapportAirms;
final int dirigeantsAvecCmu;
final double tauxKycAJourPct;
final double tauxFormationLbcFtPct;
final ConformiteIndicateur commissaireAuxComptes;
final ConformiteIndicateur fomusCi;
final double couvertureUboPct;
final int scoreGlobal;
const ComplianceSnapshot({
required this.organisationId,
required this.organisationNom,
required this.referentielComptable,
required this.complianceOfficerDesigne,
required this.agAnnuelle,
required this.rapportAirms,
required this.dirigeantsAvecCmu,
required this.tauxKycAJourPct,
required this.tauxFormationLbcFtPct,
required this.commissaireAuxComptes,
required this.fomusCi,
required this.couvertureUboPct,
required this.scoreGlobal,
});
/// Couleur indicative selon le score (Material 3 semantic).
String get scoreSeverite {
if (scoreGlobal >= 80) return 'success';
if (scoreGlobal >= 60) return 'warning';
return 'danger';
}
bool get hasAlertesCritiques =>
!complianceOfficerDesigne ||
agAnnuelle.statut == 'RETARD' ||
scoreGlobal < 60;
@override
List<Object?> get props => [
organisationId,
organisationNom,
referentielComptable,
complianceOfficerDesigne,
agAnnuelle,
rapportAirms,
dirigeantsAvecCmu,
tauxKycAJourPct,
tauxFormationLbcFtPct,
commissaireAuxComptes,
fomusCi,
couvertureUboPct,
scoreGlobal,
];
}