package dev.lions.unionflow.server.entity; import jakarta.persistence.*; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.concurrent.atomic.AtomicLong; import lombok.*; /** * Demande d'adhésion d'un utilisateur à une organisation. * *

Flux : *

    *
  1. L'utilisateur crée son compte et choisit une organisation
  2. *
  3. Une {@code DemandeAdhesion} est créée (statut EN_ATTENTE)
  4. *
  5. Si frais d'adhésion : une {@link IntentionPaiement} est créée et liée
  6. *
  7. Le manager valide → {@link MembreOrganisation} créé, quota souscription décrémenté
  8. *
* *

Remplace l'ancienne entité {@code Adhesion}. * Table : {@code demandes_adhesion} */ @Entity @Table( name = "demandes_adhesion", indexes = { @Index(name = "idx_da_utilisateur", columnList = "utilisateur_id"), @Index(name = "idx_da_organisation", columnList = "organisation_id"), @Index(name = "idx_da_statut", columnList = "statut"), @Index(name = "idx_da_date", columnList = "date_demande") }) @Data @NoArgsConstructor @AllArgsConstructor @Builder @EqualsAndHashCode(callSuper = true) public class DemandeAdhesion extends BaseEntity { @NotBlank @Column(name = "numero_reference", unique = true, nullable = false, length = 50) private String numeroReference; @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "utilisateur_id", nullable = false) private Membre utilisateur; @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "organisation_id", nullable = false) private Organisation organisation; @NotBlank @Pattern(regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE)$") @Builder.Default @Column(name = "statut", nullable = false, length = 20) private String statut = "EN_ATTENTE"; @Builder.Default @DecimalMin("0.00") @Digits(integer = 10, fraction = 2) @Column(name = "frais_adhesion", nullable = false, precision = 12, scale = 2) private BigDecimal fraisAdhesion = BigDecimal.ZERO; @Builder.Default @DecimalMin("0.00") @Digits(integer = 10, fraction = 2) @Column(name = "montant_paye", nullable = false, precision = 12, scale = 2) private BigDecimal montantPaye = BigDecimal.ZERO; @Builder.Default @Pattern(regexp = "^[A-Z]{3}$") @Column(name = "code_devise", nullable = false, length = 3) private String codeDevise = "XOF"; /** Intention de paiement Wave liée aux frais d'adhésion */ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "intention_paiement_id") private IntentionPaiement intentionPaiement; @Builder.Default @Column(name = "date_demande", nullable = false) private LocalDateTime dateDemande = LocalDateTime.now(); @Column(name = "date_traitement") private LocalDateTime dateTraitement; /** Manager/Admin qui a approuvé ou rejeté */ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "traite_par_id") private Membre traitePar; @Column(name = "motif_rejet", length = 1000) private String motifRejet; @Column(name = "observations", length = 1000) private String observations; // ── Méthodes métier ──────────────────────────────────────────────────────── public boolean isEnAttente() { return "EN_ATTENTE".equals(statut); } public boolean isApprouvee() { return "APPROUVEE".equals(statut); } public boolean isRejetee() { return "REJETEE".equals(statut); } public boolean isPayeeIntegralement() { return fraisAdhesion != null && montantPaye != null && montantPaye.compareTo(fraisAdhesion) >= 0; } private static final AtomicLong REFERENCE_COUNTER = new AtomicLong(System.currentTimeMillis() % 100000000L); public static String genererNumeroReference() { return "ADH-" + java.time.LocalDate.now().getYear() + "-" + String.format("%08d", REFERENCE_COUNTER.incrementAndGet() % 100000000L); } @PrePersist protected void onCreate() { super.onCreate(); if (dateDemande == null) dateDemande = LocalDateTime.now(); if (statut == null) statut = "EN_ATTENTE"; if (codeDevise == null) codeDevise = "XOF"; if (fraisAdhesion == null) fraisAdhesion = BigDecimal.ZERO; if (montantPaye == null) montantPaye = BigDecimal.ZERO; if (numeroReference == null || numeroReference.isEmpty()) { numeroReference = genererNumeroReference(); } } }