package dev.lions.unionflow.server.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * Entité Paiement centralisée pour tous les types de paiements. * Réutilisable pour cotisations, adhésions, événements, aides. * * @author UnionFlow Team * @version 3.0 * @since 2025-01-29 */ @Entity @Table(name = "paiements", indexes = { @Index(name = "idx_paiement_numero_reference", columnList = "numero_reference", unique = true), @Index(name = "idx_paiement_membre", columnList = "membre_id"), @Index(name = "idx_paiement_statut", columnList = "statut_paiement"), @Index(name = "idx_paiement_methode", columnList = "methode_paiement"), @Index(name = "idx_paiement_date", columnList = "date_paiement") }) @Data @NoArgsConstructor @AllArgsConstructor @Builder @EqualsAndHashCode(callSuper = true) public class Paiement extends BaseEntity { /** Numéro de référence unique */ @NotBlank @Column(name = "numero_reference", unique = true, nullable = false, length = 50) private String numeroReference; /** Montant du paiement */ @NotNull @DecimalMin(value = "0.0", message = "Le montant doit être positif") @Digits(integer = 12, fraction = 2) @Column(name = "montant", nullable = false, precision = 14, scale = 2) private BigDecimal montant; /** Code devise (ISO 3 lettres) */ @NotBlank @Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres") @Column(name = "code_devise", nullable = false, length = 3) private String codeDevise; /** Méthode de paiement */ @NotNull @Column(name = "methode_paiement", nullable = false, length = 50) private String methodePaiement; /** Statut du paiement */ @NotNull @Builder.Default @Column(name = "statut_paiement", nullable = false, length = 30) private String statutPaiement = "EN_ATTENTE"; /** Date de paiement */ @Column(name = "date_paiement") private LocalDateTime datePaiement; /** Date de validation */ @Column(name = "date_validation") private LocalDateTime dateValidation; /** Validateur (email de l'administrateur) */ @Column(name = "validateur", length = 255) private String validateur; /** Référence externe (numéro transaction, URL preuve, etc.) */ @Column(name = "reference_externe", length = 500) private String referenceExterne; /** URL de preuve de paiement */ @Column(name = "url_preuve", length = 1000) private String urlPreuve; /** Commentaires et notes */ @Column(name = "commentaire", length = 1000) private String commentaire; /** Adresse IP de l'initiateur */ @Column(name = "ip_address", length = 45) private String ipAddress; /** User-Agent de l'initiateur */ @Column(name = "user_agent", length = 500) private String userAgent; /** Membre payeur */ @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "membre_id", nullable = false) private Membre membre; /** Objets cibles de ce paiement (polymorphique) */ @JsonIgnore @OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY) @Builder.Default private List paiementsObjets = new ArrayList<>(); /** Relation avec TransactionWave (optionnelle) */ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "transaction_wave_id") private TransactionWave transactionWave; /** Génère un numéro de référence unique */ public static String genererNumeroReference() { return "PAY-" + LocalDateTime.now().getYear() + "-" + String.format("%012d", System.currentTimeMillis() % 1000000000000L); } /** Vérifie si le paiement est validé */ public boolean isValide() { return "VALIDE".equals(statutPaiement); } /** Vérifie si le paiement peut être modifié */ public boolean peutEtreModifie() { return !"VALIDE".equals(statutPaiement) && !"ANNULE".equals(statutPaiement); } @PrePersist protected void onCreate() { super.onCreate(); if (numeroReference == null || numeroReference.isEmpty()) { numeroReference = genererNumeroReference(); } if (statutPaiement == null) { statutPaiement = "EN_ATTENTE"; } if (datePaiement == null) { datePaiement = LocalDateTime.now(); } } }