package dev.lions.unionflow.server.service; import dev.lions.unionflow.server.entity.Organisation; import dev.lions.unionflow.server.repository.OrganisationRepository; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.LinkedHashSet; import java.util.Optional; import java.util.Set; import java.util.UUID; import org.jboss.logging.Logger; /** * Service de gestion des modules actifs par organisation. * *

Architecture Option C — les modules actifs sont déterminés par le TYPE * d'organisation, pas par le plan tarifaire. Le plan impacte uniquement la * profondeur fonctionnelle (reporting, API, fédération). * *

Les modules sont regroupés en deux catégories : *

*/ @ApplicationScoped public class OrganisationModuleService { private static final Logger LOG = Logger.getLogger(OrganisationModuleService.class); /** Modules présents sur toutes les organisations quelle que soit leur nature. */ public static final Set MODULES_COMMUNS = Set.of( "MEMBRES", "COTISATIONS", "EVENEMENTS", "COMMUNICATION", "DOCUMENTS", "NOTIFICATION", "AIDE" ); @Inject OrganisationRepository organisationRepository; /** * Retourne l'ensemble des modules actifs pour une organisation donnée. * Combine les modules communs avec les modules métier du type d'org. */ public Set getModulesActifs(UUID organisationId) { Optional opt = organisationRepository.findByIdOptional(organisationId); if (opt.isEmpty()) { LOG.warnf("Organisation introuvable : %s", organisationId); return MODULES_COMMUNS; } return getModulesActifs(opt.get()); } /** * Retourne l'ensemble des modules actifs pour une organisation. */ public Set getModulesActifs(Organisation organisation) { Set modules = new LinkedHashSet<>(MODULES_COMMUNS); // 1. Modules issus du champ modulesActifs persisté (calculé depuis types_reference en V18) String modulesActifsCsv = organisation.getModulesActifs(); if (modulesActifsCsv != null && !modulesActifsCsv.isBlank()) { Arrays.stream(modulesActifsCsv.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) .forEach(modules::add); return modules; } // 2. Fallback : déduction depuis le typeOrganisation si modulesActifs non renseigné modules.addAll(getModulesParType(organisation.getTypeOrganisation())); return modules; } /** * Vérifie si un module spécifique est actif pour une organisation. */ public boolean isModuleActif(UUID organisationId, String module) { return getModulesActifs(organisationId).contains(module.toUpperCase()); } /** * Retourne les modules métier associés à un type d'organisation. * Utilisé en fallback si la colonne modules_actifs n'est pas peuplée. */ public Set getModulesParType(String typeOrganisation) { if (typeOrganisation == null) { return Collections.emptySet(); } Set modules = new LinkedHashSet<>(); switch (typeOrganisation.toUpperCase()) { case "TONTINE" -> { modules.add("TONTINE"); modules.add("FINANCE"); } case "MUTUELLE", "MUTUELLE_EPARGNE", "MUTUELLE_CREDIT" -> { modules.add("EPARGNE"); modules.add("CREDIT"); modules.add("FINANCE"); modules.add("LCB_FT"); } case "COOPERATIVE" -> { modules.add("AGRICULTURE"); modules.add("FINANCE"); } case "ONG", "FONDATION" -> { modules.add("PROJETS_ONG"); modules.add("COLLECTE_FONDS"); modules.add("FINANCE"); } case "EGLISE", "GROUPE_PRIERE" -> { modules.add("CULTE_DONS"); } case "SYNDICAT", "ORDRE_PROFESSIONNEL", "FEDERATION" -> { modules.add("VOTES"); modules.add("REGISTRE_AGREMENT"); } case "GIE" -> { modules.add("FINANCE"); } case "ASSOCIATION", "CLUB_SERVICE" -> { modules.add("TONTINE"); modules.add("VOTES"); } case "CLUB_SPORTIF", "CLUB_CULTUREL" -> { modules.add("VOTES"); } default -> LOG.debugf("Type d''organisation non reconnu pour module mapping : %s", typeOrganisation); } return modules; } /** * Retourne la liste des modules actifs sous forme de tableau JSON-friendly. * Utilisé par l'endpoint /api/organisations/{id}/modules-actifs */ public ModulesActifsResponse getModulesActifsResponse(UUID organisationId) { Optional opt = organisationRepository.findByIdOptional(organisationId); if (opt.isEmpty()) { return new ModulesActifsResponse(organisationId, Collections.emptySet(), "UNKNOWN"); } Organisation org = opt.get(); Set modules = getModulesActifs(org); return new ModulesActifsResponse(organisationId, modules, org.getTypeOrganisation()); } /** DTO de réponse pour l'endpoint modules-actifs. */ public record ModulesActifsResponse(UUID organisationId, Set modules, String typeOrganisation) {} }