feat(api): DTOs v3.0 — membre computed fields, CreateMembreRequest builder, souscription DTOs, RBAC enums enrichis

- MembreResponse: ajout getInitiales(), getDateAdhesionFormatee(), getStatut(), getSeverity() computed
- CotisationResponse: alias getMethodePaiementLibelle()
- CreateMembreRequest: builder pattern, organisationId UUID
- Nouveaux DTOs souscription: FormuleAbonnementResponse, SouscriptionDemandeRequest, SouscriptionStatutResponse
- Nouveaux enums: PlageMembres, StatutValidationSouscription, TypeOrganisationFacturation
- StatutMembre enrichi (RADIE, ARCHIVE, INVITE, EN_ATTENTE_VALIDATION)
- TypeOrganisation, TypeFormule, StatutSouscription mis à jour
This commit is contained in:
dahoud
2026-04-07 20:51:10 +00:00
parent fb14bac741
commit 5fa4711a8f
42 changed files with 2330 additions and 58 deletions

View File

@@ -0,0 +1,21 @@
package dev.lions.unionflow.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* DTO d'erreur unifié retourné par tous les endpoints REST.
* Remplace les ErrorResponse locales dupliquées dans chaque Resource.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ErrorResponse(String message, String error) {
/** Constructeur pratique avec message uniquement (cas le plus courant). */
public static ErrorResponse of(String message) {
return new ErrorResponse(message, null);
}
/** Constructeur pratique avec les deux champs (compatibilité avec l'ancien format). */
public static ErrorResponse ofError(String error) {
return new ErrorResponse(null, error);
}
}

View File

@@ -96,6 +96,32 @@ public class CotisationResponse extends BaseResponse {
private Long joursRetard;
private Boolean enRetard;
// === MÉTHODES DE FORMATAGE ===
public String getMontantDuFormatte() {
if (montantDu == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantDu, codeDevise != null ? codeDevise : "FCFA");
}
public String getMontantPayeFormatte() {
if (montantPaye == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantPaye, codeDevise != null ? codeDevise : "FCFA");
}
public String getMontantRestantFormatte() {
if (montantRestant == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantRestant, codeDevise != null ? codeDevise : "FCFA");
}
public boolean isMontantRestantPositif() {
return montantRestant != null && montantRestant.signum() > 0;
}
/** Alias de {@link #modePaiementLibelle} pour #{cotisation.methodePaiementLibelle}. */
public String getMethodePaiementLibelle() {
return modePaiementLibelle;
}
// Informations de paiement
private String methodePaiement; // WAVE_MONEY, VIREMENT, ESPECES, CARTE, MOBILE_MONEY
private String referencePaiement; // Référence externe du paiement

View File

@@ -5,6 +5,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
@@ -151,10 +152,82 @@ public class EvenementResponse extends BaseResponse {
return typeEvenement != null ? typeEvenement.getLibelle() : "Non défini";
}
public String getTypeEvenementIcon() {
if (typeEvenement == null) return "pi pi-calendar";
return switch (typeEvenement) {
case ASSEMBLEE_GENERALE -> "pi pi-building";
case FORMATION -> "pi pi-book";
case REUNION_BUREAU -> "pi pi-users";
case CONFERENCE -> "pi pi-microphone";
case ATELIER -> "pi pi-wrench";
case CEREMONIE -> "pi pi-flag";
case ACTIVITE_SOCIALE, ACTION_CARITATIVE, AUTRE -> "pi pi-calendar";
};
}
public String getTypeEvenementSeverity() {
if (typeEvenement == null) return "secondary";
return switch (typeEvenement) {
case ASSEMBLEE_GENERALE -> "warning";
case FORMATION -> "info";
case ACTIVITE_SOCIALE, ACTION_CARITATIVE, REUNION_BUREAU, CONFERENCE, ATELIER, CEREMONIE, AUTRE -> "secondary";
};
}
public String getStatutLibelle() {
return statut != null ? statut.getLibelle() : "Non défini";
}
public String getStatutSeverity() {
if (statut == null) return "secondary";
return switch (statut) {
case PLANIFIE -> "info";
case EN_COURS -> "success";
case TERMINE, CONFIRME -> "secondary";
case ANNULE -> "danger";
case REPORTE -> "warning";
};
}
public String getDateDebutFormatee() {
return dateDebut != null ? dateDebut.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) : "";
}
public String getHeureDebutFormatee() {
return heureDebut != null ? heureDebut.format(DateTimeFormatter.ofPattern("HH:mm")) : "";
}
public String getHeureFinFormatee() {
return heureFin != null ? heureFin.format(DateTimeFormatter.ofPattern("HH:mm")) : "";
}
public String getStatutIcon() {
if (statut == null) return "pi pi-question";
return switch (statut) {
case PLANIFIE -> "pi pi-clock";
case CONFIRME -> "pi pi-check-circle";
case EN_COURS -> "pi pi-play";
case TERMINE -> "pi pi-check";
case ANNULE -> "pi pi-times";
case REPORTE -> "pi pi-refresh";
};
}
public String getPrioriteSeverity() {
if (priorite == null) return "secondary";
return switch (priorite) {
case CRITIQUE -> "danger";
case HAUTE -> "warning";
case NORMALE -> "info";
case BASSE -> "secondary";
};
}
public String getBudgetFormate() {
if (budget == null) return "";
return String.format(java.util.Locale.US, "%,.0f %s", budget, codeDevise != null ? codeDevise : "FCFA");
}
public String getPrioriteLibelle() {
return priorite != null ? priorite.getLibelle() : "Normale";
}

View File

