feat(sprint-17 api 2026-04-25): bump 1.0.10 + DTO KpiPublicSnapshot pour partage KPI public signé

Sous-ensemble strictement filtré de ComplianceSnapshot — vue read-only pour autorités
externes (BCEAO, ARTCI, CENTIF). N'expose que les indicateurs agrégés, jamais
d'informations sensibles (complianceOfficerId, payloads, IDs membres, PEP details).

Champs : organisationNom, referentielComptable, scoreGlobal, complianceOfficerDesigne,
agAnnuelleStatut, rapportAirmsStatut, dirigeantsAvecCmu, tauxKycAJourPct,
tauxFormationLbcFtPct, couvertureUboPct, commissaireAuxComptesStatut, dateGeneration.

Tests Jacoco 100% (3 tests : builder all-fields, null defaults, equals/hash/toString)

ACTION USER : `mvn clean install` puis `mvn deploy` pour 1.0.10 sur Gitea.
This commit is contained in:
dahoud
2026-04-25 16:47:54 +00:00
parent 7a596f68bd
commit db36727001
3 changed files with 122 additions and 1 deletions

View File

@@ -6,7 +6,7 @@
<groupId>dev.lions.unionflow</groupId> <groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-server-api</artifactId> <artifactId>unionflow-server-api</artifactId>
<version>1.0.9</version> <version>1.0.10</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>UnionFlow Server API</name> <name>UnionFlow Server API</name>

View File

@@ -0,0 +1,57 @@
package dev.lions.unionflow.server.api.dto.compliance.response;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Builder;
/**
* Snapshot KPI public (Sprint 17) — vue read-only pour autorités externes (BCEAO, ARTCI, CENTIF).
*
* <p>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.
*
* <p>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
) {
}

View File

@@ -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");
}
}