diff --git a/pom.xml b/pom.xml
index 3cbd4bf..f437f7f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
Sous-ensemble strictement filtré de la conformité : indicateurs agrégés uniquement, + * jamais de données sensibles ({@code complianceOfficerId}, payloads détaillés, IDs membres, + * informations PEP). L'autorité voit le score, l'état des obligations, les pourcentages + * de couverture — pas l'intimité opérationnelle. + * + *
Diffusé via URL signée temporairement (HMAC-SHA256, expiry stricte). + * + * @since 2026-04-25 (Sprint 17 — transparency réglementaire publique) + */ +@Builder +public record KpiPublicSnapshot( + /** Nom de l'organisation. */ + String organisationNom, + + /** Référentiel comptable applicable (SYSCOHADA / SYCEBNL / PCSFD_UMOA). */ + String referentielComptable, + + /** Score conformité agrégé 0-100. */ + int scoreGlobal, + + /** Compliance Officer désigné (présence uniquement, pas l'identité). */ + boolean complianceOfficerDesigne, + + /** Statut AG annuelle : OK / EN_ATTENTE / RETARD. */ + String agAnnuelleStatut, + + /** Statut rapport AIRMS : OK / EN_ATTENTE. */ + String rapportAirmsStatut, + + /** Nombre de dirigeants enrôlés CMU (sans liste). */ + int dirigeantsAvecCmu, + + /** Pourcentage KYC à jour (membres). */ + BigDecimal tauxKycAJourPct, + + /** Pourcentage formation LBC/FT (membres formés dans les 12 derniers mois). */ + BigDecimal tauxFormationLbcFtPct, + + /** Pourcentage couverture UBO (Bénéficiaires Effectifs documentés). */ + BigDecimal couvertureUboPct, + + /** Statut commissaire aux comptes (OPTIONNEL / OBLIGATOIRE). */ + String commissaireAuxComptesStatut, + + /** Date de génération du snapshot. */ + LocalDateTime dateGeneration +) { +} diff --git a/src/test/java/dev/lions/unionflow/server/api/dto/compliance/response/KpiPublicSnapshotTest.java b/src/test/java/dev/lions/unionflow/server/api/dto/compliance/response/KpiPublicSnapshotTest.java new file mode 100644 index 0000000..d694845 --- /dev/null +++ b/src/test/java/dev/lions/unionflow/server/api/dto/compliance/response/KpiPublicSnapshotTest.java @@ -0,0 +1,64 @@ +package dev.lions.unionflow.server.api.dto.compliance.response; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import org.junit.jupiter.api.Test; + +class KpiPublicSnapshotTest { + + @Test + void testBuilder_AllFields() { + LocalDateTime now = LocalDateTime.of(2026, 4, 25, 12, 0); + KpiPublicSnapshot s = KpiPublicSnapshot.builder() + .organisationNom("Mutuelle Test") + .referentielComptable("SYCEBNL") + .scoreGlobal(82) + .complianceOfficerDesigne(true) + .agAnnuelleStatut("OK") + .rapportAirmsStatut("EN_ATTENTE") + .dirigeantsAvecCmu(3) + .tauxKycAJourPct(new BigDecimal("85.5")) + .tauxFormationLbcFtPct(new BigDecimal("70.0")) + .couvertureUboPct(new BigDecimal("60.0")) + .commissaireAuxComptesStatut("OPTIONNEL") + .dateGeneration(now) + .build(); + + assertThat(s.organisationNom()).isEqualTo("Mutuelle Test"); + assertThat(s.referentielComptable()).isEqualTo("SYCEBNL"); + assertThat(s.scoreGlobal()).isEqualTo(82); + assertThat(s.complianceOfficerDesigne()).isTrue(); + assertThat(s.agAnnuelleStatut()).isEqualTo("OK"); + assertThat(s.rapportAirmsStatut()).isEqualTo("EN_ATTENTE"); + assertThat(s.dirigeantsAvecCmu()).isEqualTo(3); + assertThat(s.tauxKycAJourPct()).isEqualByComparingTo("85.5"); + assertThat(s.tauxFormationLbcFtPct()).isEqualByComparingTo("70.0"); + assertThat(s.couvertureUboPct()).isEqualByComparingTo("60.0"); + assertThat(s.commissaireAuxComptesStatut()).isEqualTo("OPTIONNEL"); + assertThat(s.dateGeneration()).isEqualTo(now); + } + + @Test + void testBuilder_NullDefaults() { + KpiPublicSnapshot empty = KpiPublicSnapshot.builder().build(); + assertThat(empty.organisationNom()).isNull(); + assertThat(empty.scoreGlobal()).isZero(); + assertThat(empty.complianceOfficerDesigne()).isFalse(); + assertThat(empty.dirigeantsAvecCmu()).isZero(); + assertThat(empty.tauxKycAJourPct()).isNull(); + } + + @Test + void testEqualsHashCodeToString() { + LocalDateTime t = LocalDateTime.of(2026, 4, 25, 10, 0); + KpiPublicSnapshot a = KpiPublicSnapshot.builder() + .organisationNom("Org").scoreGlobal(80).dateGeneration(t).build(); + KpiPublicSnapshot b = KpiPublicSnapshot.builder() + .organisationNom("Org").scoreGlobal(80).dateGeneration(t).build(); + assertThat(a).isEqualTo(b); + assertThat(a.hashCode()).isEqualTo(b.hashCode()); + assertThat(a.toString()).contains("KpiPublicSnapshot"); + } +}