@@ -40,7 +40,7 @@ public record CreateMembreRequest(
@NotBlank @Size(max = 100) String nom,
@NotBlank @Email @Size(max = 255) String email,
@Size(max = 20) String telephone,
@Size(max = 13) String telephoneWave,
@Size(max = 20) String telephoneWave,
@NotNull LocalDate dateNaissance,
@Size(max = 100) String profession,
@Size(max = 500) String photoUrl,

View File

@@ -14,7 +14,7 @@ public record UpdateMembreRequest(
@NotBlank @Size(max = 100) String nom,
@NotBlank @Email @Size(max = 255) String email,
@Size(max = 20) String telephone,
@Size(max = 13) String telephoneWave,
@Size(max = 20) String telephoneWave,
@NotNull LocalDate dateNaissance,
@Size(max = 100) String profession,
@Size(max = 500) String photoUrl,

View File

@@ -2,6 +2,7 @@ package dev.lions.unionflow.server.api.dto.membre.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.List;
import lombok.AllArgsConstructor;
@@ -65,6 +66,71 @@ public class MembreResponse extends BaseResponse {
// ── Adhésion (contexte organisation) ───────
private UUID organisationId;
private String associationNom;
private String organisationNom;
private LocalDate dateAdhesion;
// ── Adresse principale ─────────────────────
private String adresse;
private String ville;
private String codePostal;
// ── Notes / biographie ─────────────────────
private String notes;
// ── Statistiques ───────────────────────────
private int nombreEvenementsParticipes;
// ── Provisionnement (retourné une seule fois à la création) ────
/** Mot de passe temporaire généré lors du provisionnement Keycloak.
* Null pour toutes les autres opérations. L'utilisateur devra le changer à la première connexion. */
private String motDePasseTemporaire;
// ── Méthodes calculées pour la compatibilité JSF EL ────────────
/** Initiales (première lettre prénom + première lettre nom) pour les avatars JSF. */
public String getInitiales() {
String p = (prenom != null && !prenom.isEmpty()) ? prenom.substring(0, 1).toUpperCase() : "";
String n = (nom != null && !nom.isEmpty()) ? nom.substring(0, 1).toUpperCase() : "";
return p + n;
}
/** Date d'adhésion formatée "dd/MM/yyyy" pour #{membre.dateAdhesionFormatee}. */
public String getDateAdhesionFormatee() {
if (dateAdhesion == null) return null;
return dateAdhesion.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
/** Alias de statutCompte pour #{membre.statut}. */
public String getStatut() {
return statutCompte;
}
/** Alias de statutCompteSeverity pour #{membre.statutSeverity}. */
public String getStatutSeverity() {
return statutCompteSeverity;
}
/** Libellé du rôle principal calculé depuis les rôles. */
public String getTypeMembre() {
if (roles == null || roles.isEmpty()) return "Membre";
if (roles.contains("PRESIDENT")) return "Président";
if (roles.contains("VICE_PRESIDENT")) return "Vice-Président";
if (roles.contains("SECRETAIRE")) return "Secrétaire";
if (roles.contains("TRESORIER")) return "Trésorier";
if (roles.contains("ADMIN_ORGANISATION")) return "Administrateur";
if (roles.contains("MODERATEUR")) return "Modérateur";
return "Membre";
}
/** Severity PrimeUI calculée depuis les rôles. */
public String getTypeSeverity() {
if (roles == null || roles.isEmpty()) return "secondary";
if (roles.contains("PRESIDENT")) return "primary";
if (roles.contains("VICE_PRESIDENT")) return "primary";
if (roles.contains("SECRETAIRE")) return "info";
if (roles.contains("TRESORIER")) return "warning";
if (roles.contains("ADMIN_ORGANISATION")) return "danger";
if (roles.contains("MODERATEUR")) return "warning";
return "secondary";
}
}

View File

@@ -1,10 +1,14 @@
package dev.lions.unionflow.server.api.dto.membre.response;
import java.util.UUID;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
/**
* DTO de réponse résumé pour Membre (listes et optimisations).
*
* <p>Ce record expose des méthodes calculées pour la compatibilité JSF EL :
* {@link #nomComplet()}, {@link #statut()}, {@link #typeMembre()}, etc.
*/
public record MembreSummaryResponse(
UUID id,
@@ -20,5 +24,107 @@ public record MembreSummaryResponse(
Boolean actif,
List<String> roles,
UUID organisationId,
String associationNom) {
String organisationNom,
LocalDate dateAdhesion) {
// ── Getters JavaBean pour compatibilité JSF EL (EL cherche getXxx()) ──────
public UUID getId() { return id; }
public String getNumeroMembre() { return numeroMembre; }
public String getPrenom() { return prenom; }
public String getNom() { return nom; }
public String getEmail() { return email; }
public String getTelephone() { return telephone; }
public String getProfession() { return profession; }
public String getStatutCompte() { return statutCompte; }
public String getStatutCompteLibelle() { return statutCompteLibelle; }
public String getStatutCompteSeverity() { return statutCompteSeverity; }
public Boolean getActif() { return actif; }
public boolean isActif() { return Boolean.TRUE.equals(actif); }
public List<String> getRoles() { return roles; }
public UUID getOrganisationId() { return organisationId; }
public String getOrganisationNom() { return organisationNom; }
public LocalDate getDateAdhesion() { return dateAdhesion; }
// Getters JavaBean pour les propriétés calculées
public String getNomComplet() { return nomComplet(); }
public String getStatut() { return statut(); }
public String getStatutLibelle() { return statutLibelle(); }
public String getStatutSeverity() { return statutSeverity(); }
public String getStatutIcon() { return statutIcon(); }
public String getTypeMembre() { return typeMembre(); }
public String getTypeSeverity() { return typeSeverity(); }
public String getTypeIcon() { return typeIcon(); }
// ── Propriétés calculées pour la compatibilité JSF EL ──────────────────
/** Prénom + Nom concaténés. */
public String nomComplet() {
String p = prenom != null ? prenom.trim() : "";
String n = nom != null ? nom.trim() : "";
return (p + " " + n).trim();
}
/** Alias de {@link #statutCompte()} pour #{membre.statut}. */
public String statut() {
return statutCompte;
}
/** Alias de {@link #statutCompteLibelle()} pour #{membre.statutLibelle}. */
public String statutLibelle() {
return statutCompteLibelle;
}
/** Alias de {@link #statutCompteSeverity()} pour #{membre.statutSeverity}. */
public String statutSeverity() {
return statutCompteSeverity;
}
/** Icône PrimeIcons calculée depuis {@link #statutCompte()}. */
public String statutIcon() {
if (statutCompte == null) return "pi pi-question-circle";
return switch (statutCompte) {
case "ACTIF" -> "pi pi-check-circle";
case "INACTIF" -> "pi pi-pause-circle";
case "SUSPENDU" -> "pi pi-ban";
case "RADIE" -> "pi pi-times-circle";
default -> "pi pi-question-circle";
};
}
/** Libellé du rôle principal calculé depuis {@link #roles()}. */
public String typeMembre() {
if (roles == null || roles.isEmpty()) return "Membre";
if (roles.contains("PRESIDENT")) return "Président";
if (roles.contains("VICE_PRESIDENT")) return "Vice-Président";
if (roles.contains("SECRETAIRE")) return "Secrétaire";
if (roles.contains("TRESORIER")) return "Trésorier";
if (roles.contains("ADMIN_ORGANISATION")) return "Administrateur";
if (roles.contains("MODERATEUR")) return "Modérateur";
return "Membre";
}
/** Severity PrimeUI calculée depuis {@link #roles()}. */
public String typeSeverity() {
if (roles == null || roles.isEmpty()) return "secondary";
if (roles.contains("PRESIDENT")) return "primary";
if (roles.contains("VICE_PRESIDENT")) return "primary";
if (roles.contains("SECRETAIRE")) return "info";
if (roles.contains("TRESORIER")) return "warning";
if (roles.contains("ADMIN_ORGANISATION")) return "danger";
if (roles.contains("MODERATEUR")) return "warning";
return "secondary";
}
/** Icône PrimeIcons calculée depuis {@link #roles()}. */
public String typeIcon() {
if (roles == null || roles.isEmpty()) return "pi pi-user";
if (roles.contains("PRESIDENT")) return "pi pi-star";
if (roles.contains("VICE_PRESIDENT")) return "pi pi-star";
if (roles.contains("SECRETAIRE")) return "pi pi-file-edit";
if (roles.contains("TRESORIER")) return "pi pi-wallet";
if (roles.contains("ADMIN_ORGANISATION")) return "pi pi-shield";
if (roles.contains("MODERATEUR")) return "pi pi-wrench";
return "pi pi-user";
}
}

View File

@@ -70,5 +70,12 @@ public record CreateOrganisationRequest(
@Size(max = 1000) String partenaires,
@Size(max = 1000) String notes,
BigDecimal latitude,
BigDecimal longitude) {
BigDecimal longitude,
@Size(max = 500) String adresse,
@Size(max = 100) String ville,
@Size(max = 100) String region,
@Size(max = 100) String pays,
@Size(max = 20) String codePostal,
Boolean organisationPublique,
Boolean accepteNouveauxMembres) {
}

View File

@@ -44,5 +44,12 @@ public record UpdateOrganisationRequest(
@Size(max = 1000) String partenaires,
@Size(max = 1000) String notes,
BigDecimal latitude,
BigDecimal longitude) {
BigDecimal longitude,
@Size(max = 500) String adresse,
@Size(max = 100) String ville,
@Size(max = 100) String region,
@Size(max = 100) String pays,
@Size(max = 20) String codePostal,
Boolean organisationPublique,
Boolean accepteNouveauxMembres) {
}

View File

@@ -29,7 +29,7 @@ public record InitierPaiementEnLigneRequest(
message = "Méthode de paiement invalide. Valeurs autorisées : WAVE, ORANGE_MONEY, FREE_MONEY, CARTE_BANCAIRE")
String methodePaiement,
@NotBlank(message = "Le numéro de téléphone est obligatoire")
/** Optionnel sur le web (QR code sans restriction de payeur). Obligatoire sur mobile. */
@Pattern(regexp = "^\\d{9,15}$", message = "Numéro de téléphone invalide (9-15 chiffres)")
String numeroTelephone,

View File

@@ -0,0 +1,24 @@
package dev.lions.unionflow.server.api.dto.paiement.response;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Builder;
/**
* Réponse du polling de statut d'une IntentionPaiement Wave.
* Utilisée par le web pour savoir si le paiement est confirmé.
*/
@Builder
public record IntentionStatutResponse(
UUID intentionId,
/** INITIEE | EN_COURS | COMPLETEE | EXPIREE | ECHOUEE */
String statut,
/** URL à encoder en QR code (wave_launch_url Wave Checkout) */
String waveLaunchUrl,
String waveCheckoutSessionId,
/** ID de transaction Wave (TCN...) — disponible quand COMPLETEE */
String waveTransactionId,
BigDecimal montant,
String referenceCotisation,
String message
) {}

View File

@@ -0,0 +1,128 @@
package dev.lions.unionflow.server.api.dto.souscription;
import java.math.BigDecimal;
/**
* Représentation publique d'une formule d'abonnement UnionFlow.
*
* <p>Retournée par le endpoint {@code GET /api/souscriptions/formules} (PermitAll).
* Les prix affichés sont les prix de base avant application des coefficients
* de type d'organisation et de période.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-30
*/
public class FormuleAbonnementResponse {
/** Code de la formule : BASIC, STANDARD, PREMIUM */
private String code;
/** Libellé court de la formule */
private String libelle;
/** Description marketing de la formule */
private String description;
/** Code de la plage de membres : PETITE, MOYENNE, GRANDE, TRES_GRANDE */
private String plage;
/** Libellé de la plage (ex: "Petite structure (1100 membres)") */
private String plageLibelle;
/** Nombre minimum de membres pour cette plage */
private int minMembres;
/**
* Nombre maximum de membres pour cette plage.
* -1 signifie illimité (TRES_GRANDE).
*/
private int maxMembres;
/** Prix mensuel de base en XOF (sans remise de période ni coefficient) */
private BigDecimal prixMensuel;
/** Prix annuel équivalent (prixMensuel × 12 × coefficient annuel) */
private BigDecimal prixAnnuel;
/** Ordre d'affichage dans le catalogue */
private int ordreAffichage;
// ── Champs Option C ───────────────────────────────────────────────────────
/** Nom commercial du plan (MICRO, DECOUVERTE, ESSENTIEL, AVANCE, PROFESSIONNEL, ENTERPRISE) */
private String planCommercial;
/** Niveau de reporting (BASIQUE, STANDARD, AVANCE) */
private String niveauReporting;
/** Accès à l'API REST inclus */
private boolean apiAccess;
/** Module fédération multi-org inclus (ENTERPRISE) */
private boolean federationAccess;
/** Support prioritaire inclus */
private boolean supportPrioritaire;
/** SLA garanti (ex: "99.0%", "99.9%") */
private String slaGaranti;
/** Nombre maximum d'administrateurs (-1 = illimité) */
private int maxAdmins;
public FormuleAbonnementResponse() {}
// ─── Getters & Setters ───────────────────────────────────────────────────────
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
public String getLibelle() { return libelle; }
public void setLibelle(String libelle) { this.libelle = libelle; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getPlage() { return plage; }
public void setPlage(String plage) { this.plage = plage; }
public String getPlageLibelle() { return plageLibelle; }
public void setPlageLibelle(String plageLibelle) { this.plageLibelle = plageLibelle; }
public int getMinMembres() { return minMembres; }
public void setMinMembres(int minMembres) { this.minMembres = minMembres; }
public int getMaxMembres() { return maxMembres; }
public void setMaxMembres(int maxMembres) { this.maxMembres = maxMembres; }
public BigDecimal getPrixMensuel() { return prixMensuel; }
public void setPrixMensuel(BigDecimal prixMensuel) { this.prixMensuel = prixMensuel; }
public BigDecimal getPrixAnnuel() { return prixAnnuel; }
public void setPrixAnnuel(BigDecimal prixAnnuel) { this.prixAnnuel = prixAnnuel; }
public int getOrdreAffichage() { return ordreAffichage; }
public void setOrdreAffichage(int ordreAffichage) { this.ordreAffichage = ordreAffichage; }
public String getPlanCommercial() { return planCommercial; }
public void setPlanCommercial(String planCommercial) { this.planCommercial = planCommercial; }
public String getNiveauReporting() { return niveauReporting; }
public void setNiveauReporting(String niveauReporting) { this.niveauReporting = niveauReporting; }
public boolean isApiAccess() { return apiAccess; }
public void setApiAccess(boolean apiAccess) { this.apiAccess = apiAccess; }
public boolean isFederationAccess() { return federationAccess; }
public void setFederationAccess(boolean federationAccess) { this.federationAccess = federationAccess; }
public boolean isSupportPrioritaire() { return supportPrioritaire; }
public void setSupportPrioritaire(boolean supportPrioritaire) { this.supportPrioritaire = supportPrioritaire; }
public String getSlaGaranti() { return slaGaranti; }
public void setSlaGaranti(String slaGaranti) { this.slaGaranti = slaGaranti; }
public int getMaxAdmins() { return maxAdmins; }
public void setMaxAdmins(int maxAdmins) { this.maxAdmins = maxAdmins; }
}

View File

@@ -0,0 +1,79 @@
package dev.lions.unionflow.server.api.dto.souscription;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* Requête de création d'une demande de souscription UnionFlow.
*
* <p>Envoyée par l'ADMIN_ORGANISATION lors de l'onboarding pour sélectionner
* la formule, la plage de membres, la période et le type de son organisation.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-30
*/
public class SouscriptionDemandeRequest {
/** Niveau de formule : BASIC, STANDARD, PREMIUM */
@NotBlank(message = "Le type de formule est obligatoire")
private String typeFormule;
/** Tranche de membres : PETITE, MOYENNE, GRANDE, TRES_GRANDE */
@NotBlank(message = "La plage de membres est obligatoire")
private String plageMembres;
/** Périodicité de facturation : MENSUEL, TRIMESTRIEL, SEMESTRIEL, ANNUEL */
@NotBlank(message = "Le type de période est obligatoire")
private String typePeriode;
/** Type d'organisation pour le calcul du coefficient : ASSOCIATION, MUTUELLE, COOPERATIVE, FEDERATION
* Optionnel — si absent, le service le dérive depuis l'entité Organisation. */
private String typeOrganisation;
/** UUID de l'organisation concernée */
@NotNull(message = "L'identifiant de l'organisation est obligatoire")
private String organisationId;
public SouscriptionDemandeRequest() {}
public String getTypeFormule() {
return typeFormule;
}
public void setTypeFormule(String typeFormule) {
this.typeFormule = typeFormule;
}
public String getPlageMembres() {
return plageMembres;
}
public void setPlageMembres(String plageMembres) {
this.plageMembres = plageMembres;
}
public String getTypePeriode() {
return typePeriode;
}
public void setTypePeriode(String typePeriode) {
this.typePeriode = typePeriode;
}
public String getTypeOrganisation() {
return typeOrganisation;
}
public void setTypeOrganisation(String typeOrganisation) {
this.typeOrganisation = typeOrganisation;
}
public String getOrganisationId() {
return organisationId;
}
public void setOrganisationId(String organisationId) {
this.organisationId = organisationId;
}
}

View File

@@ -0,0 +1,155 @@
package dev.lions.unionflow.server.api.dto.souscription;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* Réponse décrivant l'état courant d'une souscription UnionFlow.
*
* <p>Retournée par les endpoints de création de demande, de consultation
* et d'initiation du paiement Wave.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-30
*/
public class SouscriptionStatutResponse {
private String souscriptionId;
private String statutValidation;
private String statutLibelle;
private String typeFormule;
private String plageMembres;
private String plageLibelle;
private String typePeriode;
private String typeOrganisation;
private BigDecimal montantTotal;
private BigDecimal montantMensuelBase;
private BigDecimal coefficientApplique;
private String waveSessionId;
/** URL Wave à ouvrir dans le navigateur/WebView pour initier le paiement. */
private String waveLaunchUrl;
private LocalDate dateDebut;
private LocalDate dateFin;
private LocalDate dateValidation;
private String commentaireRejet;
private String organisationId;
private String organisationNom;
// ── Quota & features (Option C) ─────────────────────────────────────────
private Integer quotaMax;
private Integer quotaUtilise;
private Integer quotaRestant;
private boolean quotaDepasse;
private String planCommercial;
private boolean apiAccess;
private boolean federationAccess;
private boolean supportPrioritaire;
private String slaGaranti;
private Integer maxAdmins;
private String niveauReporting;
/** Nombre de jours avant expiration (négatif = déjà expiré) */
private long joursAvantExpiration;
/** Statut de la souscription : ACTIVE, EXPIREE, SUSPENDUE, EN_ATTENTE, RESILIEE */
private String statut;
public SouscriptionStatutResponse() {}
// ─── Getters & Setters ───────────────────────────────────────────────────────
public String getSouscriptionId() { return souscriptionId; }
public void setSouscriptionId(String souscriptionId) { this.souscriptionId = souscriptionId; }
public String getStatutValidation() { return statutValidation; }
public void setStatutValidation(String statutValidation) { this.statutValidation = statutValidation; }
public String getStatutLibelle() { return statutLibelle; }
public void setStatutLibelle(String statutLibelle) { this.statutLibelle = statutLibelle; }
public String getTypeFormule() { return typeFormule; }
public void setTypeFormule(String typeFormule) { this.typeFormule = typeFormule; }
public String getPlageMembres() { return plageMembres; }
public void setPlageMembres(String plageMembres) { this.plageMembres = plageMembres; }
public String getPlageLibelle() { return plageLibelle; }
public void setPlageLibelle(String plageLibelle) { this.plageLibelle = plageLibelle; }
public String getTypePeriode() { return typePeriode; }
public void setTypePeriode(String typePeriode) { this.typePeriode = typePeriode; }
public String getTypeOrganisation() { return typeOrganisation; }
public void setTypeOrganisation(String typeOrganisation) { this.typeOrganisation = typeOrganisation; }
public BigDecimal getMontantTotal() { return montantTotal; }
public void setMontantTotal(BigDecimal montantTotal) { this.montantTotal = montantTotal; }
public BigDecimal getMontantMensuelBase() { return montantMensuelBase; }
public void setMontantMensuelBase(BigDecimal montantMensuelBase) { this.montantMensuelBase = montantMensuelBase; }
public BigDecimal getCoefficientApplique() { return coefficientApplique; }
public void setCoefficientApplique(BigDecimal coefficientApplique) { this.coefficientApplique = coefficientApplique; }
public String getWaveSessionId() { return waveSessionId; }
public void setWaveSessionId(String waveSessionId) { this.waveSessionId = waveSessionId; }
public String getWaveLaunchUrl() { return waveLaunchUrl; }
public void setWaveLaunchUrl(String waveLaunchUrl) { this.waveLaunchUrl = waveLaunchUrl; }
public LocalDate getDateDebut() { return dateDebut; }
public void setDateDebut(LocalDate dateDebut) { this.dateDebut = dateDebut; }
public LocalDate getDateFin() { return dateFin; }
public void setDateFin(LocalDate dateFin) { this.dateFin = dateFin; }
public LocalDate getDateValidation() { return dateValidation; }
public void setDateValidation(LocalDate dateValidation) { this.dateValidation = dateValidation; }
public String getCommentaireRejet() { return commentaireRejet; }
public void setCommentaireRejet(String commentaireRejet) { this.commentaireRejet = commentaireRejet; }
public String getOrganisationId() { return organisationId; }
public void setOrganisationId(String organisationId) { this.organisationId = organisationId; }
public String getOrganisationNom() { return organisationNom; }
public void setOrganisationNom(String organisationNom) { this.organisationNom = organisationNom; }
public Integer getQuotaMax() { return quotaMax; }
public void setQuotaMax(Integer quotaMax) { this.quotaMax = quotaMax; }
public Integer getQuotaUtilise() { return quotaUtilise; }
public void setQuotaUtilise(Integer quotaUtilise) { this.quotaUtilise = quotaUtilise; }
public Integer getQuotaRestant() { return quotaRestant; }
public void setQuotaRestant(Integer quotaRestant) { this.quotaRestant = quotaRestant; }
public boolean isQuotaDepasse() { return quotaDepasse; }
public void setQuotaDepasse(boolean quotaDepasse) { this.quotaDepasse = quotaDepasse; }
public String getPlanCommercial() { return planCommercial; }
public void setPlanCommercial(String planCommercial) { this.planCommercial = planCommercial; }
public boolean isApiAccess() { return apiAccess; }
public void setApiAccess(boolean apiAccess) { this.apiAccess = apiAccess; }
public boolean isFederationAccess() { return federationAccess; }
public void setFederationAccess(boolean federationAccess) { this.federationAccess = federationAccess; }
public boolean isSupportPrioritaire() { return supportPrioritaire; }
public void setSupportPrioritaire(boolean supportPrioritaire) { this.supportPrioritaire = supportPrioritaire; }
public String getSlaGaranti() { return slaGaranti; }
public void setSlaGaranti(String slaGaranti) { this.slaGaranti = slaGaranti; }
public Integer getMaxAdmins() { return maxAdmins; }
public void setMaxAdmins(Integer maxAdmins) { this.maxAdmins = maxAdmins; }
public String getNiveauReporting() { return niveauReporting; }
public void setNiveauReporting(String niveauReporting) { this.niveauReporting = niveauReporting; }
public long getJoursAvantExpiration() { return joursAvantExpiration; }
public void setJoursAvantExpiration(long joursAvantExpiration) { this.joursAvantExpiration = joursAvantExpiration; }
public String getStatut() { return statut; }
public void setStatut(String statut) { this.statut = statut; }
}

View File

@@ -0,0 +1,64 @@
package dev.lions.unionflow.server.api.enums.abonnement;
/**
* Tranches de taille d'organisation pour le calcul tarifaire UnionFlow.
*
* <p>Chaque plage définit un intervalle [min, max] de membres.
* La plage TRES_GRANDE est sans limite supérieure (Integer.MAX_VALUE).
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-30
*/
public enum PlageMembres {
PETITE("Petite structure (1100 membres)", 1, 100),
MOYENNE("Moyenne structure (101500 membres)", 101, 500),
GRANDE("Grande structure (5012 000 membres)", 501, 2000),
TRES_GRANDE("Très grande structure (2 000+ membres)", 2001, Integer.MAX_VALUE);
private final String libelle;
private final int min;
private final int max;
PlageMembres(String libelle, int min, int max) {
this.libelle = libelle;
this.min = min;
this.max = max;
}
/**
* Détermine la plage tarifaire correspondant à un nombre de membres donné.
*
* @param nombre nombre de membres de l'organisation
* @return la plage correspondante (TRES_GRANDE si aucune ne correspond)
*/
public static PlageMembres fromNombreMembres(int nombre) {
for (PlageMembres p : values()) {
if (nombre >= p.min && nombre <= p.max) {
return p;
}
}
return TRES_GRANDE;
}
public String getLibelle() {
return libelle;
}
public int getMin() {
return min;
}
/**
* Retourne le max de membres pour la plage.
* Retourne -1 pour TRES_GRANDE (illimité) afin de faciliter la sérialisation JSON.
*/
public int getMaxAffichage() {
return this == TRES_GRANDE ? -1 : max;
}
public int getMax() {
return max;
}
}

View File

@@ -1,6 +1,7 @@
package dev.lions.unionflow.server.api.enums.abonnement;
public enum StatutSouscription {
EN_ATTENTE("En attente de validation"),
ACTIVE("Active"),
EXPIREE("Expirée"),
SUSPENDUE("Suspendue — quota dépassé ou impayé"),

View File

@@ -0,0 +1,43 @@
package dev.lions.unionflow.server.api.enums.abonnement;
/**
* Cycle de vie de validation d'une souscription UnionFlow.
*
* <p>Transitions valides :
* <pre>
* EN_ATTENTE_PAIEMENT → PAIEMENT_INITIE → PAIEMENT_CONFIRME → VALIDEE
* ↘ REJETEE
* </pre>
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-30
*/
public enum StatutValidationSouscription {
EN_ATTENTE_PAIEMENT("En attente de paiement — souscription créée"),
PAIEMENT_INITIE("Paiement initié — session Wave ouverte"),
PAIEMENT_CONFIRME("Paiement confirmé — en attente de validation SuperAdmin"),
VALIDEE("Validée — compte activé"),
REJETEE("Rejetée — souscription refusée");
private final String libelle;
StatutValidationSouscription(String libelle) {
this.libelle = libelle;
}
public String getLibelle() {
return libelle;
}
/** Indique si la souscription est dans un état terminal (non modifiable). */
public boolean isTerminal() {
return this == VALIDEE || this == REJETEE;
}
/** Indique si un paiement Wave peut être initié depuis cet état. */
public boolean peutInitierPaiement() {
return this == EN_ATTENTE_PAIEMENT;
}
}

View File

@@ -1,17 +1,23 @@
package dev.lions.unionflow.server.api.enums.abonnement;
/**
* Énumération des types de formules d'abonnement UnionFlow
* Niveaux de formule d'abonnement UnionFlow.
*
* <p>Trois niveaux disponibles, chacun applicable à toutes les plages de taille
* ({@link PlageMembres}). Les prix de base sont définis dans la matrice tarifaire
* et varient selon la plage et le type d'organisation.
*
* <p>Migration depuis l'ancien enum : STARTER→BASIC, CRYSTAL supprimé.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-10
* @version 2.0
* @since 2026-03-30
*/
public enum TypeFormule {
STARTER("Starter — 1 à 50 membres — 5 000 XOF/mois"),
STANDARD("Standard — 51 à 200 membres — 7 000 XOF/mois"),
PREMIUM("Premium — 201 à 500 membres — 9 000 XOF/mois"),
CRYSTAL("Crystal — 501+ membres — 10 000 XOF/mois");
BASIC("Basic"),
STANDARD("Standard"),
PREMIUM("Premium");
private final String libelle;

View File

@@ -0,0 +1,51 @@
package dev.lions.unionflow.server.api.enums.abonnement;
import java.math.BigDecimal;
/**
* Catégorie tarifaire d'une organisation pour le calcul du prix de souscription.
*
* <p>Distinct de {@code TypeOrganisation} (catégorisation métier).
* Chaque valeur porte deux coefficients multiplicateurs :
* <ul>
* <li>{@code coefficientBase} — appliqué aux formules BASIC et STANDARD</li>
* <li>{@code coefficientPremium} — appliqué uniquement à la formule PREMIUM</li>
* </ul>
*
* <p>Exemple : FEDERATION paye le tarif normal (×1.0) en BASIC/STANDARD
* mais un coefficient ×1.5 en PREMIUM (fonctionnalités multi-entités).
*/
public enum TypeOrganisationFacturation {
ASSOCIATION("Association / ONG locale", new BigDecimal("1.0"), new BigDecimal("1.0")),
MUTUELLE("Mutuelle (santé, fonctionnaires, scolaires)", new BigDecimal("1.2"), new BigDecimal("1.2")),
COOPERATIVE("Coopérative / Microfinance", new BigDecimal("1.3"), new BigDecimal("1.3")),
FEDERATION("Fédération / Grande ONG", new BigDecimal("1.0"), new BigDecimal("1.5"));
private final String libelle;
private final BigDecimal coefficientBase;
private final BigDecimal coefficientPremium;
TypeOrganisationFacturation(String libelle, BigDecimal coefficientBase, BigDecimal coefficientPremium) {
this.libelle = libelle;
this.coefficientBase = coefficientBase;
this.coefficientPremium = coefficientPremium;
}
/**
* Retourne le coefficient tarifaire approprié selon le code de formule.
*
* @param typeFormule code de la formule (BASIC, STANDARD, PREMIUM)
* @return coefficient multiplicateur du prix mensuel de base
*/
public BigDecimal getCoefficient(String typeFormule) {
if ("PREMIUM".equals(typeFormule) && this == FEDERATION) {
return coefficientPremium;
}
return coefficientBase;
}
public String getLibelle() { return libelle; }
public BigDecimal getCoefficientBase() { return coefficientBase; }
public BigDecimal getCoefficientPremium() { return coefficientPremium; }
}

View File

@@ -1,12 +1,50 @@
package dev.lions.unionflow.server.api.enums.abonnement;
import java.math.BigDecimal;
/**
* Périodes d'abonnement UnionFlow avec remises associées.
*
* <p>Le {@code coefficient} est multiplié par le prix mensuel de base,
* puis par le nombre de mois pour obtenir le montant total.
*
* <ul>
* <li>MENSUEL : prix normal × 1 mois</li>
* <li>TRIMESTRIEL : prix × 0.95 × 3 mois (5% de remise)</li>
* <li>SEMESTRIEL : prix × 0.90 × 6 mois (10% de remise)</li>
* <li>ANNUEL : prix × 0.80 × 12 mois (20% de remise, équivaut à 2 mois offerts)</li>
* </ul>
*
* @author UnionFlow Team
* @version 2.0
* @since 2026-03-30
*/
public enum TypePeriodeAbonnement {
MENSUEL("Mensuel"),
ANNUEL("Annuel — 2 mois offerts");
MENSUEL("Mensuel", 1, new BigDecimal("1.00")),
TRIMESTRIEL("Trimestriel — 5% de remise", 3, new BigDecimal("0.95")),
SEMESTRIEL("Semestriel — 10% de remise", 6, new BigDecimal("0.90")),
ANNUEL("Annuel — 20% de remise (2 mois offerts)", 12, new BigDecimal("0.80"));
private final String libelle;
private final int nombreMois;
private final BigDecimal coefficient;
TypePeriodeAbonnement(String libelle) { this.libelle = libelle; }
TypePeriodeAbonnement(String libelle, int nombreMois, BigDecimal coefficient) {
this.libelle = libelle;
this.nombreMois = nombreMois;
this.coefficient = coefficient;
}
public String getLibelle() { return libelle; }
public String getLibelle() {
return libelle;
}
public int getNombreMois() {
return nombreMois;
}
public BigDecimal getCoefficient() {
return coefficient;
}
}

View File

@@ -8,6 +8,7 @@ package dev.lions.unionflow.server.api.enums.membre;
* @since 2025-01-10
*/
public enum StatutMembre {
INVITE("Invité — en attente d'acceptation"),
EN_ATTENTE_VALIDATION("En attente de validation"),
ACTIF("Actif"),
INACTIF("Inactif — cotisations en retard"),
@@ -15,7 +16,8 @@ public enum StatutMembre {
DEMISSIONNAIRE("Démissionnaire"),
RADIE("Radié — exclusion définitive"),
HONORAIRE("Honoraire — sans cotisation obligatoire"),
DECEDE("Décédé — archivage / gestion ayants droit");
DECEDE("Décédé — archivage / gestion ayants droit"),
ARCHIVE("Archivé — données conservées, accès révoqué");
private final String libelle;

View File

@@ -1,21 +1,26 @@
package dev.lions.unionflow.server.api.enums.organisation;
/**
* Énumération des types d'organisations supportés par UnionFlow
* Types d'organisations supportées par UnionFlow.
*
* <p>Utilisé pour la <strong>catégorisation métier</strong> des organisations
* (Lions Club, Mutuelle santé, ONG, etc.). Ne pas confondre avec
* {@link TypeOrganisationFacturation} qui porte les coefficients tarifaires.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-10
*/
public enum TypeOrganisation {
ASSOCIATION("Association"),
MUTUELLE_EPARGNE_CREDIT("Mutuelle d'épargne et de crédit"),
MUTUELLE_SANTE("Mutuelle de santé"),
TONTINE("Tontine / épargne rotative"),
ONG("ONG / Association humanitaire"),
COOPERATIVE_AGRICOLE("Coopérative agricole / production"),
ASSOCIATION_PROFESSIONNELLE("Association professionnelle / Ordre"),
ASSOCIATION_COMMUNAUTAIRE("Association communautaire / quartier"),
COOPERATIVE_AGRICOLE("Coopérative agricole"),
ASSOCIATION_PROFESSIONNELLE("Association professionnelle"),
ASSOCIATION_COMMUNAUTAIRE("Association communautaire"),
ORGANISATION_RELIGIEUSE("Organisation religieuse"),
FEDERATION("Fédération / Union d'associations"),
SYNDICAT("Syndicat non partisan"),

View File

@@ -40,7 +40,7 @@ public final class TestDataFactory {
String email) {
return new MembreSummaryResponse(
UUID.randomUUID(), numero, prenom, nom, email, "0102030405", "Profession", "ACTIF", "Actif", "success", true,
List.of("MEMBRE"), null, null);
List.of("MEMBRE"), null, null, null);
}
public static CreateMembreRequest createCreateMembreRequest(int age) {

View File

@@ -132,6 +132,102 @@ class CotisationResponseTest {
}
}
@Nested
@DisplayName("Méthodes de formatage des montants")
class MethodesFormatageMontants {
@Test
@DisplayName("getMontantDuFormatte retourne 0 FCFA quand montantDu null")
void testMontantDuNull() {
CotisationResponse r = CotisationResponse.builder().build();
assertThat(r.getMontantDuFormatte()).isEqualTo("0 FCFA");
}
@Test
@DisplayName("getMontantDuFormatte formate avec devise par défaut FCFA")
void testMontantDuSansDevise() {
CotisationResponse r = CotisationResponse.builder().montantDu(BigDecimal.valueOf(5000)).build();
assertThat(r.getMontantDuFormatte()).isEqualTo("5,000 FCFA");
}
@Test
@DisplayName("getMontantDuFormatte formate avec codeDevise fourni")
void testMontantDuAvecDevise() {
CotisationResponse r = CotisationResponse.builder().montantDu(BigDecimal.valueOf(5000)).codeDevise("XOF").build();
assertThat(r.getMontantDuFormatte()).isEqualTo("5,000 XOF");
}
@Test
@DisplayName("getMontantPayeFormatte retourne 0 FCFA quand montantPaye null")
void testMontantPayeNull() {
CotisationResponse r = CotisationResponse.builder().build();
assertThat(r.getMontantPayeFormatte()).isEqualTo("0 FCFA");
}
@Test
@DisplayName("getMontantPayeFormatte formate avec devise par défaut FCFA")
void testMontantPayeSansDevise() {
CotisationResponse r = CotisationResponse.builder().montantPaye(BigDecimal.valueOf(2000)).build();
assertThat(r.getMontantPayeFormatte()).isEqualTo("2,000 FCFA");
}
@Test
@DisplayName("getMontantPayeFormatte formate avec codeDevise fourni")
void testMontantPayeAvecDevise() {
CotisationResponse r = CotisationResponse.builder().montantPaye(BigDecimal.valueOf(2000)).codeDevise("EUR").build();
assertThat(r.getMontantPayeFormatte()).isEqualTo("2,000 EUR");
}
@Test
@DisplayName("getMontantRestantFormatte retourne 0 FCFA quand montantRestant null")
void testMontantRestantNull() {
CotisationResponse r = CotisationResponse.builder().build();
assertThat(r.getMontantRestantFormatte()).isEqualTo("0 FCFA");
}
@Test
@DisplayName("getMontantRestantFormatte formate avec devise par défaut FCFA")
void testMontantRestantSansDevise() {
CotisationResponse r = CotisationResponse.builder().montantRestant(BigDecimal.valueOf(3000)).build();
assertThat(r.getMontantRestantFormatte()).isEqualTo("3,000 FCFA");
}
@Test
@DisplayName("getMontantRestantFormatte formate avec codeDevise fourni")
void testMontantRestantAvecDevise() {
CotisationResponse r = CotisationResponse.builder().montantRestant(BigDecimal.valueOf(3000)).codeDevise("XOF").build();
assertThat(r.getMontantRestantFormatte()).isEqualTo("3,000 XOF");
}
@Test
@DisplayName("isMontantRestantPositif retourne false quand montantRestant null")
void testMontantRestantPositifNull() {
CotisationResponse r = CotisationResponse.builder().build();
assertThat(r.isMontantRestantPositif()).isFalse();
}
@Test
@DisplayName("isMontantRestantPositif retourne false quand montantRestant = 0")
void testMontantRestantPositifZero() {
CotisationResponse r = CotisationResponse.builder().montantRestant(BigDecimal.ZERO).build();
assertThat(r.isMontantRestantPositif()).isFalse();
}
@Test
@DisplayName("isMontantRestantPositif retourne false quand montantRestant négatif")
void testMontantRestantPositifNegatif() {
CotisationResponse r = CotisationResponse.builder().montantRestant(BigDecimal.valueOf(-100)).build();
assertThat(r.isMontantRestantPositif()).isFalse();
}
@Test
@DisplayName("isMontantRestantPositif retourne true quand montantRestant > 0")
void testMontantRestantPositifVrai() {
CotisationResponse r = CotisationResponse.builder().montantRestant(BigDecimal.valueOf(500)).build();
assertThat(r.isMontantRestantPositif()).isTrue();
}
}
@Nested
@DisplayName("Builder complet")
class BuilderComplet {

View File

@@ -492,4 +492,259 @@ class EvenementResponseTest {
assertThat(r.estComplet()).isFalse();
}
}
@Nested
@DisplayName("getTypeEvenementIcon")
class GetTypeEvenementIcon {
@Test
@DisplayName("retourne icône par défaut quand type null")
void testNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-calendar");
}
@Test
@DisplayName("ASSEMBLEE_GENERALE → pi pi-building")
void testAssembleeGenerale() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.ASSEMBLEE_GENERALE).build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-building");
}
@Test
@DisplayName("FORMATION → pi pi-book")
void testFormation() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.FORMATION).build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-book");
}
@Test
@DisplayName("REUNION_BUREAU → pi pi-users")
void testReunionBureau() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.REUNION_BUREAU).build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-users");
}
@Test
@DisplayName("CONFERENCE → pi pi-microphone")
void testConference() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.CONFERENCE).build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-microphone");
}
@Test
@DisplayName("ATELIER → pi pi-wrench")
void testAtelier() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.ATELIER).build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-wrench");
}
@Test
@DisplayName("CEREMONIE → pi pi-flag")
void testCeremonie() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.CEREMONIE).build();
assertThat(r.getTypeEvenementIcon()).isEqualTo("pi pi-flag");
}
@Test
@DisplayName("ACTIVITE_SOCIALE, ACTION_CARITATIVE, AUTRE → pi pi-calendar")
void testAutres() {
assertThat(EvenementResponse.builder().typeEvenement(TypeEvenementMetier.ACTIVITE_SOCIALE).build().getTypeEvenementIcon()).isEqualTo("pi pi-calendar");
assertThat(EvenementResponse.builder().typeEvenement(TypeEvenementMetier.ACTION_CARITATIVE).build().getTypeEvenementIcon()).isEqualTo("pi pi-calendar");
assertThat(EvenementResponse.builder().typeEvenement(TypeEvenementMetier.AUTRE).build().getTypeEvenementIcon()).isEqualTo("pi pi-calendar");
}
}
@Nested
@DisplayName("getTypeEvenementSeverity")
class GetTypeEvenementSeverity {
@Test
@DisplayName("retourne secondary quand type null")
void testNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getTypeEvenementSeverity()).isEqualTo("secondary");
}
@Test
@DisplayName("ASSEMBLEE_GENERALE → warning")
void testAssembleeGenerale() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.ASSEMBLEE_GENERALE).build();
assertThat(r.getTypeEvenementSeverity()).isEqualTo("warning");
}
@Test
@DisplayName("FORMATION → info")
void testFormation() {
EvenementResponse r = EvenementResponse.builder().typeEvenement(TypeEvenementMetier.FORMATION).build();
assertThat(r.getTypeEvenementSeverity()).isEqualTo("info");
}
@Test
@DisplayName("autres types → secondary")
void testAutres() {
for (TypeEvenementMetier t : new TypeEvenementMetier[]{
TypeEvenementMetier.ACTIVITE_SOCIALE, TypeEvenementMetier.ACTION_CARITATIVE,
TypeEvenementMetier.REUNION_BUREAU, TypeEvenementMetier.CONFERENCE,
TypeEvenementMetier.ATELIER, TypeEvenementMetier.CEREMONIE, TypeEvenementMetier.AUTRE}) {
assertThat(EvenementResponse.builder().typeEvenement(t).build().getTypeEvenementSeverity())
.as("Type: %s", t).isEqualTo("secondary");
}
}
}
@Nested
@DisplayName("getStatutSeverity")
class GetStatutSeverity {
@Test
@DisplayName("retourne secondary quand statut null")
void testNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getStatutSeverity()).isEqualTo("secondary");
}
@Test
@DisplayName("PLANIFIE → info")
void testPlanifie() {
assertThat(EvenementResponse.builder().statut(StatutEvenement.PLANIFIE).build().getStatutSeverity()).isEqualTo("info");
}
@Test
@DisplayName("EN_COURS → success")
void testEnCours() {
assertThat(EvenementResponse.builder().statut(StatutEvenement.EN_COURS).build().getStatutSeverity()).isEqualTo("success");
}
@Test
@DisplayName("TERMINE et CONFIRME → secondary")
void testTermineConfirme() {
assertThat(EvenementResponse.builder().statut(StatutEvenement.TERMINE).build().getStatutSeverity()).isEqualTo("secondary");
assertThat(EvenementResponse.builder().statut(StatutEvenement.CONFIRME).build().getStatutSeverity()).isEqualTo("secondary");
}
@Test
@DisplayName("ANNULE → danger")
void testAnnule() {
assertThat(EvenementResponse.builder().statut(StatutEvenement.ANNULE).build().getStatutSeverity()).isEqualTo("danger");
}
@Test
@DisplayName("REPORTE → warning")
void testReporte() {
assertThat(EvenementResponse.builder().statut(StatutEvenement.REPORTE).build().getStatutSeverity()).isEqualTo("warning");
}
}
@Nested
@DisplayName("getStatutIcon")
class GetStatutIcon {
@Test
@DisplayName("retourne pi pi-question quand statut null")
void testNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getStatutIcon()).isEqualTo("pi pi-question");
}
@Test
@DisplayName("tous les statuts retournent la bonne icône")
void testTousStatuts() {
assertThat(EvenementResponse.builder().statut(StatutEvenement.PLANIFIE).build().getStatutIcon()).isEqualTo("pi pi-clock");
assertThat(EvenementResponse.builder().statut(StatutEvenement.CONFIRME).build().getStatutIcon()).isEqualTo("pi pi-check-circle");
assertThat(EvenementResponse.builder().statut(StatutEvenement.EN_COURS).build().getStatutIcon()).isEqualTo("pi pi-play");
assertThat(EvenementResponse.builder().statut(StatutEvenement.TERMINE).build().getStatutIcon()).isEqualTo("pi pi-check");
assertThat(EvenementResponse.builder().statut(StatutEvenement.ANNULE).build().getStatutIcon()).isEqualTo("pi pi-times");
assertThat(EvenementResponse.builder().statut(StatutEvenement.REPORTE).build().getStatutIcon()).isEqualTo("pi pi-refresh");
}
}
@Nested
@DisplayName("getPrioriteSeverity")
class GetPrioriteSeverity {
@Test
@DisplayName("retourne secondary quand priorite null")
void testNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getPrioriteSeverity()).isEqualTo("secondary");
}
@Test
@DisplayName("toutes les priorités retournent la bonne sévérité")
void testToutesPriorites() {
assertThat(EvenementResponse.builder().priorite(PrioriteEvenement.CRITIQUE).build().getPrioriteSeverity()).isEqualTo("danger");
assertThat(EvenementResponse.builder().priorite(PrioriteEvenement.HAUTE).build().getPrioriteSeverity()).isEqualTo("warning");
assertThat(EvenementResponse.builder().priorite(PrioriteEvenement.NORMALE).build().getPrioriteSeverity()).isEqualTo("info");
assertThat(EvenementResponse.builder().priorite(PrioriteEvenement.BASSE).build().getPrioriteSeverity()).isEqualTo("secondary");
}
}
@Nested
@DisplayName("getDateDebutFormatee, getHeureDebutFormatee, getHeureFinFormatee, getBudgetFormate")
class MethodesFormatage {
@Test
@DisplayName("getDateDebutFormatee retourne — quand null")
void testDateDebutNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getDateDebutFormatee()).isEqualTo("");
}
@Test
@DisplayName("getDateDebutFormatee formate en dd/MM/yyyy")
void testDateDebut() {
EvenementResponse r = EvenementResponse.builder().dateDebut(java.time.LocalDate.of(2026, 4, 7)).build();
assertThat(r.getDateDebutFormatee()).isEqualTo("07/04/2026");
}
@Test
@DisplayName("getHeureDebutFormatee retourne — quand null")
void testHeureDebutNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getHeureDebutFormatee()).isEqualTo("");
}
@Test
@DisplayName("getHeureDebutFormatee formate en HH:mm")
void testHeureDebut() {
EvenementResponse r = EvenementResponse.builder().heureDebut(java.time.LocalTime.of(9, 30)).build();
assertThat(r.getHeureDebutFormatee()).isEqualTo("09:30");
}
@Test
@DisplayName("getHeureFinFormatee retourne — quand null")
void testHeureFinNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getHeureFinFormatee()).isEqualTo("");
}
@Test
@DisplayName("getHeureFinFormatee formate en HH:mm")
void testHeureFin() {
EvenementResponse r = EvenementResponse.builder().heureFin(java.time.LocalTime.of(18, 0)).build();
assertThat(r.getHeureFinFormatee()).isEqualTo("18:00");
}
@Test
@DisplayName("getBudgetFormate retourne — quand budget null")
void testBudgetNull() {
EvenementResponse r = EvenementResponse.builder().build();
assertThat(r.getBudgetFormate()).isEqualTo("");
}
@Test
@DisplayName("getBudgetFormate formate avec devise par défaut FCFA")
void testBudgetSansDevise() {
EvenementResponse r = EvenementResponse.builder().budget(java.math.BigDecimal.valueOf(50000)).build();
assertThat(r.getBudgetFormate()).isEqualTo("50,000 FCFA");
}
@Test
@DisplayName("getBudgetFormate formate avec codeDevise fourni")
void testBudgetAvecDevise() {
EvenementResponse r = EvenementResponse.builder().budget(java.math.BigDecimal.valueOf(50000)).codeDevise("EUR").build();
assertThat(r.getBudgetFormate()).isEqualTo("50,000 EUR");
}
}
}

