feat: PHASE 2 et 3 - Paiements centralisés et intégration Wave
PHASE 2 - Système de Paiements Centralisé: - Entité Paiement centralisée avec enums MethodePaiement et StatutPaiement - Tables de liaison: PaiementCotisation, PaiementAdhesion, PaiementEvenement, PaiementAide - Repository PaiementRepository avec méthodes de recherche et calculs - Relations bidirectionnelles avec Membre PHASE 3 - Intégration Wave Mobile Money: - Entités Wave: CompteWave, TransactionWave, WebhookWave, ConfigurationWave - Enums: StatutCompteWave, TypeTransactionWave, StatutTransactionWave, TypeEvenementWebhook, StatutWebhook - Repositories: CompteWaveRepository, TransactionWaveRepository, WebhookWaveRepository, ConfigurationWaveRepository - Relations bidirectionnelles dans Organisation et Membre - Ajout champ telephoneWave dans Membre Respect strict DRY/WOU: - Enums dans module API réutilisables - Patterns de repository cohérents - Relations JPA standardisées - Numéro de référence auto-généré pour Paiement
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des méthodes de paiement dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum MethodePaiement {
|
||||
WAVE_MOBILE_MONEY("Wave Mobile Money"),
|
||||
ORANGE_MONEY("Orange Money"),
|
||||
MTN_MOBILE_MONEY("MTN Mobile Money"),
|
||||
MOOV_MONEY("Moov Money"),
|
||||
VIREMENT_BANCAIRE("Virement Bancaire"),
|
||||
CARTE_BANCAIRE("Carte Bancaire"),
|
||||
ESPECES("Espèces"),
|
||||
CHEQUE("Chèque"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
MethodePaiement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si c'est une méthode de mobile money */
|
||||
public boolean isMobileMoney() {
|
||||
return this == WAVE_MOBILE_MONEY
|
||||
|| this == ORANGE_MONEY
|
||||
|| this == MTN_MOBILE_MONEY
|
||||
|| this == MOOV_MONEY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de paiement dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutPaiement {
|
||||
EN_ATTENTE("En Attente"),
|
||||
EN_COURS("En Cours"),
|
||||
VALIDE("Validé"),
|
||||
ECHOUE("Échoué"),
|
||||
ANNULE("Annulé"),
|
||||
REMBOURSE("Remboursé");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutPaiement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si le paiement est finalisé (ne peut plus changer) */
|
||||
public boolean isFinalise() {
|
||||
return this == VALIDE || this == ECHOUE || this == ANNULE || this == REMBOURSE;
|
||||
}
|
||||
|
||||
/** Vérifie si le paiement est en cours de traitement */
|
||||
public boolean isEnTraitement() {
|
||||
return this == EN_ATTENTE || this == EN_COURS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de compte Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutCompteWave {
|
||||
NON_VERIFIE("Non Vérifié"),
|
||||
VERIFIE("Vérifié"),
|
||||
SUSPENDU("Suspendu"),
|
||||
BLOQUE("Bloqué");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutCompteWave(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de transaction Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutTransactionWave {
|
||||
INITIALISE("Initialisé"),
|
||||
EN_ATTENTE("En Attente"),
|
||||
EN_COURS("En Cours"),
|
||||
REUSSIE("Réussie"),
|
||||
ECHOUE("Échoué"),
|
||||
ANNULEE("Annulée"),
|
||||
EXPIRED("Expiré");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutTransactionWave(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si la transaction est finalisée */
|
||||
public boolean isFinalise() {
|
||||
return this == REUSSIE || this == ECHOUE || this == ANNULEE || this == EXPIRED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de traitement de webhook Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutWebhook {
|
||||
EN_ATTENTE("En Attente"),
|
||||
EN_TRAITEMENT("En Traitement"),
|
||||
TRAITE("Traité"),
|
||||
ECHOUE("Échoué"),
|
||||
IGNORE("Ignoré");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutWebhook(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des types d'événements webhook Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeEvenementWebhook {
|
||||
TRANSACTION_CREATED("Transaction Créée"),
|
||||
TRANSACTION_COMPLETED("Transaction Complétée"),
|
||||
TRANSACTION_FAILED("Transaction Échouée"),
|
||||
TRANSACTION_CANCELLED("Transaction Annulée"),
|
||||
ACCOUNT_VERIFIED("Compte Vérifié"),
|
||||
ACCOUNT_SUSPENDED("Compte Suspendu"),
|
||||
OTHER("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeEvenementWebhook(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des types de transaction Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeTransactionWave {
|
||||
DEPOT("Dépôt"),
|
||||
RETRAIT("Retrait"),
|
||||
TRANSFERT("Transfert"),
|
||||
PAIEMENT("Paiement"),
|
||||
REMBOURSEMENT("Remboursement");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeTransactionWave(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité CompteWave pour la gestion des comptes Wave Mobile Money
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "comptes_wave",
|
||||
indexes = {
|
||||
@Index(name = "idx_compte_wave_telephone", columnList = "numero_telephone", unique = true),
|
||||
@Index(name = "idx_compte_wave_statut", columnList = "statut_compte"),
|
||||
@Index(name = "idx_compte_wave_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_compte_wave_membre", columnList = "membre_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CompteWave extends BaseEntity {
|
||||
|
||||
/** Numéro de téléphone Wave (format +225XXXXXXXX) */
|
||||
@NotBlank
|
||||
@Pattern(
|
||||
regexp = "^\\+225[0-9]{8}$",
|
||||
message = "Le numéro de téléphone Wave doit être au format +225XXXXXXXX")
|
||||
@Column(name = "numero_telephone", unique = true, nullable = false, length = 13)
|
||||
private String numeroTelephone;
|
||||
|
||||
/** Statut du compte */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_compte", nullable = false, length = 30)
|
||||
private StatutCompteWave statutCompte = StatutCompteWave.NON_VERIFIE;
|
||||
|
||||
/** Identifiant Wave API (encrypté) */
|
||||
@Column(name = "wave_account_id", length = 255)
|
||||
private String waveAccountId;
|
||||
|
||||
/** Clé API Wave (encryptée) */
|
||||
@Column(name = "wave_api_key", length = 500)
|
||||
private String waveApiKey;
|
||||
|
||||
/** Environnement (SANDBOX ou PRODUCTION) */
|
||||
@Column(name = "environnement", length = 20)
|
||||
private String environnement;
|
||||
|
||||
/** Date de dernière vérification */
|
||||
@Column(name = "date_derniere_verification")
|
||||
private java.time.LocalDateTime dateDerniereVerification;
|
||||
|
||||
/** Commentaires */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id")
|
||||
private Membre membre;
|
||||
|
||||
@OneToMany(mappedBy = "compteWave", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<TransactionWave> transactions = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour vérifier si le compte est vérifié */
|
||||
public boolean isVerifie() {
|
||||
return StatutCompteWave.VERIFIE.equals(statutCompte);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le compte peut être utilisé */
|
||||
public boolean peutEtreUtilise() {
|
||||
return StatutCompteWave.VERIFIE.equals(statutCompte);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statutCompte == null) {
|
||||
statutCompte = StatutCompteWave.NON_VERIFIE;
|
||||
}
|
||||
if (environnement == null || environnement.isEmpty()) {
|
||||
environnement = "SANDBOX";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité ConfigurationWave pour la configuration de l'intégration Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "configurations_wave",
|
||||
indexes = {
|
||||
@Index(name = "idx_config_wave_cle", columnList = "cle", unique = true)
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConfigurationWave extends BaseEntity {
|
||||
|
||||
/** Clé de configuration */
|
||||
@NotBlank
|
||||
@Column(name = "cle", unique = true, nullable = false, length = 100)
|
||||
private String cle;
|
||||
|
||||
/** Valeur de configuration (peut être encryptée) */
|
||||
@Column(name = "valeur", columnDefinition = "TEXT")
|
||||
private String valeur;
|
||||
|
||||
/** Description de la configuration */
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
/** Type de valeur (STRING, NUMBER, BOOLEAN, JSON, ENCRYPTED) */
|
||||
@Column(name = "type_valeur", length = 20)
|
||||
private String typeValeur;
|
||||
|
||||
/** Environnement (SANDBOX, PRODUCTION, COMMON) */
|
||||
@Column(name = "environnement", length = 20)
|
||||
private String environnement;
|
||||
|
||||
/** Méthode métier pour vérifier si la valeur est encryptée */
|
||||
public boolean isEncryptee() {
|
||||
return "ENCRYPTED".equals(typeValeur);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (typeValeur == null || typeValeur.isEmpty()) {
|
||||
typeValeur = "STRING";
|
||||
}
|
||||
if (environnement == null || environnement.isEmpty()) {
|
||||
environnement = "COMMON";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ public class Membre extends BaseEntity {
|
||||
@Column(name = "telephone", length = 20)
|
||||
private String telephone;
|
||||
|
||||
@Pattern(
|
||||
regexp = "^\\+225[0-9]{8}$",
|
||||
message = "Le numéro de téléphone Wave doit être au format +225XXXXXXXX")
|
||||
@Column(name = "telephone_wave", length = 13)
|
||||
private String telephoneWave;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_naissance", nullable = false)
|
||||
private LocalDate dateNaissance;
|
||||
@@ -77,6 +83,14 @@ public class Membre extends BaseEntity {
|
||||
@Builder.Default
|
||||
private List<MembreRole> roles = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<CompteWave> comptesWave = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Paiement> paiements = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour obtenir le nom complet */
|
||||
public String getNomComplet() {
|
||||
return prenom + " " + nom;
|
||||
|
||||
@@ -195,6 +195,10 @@ public class Organisation extends BaseEntity {
|
||||
@Builder.Default
|
||||
private List<Adresse> adresses = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<CompteWave> comptesWave = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour obtenir le nom complet avec sigle */
|
||||
public String getNomComplet() {
|
||||
if (nomCourt != null && !nomCourt.isEmpty()) {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.paiement.MethodePaiement;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.StatutPaiement;
|
||||
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
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "methode_paiement", nullable = false, length = 50)
|
||||
private MethodePaiement methodePaiement;
|
||||
|
||||
/** Statut du paiement */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_paiement", nullable = false, length = 30)
|
||||
private StatutPaiement statutPaiement = 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;
|
||||
|
||||
/** Relations avec les tables de liaison */
|
||||
@OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<PaiementCotisation> paiementsCotisation = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<PaiementAdhesion> paiementsAdhesion = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<PaiementEvenement> paiementsEvenement = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<PaiementAide> paiementsAide = new ArrayList<>();
|
||||
|
||||
/** Relation avec TransactionWave (optionnelle) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "transaction_wave_id")
|
||||
private TransactionWave transactionWave;
|
||||
|
||||
/** Méthode métier pour générer un numéro de référence unique */
|
||||
public static String genererNumeroReference() {
|
||||
return "PAY-"
|
||||
+ LocalDateTime.now().getYear()
|
||||
+ "-"
|
||||
+ String.format("%012d", System.currentTimeMillis() % 1000000000000L);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le paiement est validé */
|
||||
public boolean isValide() {
|
||||
return StatutPaiement.VALIDE.equals(statutPaiement);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le paiement peut être modifié */
|
||||
public boolean peutEtreModifie() {
|
||||
return !statutPaiement.isFinalise();
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (numeroReference == null || numeroReference.isEmpty()) {
|
||||
numeroReference = genererNumeroReference();
|
||||
}
|
||||
if (codeDevise == null || codeDevise.isEmpty()) {
|
||||
codeDevise = "XOF";
|
||||
}
|
||||
if (statutPaiement == null) {
|
||||
statutPaiement = StatutPaiement.EN_ATTENTE;
|
||||
}
|
||||
if (datePaiement == null) {
|
||||
datePaiement = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison entre Paiement et Adhesion
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "paiements_adhesions",
|
||||
indexes = {
|
||||
@Index(name = "idx_paiement_adhesion_paiement", columnList = "paiement_id"),
|
||||
@Index(name = "idx_paiement_adhesion_adhesion", columnList = "adhesion_id")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_paiement_adhesion",
|
||||
columnNames = {"paiement_id", "adhesion_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PaiementAdhesion extends BaseEntity {
|
||||
|
||||
/** Paiement */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id", nullable = false)
|
||||
private Paiement paiement;
|
||||
|
||||
/** Adhésion */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "adhesion_id", nullable = false)
|
||||
private Adhesion adhesion;
|
||||
|
||||
/** Montant appliqué à cette adhésion */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant appliqué doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_applique", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal montantApplique;
|
||||
|
||||
/** Date d'application */
|
||||
@Column(name = "date_application")
|
||||
private LocalDateTime dateApplication;
|
||||
|
||||
/** Commentaire sur l'application */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateApplication == null) {
|
||||
dateApplication = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison entre Paiement et DemandeAide
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "paiements_aides",
|
||||
indexes = {
|
||||
@Index(name = "idx_paiement_aide_paiement", columnList = "paiement_id"),
|
||||
@Index(name = "idx_paiement_aide_demande", columnList = "demande_aide_id")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_paiement_aide",
|
||||
columnNames = {"paiement_id", "demande_aide_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PaiementAide extends BaseEntity {
|
||||
|
||||
/** Paiement */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id", nullable = false)
|
||||
private Paiement paiement;
|
||||
|
||||
/** Demande d'aide */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demande_aide_id", nullable = false)
|
||||
private DemandeAide demandeAide;
|
||||
|
||||
/** Montant appliqué à cette demande d'aide */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant appliqué doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_applique", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal montantApplique;
|
||||
|
||||
/** Date d'application */
|
||||
@Column(name = "date_application")
|
||||
private LocalDateTime dateApplication;
|
||||
|
||||
/** Commentaire sur l'application */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateApplication == null) {
|
||||
dateApplication = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison entre Paiement et Cotisation
|
||||
* Permet à un paiement de couvrir plusieurs cotisations
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "paiements_cotisations",
|
||||
indexes = {
|
||||
@Index(name = "idx_paiement_cotisation_paiement", columnList = "paiement_id"),
|
||||
@Index(name = "idx_paiement_cotisation_cotisation", columnList = "cotisation_id")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_paiement_cotisation",
|
||||
columnNames = {"paiement_id", "cotisation_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PaiementCotisation extends BaseEntity {
|
||||
|
||||
/** Paiement */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id", nullable = false)
|
||||
private Paiement paiement;
|
||||
|
||||
/** Cotisation */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "cotisation_id", nullable = false)
|
||||
private Cotisation cotisation;
|
||||
|
||||
/** Montant appliqué à cette cotisation */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant appliqué doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_applique", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal montantApplique;
|
||||
|
||||
/** Date d'application */
|
||||
@Column(name = "date_application")
|
||||
private LocalDateTime dateApplication;
|
||||
|
||||
/** Commentaire sur l'application */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateApplication == null) {
|
||||
dateApplication = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison entre Paiement et InscriptionEvenement
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "paiements_evenements",
|
||||
indexes = {
|
||||
@Index(name = "idx_paiement_evenement_paiement", columnList = "paiement_id"),
|
||||
@Index(name = "idx_paiement_evenement_inscription", columnList = "inscription_evenement_id")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_paiement_evenement",
|
||||
columnNames = {"paiement_id", "inscription_evenement_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PaiementEvenement extends BaseEntity {
|
||||
|
||||
/** Paiement */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id", nullable = false)
|
||||
private Paiement paiement;
|
||||
|
||||
/** Inscription à l'événement */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "inscription_evenement_id", nullable = false)
|
||||
private InscriptionEvenement inscriptionEvenement;
|
||||
|
||||
/** Montant appliqué à cette inscription */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant appliqué doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_applique", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal montantApplique;
|
||||
|
||||
/** Date d'application */
|
||||
@Column(name = "date_application")
|
||||
private LocalDateTime dateApplication;
|
||||
|
||||
/** Commentaire sur l'application */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateApplication == null) {
|
||||
dateApplication = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité TransactionWave pour le suivi des transactions Wave Mobile Money
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "transactions_wave",
|
||||
indexes = {
|
||||
@Index(name = "idx_transaction_wave_id", columnList = "wave_transaction_id", unique = true),
|
||||
@Index(name = "idx_transaction_wave_request_id", columnList = "wave_request_id"),
|
||||
@Index(name = "idx_transaction_wave_reference", columnList = "wave_reference"),
|
||||
@Index(name = "idx_transaction_wave_statut", columnList = "statut_transaction"),
|
||||
@Index(name = "idx_transaction_wave_compte", columnList = "compte_wave_id"),
|
||||
@Index(name = "idx_transaction_wave_paiement", columnList = "paiement_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TransactionWave extends BaseEntity {
|
||||
|
||||
/** Identifiant Wave de la transaction (unique) */
|
||||
@NotBlank
|
||||
@Column(name = "wave_transaction_id", unique = true, nullable = false, length = 100)
|
||||
private String waveTransactionId;
|
||||
|
||||
/** Identifiant de requête Wave */
|
||||
@Column(name = "wave_request_id", length = 100)
|
||||
private String waveRequestId;
|
||||
|
||||
/** Référence Wave */
|
||||
@Column(name = "wave_reference", length = 100)
|
||||
private String waveReference;
|
||||
|
||||
/** Type de transaction */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_transaction", nullable = false, length = 50)
|
||||
private TypeTransactionWave typeTransaction;
|
||||
|
||||
/** Statut de la transaction */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_transaction", nullable = false, length = 30)
|
||||
private StatutTransactionWave statutTransaction = StatutTransactionWave.INITIALISE;
|
||||
|
||||
/** Montant de la transaction */
|
||||
@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;
|
||||
|
||||
/** Frais de transaction */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "frais", precision = 12, scale = 2)
|
||||
private BigDecimal frais;
|
||||
|
||||
/** Montant net (montant - frais) */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_net", precision = 14, scale = 2)
|
||||
private BigDecimal montantNet;
|
||||
|
||||
/** Code devise */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^[A-Z]{3}$")
|
||||
@Column(name = "code_devise", nullable = false, length = 3)
|
||||
private String codeDevise;
|
||||
|
||||
/** Numéro téléphone payeur */
|
||||
@Column(name = "telephone_payeur", length = 13)
|
||||
private String telephonePayeur;
|
||||
|
||||
/** Numéro téléphone bénéficiaire */
|
||||
@Column(name = "telephone_beneficiaire", length = 13)
|
||||
private String telephoneBeneficiaire;
|
||||
|
||||
/** Métadonnées JSON (réponse complète de Wave API) */
|
||||
@Column(name = "metadonnees", columnDefinition = "TEXT")
|
||||
private String metadonnees;
|
||||
|
||||
/** Réponse complète de Wave API (JSON) */
|
||||
@Column(name = "reponse_wave_api", columnDefinition = "TEXT")
|
||||
private String reponseWaveApi;
|
||||
|
||||
/** Nombre de tentatives */
|
||||
@Builder.Default
|
||||
@Column(name = "nombre_tentatives", nullable = false)
|
||||
private Integer nombreTentatives = 0;
|
||||
|
||||
/** Date de dernière tentative */
|
||||
@Column(name = "date_derniere_tentative")
|
||||
private LocalDateTime dateDerniereTentative;
|
||||
|
||||
/** Message d'erreur (si échec) */
|
||||
@Column(name = "message_erreur", length = 1000)
|
||||
private String messageErreur;
|
||||
|
||||
// Relations
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "compte_wave_id", nullable = false)
|
||||
private CompteWave compteWave;
|
||||
|
||||
@OneToMany(mappedBy = "transactionWave", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<WebhookWave> webhooks = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour vérifier si la transaction est réussie */
|
||||
public boolean isReussie() {
|
||||
return StatutTransactionWave.REUSSIE.equals(statutTransaction);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si la transaction peut être retentée */
|
||||
public boolean peutEtreRetentee() {
|
||||
return (statutTransaction == StatutTransactionWave.ECHOUE
|
||||
|| statutTransaction == StatutTransactionWave.EXPIRED)
|
||||
&& (nombreTentatives == null || nombreTentatives < 5);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statutTransaction == null) {
|
||||
statutTransaction = StatutTransactionWave.INITIALISE;
|
||||
}
|
||||
if (codeDevise == null || codeDevise.isEmpty()) {
|
||||
codeDevise = "XOF";
|
||||
}
|
||||
if (nombreTentatives == null) {
|
||||
nombreTentatives = 0;
|
||||
}
|
||||
if (montantNet == null && montant != null && frais != null) {
|
||||
montantNet = montant.subtract(frais);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutWebhook;
|
||||
import dev.lions.unionflow.server.api.enums.wave.TypeEvenementWebhook;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité WebhookWave pour le traitement des événements Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "webhooks_wave",
|
||||
indexes = {
|
||||
@Index(name = "idx_webhook_wave_event_id", columnList = "wave_event_id", unique = true),
|
||||
@Index(name = "idx_webhook_wave_statut", columnList = "statut_traitement"),
|
||||
@Index(name = "idx_webhook_wave_type", columnList = "type_evenement"),
|
||||
@Index(name = "idx_webhook_wave_transaction", columnList = "transaction_wave_id"),
|
||||
@Index(name = "idx_webhook_wave_paiement", columnList = "paiement_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WebhookWave extends BaseEntity {
|
||||
|
||||
/** Identifiant unique de l'événement Wave */
|
||||
@NotBlank
|
||||
@Column(name = "wave_event_id", unique = true, nullable = false, length = 100)
|
||||
private String waveEventId;
|
||||
|
||||
/** Type d'événement */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_evenement", length = 50)
|
||||
private TypeEvenementWebhook typeEvenement;
|
||||
|
||||
/** Statut de traitement */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_traitement", nullable = false, length = 30)
|
||||
private StatutWebhook statutTraitement = StatutWebhook.EN_ATTENTE;
|
||||
|
||||
/** Payload JSON reçu */
|
||||
@Column(name = "payload", columnDefinition = "TEXT")
|
||||
private String payload;
|
||||
|
||||
/** Signature de validation */
|
||||
@Column(name = "signature", length = 500)
|
||||
private String signature;
|
||||
|
||||
/** Date de réception */
|
||||
@Column(name = "date_reception")
|
||||
private LocalDateTime dateReception;
|
||||
|
||||
/** Date de traitement */
|
||||
@Column(name = "date_traitement")
|
||||
private LocalDateTime dateTraitement;
|
||||
|
||||
/** Nombre de tentatives de traitement */
|
||||
@Builder.Default
|
||||
@Column(name = "nombre_tentatives", nullable = false)
|
||||
private Integer nombreTentatives = 0;
|
||||
|
||||
/** Message d'erreur (si échec) */
|
||||
@Column(name = "message_erreur", length = 1000)
|
||||
private String messageErreur;
|
||||
|
||||
/** Commentaires */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "transaction_wave_id")
|
||||
private TransactionWave transactionWave;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id")
|
||||
private Paiement paiement;
|
||||
|
||||
/** Méthode métier pour vérifier si le webhook est traité */
|
||||
public boolean isTraite() {
|
||||
return StatutWebhook.TRAITE.equals(statutTraitement);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le webhook peut être retenté */
|
||||
public boolean peutEtreRetente() {
|
||||
return (statutTraitement == StatutWebhook.ECHOUE || statutTraitement == StatutWebhook.EN_ATTENTE)
|
||||
&& (nombreTentatives == null || nombreTentatives < 5);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statutTraitement == null) {
|
||||
statutTraitement = StatutWebhook.EN_ATTENTE;
|
||||
}
|
||||
if (dateReception == null) {
|
||||
dateReception = LocalDateTime.now();
|
||||
}
|
||||
if (nombreTentatives == null) {
|
||||
nombreTentatives = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
|
||||
import dev.lions.unionflow.server.entity.CompteWave;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité CompteWave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class CompteWaveRepository implements PanacheRepository<CompteWave> {
|
||||
|
||||
/**
|
||||
* Trouve un compte Wave par numéro de téléphone
|
||||
*
|
||||
* @param numeroTelephone Numéro de téléphone
|
||||
* @return Compte Wave ou Optional.empty()
|
||||
*/
|
||||
public Optional<CompteWave> findByNumeroTelephone(String numeroTelephone) {
|
||||
return find("numeroTelephone = ?1 AND actif = true", numeroTelephone).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve tous les comptes Wave d'une organisation
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @return Liste des comptes Wave
|
||||
*/
|
||||
public List<CompteWave> findByOrganisationId(UUID organisationId) {
|
||||
return find("organisation.id = ?1 AND actif = true", organisationId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve le compte Wave principal d'une organisation (premier vérifié)
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @return Compte Wave ou Optional.empty()
|
||||
*/
|
||||
public Optional<CompteWave> findPrincipalByOrganisationId(UUID organisationId) {
|
||||
return find(
|
||||
"organisation.id = ?1 AND statutCompte = ?2 AND actif = true",
|
||||
organisationId,
|
||||
StatutCompteWave.VERIFIE)
|
||||
.firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve tous les comptes Wave d'un membre
|
||||
*
|
||||
* @param membreId ID du membre
|
||||
* @return Liste des comptes Wave
|
||||
*/
|
||||
public List<CompteWave> findByMembreId(UUID membreId) {
|
||||
return find("membre.id = ?1 AND actif = true", membreId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve le compte Wave principal d'un membre (premier vérifié)
|
||||
*
|
||||
* @param membreId ID du membre
|
||||
* @return Compte Wave ou Optional.empty()
|
||||
*/
|
||||
public Optional<CompteWave> findPrincipalByMembreId(UUID membreId) {
|
||||
return find(
|
||||
"membre.id = ?1 AND statutCompte = ?2 AND actif = true",
|
||||
membreId,
|
||||
StatutCompteWave.VERIFIE)
|
||||
.firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve tous les comptes Wave vérifiés
|
||||
*
|
||||
* @return Liste des comptes Wave vérifiés
|
||||
*/
|
||||
public List<CompteWave> findComptesVerifies() {
|
||||
return find("statutCompte = ?1 AND actif = true", StatutCompteWave.VERIFIE).list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.entity.ConfigurationWave;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité ConfigurationWave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class ConfigurationWaveRepository implements PanacheRepository<ConfigurationWave> {
|
||||
|
||||
/**
|
||||
* Trouve une configuration par sa clé
|
||||
*
|
||||
* @param cle Clé de configuration
|
||||
* @return Configuration ou Optional.empty()
|
||||
*/
|
||||
public Optional<ConfigurationWave> findByCle(String cle) {
|
||||
return find("cle = ?1 AND actif = true", cle).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les configurations d'un environnement
|
||||
*
|
||||
* @param environnement Environnement (SANDBOX, PRODUCTION, COMMON)
|
||||
* @return Liste des configurations
|
||||
*/
|
||||
public List<ConfigurationWave> findByEnvironnement(String environnement) {
|
||||
return find("environnement = ?1 AND actif = true", environnement).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les configurations actives
|
||||
*
|
||||
* @return Liste des configurations actives
|
||||
*/
|
||||
public List<ConfigurationWave> findAllActives() {
|
||||
return find("actif = true ORDER BY cle ASC").list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.paiement.MethodePaiement;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.StatutPaiement;
|
||||
import dev.lions.unionflow.server.entity.Paiement;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité Paiement
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PaiementRepository implements PanacheRepository<Paiement> {
|
||||
|
||||
/**
|
||||
* Trouve un paiement par son numéro de référence
|
||||
*
|
||||
* @param numeroReference Numéro de référence
|
||||
* @return Paiement ou Optional.empty()
|
||||
*/
|
||||
public Optional<Paiement> findByNumeroReference(String numeroReference) {
|
||||
return find("numeroReference", numeroReference).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve tous les paiements d'un membre
|
||||
*
|
||||
* @param membreId ID du membre
|
||||
* @return Liste des paiements
|
||||
*/
|
||||
public List<Paiement> findByMembreId(UUID membreId) {
|
||||
return find("membre.id = ?1 AND actif = true", membreId)
|
||||
.order("datePaiement DESC")
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les paiements par statut
|
||||
*
|
||||
* @param statut Statut du paiement
|
||||
* @return Liste des paiements
|
||||
*/
|
||||
public List<Paiement> findByStatut(StatutPaiement statut) {
|
||||
return find("statutPaiement = ?1 AND actif = true", statut)
|
||||
.order("datePaiement DESC")
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les paiements par méthode
|
||||
*
|
||||
* @param methode Méthode de paiement
|
||||
* @return Liste des paiements
|
||||
*/
|
||||
public List<Paiement> findByMethode(MethodePaiement methode) {
|
||||
return find("methodePaiement = ?1 AND actif = true", methode)
|
||||
.order("datePaiement DESC")
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les paiements validés dans une période
|
||||
*
|
||||
* @param dateDebut Date de début
|
||||
* @param dateFin Date de fin
|
||||
* @return Liste des paiements
|
||||
*/
|
||||
public List<Paiement> findValidesParPeriode(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
return find(
|
||||
"statutPaiement = ?1 AND dateValidation >= ?2 AND dateValidation <= ?3 AND actif = true",
|
||||
StatutPaiement.VALIDE,
|
||||
dateDebut,
|
||||
dateFin)
|
||||
.order("dateValidation DESC")
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le montant total des paiements validés dans une période
|
||||
*
|
||||
* @param dateDebut Date de début
|
||||
* @param dateFin Date de fin
|
||||
* @return Montant total
|
||||
*/
|
||||
public BigDecimal calculerMontantTotalValides(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
List<Paiement> paiements = findValidesParPeriode(dateDebut, dateFin);
|
||||
return paiements.stream()
|
||||
.map(Paiement::getMontant)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
|
||||
import dev.lions.unionflow.server.entity.TransactionWave;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité TransactionWave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class TransactionWaveRepository implements PanacheRepository<TransactionWave> {
|
||||
|
||||
/**
|
||||
* Trouve une transaction par son identifiant Wave
|
||||
*
|
||||
* @param waveTransactionId Identifiant Wave
|
||||
* @return Transaction ou Optional.empty()
|
||||
*/
|
||||
public Optional<TransactionWave> findByWaveTransactionId(String waveTransactionId) {
|
||||
return find("waveTransactionId = ?1", waveTransactionId).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve une transaction par son identifiant de requête
|
||||
*
|
||||
* @param waveRequestId Identifiant de requête
|
||||
* @return Transaction ou Optional.empty()
|
||||
*/
|
||||
public Optional<TransactionWave> findByWaveRequestId(String waveRequestId) {
|
||||
return find("waveRequestId = ?1", waveRequestId).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve toutes les transactions d'un compte Wave
|
||||
*
|
||||
* @param compteWaveId ID du compte Wave
|
||||
* @return Liste des transactions
|
||||
*/
|
||||
public List<TransactionWave> findByCompteWaveId(UUID compteWaveId) {
|
||||
return find("compteWave.id = ?1 ORDER BY dateCreation DESC", compteWaveId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les transactions par statut
|
||||
*
|
||||
* @param statut Statut de la transaction
|
||||
* @return Liste des transactions
|
||||
*/
|
||||
public List<TransactionWave> findByStatut(StatutTransactionWave statut) {
|
||||
return find("statutTransaction = ?1 ORDER BY dateCreation DESC", statut).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les transactions par type
|
||||
*
|
||||
* @param type Type de transaction
|
||||
* @return Liste des transactions
|
||||
*/
|
||||
public List<TransactionWave> findByType(TypeTransactionWave type) {
|
||||
return find("typeTransaction = ?1 ORDER BY dateCreation DESC", type).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les transactions réussies dans une période
|
||||
*
|
||||
* @param compteWaveId ID du compte Wave
|
||||
* @return Liste des transactions réussies
|
||||
*/
|
||||
public List<TransactionWave> findReussiesByCompteWave(UUID compteWaveId) {
|
||||
return find(
|
||||
"compteWave.id = ?1 AND statutTransaction = ?2 ORDER BY dateCreation DESC",
|
||||
compteWaveId,
|
||||
StatutTransactionWave.REUSSIE)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les transactions échouées pouvant être retentées
|
||||
*
|
||||
* @return Liste des transactions échouées
|
||||
*/
|
||||
public List<TransactionWave> findEchoueesRetentables() {
|
||||
return find(
|
||||
"statutTransaction IN (?1, ?2) AND (nombreTentatives IS NULL OR nombreTentatives < 5) ORDER BY dateCreation ASC",
|
||||
StatutTransactionWave.ECHOUE,
|
||||
StatutTransactionWave.EXPIRED)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutWebhook;
|
||||
import dev.lions.unionflow.server.api.enums.wave.TypeEvenementWebhook;
|
||||
import dev.lions.unionflow.server.entity.WebhookWave;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité WebhookWave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class WebhookWaveRepository implements PanacheRepository<WebhookWave> {
|
||||
|
||||
/**
|
||||
* Trouve un webhook par son identifiant d'événement Wave
|
||||
*
|
||||
* @param waveEventId Identifiant d'événement
|
||||
* @return Webhook ou Optional.empty()
|
||||
*/
|
||||
public Optional<WebhookWave> findByWaveEventId(String waveEventId) {
|
||||
return find("waveEventId = ?1", waveEventId).firstResultOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve tous les webhooks d'une transaction
|
||||
*
|
||||
* @param transactionWaveId ID de la transaction
|
||||
* @return Liste des webhooks
|
||||
*/
|
||||
public List<WebhookWave> findByTransactionWaveId(UUID transactionWaveId) {
|
||||
return find("transactionWave.id = ?1 ORDER BY dateReception DESC", transactionWaveId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve tous les webhooks d'un paiement
|
||||
*
|
||||
* @param paiementId ID du paiement
|
||||
* @return Liste des webhooks
|
||||
*/
|
||||
public List<WebhookWave> findByPaiementId(UUID paiementId) {
|
||||
return find("paiement.id = ?1 ORDER BY dateReception DESC", paiementId).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les webhooks par statut
|
||||
*
|
||||
* @param statut Statut de traitement
|
||||
* @return Liste des webhooks
|
||||
*/
|
||||
public List<WebhookWave> findByStatut(StatutWebhook statut) {
|
||||
return find("statutTraitement = ?1 ORDER BY dateReception DESC", statut).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les webhooks par type d'événement
|
||||
*
|
||||
* @param type Type d'événement
|
||||
* @return Liste des webhooks
|
||||
*/
|
||||
public List<WebhookWave> findByType(TypeEvenementWebhook type) {
|
||||
return find("typeEvenement = ?1 ORDER BY dateReception DESC", type).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les webhooks en attente de traitement
|
||||
*
|
||||
* @return Liste des webhooks en attente
|
||||
*/
|
||||
public List<WebhookWave> findEnAttente() {
|
||||
return find("statutTraitement = ?1 ORDER BY dateReception ASC", StatutWebhook.EN_ATTENTE)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve les webhooks échoués pouvant être retentés
|
||||
*
|
||||
* @return Liste des webhooks échoués
|
||||
*/
|
||||
public List<WebhookWave> findEchouesRetentables() {
|
||||
return find(
|
||||
"statutTraitement = ?1 AND (nombreTentatives IS NULL OR nombreTentatives < 5) ORDER BY dateReception ASC",
|
||||
StatutWebhook.ECHOUE)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user