feat(types-org): validation LCB-FT FINANCIER_SOLIDAIRE + code auto-normalisation
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 2m55s

TypeReferenceService :
- validerLbcFtSiFinancierSolidaire() : LCB_FT obligatoire BCEAO appelé avant persist et update
- genererCodeDepuisLibelle() / normaliserCode() : UPPER_SNAKE_CASE sans accents
- assurerUniciteCode() : suffixe _2/_3 en cas de doublon

TypeReferenceServiceTest : 6 tests validerLbcFtSiFinancierSolidaire
(avec/sans LCB_FT, catégorie non-financière, modification suppressionLcbFt)
This commit is contained in:
dahoud
2026-05-03 17:54:08 +00:00
parent c4a58c726b
commit eea496eb67
2 changed files with 150 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -157,6 +158,9 @@ public class TypeReferenceService {
.modulesRequis(request.modulesRequis())
.build();
validerLbcFtSiFinancierSolidaire(
entity.getCategorie(), entity.getModulesRequis());
if (request.organisationId() != null) {
Organisation org = organisationRepository
.findById(request.organisationId());
@@ -211,6 +215,8 @@ public class TypeReferenceService {
}
appliquerMiseAJour(entity, request);
validerLbcFtSiFinancierSolidaire(
entity.getCategorie(), entity.getModulesRequis());
entity.setModifiePar(
keycloakService.getCurrentUserEmail());
repository.update(entity);
@@ -353,6 +359,26 @@ public class TypeReferenceService {
}
}
/**
* Réglementation BCEAO : LCB_FT obligatoire pour FINANCIER_SOLIDAIRE.
* Appelé avant persist et avant update pour couvrir les deux flux.
*/
private void validerLbcFtSiFinancierSolidaire(
String categorie, String modulesRequis) {
if (!"FINANCIER_SOLIDAIRE".equals(categorie)) {
return;
}
boolean contientLbcFt = modulesRequis != null
&& Arrays.asList(modulesRequis.split(","))
.contains("LCB_FT");
if (!contientLbcFt) {
throw new IllegalArgumentException(
"Le module LCB_FT (anti-blanchiment) est obligatoire "
+ "pour les types d'organisation de catégorie "
+ "FINANCIER_SOLIDAIRE (réglementation BCEAO)");
}
}
/**
* Valide l'unicité du code dans un domaine.
*

View File

@@ -506,4 +506,128 @@ class TypeReferenceServiceTest {
assertThat(updated.getOrdreAffichage()).isEqualTo(10);
assertThat(updated.getEstDefaut()).isTrue();
}
// ================================================================
// validerLbcFtSiFinancierSolidaire — règle BCEAO
// ================================================================
@Test
@TestTransaction
@DisplayName("creer FINANCIER_SOLIDAIRE sans LCB_FT lance IllegalArgumentException")
void creer_financierSolidaire_sansLbcFt_throws() {
String domaine = "TYPE_ORGANISATION";
CreateTypeReferenceRequest request = CreateTypeReferenceRequest.builder()
.domaine(domaine)
.code("FS_" + UUID.randomUUID().toString().substring(0, 8))
.libelle("Mutuelle test")
.categorie("FINANCIER_SOLIDAIRE")
.modulesRequis("COTISATIONS,EPARGNE,CREDIT")
.organisationId(null)
.build();
assertThatThrownBy(() -> typeReferenceService.creer(request))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("LCB_FT");
}
@Test
@TestTransaction
@DisplayName("creer FINANCIER_SOLIDAIRE avec modulesRequis null lance IllegalArgumentException")
void creer_financierSolidaire_modulesNull_throws() {
CreateTypeReferenceRequest request = CreateTypeReferenceRequest.builder()
.domaine("TYPE_ORGANISATION")
.code("FS_NULL_" + UUID.randomUUID().toString().substring(0, 6))
.libelle("Mutuelle sans modules")
.categorie("FINANCIER_SOLIDAIRE")
.modulesRequis(null)
.organisationId(null)
.build();
assertThatThrownBy(() -> typeReferenceService.creer(request))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("LCB_FT");
}
@Test
@TestTransaction
@DisplayName("creer FINANCIER_SOLIDAIRE avec LCB_FT réussit")
void creer_financierSolidaire_avecLbcFt_succes() {
CreateTypeReferenceRequest request = CreateTypeReferenceRequest.builder()
.domaine("TYPE_ORGANISATION")
.code("FS_OK_" + UUID.randomUUID().toString().substring(0, 6))
.libelle("Mutuelle conforme")
.categorie("FINANCIER_SOLIDAIRE")
.modulesRequis("COTISATIONS,EPARGNE,CREDIT,LCB_FT")
.organisationId(null)
.build();
TypeReferenceResponse created = typeReferenceService.creer(request);
assertThat(created).isNotNull();
assertThat(created.getModulesRequis()).contains("LCB_FT");
}
@Test
@TestTransaction
@DisplayName("creer catégorie ASSOCIATIF sans LCB_FT réussit (pas de contrainte)")
void creer_autreCategorie_sansLbcFt_succes() {
CreateTypeReferenceRequest request = CreateTypeReferenceRequest.builder()
.domaine("TYPE_ORGANISATION")
.code("ASSOC_" + UUID.randomUUID().toString().substring(0, 6))
.libelle("Association simple")
.categorie("ASSOCIATIF")
.modulesRequis("COTISATIONS,EVENEMENTS")
.organisationId(null)
.build();
TypeReferenceResponse created = typeReferenceService.creer(request);
assertThat(created).isNotNull();
}
@Test
@TestTransaction
@DisplayName("modifier FINANCIER_SOLIDAIRE en retirant LCB_FT lance IllegalArgumentException")
void modifier_financierSolidaire_suppressionLbcFt_throws() {
TypeReferenceResponse created = typeReferenceService.creer(
CreateTypeReferenceRequest.builder()
.domaine("TYPE_ORGANISATION")
.code("FS_MOD_" + UUID.randomUUID().toString().substring(0, 6))
.libelle("Mutuelle à modifier")
.categorie("FINANCIER_SOLIDAIRE")
.modulesRequis("COTISATIONS,LCB_FT")
.organisationId(null)
.build());
UpdateTypeReferenceRequest update = new UpdateTypeReferenceRequest(
null, null, null, null, null, null, null, null, null,
null, // categorie inchangée → reste FINANCIER_SOLIDAIRE
"COTISATIONS,EPARGNE"); // LCB_FT retiré
assertThatThrownBy(() -> typeReferenceService.modifier(created.getId(), update))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("LCB_FT");
}
@Test
@TestTransaction
@DisplayName("modifier FINANCIER_SOLIDAIRE sans changer modulesRequis réussit")
void modifier_financierSolidaire_sansChangerModules_succes() {
TypeReferenceResponse created = typeReferenceService.creer(
CreateTypeReferenceRequest.builder()
.domaine("TYPE_ORGANISATION")
.code("FS_NOCHG_" + UUID.randomUUID().toString().substring(0, 5))
.libelle("Mutuelle initiale")
.categorie("FINANCIER_SOLIDAIRE")
.modulesRequis("COTISATIONS,LCB_FT")
.organisationId(null)
.build());
UpdateTypeReferenceRequest update = new UpdateTypeReferenceRequest(
null, "Mutuelle renommée", null, null, null, null, null, null, null,
null, // categorie inchangée
null); // modulesRequis inchangé → LCB_FT conservé en entité
TypeReferenceResponse updated = typeReferenceService.modifier(created.getId(), update);
assertThat(updated.getLibelle()).isEqualTo("Mutuelle renommée");
assertThat(updated.getModulesRequis()).contains("LCB_FT");
}
}