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:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
@@ -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 (1–100 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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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 (1–100 membres)", 1, 100),
|
||||
MOYENNE("Moyenne structure (101–500 membres)", 101, 500),
|
||||
GRANDE("Grande structure (501–2 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;
|
||||
}
|
||||
}
|
||||
@@ -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é"),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
public String getLibelle() { return libelle; }
|
||||
TypePeriodeAbonnement(String libelle, int nombreMois, BigDecimal coefficient) {
|
||||
this.libelle = libelle;
|
||||
this.nombreMois = nombreMois;
|
||||
this.coefficient = coefficient;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public int getNombreMois() {
|
||||
return nombreMois;
|
||||
}
|
||||
|
||||
public BigDecimal getCoefficient() {
|
||||
return coefficient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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 (1–100 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 (1–100 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 (1–100 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 (1–100 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user