feat(dashboard): cotisations tout temps + synthèse membre

- CotisationRepository: calculerTotalCotisationsPayeesToutTemps(membreId)
- MembreDashboardService: envoi totalCotisationsPayeesToutTemps dans la synthèse

Made-with: Cursor
This commit is contained in:
dahoud
2026-03-09 19:58:25 +00:00
parent 23528197cf
commit a1e30b51fb
2 changed files with 438 additions and 70 deletions

View File

@@ -0,0 +1,158 @@
package dev.lions.unionflow.server.service;
import dev.lions.unionflow.server.api.dto.dashboard.MembreDashboardSyntheseResponse;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.DemandeAideRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.CompteEpargneRepository;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.jboss.logging.Logger;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class MembreDashboardService {
private static final Logger LOG = Logger.getLogger(MembreDashboardService.class);
@Inject
SecurityIdentity securityIdentity;
@Inject
MembreRepository membreRepository;
@Inject
CotisationRepository cotisationRepository;
@Inject
CompteEpargneRepository compteEpargneRepository;
@Inject
DemandeAideRepository demandeAideRepository;
/**
* Récupère l'email du principal : d'abord la claim JWT "email" (Keycloak envoie souvent
* preferred_username comme getName()), puis fallback sur getName().
*/
private String getEmailFromPrincipal() {
if (securityIdentity == null || securityIdentity.getPrincipal() == null) return null;
if (securityIdentity.getPrincipal() instanceof JsonWebToken jwt) {
try {
String email = jwt.getClaim("email");
if (email != null && !email.isBlank()) return email;
} catch (Exception e) {
LOG.debugf("Claim email non disponible: %s", e.getMessage());
}
}
return securityIdentity.getPrincipal().getName();
}
public MembreDashboardSyntheseResponse getDashboardData() {
String email = getEmailFromPrincipal();
if (email == null || email.isBlank()) {
throw new NotFoundException("Identité non disponible pour le dashboard membre.");
}
LOG.infof("Génération du dashboard pour le membre: %s", email);
Membre membre = membreRepository.findByEmail(email.trim())
.or(() -> membreRepository.findByEmail(email.trim().toLowerCase()))
.filter(m -> m.getActif() == null || m.getActif())
.orElseThrow(() -> new NotFoundException("Membre non trouvé pour l'email: " + email));
UUID membreId = membre.getId();
LocalDate now = LocalDate.now();
// 1. Infos membre
String prenom = membre.getPrenom();
String nom = membre.getNom();
LocalDate dateInscription = null;
if (membre.getDateCreation() != null) {
dateInscription = membre.getDateCreation().toLocalDate();
}
// 2. Cotisations
// Approximations : on somme les cotisations payées ce mois, on regarde s'il y a
// des cotisations en retard, etc.
// On utilisera des méthodes custom sur le repository si besoin, ou on le fait
// en Java (sur les petits sets test)
BigDecimal paiementsMois = cotisationRepository.calculerTotalCotisationsPayeesCeMois(membreId);
// Statut : "En retard", "À jour", "En attente"
long enRetardCount = cotisationRepository.countRetardByMembreId(membreId);
String statutCotisations = enRetardCount > 0 ? "En retard" : "À jour";
// Taux de cotisation (payé vs dû pour l'année en cours)
BigDecimal totalAnneeDu = cotisationRepository.calculerTotalCotisationsAnneeEnCours(membreId);
BigDecimal totalAnneePaye = cotisationRepository.calculerTotalCotisationsPayeesAnneeEnCours(membreId);
Integer tauxCotisations = null;
if (totalAnneeDu != null && totalAnneeDu.compareTo(BigDecimal.ZERO) > 0) {
if (totalAnneePaye == null)
totalAnneePaye = BigDecimal.ZERO;
tauxCotisations = totalAnneePaye.multiply(new BigDecimal("100"))
.divide(totalAnneeDu, 0, java.math.RoundingMode.HALF_UP)
.intValue();
}
BigDecimal totalCotisationsPayeesAnnee = (totalAnneePaye != null ? totalAnneePaye : BigDecimal.ZERO);
BigDecimal totalCotisationsPayeesToutTemps = cotisationRepository.calculerTotalCotisationsPayeesToutTemps(membreId);
int nombreCotisationsPayees = (int) cotisationRepository.countPayeesByMembreId(membreId);
// 3. Epargne (somme des soldes des comptes actifs du membre)
BigDecimal soldeEpargne = compteEpargneRepository.sumSoldeActuelByMembreId(membreId);
BigDecimal evolutionEpargneNb = BigDecimal.ZERO;
String evolutionEpargneTxt = soldeEpargne.compareTo(BigDecimal.ZERO) > 0 ? "+0%" : "0%";
Integer objectifEpargne = 0;
// 4. Événements (pas de repository InscriptionEvenement dédié pour l'instant)
Integer mesEvenements = 0;
Integer evenementsAVenir = 0;
Integer tauxParticipation = null;
// 5. Aides (demandes du membre)
List<dev.lions.unionflow.server.entity.DemandeAide> demandes = demandeAideRepository.findByDemandeurId(membreId);
int mesDemandes = demandes != null ? demandes.size() : 0;
int aidesCours = demandes != null ? (int) demandes.stream()
.filter(d -> d.getStatut() != null && d.getStatut() != StatutAide.APPROUVEE && d.getStatut() != StatutAide.REJETEE && d.getStatut() != StatutAide.ANNULEE)
.count() : 0;
Integer tauxAidesApprouvees = null;
if (mesDemandes > 0 && demandes != null) {
long acceptees = demandes.stream().filter(d -> d.getStatut() == StatutAide.APPROUVEE).count();
tauxAidesApprouvees = (int) (acceptees * 100 / mesDemandes);
}
return new MembreDashboardSyntheseResponse(
prenom,
nom,
dateInscription,
paiementsMois != null ? paiementsMois : BigDecimal.ZERO,
totalCotisationsPayeesAnnee,
totalCotisationsPayeesToutTemps != null ? totalCotisationsPayeesToutTemps : BigDecimal.ZERO,
Integer.valueOf(nombreCotisationsPayees),
statutCotisations,
tauxCotisations,
soldeEpargne,
evolutionEpargneNb,
evolutionEpargneTxt,
objectifEpargne,
mesEvenements,
evenementsAVenir,
tauxParticipation,
Integer.valueOf(mesDemandes),
Integer.valueOf(aidesCours),
tauxAidesApprouvees);
}
}