- Nouveau MembreRoleSyncService.ensureOrgAdminRole : auto-crée un MembreRole ORGADMIN quand un user avec rôle Keycloak ADMIN_ORGANISATION se connecte sans entrée DB (couvre les comptes créés directement dans Keycloak). - OrganisationContextFilter appelle syncService.ensureOrgAdminRole quand le rôle Keycloak est présent mais MembreRole absent (non bloquant sur erreur). - MembreRoleRepository.countAdminsByOrganisationId : count strict (ORGADMIN + actif + dateDebut/dateFin valides) avec fallback sur codes alternatifs si strict=0. - OrganisationService.convertToResponse : nombreAdministrateurs dynamique via MembreRoleRepository (remplace le champ Organisation jamais mis à jour).
778 lines
32 KiB
Java
778 lines
32 KiB
Java
package dev.lions.unionflow.server.service;
|
|
|
|
import dev.lions.unionflow.server.api.dto.organisation.request.CreateOrganisationRequest;
|
|
import dev.lions.unionflow.server.api.dto.organisation.request.UpdateOrganisationRequest;
|
|
import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationResponse;
|
|
import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationSummaryResponse;
|
|
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
|
|
import dev.lions.unionflow.server.entity.Membre;
|
|
import dev.lions.unionflow.server.entity.MembreOrganisation;
|
|
import dev.lions.unionflow.server.repository.AdresseRepository;
|
|
import dev.lions.unionflow.server.repository.EvenementRepository;
|
|
import dev.lions.unionflow.server.repository.MembreOrganisationRepository;
|
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
|
import dev.lions.unionflow.server.repository.MembreRoleRepository;
|
|
import dev.lions.unionflow.server.repository.TypeReferenceRepository;
|
|
import dev.lions.unionflow.server.entity.Organisation;
|
|
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
|
import io.quarkus.panache.common.Page;
|
|
import io.quarkus.panache.common.Sort;
|
|
import jakarta.enterprise.context.ApplicationScoped;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.transaction.Transactional;
|
|
import jakarta.ws.rs.NotFoundException;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDateTime;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.UUID;
|
|
import java.util.stream.Collectors;
|
|
import org.jboss.logging.Logger;
|
|
|
|
/**
|
|
* Service métier pour la gestion des organisations
|
|
*
|
|
* @author UnionFlow Team
|
|
* @version 1.0
|
|
* @since 2025-01-15
|
|
*/
|
|
@ApplicationScoped
|
|
public class OrganisationService {
|
|
|
|
private static final Logger LOG = Logger.getLogger(OrganisationService.class);
|
|
|
|
@Inject
|
|
OrganisationRepository organisationRepository;
|
|
|
|
@Inject
|
|
MembreRepository membreRepository;
|
|
|
|
@Inject
|
|
DefaultsService defaultsService;
|
|
|
|
@Inject
|
|
TypeReferenceRepository typeReferenceRepository;
|
|
|
|
@Inject
|
|
MembreOrganisationRepository membreOrganisationRepository;
|
|
|
|
@Inject
|
|
AdresseRepository adresseRepository;
|
|
|
|
@Inject
|
|
EvenementRepository evenementRepository;
|
|
|
|
@Inject
|
|
MembreRoleRepository membreRoleRepository;
|
|
|
|
/**
|
|
* Crée une nouvelle organisation
|
|
*
|
|
* @param organisation l'organisation à créer
|
|
* @param utilisateur identifiant de l'utilisateur effectuant la création
|
|
* (email ou "system")
|
|
* @return l'organisation créée
|
|
*/
|
|
@Transactional
|
|
public Organisation creerOrganisation(Organisation organisation, String utilisateur) {
|
|
LOG.infof("Création d'une nouvelle organisation: %s", organisation.getNom());
|
|
|
|
// Vérifier l'unicité de l'email
|
|
if (organisationRepository.findByEmail(organisation.getEmail()).isPresent()) {
|
|
throw new IllegalStateException("Une organisation avec cet email existe déjà");
|
|
}
|
|
|
|
// Vérifier l'unicité du nom
|
|
if (organisationRepository.findByNom(organisation.getNom()).isPresent()) {
|
|
throw new IllegalArgumentException("Une organisation avec ce nom existe déjà");
|
|
}
|
|
|
|
// Vérifier l'unicité du numéro d'enregistrement si fourni
|
|
if (organisation.getNumeroEnregistrement() != null
|
|
&& !organisation.getNumeroEnregistrement().isEmpty()) {
|
|
if (organisationRepository
|
|
.findByNumeroEnregistrement(organisation.getNumeroEnregistrement())
|
|
.isPresent()) {
|
|
throw new IllegalArgumentException(
|
|
"Une organisation avec ce numéro d'enregistrement existe déjà");
|
|
}
|
|
}
|
|
|
|
// Définir les valeurs par défaut
|
|
if (organisation.getStatut() == null) {
|
|
organisation.setStatut("ACTIVE");
|
|
}
|
|
if (organisation.getTypeOrganisation() == null) {
|
|
organisation.setTypeOrganisation("ASSOCIATION");
|
|
}
|
|
|
|
// Initialiser modulesActifs et categorieType depuis types_reference
|
|
// Cela permet aux types créés via CRUD (ex: "TANTANPION") d'hériter
|
|
// automatiquement de leurs modules_requis sans modifier le code Java.
|
|
if (organisation.getModulesActifs() == null || organisation.getModulesActifs().isBlank()) {
|
|
typeReferenceRepository
|
|
.findByDomaineAndCode("TYPE_ORGANISATION", organisation.getTypeOrganisation())
|
|
.ifPresentOrElse(
|
|
tr -> {
|
|
if (tr.getModulesRequis() != null && !tr.getModulesRequis().isBlank()) {
|
|
organisation.setModulesActifs(tr.getModulesRequis());
|
|
LOG.infof("Modules initialisés depuis types_reference pour le type '%s': %s",
|
|
organisation.getTypeOrganisation(), tr.getModulesRequis());
|
|
}
|
|
if (tr.getCategorie() != null && organisation.getCategorieType() == null) {
|
|
organisation.setCategorieType(tr.getCategorie());
|
|
}
|
|
},
|
|
() -> LOG.warnf(
|
|
"Type d'organisation '%s' absent de types_reference — modules non initialisés. " +
|
|
"Ajoutez ce type via l'administration pour activer les modules métier.",
|
|
organisation.getTypeOrganisation()));
|
|
}
|
|
|
|
// Audit : créé par / modifié par (BaseEntity n'initialise pas creePar dans
|
|
// @PrePersist)
|
|
String auditUser = utilisateur != null && !utilisateur.isBlank() ? utilisateur : "system";
|
|
organisation.setCreePar(auditUser);
|
|
organisation.setModifiePar(auditUser);
|
|
if (organisation.getDateCreation() == null) {
|
|
organisation.setDateCreation(LocalDateTime.now());
|
|
}
|
|
organisation.setDateModification(organisation.getDateCreation());
|
|
|
|
organisationRepository.persist(organisation);
|
|
LOG.infof(
|
|
"Organisation créée avec succès: ID=%s, Nom=%s", organisation.getId(), organisation.getNom());
|
|
|
|
return organisation;
|
|
}
|
|
|
|
/**
|
|
* Met à jour une organisation existante
|
|
*
|
|
* @param id l'ID de l'organisation
|
|
* @param organisationMiseAJour les données de mise à jour
|
|
* @param utilisateur l'utilisateur effectuant la modification
|
|
* @return l'organisation mise à jour
|
|
*/
|
|
@Transactional
|
|
public Organisation mettreAJourOrganisation(
|
|
UUID id, Organisation organisationMiseAJour, String utilisateur) {
|
|
LOG.infof("Mise à jour de l'organisation ID: %s", id);
|
|
|
|
Organisation organisation = organisationRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(() -> new NotFoundException("Organisation non trouvée avec l'ID: " + id));
|
|
|
|
// Vérifier l'unicité de l'email si modifié
|
|
if (!organisation.getEmail().equals(organisationMiseAJour.getEmail())) {
|
|
if (organisationRepository.findByEmail(organisationMiseAJour.getEmail()).isPresent()) {
|
|
throw new IllegalStateException("Une organisation avec cet email existe déjà");
|
|
}
|
|
organisation.setEmail(organisationMiseAJour.getEmail());
|
|
}
|
|
|
|
// Vérifier l'unicité du nom si modifié
|
|
if (!organisation.getNom().equals(organisationMiseAJour.getNom())) {
|
|
if (organisationRepository.findByNom(organisationMiseAJour.getNom()).isPresent()) {
|
|
throw new IllegalArgumentException("Une organisation avec ce nom existe déjà");
|
|
}
|
|
organisation.setNom(organisationMiseAJour.getNom());
|
|
}
|
|
|
|
// Mettre à jour tous les champs métier (alignés sur detail.xhtml et
|
|
// organisation-form)
|
|
organisation.setNomCourt(organisationMiseAJour.getNomCourt());
|
|
organisation.setDescription(organisationMiseAJour.getDescription());
|
|
organisation.setDateFondation(organisationMiseAJour.getDateFondation());
|
|
organisation.setNumeroEnregistrement(organisationMiseAJour.getNumeroEnregistrement());
|
|
organisation.setTelephone(organisationMiseAJour.getTelephone());
|
|
organisation.setTelephoneSecondaire(organisationMiseAJour.getTelephoneSecondaire());
|
|
organisation.setEmailSecondaire(organisationMiseAJour.getEmailSecondaire());
|
|
organisation.setAdresse(organisationMiseAJour.getAdresse());
|
|
organisation.setVille(organisationMiseAJour.getVille());
|
|
organisation.setRegion(organisationMiseAJour.getRegion());
|
|
organisation.setPays(organisationMiseAJour.getPays());
|
|
organisation.setCodePostal(organisationMiseAJour.getCodePostal());
|
|
organisation.setLatitude(organisationMiseAJour.getLatitude());
|
|
organisation.setLongitude(organisationMiseAJour.getLongitude());
|
|
organisation.setSiteWeb(organisationMiseAJour.getSiteWeb());
|
|
organisation.setLogo(organisationMiseAJour.getLogo());
|
|
organisation.setReseauxSociaux(organisationMiseAJour.getReseauxSociaux());
|
|
organisation.setObjectifs(organisationMiseAJour.getObjectifs());
|
|
organisation.setActivitesPrincipales(organisationMiseAJour.getActivitesPrincipales());
|
|
organisation.setCertifications(organisationMiseAJour.getCertifications());
|
|
organisation.setPartenaires(organisationMiseAJour.getPartenaires());
|
|
organisation.setNotes(organisationMiseAJour.getNotes());
|
|
if (organisationMiseAJour.getStatut() != null) {
|
|
organisation.setStatut(organisationMiseAJour.getStatut());
|
|
}
|
|
organisation.setTypeOrganisation(organisationMiseAJour.getTypeOrganisation());
|
|
organisation.setNiveauHierarchique(
|
|
organisationMiseAJour.getNiveauHierarchique() != null ? organisationMiseAJour.getNiveauHierarchique() : 0);
|
|
organisation.setNombreMembres(
|
|
organisationMiseAJour.getNombreMembres() != null ? organisationMiseAJour.getNombreMembres() : 0);
|
|
organisation.setNombreAdministrateurs(
|
|
organisationMiseAJour.getNombreAdministrateurs() != null ? organisationMiseAJour.getNombreAdministrateurs()
|
|
: 0);
|
|
// Budget & Finances
|
|
organisation.setBudgetAnnuel(organisationMiseAJour.getBudgetAnnuel());
|
|
organisation.setDevise(
|
|
organisationMiseAJour.getDevise() != null ? organisationMiseAJour.getDevise() : defaultsService.getDevise());
|
|
organisation.setCotisationObligatoire(
|
|
organisationMiseAJour.getCotisationObligatoire() != null ? organisationMiseAJour.getCotisationObligatoire()
|
|
: false);
|
|
organisation.setMontantCotisationAnnuelle(organisationMiseAJour.getMontantCotisationAnnuelle());
|
|
organisation.setOrganisationPublique(
|
|
organisationMiseAJour.getOrganisationPublique() != null ? organisationMiseAJour.getOrganisationPublique()
|
|
: true);
|
|
organisation.setAccepteNouveauxMembres(
|
|
organisationMiseAJour.getAccepteNouveauxMembres() != null ? organisationMiseAJour.getAccepteNouveauxMembres()
|
|
: true);
|
|
// Hiérarchie
|
|
organisation.setOrganisationParente(organisationMiseAJour.getOrganisationParente());
|
|
|
|
organisation.marquerCommeModifie(utilisateur);
|
|
|
|
LOG.infof("Organisation mise à jour avec succès: ID=%s", id);
|
|
return organisation;
|
|
}
|
|
|
|
/**
|
|
* Supprime une organisation
|
|
*
|
|
* @param id l'UUID de l'organisation
|
|
* @param utilisateur l'utilisateur effectuant la suppression
|
|
*/
|
|
@Transactional
|
|
public void supprimerOrganisation(UUID id, String utilisateur) {
|
|
LOG.infof("Suppression de l'organisation ID: %s", id);
|
|
|
|
Organisation organisation = organisationRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(() -> new NotFoundException("Organisation non trouvée avec l'ID: " + id));
|
|
|
|
// Vérifier qu'il n'y a pas de membres actifs
|
|
if (organisation.getNombreMembres() > 0) {
|
|
throw new IllegalStateException(
|
|
"Impossible de supprimer une organisation avec des membres actifs");
|
|
}
|
|
|
|
// Soft delete - marquer comme inactive
|
|
organisation.setActif(false);
|
|
organisation.setStatut("DISSOUTE");
|
|
organisation.marquerCommeModifie(utilisateur);
|
|
|
|
LOG.infof("Organisation supprimée (soft delete) avec succès: ID=%s", id);
|
|
}
|
|
|
|
/**
|
|
* Trouve une organisation par son ID
|
|
*
|
|
* @param id l'UUID de l'organisation
|
|
* @return Optional contenant l'organisation si trouvée
|
|
*/
|
|
public Optional<Organisation> trouverParId(UUID id) {
|
|
return organisationRepository.findByIdOptional(id);
|
|
}
|
|
|
|
/**
|
|
* Trouve une organisation par son email
|
|
*
|
|
* @param email l'email de l'organisation
|
|
* @return Optional contenant l'organisation si trouvée
|
|
*/
|
|
public Optional<Organisation> trouverParEmail(String email) {
|
|
return organisationRepository.findByEmail(email);
|
|
}
|
|
|
|
/**
|
|
* Liste les organisations auxquelles l'utilisateur connecté (membre) appartient.
|
|
* Utilisé pour un administrateur d'organisation qui ne doit voir que son/ses organisation(s).
|
|
*
|
|
* @param emailUtilisateur email du principal (SecurityIdentity)
|
|
* @return liste des organisations du membre, ou liste vide si membre non trouvé
|
|
*/
|
|
@Transactional
|
|
public List<Organisation> listerOrganisationsPourUtilisateur(String emailUtilisateur) {
|
|
if (emailUtilisateur == null || emailUtilisateur.isBlank()) {
|
|
return List.of();
|
|
}
|
|
return membreRepository.findByEmail(emailUtilisateur)
|
|
.map(m -> m.getMembresOrganisations().stream()
|
|
.map(mo -> mo.getOrganisation())
|
|
.distinct()
|
|
.collect(Collectors.toList()))
|
|
.orElse(List.of());
|
|
}
|
|
|
|
/**
|
|
* Associe un utilisateur (par email) à une organisation.
|
|
* Réservé au SUPER_ADMIN. Crée un Membre minimal si aucun n'existe pour cet email,
|
|
* puis crée le lien MembreOrganisation (idempotent si déjà associé).
|
|
*
|
|
* @param email email de l'utilisateur (doit correspondre à un compte Keycloak / Membre)
|
|
* @param organisationId UUID de l'organisation
|
|
* @throws NotFoundException si l'organisation n'existe pas
|
|
*/
|
|
@Transactional
|
|
public void associerUtilisateurAOrganisation(String email, UUID organisationId) {
|
|
if (email == null || email.isBlank()) {
|
|
throw new IllegalArgumentException("L'email est obligatoire");
|
|
}
|
|
if (organisationId == null) {
|
|
throw new IllegalArgumentException("L'organisation est obligatoire");
|
|
}
|
|
Organisation organisation = organisationRepository.findByIdOptional(organisationId)
|
|
.orElseThrow(() -> new NotFoundException("Organisation non trouvée: " + organisationId));
|
|
String emailNorm = email.trim().toLowerCase();
|
|
Membre membre = membreRepository.findByEmail(emailNorm).orElseGet(() -> {
|
|
Membre nouveau = creerMembreMinimalPourEmail(emailNorm);
|
|
membreRepository.persist(nouveau);
|
|
LOG.infof("Membre minimal créé pour associer l'utilisateur %s à l'organisation %s", emailNorm, organisation.getNom());
|
|
return nouveau;
|
|
});
|
|
if (membreOrganisationRepository.findByMembreIdAndOrganisationId(membre.getId(), organisationId).isPresent()) {
|
|
LOG.infof("L'utilisateur %s est déjà associé à l'organisation %s", emailNorm, organisation.getNom());
|
|
return;
|
|
}
|
|
MembreOrganisation mo = MembreOrganisation.builder()
|
|
.membre(membre)
|
|
.organisation(organisation)
|
|
.statutMembre(StatutMembre.ACTIF)
|
|
.dateAdhesion(LocalDate.now())
|
|
.build();
|
|
membreOrganisationRepository.persist(mo);
|
|
organisation.ajouterMembre();
|
|
organisationRepository.persist(organisation);
|
|
LOG.infof("Utilisateur %s associé à l'organisation %s (MembreOrganisation créé)", emailNorm, organisation.getNom());
|
|
}
|
|
|
|
/**
|
|
* Crée un Membre minimal à partir d'un email (pour associer un compte Keycloak sans fiche membre).
|
|
*/
|
|
private Membre creerMembreMinimalPourEmail(String email) {
|
|
String partieLocale = email.contains("@") ? email.substring(0, email.indexOf('@')) : email;
|
|
String prenom = partieLocale.contains(".") ? partieLocale.substring(0, partieLocale.indexOf('.')) : "Admin";
|
|
String nom = partieLocale.contains(".") ? partieLocale.substring(partieLocale.indexOf('.') + 1) : partieLocale;
|
|
if (nom.isBlank()) nom = "Utilisateur";
|
|
if (prenom.isBlank()) prenom = "Admin";
|
|
prenom = prenom.substring(0, 1).toUpperCase() + (prenom.length() > 1 ? prenom.substring(1).toLowerCase() : "");
|
|
nom = nom.substring(0, 1).toUpperCase() + (nom.length() > 1 ? nom.substring(1).toLowerCase() : "");
|
|
String numeroMembre = "UF-ADM-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
|
|
Membre m = Membre.builder()
|
|
.email(email)
|
|
.numeroMembre(numeroMembre)
|
|
.prenom(prenom)
|
|
.nom(nom)
|
|
.dateNaissance(LocalDate.now().minusYears(25))
|
|
.statutCompte("ACTIF")
|
|
.build();
|
|
m.setActif(Boolean.TRUE); // actif est dans BaseEntity, pas dans MembreBuilder
|
|
return m;
|
|
}
|
|
|
|
/**
|
|
* Liste toutes les organisations actives
|
|
*
|
|
* @return liste des organisations actives
|
|
*/
|
|
public List<Organisation> listerOrganisationsActives() {
|
|
return organisationRepository.findAllActives();
|
|
}
|
|
|
|
/**
|
|
* Liste toutes les organisations actives avec pagination
|
|
*
|
|
* @param page numéro de page
|
|
* @param size taille de la page
|
|
* @return liste paginée des organisations actives
|
|
*/
|
|
public List<Organisation> listerOrganisationsActives(int page, int size) {
|
|
return organisationRepository.findAllActives(Page.of(page, size), Sort.by("nom").ascending());
|
|
}
|
|
|
|
/**
|
|
* Compte le nombre d'organisations actives
|
|
*
|
|
* @return nombre total d'organisations actives
|
|
*/
|
|
public long compterOrganisationsActives() {
|
|
return organisationRepository.countActives();
|
|
}
|
|
|
|
/**
|
|
* Recherche d'organisations par nom
|
|
*
|
|
* @param recherche terme de recherche
|
|
* @param page numéro de page
|
|
* @param size taille de la page
|
|
* @return liste paginée des organisations correspondantes
|
|
*/
|
|
public List<Organisation> rechercherOrganisations(String recherche, int page, int size) {
|
|
return organisationRepository.findByNomOrNomCourt(
|
|
recherche, Page.of(page, size), Sort.by("nom").ascending());
|
|
}
|
|
|
|
public long rechercherOrganisationsCount(String recherche) {
|
|
if (recherche == null || recherche.trim().isEmpty()) {
|
|
return organisationRepository.count();
|
|
}
|
|
String pattern = "%" + recherche.trim().toLowerCase() + "%";
|
|
return organisationRepository.getEntityManager()
|
|
.createQuery(
|
|
"SELECT COUNT(o) FROM Organisation o WHERE LOWER(o.nom) LIKE :p OR LOWER(o.description) LIKE :p",
|
|
Long.class)
|
|
.setParameter("p", pattern)
|
|
.getSingleResult();
|
|
}
|
|
|
|
/**
|
|
* Recherche avancée d'organisations
|
|
*
|
|
* @param nom nom (optionnel)
|
|
* @param typeOrganisation type (optionnel)
|
|
* @param statut statut (optionnel)
|
|
* @param ville ville (optionnel)
|
|
* @param region région (optionnel)
|
|
* @param pays pays (optionnel)
|
|
* @param page numéro de page
|
|
* @param size taille de la page
|
|
* @return liste filtrée des organisations
|
|
*/
|
|
public List<Organisation> rechercheAvancee(
|
|
String nom,
|
|
String typeOrganisation,
|
|
String statut,
|
|
String ville,
|
|
String region,
|
|
String pays,
|
|
int page,
|
|
int size) {
|
|
return organisationRepository.rechercheAvancee(
|
|
nom, typeOrganisation, statut, ville, region, pays, Page.of(page, size));
|
|
}
|
|
|
|
/**
|
|
* Active une organisation
|
|
*
|
|
* @param id l'ID de l'organisation
|
|
* @param utilisateur l'utilisateur effectuant l'activation
|
|
* @return l'organisation activée
|
|
*/
|
|
@Transactional
|
|
public Organisation activerOrganisation(UUID id, String utilisateur) {
|
|
LOG.infof("Activation de l'organisation ID: %s", id);
|
|
|
|
Organisation organisation = organisationRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(() -> new NotFoundException("Organisation non trouvée avec l'ID: " + id));
|
|
|
|
organisation.activer(utilisateur);
|
|
|
|
LOG.infof("Organisation activée avec succès: ID=%s", id);
|
|
return organisation;
|
|
}
|
|
|
|
/**
|
|
* Suspend une organisation
|
|
*
|
|
* @param id l'UUID de l'organisation
|
|
* @param utilisateur l'utilisateur effectuant la suspension
|
|
* @return l'organisation suspendue
|
|
*/
|
|
@Transactional
|
|
public Organisation suspendreOrganisation(UUID id, String utilisateur) {
|
|
LOG.infof("Suspension de l'organisation ID: %s", id);
|
|
|
|
Organisation organisation = organisationRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(() -> new NotFoundException("Organisation non trouvée avec l'ID: " + id));
|
|
|
|
organisation.suspendre(utilisateur);
|
|
|
|
LOG.infof("Organisation suspendue avec succès: ID=%s", id);
|
|
return organisation;
|
|
}
|
|
|
|
/**
|
|
* Obtient les statistiques des organisations (clés compatibles client DTO
|
|
* StatistiquesAssociationDTO).
|
|
*
|
|
* @return map contenant les statistiques
|
|
*/
|
|
public Map<String, Object> obtenirStatistiques() {
|
|
LOG.info("Calcul des statistiques des organisations");
|
|
|
|
long total = organisationRepository.count();
|
|
long actives = organisationRepository.countActives();
|
|
long inactives = total - actives;
|
|
long suspendues = organisationRepository.countByStatut("SUSPENDUE");
|
|
long dissoutes = organisationRepository.countByStatut("DISSOLUE");
|
|
long nouvelles30Jours = organisationRepository.countNouvellesOrganisations(LocalDate.now().minusDays(30));
|
|
double tauxActivite = total > 0 ? (actives * 100.0 / total) : 0.0;
|
|
|
|
List<Organisation> all = organisationRepository.listAll();
|
|
Map<String, Long> repartitionType = all.stream()
|
|
.collect(Collectors.groupingBy(
|
|
o -> o.getTypeOrganisation() != null ? o.getTypeOrganisation() : "NON_DEFINI",
|
|
Collectors.counting()));
|
|
Map<String, Long> repartitionRegion = adresseRepository
|
|
.find("organisation IS NOT NULL AND region IS NOT NULL")
|
|
.list()
|
|
.stream()
|
|
.collect(Collectors.groupingBy(
|
|
a -> a.getRegion(),
|
|
Collectors.counting()));
|
|
|
|
Map<String, Object> map = new HashMap<>();
|
|
map.put("totalAssociations", total);
|
|
map.put("associationsActives", actives);
|
|
map.put("associationsInactives", inactives);
|
|
map.put("associationsSuspendues", suspendues);
|
|
map.put("associationsDissoutes", dissoutes);
|
|
map.put("nouvellesAssociations30Jours", nouvelles30Jours);
|
|
map.put("tauxActivite", tauxActivite);
|
|
map.put("repartitionParType", repartitionType);
|
|
map.put("repartitionParRegion", repartitionRegion);
|
|
return map;
|
|
}
|
|
|
|
/**
|
|
* Convertit une entité Organisation en DTO complet
|
|
*/
|
|
public OrganisationResponse convertToResponse(Organisation organisation) {
|
|
if (organisation == null) {
|
|
return null;
|
|
}
|
|
|
|
OrganisationResponse dto = new OrganisationResponse();
|
|
dto.setId(organisation.getId());
|
|
dto.setNom(organisation.getNom());
|
|
dto.setNomCourt(organisation.getNomCourt());
|
|
dto.setDescription(organisation.getDescription());
|
|
dto.setEmail(organisation.getEmail());
|
|
dto.setTelephone(organisation.getTelephone());
|
|
dto.setTelephoneSecondaire(organisation.getTelephoneSecondaire());
|
|
dto.setEmailSecondaire(organisation.getEmailSecondaire());
|
|
dto.setAdresse(organisation.getAdresse());
|
|
dto.setVille(organisation.getVille());
|
|
dto.setRegion(organisation.getRegion());
|
|
dto.setPays(organisation.getPays());
|
|
dto.setCodePostal(organisation.getCodePostal());
|
|
dto.setLatitude(organisation.getLatitude());
|
|
dto.setLongitude(organisation.getLongitude());
|
|
dto.setSiteWeb(organisation.getSiteWeb());
|
|
dto.setLogo(organisation.getLogo());
|
|
dto.setReseauxSociaux(organisation.getReseauxSociaux());
|
|
dto.setObjectifs(organisation.getObjectifs());
|
|
dto.setActivitesPrincipales(organisation.getActivitesPrincipales());
|
|
dto.setNombreMembres(organisation.getNombreMembres());
|
|
if (organisation.getId() != null) {
|
|
// Compte dynamique des administrateurs (rôle ADMIN_ORGANISATION actif)
|
|
// — le champ Organisation.nombreAdministrateurs n'est pas tenu à jour.
|
|
long countAdmins = membreRoleRepository.countAdminsByOrganisationId(organisation.getId());
|
|
dto.setNombreAdministrateurs((int) countAdmins);
|
|
|
|
long countEvenements = evenementRepository.countActifsByOrganisationId(organisation.getId());
|
|
dto.setNombreEvenements((int) countEvenements);
|
|
} else {
|
|
dto.setNombreAdministrateurs(0);
|
|
dto.setNombreEvenements(0);
|
|
}
|
|
dto.setBudgetAnnuel(organisation.getBudgetAnnuel());
|
|
dto.setDevise(organisation.getDevise());
|
|
dto.setDateFondation(organisation.getDateFondation());
|
|
dto.setNumeroEnregistrement(organisation.getNumeroEnregistrement());
|
|
dto.setNiveauHierarchique(organisation.getNiveauHierarchique());
|
|
|
|
if (organisation.getOrganisationParente() != null) {
|
|
dto.setOrganisationParenteId(organisation.getOrganisationParente().getId());
|
|
dto.setOrganisationParenteNom(organisation.getOrganisationParente().getNom());
|
|
}
|
|
|
|
dto.setTypeOrganisation(organisation.getTypeOrganisation());
|
|
dto.setTypeAssociation(organisation.getTypeOrganisation());
|
|
dto.setStatut(organisation.getStatut());
|
|
|
|
// Résolution des libellés
|
|
if (organisation.getTypeOrganisation() != null) {
|
|
typeReferenceRepository.findByDomaineAndCode("TYPE_ORGANISATION", organisation.getTypeOrganisation())
|
|
.ifPresent(ref -> {
|
|
dto.setTypeOrganisationLibelle(ref.getLibelle());
|
|
dto.setTypeLibelle(ref.getLibelle());
|
|
});
|
|
if (dto.getTypeLibelle() == null) {
|
|
dto.setTypeLibelle(organisation.getTypeOrganisation());
|
|
}
|
|
}
|
|
if (organisation.getStatut() != null) {
|
|
typeReferenceRepository.findByDomaineAndCode("STATUT_ORGANISATION", organisation.getStatut())
|
|
.ifPresent(ref -> {
|
|
dto.setStatutLibelle(ref.getLibelle());
|
|
dto.setStatutSeverity(ref.getCouleur()); // ou severity si dispo
|
|
});
|
|
}
|
|
|
|
dto.setDateCreation(organisation.getDateCreation());
|
|
dto.setDateModification(organisation.getDateModification());
|
|
dto.setCreePar(organisation.getCreePar());
|
|
dto.setModifiePar(organisation.getModifiePar());
|
|
dto.setActif(organisation.getActif());
|
|
dto.setVersion(organisation.getVersion());
|
|
|
|
dto.setOrganisationPublique(organisation.getOrganisationPublique());
|
|
dto.setAccepteNouveauxMembres(organisation.getAccepteNouveauxMembres());
|
|
dto.setCotisationObligatoire(organisation.getCotisationObligatoire());
|
|
dto.setMontantCotisationAnnuelle(organisation.getMontantCotisationAnnuelle());
|
|
|
|
return dto;
|
|
}
|
|
|
|
/**
|
|
* Convertit une entité Organisation en Summary DTO
|
|
*/
|
|
public OrganisationSummaryResponse convertToSummaryResponse(Organisation organisation) {
|
|
if (organisation == null)
|
|
return null;
|
|
|
|
String typeLibelle = organisation.getTypeOrganisation();
|
|
if (organisation.getTypeOrganisation() != null) {
|
|
typeLibelle = typeReferenceRepository
|
|
.findByDomaineAndCode("TYPE_ORGANISATION", organisation.getTypeOrganisation())
|
|
.map(dev.lions.unionflow.server.entity.TypeReference::getLibelle)
|
|
.orElse(organisation.getTypeOrganisation());
|
|
}
|
|
|
|
String statutLibelle = organisation.getStatut();
|
|
String statutSeverity = null;
|
|
if (organisation.getStatut() != null) {
|
|
var refOpt = typeReferenceRepository.findByDomaineAndCode("STATUT_ORGANISATION", organisation.getStatut());
|
|
if (refOpt.isPresent()) {
|
|
statutLibelle = refOpt.get().getLibelle();
|
|
statutSeverity = refOpt.get().getCouleur();
|
|
}
|
|
}
|
|
|
|
return new OrganisationSummaryResponse(
|
|
organisation.getId(),
|
|
organisation.getNom(),
|
|
organisation.getNomCourt(),
|
|
organisation.getTypeOrganisation(),
|
|
typeLibelle,
|
|
organisation.getStatut(),
|
|
statutLibelle,
|
|
statutSeverity,
|
|
organisation.getNombreMembres(),
|
|
organisation.getActif());
|
|
}
|
|
|
|
/**
|
|
* Crée une entité Organisation depuis CreateOrganisationRequest
|
|
*/
|
|
public Organisation convertFromCreateRequest(CreateOrganisationRequest req) {
|
|
if (req == null)
|
|
return null;
|
|
return Organisation.builder()
|
|
.nom(req.nom())
|
|
.nomCourt(req.nomCourt())
|
|
.description(req.description())
|
|
.email(req.email())
|
|
.telephone(req.telephone())
|
|
.telephoneSecondaire(req.telephoneSecondaire())
|
|
.emailSecondaire(req.emailSecondaire())
|
|
.latitude(req.latitude())
|
|
.longitude(req.longitude())
|
|
.siteWeb(req.siteWeb())
|
|
.logo(req.logo())
|
|
.reseauxSociaux(req.reseauxSociaux())
|
|
.objectifs(req.objectifs())
|
|
.activitesPrincipales(req.activitesPrincipales())
|
|
.certifications(req.certifications())
|
|
.partenaires(req.partenaires())
|
|
.notes(req.notes())
|
|
.dateFondation(req.dateFondation())
|
|
.numeroEnregistrement(req.numeroEnregistrement())
|
|
.typeOrganisation(req.typeOrganisation() != null ? req.typeOrganisation() : "ASSOCIATION")
|
|
.statut(req.statut() != null ? req.statut() : "ACTIVE")
|
|
.budgetAnnuel(req.budgetAnnuel())
|
|
.devise(req.devise() != null ? req.devise() : defaultsService.getDevise())
|
|
.cotisationObligatoire(req.cotisationObligatoire() != null ? req.cotisationObligatoire() : false)
|
|
.montantCotisationAnnuelle(req.montantCotisationAnnuelle())
|
|
.adresse(req.adresse())
|
|
.ville(req.ville())
|
|
.region(req.region())
|
|
.pays(req.pays())
|
|
.codePostal(req.codePostal())
|
|
.organisationPublique(req.organisationPublique() != null ? req.organisationPublique() : true)
|
|
.accepteNouveauxMembres(req.accepteNouveauxMembres() != null ? req.accepteNouveauxMembres() : true)
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* Crée une entité Organisation depuis UpdateOrganisationRequest
|
|
*/
|
|
public Organisation convertFromUpdateRequest(UpdateOrganisationRequest req) {
|
|
if (req == null)
|
|
return null;
|
|
return Organisation.builder()
|
|
.nom(req.nom())
|
|
.nomCourt(req.nomCourt())
|
|
.description(req.description())
|
|
.email(req.email())
|
|
.telephone(req.telephone())
|
|
.telephoneSecondaire(req.telephoneSecondaire())
|
|
.emailSecondaire(req.emailSecondaire())
|
|
.latitude(req.latitude())
|
|
.longitude(req.longitude())
|
|
.siteWeb(req.siteWeb())
|
|
.logo(req.logo())
|
|
.reseauxSociaux(req.reseauxSociaux())
|
|
.objectifs(req.objectifs())
|
|
.activitesPrincipales(req.activitesPrincipales())
|
|
.certifications(req.certifications())
|
|
.partenaires(req.partenaires())
|
|
.notes(req.notes())
|
|
.dateFondation(req.dateFondation())
|
|
.numeroEnregistrement(req.numeroEnregistrement())
|
|
.typeOrganisation(req.typeOrganisation())
|
|
.statut(req.statut())
|
|
.budgetAnnuel(req.budgetAnnuel())
|
|
.devise(req.devise() != null ? req.devise() : defaultsService.getDevise())
|
|
.cotisationObligatoire(req.cotisationObligatoire() != null ? req.cotisationObligatoire() : false)
|
|
.montantCotisationAnnuelle(req.montantCotisationAnnuelle())
|
|
.adresse(req.adresse())
|
|
.ville(req.ville())
|
|
.region(req.region())
|
|
.pays(req.pays())
|
|
.codePostal(req.codePostal())
|
|
.organisationPublique(req.organisationPublique() != null ? req.organisationPublique() : true)
|
|
.accepteNouveauxMembres(req.accepteNouveauxMembres() != null ? req.accepteNouveauxMembres() : true)
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* Retourne la liste des organisations d'un membre (pour le sélecteur multi-org).
|
|
* Inclut les infos nécessaires au sélecteur : id, nom, type, catégorie, modules, rôle du membre.
|
|
*/
|
|
public java.util.List<java.util.Map<String, Object>> listerOrganisationsParMembre(java.util.UUID membreId) {
|
|
java.util.List<MembreOrganisation> liens = membreOrganisationRepository.findOrganisationsActivesParMembre(membreId);
|
|
return liens.stream().map(lien -> {
|
|
Organisation org = lien.getOrganisation();
|
|
java.util.Map<String, Object> entry = new java.util.LinkedHashMap<>();
|
|
entry.put("organisationId", org.getId());
|
|
entry.put("nom", org.getNom());
|
|
entry.put("nomCourt", org.getNomCourt());
|
|
entry.put("typeOrganisation", org.getTypeOrganisation());
|
|
entry.put("categorieType", org.getCategorieType());
|
|
entry.put("modulesActifs", org.getModulesActifs());
|
|
entry.put("statut", org.getStatut());
|
|
entry.put("statutMembre", lien.getStatutMembre() != null ? lien.getStatutMembre().name() : null);
|
|
entry.put("roleOrg", lien.getRoleOrg());
|
|
entry.put("dateAdhesion", lien.getDateAdhesion());
|
|
return entry;
|
|
}).toList();
|
|
}
|
|
}
|