View File

@@ -23,7 +23,7 @@ class MembreSearchResultDTOTest {
private static MembreSummaryResponse unMembre() {
return new MembreSummaryResponse(
UUID.randomUUID(), null, "Prenom", "Nom", null, null, null, null, null, null, true, List.of(), null, null);
UUID.randomUUID(), null, "Prenom", "Nom", null, null, null, null, null, null, true, List.of(), null, null, null);
}
@Nested

View File

@@ -0,0 +1,266 @@
package dev.lions.unionflow.server.api.dto.membre;
import static org.assertj.core.api.Assertions.assertThat;
import dev.lions.unionflow.server.api.dto.membre.response.MembreSummaryResponse;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@DisplayName("Tests MembreSummaryResponse — méthodes calculées")
class MembreSummaryResponseTest {
private static MembreSummaryResponse make(String statut, List<String> roles) {
return new MembreSummaryResponse(
UUID.randomUUID(), "UF-001", "Jean", "Dupont", "jean@test.com",
"0100000000", "Dev", statut, statut + "_libelle", statut + "_severity",
"ACTIF".equals(statut), roles, null, null, null);
}
// ── nomComplet ─────────────────────────────────────────────────────────
@Nested
@DisplayName("nomComplet()")
class NomComplet {
@Test
@DisplayName("prenom + nom concaténés")
void nominal() {
MembreSummaryResponse r = make("ACTIF", List.of());
assertThat(r.nomComplet()).isEqualTo("Jean Dupont");
}
@Test
@DisplayName("prenom null → uniquement le nom")
void prenomNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, "Dupont", null,
null, null, null, null, null, false, List.of(), null, null, null);
assertThat(r.nomComplet()).isEqualTo("Dupont");
}
@Test
@DisplayName("nom null → uniquement le prenom")
void nomNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, "Jean", null, null,
null, null, null, null, null, false, List.of(), null, null, null);
assertThat(r.nomComplet()).isEqualTo("Jean");
}
@Test
@DisplayName("prenom et nom null → chaîne vide")
void tousNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, null, null,
null, null, null, null, null, false, List.of(), null, null, null);
assertThat(r.nomComplet()).isEmpty();
}
}
// ── Alias statut ───────────────────────────────────────────────────────
@Nested
@DisplayName("aliases statut*()")
class AliasStatut {
@Test
@DisplayName("statut() retourne statutCompte")
void statut() {
MembreSummaryResponse r = make("ACTIF", List.of());
assertThat(r.statut()).isEqualTo(r.statutCompte());
}
@Test
@DisplayName("statutLibelle() retourne statutCompteLibelle")
void statutLibelle() {
MembreSummaryResponse r = make("ACTIF", List.of());
assertThat(r.statutLibelle()).isEqualTo(r.statutCompteLibelle());
}
@Test
@DisplayName("statutSeverity() retourne statutCompteSeverity")
void statutSeverity() {
MembreSummaryResponse r = make("ACTIF", List.of());
assertThat(r.statutSeverity()).isEqualTo(r.statutCompteSeverity());
}
}
// ── statutIcon ─────────────────────────────────────────────────────────
@Nested
@DisplayName("statutIcon()")
class StatutIcon {
@Test void actif() { assertThat(make("ACTIF", List.of()).statutIcon()).isEqualTo("pi pi-check-circle"); }
@Test void inactif() { assertThat(make("INACTIF", List.of()).statutIcon()).isEqualTo("pi pi-pause-circle"); }
@Test void suspendu() { assertThat(make("SUSPENDU", List.of()).statutIcon()).isEqualTo("pi pi-ban"); }
@Test void radie() { assertThat(make("RADIE", List.of()).statutIcon()).isEqualTo("pi pi-times-circle"); }
@Test
@DisplayName("statut inconnu → pi pi-question-circle")
void inconnu() {
assertThat(make("AUTRE", List.of()).statutIcon()).isEqualTo("pi pi-question-circle");
}
@Test
@DisplayName("statut null → pi pi-question-circle")
void statutNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, null, null,
null, null, null, null, null, false, List.of(), null, null, null);
assertThat(r.statutIcon()).isEqualTo("pi pi-question-circle");
}
}
// ── typeMembre ─────────────────────────────────────────────────────────
@Nested
@DisplayName("typeMembre()")
class TypeMembre {
@Test void president() { assertThat(make("ACTIF", List.of("PRESIDENT")).typeMembre()).isEqualTo("Président"); }
@Test void vicePresident() { assertThat(make("ACTIF", List.of("VICE_PRESIDENT")).typeMembre()).isEqualTo("Vice-Président"); }
@Test void secretaire() { assertThat(make("ACTIF", List.of("SECRETAIRE")).typeMembre()).isEqualTo("Secrétaire"); }
@Test void tresorier() { assertThat(make("ACTIF", List.of("TRESORIER")).typeMembre()).isEqualTo("Trésorier"); }
@Test void adminOrg() { assertThat(make("ACTIF", List.of("ADMIN_ORGANISATION")).typeMembre()).isEqualTo("Administrateur"); }
@Test void moderateur() { assertThat(make("ACTIF", List.of("MODERATEUR")).typeMembre()).isEqualTo("Modérateur"); }
@Test void membreActif() { assertThat(make("ACTIF", List.of("MEMBRE_ACTIF")).typeMembre()).isEqualTo("Membre"); }
@Test void rolesVide() { assertThat(make("ACTIF", List.of()).typeMembre()).isEqualTo("Membre"); }
@Test void rolesNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, null, null,
null, null, null, null, null, false, null, null, null, null);
assertThat(r.typeMembre()).isEqualTo("Membre");
}
}
// ── typeSeverity ───────────────────────────────────────────────────────
@Nested
@DisplayName("typeSeverity()")
class TypeSeverity {
@Test void president() { assertThat(make("ACTIF", List.of("PRESIDENT")).typeSeverity()).isEqualTo("primary"); }
@Test void vicePresident() { assertThat(make("ACTIF", List.of("VICE_PRESIDENT")).typeSeverity()).isEqualTo("primary"); }
@Test void secretaire() { assertThat(make("ACTIF", List.of("SECRETAIRE")).typeSeverity()).isEqualTo("info"); }
@Test void tresorier() { assertThat(make("ACTIF", List.of("TRESORIER")).typeSeverity()).isEqualTo("warning"); }
@Test void adminOrg() { assertThat(make("ACTIF", List.of("ADMIN_ORGANISATION")).typeSeverity()).isEqualTo("danger"); }
@Test void moderateur() { assertThat(make("ACTIF", List.of("MODERATEUR")).typeSeverity()).isEqualTo("warning"); }
@Test void roleDefault() { assertThat(make("ACTIF", List.of("MEMBRE_ACTIF")).typeSeverity()).isEqualTo("secondary"); }
@Test void rolesVide() { assertThat(make("ACTIF", List.of()).typeSeverity()).isEqualTo("secondary"); }
@Test void rolesNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, null, null,
null, null, null, null, null, false, null, null, null, null);
assertThat(r.typeSeverity()).isEqualTo("secondary");
}
}
// ── typeIcon ───────────────────────────────────────────────────────────
@Nested
@DisplayName("typeIcon()")
class TypeIcon {
@Test void president() { assertThat(make("ACTIF", List.of("PRESIDENT")).typeIcon()).isEqualTo("pi pi-star"); }
@Test void vicePresident() { assertThat(make("ACTIF", List.of("VICE_PRESIDENT")).typeIcon()).isEqualTo("pi pi-star"); }
@Test void secretaire() { assertThat(make("ACTIF", List.of("SECRETAIRE")).typeIcon()).isEqualTo("pi pi-file-edit"); }
@Test void tresorier() { assertThat(make("ACTIF", List.of("TRESORIER")).typeIcon()).isEqualTo("pi pi-wallet"); }
@Test void adminOrg() { assertThat(make("ACTIF", List.of("ADMIN_ORGANISATION")).typeIcon()).isEqualTo("pi pi-shield"); }
@Test void moderateur() { assertThat(make("ACTIF", List.of("MODERATEUR")).typeIcon()).isEqualTo("pi pi-wrench"); }
@Test void roleDefault() { assertThat(make("ACTIF", List.of("MEMBRE_ACTIF")).typeIcon()).isEqualTo("pi pi-user"); }
@Test void rolesVide() { assertThat(make("ACTIF", List.of()).typeIcon()).isEqualTo("pi pi-user"); }
@Test void rolesNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, null, null,
null, null, null, null, null, false, null, null, null, null);
assertThat(r.typeIcon()).isEqualTo("pi pi-user");
}
}
// ── dateAdhesion ───────────────────────────────────────────────────────
@Nested
@DisplayName("dateAdhesion()")
class DateAdhesion {
@Test
@DisplayName("dateAdhesion est accessible via le composant record")
void accesseur() {
LocalDate date = LocalDate.of(2024, 3, 15);
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, "Jean", "Dupont", null,
null, null, "ACTIF", null, null, true, List.of(), null, null, date);
assertThat(r.dateAdhesion()).isEqualTo(date);
}
@Test
@DisplayName("dateAdhesion null par défaut")
void nullParDefaut() {
assertThat(make("ACTIF", List.of()).dateAdhesion()).isNull();
}
}
// ── Getters JavaBean ───────────────────────────────────────────────────
@Nested
@DisplayName("getters JavaBean (compatibilité JSF EL)")
class GettersJavaBean {
@Test
@DisplayName("getId/getNumeroMembre/getPrenom/getNom/getEmail/getTelephone/getProfession")
void champsSimples() {
LocalDate date = LocalDate.of(2024, 1, 1);
UUID orgId = UUID.randomUUID();
UUID id = UUID.randomUUID();
MembreSummaryResponse r = new MembreSummaryResponse(
id, "UF-001", "Jean", "Dupont", "jean@test.com",
"0100000000", "Dev", "ACTIF", "Actif", "success",
true, List.of("MEMBRE_ACTIF"), orgId, "Org Test", date);
assertThat(r.getId()).isEqualTo(id);
assertThat(r.getNumeroMembre()).isEqualTo("UF-001");
assertThat(r.getPrenom()).isEqualTo("Jean");
assertThat(r.getNom()).isEqualTo("Dupont");
assertThat(r.getEmail()).isEqualTo("jean@test.com");
assertThat(r.getTelephone()).isEqualTo("0100000000");
assertThat(r.getProfession()).isEqualTo("Dev");
assertThat(r.getStatutCompte()).isEqualTo("ACTIF");
assertThat(r.getStatutCompteLibelle()).isEqualTo("Actif");
assertThat(r.getStatutCompteSeverity()).isEqualTo("success");
assertThat(r.getActif()).isTrue();
assertThat(r.isActif()).isTrue();
assertThat(r.getRoles()).containsExactly("MEMBRE_ACTIF");
assertThat(r.getOrganisationId()).isEqualTo(orgId);
assertThat(r.getOrganisationNom()).isEqualTo("Org Test");
assertThat(r.getDateAdhesion()).isEqualTo(date);
}
@Test
@DisplayName("isActif false quand actif null")
void isActifNull() {
MembreSummaryResponse r = new MembreSummaryResponse(
UUID.randomUUID(), null, null, null, null,
null, null, null, null, null, null, null, null, null, null);
assertThat(r.isActif()).isFalse();
assertThat(r.getActif()).isNull();
}
@Test
@DisplayName("getters calculés délèguent aux méthodes calculées")
void gettersCalcules() {
MembreSummaryResponse r = make("ACTIF", List.of("PRESIDENT"));
assertThat(r.getNomComplet()).isEqualTo(r.nomComplet());
assertThat(r.getStatut()).isEqualTo(r.statut());
assertThat(r.getStatutLibelle()).isEqualTo(r.statutLibelle());
assertThat(r.getStatutSeverity()).isEqualTo(r.statutSeverity());
assertThat(r.getStatutIcon()).isEqualTo(r.statutIcon());
assertThat(r.getTypeMembre()).isEqualTo(r.typeMembre());
assertThat(r.getTypeSeverity()).isEqualTo(r.typeSeverity());
assertThat(r.getTypeIcon()).isEqualTo(r.typeIcon());
}
}
}

