Configure Maven repository for unionflow-server-api dependency
This commit is contained in:
132
src/main/java/dev/lions/unionflow/server/entity/Adhesion.java
Normal file
132
src/main/java/dev/lions/unionflow/server/entity/Adhesion.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Adhesion avec UUID
|
||||
* Représente une demande d'adhésion d'un membre à une organisation
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "adhesions",
|
||||
indexes = {
|
||||
@Index(name = "idx_adhesion_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_adhesion_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_adhesion_reference", columnList = "numero_reference", unique = true),
|
||||
@Index(name = "idx_adhesion_statut", columnList = "statut"),
|
||||
@Index(name = "idx_adhesion_date_demande", columnList = "date_demande")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Adhesion extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "numero_reference", unique = true, nullable = false, length = 50)
|
||||
private String numeroReference;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_demande", nullable = false)
|
||||
private LocalDate dateDemande;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant des frais d'adhésion doit être positif")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "frais_adhesion", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal fraisAdhesion;
|
||||
|
||||
@Builder.Default
|
||||
@DecimalMin(value = "0.0", message = "Le montant payé doit être positif")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_paye", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal montantPaye = BigDecimal.ZERO;
|
||||
|
||||
@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;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(
|
||||
regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE|EN_PAIEMENT|PAYEE)$",
|
||||
message = "Statut invalide")
|
||||
@Column(name = "statut", nullable = false, length = 30)
|
||||
private String statut;
|
||||
|
||||
@Column(name = "date_approbation")
|
||||
private LocalDate dateApprobation;
|
||||
|
||||
@Column(name = "date_paiement")
|
||||
private LocalDateTime datePaiement;
|
||||
|
||||
@Size(max = 20)
|
||||
@Column(name = "methode_paiement", length = 20)
|
||||
private String methodePaiement;
|
||||
|
||||
@Size(max = 100)
|
||||
@Column(name = "reference_paiement", length = 100)
|
||||
private String referencePaiement;
|
||||
|
||||
@Size(max = 1000)
|
||||
@Column(name = "motif_rejet", length = 1000)
|
||||
private String motifRejet;
|
||||
|
||||
@Size(max = 1000)
|
||||
@Column(name = "observations", length = 1000)
|
||||
private String observations;
|
||||
|
||||
@Column(name = "approuve_par", length = 255)
|
||||
private String approuvePar;
|
||||
|
||||
@Column(name = "date_validation")
|
||||
private LocalDate dateValidation;
|
||||
|
||||
/** Méthode métier pour vérifier si l'adhésion est payée intégralement */
|
||||
public boolean isPayeeIntegralement() {
|
||||
return montantPaye != null
|
||||
&& fraisAdhesion != null
|
||||
&& montantPaye.compareTo(fraisAdhesion) >= 0;
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'adhésion est en attente de paiement */
|
||||
public boolean isEnAttentePaiement() {
|
||||
return "APPROUVEE".equals(statut) && !isPayeeIntegralement();
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer le montant restant à payer */
|
||||
public BigDecimal getMontantRestant() {
|
||||
if (fraisAdhesion == null) return BigDecimal.ZERO;
|
||||
if (montantPaye == null) return fraisAdhesion;
|
||||
BigDecimal restant = fraisAdhesion.subtract(montantPaye);
|
||||
return restant.compareTo(BigDecimal.ZERO) > 0 ? restant : BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
154
src/main/java/dev/lions/unionflow/server/entity/Adresse.java
Normal file
154
src/main/java/dev/lions/unionflow/server/entity/Adresse.java
Normal file
@@ -0,0 +1,154 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.adresse.TypeAdresse;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Adresse pour la gestion des adresses des organisations, membres et événements
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "adresses",
|
||||
indexes = {
|
||||
@Index(name = "idx_adresse_ville", columnList = "ville"),
|
||||
@Index(name = "idx_adresse_pays", columnList = "pays"),
|
||||
@Index(name = "idx_adresse_type", columnList = "type_adresse"),
|
||||
@Index(name = "idx_adresse_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_adresse_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_adresse_evenement", columnList = "evenement_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Adresse extends BaseEntity {
|
||||
|
||||
/** Type d'adresse */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_adresse", nullable = false, length = 50)
|
||||
private TypeAdresse typeAdresse;
|
||||
|
||||
/** Adresse complète */
|
||||
@Column(name = "adresse", length = 500)
|
||||
private String adresse;
|
||||
|
||||
/** Complément d'adresse */
|
||||
@Column(name = "complement_adresse", length = 200)
|
||||
private String complementAdresse;
|
||||
|
||||
/** Code postal */
|
||||
@Column(name = "code_postal", length = 20)
|
||||
private String codePostal;
|
||||
|
||||
/** Ville */
|
||||
@Column(name = "ville", length = 100)
|
||||
private String ville;
|
||||
|
||||
/** Région */
|
||||
@Column(name = "region", length = 100)
|
||||
private String region;
|
||||
|
||||
/** Pays */
|
||||
@Column(name = "pays", length = 100)
|
||||
private String pays;
|
||||
|
||||
/** Coordonnées géographiques - Latitude */
|
||||
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@Digits(integer = 3, fraction = 6)
|
||||
@Column(name = "latitude", precision = 9, scale = 6)
|
||||
private BigDecimal latitude;
|
||||
|
||||
/** Coordonnées géographiques - Longitude */
|
||||
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@Digits(integer = 3, fraction = 6)
|
||||
@Column(name = "longitude", precision = 9, scale = 6)
|
||||
private BigDecimal longitude;
|
||||
|
||||
/** Adresse principale (une seule par entité) */
|
||||
@Builder.Default
|
||||
@Column(name = "principale", nullable = false)
|
||||
private Boolean principale = false;
|
||||
|
||||
/** Libellé personnalisé */
|
||||
@Column(name = "libelle", length = 100)
|
||||
private String libelle;
|
||||
|
||||
/** Notes et commentaires */
|
||||
@Column(name = "notes", length = 500)
|
||||
private String notes;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id")
|
||||
private Membre membre;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "evenement_id")
|
||||
private Evenement evenement;
|
||||
|
||||
/** Méthode métier pour obtenir l'adresse complète formatée */
|
||||
public String getAdresseComplete() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (adresse != null && !adresse.isEmpty()) {
|
||||
sb.append(adresse);
|
||||
}
|
||||
if (complementAdresse != null && !complementAdresse.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(complementAdresse);
|
||||
}
|
||||
if (codePostal != null && !codePostal.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(codePostal);
|
||||
}
|
||||
if (ville != null && !ville.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(" ");
|
||||
sb.append(ville);
|
||||
}
|
||||
if (region != null && !region.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(region);
|
||||
}
|
||||
if (pays != null && !pays.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(pays);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'adresse a des coordonnées GPS */
|
||||
public boolean hasCoordinates() {
|
||||
return latitude != null && longitude != null;
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate(); // Appelle le onCreate de BaseEntity
|
||||
if (typeAdresse == null) {
|
||||
typeAdresse = dev.lions.unionflow.server.api.enums.adresse.TypeAdresse.AUTRE;
|
||||
}
|
||||
if (principale == null) {
|
||||
principale = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Entité pour les logs d'audit
|
||||
* Enregistre toutes les actions importantes du système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_logs", indexes = {
|
||||
@Index(name = "idx_audit_date_heure", columnList = "date_heure"),
|
||||
@Index(name = "idx_audit_utilisateur", columnList = "utilisateur"),
|
||||
@Index(name = "idx_audit_module", columnList = "module"),
|
||||
@Index(name = "idx_audit_type_action", columnList = "type_action"),
|
||||
@Index(name = "idx_audit_severite", columnList = "severite")
|
||||
})
|
||||
@Getter
|
||||
@Setter
|
||||
public class AuditLog extends BaseEntity {
|
||||
|
||||
@Column(name = "type_action", nullable = false, length = 50)
|
||||
private String typeAction;
|
||||
|
||||
@Column(name = "severite", nullable = false, length = 20)
|
||||
private String severite;
|
||||
|
||||
@Column(name = "utilisateur", length = 255)
|
||||
private String utilisateur;
|
||||
|
||||
@Column(name = "role", length = 50)
|
||||
private String role;
|
||||
|
||||
@Column(name = "module", length = 50)
|
||||
private String module;
|
||||
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
@Column(name = "details", columnDefinition = "TEXT")
|
||||
private String details;
|
||||
|
||||
@Column(name = "ip_address", length = 45)
|
||||
private String ipAddress;
|
||||
|
||||
@Column(name = "user_agent", length = 500)
|
||||
private String userAgent;
|
||||
|
||||
@Column(name = "session_id", length = 255)
|
||||
private String sessionId;
|
||||
|
||||
@Column(name = "date_heure", nullable = false)
|
||||
private LocalDateTime dateHeure;
|
||||
|
||||
@Column(name = "donnees_avant", columnDefinition = "TEXT")
|
||||
private String donneesAvant;
|
||||
|
||||
@Column(name = "donnees_apres", columnDefinition = "TEXT")
|
||||
private String donneesApres;
|
||||
|
||||
@Column(name = "entite_id", length = 255)
|
||||
private String entiteId;
|
||||
|
||||
@Column(name = "entite_type", length = 100)
|
||||
private String entiteType;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (dateHeure == null) {
|
||||
dateHeure = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
141
src/main/java/dev/lions/unionflow/server/entity/BaseEntity.java
Normal file
141
src/main/java/dev/lions/unionflow/server/entity/BaseEntity.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Classe de base pour les entités UnionFlow utilisant UUID comme identifiant
|
||||
*
|
||||
* <p>Remplace PanacheEntity pour utiliser UUID au lieu de Long comme ID.
|
||||
* Fournit les fonctionnalités de base de Panache avec UUID.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(name = "id", updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "date_creation", nullable = false, updatable = false)
|
||||
protected LocalDateTime dateCreation;
|
||||
|
||||
@Column(name = "date_modification")
|
||||
protected LocalDateTime dateModification;
|
||||
|
||||
@Column(name = "cree_par", length = 255)
|
||||
protected String creePar;
|
||||
|
||||
@Column(name = "modifie_par", length = 255)
|
||||
protected String modifiePar;
|
||||
|
||||
@Version
|
||||
@Column(name = "version")
|
||||
protected Long version;
|
||||
|
||||
@Column(name = "actif", nullable = false)
|
||||
protected Boolean actif = true;
|
||||
|
||||
// Constructeur par défaut
|
||||
public BaseEntity() {
|
||||
this.dateCreation = LocalDateTime.now();
|
||||
this.actif = true;
|
||||
this.version = 0L;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateCreation() {
|
||||
return dateCreation;
|
||||
}
|
||||
|
||||
public void setDateCreation(LocalDateTime dateCreation) {
|
||||
this.dateCreation = dateCreation;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateModification() {
|
||||
return dateModification;
|
||||
}
|
||||
|
||||
public void setDateModification(LocalDateTime dateModification) {
|
||||
this.dateModification = dateModification;
|
||||
}
|
||||
|
||||
public String getCreePar() {
|
||||
return creePar;
|
||||
}
|
||||
|
||||
public void setCreePar(String creePar) {
|
||||
this.creePar = creePar;
|
||||
}
|
||||
|
||||
public String getModifiePar() {
|
||||
return modifiePar;
|
||||
}
|
||||
|
||||
public void setModifiePar(String modifiePar) {
|
||||
this.modifiePar = modifiePar;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Boolean getActif() {
|
||||
return actif;
|
||||
}
|
||||
|
||||
public void setActif(Boolean actif) {
|
||||
this.actif = actif;
|
||||
}
|
||||
|
||||
// Callbacks JPA
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (this.dateCreation == null) {
|
||||
this.dateCreation = LocalDateTime.now();
|
||||
}
|
||||
if (this.actif == null) {
|
||||
this.actif = true;
|
||||
}
|
||||
if (this.version == null) {
|
||||
this.version = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
this.dateModification = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// Méthodes utilitaires Panache-like
|
||||
public void persist() {
|
||||
// Cette méthode sera implémentée par les repositories ou services
|
||||
// Pour l'instant, elle est là pour compatibilité avec le code existant
|
||||
throw new UnsupportedOperationException(
|
||||
"Utilisez le repository approprié pour persister cette entité");
|
||||
}
|
||||
|
||||
public static <T extends BaseEntity> T findById(UUID id) {
|
||||
// Cette méthode sera implémentée par les repositories
|
||||
throw new UnsupportedOperationException(
|
||||
"Utilisez le repository approprié pour rechercher par ID");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité CompteComptable pour le plan comptable
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "comptes_comptables",
|
||||
indexes = {
|
||||
@Index(name = "idx_compte_numero", columnList = "numero_compte", unique = true),
|
||||
@Index(name = "idx_compte_type", columnList = "type_compte"),
|
||||
@Index(name = "idx_compte_classe", columnList = "classe_comptable")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CompteComptable extends BaseEntity {
|
||||
|
||||
/** Numéro de compte unique (ex: 411000, 512000) */
|
||||
@NotBlank
|
||||
@Column(name = "numero_compte", unique = true, nullable = false, length = 10)
|
||||
private String numeroCompte;
|
||||
|
||||
/** Libellé du compte */
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 200)
|
||||
private String libelle;
|
||||
|
||||
/** Type de compte */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_compte", nullable = false, length = 30)
|
||||
private TypeCompteComptable typeCompte;
|
||||
|
||||
/** Classe comptable (1-7) */
|
||||
@NotNull
|
||||
@Min(value = 1, message = "La classe comptable doit être entre 1 et 7")
|
||||
@Max(value = 7, message = "La classe comptable doit être entre 1 et 7")
|
||||
@Column(name = "classe_comptable", nullable = false)
|
||||
private Integer classeComptable;
|
||||
|
||||
/** Solde initial */
|
||||
@Builder.Default
|
||||
@DecimalMin(value = "0.0", message = "Le solde initial doit être positif ou nul")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "solde_initial", precision = 14, scale = 2)
|
||||
private BigDecimal soldeInitial = BigDecimal.ZERO;
|
||||
|
||||
/** Solde actuel (calculé) */
|
||||
@Builder.Default
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "solde_actuel", precision = 14, scale = 2)
|
||||
private BigDecimal soldeActuel = BigDecimal.ZERO;
|
||||
|
||||
/** Compte collectif (regroupe plusieurs sous-comptes) */
|
||||
@Builder.Default
|
||||
@Column(name = "compte_collectif", nullable = false)
|
||||
private Boolean compteCollectif = false;
|
||||
|
||||
/** Compte analytique */
|
||||
@Builder.Default
|
||||
@Column(name = "compte_analytique", nullable = false)
|
||||
private Boolean compteAnalytique = false;
|
||||
|
||||
/** Description du compte */
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
/** Lignes d'écriture associées */
|
||||
@OneToMany(mappedBy = "compteComptable", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<LigneEcriture> lignesEcriture = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour obtenir le numéro formaté */
|
||||
public String getNumeroFormate() {
|
||||
return String.format("%-10s", numeroCompte);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si c'est un compte de trésorerie */
|
||||
public boolean isTresorerie() {
|
||||
return TypeCompteComptable.TRESORERIE.equals(typeCompte);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (soldeInitial == null) {
|
||||
soldeInitial = BigDecimal.ZERO;
|
||||
}
|
||||
if (soldeActuel == null) {
|
||||
soldeActuel = soldeInitial;
|
||||
}
|
||||
if (compteCollectif == null) {
|
||||
compteCollectif = false;
|
||||
}
|
||||
if (compteAnalytique == null) {
|
||||
compteAnalytique = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
107
src/main/java/dev/lions/unionflow/server/entity/CompteWave.java
Normal file
107
src/main/java/dev/lions/unionflow/server/entity/CompteWave.java
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
184
src/main/java/dev/lions/unionflow/server/entity/Cotisation.java
Normal file
184
src/main/java/dev/lions/unionflow/server/entity/Cotisation.java
Normal file
@@ -0,0 +1,184 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Cotisation avec UUID Représente une cotisation d'un membre à son organisation
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "cotisations",
|
||||
indexes = {
|
||||
@Index(name = "idx_cotisation_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_cotisation_reference", columnList = "numero_reference", unique = true),
|
||||
@Index(name = "idx_cotisation_statut", columnList = "statut"),
|
||||
@Index(name = "idx_cotisation_echeance", columnList = "date_echeance"),
|
||||
@Index(name = "idx_cotisation_type", columnList = "type_cotisation"),
|
||||
@Index(name = "idx_cotisation_annee_mois", columnList = "annee, mois")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Cotisation extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "numero_reference", unique = true, nullable = false, length = 50)
|
||||
private String numeroReference;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "type_cotisation", nullable = false, length = 50)
|
||||
private String typeCotisation;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant dû doit être positif")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_du", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal montantDu;
|
||||
|
||||
@Builder.Default
|
||||
@DecimalMin(value = "0.0", message = "Le montant payé doit être positif")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_paye", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal montantPaye = BigDecimal.ZERO;
|
||||
|
||||
@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;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(EN_ATTENTE|PAYEE|EN_RETARD|PARTIELLEMENT_PAYEE|ANNULEE)$")
|
||||
@Column(name = "statut", nullable = false, length = 30)
|
||||
private String statut;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_echeance", nullable = false)
|
||||
private LocalDate dateEcheance;
|
||||
|
||||
@Column(name = "date_paiement")
|
||||
private LocalDateTime datePaiement;
|
||||
|
||||
@Size(max = 500)
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
@Size(max = 20)
|
||||
@Column(name = "periode", length = 20)
|
||||
private String periode;
|
||||
|
||||
@NotNull
|
||||
@Min(value = 2020, message = "L'année doit être supérieure à 2020")
|
||||
@Max(value = 2100, message = "L'année doit être inférieure à 2100")
|
||||
@Column(name = "annee", nullable = false)
|
||||
private Integer annee;
|
||||
|
||||
@Min(value = 1, message = "Le mois doit être entre 1 et 12")
|
||||
@Max(value = 12, message = "Le mois doit être entre 1 et 12")
|
||||
@Column(name = "mois")
|
||||
private Integer mois;
|
||||
|
||||
@Size(max = 1000)
|
||||
@Column(name = "observations", length = 1000)
|
||||
private String observations;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "recurrente", nullable = false)
|
||||
private Boolean recurrente = false;
|
||||
|
||||
@Builder.Default
|
||||
@Min(value = 0, message = "Le nombre de rappels doit être positif")
|
||||
@Column(name = "nombre_rappels", nullable = false)
|
||||
private Integer nombreRappels = 0;
|
||||
|
||||
@Column(name = "date_dernier_rappel")
|
||||
private LocalDateTime dateDernierRappel;
|
||||
|
||||
@Column(name = "valide_par_id")
|
||||
private UUID valideParId;
|
||||
|
||||
@Size(max = 100)
|
||||
@Column(name = "nom_validateur", length = 100)
|
||||
private String nomValidateur;
|
||||
|
||||
@Column(name = "date_validation")
|
||||
private LocalDateTime dateValidation;
|
||||
|
||||
@Size(max = 50)
|
||||
@Column(name = "methode_paiement", length = 50)
|
||||
private String methodePaiement;
|
||||
|
||||
@Size(max = 100)
|
||||
@Column(name = "reference_paiement", length = 100)
|
||||
private String referencePaiement;
|
||||
|
||||
/** Méthode métier pour calculer le montant restant à payer */
|
||||
public BigDecimal getMontantRestant() {
|
||||
if (montantDu == null || montantPaye == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return montantDu.subtract(montantPaye);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si la cotisation est entièrement payée */
|
||||
public boolean isEntierementPayee() {
|
||||
return getMontantRestant().compareTo(BigDecimal.ZERO) <= 0;
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si la cotisation est en retard */
|
||||
public boolean isEnRetard() {
|
||||
return dateEcheance != null && dateEcheance.isBefore(LocalDate.now()) && !isEntierementPayee();
|
||||
}
|
||||
|
||||
/** Méthode métier pour générer un numéro de référence unique */
|
||||
public static String genererNumeroReference() {
|
||||
return "COT-"
|
||||
+ LocalDate.now().getYear()
|
||||
+ "-"
|
||||
+ String.format("%08d", System.currentTimeMillis() % 100000000);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate(); // Appelle le onCreate de BaseEntity
|
||||
if (numeroReference == null || numeroReference.isEmpty()) {
|
||||
numeroReference = genererNumeroReference();
|
||||
}
|
||||
if (codeDevise == null) {
|
||||
codeDevise = "XOF";
|
||||
}
|
||||
if (statut == null) {
|
||||
statut = "EN_ATTENTE";
|
||||
}
|
||||
if (montantPaye == null) {
|
||||
montantPaye = BigDecimal.ZERO;
|
||||
}
|
||||
if (nombreRappels == null) {
|
||||
nombreRappels = 0;
|
||||
}
|
||||
if (recurrente == null) {
|
||||
recurrente = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
src/main/java/dev/lions/unionflow/server/entity/DemandeAide.java
Normal file
130
src/main/java/dev/lions/unionflow/server/entity/DemandeAide.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import jakarta.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/** Entité représentant une demande d'aide dans le système de solidarité */
|
||||
@Entity
|
||||
@Table(name = "demandes_aide")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DemandeAide extends BaseEntity {
|
||||
|
||||
@Column(name = "titre", nullable = false, length = 200)
|
||||
private String titre;
|
||||
|
||||
@Column(name = "description", nullable = false, columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_aide", nullable = false)
|
||||
private TypeAide typeAide;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false)
|
||||
private StatutAide statut;
|
||||
|
||||
@Column(name = "montant_demande", precision = 10, scale = 2)
|
||||
private BigDecimal montantDemande;
|
||||
|
||||
@Column(name = "montant_approuve", precision = 10, scale = 2)
|
||||
private BigDecimal montantApprouve;
|
||||
|
||||
@Column(name = "date_demande", nullable = false)
|
||||
private LocalDateTime dateDemande;
|
||||
|
||||
@Column(name = "date_evaluation")
|
||||
private LocalDateTime dateEvaluation;
|
||||
|
||||
@Column(name = "date_versement")
|
||||
private LocalDateTime dateVersement;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demandeur_id", nullable = false)
|
||||
private Membre demandeur;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "evaluateur_id")
|
||||
private Membre evaluateur;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@Column(name = "justification", columnDefinition = "TEXT")
|
||||
private String justification;
|
||||
|
||||
@Column(name = "commentaire_evaluation", columnDefinition = "TEXT")
|
||||
private String commentaireEvaluation;
|
||||
|
||||
@Column(name = "urgence", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean urgence = false;
|
||||
|
||||
@Column(name = "documents_fournis")
|
||||
private String documentsFournis;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate(); // Appelle le onCreate de BaseEntity
|
||||
if (dateDemande == null) {
|
||||
dateDemande = LocalDateTime.now();
|
||||
}
|
||||
if (statut == null) {
|
||||
statut = StatutAide.EN_ATTENTE;
|
||||
}
|
||||
if (urgence == null) {
|
||||
urgence = false;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
// Méthode appelée avant mise à jour
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est en attente */
|
||||
public boolean isEnAttente() {
|
||||
return StatutAide.EN_ATTENTE.equals(statut);
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est approuvée */
|
||||
public boolean isApprouvee() {
|
||||
return StatutAide.APPROUVEE.equals(statut);
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est rejetée */
|
||||
public boolean isRejetee() {
|
||||
return StatutAide.REJETEE.equals(statut);
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est urgente */
|
||||
public boolean isUrgente() {
|
||||
return Boolean.TRUE.equals(urgence);
|
||||
}
|
||||
|
||||
/** Calcule le pourcentage d'approbation par rapport au montant demandé */
|
||||
public BigDecimal getPourcentageApprobation() {
|
||||
if (montantDemande == null || montantDemande.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
if (montantApprouve == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return montantApprouve
|
||||
.divide(montantDemande, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
}
|
||||
}
|
||||
128
src/main/java/dev/lions/unionflow/server/entity/Document.java
Normal file
128
src/main/java/dev/lions/unionflow/server/entity/Document.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Document pour la gestion documentaire sécurisée
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "documents",
|
||||
indexes = {
|
||||
@Index(name = "idx_document_nom_fichier", columnList = "nom_fichier"),
|
||||
@Index(name = "idx_document_type", columnList = "type_document"),
|
||||
@Index(name = "idx_document_hash_md5", columnList = "hash_md5"),
|
||||
@Index(name = "idx_document_hash_sha256", columnList = "hash_sha256")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Document extends BaseEntity {
|
||||
|
||||
/** Nom du fichier original */
|
||||
@NotBlank
|
||||
@Column(name = "nom_fichier", nullable = false, length = 255)
|
||||
private String nomFichier;
|
||||
|
||||
/** Nom original du fichier (tel que téléchargé) */
|
||||
@Column(name = "nom_original", length = 255)
|
||||
private String nomOriginal;
|
||||
|
||||
/** Chemin de stockage */
|
||||
@NotBlank
|
||||
@Column(name = "chemin_stockage", nullable = false, length = 1000)
|
||||
private String cheminStockage;
|
||||
|
||||
/** Type MIME */
|
||||
@Column(name = "type_mime", length = 100)
|
||||
private String typeMime;
|
||||
|
||||
/** Taille du fichier en octets */
|
||||
@NotNull
|
||||
@Min(value = 0, message = "La taille doit être positive")
|
||||
@Column(name = "taille_octets", nullable = false)
|
||||
private Long tailleOctets;
|
||||
|
||||
/** Type de document */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_document", length = 50)
|
||||
private TypeDocument typeDocument;
|
||||
|
||||
/** Hash MD5 pour vérification d'intégrité */
|
||||
@Column(name = "hash_md5", length = 32)
|
||||
private String hashMd5;
|
||||
|
||||
/** Hash SHA256 pour vérification d'intégrité */
|
||||
@Column(name = "hash_sha256", length = 64)
|
||||
private String hashSha256;
|
||||
|
||||
/** Description du document */
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
/** Nombre de téléchargements */
|
||||
@Builder.Default
|
||||
@Column(name = "nombre_telechargements", nullable = false)
|
||||
private Integer nombreTelechargements = 0;
|
||||
|
||||
/** Date de dernier téléchargement */
|
||||
@Column(name = "date_dernier_telechargement")
|
||||
private java.time.LocalDateTime dateDernierTelechargement;
|
||||
|
||||
/** Pièces jointes associées */
|
||||
@OneToMany(mappedBy = "document", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<PieceJointe> piecesJointes = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour vérifier l'intégrité avec MD5 */
|
||||
public boolean verifierIntegriteMd5(String hashAttendu) {
|
||||
return hashMd5 != null && hashMd5.equalsIgnoreCase(hashAttendu);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier l'intégrité avec SHA256 */
|
||||
public boolean verifierIntegriteSha256(String hashAttendu) {
|
||||
return hashSha256 != null && hashSha256.equalsIgnoreCase(hashAttendu);
|
||||
}
|
||||
|
||||
/** Méthode métier pour obtenir la taille formatée */
|
||||
public String getTailleFormatee() {
|
||||
if (tailleOctets == null) {
|
||||
return "0 B";
|
||||
}
|
||||
if (tailleOctets < 1024) {
|
||||
return tailleOctets + " B";
|
||||
} else if (tailleOctets < 1024 * 1024) {
|
||||
return String.format("%.2f KB", tailleOctets / 1024.0);
|
||||
} else {
|
||||
return String.format("%.2f MB", tailleOctets / (1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (nombreTelechargements == null) {
|
||||
nombreTelechargements = 0;
|
||||
}
|
||||
if (typeDocument == null) {
|
||||
typeDocument = TypeDocument.AUTRE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité EcritureComptable pour les écritures comptables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "ecritures_comptables",
|
||||
indexes = {
|
||||
@Index(name = "idx_ecriture_numero_piece", columnList = "numero_piece", unique = true),
|
||||
@Index(name = "idx_ecriture_date", columnList = "date_ecriture"),
|
||||
@Index(name = "idx_ecriture_journal", columnList = "journal_id"),
|
||||
@Index(name = "idx_ecriture_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_ecriture_paiement", columnList = "paiement_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EcritureComptable extends BaseEntity {
|
||||
|
||||
/** Numéro de pièce unique */
|
||||
@NotBlank
|
||||
@Column(name = "numero_piece", unique = true, nullable = false, length = 50)
|
||||
private String numeroPiece;
|
||||
|
||||
/** Date de l'écriture */
|
||||
@NotNull
|
||||
@Column(name = "date_ecriture", nullable = false)
|
||||
private LocalDate dateEcriture;
|
||||
|
||||
/** Libellé de l'écriture */
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 500)
|
||||
private String libelle;
|
||||
|
||||
/** Référence externe */
|
||||
@Column(name = "reference", length = 100)
|
||||
private String reference;
|
||||
|
||||
/** Lettrage (pour rapprochement) */
|
||||
@Column(name = "lettrage", length = 20)
|
||||
private String lettrage;
|
||||
|
||||
/** Pointage (pour rapprochement bancaire) */
|
||||
@Builder.Default
|
||||
@Column(name = "pointe", nullable = false)
|
||||
private Boolean pointe = false;
|
||||
|
||||
/** Montant total débit (somme des lignes) */
|
||||
@Builder.Default
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_debit", precision = 14, scale = 2)
|
||||
private BigDecimal montantDebit = BigDecimal.ZERO;
|
||||
|
||||
/** Montant total crédit (somme des lignes) */
|
||||
@Builder.Default
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_credit", precision = 14, scale = 2)
|
||||
private BigDecimal montantCredit = BigDecimal.ZERO;
|
||||
|
||||
/** Commentaires */
|
||||
@Column(name = "commentaire", length = 1000)
|
||||
private String commentaire;
|
||||
|
||||
// Relations
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "journal_id", nullable = false)
|
||||
private JournalComptable journal;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id")
|
||||
private Paiement paiement;
|
||||
|
||||
/** Lignes d'écriture */
|
||||
@OneToMany(mappedBy = "ecriture", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<LigneEcriture> lignes = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour vérifier l'équilibre (Débit = Crédit) */
|
||||
public boolean isEquilibree() {
|
||||
if (montantDebit == null || montantCredit == null) {
|
||||
return false;
|
||||
}
|
||||
return montantDebit.compareTo(montantCredit) == 0;
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer les totaux à partir des lignes */
|
||||
public void calculerTotaux() {
|
||||
if (lignes == null || lignes.isEmpty()) {
|
||||
montantDebit = BigDecimal.ZERO;
|
||||
montantCredit = BigDecimal.ZERO;
|
||||
return;
|
||||
}
|
||||
|
||||
montantDebit =
|
||||
lignes.stream()
|
||||
.map(LigneEcriture::getMontantDebit)
|
||||
.filter(amount -> amount != null)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
montantCredit =
|
||||
lignes.stream()
|
||||
.map(LigneEcriture::getMontantCredit)
|
||||
.filter(amount -> amount != null)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
/** Méthode métier pour générer un numéro de pièce unique */
|
||||
public static String genererNumeroPiece(String prefixe, LocalDate date) {
|
||||
return String.format(
|
||||
"%s-%04d%02d%02d-%012d",
|
||||
prefixe, date.getYear(), date.getMonthValue(), date.getDayOfMonth(),
|
||||
System.currentTimeMillis() % 1000000000000L);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (numeroPiece == null || numeroPiece.isEmpty()) {
|
||||
numeroPiece = genererNumeroPiece("ECR", dateEcriture != null ? dateEcriture : LocalDate.now());
|
||||
}
|
||||
if (dateEcriture == null) {
|
||||
dateEcriture = LocalDate.now();
|
||||
}
|
||||
if (montantDebit == null) {
|
||||
montantDebit = BigDecimal.ZERO;
|
||||
}
|
||||
if (montantCredit == null) {
|
||||
montantCredit = BigDecimal.ZERO;
|
||||
}
|
||||
if (pointe == null) {
|
||||
pointe = false;
|
||||
}
|
||||
// Calculer les totaux si les lignes sont déjà présentes
|
||||
if (lignes != null && !lignes.isEmpty()) {
|
||||
calculerTotaux();
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback JPA avant la mise à jour */
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
calculerTotaux();
|
||||
}
|
||||
}
|
||||
|
||||
267
src/main/java/dev/lions/unionflow/server/entity/Evenement.java
Normal file
267
src/main/java/dev/lions/unionflow/server/entity/Evenement.java
Normal file
@@ -0,0 +1,267 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Entité Événement pour la gestion des événements de l'union
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "evenements",
|
||||
indexes = {
|
||||
@Index(name = "idx_evenement_date_debut", columnList = "date_debut"),
|
||||
@Index(name = "idx_evenement_statut", columnList = "statut"),
|
||||
@Index(name = "idx_evenement_type", columnList = "type_evenement"),
|
||||
@Index(name = "idx_evenement_organisation", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Evenement extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 200)
|
||||
@Column(name = "titre", nullable = false, length = 200)
|
||||
private String titre;
|
||||
|
||||
@Size(max = 2000)
|
||||
@Column(name = "description", length = 2000)
|
||||
private String description;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_debut", nullable = false)
|
||||
private LocalDateTime dateDebut;
|
||||
|
||||
@Column(name = "date_fin")
|
||||
private LocalDateTime dateFin;
|
||||
|
||||
@Size(max = 500)
|
||||
@Column(name = "lieu", length = 500)
|
||||
private String lieu;
|
||||
|
||||
@Size(max = 1000)
|
||||
@Column(name = "adresse", length = 1000)
|
||||
private String adresse;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_evenement", length = 50)
|
||||
private TypeEvenement typeEvenement;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut", nullable = false, length = 30)
|
||||
private StatutEvenement statut = StatutEvenement.PLANIFIE;
|
||||
|
||||
@Min(0)
|
||||
@Column(name = "capacite_max")
|
||||
private Integer capaciteMax;
|
||||
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 8, fraction = 2)
|
||||
@Column(name = "prix", precision = 10, scale = 2)
|
||||
private BigDecimal prix;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "inscription_requise", nullable = false)
|
||||
private Boolean inscriptionRequise = false;
|
||||
|
||||
@Column(name = "date_limite_inscription")
|
||||
private LocalDateTime dateLimiteInscription;
|
||||
|
||||
@Size(max = 1000)
|
||||
@Column(name = "instructions_particulieres", length = 1000)
|
||||
private String instructionsParticulieres;
|
||||
|
||||
@Size(max = 500)
|
||||
@Column(name = "contact_organisateur", length = 500)
|
||||
private String contactOrganisateur;
|
||||
|
||||
@Size(max = 2000)
|
||||
@Column(name = "materiel_requis", length = 2000)
|
||||
private String materielRequis;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "visible_public", nullable = false)
|
||||
private Boolean visiblePublic = true;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "actif", nullable = false)
|
||||
private Boolean actif = true;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisateur_id")
|
||||
private Membre organisateur;
|
||||
|
||||
@OneToMany(
|
||||
mappedBy = "evenement",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true,
|
||||
fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<InscriptionEvenement> inscriptions = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "evenement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Adresse> adresses = new ArrayList<>();
|
||||
|
||||
/** Types d'événements */
|
||||
public enum TypeEvenement {
|
||||
ASSEMBLEE_GENERALE("Assemblée Générale"),
|
||||
REUNION("Réunion"),
|
||||
FORMATION("Formation"),
|
||||
CONFERENCE("Conférence"),
|
||||
ATELIER("Atelier"),
|
||||
SEMINAIRE("Séminaire"),
|
||||
EVENEMENT_SOCIAL("Événement Social"),
|
||||
MANIFESTATION("Manifestation"),
|
||||
CELEBRATION("Célébration"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeEvenement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
/** Statuts d'événement */
|
||||
public enum StatutEvenement {
|
||||
PLANIFIE("Planifié"),
|
||||
CONFIRME("Confirmé"),
|
||||
EN_COURS("En cours"),
|
||||
TERMINE("Terminé"),
|
||||
ANNULE("Annulé"),
|
||||
REPORTE("Reporté");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutEvenement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes métier
|
||||
|
||||
/** Vérifie si l'événement est ouvert aux inscriptions */
|
||||
public boolean isOuvertAuxInscriptions() {
|
||||
if (!inscriptionRequise || !actif) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
|
||||
// Vérifier si la date limite d'inscription n'est pas dépassée
|
||||
if (dateLimiteInscription != null && maintenant.isAfter(dateLimiteInscription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier si l'événement n'a pas déjà commencé
|
||||
if (maintenant.isAfter(dateDebut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier la capacité
|
||||
if (capaciteMax != null && getNombreInscrits() >= capaciteMax) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return statut == StatutEvenement.PLANIFIE || statut == StatutEvenement.CONFIRME;
|
||||
}
|
||||
|
||||
/** Obtient le nombre d'inscrits à l'événement */
|
||||
public int getNombreInscrits() {
|
||||
return inscriptions != null
|
||||
? (int)
|
||||
inscriptions.stream()
|
||||
.filter(
|
||||
inscription ->
|
||||
inscription.getStatut() == InscriptionEvenement.StatutInscription.CONFIRMEE)
|
||||
.count()
|
||||
: 0;
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement est complet */
|
||||
public boolean isComplet() {
|
||||
return capaciteMax != null && getNombreInscrits() >= capaciteMax;
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement est en cours */
|
||||
public boolean isEnCours() {
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
return maintenant.isAfter(dateDebut) && (dateFin == null || maintenant.isBefore(dateFin));
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement est terminé */
|
||||
public boolean isTermine() {
|
||||
if (statut == StatutEvenement.TERMINE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
return dateFin != null && maintenant.isAfter(dateFin);
|
||||
}
|
||||
|
||||
/** Calcule la durée de l'événement en heures */
|
||||
public Long getDureeEnHeures() {
|
||||
if (dateFin == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return java.time.Duration.between(dateDebut, dateFin).toHours();
|
||||
}
|
||||
|
||||
/** Obtient le nombre de places restantes */
|
||||
public Integer getPlacesRestantes() {
|
||||
if (capaciteMax == null) {
|
||||
return null; // Capacité illimitée
|
||||
}
|
||||
|
||||
return Math.max(0, capaciteMax - getNombreInscrits());
|
||||
}
|
||||
|
||||
/** Vérifie si un membre est inscrit à l'événement */
|
||||
public boolean isMemberInscrit(UUID membreId) {
|
||||
return inscriptions != null
|
||||
&& inscriptions.stream()
|
||||
.anyMatch(
|
||||
inscription ->
|
||||
inscription.getMembre().getId().equals(membreId)
|
||||
&& inscription.getStatut()
|
||||
== InscriptionEvenement.StatutInscription.CONFIRMEE);
|
||||
}
|
||||
|
||||
/** Obtient le taux de remplissage en pourcentage */
|
||||
public Double getTauxRemplissage() {
|
||||
if (capaciteMax == null || capaciteMax == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (double) getNombreInscrits() / capaciteMax * 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Entité InscriptionEvenement représentant l'inscription d'un membre à un événement
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "inscriptions_evenement",
|
||||
indexes = {
|
||||
@Index(name = "idx_inscription_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_inscription_evenement", columnList = "evenement_id"),
|
||||
@Index(name = "idx_inscription_date", columnList = "date_inscription")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class InscriptionEvenement extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "evenement_id", nullable = false)
|
||||
private Evenement evenement;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "date_inscription", nullable = false)
|
||||
private LocalDateTime dateInscription = LocalDateTime.now();
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", length = 20)
|
||||
@Builder.Default
|
||||
private StatutInscription statut = StatutInscription.CONFIRMEE;
|
||||
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Énumération des statuts d'inscription */
|
||||
public enum StatutInscription {
|
||||
CONFIRMEE("Confirmée"),
|
||||
EN_ATTENTE("En attente"),
|
||||
ANNULEE("Annulée"),
|
||||
REFUSEE("Refusée");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutInscription(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Vérifie si l'inscription est confirmée
|
||||
*
|
||||
* @return true si l'inscription est confirmée
|
||||
*/
|
||||
public boolean isConfirmee() {
|
||||
return StatutInscription.CONFIRMEE.equals(this.statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'inscription est en attente
|
||||
*
|
||||
* @return true si l'inscription est en attente
|
||||
*/
|
||||
public boolean isEnAttente() {
|
||||
return StatutInscription.EN_ATTENTE.equals(this.statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'inscription est annulée
|
||||
*
|
||||
* @return true si l'inscription est annulée
|
||||
*/
|
||||
public boolean isAnnulee() {
|
||||
return StatutInscription.ANNULEE.equals(this.statut);
|
||||
}
|
||||
|
||||
/** Confirme l'inscription */
|
||||
public void confirmer() {
|
||||
this.statut = StatutInscription.CONFIRMEE;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Annule l'inscription
|
||||
*
|
||||
* @param commentaire le commentaire d'annulation
|
||||
*/
|
||||
public void annuler(String commentaire) {
|
||||
this.statut = StatutInscription.ANNULEE;
|
||||
this.commentaire = commentaire;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Met l'inscription en attente
|
||||
*
|
||||
* @param commentaire le commentaire de mise en attente
|
||||
*/
|
||||
public void mettreEnAttente(String commentaire) {
|
||||
this.statut = StatutInscription.EN_ATTENTE;
|
||||
this.commentaire = commentaire;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refuse l'inscription
|
||||
*
|
||||
* @param commentaire le commentaire de refus
|
||||
*/
|
||||
public void refuser(String commentaire) {
|
||||
this.statut = StatutInscription.REFUSEE;
|
||||
this.commentaire = commentaire;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// Callbacks JPA
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
super.onUpdate(); // Appelle le onUpdate de BaseEntity
|
||||
this.dateModification = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"InscriptionEvenement{id=%s, membre=%s, evenement=%s, statut=%s, dateInscription=%s}",
|
||||
getId(),
|
||||
membre != null ? membre.getEmail() : "null",
|
||||
evenement != null ? evenement.getTitre() : "null",
|
||||
statut,
|
||||
dateInscription);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité JournalComptable pour la gestion des journaux
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "journaux_comptables",
|
||||
indexes = {
|
||||
@Index(name = "idx_journal_code", columnList = "code", unique = true),
|
||||
@Index(name = "idx_journal_type", columnList = "type_journal"),
|
||||
@Index(name = "idx_journal_periode", columnList = "date_debut, date_fin")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class JournalComptable extends BaseEntity {
|
||||
|
||||
/** Code unique du journal */
|
||||
@NotBlank
|
||||
@Column(name = "code", unique = true, nullable = false, length = 10)
|
||||
private String code;
|
||||
|
||||
/** Libellé du journal */
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 100)
|
||||
private String libelle;
|
||||
|
||||
/** Type de journal */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_journal", nullable = false, length = 30)
|
||||
private TypeJournalComptable typeJournal;
|
||||
|
||||
/** Date de début de la période */
|
||||
@Column(name = "date_debut")
|
||||
private LocalDate dateDebut;
|
||||
|
||||
/** Date de fin de la période */
|
||||
@Column(name = "date_fin")
|
||||
private LocalDate dateFin;
|
||||
|
||||
/** Statut du journal (OUVERT, FERME, ARCHIVE) */
|
||||
@Builder.Default
|
||||
@Column(name = "statut", length = 20)
|
||||
private String statut = "OUVERT";
|
||||
|
||||
/** Description */
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
/** Écritures comptables associées */
|
||||
@OneToMany(mappedBy = "journal", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<EcritureComptable> ecritures = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour vérifier si le journal est ouvert */
|
||||
public boolean isOuvert() {
|
||||
return "OUVERT".equals(statut);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si une date est dans la période */
|
||||
public boolean estDansPeriode(LocalDate date) {
|
||||
if (dateDebut == null || dateFin == null) {
|
||||
return true; // Période illimitée
|
||||
}
|
||||
return !date.isBefore(dateDebut) && !date.isAfter(dateFin);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statut == null || statut.isEmpty()) {
|
||||
statut = "OUVERT";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité LigneEcriture pour les lignes d'une écriture comptable
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "lignes_ecriture",
|
||||
indexes = {
|
||||
@Index(name = "idx_ligne_ecriture_ecriture", columnList = "ecriture_id"),
|
||||
@Index(name = "idx_ligne_ecriture_compte", columnList = "compte_comptable_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LigneEcriture extends BaseEntity {
|
||||
|
||||
/** Numéro de ligne */
|
||||
@NotNull
|
||||
@Min(value = 1, message = "Le numéro de ligne doit être positif")
|
||||
@Column(name = "numero_ligne", nullable = false)
|
||||
private Integer numeroLigne;
|
||||
|
||||
/** Montant débit */
|
||||
@DecimalMin(value = "0.0", message = "Le montant débit doit être positif ou nul")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_debit", precision = 14, scale = 2)
|
||||
private BigDecimal montantDebit;
|
||||
|
||||
/** Montant crédit */
|
||||
@DecimalMin(value = "0.0", message = "Le montant crédit doit être positif ou nul")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_credit", precision = 14, scale = 2)
|
||||
private BigDecimal montantCredit;
|
||||
|
||||
/** Libellé de la ligne */
|
||||
@Column(name = "libelle", length = 500)
|
||||
private String libelle;
|
||||
|
||||
/** Référence */
|
||||
@Column(name = "reference", length = 100)
|
||||
private String reference;
|
||||
|
||||
// Relations
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "ecriture_id", nullable = false)
|
||||
private EcritureComptable ecriture;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "compte_comptable_id", nullable = false)
|
||||
private CompteComptable compteComptable;
|
||||
|
||||
/** Méthode métier pour vérifier que la ligne a soit un débit soit un crédit (pas les deux) */
|
||||
public boolean isValide() {
|
||||
boolean aDebit = montantDebit != null && montantDebit.compareTo(BigDecimal.ZERO) > 0;
|
||||
boolean aCredit = montantCredit != null && montantCredit.compareTo(BigDecimal.ZERO) > 0;
|
||||
return aDebit != aCredit; // XOR : soit débit, soit crédit, pas les deux
|
||||
}
|
||||
|
||||
/** Méthode métier pour obtenir le montant (débit ou crédit) */
|
||||
public BigDecimal getMontant() {
|
||||
if (montantDebit != null && montantDebit.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return montantDebit;
|
||||
}
|
||||
if (montantCredit != null && montantCredit.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return montantCredit;
|
||||
}
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (montantDebit == null) {
|
||||
montantDebit = BigDecimal.ZERO;
|
||||
}
|
||||
if (montantCredit == null) {
|
||||
montantCredit = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
106
src/main/java/dev/lions/unionflow/server/entity/Membre.java
Normal file
106
src/main/java/dev/lions/unionflow/server/entity/Membre.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/** Entité Membre avec UUID */
|
||||
@Entity
|
||||
@Table(
|
||||
name = "membres",
|
||||
indexes = {
|
||||
@Index(name = "idx_membre_email", columnList = "email", unique = true),
|
||||
@Index(name = "idx_membre_numero", columnList = "numero_membre", unique = true),
|
||||
@Index(name = "idx_membre_actif", columnList = "actif")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Membre extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "numero_membre", unique = true, nullable = false, length = 20)
|
||||
private String numeroMembre;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "prenom", nullable = false, length = 100)
|
||||
private String prenom;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "nom", nullable = false, length = 100)
|
||||
private String nom;
|
||||
|
||||
@Email
|
||||
@NotBlank
|
||||
@Column(name = "email", unique = true, nullable = false, length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(name = "mot_de_passe", length = 255)
|
||||
private String motDePasse;
|
||||
|
||||
@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;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_adhesion", nullable = false)
|
||||
private LocalDate dateAdhesion;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Adresse> adresses = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@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;
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le membre est majeur */
|
||||
public boolean isMajeur() {
|
||||
return dateNaissance.isBefore(LocalDate.now().minusYears(18));
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer l'âge */
|
||||
public int getAge() {
|
||||
return LocalDate.now().getYear() - dateNaissance.getYear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison entre Membre et Role
|
||||
* Permet à un membre d'avoir plusieurs rôles avec dates de début/fin
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "membres_roles",
|
||||
indexes = {
|
||||
@Index(name = "idx_membre_role_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_membre_role_role", columnList = "role_id"),
|
||||
@Index(name = "idx_membre_role_actif", columnList = "actif")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_membre_role",
|
||||
columnNames = {"membre_id", "role_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MembreRole extends BaseEntity {
|
||||
|
||||
/** Membre */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
/** Rôle */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "role_id", nullable = false)
|
||||
private Role role;
|
||||
|
||||
/** Date de début d'attribution */
|
||||
@Column(name = "date_debut")
|
||||
private LocalDate dateDebut;
|
||||
|
||||
/** Date de fin d'attribution (null = permanent) */
|
||||
@Column(name = "date_fin")
|
||||
private LocalDate dateFin;
|
||||
|
||||
/** Commentaire sur l'attribution */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Méthode métier pour vérifier si l'attribution est active */
|
||||
public boolean isActif() {
|
||||
if (!Boolean.TRUE.equals(getActif())) {
|
||||
return false;
|
||||
}
|
||||
LocalDate aujourdhui = LocalDate.now();
|
||||
if (dateDebut != null && aujourdhui.isBefore(dateDebut)) {
|
||||
return false;
|
||||
}
|
||||
if (dateFin != null && aujourdhui.isAfter(dateFin)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateDebut == null) {
|
||||
dateDebut = LocalDate.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.notification.PrioriteNotification;
|
||||
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Notification pour la gestion des notifications
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "notifications",
|
||||
indexes = {
|
||||
@Index(name = "idx_notification_type", columnList = "type_notification"),
|
||||
@Index(name = "idx_notification_statut", columnList = "statut"),
|
||||
@Index(name = "idx_notification_priorite", columnList = "priorite"),
|
||||
@Index(name = "idx_notification_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_notification_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_notification_date_envoi", columnList = "date_envoi")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Notification extends BaseEntity {
|
||||
|
||||
/** Type de notification */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_notification", nullable = false, length = 30)
|
||||
private TypeNotification typeNotification;
|
||||
|
||||
/** Priorité */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "priorite", length = 20)
|
||||
private PrioriteNotification priorite = PrioriteNotification.NORMALE;
|
||||
|
||||
/** Statut */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut", length = 30)
|
||||
private dev.lions.unionflow.server.api.enums.notification.StatutNotification statut =
|
||||
dev.lions.unionflow.server.api.enums.notification.StatutNotification.EN_ATTENTE;
|
||||
|
||||
/** Sujet */
|
||||
@Column(name = "sujet", length = 500)
|
||||
private String sujet;
|
||||
|
||||
/** Corps du message */
|
||||
@Column(name = "corps", columnDefinition = "TEXT")
|
||||
private String corps;
|
||||
|
||||
/** Date d'envoi prévue */
|
||||
@Column(name = "date_envoi_prevue")
|
||||
private LocalDateTime dateEnvoiPrevue;
|
||||
|
||||
/** Date d'envoi réelle */
|
||||
@Column(name = "date_envoi")
|
||||
private LocalDateTime dateEnvoi;
|
||||
|
||||
/** Date de lecture */
|
||||
@Column(name = "date_lecture")
|
||||
private LocalDateTime dateLecture;
|
||||
|
||||
/** Nombre de tentatives d'envoi */
|
||||
@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;
|
||||
|
||||
/** Données additionnelles (JSON) */
|
||||
@Column(name = "donnees_additionnelles", columnDefinition = "TEXT")
|
||||
private String donneesAdditionnelles;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id")
|
||||
private Membre membre;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "template_id")
|
||||
private TemplateNotification template;
|
||||
|
||||
/** Méthode métier pour vérifier si la notification est envoyée */
|
||||
public boolean isEnvoyee() {
|
||||
return dev.lions.unionflow.server.api.enums.notification.StatutNotification.ENVOYEE.equals(statut);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si la notification est lue */
|
||||
public boolean isLue() {
|
||||
return dev.lions.unionflow.server.api.enums.notification.StatutNotification.LUE.equals(statut);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (priorite == null) {
|
||||
priorite = PrioriteNotification.NORMALE;
|
||||
}
|
||||
if (statut == null) {
|
||||
statut = dev.lions.unionflow.server.api.enums.notification.StatutNotification.EN_ATTENTE;
|
||||
}
|
||||
if (nombreTentatives == null) {
|
||||
nombreTentatives = 0;
|
||||
}
|
||||
if (dateEnvoiPrevue == null) {
|
||||
dateEnvoiPrevue = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Period;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Organisation avec UUID Représente une organisation (Lions Club, Association,
|
||||
* Coopérative, etc.)
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "organisations",
|
||||
indexes = {
|
||||
@Index(name = "idx_organisation_nom", columnList = "nom"),
|
||||
@Index(name = "idx_organisation_email", columnList = "email", unique = true),
|
||||
@Index(name = "idx_organisation_statut", columnList = "statut"),
|
||||
@Index(name = "idx_organisation_type", columnList = "type_organisation"),
|
||||
@Index(name = "idx_organisation_ville", columnList = "ville"),
|
||||
@Index(name = "idx_organisation_pays", columnList = "pays"),
|
||||
@Index(name = "idx_organisation_parente", columnList = "organisation_parente_id"),
|
||||
@Index(
|
||||
name = "idx_organisation_numero_enregistrement",
|
||||
columnList = "numero_enregistrement",
|
||||
unique = true)
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Organisation extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "nom", nullable = false, length = 200)
|
||||
private String nom;
|
||||
|
||||
@Column(name = "nom_court", length = 50)
|
||||
private String nomCourt;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "type_organisation", nullable = false, length = 50)
|
||||
private String typeOrganisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
private String statut;
|
||||
|
||||
@Column(name = "description", length = 2000)
|
||||
private String description;
|
||||
|
||||
@Column(name = "date_fondation")
|
||||
private LocalDate dateFondation;
|
||||
|
||||
@Column(name = "numero_enregistrement", unique = true, length = 100)
|
||||
private String numeroEnregistrement;
|
||||
|
||||
// Informations de contact
|
||||
@Email
|
||||
@NotBlank
|
||||
@Column(name = "email", unique = true, nullable = false, length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(name = "telephone", length = 20)
|
||||
private String telephone;
|
||||
|
||||
@Column(name = "telephone_secondaire", length = 20)
|
||||
private String telephoneSecondaire;
|
||||
|
||||
@Email
|
||||
@Column(name = "email_secondaire", length = 255)
|
||||
private String emailSecondaire;
|
||||
|
||||
// Adresse
|
||||
@Column(name = "adresse", length = 500)
|
||||
private String adresse;
|
||||
|
||||
@Column(name = "ville", length = 100)
|
||||
private String ville;
|
||||
|
||||
@Column(name = "code_postal", length = 20)
|
||||
private String codePostal;
|
||||
|
||||
@Column(name = "region", length = 100)
|
||||
private String region;
|
||||
|
||||
@Column(name = "pays", length = 100)
|
||||
private String pays;
|
||||
|
||||
// Coordonnées géographiques
|
||||
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@Digits(integer = 3, fraction = 6)
|
||||
@Column(name = "latitude", precision = 9, scale = 6)
|
||||
private BigDecimal latitude;
|
||||
|
||||
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@Digits(integer = 3, fraction = 6)
|
||||
@Column(name = "longitude", precision = 9, scale = 6)
|
||||
private BigDecimal longitude;
|
||||
|
||||
// Web et réseaux sociaux
|
||||
@Column(name = "site_web", length = 500)
|
||||
private String siteWeb;
|
||||
|
||||
@Column(name = "logo", length = 500)
|
||||
private String logo;
|
||||
|
||||
@Column(name = "reseaux_sociaux", length = 1000)
|
||||
private String reseauxSociaux;
|
||||
|
||||
// Hiérarchie
|
||||
@Column(name = "organisation_parente_id")
|
||||
private UUID organisationParenteId;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "niveau_hierarchique", nullable = false)
|
||||
private Integer niveauHierarchique = 0;
|
||||
|
||||
// Statistiques
|
||||
@Builder.Default
|
||||
@Column(name = "nombre_membres", nullable = false)
|
||||
private Integer nombreMembres = 0;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "nombre_administrateurs", nullable = false)
|
||||
private Integer nombreAdministrateurs = 0;
|
||||
|
||||
// Finances
|
||||
@DecimalMin(value = "0.0", message = "Le budget annuel doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "budget_annuel", precision = 14, scale = 2)
|
||||
private BigDecimal budgetAnnuel;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "devise", length = 3)
|
||||
private String devise = "XOF";
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "cotisation_obligatoire", nullable = false)
|
||||
private Boolean cotisationObligatoire = false;
|
||||
|
||||
@DecimalMin(value = "0.0", message = "Le montant de cotisation doit être positif")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_cotisation_annuelle", precision = 12, scale = 2)
|
||||
private BigDecimal montantCotisationAnnuelle;
|
||||
|
||||
// Informations complémentaires
|
||||
@Column(name = "objectifs", length = 2000)
|
||||
private String objectifs;
|
||||
|
||||
@Column(name = "activites_principales", length = 2000)
|
||||
private String activitesPrincipales;
|
||||
|
||||
@Column(name = "certifications", length = 500)
|
||||
private String certifications;
|
||||
|
||||
@Column(name = "partenaires", length = 1000)
|
||||
private String partenaires;
|
||||
|
||||
@Column(name = "notes", length = 1000)
|
||||
private String notes;
|
||||
|
||||
// Paramètres
|
||||
@Builder.Default
|
||||
@Column(name = "organisation_publique", nullable = false)
|
||||
private Boolean organisationPublique = true;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "accepte_nouveaux_membres", nullable = false)
|
||||
private Boolean accepteNouveauxMembres = true;
|
||||
|
||||
// Relations
|
||||
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Membre> membres = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@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()) {
|
||||
return nom + " (" + nomCourt + ")";
|
||||
}
|
||||
return nom;
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer l'ancienneté en années */
|
||||
public int getAncienneteAnnees() {
|
||||
if (dateFondation == null) {
|
||||
return 0;
|
||||
}
|
||||
return Period.between(dateFondation, LocalDate.now()).getYears();
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'organisation est récente (moins de 2 ans) */
|
||||
public boolean isRecente() {
|
||||
return getAncienneteAnnees() < 2;
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'organisation est active */
|
||||
public boolean isActive() {
|
||||
return "ACTIVE".equals(statut) && Boolean.TRUE.equals(getActif());
|
||||
}
|
||||
|
||||
/** Méthode métier pour ajouter un membre */
|
||||
public void ajouterMembre() {
|
||||
if (nombreMembres == null) {
|
||||
nombreMembres = 0;
|
||||
}
|
||||
nombreMembres++;
|
||||
}
|
||||
|
||||
/** Méthode métier pour retirer un membre */
|
||||
public void retirerMembre() {
|
||||
if (nombreMembres != null && nombreMembres > 0) {
|
||||
nombreMembres--;
|
||||
}
|
||||
}
|
||||
|
||||
/** Méthode métier pour activer l'organisation */
|
||||
public void activer(String utilisateur) {
|
||||
this.statut = "ACTIVE";
|
||||
this.setActif(true);
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/** Méthode métier pour suspendre l'organisation */
|
||||
public void suspendre(String utilisateur) {
|
||||
this.statut = "SUSPENDUE";
|
||||
this.accepteNouveauxMembres = false;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/** Méthode métier pour dissoudre l'organisation */
|
||||
public void dissoudre(String utilisateur) {
|
||||
this.statut = "DISSOUTE";
|
||||
this.setActif(false);
|
||||
this.accepteNouveauxMembres = false;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/** Marque l'entité comme modifiée */
|
||||
public void marquerCommeModifie(String utilisateur) {
|
||||
this.setDateModification(LocalDateTime.now());
|
||||
this.setModifiePar(utilisateur);
|
||||
if (this.getVersion() != null) {
|
||||
this.setVersion(this.getVersion() + 1);
|
||||
} else {
|
||||
this.setVersion(1L);
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate(); // Appelle le onCreate de BaseEntity
|
||||
if (statut == null) {
|
||||
statut = "ACTIVE";
|
||||
}
|
||||
if (typeOrganisation == null) {
|
||||
typeOrganisation = "ASSOCIATION";
|
||||
}
|
||||
if (devise == null) {
|
||||
devise = "XOF";
|
||||
}
|
||||
if (niveauHierarchique == null) {
|
||||
niveauHierarchique = 0;
|
||||
}
|
||||
if (nombreMembres == null) {
|
||||
nombreMembres = 0;
|
||||
}
|
||||
if (nombreAdministrateurs == null) {
|
||||
nombreAdministrateurs = 0;
|
||||
}
|
||||
if (organisationPublique == null) {
|
||||
organisationPublique = true;
|
||||
}
|
||||
if (accepteNouveauxMembres == null) {
|
||||
accepteNouveauxMembres = true;
|
||||
}
|
||||
if (cotisationObligatoire == null) {
|
||||
cotisationObligatoire = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/main/java/dev/lions/unionflow/server/entity/Paiement.java
Normal file
169
src/main/java/dev/lions/unionflow/server/entity/Paiement.java
Normal file
@@ -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,90 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Permission pour la gestion des permissions granulaires
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "permissions",
|
||||
indexes = {
|
||||
@Index(name = "idx_permission_code", columnList = "code", unique = true),
|
||||
@Index(name = "idx_permission_module", columnList = "module"),
|
||||
@Index(name = "idx_permission_ressource", columnList = "ressource")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Permission extends BaseEntity {
|
||||
|
||||
/** Code unique de la permission (format: MODULE > RESSOURCE > ACTION) */
|
||||
@NotBlank
|
||||
@Column(name = "code", unique = true, nullable = false, length = 100)
|
||||
private String code;
|
||||
|
||||
/** Module (ex: ORGANISATION, MEMBRE, COTISATION) */
|
||||
@NotBlank
|
||||
@Column(name = "module", nullable = false, length = 50)
|
||||
private String module;
|
||||
|
||||
/** Ressource (ex: MEMBRE, COTISATION, ADHESION) */
|
||||
@NotBlank
|
||||
@Column(name = "ressource", nullable = false, length = 50)
|
||||
private String ressource;
|
||||
|
||||
/** Action (ex: CREATE, READ, UPDATE, DELETE, VALIDATE) */
|
||||
@NotBlank
|
||||
@Column(name = "action", nullable = false, length = 50)
|
||||
private String action;
|
||||
|
||||
/** Libellé de la permission */
|
||||
@Column(name = "libelle", length = 200)
|
||||
private String libelle;
|
||||
|
||||
/** Description de la permission */
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
/** Rôles associés */
|
||||
@OneToMany(mappedBy = "permission", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<RolePermission> roles = new ArrayList<>();
|
||||
|
||||
/** Méthode métier pour générer le code à partir des composants */
|
||||
public static String genererCode(String module, String ressource, String action) {
|
||||
return String.format("%s > %s > %s", module.toUpperCase(), ressource.toUpperCase(), action.toUpperCase());
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le code est valide */
|
||||
public boolean isCodeValide() {
|
||||
return code != null && code.contains(" > ") && code.split(" > ").length == 3;
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
// Générer le code si non fourni
|
||||
if (code == null || code.isEmpty()) {
|
||||
if (module != null && ressource != null && action != null) {
|
||||
code = genererCode(module, ressource, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
src/main/java/dev/lions/unionflow/server/entity/PieceJointe.java
Normal file
103
src/main/java/dev/lions/unionflow/server/entity/PieceJointe.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité PieceJointe pour l'association flexible de documents
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "pieces_jointes",
|
||||
indexes = {
|
||||
@Index(name = "idx_piece_jointe_document", columnList = "document_id"),
|
||||
@Index(name = "idx_piece_jointe_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_piece_jointe_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_piece_jointe_cotisation", columnList = "cotisation_id"),
|
||||
@Index(name = "idx_piece_jointe_adhesion", columnList = "adhesion_id"),
|
||||
@Index(name = "idx_piece_jointe_demande_aide", columnList = "demande_aide_id"),
|
||||
@Index(name = "idx_piece_jointe_transaction_wave", columnList = "transaction_wave_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PieceJointe extends BaseEntity {
|
||||
|
||||
/** Ordre d'affichage */
|
||||
@NotNull
|
||||
@Min(value = 1, message = "L'ordre doit être positif")
|
||||
@Column(name = "ordre", nullable = false)
|
||||
private Integer ordre;
|
||||
|
||||
/** Libellé de la pièce jointe */
|
||||
@Column(name = "libelle", length = 200)
|
||||
private String libelle;
|
||||
|
||||
/** Commentaire */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Document associé */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "document_id", nullable = false)
|
||||
private Document document;
|
||||
|
||||
// Relations flexibles (une seule doit être renseignée)
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id")
|
||||
private Membre membre;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "cotisation_id")
|
||||
private Cotisation cotisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "adhesion_id")
|
||||
private Adhesion adhesion;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demande_aide_id")
|
||||
private DemandeAide demandeAide;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "transaction_wave_id")
|
||||
private TransactionWave transactionWave;
|
||||
|
||||
/** Méthode métier pour vérifier qu'une seule relation est renseignée */
|
||||
public boolean isValide() {
|
||||
int count = 0;
|
||||
if (membre != null) count++;
|
||||
if (organisation != null) count++;
|
||||
if (cotisation != null) count++;
|
||||
if (adhesion != null) count++;
|
||||
if (demandeAide != null) count++;
|
||||
if (transactionWave != null) count++;
|
||||
return count == 1; // Exactement une relation doit être renseignée
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (ordre == null) {
|
||||
ordre = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
105
src/main/java/dev/lions/unionflow/server/entity/Role.java
Normal file
105
src/main/java/dev/lions/unionflow/server/entity/Role.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Role pour la gestion des rôles dans le système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "roles",
|
||||
indexes = {
|
||||
@Index(name = "idx_role_code", columnList = "code", unique = true),
|
||||
@Index(name = "idx_role_actif", columnList = "actif"),
|
||||
@Index(name = "idx_role_niveau", columnList = "niveau_hierarchique")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Role extends BaseEntity {
|
||||
|
||||
/** Code unique du rôle */
|
||||
@NotBlank
|
||||
@Column(name = "code", unique = true, nullable = false, length = 50)
|
||||
private String code;
|
||||
|
||||
/** Libellé du rôle */
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 100)
|
||||
private String libelle;
|
||||
|
||||
/** Description du rôle */
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
/** Niveau hiérarchique (plus bas = plus prioritaire) */
|
||||
@NotNull
|
||||
@Builder.Default
|
||||
@Column(name = "niveau_hierarchique", nullable = false)
|
||||
private Integer niveauHierarchique = 100;
|
||||
|
||||
/** Type de rôle */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_role", nullable = false, length = 50)
|
||||
private TypeRole typeRole;
|
||||
|
||||
/** Organisation propriétaire (null pour rôles système) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
/** Permissions associées */
|
||||
@OneToMany(mappedBy = "role", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<RolePermission> permissions = new ArrayList<>();
|
||||
|
||||
/** Énumération des types de rôle */
|
||||
public enum TypeRole {
|
||||
SYSTEME("Rôle Système"),
|
||||
ORGANISATION("Rôle Organisation"),
|
||||
PERSONNALISE("Rôle Personnalisé");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeRole(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si c'est un rôle système */
|
||||
public boolean isRoleSysteme() {
|
||||
return TypeRole.SYSTEME.equals(typeRole);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (typeRole == null) {
|
||||
typeRole = TypeRole.PERSONNALISE;
|
||||
}
|
||||
if (niveauHierarchique == null) {
|
||||
niveauHierarchique = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison entre Role et Permission
|
||||
* Permet à un rôle d'avoir plusieurs permissions
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "roles_permissions",
|
||||
indexes = {
|
||||
@Index(name = "idx_role_permission_role", columnList = "role_id"),
|
||||
@Index(name = "idx_role_permission_permission", columnList = "permission_id")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_role_permission",
|
||||
columnNames = {"role_id", "permission_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RolePermission extends BaseEntity {
|
||||
|
||||
/** Rôle */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "role_id", nullable = false)
|
||||
private Role role;
|
||||
|
||||
/** Permission */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "permission_id", nullable = false)
|
||||
private Permission permission;
|
||||
|
||||
/** Commentaire sur l'association */
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité TemplateNotification pour les templates de notifications réutilisables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "templates_notifications",
|
||||
indexes = {
|
||||
@Index(name = "idx_template_code", columnList = "code", unique = true),
|
||||
@Index(name = "idx_template_actif", columnList = "actif")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TemplateNotification extends BaseEntity {
|
||||
|
||||
/** Code unique du template */
|
||||
@NotBlank
|
||||
@Column(name = "code", unique = true, nullable = false, length = 100)
|
||||
private String code;
|
||||
|
||||
/** Sujet du template */
|
||||
@Column(name = "sujet", length = 500)
|
||||
private String sujet;
|
||||
|
||||
/** Corps du template (texte) */
|
||||
@Column(name = "corps_texte", columnDefinition = "TEXT")
|
||||
private String corpsTexte;
|
||||
|
||||
/** Corps du template (HTML) */
|
||||
@Column(name = "corps_html", columnDefinition = "TEXT")
|
||||
private String corpsHtml;
|
||||
|
||||
/** Variables disponibles (JSON) */
|
||||
@Column(name = "variables_disponibles", columnDefinition = "TEXT")
|
||||
private String variablesDisponibles;
|
||||
|
||||
/** Canaux supportés (JSON array) */
|
||||
@Column(name = "canaux_supportes", length = 500)
|
||||
private String canauxSupportes;
|
||||
|
||||
/** Langue du template */
|
||||
@Column(name = "langue", length = 10)
|
||||
private String langue;
|
||||
|
||||
/** Description */
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
/** Notifications utilisant ce template */
|
||||
@OneToMany(mappedBy = "template", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Notification> notifications = new ArrayList<>();
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (langue == null || langue.isEmpty()) {
|
||||
langue = "fr";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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")
|
||||
})
|
||||
@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,73 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
/**
|
||||
* Entité persistée représentant un type d'organisation.
|
||||
*
|
||||
* <p>Cette entité permet de gérer dynamiquement le catalogue des types d'organisations
|
||||
* (codes, libellés, description, ordre d'affichage, activation/désactivation).
|
||||
*
|
||||
* <p>Le champ {@code code} doit rester synchronisé avec l'enum {@link
|
||||
* dev.lions.unionflow.server.api.enums.organisation.TypeOrganisation} pour les types
|
||||
* standards fournis par la plateforme.
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "uf_type_organisation",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_type_organisation_code",
|
||||
columnNames = {"code"})
|
||||
})
|
||||
public class TypeOrganisationEntity extends BaseEntity {
|
||||
|
||||
@Column(name = "code", length = 50, nullable = false, unique = true)
|
||||
private String code;
|
||||
|
||||
@Column(name = "libelle", length = 150, nullable = false)
|
||||
private String libelle;
|
||||
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
@Column(name = "ordre_affichage")
|
||||
private Integer ordreAffichage;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public void setLibelle(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Integer getOrdreAffichage() {
|
||||
return ordreAffichage;
|
||||
}
|
||||
|
||||
public void setOrdreAffichage(Integer ordreAffichage) {
|
||||
this.ordreAffichage = ordreAffichage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
118
src/main/java/dev/lions/unionflow/server/entity/WebhookWave.java
Normal file
118
src/main/java/dev/lions/unionflow/server/entity/WebhookWave.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user