Configure Maven repository for unionflow-server-api dependency

This commit is contained in:
dahoud
2025-12-10 01:08:17 +00:00
commit 4a0c5f9d33
320 changed files with 33373 additions and 0 deletions

View File

@@ -0,0 +1,493 @@
package dev.lions.unionflow.server.service;
import dev.lions.unionflow.server.api.dto.finance.CotisationDTO;
import dev.lions.unionflow.server.entity.Cotisation;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
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.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.NotFoundException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
/**
* Service métier pour la gestion des cotisations Contient la logique métier et les règles de
* validation
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-15
*/
@ApplicationScoped
@Slf4j
public class CotisationService {
@Inject CotisationRepository cotisationRepository;
@Inject MembreRepository membreRepository;
/**
* Récupère toutes les cotisations avec pagination
*
* @param page numéro de page (0-based)
* @param size taille de la page
* @return liste des cotisations converties en DTO
*/
public List<CotisationDTO> getAllCotisations(int page, int size) {
log.debug("Récupération des cotisations - page: {}, size: {}", page, size);
// Utilisation de EntityManager pour la pagination
jakarta.persistence.TypedQuery<Cotisation> query =
cotisationRepository.getEntityManager().createQuery(
"SELECT c FROM Cotisation c ORDER BY c.dateEcheance DESC",
Cotisation.class);
query.setFirstResult(page * size);
query.setMaxResults(size);
List<Cotisation> cotisations = query.getResultList();
return cotisations.stream().map(this::convertToDTO).collect(Collectors.toList());
}
/**
* Récupère une cotisation par son ID
*
* @param id identifiant UUID de la cotisation
* @return DTO de la cotisation
* @throws NotFoundException si la cotisation n'existe pas
*/
public CotisationDTO getCotisationById(@NotNull UUID id) {
log.debug("Récupération de la cotisation avec ID: {}", id);
Cotisation cotisation =
cotisationRepository
.findByIdOptional(id)
.orElseThrow(() -> new NotFoundException("Cotisation non trouvée avec l'ID: " + id));
return convertToDTO(cotisation);
}
/**
* Récupère une cotisation par son numéro de référence
*
* @param numeroReference numéro de référence unique
* @return DTO de la cotisation
* @throws NotFoundException si la cotisation n'existe pas
*/
public CotisationDTO getCotisationByReference(@NotNull String numeroReference) {
log.debug("Récupération de la cotisation avec référence: {}", numeroReference);
Cotisation cotisation =
cotisationRepository
.findByNumeroReference(numeroReference)
.orElseThrow(
() ->
new NotFoundException(
"Cotisation non trouvée avec la référence: " + numeroReference));
return convertToDTO(cotisation);
}
/**
* Crée une nouvelle cotisation
*
* @param cotisationDTO données de la cotisation à créer
* @return DTO de la cotisation créée
*/
@Transactional
public CotisationDTO createCotisation(@Valid CotisationDTO cotisationDTO) {
log.info("Création d'une nouvelle cotisation pour le membre: {}", cotisationDTO.getMembreId());
// Validation du membre - UUID direct maintenant
Membre membre =
membreRepository
.findByIdOptional(cotisationDTO.getMembreId())
.orElseThrow(
() ->
new NotFoundException(
"Membre non trouvé avec l'ID: " + cotisationDTO.getMembreId()));
// Conversion DTO vers entité
Cotisation cotisation = convertToEntity(cotisationDTO);
cotisation.setMembre(membre);
// Génération automatique du numéro de référence si absent
if (cotisation.getNumeroReference() == null || cotisation.getNumeroReference().isEmpty()) {
cotisation.setNumeroReference(Cotisation.genererNumeroReference());
}
// Validation des règles métier
validateCotisationRules(cotisation);
// Persistance
cotisationRepository.persist(cotisation);
log.info(
"Cotisation créée avec succès - ID: {}, Référence: {}",
cotisation.getId(),
cotisation.getNumeroReference());
return convertToDTO(cotisation);
}
/**
* Met à jour une cotisation existante
*
* @param id identifiant UUID de la cotisation
* @param cotisationDTO nouvelles données
* @return DTO de la cotisation mise à jour
*/
@Transactional
public CotisationDTO updateCotisation(@NotNull UUID id, @Valid CotisationDTO cotisationDTO) {
log.info("Mise à jour de la cotisation avec ID: {}", id);
Cotisation cotisationExistante =
cotisationRepository
.findByIdOptional(id)
.orElseThrow(() -> new NotFoundException("Cotisation non trouvée avec l'ID: " + id));
// Mise à jour des champs modifiables
updateCotisationFields(cotisationExistante, cotisationDTO);
// Validation des règles métier
validateCotisationRules(cotisationExistante);
log.info("Cotisation mise à jour avec succès - ID: {}", id);
return convertToDTO(cotisationExistante);
}
/**
* Supprime (désactive) une cotisation
*
* @param id identifiant UUID de la cotisation
*/
@Transactional
public void deleteCotisation(@NotNull UUID id) {
log.info("Suppression de la cotisation avec ID: {}", id);
Cotisation cotisation =
cotisationRepository
.findByIdOptional(id)
.orElseThrow(() -> new NotFoundException("Cotisation non trouvée avec l'ID: " + id));
// Vérification si la cotisation peut être supprimée
if ("PAYEE".equals(cotisation.getStatut())) {
throw new IllegalStateException("Impossible de supprimer une cotisation déjà payée");
}
cotisation.setStatut("ANNULEE");
log.info("Cotisation supprimée avec succès - ID: {}", id);
}
/**
* Récupère les cotisations d'un membre
*
* @param membreId identifiant UUID du membre
* @param page numéro de page
* @param size taille de la page
* @return liste des cotisations du membre
*/
public List<CotisationDTO> getCotisationsByMembre(@NotNull UUID membreId, int page, int size) {
log.debug("Récupération des cotisations du membre: {}", membreId);
// Vérification de l'existence du membre
if (!membreRepository.findByIdOptional(membreId).isPresent()) {
throw new NotFoundException("Membre non trouvé avec l'ID: " + membreId);
}
List<Cotisation> cotisations =
cotisationRepository.findByMembreId(
membreId, Page.of(page, size), Sort.by("dateEcheance").descending());
return cotisations.stream().map(this::convertToDTO).collect(Collectors.toList());
}
/**
* Récupère les cotisations par statut
*
* @param statut statut recherché
* @param page numéro de page
* @param size taille de la page
* @return liste des cotisations avec le statut spécifié
*/
public List<CotisationDTO> getCotisationsByStatut(@NotNull String statut, int page, int size) {
log.debug("Récupération des cotisations avec statut: {}", statut);
List<Cotisation> cotisations = cotisationRepository.findByStatut(statut, Page.of(page, size));
return cotisations.stream().map(this::convertToDTO).collect(Collectors.toList());
}
/**
* Récupère les cotisations en retard
*
* @param page numéro de page
* @param size taille de la page
* @return liste des cotisations en retard
*/
public List<CotisationDTO> getCotisationsEnRetard(int page, int size) {
log.debug("Récupération des cotisations en retard");
List<Cotisation> cotisations =
cotisationRepository.findCotisationsEnRetard(LocalDate.now(), Page.of(page, size));
return cotisations.stream().map(this::convertToDTO).collect(Collectors.toList());
}
/**
* Recherche avancée de cotisations
*
* @param membreId identifiant du membre (optionnel)
* @param statut statut (optionnel)
* @param typeCotisation type (optionnel)
* @param annee année (optionnel)
* @param mois mois (optionnel)
* @param page numéro de page
* @param size taille de la page
* @return liste filtrée des cotisations
*/
public List<CotisationDTO> rechercherCotisations(
UUID membreId,
String statut,
String typeCotisation,
Integer annee,
Integer mois,
int page,
int size) {
log.debug("Recherche avancée de cotisations avec filtres");
List<Cotisation> cotisations =
cotisationRepository.rechercheAvancee(
membreId, statut, typeCotisation, annee, mois, Page.of(page, size));
return cotisations.stream().map(this::convertToDTO).collect(Collectors.toList());
}
/**
* Récupère les statistiques des cotisations
*
* @return map contenant les statistiques
*/
public Map<String, Object> getStatistiquesCotisations() {
log.debug("Calcul des statistiques des cotisations");
long totalCotisations = cotisationRepository.count();
long cotisationsPayees = cotisationRepository.compterParStatut("PAYEE");
long cotisationsEnRetard =
cotisationRepository
.findCotisationsEnRetard(LocalDate.now(), Page.of(0, Integer.MAX_VALUE))
.size();
return Map.of(
"totalCotisations", totalCotisations,
"cotisationsPayees", cotisationsPayees,
"cotisationsEnRetard", cotisationsEnRetard,
"tauxPaiement",
totalCotisations > 0 ? (cotisationsPayees * 100.0 / totalCotisations) : 0.0);
}
/** Convertit une entité Cotisation en DTO */
private CotisationDTO convertToDTO(Cotisation cotisation) {
if (cotisation == null) {
return null;
}
CotisationDTO dto = new CotisationDTO();
// Conversion de l'ID UUID vers UUID (pas de conversion nécessaire maintenant)
dto.setId(cotisation.getId());
dto.setNumeroReference(cotisation.getNumeroReference());
// Conversion du membre associé
if (cotisation.getMembre() != null) {
dto.setMembreId(cotisation.getMembre().getId());
dto.setNomMembre(cotisation.getMembre().getNomComplet());
dto.setNumeroMembre(cotisation.getMembre().getNumeroMembre());
// Conversion de l'organisation du membre (associationId)
if (cotisation.getMembre().getOrganisation() != null
&& cotisation.getMembre().getOrganisation().getId() != null) {
dto.setAssociationId(cotisation.getMembre().getOrganisation().getId());
dto.setNomAssociation(cotisation.getMembre().getOrganisation().getNom());
}
}
// Propriétés de la cotisation
dto.setTypeCotisation(cotisation.getTypeCotisation());
dto.setMontantDu(cotisation.getMontantDu());
dto.setMontantPaye(cotisation.getMontantPaye());
dto.setCodeDevise(cotisation.getCodeDevise());
dto.setStatut(cotisation.getStatut());
dto.setDateEcheance(cotisation.getDateEcheance());
dto.setDatePaiement(cotisation.getDatePaiement());
dto.setDescription(cotisation.getDescription());
dto.setPeriode(cotisation.getPeriode());
dto.setAnnee(cotisation.getAnnee());
dto.setMois(cotisation.getMois());
dto.setObservations(cotisation.getObservations());
dto.setRecurrente(cotisation.getRecurrente());
dto.setNombreRappels(cotisation.getNombreRappels());
dto.setDateDernierRappel(cotisation.getDateDernierRappel());
// Conversion du validateur
dto.setValidePar(
cotisation.getValideParId() != null
? cotisation.getValideParId()
: null);
dto.setNomValidateur(cotisation.getNomValidateur());
dto.setMethodePaiement(cotisation.getMethodePaiement());
dto.setReferencePaiement(cotisation.getReferencePaiement());
dto.setDateCreation(cotisation.getDateCreation());
dto.setDateModification(cotisation.getDateModification());
// Propriétés héritées de BaseDTO
dto.setActif(true); // Les cotisations sont toujours actives
dto.setVersion(0L); // Version par défaut
return dto;
}
/** Convertit un DTO en entité Cotisation */
private Cotisation convertToEntity(CotisationDTO dto) {
return Cotisation.builder()
.numeroReference(dto.getNumeroReference())
.typeCotisation(dto.getTypeCotisation())
.montantDu(dto.getMontantDu())
.montantPaye(dto.getMontantPaye() != null ? dto.getMontantPaye() : BigDecimal.ZERO)
.codeDevise(dto.getCodeDevise() != null ? dto.getCodeDevise() : "XOF")
.statut(dto.getStatut() != null ? dto.getStatut() : "EN_ATTENTE")
.dateEcheance(dto.getDateEcheance())
.datePaiement(dto.getDatePaiement())
.description(dto.getDescription())
.periode(dto.getPeriode())
.annee(dto.getAnnee())
.mois(dto.getMois())
.observations(dto.getObservations())
.recurrente(dto.getRecurrente() != null ? dto.getRecurrente() : false)
.nombreRappels(dto.getNombreRappels() != null ? dto.getNombreRappels() : 0)
.dateDernierRappel(dto.getDateDernierRappel())
.methodePaiement(dto.getMethodePaiement())
.referencePaiement(dto.getReferencePaiement())
.build();
}
/** Met à jour les champs d'une cotisation existante */
private void updateCotisationFields(Cotisation cotisation, CotisationDTO dto) {
if (dto.getTypeCotisation() != null) {
cotisation.setTypeCotisation(dto.getTypeCotisation());
}
if (dto.getMontantDu() != null) {
cotisation.setMontantDu(dto.getMontantDu());
}
if (dto.getMontantPaye() != null) {
cotisation.setMontantPaye(dto.getMontantPaye());
}
if (dto.getStatut() != null) {
cotisation.setStatut(dto.getStatut());
}
if (dto.getDateEcheance() != null) {
cotisation.setDateEcheance(dto.getDateEcheance());
}
if (dto.getDatePaiement() != null) {
cotisation.setDatePaiement(dto.getDatePaiement());
}
if (dto.getDescription() != null) {
cotisation.setDescription(dto.getDescription());
}
if (dto.getObservations() != null) {
cotisation.setObservations(dto.getObservations());
}
if (dto.getMethodePaiement() != null) {
cotisation.setMethodePaiement(dto.getMethodePaiement());
}
if (dto.getReferencePaiement() != null) {
cotisation.setReferencePaiement(dto.getReferencePaiement());
}
}
/** Valide les règles métier pour une cotisation */
private void validateCotisationRules(Cotisation cotisation) {
// Validation du montant
if (cotisation.getMontantDu().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Le montant dû doit être positif");
}
// Validation de la date d'échéance
if (cotisation.getDateEcheance().isBefore(LocalDate.now().minusYears(1))) {
throw new IllegalArgumentException("La date d'échéance ne peut pas être antérieure à un an");
}
// Validation du montant payé
if (cotisation.getMontantPaye().compareTo(cotisation.getMontantDu()) > 0) {
throw new IllegalArgumentException("Le montant payé ne peut pas dépasser le montant dû");
}
// Validation de la cohérence statut/paiement
if ("PAYEE".equals(cotisation.getStatut())
&& cotisation.getMontantPaye().compareTo(cotisation.getMontantDu()) < 0) {
throw new IllegalArgumentException(
"Une cotisation marquée comme payée doit avoir un montant payé égal au montant dû");
}
}
/**
* Envoie des rappels de cotisations groupés à plusieurs membres (WOU/DRY)
*
* @param membreIds Liste des IDs des membres destinataires
* @return Nombre de rappels envoyés
*/
@Transactional
public int envoyerRappelsCotisationsGroupes(List<UUID> membreIds) {
log.info("Envoi de rappels de cotisations groupés à {} membres", membreIds.size());
if (membreIds == null || membreIds.isEmpty()) {
throw new IllegalArgumentException("La liste des membres ne peut pas être vide");
}
int rappelsEnvoyes = 0;
for (UUID membreId : membreIds) {
try {
Membre membre =
membreRepository
.findByIdOptional(membreId)
.orElseThrow(
() ->
new IllegalArgumentException(
"Membre non trouvé avec l'ID: " + membreId));
// Trouver les cotisations en retard pour ce membre
List<Cotisation> cotisationsEnRetard =
cotisationRepository.findCotisationsAuRappel(7, 3).stream()
.filter(c -> c.getMembre() != null && c.getMembre().getId().equals(membreId))
.collect(Collectors.toList());
for (Cotisation cotisation : cotisationsEnRetard) {
// Incrémenter le nombre de rappels
cotisationRepository.incrementerNombreRappels(cotisation.getId());
rappelsEnvoyes++;
}
} catch (Exception e) {
log.warn(
"Erreur lors de l'envoi du rappel de cotisation pour le membre {}: {}",
membreId,
e.getMessage());
}
}
log.info("{} rappels envoyés sur {} membres demandés", rappelsEnvoyes, membreIds.size());
return rappelsEnvoyes;
}
}