View File

@@ -158,7 +158,7 @@ class CreateMembreRequestTest {
.nom("User")
.email("test@example.com")
.dateNaissance(LocalDate.of(1990, 1, 1))
.telephoneWave("+22177123456789")
.telephoneWave("+221771234567890123456") // 22 chars > max 20
.build();
Set<ConstraintViolation<CreateMembreRequest>> violations = validator.validate(request);

View File

@@ -52,6 +52,13 @@ class CreateOrganisationRequestTest {
.notes("Organisation de référence dans le secteur")
.latitude(new BigDecimal("14.6937"))
.longitude(new BigDecimal("-17.4441"))
.adresse("123 Rue des Entreprises, Plateau")
.ville("Dakar")
.region("Dakar")
.pays("Sénégal")
.codePostal("BP 3456")
.organisationPublique(true)
.accepteNouveauxMembres(false)
.build();
assertThat(request).isNotNull();
@@ -76,6 +83,13 @@ class CreateOrganisationRequestTest {
assertThat(request.certifications()).isEqualTo("ISO 9001");
assertThat(request.latitude()).isEqualByComparingTo(new BigDecimal("14.6937"));
assertThat(request.longitude()).isEqualByComparingTo(new BigDecimal("-17.4441"));
assertThat(request.adresse()).isEqualTo("123 Rue des Entreprises, Plateau");
assertThat(request.ville()).isEqualTo("Dakar");
assertThat(request.region()).isEqualTo("Dakar");
assertThat(request.pays()).isEqualTo("Sénégal");
assertThat(request.codePostal()).isEqualTo("BP 3456");
assertThat(request.organisationPublique()).isTrue();
assertThat(request.accepteNouveauxMembres()).isFalse();
}
@Test
@@ -191,6 +205,59 @@ class CreateOrganisationRequestTest {
assertThat(violations).isEmpty();
}
@Test
void testBuilder_AdresseFields() {
CreateOrganisationRequest request = CreateOrganisationRequest.builder()
.nom("ONG Dakar Centre")
.email("contact@ongdc.sn")
.adresse("Rue 10, Médina")
.ville("Dakar")
.region("Dakar")
.pays("Sénégal")
.codePostal("BP 1234")
.organisationPublique(false)
.accepteNouveauxMembres(true)
.build();
assertThat(request.adresse()).isEqualTo("Rue 10, Médina");
assertThat(request.ville()).isEqualTo("Dakar");
assertThat(request.region()).isEqualTo("Dakar");
assertThat(request.pays()).isEqualTo("Sénégal");
assertThat(request.codePostal()).isEqualTo("BP 1234");
assertThat(request.organisationPublique()).isFalse();
assertThat(request.accepteNouveauxMembres()).isTrue();
}
@Test
void testBuilder_AdresseFieldsNull_WhenNotProvided() {
CreateOrganisationRequest request = CreateOrganisationRequest.builder()
.nom("Org Minimal")
.email("min@org.sn")
.build();
assertThat(request.adresse()).isNull();
assertThat(request.ville()).isNull();
assertThat(request.region()).isNull();
assertThat(request.pays()).isNull();
assertThat(request.codePostal()).isNull();
assertThat(request.organisationPublique()).isNull();
assertThat(request.accepteNouveauxMembres()).isNull();
}
@Test
void testValidation_CodePostalTooLong() {
CreateOrganisationRequest request = CreateOrganisationRequest.builder()
.nom("Organisation Test")
.email("test@org.sn")
.codePostal("A".repeat(21))
.build();
Set<ConstraintViolation<CreateOrganisationRequest>> violations = validator.validate(request);
assertThat(violations).isNotEmpty();
assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("codePostal"));
}
@Test
void testEquals() {
CreateOrganisationRequest request1 = CreateOrganisationRequest.builder()

View File

@@ -52,6 +52,13 @@ class UpdateOrganisationRequestTest {
.notes("Notes mises à jour")
.latitude(new BigDecimal("12.5"))
.longitude(new BigDecimal("-15.5"))
.adresse("Avenue Cheikh Anta Diop, Fann")
.ville("Dakar")
.region("Dakar")
.pays("Sénégal")
.codePostal("BP 5000")
.organisationPublique(false)
.accepteNouveauxMembres(true)
.build();
assertThat(request).isNotNull();
@@ -69,6 +76,13 @@ class UpdateOrganisationRequestTest {
assertThat(request.budgetAnnuel()).isEqualByComparingTo(new BigDecimal("75000000.00"));
assertThat(request.cotisationObligatoire()).isFalse();
assertThat(request.certifications()).isEqualTo("ISO 14001");
assertThat(request.adresse()).isEqualTo("Avenue Cheikh Anta Diop, Fann");
assertThat(request.ville()).isEqualTo("Dakar");
assertThat(request.region()).isEqualTo("Dakar");
assertThat(request.pays()).isEqualTo("Sénégal");
assertThat(request.codePostal()).isEqualTo("BP 5000");
assertThat(request.organisationPublique()).isFalse();
assertThat(request.accepteNouveauxMembres()).isTrue();
}
@Test
@@ -168,6 +182,43 @@ class UpdateOrganisationRequestTest {
assertThat(violations).isEmpty();
}
@Test
void testBuilder_AdresseFields() {
UpdateOrganisationRequest request = UpdateOrganisationRequest.builder()
.nom("Association Abidjan Nord")
.email("contact@abn.ci")
.adresse("Quartier Deux Plateaux, Rue des Jardins")
.ville("Abidjan")
.region("Lagunes")
.pays("Côte d'Ivoire")
.codePostal("01 BP 1234")
.organisationPublique(true)
.accepteNouveauxMembres(true)
.build();
assertThat(request.adresse()).isEqualTo("Quartier Deux Plateaux, Rue des Jardins");
assertThat(request.ville()).isEqualTo("Abidjan");
assertThat(request.region()).isEqualTo("Lagunes");
assertThat(request.pays()).isEqualTo("Côte d'Ivoire");
assertThat(request.codePostal()).isEqualTo("01 BP 1234");
assertThat(request.organisationPublique()).isTrue();
assertThat(request.accepteNouveauxMembres()).isTrue();
}
@Test
void testValidation_VilleTooLong() {
UpdateOrganisationRequest request = UpdateOrganisationRequest.builder()
.nom("Test Org")
.email("test@org.ci")
.ville("A".repeat(101))
.build();
Set<ConstraintViolation<UpdateOrganisationRequest>> violations = validator.validate(request);
assertThat(violations).isNotEmpty();
assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("ville"));
}
@Test
void testEquals() {
UpdateOrganisationRequest request1 = UpdateOrganisationRequest.builder()

View File

@@ -65,10 +65,12 @@ class InitierPaiementEnLigneRequestTest {
Set<ConstraintViolation<InitierPaiementEnLigneRequest>> violations = validator.validate(request);
// cotisationId et methodePaiement sont obligatoires
// numeroTelephone est optionnel (web QR sans restriction de payeur)
assertThat(violations).isNotEmpty();
assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("cotisationId"));
assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("methodePaiement"));
assertThat(violations).anyMatch(v -> v.getPropertyPath().toString().equals("numeroTelephone"));
assertThat(violations).noneMatch(v -> v.getPropertyPath().toString().equals("numeroTelephone"));
}
@Test

View File

@@ -0,0 +1,112 @@
package dev.lions.unionflow.server.api.dto.paiement.response;
import static org.assertj.core.api.Assertions.assertThat;
import java.math.BigDecimal;
import java.util.UUID;
import org.junit.jupiter.api.Test;
class IntentionStatutResponseTest {
@Test
void testBuilder_AllFields() {
UUID intentionId = UUID.randomUUID();
IntentionStatutResponse response = IntentionStatutResponse.builder()
.intentionId(intentionId)
.statut("COMPLETEE")
.waveLaunchUrl("https://pay.wave.com/c/cos-abc123")
.waveCheckoutSessionId("cos-abc123")
.waveTransactionId("TCN4Y4ZC3FM")
.montant(new BigDecimal("5000"))
.referenceCotisation("COT-2025-001")
.message("Paiement confirmé !")
.build();
assertThat(response).isNotNull();
assertThat(response.intentionId()).isEqualTo(intentionId);
assertThat(response.statut()).isEqualTo("COMPLETEE");
assertThat(response.waveLaunchUrl()).isEqualTo("https://pay.wave.com/c/cos-abc123");
assertThat(response.waveCheckoutSessionId()).isEqualTo("cos-abc123");
assertThat(response.waveTransactionId()).isEqualTo("TCN4Y4ZC3FM");
assertThat(response.montant()).isEqualByComparingTo(new BigDecimal("5000"));
assertThat(response.referenceCotisation()).isEqualTo("COT-2025-001");
assertThat(response.message()).isEqualTo("Paiement confirmé !");
}
@Test
void testBuilder_EnCours() {
UUID intentionId = UUID.randomUUID();
IntentionStatutResponse response = IntentionStatutResponse.builder()
.intentionId(intentionId)
.statut("EN_COURS")
.waveLaunchUrl("https://pay.wave.com/c/cos-xyz789")
.waveCheckoutSessionId("cos-xyz789")
.montant(new BigDecimal("10000"))
.message("En attente de confirmation Wave...")
.build();
assertThat(response.intentionId()).isEqualTo(intentionId);
assertThat(response.statut()).isEqualTo("EN_COURS");
assertThat(response.waveLaunchUrl()).isEqualTo("https://pay.wave.com/c/cos-xyz789");
assertThat(response.waveTransactionId()).isNull();
assertThat(response.referenceCotisation()).isNull();
}
@Test
void testBuilder_Expiree() {
UUID intentionId = UUID.randomUUID();
IntentionStatutResponse response = IntentionStatutResponse.builder()
.intentionId(intentionId)
.statut("EXPIREE")
.montant(new BigDecimal("2500"))
.message("Session Wave expirée")
.build();
assertThat(response.statut()).isEqualTo("EXPIREE");
assertThat(response.waveLaunchUrl()).isNull();
assertThat(response.waveCheckoutSessionId()).isNull();
assertThat(response.waveTransactionId()).isNull();
}
@Test
void testEquals_SameFields() {
UUID intentionId = UUID.randomUUID();
IntentionStatutResponse r1 = IntentionStatutResponse.builder()
.intentionId(intentionId)
.statut("EN_COURS")
.waveLaunchUrl("https://pay.wave.com/c/cos-abc")
.waveCheckoutSessionId("cos-abc")
.montant(new BigDecimal("5000"))
.build();
IntentionStatutResponse r2 = IntentionStatutResponse.builder()
.intentionId(intentionId)
.statut("EN_COURS")
.waveLaunchUrl("https://pay.wave.com/c/cos-abc")
.waveCheckoutSessionId("cos-abc")
.montant(new BigDecimal("5000"))
.build();
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testToString() {
IntentionStatutResponse response = IntentionStatutResponse.builder()
.intentionId(UUID.randomUUID())
.statut("COMPLETEE")
.waveTransactionId("TCN4Y4ZC3FM")
.build();
String str = response.toString();
assertThat(str).isNotNull();
assertThat(str).contains("IntentionStatutResponse");
assertThat(str).contains("COMPLETEE");
assertThat(str).contains("TCN4Y4ZC3FM");
}
}

View File

@@ -0,0 +1,172 @@
package dev.lions.unionflow.server.api.dto.souscription;
import static org.assertj.core.api.Assertions.assertThat;
import java.math.BigDecimal;
import java.time.LocalDate;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@DisplayName("Tests des DTOs souscription")
class SouscriptionDtosTest {
@Nested
@DisplayName("SouscriptionDemandeRequest")
class SouscriptionDemandeRequestTest {
@Test
@DisplayName("Getters/Setters fonctionnent correctement")
void testGettersSetters() {
SouscriptionDemandeRequest req = new SouscriptionDemandeRequest();
req.setTypeFormule("BASIC");
req.setPlageMembres("PETITE");
req.setTypePeriode("MENSUEL");
req.setTypeOrganisation("ASSOCIATION");
req.setOrganisationId("org-uuid-123");
assertThat(req.getTypeFormule()).isEqualTo("BASIC");
assertThat(req.getPlageMembres()).isEqualTo("PETITE");
assertThat(req.getTypePeriode()).isEqualTo("MENSUEL");
assertThat(req.getTypeOrganisation()).isEqualTo("ASSOCIATION");
assertThat(req.getOrganisationId()).isEqualTo("org-uuid-123");
}
}
@Nested
@DisplayName("SouscriptionStatutResponse")
class SouscriptionStatutResponseTest {
@Test
@DisplayName("Getters/Setters fonctionnent correctement")
void testGettersSetters() {
LocalDate today = LocalDate.now();
SouscriptionStatutResponse resp = new SouscriptionStatutResponse();
resp.setSouscriptionId("sous-uuid-456");
resp.setStatutValidation("EN_ATTENTE_PAIEMENT");
resp.setStatutLibelle("En attente de paiement");
resp.setTypeFormule("BASIC");
resp.setPlageMembres("PETITE");
resp.setPlageLibelle("Petite structure (1100 membres)");
resp.setTypePeriode("MENSUEL");
resp.setTypeOrganisation("ASSOCIATION");
resp.setMontantTotal(new BigDecimal("3000"));
resp.setMontantMensuelBase(new BigDecimal("3000"));
resp.setCoefficientApplique(new BigDecimal("1.0"));
resp.setWaveSessionId("wave-session-789");
resp.setWaveLaunchUrl("https://pay.wave.com/c/session-789");
resp.setDateDebut(today);
resp.setDateFin(today.plusMonths(1));
resp.setDateValidation(null);
resp.setCommentaireRejet(null);
resp.setOrganisationId("org-uuid-123");
resp.setOrganisationNom("Association Test");
resp.setStatut("ACTIVE");
assertThat(resp.getSouscriptionId()).isEqualTo("sous-uuid-456");
assertThat(resp.getStatutValidation()).isEqualTo("EN_ATTENTE_PAIEMENT");
assertThat(resp.getStatutLibelle()).isEqualTo("En attente de paiement");
assertThat(resp.getTypeFormule()).isEqualTo("BASIC");
assertThat(resp.getPlageMembres()).isEqualTo("PETITE");
assertThat(resp.getPlageLibelle()).isEqualTo("Petite structure (1100 membres)");
assertThat(resp.getTypePeriode()).isEqualTo("MENSUEL");
assertThat(resp.getTypeOrganisation()).isEqualTo("ASSOCIATION");
assertThat(resp.getMontantTotal()).isEqualByComparingTo(new BigDecimal("3000"));
assertThat(resp.getMontantMensuelBase()).isEqualByComparingTo(new BigDecimal("3000"));
assertThat(resp.getCoefficientApplique()).isEqualByComparingTo(new BigDecimal("1.0"));
assertThat(resp.getWaveSessionId()).isEqualTo("wave-session-789");
assertThat(resp.getWaveLaunchUrl()).isEqualTo("https://pay.wave.com/c/session-789");
assertThat(resp.getDateDebut()).isEqualTo(today);
assertThat(resp.getDateFin()).isEqualTo(today.plusMonths(1));
assertThat(resp.getDateValidation()).isNull();
assertThat(resp.getCommentaireRejet()).isNull();
assertThat(resp.getOrganisationId()).isEqualTo("org-uuid-123");
assertThat(resp.getOrganisationNom()).isEqualTo("Association Test");
assertThat(resp.getStatut()).isEqualTo("ACTIVE");
}
@Test
@DisplayName("Champs Option C (quota + plan commercial) fonctionnent correctement")
void testOptionCFields() {
SouscriptionStatutResponse resp = new SouscriptionStatutResponse();
resp.setQuotaMax(200);
resp.setQuotaUtilise(75);
resp.setQuotaRestant(125);
resp.setQuotaDepasse(false);
resp.setPlanCommercial("ENTERPRISE");
resp.setApiAccess(true);
resp.setFederationAccess(true);
resp.setSupportPrioritaire(true);
resp.setSlaGaranti("99.9%");
resp.setMaxAdmins(10);
resp.setNiveauReporting("AVANCE");
resp.setJoursAvantExpiration(180L);
assertThat(resp.getQuotaMax()).isEqualTo(200);
assertThat(resp.getQuotaUtilise()).isEqualTo(75);
assertThat(resp.getQuotaRestant()).isEqualTo(125);
assertThat(resp.isQuotaDepasse()).isFalse();
assertThat(resp.getPlanCommercial()).isEqualTo("ENTERPRISE");
assertThat(resp.isApiAccess()).isTrue();
assertThat(resp.isFederationAccess()).isTrue();
assertThat(resp.isSupportPrioritaire()).isTrue();
assertThat(resp.getSlaGaranti()).isEqualTo("99.9%");
assertThat(resp.getMaxAdmins()).isEqualTo(10);
assertThat(resp.getNiveauReporting()).isEqualTo("AVANCE");
assertThat(resp.getJoursAvantExpiration()).isEqualTo(180L);
}
}
@Nested
@DisplayName("FormuleAbonnementResponse")
class FormuleAbonnementResponseTest {
@Test
@DisplayName("Getters/Setters fonctionnent correctement")
void testGettersSetters() {
FormuleAbonnementResponse resp = new FormuleAbonnementResponse();
resp.setCode("BASIC");
resp.setLibelle("Basic");
resp.setDescription("Formule de base pour petites structures");
resp.setPlage("PETITE");
resp.setPlageLibelle("Petite structure (1100 membres)");
resp.setMinMembres(1);
resp.setMaxMembres(100);
resp.setPrixMensuel(new BigDecimal("3000"));
resp.setPrixAnnuel(new BigDecimal("28800"));
resp.setOrdreAffichage(1);
assertThat(resp.getCode()).isEqualTo("BASIC");
assertThat(resp.getLibelle()).isEqualTo("Basic");
assertThat(resp.getDescription()).isEqualTo("Formule de base pour petites structures");
assertThat(resp.getPlage()).isEqualTo("PETITE");
assertThat(resp.getPlageLibelle()).isEqualTo("Petite structure (1100 membres)");
assertThat(resp.getMinMembres()).isEqualTo(1);
assertThat(resp.getMaxMembres()).isEqualTo(100);
assertThat(resp.getPrixMensuel()).isEqualByComparingTo(new BigDecimal("3000"));
assertThat(resp.getPrixAnnuel()).isEqualByComparingTo(new BigDecimal("28800"));
assertThat(resp.getOrdreAffichage()).isEqualTo(1);
}
@Test
@DisplayName("Champs Option C fonctionnent correctement")
void testOptionCFields() {
FormuleAbonnementResponse resp = new FormuleAbonnementResponse();
resp.setPlanCommercial("ENTERPRISE");
resp.setNiveauReporting("AVANCE");
resp.setApiAccess(true);
resp.setFederationAccess(true);
resp.setSupportPrioritaire(true);
resp.setSlaGaranti("99.9%");
resp.setMaxAdmins(10);
assertThat(resp.getPlanCommercial()).isEqualTo("ENTERPRISE");
assertThat(resp.getNiveauReporting()).isEqualTo("AVANCE");
assertThat(resp.isApiAccess()).isTrue();
assertThat(resp.isFederationAccess()).isTrue();
assertThat(resp.isSupportPrioritaire()).isTrue();
assertThat(resp.getSlaGaranti()).isEqualTo("99.9%");
assertThat(resp.getMaxAdmins()).isEqualTo(10);
}
}
}

View File

@@ -139,10 +139,9 @@ class EnumsRefactoringTest {
@DisplayName("TypeFormule - Tous les types disponibles")
void testTypeFormuleTousLesTypes() {
// Given & When & Then
assertThat(TypeFormule.STARTER.getLibelle()).contains("Starter");
assertThat(TypeFormule.BASIC.getLibelle()).contains("Basic");
assertThat(TypeFormule.STANDARD.getLibelle()).contains("Standard");
assertThat(TypeFormule.PREMIUM.getLibelle()).contains("Premium");
assertThat(TypeFormule.CRYSTAL.getLibelle()).contains("Crystal");
}
@Test

View File

@@ -0,0 +1,125 @@
package dev.lions.unionflow.server.api.enums.abonnement;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
@DisplayName("Tests pour PlageMembres")
class PlageMembresTest {
@Test
@DisplayName("Test de base - enum peut être utilisé")
void testEnumUtilisable() {
assertThat(PlageMembres.PETITE).isNotNull();
assertThat(PlageMembres.TRES_GRANDE).isNotNull();
}
@Nested
@DisplayName("Tests des valeurs enum")
class TestsValeursEnum {
@Test
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
PlageMembres[] values = PlageMembres.values();
assertThat(values).hasSize(4);
assertThat(values).containsExactly(
PlageMembres.PETITE,
PlageMembres.MOYENNE,
PlageMembres.GRANDE,
PlageMembres.TRES_GRANDE);
}
@Test
@DisplayName("Test valueOf - toutes les constantes")
void testValueOf() {
assertThat(PlageMembres.valueOf("PETITE")).isEqualTo(PlageMembres.PETITE);
assertThat(PlageMembres.valueOf("MOYENNE")).isEqualTo(PlageMembres.MOYENNE);
assertThat(PlageMembres.valueOf("GRANDE")).isEqualTo(PlageMembres.GRANDE);
assertThat(PlageMembres.valueOf("TRES_GRANDE")).isEqualTo(PlageMembres.TRES_GRANDE);
assertThatThrownBy(() -> PlageMembres.valueOf("INEXISTANT"))
.isInstanceOf(IllegalArgumentException.class);
}
@ParameterizedTest
@EnumSource(PlageMembres.class)
@DisplayName("Test getLibelle pour toutes les valeurs - non null et non vide")
void testGetLibelle(PlageMembres plage) {
assertThat(plage.getLibelle()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("Test bornes min/max de chaque plage")
void testBornesMinMax() {
assertThat(PlageMembres.PETITE.getMin()).isEqualTo(1);
assertThat(PlageMembres.PETITE.getMax()).isEqualTo(100);
assertThat(PlageMembres.MOYENNE.getMin()).isEqualTo(101);
assertThat(PlageMembres.MOYENNE.getMax()).isEqualTo(500);
assertThat(PlageMembres.GRANDE.getMin()).isEqualTo(501);
assertThat(PlageMembres.GRANDE.getMax()).isEqualTo(2000);
assertThat(PlageMembres.TRES_GRANDE.getMin()).isEqualTo(2001);
assertThat(PlageMembres.TRES_GRANDE.getMax()).isEqualTo(Integer.MAX_VALUE);
}
@Test
@DisplayName("Test getMaxAffichage - TRES_GRANDE retourne -1")
void testGetMaxAffichage() {
assertThat(PlageMembres.PETITE.getMaxAffichage()).isEqualTo(100);
assertThat(PlageMembres.MOYENNE.getMaxAffichage()).isEqualTo(500);
assertThat(PlageMembres.GRANDE.getMaxAffichage()).isEqualTo(2000);
assertThat(PlageMembres.TRES_GRANDE.getMaxAffichage()).isEqualTo(-1);
}
}
@Nested
@DisplayName("Tests de fromNombreMembres")
class TestsFromNombreMembres {
@Test
@DisplayName("Valeurs limites - PETITE (1-100)")
void testPetite() {
assertThat(PlageMembres.fromNombreMembres(1)).isEqualTo(PlageMembres.PETITE);
assertThat(PlageMembres.fromNombreMembres(50)).isEqualTo(PlageMembres.PETITE);
assertThat(PlageMembres.fromNombreMembres(100)).isEqualTo(PlageMembres.PETITE);
}
@Test
@DisplayName("Valeurs limites - MOYENNE (101-500)")
void testMoyenne() {
assertThat(PlageMembres.fromNombreMembres(101)).isEqualTo(PlageMembres.MOYENNE);
assertThat(PlageMembres.fromNombreMembres(300)).isEqualTo(PlageMembres.MOYENNE);
assertThat(PlageMembres.fromNombreMembres(500)).isEqualTo(PlageMembres.MOYENNE);
}
@Test
@DisplayName("Valeurs limites - GRANDE (501-2000)")
void testGrande() {
assertThat(PlageMembres.fromNombreMembres(501)).isEqualTo(PlageMembres.GRANDE);
assertThat(PlageMembres.fromNombreMembres(1000)).isEqualTo(PlageMembres.GRANDE);
assertThat(PlageMembres.fromNombreMembres(2000)).isEqualTo(PlageMembres.GRANDE);
}
@Test
@DisplayName("Valeurs limites - TRES_GRANDE (2001+)")
void testTresGrande() {
assertThat(PlageMembres.fromNombreMembres(2001)).isEqualTo(PlageMembres.TRES_GRANDE);
assertThat(PlageMembres.fromNombreMembres(10000)).isEqualTo(PlageMembres.TRES_GRANDE);
assertThat(PlageMembres.fromNombreMembres(Integer.MAX_VALUE)).isEqualTo(PlageMembres.TRES_GRANDE);
}
@Test
@DisplayName("Nombre négatif - retourne TRES_GRANDE par défaut")
void testNombreNegatif() {
assertThat(PlageMembres.fromNombreMembres(-1)).isEqualTo(PlageMembres.TRES_GRANDE);
}
}
}

View File

@@ -27,8 +27,9 @@ class StatutSouscriptionTest {
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
StatutSouscription[] values = StatutSouscription.values();
assertThat(values).hasSize(4);
assertThat(values).hasSize(5);
assertThat(values).containsExactly(
StatutSouscription.EN_ATTENTE,
StatutSouscription.ACTIVE,
StatutSouscription.EXPIREE,
StatutSouscription.SUSPENDUE,
@@ -38,6 +39,7 @@ class StatutSouscriptionTest {
@Test
@DisplayName("Test valueOf - toutes les constantes")
void testValueOf() {
assertThat(StatutSouscription.valueOf("EN_ATTENTE")).isEqualTo(StatutSouscription.EN_ATTENTE);
assertThat(StatutSouscription.valueOf("ACTIVE")).isEqualTo(StatutSouscription.ACTIVE);
assertThat(StatutSouscription.valueOf("EXPIREE")).isEqualTo(StatutSouscription.EXPIREE);
assertThat(StatutSouscription.valueOf("SUSPENDUE")).isEqualTo(StatutSouscription.SUSPENDUE);

View File

@@ -0,0 +1,89 @@
package dev.lions.unionflow.server.api.enums.abonnement;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
@DisplayName("Tests pour StatutValidationSouscription")
class StatutValidationSouscriptionTest {
@Test
@DisplayName("Test de base - enum peut être utilisé")
void testEnumUtilisable() {
assertThat(StatutValidationSouscription.EN_ATTENTE_PAIEMENT).isNotNull();
assertThat(StatutValidationSouscription.VALIDEE).isNotNull();
}
@Nested
@DisplayName("Tests des valeurs enum")
class TestsValeursEnum {
@Test
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
StatutValidationSouscription[] values = StatutValidationSouscription.values();
assertThat(values).hasSize(5);
assertThat(values).containsExactly(
StatutValidationSouscription.EN_ATTENTE_PAIEMENT,
StatutValidationSouscription.PAIEMENT_INITIE,
StatutValidationSouscription.PAIEMENT_CONFIRME,
StatutValidationSouscription.VALIDEE,
StatutValidationSouscription.REJETEE);
}
@Test
@DisplayName("Test valueOf - toutes les constantes")
void testValueOf() {
assertThat(StatutValidationSouscription.valueOf("EN_ATTENTE_PAIEMENT"))
.isEqualTo(StatutValidationSouscription.EN_ATTENTE_PAIEMENT);
assertThat(StatutValidationSouscription.valueOf("PAIEMENT_INITIE"))
.isEqualTo(StatutValidationSouscription.PAIEMENT_INITIE);
assertThat(StatutValidationSouscription.valueOf("PAIEMENT_CONFIRME"))
.isEqualTo(StatutValidationSouscription.PAIEMENT_CONFIRME);
assertThat(StatutValidationSouscription.valueOf("VALIDEE"))
.isEqualTo(StatutValidationSouscription.VALIDEE);
assertThat(StatutValidationSouscription.valueOf("REJETEE"))
.isEqualTo(StatutValidationSouscription.REJETEE);
assertThatThrownBy(() -> StatutValidationSouscription.valueOf("INEXISTANT"))
.isInstanceOf(IllegalArgumentException.class);
}
@ParameterizedTest
@EnumSource(StatutValidationSouscription.class)
@DisplayName("Test getLibelle pour toutes les valeurs - non null et non vide")
void testGetLibelle(StatutValidationSouscription statut) {
assertThat(statut.getLibelle()).isNotNull().isNotEmpty();
}
}
@Nested
@DisplayName("Tests des méthodes métier")
class TestsMethodesMetier {
@Test
@DisplayName("isTerminal - seuls VALIDEE et REJETEE sont terminaux")
void testIsTerminal() {
assertThat(StatutValidationSouscription.EN_ATTENTE_PAIEMENT.isTerminal()).isFalse();
assertThat(StatutValidationSouscription.PAIEMENT_INITIE.isTerminal()).isFalse();
assertThat(StatutValidationSouscription.PAIEMENT_CONFIRME.isTerminal()).isFalse();
assertThat(StatutValidationSouscription.VALIDEE.isTerminal()).isTrue();
assertThat(StatutValidationSouscription.REJETEE.isTerminal()).isTrue();
}
@Test
@DisplayName("peutInitierPaiement - seul EN_ATTENTE_PAIEMENT permet d'initier")
void testPeutInitierPaiement() {
assertThat(StatutValidationSouscription.EN_ATTENTE_PAIEMENT.peutInitierPaiement()).isTrue();
assertThat(StatutValidationSouscription.PAIEMENT_INITIE.peutInitierPaiement()).isFalse();
assertThat(StatutValidationSouscription.PAIEMENT_CONFIRME.peutInitierPaiement()).isFalse();
assertThat(StatutValidationSouscription.VALIDEE.peutInitierPaiement()).isFalse();
assertThat(StatutValidationSouscription.REJETEE.peutInitierPaiement()).isFalse();
}
}
}

View File

@@ -15,7 +15,7 @@ class TypeFormuleTest {
@Test
@DisplayName("Test de base - enum peut être utilisé")
void testEnumUtilisable() {
assertThat(TypeFormule.STARTER).isNotNull();
assertThat(TypeFormule.BASIC).isNotNull();
}
@Nested
@@ -26,21 +26,19 @@ class TypeFormuleTest {
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
TypeFormule[] values = TypeFormule.values();
assertThat(values).hasSize(4);
assertThat(values).hasSize(3);
assertThat(values).containsExactly(
TypeFormule.STARTER,
TypeFormule.BASIC,
TypeFormule.STANDARD,
TypeFormule.PREMIUM,
TypeFormule.CRYSTAL);
TypeFormule.PREMIUM);
}
@Test
@DisplayName("Test valueOf")
void testValueOf() {
assertThat(TypeFormule.valueOf("STARTER")).isEqualTo(TypeFormule.STARTER);
assertThat(TypeFormule.valueOf("BASIC")).isEqualTo(TypeFormule.BASIC);
assertThat(TypeFormule.valueOf("STANDARD")).isEqualTo(TypeFormule.STANDARD);
assertThat(TypeFormule.valueOf("PREMIUM")).isEqualTo(TypeFormule.PREMIUM);
assertThat(TypeFormule.valueOf("CRYSTAL")).isEqualTo(TypeFormule.CRYSTAL);
assertThatThrownBy(() -> TypeFormule.valueOf("INEXISTANT"))
.isInstanceOf(IllegalArgumentException.class);

View File

@@ -0,0 +1,124 @@
package dev.lions.unionflow.server.api.enums.abonnement;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.math.BigDecimal;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
@DisplayName("Tests pour TypeOrganisationFacturation")
class TypeOrganisationFacturationTest {
@Test
@DisplayName("Test de base - enum peut être utilisé")
void testEnumUtilisable() {
assertThat(TypeOrganisationFacturation.ASSOCIATION).isNotNull();
assertThat(TypeOrganisationFacturation.FEDERATION).isNotNull();
}
@Nested
@DisplayName("Tests des valeurs enum")
class TestsValeursEnum {
@Test
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
TypeOrganisationFacturation[] values = TypeOrganisationFacturation.values();
assertThat(values).hasSize(4);
assertThat(values).containsExactly(
TypeOrganisationFacturation.ASSOCIATION,
TypeOrganisationFacturation.MUTUELLE,
TypeOrganisationFacturation.COOPERATIVE,
TypeOrganisationFacturation.FEDERATION);
}
@Test
@DisplayName("Test valueOf - toutes les constantes")
void testValueOf() {
assertThat(TypeOrganisationFacturation.valueOf("ASSOCIATION"))
.isEqualTo(TypeOrganisationFacturation.ASSOCIATION);
assertThat(TypeOrganisationFacturation.valueOf("MUTUELLE"))
.isEqualTo(TypeOrganisationFacturation.MUTUELLE);
assertThat(TypeOrganisationFacturation.valueOf("COOPERATIVE"))
.isEqualTo(TypeOrganisationFacturation.COOPERATIVE);
assertThat(TypeOrganisationFacturation.valueOf("FEDERATION"))
.isEqualTo(TypeOrganisationFacturation.FEDERATION);
assertThatThrownBy(() -> TypeOrganisationFacturation.valueOf("INEXISTANT"))
.isInstanceOf(IllegalArgumentException.class);
}
@ParameterizedTest
@EnumSource(TypeOrganisationFacturation.class)
@DisplayName("Test getLibelle pour toutes les valeurs - non null et non vide")
void testGetLibelle(TypeOrganisationFacturation type) {
assertThat(type.getLibelle()).isNotNull().isNotEmpty();
}
@ParameterizedTest
@EnumSource(TypeOrganisationFacturation.class)
@DisplayName("Test coefficients - toujours positifs")
void testCoefficientsPositifs(TypeOrganisationFacturation type) {
assertThat(type.getCoefficientBase()).isGreaterThan(BigDecimal.ZERO);
assertThat(type.getCoefficientPremium()).isGreaterThan(BigDecimal.ZERO);
}
}
@Nested
@DisplayName("Tests de getCoefficient")
class TestsGetCoefficient {
@Test
@DisplayName("ASSOCIATION - coefficient 1.0 pour toutes les formules")
void testAssociation() {
assertThat(TypeOrganisationFacturation.ASSOCIATION.getCoefficient("BASIC"))
.isEqualByComparingTo(new BigDecimal("1.0"));
assertThat(TypeOrganisationFacturation.ASSOCIATION.getCoefficient("STANDARD"))
.isEqualByComparingTo(new BigDecimal("1.0"));
assertThat(TypeOrganisationFacturation.ASSOCIATION.getCoefficient("PREMIUM"))
.isEqualByComparingTo(new BigDecimal("1.0"));
}
@Test
@DisplayName("MUTUELLE - coefficient 1.2 pour toutes les formules")
void testMutuelle() {
assertThat(TypeOrganisationFacturation.MUTUELLE.getCoefficient("BASIC"))
.isEqualByComparingTo(new BigDecimal("1.2"));
assertThat(TypeOrganisationFacturation.MUTUELLE.getCoefficient("PREMIUM"))
.isEqualByComparingTo(new BigDecimal("1.2"));
}
@Test
@DisplayName("COOPERATIVE - coefficient 1.3 pour toutes les formules")
void testCooperative() {
assertThat(TypeOrganisationFacturation.COOPERATIVE.getCoefficient("BASIC"))
.isEqualByComparingTo(new BigDecimal("1.3"));
assertThat(TypeOrganisationFacturation.COOPERATIVE.getCoefficient("PREMIUM"))
.isEqualByComparingTo(new BigDecimal("1.3"));
}
@Test
@DisplayName("FEDERATION - coefficient 1.0 en BASIC/STANDARD, 1.5 en PREMIUM")
void testFederation() {
assertThat(TypeOrganisationFacturation.FEDERATION.getCoefficient("BASIC"))
.isEqualByComparingTo(new BigDecimal("1.0"));
assertThat(TypeOrganisationFacturation.FEDERATION.getCoefficient("STANDARD"))
.isEqualByComparingTo(new BigDecimal("1.0"));
assertThat(TypeOrganisationFacturation.FEDERATION.getCoefficient("PREMIUM"))
.isEqualByComparingTo(new BigDecimal("1.5"));
}
@Test
@DisplayName("Formule inconnue - utilise coefficientBase")
void testFormuleInconnue() {
assertThat(TypeOrganisationFacturation.FEDERATION.getCoefficient("STARTER"))
.isEqualByComparingTo(new BigDecimal("1.0"));
assertThat(TypeOrganisationFacturation.MUTUELLE.getCoefficient("INCONNU"))
.isEqualByComparingTo(new BigDecimal("1.2"));
}
}
}

View File

@@ -27,9 +27,11 @@ class TypePeriodeAbonnementTest {
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
TypePeriodeAbonnement[] values = TypePeriodeAbonnement.values();
assertThat(values).hasSize(2);
assertThat(values).hasSize(4);
assertThat(values).containsExactly(
TypePeriodeAbonnement.MENSUEL,
TypePeriodeAbonnement.TRIMESTRIEL,
TypePeriodeAbonnement.SEMESTRIEL,
TypePeriodeAbonnement.ANNUEL);
}
@@ -37,6 +39,8 @@ class TypePeriodeAbonnementTest {
@DisplayName("Test valueOf - toutes les constantes")
void testValueOf() {
assertThat(TypePeriodeAbonnement.valueOf("MENSUEL")).isEqualTo(TypePeriodeAbonnement.MENSUEL);
assertThat(TypePeriodeAbonnement.valueOf("TRIMESTRIEL")).isEqualTo(TypePeriodeAbonnement.TRIMESTRIEL);
assertThat(TypePeriodeAbonnement.valueOf("SEMESTRIEL")).isEqualTo(TypePeriodeAbonnement.SEMESTRIEL);
assertThat(TypePeriodeAbonnement.valueOf("ANNUEL")).isEqualTo(TypePeriodeAbonnement.ANNUEL);
assertThatThrownBy(() -> TypePeriodeAbonnement.valueOf("INEXISTANT"))
@@ -54,14 +58,36 @@ class TypePeriodeAbonnementTest {
@DisplayName("Test getLibelle valeurs exactes")
void testGetLibelleValeursExactes() {
assertThat(TypePeriodeAbonnement.MENSUEL.getLibelle()).isEqualTo("Mensuel");
assertThat(TypePeriodeAbonnement.ANNUEL.getLibelle()).isEqualTo("Annuel — 2 mois offerts");
assertThat(TypePeriodeAbonnement.TRIMESTRIEL.getLibelle()).contains("Trimestriel");
assertThat(TypePeriodeAbonnement.SEMESTRIEL.getLibelle()).contains("Semestriel");
assertThat(TypePeriodeAbonnement.ANNUEL.getLibelle()).contains("Annuel");
}
@Test
@DisplayName("Test name()")
void testName() {
assertThat(TypePeriodeAbonnement.MENSUEL.name()).isEqualTo("MENSUEL");
assertThat(TypePeriodeAbonnement.TRIMESTRIEL.name()).isEqualTo("TRIMESTRIEL");
assertThat(TypePeriodeAbonnement.SEMESTRIEL.name()).isEqualTo("SEMESTRIEL");
assertThat(TypePeriodeAbonnement.ANNUEL.name()).isEqualTo("ANNUEL");
}
@Test
@DisplayName("Test getNombreMois - durée en mois de chaque période")
void testGetNombreMois() {
assertThat(TypePeriodeAbonnement.MENSUEL.getNombreMois()).isEqualTo(1);
assertThat(TypePeriodeAbonnement.TRIMESTRIEL.getNombreMois()).isEqualTo(3);
assertThat(TypePeriodeAbonnement.SEMESTRIEL.getNombreMois()).isEqualTo(6);
assertThat(TypePeriodeAbonnement.ANNUEL.getNombreMois()).isEqualTo(12);
}
@Test
@DisplayName("Test getCoefficient - remise appliquée par période")
void testGetCoefficient() {
assertThat(TypePeriodeAbonnement.MENSUEL.getCoefficient()).isEqualByComparingTo("1.00");
assertThat(TypePeriodeAbonnement.TRIMESTRIEL.getCoefficient()).isEqualByComparingTo("0.95");
assertThat(TypePeriodeAbonnement.SEMESTRIEL.getCoefficient()).isEqualByComparingTo("0.90");
assertThat(TypePeriodeAbonnement.ANNUEL.getCoefficient()).isEqualByComparingTo("0.80");
}
}
}

View File

@@ -27,8 +27,9 @@ class StatutMembreTest {
@DisplayName("Test toutes les valeurs enum")
void testToutesValeurs() {
StatutMembre[] values = StatutMembre.values();
assertThat(values).hasSize(8);
assertThat(values).hasSize(10);
assertThat(values).contains(
StatutMembre.INVITE,
StatutMembre.EN_ATTENTE_VALIDATION,
StatutMembre.ACTIF,
StatutMembre.INACTIF,
@@ -36,7 +37,8 @@ class StatutMembreTest {
StatutMembre.DEMISSIONNAIRE,
StatutMembre.RADIE,
StatutMembre.HONORAIRE,
StatutMembre.DECEDE);
StatutMembre.DECEDE,
StatutMembre.ARCHIVE);
}
@Test

View File

@@ -7,7 +7,6 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EnumSource;
@DisplayName("Tests pour TypeOrganisation")
@@ -75,20 +74,6 @@ class TypeOrganisationTest {
assertThat(type.getLibelle()).isNotNull().isNotEmpty();
}
@ParameterizedTest
@CsvSource({
"LIONS_CLUB, Lions Club",
"ASSOCIATION, Association",
"ONG, ONG / Association humanitaire",
"ORGANISATION_RELIGIEUSE, Organisation religieuse",
"FEDERATION, Fédération / Union d'associations",
"AUTRE, Autre"
})
@DisplayName("Test getLibelle avec valeurs exactes")
void testGetLibelleValeursExactes(String name, String expectedLibelle) {
assertThat(TypeOrganisation.valueOf(name).getLibelle()).isEqualTo(expectedLibelle);
}
@Test
@DisplayName("Test name() pour toutes les constantes")
void testOrdinalEtName() {
@@ -101,4 +86,3 @@ class TypeOrganisationTest {
}
}
}