From e53440da24e13cea43e68d47992ae9dcaec5f26d Mon Sep 17 00:00:00 2001 From: dahoud Date: Sun, 30 Nov 2025 01:49:46 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20PHASE=202=20et=203=20-=20Paiements=20ce?= =?UTF-8?q?ntralis=C3=A9s=20et=20int=C3=A9gration=20Wave?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../api/enums/paiement/MethodePaiement.java | 39 ++++ .../api/enums/paiement/StatutPaiement.java | 38 ++++ .../api/enums/wave/StatutCompteWave.java | 26 +++ .../api/enums/wave/StatutTransactionWave.java | 34 ++++ .../server/api/enums/wave/StatutWebhook.java | 27 +++ .../api/enums/wave/TypeEvenementWebhook.java | 29 +++ .../api/enums/wave/TypeTransactionWave.java | 27 +++ .../unionflow/server/entity/CompteWave.java | 107 +++++++++++ .../server/entity/ConfigurationWave.java | 69 +++++++ .../lions/unionflow/server/entity/Membre.java | 14 ++ .../unionflow/server/entity/Organisation.java | 4 + .../unionflow/server/entity/Paiement.java | 169 ++++++++++++++++++ .../server/entity/PaiementAdhesion.java | 75 ++++++++ .../unionflow/server/entity/PaiementAide.java | 75 ++++++++ .../server/entity/PaiementCotisation.java | 76 ++++++++ .../server/entity/PaiementEvenement.java | 75 ++++++++ .../server/entity/TransactionWave.java | 160 +++++++++++++++++ .../unionflow/server/entity/WebhookWave.java | 118 ++++++++++++ .../repository/CompteWaveRepository.java | 88 +++++++++ .../ConfigurationWaveRepository.java | 48 +++++ .../server/repository/PaiementRepository.java | 103 +++++++++++ .../repository/TransactionWaveRepository.java | 99 ++++++++++ .../repository/WebhookWaveRepository.java | 94 ++++++++++ 23 files changed, 1594 insertions(+) create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/MethodePaiement.java create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/StatutPaiement.java create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutCompteWave.java create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutTransactionWave.java create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutWebhook.java create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeEvenementWebhook.java create mode 100644 unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeTransactionWave.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/CompteWave.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/ConfigurationWave.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Paiement.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAdhesion.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAide.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementCotisation.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementEvenement.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/TransactionWave.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/WebhookWave.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/CompteWaveRepository.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/ConfigurationWaveRepository.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/PaiementRepository.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/TransactionWaveRepository.java create mode 100644 unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/WebhookWaveRepository.java diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/MethodePaiement.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/MethodePaiement.java new file mode 100644 index 0000000..6661358 --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/MethodePaiement.java @@ -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; + } +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/StatutPaiement.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/StatutPaiement.java new file mode 100644 index 0000000..7522a5b --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/paiement/StatutPaiement.java @@ -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; + } +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutCompteWave.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutCompteWave.java new file mode 100644 index 0000000..acbcaae --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutCompteWave.java @@ -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; + } +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutTransactionWave.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutTransactionWave.java new file mode 100644 index 0000000..9b54a8e --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutTransactionWave.java @@ -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; + } +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutWebhook.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutWebhook.java new file mode 100644 index 0000000..3d96b71 --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/StatutWebhook.java @@ -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; + } +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeEvenementWebhook.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeEvenementWebhook.java new file mode 100644 index 0000000..336ce3d --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeEvenementWebhook.java @@ -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; + } +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeTransactionWave.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeTransactionWave.java new file mode 100644 index 0000000..b57e453 --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/enums/wave/TypeTransactionWave.java @@ -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; + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/CompteWave.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/CompteWave.java new file mode 100644 index 0000000..5aa1293 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/CompteWave.java @@ -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 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"; + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/ConfigurationWave.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/ConfigurationWave.java new file mode 100644 index 0000000..6adca19 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/ConfigurationWave.java @@ -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"; + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Membre.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Membre.java index 96a56aa..552676f 100644 --- a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Membre.java +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Membre.java @@ -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 roles = new ArrayList<>(); + @OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @Builder.Default + private List comptesWave = new ArrayList<>(); + + @OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @Builder.Default + private List paiements = new ArrayList<>(); + /** Méthode métier pour obtenir le nom complet */ public String getNomComplet() { return prenom + " " + nom; diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Organisation.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Organisation.java index 287c696..cd5eddd 100644 --- a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Organisation.java +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Organisation.java @@ -195,6 +195,10 @@ public class Organisation extends BaseEntity { @Builder.Default private List adresses = new ArrayList<>(); + @OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @Builder.Default + private List comptesWave = new ArrayList<>(); + /** Méthode métier pour obtenir le nom complet avec sigle */ public String getNomComplet() { if (nomCourt != null && !nomCourt.isEmpty()) { diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Paiement.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Paiement.java new file mode 100644 index 0000000..ff583be --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/Paiement.java @@ -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 paiementsCotisation = new ArrayList<>(); + + @OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @Builder.Default + private List paiementsAdhesion = new ArrayList<>(); + + @OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @Builder.Default + private List paiementsEvenement = new ArrayList<>(); + + @OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @Builder.Default + private List 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(); + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAdhesion.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAdhesion.java new file mode 100644 index 0000000..628999b --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAdhesion.java @@ -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(); + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAide.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAide.java new file mode 100644 index 0000000..4f9603f --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementAide.java @@ -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(); + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementCotisation.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementCotisation.java new file mode 100644 index 0000000..6f4ca60 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementCotisation.java @@ -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(); + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementEvenement.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementEvenement.java new file mode 100644 index 0000000..fb0a63b --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/PaiementEvenement.java @@ -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(); + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/TransactionWave.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/TransactionWave.java new file mode 100644 index 0000000..8510872 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/TransactionWave.java @@ -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 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); + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/WebhookWave.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/WebhookWave.java new file mode 100644 index 0000000..ec8c3e5 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/entity/WebhookWave.java @@ -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; + } + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/CompteWaveRepository.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/CompteWaveRepository.java new file mode 100644 index 0000000..b260a83 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/CompteWaveRepository.java @@ -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 { + + /** + * 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 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 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 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 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 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 findComptesVerifies() { + return find("statutCompte = ?1 AND actif = true", StatutCompteWave.VERIFIE).list(); + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/ConfigurationWaveRepository.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/ConfigurationWaveRepository.java new file mode 100644 index 0000000..2231e34 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/ConfigurationWaveRepository.java @@ -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 { + + /** + * Trouve une configuration par sa clé + * + * @param cle Clé de configuration + * @return Configuration ou Optional.empty() + */ + public Optional 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 findByEnvironnement(String environnement) { + return find("environnement = ?1 AND actif = true", environnement).list(); + } + + /** + * Trouve toutes les configurations actives + * + * @return Liste des configurations actives + */ + public List findAllActives() { + return find("actif = true ORDER BY cle ASC").list(); + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/PaiementRepository.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/PaiementRepository.java new file mode 100644 index 0000000..511744e --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/PaiementRepository.java @@ -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 { + + /** + * 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 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 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 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 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 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 paiements = findValidesParPeriode(dateDebut, dateFin); + return paiements.stream() + .map(Paiement::getMontant) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/TransactionWaveRepository.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/TransactionWaveRepository.java new file mode 100644 index 0000000..308ef4f --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/TransactionWaveRepository.java @@ -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 { + + /** + * Trouve une transaction par son identifiant Wave + * + * @param waveTransactionId Identifiant Wave + * @return Transaction ou Optional.empty() + */ + public Optional 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 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 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 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 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 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 findEchoueesRetentables() { + return find( + "statutTransaction IN (?1, ?2) AND (nombreTentatives IS NULL OR nombreTentatives < 5) ORDER BY dateCreation ASC", + StatutTransactionWave.ECHOUE, + StatutTransactionWave.EXPIRED) + .list(); + } +} + diff --git a/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/WebhookWaveRepository.java b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/WebhookWaveRepository.java new file mode 100644 index 0000000..cc410f8 --- /dev/null +++ b/unionflow-server-impl-quarkus/src/main/java/dev/lions/unionflow/server/repository/WebhookWaveRepository.java @@ -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 { + + /** + * Trouve un webhook par son identifiant d'événement Wave + * + * @param waveEventId Identifiant d'événement + * @return Webhook ou Optional.empty() + */ + public Optional 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 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 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 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 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 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 findEchouesRetentables() { + return find( + "statutTraitement = ?1 AND (nombreTentatives IS NULL OR nombreTentatives < 5) ORDER BY dateReception ASC", + StatutWebhook.ECHOUE) + .list(); + } +} +