Sync: code local unifié
Synchronisation du code source local (fait foi). Signed-off-by: lions dev Team
This commit is contained in:
@@ -1,132 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
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;
|
||||
@@ -12,23 +10,22 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Adresse pour la gestion des adresses des organisations, membres et événements
|
||||
* 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")
|
||||
})
|
||||
@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
|
||||
@@ -36,10 +33,9 @@ import lombok.NoArgsConstructor;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Adresse extends BaseEntity {
|
||||
|
||||
/** Type d'adresse */
|
||||
@Enumerated(EnumType.STRING)
|
||||
/** Type d'adresse (code depuis types_reference) */
|
||||
@Column(name = "type_adresse", nullable = false, length = 50)
|
||||
private TypeAdresse typeAdresse;
|
||||
private String typeAdresse;
|
||||
|
||||
/** Adresse complète */
|
||||
@Column(name = "adresse", length = 500)
|
||||
@@ -112,23 +108,28 @@ public class Adresse extends BaseEntity {
|
||||
sb.append(adresse);
|
||||
}
|
||||
if (complementAdresse != null && !complementAdresse.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
if (sb.length() > 0)
|
||||
sb.append(", ");
|
||||
sb.append(complementAdresse);
|
||||
}
|
||||
if (codePostal != null && !codePostal.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
if (sb.length() > 0)
|
||||
sb.append(", ");
|
||||
sb.append(codePostal);
|
||||
}
|
||||
if (ville != null && !ville.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(" ");
|
||||
if (sb.length() > 0)
|
||||
sb.append(" ");
|
||||
sb.append(ville);
|
||||
}
|
||||
if (region != null && !region.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
if (sb.length() > 0)
|
||||
sb.append(", ");
|
||||
sb.append(region);
|
||||
}
|
||||
if (pays != null && !pays.isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
if (sb.length() > 0)
|
||||
sb.append(", ");
|
||||
sb.append(pays);
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -140,15 +141,10 @@ public class Adresse extends BaseEntity {
|
||||
}
|
||||
|
||||
/** 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,94 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Action d'Approbateur
|
||||
*
|
||||
* Représente l'action (approve/reject) d'un approbateur sur une demande d'approbation.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "approver_actions", indexes = {
|
||||
@Index(name = "idx_approver_action_approval", columnList = "approval_id"),
|
||||
@Index(name = "idx_approver_action_approver", columnList = "approver_id"),
|
||||
@Index(name = "idx_approver_action_decision", columnList = "decision"),
|
||||
@Index(name = "idx_approver_action_decided_at", columnList = "decided_at")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ApproverAction extends BaseEntity {
|
||||
|
||||
/** Approbation parente */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "approval_id", nullable = false)
|
||||
private TransactionApproval approval;
|
||||
|
||||
/** ID de l'approbateur (membre) */
|
||||
@NotNull
|
||||
@Column(name = "approver_id", nullable = false)
|
||||
private UUID approverId;
|
||||
|
||||
/** Nom complet de l'approbateur (cache) */
|
||||
@NotBlank
|
||||
@Column(name = "approver_name", nullable = false, length = 200)
|
||||
private String approverName;
|
||||
|
||||
/** Rôle de l'approbateur au moment de l'action */
|
||||
@NotBlank
|
||||
@Column(name = "approver_role", nullable = false, length = 50)
|
||||
private String approverRole;
|
||||
|
||||
/** Décision (PENDING, APPROVED, REJECTED) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(PENDING|APPROVED|REJECTED)$")
|
||||
@Builder.Default
|
||||
@Column(name = "decision", nullable = false, length = 10)
|
||||
private String decision = "PENDING";
|
||||
|
||||
/** Commentaire optionnel */
|
||||
@Size(max = 1000)
|
||||
@Column(name = "comment", length = 1000)
|
||||
private String comment;
|
||||
|
||||
/** Date de la décision */
|
||||
@Column(name = "decided_at")
|
||||
private LocalDateTime decidedAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (decision == null) {
|
||||
decision = "PENDING";
|
||||
}
|
||||
}
|
||||
|
||||
/** Méthode métier pour approuver avec commentaire */
|
||||
public void approve(String comment) {
|
||||
this.decision = "APPROVED";
|
||||
this.comment = comment;
|
||||
this.decidedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/** Méthode métier pour rejeter avec raison */
|
||||
public void reject(String reason) {
|
||||
this.decision = "REJECTED";
|
||||
this.comment = reason;
|
||||
this.decidedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.audit.PorteeAudit;
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -70,7 +70,24 @@ public class AuditLog extends BaseEntity {
|
||||
|
||||
@Column(name = "entite_type", length = 100)
|
||||
private String entiteType;
|
||||
|
||||
|
||||
/**
|
||||
* Organisation concernée par cet événement d'audit.
|
||||
* NULL pour les événements de portée PLATEFORME.
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
/**
|
||||
* Portée de visibilité :
|
||||
* ORGANISATION = visible par le manager de l'organisation
|
||||
* PLATEFORME = visible uniquement par le Super Admin UnionFlow
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "portee", nullable = false, length = 15)
|
||||
private PorteeAudit portee = PorteeAudit.PLATEFORME;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (dateHeure == null) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.ayantdroit.LienParente;
|
||||
import dev.lions.unionflow.server.api.enums.ayantdroit.StatutAyantDroit;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDate;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Ayant droit d'un membre dans une mutuelle de santé.
|
||||
*
|
||||
* <p>
|
||||
* Permet la gestion des bénéficiaires (conjoint, enfants, parents) pour
|
||||
* les conventions avec les centres de santé partenaires et les plafonds
|
||||
* annuels.
|
||||
*
|
||||
* <p>
|
||||
* Table : {@code ayants_droit}
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "ayants_droit", indexes = {
|
||||
@Index(name = "idx_ad_membre_org", columnList = "membre_organisation_id"),
|
||||
@Index(name = "idx_ad_couverture", columnList = "date_debut_couverture, date_fin_couverture")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AyantDroit extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_organisation_id", nullable = false)
|
||||
private MembreOrganisation membreOrganisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "prenom", nullable = false, length = 100)
|
||||
private String prenom;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "nom", nullable = false, length = 100)
|
||||
private String nom;
|
||||
|
||||
@Column(name = "date_naissance")
|
||||
private LocalDate dateNaissance;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@NotNull
|
||||
@Column(name = "lien_parente", nullable = false, length = 20)
|
||||
private LienParente lienParente;
|
||||
|
||||
/** Numéro attribué pour les conventions santé avec les centres partenaires */
|
||||
@Column(name = "numero_beneficiaire", length = 50)
|
||||
private String numeroBeneficiaire;
|
||||
|
||||
@Column(name = "date_debut_couverture")
|
||||
private LocalDate dateDebutCouverture;
|
||||
|
||||
/** NULL = couverture ouverte */
|
||||
@Column(name = "date_fin_couverture")
|
||||
private LocalDate dateFinCouverture;
|
||||
|
||||
@Column(name = "sexe", length = 20)
|
||||
private String sexe;
|
||||
|
||||
@Column(name = "piece_identite", length = 100)
|
||||
private String pieceIdentite;
|
||||
|
||||
@Column(name = "pourcentage_couverture", precision = 5, scale = 2)
|
||||
private BigDecimal pourcentageCouvertureSante;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutAyantDroit statut = StatutAyantDroit.EN_ATTENTE;
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isCouvertAujourdhui() {
|
||||
LocalDate today = LocalDate.now();
|
||||
if (dateDebutCouverture != null && today.isBefore(dateDebutCouverture))
|
||||
return false;
|
||||
if (dateFinCouverture != null && today.isAfter(dateFinCouverture))
|
||||
return false;
|
||||
return Boolean.TRUE.equals(getActif());
|
||||
}
|
||||
|
||||
public String getNomComplet() {
|
||||
return prenom + " " + nom;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +1,79 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import dev.lions.unionflow.server.entity.listener.AuditEntityListener;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
import jakarta.persistence.Version;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Classe de base pour toutes les entités UnionFlow.
|
||||
*
|
||||
* <p>
|
||||
* Étend PanacheEntityBase pour bénéficier du pattern Active Record et résoudre
|
||||
* les warnings Hibernate.
|
||||
* Fournit les champs communs d'audit et le versioning optimistic.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
* @version 4.0
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
@EntityListeners(AuditEntityListener.class)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public abstract class BaseEntity extends PanacheEntityBase {
|
||||
|
||||
/** Identifiant unique auto-généré. */
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(name = "id", updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
/**
|
||||
* Date de création.
|
||||
*/
|
||||
@Column(name = "date_creation", nullable = false, updatable = false)
|
||||
protected LocalDateTime dateCreation;
|
||||
private LocalDateTime dateCreation;
|
||||
|
||||
/**
|
||||
* Date de dernière modification.
|
||||
*/
|
||||
@Column(name = "date_modification")
|
||||
protected LocalDateTime dateModification;
|
||||
private LocalDateTime dateModification;
|
||||
|
||||
/**
|
||||
* Email de l'utilisateur ayant créé l'entité.
|
||||
*/
|
||||
@Column(name = "cree_par", length = 255)
|
||||
protected String creePar;
|
||||
private String creePar;
|
||||
|
||||
/**
|
||||
* Email du dernier utilisateur ayant modifié l'entité.
|
||||
*/
|
||||
@Column(name = "modifie_par", length = 255)
|
||||
protected String modifiePar;
|
||||
private String modifiePar;
|
||||
|
||||
/** Version pour l'optimistic locking JPA. */
|
||||
@Version
|
||||
@Column(name = "version")
|
||||
protected Long version;
|
||||
private Long version;
|
||||
|
||||
/**
|
||||
* État actif/inactif pour le soft-delete.
|
||||
*/
|
||||
@Column(name = "actif", nullable = false)
|
||||
protected Boolean actif = true;
|
||||
private Boolean actif;
|
||||
|
||||
// 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) {
|
||||
@@ -114,9 +82,6 @@ public abstract class BaseEntity {
|
||||
if (this.actif == null) {
|
||||
this.actif = true;
|
||||
}
|
||||
if (this.version == null) {
|
||||
this.version = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
@@ -124,18 +89,13 @@ public abstract class BaseEntity {
|
||||
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");
|
||||
/**
|
||||
* Marque l'entité comme modifiée par un utilisateur donné.
|
||||
*
|
||||
* @param utilisateur email de l'utilisateur
|
||||
*/
|
||||
public void marquerCommeModifie(String utilisateur) {
|
||||
this.dateModification = LocalDateTime.now();
|
||||
this.modifiePar = utilisateur;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
218
src/main/java/dev/lions/unionflow/server/entity/Budget.java
Normal file
218
src/main/java/dev/lions/unionflow/server/entity/Budget.java
Normal file
@@ -0,0 +1,218 @@
|
||||
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.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é Budget
|
||||
*
|
||||
* Représente un budget prévisionnel (mensuel/trimestriel/annuel) avec suivi de réalisation.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "budgets", indexes = {
|
||||
@Index(name = "idx_budget_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_budget_status", columnList = "status"),
|
||||
@Index(name = "idx_budget_period", columnList = "period"),
|
||||
@Index(name = "idx_budget_year_month", columnList = "year, month"),
|
||||
@Index(name = "idx_budget_created_by", columnList = "created_by_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Budget extends BaseEntity {
|
||||
|
||||
/** Nom du budget */
|
||||
@NotBlank
|
||||
@Size(max = 200)
|
||||
@Column(name = "name", nullable = false, length = 200)
|
||||
private String name;
|
||||
|
||||
/** Description optionnelle */
|
||||
@Size(max = 1000)
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
/** Organisation concernée */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
/** Période (MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(MONTHLY|QUARTERLY|SEMIANNUAL|ANNUAL)$")
|
||||
@Column(name = "period", nullable = false, length = 20)
|
||||
private String period;
|
||||
|
||||
/** Année du budget */
|
||||
@NotNull
|
||||
@Min(value = 2020, message = "L'année doit être >= 2020")
|
||||
@Max(value = 2100, message = "L'année doit être <= 2100")
|
||||
@Column(name = "year", nullable = false)
|
||||
private Integer year;
|
||||
|
||||
/** Mois (1-12) pour budget mensuel, null sinon */
|
||||
@Min(value = 1)
|
||||
@Max(value = 12)
|
||||
@Column(name = "month")
|
||||
private Integer month;
|
||||
|
||||
/** Statut (DRAFT, ACTIVE, CLOSED, CANCELLED) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(DRAFT|ACTIVE|CLOSED|CANCELLED)$")
|
||||
@Builder.Default
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status = "DRAFT";
|
||||
|
||||
/** Lignes budgétaires */
|
||||
@OneToMany(mappedBy = "budget", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<BudgetLine> lines = new ArrayList<>();
|
||||
|
||||
/** Total prévu (somme des montants prévus des lignes) */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 14, fraction = 2)
|
||||
@Builder.Default
|
||||
@Column(name = "total_planned", nullable = false, precision = 16, scale = 2)
|
||||
private BigDecimal totalPlanned = BigDecimal.ZERO;
|
||||
|
||||
/** Total réalisé (somme des montants réalisés des lignes) */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 14, fraction = 2)
|
||||
@Builder.Default
|
||||
@Column(name = "total_realized", nullable = false, precision = 16, scale = 2)
|
||||
private BigDecimal totalRealized = BigDecimal.ZERO;
|
||||
|
||||
/** Code devise ISO 3 lettres */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^[A-Z]{3}$")
|
||||
@Builder.Default
|
||||
@Column(name = "currency", nullable = false, length = 3)
|
||||
private String currency = "XOF";
|
||||
|
||||
/** ID du créateur du budget */
|
||||
@NotNull
|
||||
@Column(name = "created_by_id", nullable = false)
|
||||
private UUID createdById;
|
||||
|
||||
/** Date de création */
|
||||
@NotNull
|
||||
@Column(name = "created_at_budget", nullable = false)
|
||||
private LocalDateTime createdAtBudget;
|
||||
|
||||
/** Date d'approbation */
|
||||
@Column(name = "approved_at")
|
||||
private LocalDateTime approvedAt;
|
||||
|
||||
/** ID de l'approbateur */
|
||||
@Column(name = "approved_by_id")
|
||||
private UUID approvedById;
|
||||
|
||||
/** Date de début de la période budgétaire */
|
||||
@NotNull
|
||||
@Column(name = "start_date", nullable = false)
|
||||
private LocalDate startDate;
|
||||
|
||||
/** Date de fin de la période budgétaire */
|
||||
@NotNull
|
||||
@Column(name = "end_date", nullable = false)
|
||||
private LocalDate endDate;
|
||||
|
||||
/** Métadonnées additionnelles (JSON) */
|
||||
@Column(name = "metadata", columnDefinition = "TEXT")
|
||||
private String metadata;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (createdAtBudget == null) {
|
||||
createdAtBudget = LocalDateTime.now();
|
||||
}
|
||||
if (currency == null) {
|
||||
currency = "XOF";
|
||||
}
|
||||
if (status == null) {
|
||||
status = "DRAFT";
|
||||
}
|
||||
if (totalPlanned == null) {
|
||||
totalPlanned = BigDecimal.ZERO;
|
||||
}
|
||||
if (totalRealized == null) {
|
||||
totalRealized = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
/** Méthode métier pour ajouter une ligne budgétaire */
|
||||
public void addLine(BudgetLine line) {
|
||||
lines.add(line);
|
||||
line.setBudget(this);
|
||||
recalculateTotals();
|
||||
}
|
||||
|
||||
/** Méthode métier pour supprimer une ligne budgétaire */
|
||||
public void removeLine(BudgetLine line) {
|
||||
lines.remove(line);
|
||||
line.setBudget(null);
|
||||
recalculateTotals();
|
||||
}
|
||||
|
||||
/** Méthode métier pour recalculer les totaux */
|
||||
public void recalculateTotals() {
|
||||
this.totalPlanned = lines.stream()
|
||||
.map(BudgetLine::getAmountPlanned)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
this.totalRealized = lines.stream()
|
||||
.map(BudgetLine::getAmountRealized)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer le taux de réalisation (%) */
|
||||
public double getRealizationRate() {
|
||||
if (totalPlanned.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return totalRealized.divide(totalPlanned, 4, java.math.RoundingMode.HALF_UP)
|
||||
.multiply(new BigDecimal("100"))
|
||||
.doubleValue();
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer l'écart (réalisé - prévu) */
|
||||
public BigDecimal getVariance() {
|
||||
return totalRealized.subtract(totalPlanned);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le budget est dépassé */
|
||||
public boolean isOverBudget() {
|
||||
return totalRealized.compareTo(totalPlanned) > 0;
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le budget est actif */
|
||||
public boolean isActive() {
|
||||
return "ACTIVE".equals(status);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si la période est en cours */
|
||||
public boolean isCurrentPeriod() {
|
||||
LocalDate now = LocalDate.now();
|
||||
return !now.isBefore(startDate) && !now.isAfter(endDate);
|
||||
}
|
||||
}
|
||||
102
src/main/java/dev/lions/unionflow/server/entity/BudgetLine.java
Normal file
102
src/main/java/dev/lions/unionflow/server/entity/BudgetLine.java
Normal file
@@ -0,0 +1,102 @@
|
||||
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é Ligne Budgétaire
|
||||
*
|
||||
* Représente une ligne dans un budget (catégorie de dépense/recette).
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "budget_lines", indexes = {
|
||||
@Index(name = "idx_budget_line_budget", columnList = "budget_id"),
|
||||
@Index(name = "idx_budget_line_category", columnList = "category")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BudgetLine extends BaseEntity {
|
||||
|
||||
/** Budget parent */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "budget_id", nullable = false)
|
||||
private Budget budget;
|
||||
|
||||
/** Catégorie (CONTRIBUTIONS, SAVINGS, SOLIDARITY, EVENTS, OPERATIONAL, INVESTMENTS, OTHER) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(CONTRIBUTIONS|SAVINGS|SOLIDARITY|EVENTS|OPERATIONAL|INVESTMENTS|OTHER)$")
|
||||
@Column(name = "category", nullable = false, length = 20)
|
||||
private String category;
|
||||
|
||||
/** Nom de la ligne */
|
||||
@NotBlank
|
||||
@Size(max = 200)
|
||||
@Column(name = "name", nullable = false, length = 200)
|
||||
private String name;
|
||||
|
||||
/** Description optionnelle */
|
||||
@Size(max = 500)
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
/** Montant prévu */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 14, fraction = 2)
|
||||
@Column(name = "amount_planned", nullable = false, precision = 16, scale = 2)
|
||||
private BigDecimal amountPlanned;
|
||||
|
||||
/** Montant réalisé */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 14, fraction = 2)
|
||||
@Builder.Default
|
||||
@Column(name = "amount_realized", nullable = false, precision = 16, scale = 2)
|
||||
private BigDecimal amountRealized = BigDecimal.ZERO;
|
||||
|
||||
/** Notes additionnelles */
|
||||
@Size(max = 1000)
|
||||
@Column(name = "notes", length = 1000)
|
||||
private String notes;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (amountRealized == null) {
|
||||
amountRealized = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer le taux de réalisation (%) */
|
||||
public double getRealizationRate() {
|
||||
if (amountPlanned.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return amountRealized.divide(amountPlanned, 4, java.math.RoundingMode.HALF_UP)
|
||||
.multiply(new BigDecimal("100"))
|
||||
.doubleValue();
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer l'écart */
|
||||
public BigDecimal getVariance() {
|
||||
return amountRealized.subtract(amountPlanned);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si la ligne est dépassée */
|
||||
public boolean isOverBudget() {
|
||||
return amountRealized.compareTo(amountPlanned) > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
@@ -85,6 +86,7 @@ public class CompteComptable extends BaseEntity {
|
||||
private String description;
|
||||
|
||||
/** Lignes d'écriture associées */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "compteComptable", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<LigneEcriture> lignesEcriture = new ArrayList<>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
@@ -20,14 +21,12 @@ import lombok.NoArgsConstructor;
|
||||
* @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")
|
||||
})
|
||||
@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
|
||||
@@ -37,9 +36,7 @@ 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")
|
||||
@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;
|
||||
|
||||
@@ -78,6 +75,8 @@ public class CompteWave extends BaseEntity {
|
||||
@JoinColumn(name = "membre_id")
|
||||
private Membre membre;
|
||||
|
||||
@JsonIgnore
|
||||
|
||||
@OneToMany(mappedBy = "compteWave", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<TransactionWave> transactions = new ArrayList<>();
|
||||
@@ -104,4 +103,3 @@ public class CompteWave extends BaseEntity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
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é Configuration pour la gestion de la configuration système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "configurations",
|
||||
indexes = {
|
||||
@Index(name = "idx_config_cle", columnList = "cle", unique = true),
|
||||
@Index(name = "idx_config_categorie", columnList = "categorie")
|
||||
}
|
||||
)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Configuration extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "cle", nullable = false, unique = true, length = 255)
|
||||
private String cle;
|
||||
|
||||
@Column(name = "valeur", columnDefinition = "TEXT")
|
||||
private String valeur;
|
||||
|
||||
@Column(name = "type", length = 50)
|
||||
private String type; // STRING, NUMBER, BOOLEAN, JSON, DATE
|
||||
|
||||
@Column(name = "categorie", length = 50)
|
||||
private String categorie; // SYSTEME, SECURITE, NOTIFICATION, INTEGRATION, APPEARANCE
|
||||
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
@Column(name = "modifiable")
|
||||
@Builder.Default
|
||||
private Boolean modifiable = true;
|
||||
|
||||
@Column(name = "visible")
|
||||
@Builder.Default
|
||||
private Boolean visible = true;
|
||||
|
||||
@Column(name = "metadonnees", columnDefinition = "TEXT")
|
||||
private String metadonnees; // JSON string pour stocker les métadonnées
|
||||
}
|
||||
|
||||
@@ -13,23 +13,22 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Cotisation avec UUID Représente une cotisation d'un membre à son organisation
|
||||
* 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")
|
||||
})
|
||||
@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
|
||||
@@ -46,10 +45,25 @@ public class Cotisation extends BaseEntity {
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
/** Organisation pour laquelle la cotisation est due */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
/** Intention de paiement Wave associée (null si cotisation en attente) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "intention_paiement_id")
|
||||
private IntentionPaiement intentionPaiement;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "type_cotisation", nullable = false, length = 50)
|
||||
private String typeCotisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 100)
|
||||
private String libelle;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant dû doit être positif")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@@ -124,14 +138,6 @@ public class Cotisation extends BaseEntity {
|
||||
@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) {
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Demande d'adhésion d'un utilisateur à une organisation.
|
||||
*
|
||||
* <p>Flux :
|
||||
* <ol>
|
||||
* <li>L'utilisateur crée son compte et choisit une organisation</li>
|
||||
* <li>Une {@code DemandeAdhesion} est créée (statut EN_ATTENTE)</li>
|
||||
* <li>Si frais d'adhésion : une {@link IntentionPaiement} est créée et liée</li>
|
||||
* <li>Le manager valide → {@link MembreOrganisation} créé, quota souscription décrémenté</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Remplace l'ancienne entité {@code Adhesion}.
|
||||
* Table : {@code demandes_adhesion}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "demandes_adhesion",
|
||||
indexes = {
|
||||
@Index(name = "idx_da_utilisateur", columnList = "utilisateur_id"),
|
||||
@Index(name = "idx_da_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_da_statut", columnList = "statut"),
|
||||
@Index(name = "idx_da_date", columnList = "date_demande")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DemandeAdhesion extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "numero_reference", unique = true, nullable = false, length = 50)
|
||||
private String numeroReference;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "utilisateur_id", nullable = false)
|
||||
private Membre utilisateur;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE)$")
|
||||
@Builder.Default
|
||||
@Column(name = "statut", nullable = false, length = 20)
|
||||
private String statut = "EN_ATTENTE";
|
||||
|
||||
@Builder.Default
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "frais_adhesion", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal fraisAdhesion = BigDecimal.ZERO;
|
||||
|
||||
@Builder.Default
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_paye", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal montantPaye = BigDecimal.ZERO;
|
||||
|
||||
@Builder.Default
|
||||
@Pattern(regexp = "^[A-Z]{3}$")
|
||||
@Column(name = "code_devise", nullable = false, length = 3)
|
||||
private String codeDevise = "XOF";
|
||||
|
||||
/** Intention de paiement Wave liée aux frais d'adhésion */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "intention_paiement_id")
|
||||
private IntentionPaiement intentionPaiement;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "date_demande", nullable = false)
|
||||
private LocalDateTime dateDemande = LocalDateTime.now();
|
||||
|
||||
@Column(name = "date_traitement")
|
||||
private LocalDateTime dateTraitement;
|
||||
|
||||
/** Manager/Admin qui a approuvé ou rejeté */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "traite_par_id")
|
||||
private Membre traitePar;
|
||||
|
||||
@Column(name = "motif_rejet", length = 1000)
|
||||
private String motifRejet;
|
||||
|
||||
@Column(name = "observations", length = 1000)
|
||||
private String observations;
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isEnAttente() { return "EN_ATTENTE".equals(statut); }
|
||||
public boolean isApprouvee() { return "APPROUVEE".equals(statut); }
|
||||
public boolean isRejetee() { return "REJETEE".equals(statut); }
|
||||
|
||||
public boolean isPayeeIntegralement() {
|
||||
return fraisAdhesion != null
|
||||
&& montantPaye != null
|
||||
&& montantPaye.compareTo(fraisAdhesion) >= 0;
|
||||
}
|
||||
|
||||
public static String genererNumeroReference() {
|
||||
return "ADH-" + java.time.LocalDate.now().getYear()
|
||||
+ "-" + String.format("%08d", System.currentTimeMillis() % 100000000);
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateDemande == null) dateDemande = LocalDateTime.now();
|
||||
if (statut == null) statut = "EN_ATTENTE";
|
||||
if (codeDevise == null) codeDevise = "XOF";
|
||||
if (fraisAdhesion == null) fraisAdhesion = BigDecimal.ZERO;
|
||||
if (montantPaye == null) montantPaye = BigDecimal.ZERO;
|
||||
if (numeroReference == null || numeroReference.isEmpty()) {
|
||||
numeroReference = genererNumeroReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.ArrayList;
|
||||
@@ -85,6 +86,7 @@ public class Document extends BaseEntity {
|
||||
private java.time.LocalDateTime dateDernierTelechargement;
|
||||
|
||||
/** Pièces jointes associées */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "document", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<PieceJointe> piecesJointes = new ArrayList<>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
@@ -97,6 +98,7 @@ public class EcritureComptable extends BaseEntity {
|
||||
private Paiement paiement;
|
||||
|
||||
/** Lignes d'écriture */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "ecriture", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<LigneEcriture> lignes = new ArrayList<>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
@@ -17,14 +18,12 @@ import lombok.*;
|
||||
* @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")
|
||||
})
|
||||
@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
|
||||
@@ -56,14 +55,12 @@ public class Evenement extends BaseEntity {
|
||||
@Column(name = "adresse", length = 1000)
|
||||
private String adresse;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_evenement", length = 50)
|
||||
private TypeEvenement typeEvenement;
|
||||
private String typeEvenement;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut", nullable = false, length = 30)
|
||||
private StatutEvenement statut = StatutEvenement.PLANIFIE;
|
||||
private String statut = "PLANIFIE";
|
||||
|
||||
@Min(0)
|
||||
@Column(name = "capacite_max")
|
||||
@@ -97,10 +94,6 @@ public class Evenement extends BaseEntity {
|
||||
@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")
|
||||
@@ -110,14 +103,12 @@ public class Evenement extends BaseEntity {
|
||||
@JoinColumn(name = "organisateur_id")
|
||||
private Membre organisateur;
|
||||
|
||||
@OneToMany(
|
||||
mappedBy = "evenement",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true,
|
||||
fetch = FetchType.LAZY)
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "evenement", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<InscriptionEvenement> inscriptions = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "evenement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Adresse> adresses = new ArrayList<>();
|
||||
@@ -169,8 +160,9 @@ public class Evenement extends BaseEntity {
|
||||
// Méthodes métier
|
||||
|
||||
/** Vérifie si l'événement est ouvert aux inscriptions */
|
||||
@JsonIgnore
|
||||
public boolean isOuvertAuxInscriptions() {
|
||||
if (!inscriptionRequise || !actif) {
|
||||
if (!inscriptionRequise || !getActif()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -191,22 +183,22 @@ public class Evenement extends BaseEntity {
|
||||
return false;
|
||||
}
|
||||
|
||||
return statut == StatutEvenement.PLANIFIE || statut == StatutEvenement.CONFIRME;
|
||||
return "PLANIFIE".equals(statut) || "CONFIRME".equals(statut);
|
||||
}
|
||||
|
||||
/** Obtient le nombre d'inscrits à l'événement */
|
||||
@JsonIgnore
|
||||
public int getNombreInscrits() {
|
||||
return inscriptions != null
|
||||
? (int)
|
||||
inscriptions.stream()
|
||||
.filter(
|
||||
inscription ->
|
||||
inscription.getStatut() == InscriptionEvenement.StatutInscription.CONFIRMEE)
|
||||
.count()
|
||||
? (int) inscriptions.stream()
|
||||
.filter(
|
||||
inscription -> InscriptionEvenement.StatutInscription.CONFIRMEE.name().equals(inscription.getStatut()))
|
||||
.count()
|
||||
: 0;
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement est complet */
|
||||
@JsonIgnore
|
||||
public boolean isComplet() {
|
||||
return capaciteMax != null && getNombreInscrits() >= capaciteMax;
|
||||
}
|
||||
@@ -219,7 +211,7 @@ public class Evenement extends BaseEntity {
|
||||
|
||||
/** Vérifie si l'événement est terminé */
|
||||
public boolean isTermine() {
|
||||
if (statut == StatutEvenement.TERMINE) {
|
||||
if ("TERMINE".equals(statut)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -237,6 +229,7 @@ public class Evenement extends BaseEntity {
|
||||
}
|
||||
|
||||
/** Obtient le nombre de places restantes */
|
||||
@JsonIgnore
|
||||
public Integer getPlacesRestantes() {
|
||||
if (capaciteMax == null) {
|
||||
return null; // Capacité illimitée
|
||||
@@ -250,13 +243,12 @@ public class Evenement extends BaseEntity {
|
||||
return inscriptions != null
|
||||
&& inscriptions.stream()
|
||||
.anyMatch(
|
||||
inscription ->
|
||||
inscription.getMembre().getId().equals(membreId)
|
||||
&& inscription.getStatut()
|
||||
== InscriptionEvenement.StatutInscription.CONFIRMEE);
|
||||
inscription -> inscription.getMembre().getId().equals(membreId)
|
||||
&& InscriptionEvenement.StatutInscription.CONFIRMEE.name().equals(inscription.getStatut()));
|
||||
}
|
||||
|
||||
/** Obtient le taux de remplissage en pourcentage */
|
||||
@JsonIgnore
|
||||
public Double getTauxRemplissage() {
|
||||
if (capaciteMax == null || capaciteMax == 0) {
|
||||
return null;
|
||||
|
||||
79
src/main/java/dev/lions/unionflow/server/entity/Favori.java
Normal file
79
src/main/java/dev/lions/unionflow/server/entity/Favori.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité Favori pour la gestion des favoris utilisateur
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "favoris",
|
||||
indexes = {
|
||||
@Index(name = "idx_favori_utilisateur", columnList = "utilisateur_id"),
|
||||
@Index(name = "idx_favori_type", columnList = "type_favori"),
|
||||
@Index(name = "idx_favori_categorie", columnList = "categorie")
|
||||
}
|
||||
)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Favori extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@Column(name = "utilisateur_id", nullable = false)
|
||||
private UUID utilisateurId;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "type_favori", nullable = false, length = 50)
|
||||
private String typeFavori; // PAGE, DOCUMENT, CONTACT, RACCOURCI
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "titre", nullable = false, length = 255)
|
||||
private String titre;
|
||||
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
@Column(name = "url", length = 1000)
|
||||
private String url;
|
||||
|
||||
@Column(name = "icon", length = 100)
|
||||
private String icon;
|
||||
|
||||
@Column(name = "couleur", length = 50)
|
||||
private String couleur;
|
||||
|
||||
@Column(name = "categorie", length = 100)
|
||||
private String categorie;
|
||||
|
||||
@Column(name = "ordre")
|
||||
@Builder.Default
|
||||
private Integer ordre = 0;
|
||||
|
||||
@Column(name = "nb_visites")
|
||||
@Builder.Default
|
||||
private Integer nbVisites = 0;
|
||||
|
||||
@Column(name = "derniere_visite")
|
||||
private LocalDateTime derniereVisite;
|
||||
|
||||
@Column(name = "est_plus_utilise")
|
||||
@Builder.Default
|
||||
private Boolean estPlusUtilise = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.abonnement.TypeFormule;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Catalogue des forfaits SaaS UnionFlow.
|
||||
*
|
||||
* <p>Starter (≤50) → Standard (≤200) → Premium (≤500) → Crystal (illimité)
|
||||
* Fourchette tarifaire : 5 000 à 10 000 XOF/mois. Stockage max : 1 Go.
|
||||
*
|
||||
* <p>Table : {@code formules_abonnement}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "formules_abonnement",
|
||||
indexes = {
|
||||
@Index(name = "idx_formule_code", columnList = "code", unique = true),
|
||||
@Index(name = "idx_formule_actif", columnList = "actif")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class FormuleAbonnement extends BaseEntity {
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@NotNull
|
||||
@Column(name = "code", unique = true, nullable = false, length = 20)
|
||||
private TypeFormule code;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 100)
|
||||
private String libelle;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
/** Nombre maximum de membres. NULL = illimité (Crystal) */
|
||||
@Column(name = "max_membres")
|
||||
private Integer maxMembres;
|
||||
|
||||
/** Stockage maximum en Mo — 1 024 Mo (1 Go) par défaut */
|
||||
@Builder.Default
|
||||
@Column(name = "max_stockage_mo", nullable = false)
|
||||
private Integer maxStockageMo = 1024;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 8, fraction = 2)
|
||||
@Column(name = "prix_mensuel", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal prixMensuel;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 8, fraction = 2)
|
||||
@Column(name = "prix_annuel", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal prixAnnuel;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "ordre_affichage", nullable = false)
|
||||
private Integer ordreAffichage = 0;
|
||||
|
||||
public boolean isIllimitee() {
|
||||
return maxMembres == null;
|
||||
}
|
||||
|
||||
public boolean accepteNouveauMembre(int quotaActuel) {
|
||||
return isIllimitee() || quotaActuel < maxMembres;
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,19 @@ import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Entité InscriptionEvenement représentant l'inscription d'un membre à un événement
|
||||
* 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")
|
||||
})
|
||||
@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
|
||||
@@ -41,30 +40,19 @@ public class InscriptionEvenement extends BaseEntity {
|
||||
@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;
|
||||
private String statut = StatutInscription.CONFIRMEE.name();
|
||||
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Énumération des statuts d'inscription */
|
||||
/** Énumération des statuts d'inscription (pour constantes) */
|
||||
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;
|
||||
}
|
||||
CONFIRMEE,
|
||||
EN_ATTENTE,
|
||||
ANNULEE,
|
||||
REFUSEE;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
@@ -75,7 +63,7 @@ public class InscriptionEvenement extends BaseEntity {
|
||||
* @return true si l'inscription est confirmée
|
||||
*/
|
||||
public boolean isConfirmee() {
|
||||
return StatutInscription.CONFIRMEE.equals(this.statut);
|
||||
return StatutInscription.CONFIRMEE.name().equals(this.statut);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +72,7 @@ public class InscriptionEvenement extends BaseEntity {
|
||||
* @return true si l'inscription est en attente
|
||||
*/
|
||||
public boolean isEnAttente() {
|
||||
return StatutInscription.EN_ATTENTE.equals(this.statut);
|
||||
return StatutInscription.EN_ATTENTE.name().equals(this.statut);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,13 +81,13 @@ public class InscriptionEvenement extends BaseEntity {
|
||||
* @return true si l'inscription est annulée
|
||||
*/
|
||||
public boolean isAnnulee() {
|
||||
return StatutInscription.ANNULEE.equals(this.statut);
|
||||
return StatutInscription.ANNULEE.name().equals(this.statut);
|
||||
}
|
||||
|
||||
/** Confirme l'inscription */
|
||||
public void confirmer() {
|
||||
this.statut = StatutInscription.CONFIRMEE;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
this.statut = StatutInscription.CONFIRMEE.name();
|
||||
setDateModification(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,9 +96,9 @@ public class InscriptionEvenement extends BaseEntity {
|
||||
* @param commentaire le commentaire d'annulation
|
||||
*/
|
||||
public void annuler(String commentaire) {
|
||||
this.statut = StatutInscription.ANNULEE;
|
||||
this.statut = StatutInscription.ANNULEE.name();
|
||||
this.commentaire = commentaire;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
setDateModification(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,28 +107,27 @@ public class InscriptionEvenement extends BaseEntity {
|
||||
* @param commentaire le commentaire de mise en attente
|
||||
*/
|
||||
public void mettreEnAttente(String commentaire) {
|
||||
this.statut = StatutInscription.EN_ATTENTE;
|
||||
this.statut = StatutInscription.EN_ATTENTE.name();
|
||||
this.commentaire = commentaire;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
setDateModification(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Refuse l'inscription
|
||||
* Refuser l'inscription
|
||||
*
|
||||
* @param commentaire le commentaire de refus
|
||||
*/
|
||||
public void refuser(String commentaire) {
|
||||
this.statut = StatutInscription.REFUSEE;
|
||||
this.statut = StatutInscription.REFUSEE.name();
|
||||
this.commentaire = commentaire;
|
||||
this.dateModification = LocalDateTime.now();
|
||||
setDateModification(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// Callbacks JPA
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
super.onUpdate(); // Appelle le onUpdate de BaseEntity
|
||||
this.dateModification = LocalDateTime.now();
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.paiement.StatutIntentionPaiement;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.TypeObjetIntentionPaiement;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Hub centralisé pour tout paiement Wave initié depuis UnionFlow.
|
||||
*
|
||||
* <p>Flux :
|
||||
* <ol>
|
||||
* <li>UnionFlow crée une {@code IntentionPaiement} avec les objets cibles (cotisations, etc.)</li>
|
||||
* <li>UnionFlow appelle l'API Wave → récupère {@code waveCheckoutSessionId}</li>
|
||||
* <li>Le membre confirme dans l'app Wave</li>
|
||||
* <li>Wave envoie un webhook → UnionFlow réconcilie via {@code waveCheckoutSessionId}</li>
|
||||
* <li>UnionFlow valide automatiquement les objets listés dans {@code objetsCibles}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Table : {@code intentions_paiement}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "intentions_paiement",
|
||||
indexes = {
|
||||
@Index(name = "idx_intention_utilisateur", columnList = "utilisateur_id"),
|
||||
@Index(name = "idx_intention_statut", columnList = "statut"),
|
||||
@Index(name = "idx_intention_wave_session", columnList = "wave_checkout_session_id", unique = true),
|
||||
@Index(name = "idx_intention_expiration", columnList = "date_expiration")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class IntentionPaiement extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "utilisateur_id", nullable = false)
|
||||
private Membre utilisateur;
|
||||
|
||||
/** NULL pour les abonnements UnionFlow SA (payés par l'organisation directement) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin("0.01")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_total", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal montantTotal;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^[A-Z]{3}$")
|
||||
@Builder.Default
|
||||
@Column(name = "code_devise", nullable = false, length = 3)
|
||||
private String codeDevise = "XOF";
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@NotNull
|
||||
@Column(name = "type_objet", nullable = false, length = 30)
|
||||
private TypeObjetIntentionPaiement typeObjet;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut", nullable = false, length = 20)
|
||||
private StatutIntentionPaiement statut = StatutIntentionPaiement.INITIEE;
|
||||
|
||||
/** ID de session Wave — clé de réconciliation sur webhook */
|
||||
@Column(name = "wave_checkout_session_id", unique = true, length = 255)
|
||||
private String waveCheckoutSessionId;
|
||||
|
||||
/** URL de paiement Wave à rediriger l'utilisateur */
|
||||
@Column(name = "wave_launch_url", length = 1000)
|
||||
private String waveLaunchUrl;
|
||||
|
||||
/** ID transaction Wave reçu via webhook */
|
||||
@Column(name = "wave_transaction_id", length = 100)
|
||||
private String waveTransactionId;
|
||||
|
||||
/**
|
||||
* JSON : liste des objets couverts par ce paiement.
|
||||
* Exemple : [{\"type\":\"COTISATION\",\"id\":\"uuid\",\"montant\":5000}, ...]
|
||||
*/
|
||||
@Column(name = "objets_cibles", columnDefinition = "TEXT")
|
||||
private String objetsCibles;
|
||||
|
||||
@Column(name = "date_expiration")
|
||||
private LocalDateTime dateExpiration;
|
||||
|
||||
@Column(name = "date_completion")
|
||||
private LocalDateTime dateCompletion;
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isActive() {
|
||||
return StatutIntentionPaiement.INITIEE.equals(statut)
|
||||
|| StatutIntentionPaiement.EN_COURS.equals(statut);
|
||||
}
|
||||
|
||||
public boolean isExpiree() {
|
||||
return dateExpiration != null && LocalDateTime.now().isAfter(dateExpiration);
|
||||
}
|
||||
|
||||
public boolean isCompletee() {
|
||||
return StatutIntentionPaiement.COMPLETEE.equals(statut);
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statut == null) statut = StatutIntentionPaiement.INITIEE;
|
||||
if (codeDevise == null) codeDevise = "XOF";
|
||||
if (dateExpiration == null) {
|
||||
dateExpiration = LocalDateTime.now().plusMinutes(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -69,6 +70,7 @@ public class JournalComptable extends BaseEntity {
|
||||
private String description;
|
||||
|
||||
/** Écritures comptables associées */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "journal", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<EcritureComptable> ecritures = new ArrayList<>();
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.*;
|
||||
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;
|
||||
import lombok.*;
|
||||
|
||||
/** Entité Membre avec UUID */
|
||||
/**
|
||||
* Identité globale unique d'un utilisateur UnionFlow.
|
||||
*
|
||||
* <p>
|
||||
* Un utilisateur possède un seul compte sur toute la plateforme.
|
||||
* Ses adhésions aux organisations sont gérées dans {@link MembreOrganisation}.
|
||||
*
|
||||
* <p>
|
||||
* Table : {@code utilisateurs}
|
||||
*/
|
||||
@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")
|
||||
})
|
||||
@Table(name = "utilisateurs", indexes = {
|
||||
@Index(name = "idx_utilisateur_email", columnList = "email", unique = true),
|
||||
@Index(name = "idx_utilisateur_numero", columnList = "numero_membre", unique = true),
|
||||
@Index(name = "idx_utilisateur_keycloak", columnList = "keycloak_id", unique = true),
|
||||
@Index(name = "idx_utilisateur_actif", columnList = "actif"),
|
||||
@Index(name = "idx_utilisateur_statut", columnList = "statut_compte")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -31,6 +34,11 @@ import lombok.NoArgsConstructor;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Membre extends BaseEntity {
|
||||
|
||||
/** Identifiant Keycloak (UUID du compte OIDC) */
|
||||
@Column(name = "keycloak_id", unique = true)
|
||||
private UUID keycloakId;
|
||||
|
||||
/** Numéro de membre — unique globalement sur toute la plateforme */
|
||||
@NotBlank
|
||||
@Column(name = "numero_membre", unique = true, nullable = false, length = 20)
|
||||
private String numeroMembre;
|
||||
@@ -48,15 +56,10 @@ public class Membre extends BaseEntity {
|
||||
@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")
|
||||
@Pattern(regexp = "^\\+225[0-9]{8}$", message = "Le numéro Wave doit être au format +225XXXXXXXX")
|
||||
@Column(name = "telephone_wave", length = 13)
|
||||
private String telephoneWave;
|
||||
|
||||
@@ -64,43 +67,94 @@ public class Membre extends BaseEntity {
|
||||
@Column(name = "date_naissance", nullable = false)
|
||||
private LocalDate dateNaissance;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_adhesion", nullable = false)
|
||||
private LocalDate dateAdhesion;
|
||||
@Column(name = "profession", length = 100)
|
||||
private String profession;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
@Column(name = "photo_url", length = 500)
|
||||
private String photoUrl;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "statut_compte", nullable = false, length = 30)
|
||||
private String statutCompte = "EN_ATTENTE_VALIDATION";
|
||||
|
||||
/**
|
||||
* Statut matrimonial (domaine
|
||||
* {@code STATUT_MATRIMONIAL} dans
|
||||
* {@code types_reference}).
|
||||
*/
|
||||
@Column(name = "statut_matrimonial", length = 50)
|
||||
private String statutMatrimonial;
|
||||
|
||||
/** Nationalité. */
|
||||
@Column(name = "nationalite", length = 100)
|
||||
private String nationalite;
|
||||
|
||||
/**
|
||||
* Type de pièce d'identité (domaine
|
||||
* {@code TYPE_IDENTITE} dans
|
||||
* {@code types_reference}).
|
||||
*/
|
||||
@Column(name = "type_identite", length = 50)
|
||||
private String typeIdentite;
|
||||
|
||||
/** Numéro de la pièce d'identité. */
|
||||
@Column(name = "numero_identite", length = 100)
|
||||
private String numeroIdentite;
|
||||
|
||||
/** Niveau de vigilance KYC LCB-FT (SIMPLIFIE, RENFORCE). */
|
||||
@Column(name = "niveau_vigilance_kyc", length = 20)
|
||||
private String niveauVigilanceKyc;
|
||||
|
||||
/** Statut de vérification d'identité (NON_VERIFIE, EN_COURS, VERIFIE, REFUSE). */
|
||||
@Column(name = "statut_kyc", length = 20)
|
||||
private String statutKyc;
|
||||
|
||||
/** Date de dernière vérification d'identité. */
|
||||
@Column(name = "date_verification_identite")
|
||||
private LocalDate dateVerificationIdentite;
|
||||
|
||||
// ── Relations ────────────────────────────────────────────────────────────
|
||||
|
||||
/** Adhésions à des organisations */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<MembreOrganisation> membresOrganisations = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
@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<>();
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<CompteWave> comptesWave = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
@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 */
|
||||
// ── Méthodes métier ───────────────────────────────────────────────────────
|
||||
|
||||
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));
|
||||
return dateNaissance != null && dateNaissance.isBefore(LocalDate.now().minusYears(18));
|
||||
}
|
||||
|
||||
/** Méthode métier pour calculer l'âge */
|
||||
public int getAge() {
|
||||
return LocalDate.now().getYear() - dateNaissance.getYear();
|
||||
return dateNaissance != null ? LocalDate.now().getYear() - dateNaissance.getYear() : 0;
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statutCompte == null) {
|
||||
statutCompte = "EN_ATTENTE_VALIDATION";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Lien entre un utilisateur et une organisation.
|
||||
*
|
||||
* <p>Un utilisateur peut adhérer à plusieurs organisations simultanément.
|
||||
* Chaque adhésion a son propre statut, date et unité d'affectation.
|
||||
*
|
||||
* <p>Table : {@code membres_organisations}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "membres_organisations",
|
||||
indexes = {
|
||||
@Index(name = "idx_mo_utilisateur", columnList = "utilisateur_id"),
|
||||
@Index(name = "idx_mo_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_mo_statut", columnList = "statut_membre"),
|
||||
@Index(name = "idx_mo_unite", columnList = "unite_id")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_mo_utilisateur_organisation",
|
||||
columnNames = {"utilisateur_id", "organisation_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MembreOrganisation extends BaseEntity {
|
||||
|
||||
/** L'utilisateur (identité globale) */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "utilisateur_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
/** L'organisation racine à laquelle appartient ce membre */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
/**
|
||||
* Unité d'affectation (agence/bureau).
|
||||
* NULL = affecté au siège.
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "unite_id")
|
||||
private Organisation unite;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_membre", nullable = false, length = 30)
|
||||
private StatutMembre statutMembre = StatutMembre.EN_ATTENTE_VALIDATION;
|
||||
|
||||
@Column(name = "date_adhesion")
|
||||
private LocalDate dateAdhesion;
|
||||
|
||||
@Column(name = "date_changement_statut")
|
||||
private LocalDate dateChangementStatut;
|
||||
|
||||
@Column(name = "motif_statut", length = 500)
|
||||
private String motifStatut;
|
||||
|
||||
/** Utilisateur qui a approuvé ou traité ce changement de statut */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "approuve_par_id")
|
||||
private Membre approuvePar;
|
||||
|
||||
// ── Relations ─────────────────────────────────────────────────────────────
|
||||
|
||||
/** Rôles de ce membre dans cette organisation */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "membreOrganisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<MembreRole> roles = new ArrayList<>();
|
||||
|
||||
/** Ayants droit (mutuelles de santé uniquement) */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "membreOrganisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<AyantDroit> ayantsDroit = new ArrayList<>();
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isActif() {
|
||||
return StatutMembre.ACTIF.equals(statutMembre) && Boolean.TRUE.equals(getActif());
|
||||
}
|
||||
|
||||
public boolean peutDemanderAide() {
|
||||
return StatutMembre.ACTIF.equals(statutMembre);
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statutMembre == null) {
|
||||
statutMembre = StatutMembre.EN_ATTENTE_VALIDATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,15 @@ import lombok.NoArgsConstructor;
|
||||
@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")
|
||||
@Index(name = "idx_mr_membre_org", columnList = "membre_organisation_id"),
|
||||
@Index(name = "idx_mr_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_mr_role", columnList = "role_id"),
|
||||
@Index(name = "idx_mr_actif", columnList = "actif")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_membre_role",
|
||||
columnNames = {"membre_id", "role_id"})
|
||||
name = "uk_mr_membre_org_role",
|
||||
columnNames = {"membre_organisation_id", "role_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -37,11 +38,16 @@ import lombok.NoArgsConstructor;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MembreRole extends BaseEntity {
|
||||
|
||||
/** Membre */
|
||||
/** Lien membership (utilisateur dans le contexte de son organisation) */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
@JoinColumn(name = "membre_organisation_id", nullable = false)
|
||||
private MembreOrganisation membreOrganisation;
|
||||
|
||||
/** Organisation dans laquelle ce rôle est actif (dénormalisé pour les requêtes) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
/** Rôle */
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Lien « suivi » entre deux membres : le membre connecté (follower) suit un autre membre (suivi).
|
||||
* Utilisé pour la fonctionnalité Réseau / Suivre dans l’app mobile.
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "membre_suivi",
|
||||
uniqueConstraints = @UniqueConstraint(columnNames = { "follower_utilisateur_id", "suivi_utilisateur_id" }),
|
||||
indexes = {
|
||||
@Index(name = "idx_membre_suivi_follower", columnList = "follower_utilisateur_id"),
|
||||
@Index(name = "idx_membre_suivi_suivi", columnList = "suivi_utilisateur_id")
|
||||
}
|
||||
)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MembreSuivi extends BaseEntity {
|
||||
|
||||
/** Utilisateur qui suit (membre connecté). */
|
||||
@NotNull
|
||||
@Column(name = "follower_utilisateur_id", nullable = false)
|
||||
private UUID followerUtilisateurId;
|
||||
|
||||
/** Utilisateur suivi (membre cible). */
|
||||
@NotNull
|
||||
@Column(name = "suivi_utilisateur_id", nullable = false)
|
||||
private UUID suiviUtilisateurId;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Catalogue des modules métier activables par type d'organisation.
|
||||
*
|
||||
* <p>Géré uniquement par le Super Admin UnionFlow.
|
||||
* Les organisations ne peuvent pas créer de nouveaux modules.
|
||||
*
|
||||
* <p>Table : {@code modules_disponibles}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "modules_disponibles",
|
||||
indexes = {
|
||||
@Index(name = "idx_module_code", columnList = "code", unique = true),
|
||||
@Index(name = "idx_module_actif", columnList = "actif")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ModuleDisponible extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "code", unique = true, nullable = false, length = 50)
|
||||
private String code;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "libelle", nullable = false, length = 150)
|
||||
private String libelle;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* JSON array des types d'organisations compatibles.
|
||||
* Exemple : ["MUTUELLE_SANTE","ONG"] ou ["ALL"] pour tous.
|
||||
*/
|
||||
@Column(name = "types_org_compatibles", columnDefinition = "TEXT")
|
||||
private String typesOrgCompatibles;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "ordre_affichage", nullable = false)
|
||||
private Integer ordreAffichage = 0;
|
||||
|
||||
public boolean estCompatibleAvec(String typeOrganisation) {
|
||||
if (typesOrgCompatibles == null) return false;
|
||||
return typesOrgCompatibles.contains("ALL")
|
||||
|| typesOrgCompatibles.contains(typeOrganisation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Module activé pour une organisation donnée.
|
||||
*
|
||||
* <p>
|
||||
* Les modules sont activés automatiquement selon le type d'organisation
|
||||
* lors de la première souscription, et peuvent être désactivés par le manager.
|
||||
*
|
||||
* <p>
|
||||
* Table : {@code modules_organisation_actifs}
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "modules_organisation_actifs", indexes = {
|
||||
@Index(name = "idx_moa_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_moa_module", columnList = "module_code")
|
||||
}, uniqueConstraints = {
|
||||
@UniqueConstraint(name = "uk_moa_org_module", columnNames = { "organisation_id", "module_code" })
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ModuleOrganisationActif extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "module_code", nullable = false, length = 50)
|
||||
private String moduleCode;
|
||||
|
||||
/**
|
||||
* Référence vers le catalogue des modules.
|
||||
* Assure l'intégrité référentielle avec
|
||||
* {@code modules_disponibles}.
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "module_disponible_id")
|
||||
private ModuleDisponible moduleDisponible;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "date_activation", nullable = false)
|
||||
private LocalDateTime dateActivation = LocalDateTime.now();
|
||||
|
||||
/**
|
||||
* Configuration JSON spécifique au module pour cette organisation.
|
||||
* Exemple pour CREDIT_EPARGNE : {"taux_interet_max": 18, "duree_max_mois": 24}
|
||||
*/
|
||||
@Column(name = "parametres", columnDefinition = "TEXT")
|
||||
private String parametres;
|
||||
|
||||
public boolean isActif() {
|
||||
return Boolean.TRUE.equals(getActif());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
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;
|
||||
@@ -19,16 +17,14 @@ import lombok.NoArgsConstructor;
|
||||
* @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")
|
||||
})
|
||||
@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
|
||||
@@ -38,22 +34,18 @@ public class Notification extends BaseEntity {
|
||||
|
||||
/** Type de notification */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_notification", nullable = false, length = 30)
|
||||
private TypeNotification typeNotification;
|
||||
private String typeNotification;
|
||||
|
||||
/** Priorité */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "priorite", length = 20)
|
||||
private PrioriteNotification priorite = PrioriteNotification.NORMALE;
|
||||
private String priorite = "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;
|
||||
private String statut = "EN_ATTENTE";
|
||||
|
||||
/** Sujet */
|
||||
@Column(name = "sujet", length = 500)
|
||||
@@ -103,12 +95,12 @@ public class Notification extends BaseEntity {
|
||||
|
||||
/** 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);
|
||||
return statut != null && dev.lions.unionflow.server.api.enums.notification.StatutNotification.ENVOYEE.name().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);
|
||||
return statut != null && dev.lions.unionflow.server.api.enums.notification.StatutNotification.LUE.name().equals(statut);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@@ -116,10 +108,10 @@ public class Notification extends BaseEntity {
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (priorite == null) {
|
||||
priorite = PrioriteNotification.NORMALE;
|
||||
priorite = "NORMALE";
|
||||
}
|
||||
if (statut == null) {
|
||||
statut = dev.lions.unionflow.server.api.enums.notification.StatutNotification.EN_ATTENTE;
|
||||
statut = "EN_ATTENTE";
|
||||
}
|
||||
if (nombreTentatives == null) {
|
||||
nombreTentatives = 0;
|
||||
@@ -129,4 +121,3 @@ public class Notification extends BaseEntity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.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;
|
||||
@@ -16,7 +15,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Organisation avec UUID Représente une organisation (Lions Club, Association,
|
||||
* Entité Organisation avec UUID Représente une organisation (Lions Club,
|
||||
* Association,
|
||||
* Coopérative, etc.)
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
@@ -24,21 +24,14 @@ import lombok.NoArgsConstructor;
|
||||
* @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)
|
||||
})
|
||||
@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_parente", columnList = "organisation_parente_id"),
|
||||
@Index(name = "idx_organisation_numero_enregistrement", columnList = "numero_enregistrement", unique = true)
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -86,22 +79,22 @@ public class Organisation extends BaseEntity {
|
||||
@Column(name = "email_secondaire", length = 255)
|
||||
private String emailSecondaire;
|
||||
|
||||
// Adresse
|
||||
// Adresse principale (champs dénormalisés pour performance)
|
||||
@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;
|
||||
|
||||
@Column(name = "code_postal", length = 20)
|
||||
private String codePostal;
|
||||
|
||||
// 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")
|
||||
@@ -125,14 +118,32 @@ public class Organisation extends BaseEntity {
|
||||
@Column(name = "reseaux_sociaux", length = 1000)
|
||||
private String reseauxSociaux;
|
||||
|
||||
// Hiérarchie
|
||||
@Column(name = "organisation_parente_id")
|
||||
private UUID organisationParenteId;
|
||||
// ── Hiérarchie ──────────────────────────────────────────────────────────────
|
||||
|
||||
/** Organisation parente — FK propre (null = organisation racine) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_parente_id")
|
||||
private Organisation organisationParente;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "niveau_hierarchique", nullable = false)
|
||||
private Integer niveauHierarchique = 0;
|
||||
|
||||
/**
|
||||
* TRUE si c'est l'organisation racine qui porte la souscription SaaS
|
||||
* pour toute sa hiérarchie.
|
||||
*/
|
||||
@Builder.Default
|
||||
@Column(name = "est_organisation_racine", nullable = false)
|
||||
private Boolean estOrganisationRacine = true;
|
||||
|
||||
/**
|
||||
* Chemin hiérarchique complet — ex: /uuid-racine/uuid-intermediate/uuid-feuille
|
||||
* Permet des requêtes récursives optimisées sans CTE.
|
||||
*/
|
||||
@Column(name = "chemin_hierarchique", length = 2000)
|
||||
private String cheminHierarchique;
|
||||
|
||||
// Statistiques
|
||||
@Builder.Default
|
||||
@Column(name = "nombre_membres", nullable = false)
|
||||
@@ -187,14 +198,19 @@ public class Organisation extends BaseEntity {
|
||||
private Boolean accepteNouveauxMembres = true;
|
||||
|
||||
// Relations
|
||||
|
||||
/** Adhésions des membres à cette organisation */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Membre> membres = new ArrayList<>();
|
||||
private List<MembreOrganisation> membresOrganisations = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Adresse> adresses = new ArrayList<>();
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<CompteWave> comptesWave = new ArrayList<>();
|
||||
@@ -215,7 +231,9 @@ public class Organisation extends BaseEntity {
|
||||
return Period.between(dateFondation, LocalDate.now()).getYears();
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'organisation est récente (moins de 2 ans) */
|
||||
/**
|
||||
* Méthode métier pour vérifier si l'organisation est récente (moins de 2 ans)
|
||||
*/
|
||||
public boolean isRecente() {
|
||||
return getAncienneteAnnees() < 2;
|
||||
}
|
||||
@@ -262,17 +280,6 @@ public class Organisation extends BaseEntity {
|
||||
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() {
|
||||
@@ -289,6 +296,9 @@ public class Organisation extends BaseEntity {
|
||||
if (niveauHierarchique == null) {
|
||||
niveauHierarchique = 0;
|
||||
}
|
||||
if (estOrganisationRacine == null) {
|
||||
estOrganisationRacine = (organisationParente == null);
|
||||
}
|
||||
if (nombreMembres == null) {
|
||||
nombreMembres = 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
@@ -23,15 +22,13 @@ import lombok.NoArgsConstructor;
|
||||
* @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")
|
||||
})
|
||||
@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
|
||||
@@ -59,16 +56,14 @@ public class Paiement extends BaseEntity {
|
||||
|
||||
/** Méthode de paiement */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "methode_paiement", nullable = false, length = 50)
|
||||
private MethodePaiement methodePaiement;
|
||||
private String methodePaiement;
|
||||
|
||||
/** Statut du paiement */
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_paiement", nullable = false, length = 30)
|
||||
private StatutPaiement statutPaiement = StatutPaiement.EN_ATTENTE;
|
||||
private String statutPaiement = "EN_ATTENTE";
|
||||
|
||||
/** Date de paiement */
|
||||
@Column(name = "date_paiement")
|
||||
@@ -108,22 +103,11 @@ public class Paiement extends BaseEntity {
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
/** Relations avec les tables de liaison */
|
||||
/** Objets cibles de ce paiement (Cat.2 — polymorphique) */
|
||||
@JsonIgnore
|
||||
@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<>();
|
||||
private List<PaiementObjet> paiementsObjets = new ArrayList<>();
|
||||
|
||||
/** Relation avec TransactionWave (optionnelle) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@@ -140,30 +124,28 @@ public class Paiement extends BaseEntity {
|
||||
|
||||
/** Méthode métier pour vérifier si le paiement est validé */
|
||||
public boolean isValide() {
|
||||
return StatutPaiement.VALIDE.equals(statutPaiement);
|
||||
return "VALIDE".equals(statutPaiement);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si le paiement peut être modifié */
|
||||
/** Vérifie si le paiement peut être modifié */
|
||||
public boolean peutEtreModifie() {
|
||||
return !statutPaiement.isFinalise();
|
||||
return !"VALIDE".equals(statutPaiement)
|
||||
&& !"ANNULE".equals(statutPaiement);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (numeroReference == null || numeroReference.isEmpty()) {
|
||||
if (numeroReference == null
|
||||
|| numeroReference.isEmpty()) {
|
||||
numeroReference = genererNumeroReference();
|
||||
}
|
||||
if (codeDevise == null || codeDevise.isEmpty()) {
|
||||
codeDevise = "XOF";
|
||||
}
|
||||
if (statutPaiement == null) {
|
||||
statutPaiement = StatutPaiement.EN_ATTENTE;
|
||||
statutPaiement = "EN_ATTENTE";
|
||||
}
|
||||
if (datePaiement == null) {
|
||||
datePaiement = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
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,130 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Table de liaison polymorphique entre un paiement
|
||||
* et son objet cible.
|
||||
*
|
||||
* <p>
|
||||
* Remplace les 4 tables dupliquées
|
||||
* {@code paiements_cotisations},
|
||||
* {@code paiements_adhesions},
|
||||
* {@code paiements_evenements} et
|
||||
* {@code paiements_aides} par une table unique
|
||||
* utilisant le pattern
|
||||
* {@code (type_objet_cible, objet_cible_id)}.
|
||||
*
|
||||
* <p>
|
||||
* Les types d'objet cible sont définis dans le
|
||||
* domaine {@code OBJET_PAIEMENT} de la table
|
||||
* {@code types_reference} (ex: COTISATION,
|
||||
* ADHESION, EVENEMENT, AIDE).
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2026-02-21
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "paiements_objets", indexes = {
|
||||
@Index(name = "idx_po_paiement", columnList = "paiement_id"),
|
||||
@Index(name = "idx_po_objet", columnList = "type_objet_cible,"
|
||||
+ " objet_cible_id"),
|
||||
@Index(name = "idx_po_type", columnList = "type_objet_cible")
|
||||
}, uniqueConstraints = {
|
||||
@UniqueConstraint(name = "uk_paiement_objet", columnNames = {
|
||||
"paiement_id",
|
||||
"type_objet_cible",
|
||||
"objet_cible_id"
|
||||
})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PaiementObjet extends BaseEntity {
|
||||
|
||||
/** Paiement parent. */
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "paiement_id", nullable = false)
|
||||
private Paiement paiement;
|
||||
|
||||
/**
|
||||
* Type de l'objet cible (code du domaine
|
||||
* {@code OBJET_PAIEMENT} dans
|
||||
* {@code types_reference}).
|
||||
*
|
||||
* <p>
|
||||
* Valeurs attendues : {@code COTISATION},
|
||||
* {@code ADHESION}, {@code EVENEMENT},
|
||||
* {@code AIDE}.
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 50)
|
||||
@Column(name = "type_objet_cible", nullable = false, length = 50)
|
||||
private String typeObjetCible;
|
||||
|
||||
/**
|
||||
* UUID de l'objet cible (cotisation, demande
|
||||
* d'adhésion, inscription événement, ou demande
|
||||
* d'aide).
|
||||
*/
|
||||
@NotNull
|
||||
@Column(name = "objet_cible_id", nullable = false)
|
||||
private UUID objetCibleId;
|
||||
|
||||
/** Montant appliqué à cet objet cible. */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "montant_applique", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal montantApplique;
|
||||
|
||||
/** Date d'application du paiement. */
|
||||
@Column(name = "date_application")
|
||||
private LocalDateTime dateApplication;
|
||||
|
||||
/** Commentaire sur l'application. */
|
||||
@Size(max = 500)
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/**
|
||||
* Callback JPA avant la persistance.
|
||||
*
|
||||
* <p>
|
||||
* Initialise {@code dateApplication} si non
|
||||
* renseignée.
|
||||
*/
|
||||
@Override
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (dateApplication == null) {
|
||||
dateApplication = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Paramètres de cotisation configurés par le manager de chaque organisation.
|
||||
*
|
||||
* <p>
|
||||
* Le manager peut définir :
|
||||
* <ul>
|
||||
* <li>Le montant mensuel et annuel fixé pour tous les membres</li>
|
||||
* <li>La date de départ du calcul des impayés (configurable)</li>
|
||||
* <li>Le délai en jours avant passage automatique en statut INACTIF</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Table : {@code parametres_cotisation_organisation}
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "parametres_cotisation_organisation", indexes = {
|
||||
@Index(name = "idx_param_cot_org", columnList = "organisation_id", unique = true)
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ParametresCotisationOrganisation extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false, unique = true)
|
||||
private Organisation organisation;
|
||||
|
||||
@Builder.Default
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_cotisation_mensuelle", precision = 12, scale = 2)
|
||||
private BigDecimal montantCotisationMensuelle = BigDecimal.ZERO;
|
||||
|
||||
@Builder.Default
|
||||
@DecimalMin("0.00")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
@Column(name = "montant_cotisation_annuelle", precision = 12, scale = 2)
|
||||
private BigDecimal montantCotisationAnnuelle = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "devise", nullable = false, length = 3)
|
||||
private String devise;
|
||||
|
||||
/**
|
||||
* Date de référence pour le calcul des membres «à jour».
|
||||
* Toutes les échéances depuis cette date doivent être payées.
|
||||
* Configurable par le manager.
|
||||
*/
|
||||
@Column(name = "date_debut_calcul_ajour")
|
||||
private LocalDate dateDebutCalculAjour;
|
||||
|
||||
/**
|
||||
* Nombre de jours de retard avant passage automatique du statut membre →
|
||||
* INACTIF.
|
||||
* Défaut : 30 jours.
|
||||
*/
|
||||
@Builder.Default
|
||||
@Min(1)
|
||||
@Column(name = "delai_retard_avant_inactif_jours", nullable = false)
|
||||
private Integer delaiRetardAvantInactifJours = 30;
|
||||
|
||||
@Builder.Default
|
||||
@Column(name = "cotisation_obligatoire", nullable = false)
|
||||
private Boolean cotisationObligatoire = true;
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Vérifie si la date de référence pour les impayés est définie.
|
||||
* Sans cette date, aucun calcul d'ancienneté des impayés n'est possible.
|
||||
*/
|
||||
public boolean isCalculAjourActive() {
|
||||
return dateDebutCalculAjour != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Paramètres LCB-FT par organisation ou globaux (organisationId null).
|
||||
* Seuils au-dessus desquels l'origine des fonds est obligatoire / validation manuelle.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "parametres_lcb_ft", indexes = {
|
||||
@Index(name = "idx_param_lcb_ft_org", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ParametresLcbFt extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
@Column(name = "code_devise", nullable = false, length = 3)
|
||||
private String codeDevise;
|
||||
|
||||
@Column(name = "montant_seuil_justification", nullable = false, precision = 18, scale = 4)
|
||||
private BigDecimal montantSeuilJustification;
|
||||
|
||||
@Column(name = "montant_seuil_validation_manuelle", precision = 18, scale = 4)
|
||||
private BigDecimal montantSeuilValidationManuelle;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.ArrayList;
|
||||
@@ -61,6 +62,7 @@ public class Permission extends BaseEntity {
|
||||
private String description;
|
||||
|
||||
/** Rôles associés */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "permission", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<RolePermission> roles = new ArrayList<>();
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -9,24 +20,34 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité PieceJointe pour l'association flexible de documents
|
||||
* Association polymorphique entre un document et
|
||||
* une entité métier quelconque.
|
||||
*
|
||||
* <p>
|
||||
* Remplace les 6 FK nullables mutuellement
|
||||
* exclusives (membre, organisation, cotisation,
|
||||
* adhesion, demandeAide, transactionWave) par un
|
||||
* couple {@code (type_entite_rattachee,
|
||||
* entite_rattachee_id)}.
|
||||
*
|
||||
* <p>
|
||||
* Les types autorisés sont définis dans le
|
||||
* domaine {@code ENTITE_RATTACHEE} de la table
|
||||
* {@code types_reference} (ex: MEMBRE,
|
||||
* ORGANISATION, COTISATION, ADHESION, AIDE,
|
||||
* TRANSACTION_WAVE).
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
* @since 2026-02-21
|
||||
*/
|
||||
@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")
|
||||
})
|
||||
@Table(name = "pieces_jointes", indexes = {
|
||||
@Index(name = "idx_pj_document", columnList = "document_id"),
|
||||
@Index(name = "idx_pj_entite", columnList = "type_entite_rattachee,"
|
||||
+ " entite_rattachee_id"),
|
||||
@Index(name = "idx_pj_type_entite", columnList = "type_entite_rattachee")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -34,70 +55,68 @@ import lombok.NoArgsConstructor;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PieceJointe extends BaseEntity {
|
||||
|
||||
/** Ordre d'affichage */
|
||||
/** 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 */
|
||||
/** Libellé de la pièce jointe. */
|
||||
@Size(max = 200)
|
||||
@Column(name = "libelle", length = 200)
|
||||
private String libelle;
|
||||
|
||||
/** Commentaire */
|
||||
/** Commentaire. */
|
||||
@Size(max = 500)
|
||||
@Column(name = "commentaire", length = 500)
|
||||
private String commentaire;
|
||||
|
||||
/** Document associé */
|
||||
/** Document associé (obligatoire). */
|
||||
@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;
|
||||
/**
|
||||
* Type de l'entité rattachée (code du domaine
|
||||
* {@code ENTITE_RATTACHEE} dans
|
||||
* {@code types_reference}).
|
||||
*
|
||||
* <p>
|
||||
* Valeurs attendues : {@code MEMBRE},
|
||||
* {@code ORGANISATION}, {@code COTISATION},
|
||||
* {@code ADHESION}, {@code AIDE},
|
||||
* {@code TRANSACTION_WAVE}.
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 50)
|
||||
@Column(name = "type_entite_rattachee", nullable = false, length = 50)
|
||||
private String typeEntiteRattachee;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
/**
|
||||
* UUID de l'entité rattachée (membre,
|
||||
* organisation, cotisation, etc.).
|
||||
*/
|
||||
@NotNull
|
||||
@Column(name = "entite_rattachee_id", nullable = false)
|
||||
private UUID entiteRattacheeId;
|
||||
|
||||
@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 */
|
||||
/**
|
||||
* Callback JPA avant la persistance.
|
||||
*
|
||||
* <p>
|
||||
* Initialise {@code ordre} à 1 si non
|
||||
* renseigné. Normalise le type en majuscules.
|
||||
*/
|
||||
@Override
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (ordre == null) {
|
||||
ordre = 1;
|
||||
}
|
||||
if (typeEntiteRattachee != null) {
|
||||
typeEntiteRattachee = typeEntiteRattachee.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -15,17 +16,15 @@ import lombok.NoArgsConstructor;
|
||||
* Entité Role pour la gestion des rôles dans le système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @version 3.1
|
||||
* @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")
|
||||
})
|
||||
@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
|
||||
@@ -53,10 +52,9 @@ public class Role extends BaseEntity {
|
||||
@Column(name = "niveau_hierarchique", nullable = false)
|
||||
private Integer niveauHierarchique = 100;
|
||||
|
||||
/** Type de rôle */
|
||||
@Enumerated(EnumType.STRING)
|
||||
/** Type de rôle (SYSTEME, ORGANISATION, PERSONNALISE) */
|
||||
@Column(name = "type_role", nullable = false, length = 50)
|
||||
private TypeRole typeRole;
|
||||
private String typeRole;
|
||||
|
||||
/** Organisation propriétaire (null pour rôles système) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@@ -64,30 +62,21 @@ public class Role extends BaseEntity {
|
||||
private Organisation organisation;
|
||||
|
||||
/** Permissions associées */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "role", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<RolePermission> permissions = new ArrayList<>();
|
||||
|
||||
/** Énumération des types de rôle */
|
||||
/** Énumération des constantes de 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;
|
||||
}
|
||||
SYSTEME,
|
||||
ORGANISATION,
|
||||
PERSONNALISE;
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si c'est un rôle système */
|
||||
public boolean isRoleSysteme() {
|
||||
return TypeRole.SYSTEME.equals(typeRole);
|
||||
return TypeRole.SYSTEME.name().equals(typeRole);
|
||||
}
|
||||
|
||||
/** Callback JPA avant la persistance */
|
||||
@@ -95,11 +84,10 @@ public class Role extends BaseEntity {
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (typeRole == null) {
|
||||
typeRole = TypeRole.PERSONNALISE;
|
||||
typeRole = TypeRole.PERSONNALISE.name();
|
||||
}
|
||||
if (niveauHierarchique == null) {
|
||||
niveauHierarchique = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.abonnement.StatutSouscription;
|
||||
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDate;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Abonnement actif d'une organisation racine à un forfait UnionFlow.
|
||||
*
|
||||
* <p>Règle clé : quand {@code quotaUtilise >= quotaMax}, toute nouvelle
|
||||
* validation d'adhésion est bloquée avec un message explicite.
|
||||
* Le manager peut upgrader son forfait à tout moment.
|
||||
*
|
||||
* <p>Table : {@code souscriptions_organisation}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "souscriptions_organisation",
|
||||
indexes = {
|
||||
@Index(name = "idx_souscription_org", columnList = "organisation_id", unique = true),
|
||||
@Index(name = "idx_souscription_statut", columnList = "statut"),
|
||||
@Index(name = "idx_souscription_fin", columnList = "date_fin")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SouscriptionOrganisation extends BaseEntity {
|
||||
|
||||
/** Organisation racine abonnée (une seule souscription active par org) */
|
||||
@NotNull
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false, unique = true)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "formule_id", nullable = false)
|
||||
private FormuleAbonnement formule;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "type_periode", nullable = false, length = 10)
|
||||
private TypePeriodeAbonnement typePeriode = TypePeriodeAbonnement.MENSUEL;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_debut", nullable = false)
|
||||
private LocalDate dateDebut;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_fin", nullable = false)
|
||||
private LocalDate dateFin;
|
||||
|
||||
/** Snapshot du quota max au moment de la souscription */
|
||||
@Column(name = "quota_max")
|
||||
private Integer quotaMax;
|
||||
|
||||
/** Compteur incrémenté à chaque adhésion validée */
|
||||
@Builder.Default
|
||||
@Min(0)
|
||||
@Column(name = "quota_utilise", nullable = false)
|
||||
private Integer quotaUtilise = 0;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut", nullable = false, length = 30)
|
||||
private StatutSouscription statut = StatutSouscription.ACTIVE;
|
||||
|
||||
@Column(name = "reference_paiement_wave", length = 100)
|
||||
private String referencePaiementWave;
|
||||
|
||||
@Column(name = "wave_session_id", length = 255)
|
||||
private String waveSessionId;
|
||||
|
||||
@Column(name = "date_dernier_paiement")
|
||||
private LocalDate dateDernierPaiement;
|
||||
|
||||
@Column(name = "date_prochain_paiement")
|
||||
private LocalDate dateProchainePaiement;
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isActive() {
|
||||
return StatutSouscription.ACTIVE.equals(statut)
|
||||
&& LocalDate.now().isBefore(dateFin.plusDays(1));
|
||||
}
|
||||
|
||||
public boolean isQuotaDepasse() {
|
||||
return quotaMax != null && quotaUtilise >= quotaMax;
|
||||
}
|
||||
|
||||
public int getPlacesRestantes() {
|
||||
if (quotaMax == null) return Integer.MAX_VALUE;
|
||||
return Math.max(0, quotaMax - quotaUtilise);
|
||||
}
|
||||
|
||||
/** Incrémente le quota lors de la validation d'une adhésion */
|
||||
public void incrementerQuota() {
|
||||
if (quotaUtilise == null) quotaUtilise = 0;
|
||||
quotaUtilise++;
|
||||
}
|
||||
|
||||
/** Décrémente le quota lors de la radiation d'un membre */
|
||||
public void decrementerQuota() {
|
||||
if (quotaUtilise != null && quotaUtilise > 0) quotaUtilise--;
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statut == null) statut = StatutSouscription.ACTIVE;
|
||||
if (typePeriode == null) typePeriode = TypePeriodeAbonnement.MENSUEL;
|
||||
if (quotaUtilise == null) quotaUtilise = 0;
|
||||
if (formule != null && quotaMax == null) quotaMax = formule.getMaxMembres();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité Suggestion pour la gestion des suggestions utilisateur
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "suggestions",
|
||||
indexes = {
|
||||
@Index(name = "idx_suggestion_utilisateur", columnList = "utilisateur_id"),
|
||||
@Index(name = "idx_suggestion_statut", columnList = "statut"),
|
||||
@Index(name = "idx_suggestion_categorie", columnList = "categorie")
|
||||
}
|
||||
)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Suggestion extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@Column(name = "utilisateur_id", nullable = false)
|
||||
private UUID utilisateurId;
|
||||
|
||||
@Column(name = "utilisateur_nom", length = 255)
|
||||
private String utilisateurNom;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "titre", nullable = false, length = 255)
|
||||
private String titre;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "justification", columnDefinition = "TEXT")
|
||||
private String justification;
|
||||
|
||||
@Column(name = "categorie", length = 50)
|
||||
private String categorie; // UI, FEATURE, PERFORMANCE, SECURITE, INTEGRATION, MOBILE, REPORTING
|
||||
|
||||
@Column(name = "priorite_estimee", length = 50)
|
||||
private String prioriteEstimee; // BASSE, MOYENNE, HAUTE, CRITIQUE
|
||||
|
||||
@Column(name = "statut", length = 50)
|
||||
@Builder.Default
|
||||
private String statut = "NOUVELLE"; // NOUVELLE, EVALUATION, APPROUVEE, DEVELOPPEMENT, IMPLEMENTEE, REJETEE
|
||||
|
||||
@Column(name = "nb_votes")
|
||||
@Builder.Default
|
||||
private Integer nbVotes = 0;
|
||||
|
||||
@Column(name = "nb_commentaires")
|
||||
@Builder.Default
|
||||
private Integer nbCommentaires = 0;
|
||||
|
||||
@Column(name = "nb_vues")
|
||||
@Builder.Default
|
||||
private Integer nbVues = 0;
|
||||
|
||||
@Column(name = "date_soumission")
|
||||
private LocalDateTime dateSoumission;
|
||||
|
||||
@Column(name = "date_evaluation")
|
||||
private LocalDateTime dateEvaluation;
|
||||
|
||||
@Column(name = "date_implementation")
|
||||
private LocalDateTime dateImplementation;
|
||||
|
||||
@Column(name = "version_ciblee", length = 50)
|
||||
private String versionCiblee;
|
||||
|
||||
@Column(name = "mise_a_jour", columnDefinition = "TEXT")
|
||||
private String miseAJour;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
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;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité SuggestionVote pour gérer les votes sur les suggestions
|
||||
*
|
||||
* <p>Permet d'éviter qu'un utilisateur vote plusieurs fois pour la même suggestion.
|
||||
* La contrainte d'unicité (suggestion_id, utilisateur_id) est gérée au niveau de la base de données.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "suggestion_votes",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_suggestion_vote",
|
||||
columnNames = {"suggestion_id", "utilisateur_id"}
|
||||
)
|
||||
},
|
||||
indexes = {
|
||||
@Index(name = "idx_vote_suggestion", columnList = "suggestion_id"),
|
||||
@Index(name = "idx_vote_utilisateur", columnList = "utilisateur_id")
|
||||
}
|
||||
)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SuggestionVote extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@Column(name = "suggestion_id", nullable = false)
|
||||
private UUID suggestionId;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "utilisateur_id", nullable = false)
|
||||
private UUID utilisateurId;
|
||||
|
||||
@Column(name = "date_vote", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDateTime dateVote = LocalDateTime.now();
|
||||
|
||||
@PrePersist
|
||||
protected void onPrePersist() {
|
||||
if (dateVote == null) {
|
||||
dateVote = LocalDateTime.now();
|
||||
}
|
||||
if (getDateCreation() == null) {
|
||||
setDateCreation(LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.ArrayList;
|
||||
@@ -65,6 +66,7 @@ public class TemplateNotification extends BaseEntity {
|
||||
private String description;
|
||||
|
||||
/** Notifications utilisant ce template */
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "template", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<Notification> notifications = new ArrayList<>();
|
||||
|
||||
92
src/main/java/dev/lions/unionflow/server/entity/Ticket.java
Normal file
92
src/main/java/dev/lions/unionflow/server/entity/Ticket.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité Ticket pour la gestion des tickets support
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "tickets",
|
||||
indexes = {
|
||||
@Index(name = "idx_ticket_utilisateur", columnList = "utilisateur_id"),
|
||||
@Index(name = "idx_ticket_statut", columnList = "statut"),
|
||||
@Index(name = "idx_ticket_categorie", columnList = "categorie"),
|
||||
@Index(name = "idx_ticket_numero", columnList = "numero_ticket", unique = true)
|
||||
}
|
||||
)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Ticket extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "numero_ticket", nullable = false, unique = true, length = 50)
|
||||
private String numeroTicket;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "utilisateur_id", nullable = false)
|
||||
private UUID utilisateurId;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "sujet", nullable = false, length = 255)
|
||||
private String sujet;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "categorie", length = 50)
|
||||
private String categorie; // TECHNIQUE, FONCTIONNALITE, UTILISATION, COMPTE, AUTRE
|
||||
|
||||
@Column(name = "priorite", length = 50)
|
||||
private String priorite; // BASSE, NORMALE, HAUTE, URGENTE
|
||||
|
||||
@Column(name = "statut", length = 50)
|
||||
@Builder.Default
|
||||
private String statut = "OUVERT"; // OUVERT, EN_COURS, EN_ATTENTE, RESOLU, FERME
|
||||
|
||||
@Column(name = "agent_id")
|
||||
private UUID agentId;
|
||||
|
||||
@Column(name = "agent_nom", length = 255)
|
||||
private String agentNom;
|
||||
|
||||
@Column(name = "date_derniere_reponse")
|
||||
private LocalDateTime dateDerniereReponse;
|
||||
|
||||
@Column(name = "date_resolution")
|
||||
private LocalDateTime dateResolution;
|
||||
|
||||
@Column(name = "date_fermeture")
|
||||
private LocalDateTime dateFermeture;
|
||||
|
||||
@Column(name = "nb_messages")
|
||||
@Builder.Default
|
||||
private Integer nbMessages = 0;
|
||||
|
||||
@Column(name = "nb_fichiers")
|
||||
@Builder.Default
|
||||
private Integer nbFichiers = 0;
|
||||
|
||||
@Column(name = "note_satisfaction")
|
||||
private Integer noteSatisfaction;
|
||||
|
||||
@Column(name = "resolution", columnDefinition = "TEXT")
|
||||
private String resolution;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
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.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Entité Approbation de Transaction
|
||||
*
|
||||
* Représente une approbation dans le workflow financier multi-niveaux.
|
||||
* Chaque transaction financière au-dessus d'un certain seuil nécessite une ou plusieurs approbations.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "transaction_approvals", indexes = {
|
||||
@Index(name = "idx_approval_transaction", columnList = "transaction_id"),
|
||||
@Index(name = "idx_approval_status", columnList = "status"),
|
||||
@Index(name = "idx_approval_requester", columnList = "requester_id"),
|
||||
@Index(name = "idx_approval_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_approval_created", columnList = "created_at"),
|
||||
@Index(name = "idx_approval_level", columnList = "required_level")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TransactionApproval extends BaseEntity {
|
||||
|
||||
/** ID de la transaction financière à approuver */
|
||||
@NotNull
|
||||
@Column(name = "transaction_id", nullable = false)
|
||||
private UUID transactionId;
|
||||
|
||||
/** Type de transaction (CONTRIBUTION, DEPOSIT, WITHDRAWAL, TRANSFER, SOLIDARITY, EVENT, OTHER) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(CONTRIBUTION|DEPOSIT|WITHDRAWAL|TRANSFER|SOLIDARITY|EVENT|OTHER)$")
|
||||
@Column(name = "transaction_type", nullable = false, length = 20)
|
||||
private String transactionType;
|
||||
|
||||
/** Montant de la transaction */
|
||||
@NotNull
|
||||
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
@Column(name = "amount", nullable = false, precision = 14, scale = 2)
|
||||
private BigDecimal amount;
|
||||
|
||||
/** Code devise ISO 3 lettres */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^[A-Z]{3}$")
|
||||
@Builder.Default
|
||||
@Column(name = "currency", nullable = false, length = 3)
|
||||
private String currency = "XOF";
|
||||
|
||||
/** ID du membre demandeur */
|
||||
@NotNull
|
||||
@Column(name = "requester_id", nullable = false)
|
||||
private UUID requesterId;
|
||||
|
||||
/** Nom complet du demandeur (cache pour performance) */
|
||||
@NotBlank
|
||||
@Column(name = "requester_name", nullable = false, length = 200)
|
||||
private String requesterName;
|
||||
|
||||
/** Organisation concernée (peut être null pour transactions globales) */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
/** Niveau d'approbation requis (NONE, LEVEL1, LEVEL2, LEVEL3) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(NONE|LEVEL1|LEVEL2|LEVEL3)$")
|
||||
@Column(name = "required_level", nullable = false, length = 10)
|
||||
private String requiredLevel;
|
||||
|
||||
/** Statut de l'approbation (PENDING, APPROVED, VALIDATED, REJECTED, EXPIRED, CANCELLED) */
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^(PENDING|APPROVED|VALIDATED|REJECTED|EXPIRED|CANCELLED)$")
|
||||
@Builder.Default
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status = "PENDING";
|
||||
|
||||
/** Liste des actions d'approbateurs */
|
||||
@OneToMany(mappedBy = "approval", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<ApproverAction> approvers = new ArrayList<>();
|
||||
|
||||
/** Raison du rejet (si status = REJECTED) */
|
||||
@Size(max = 1000)
|
||||
@Column(name = "rejection_reason", length = 1000)
|
||||
private String rejectionReason;
|
||||
|
||||
/** Date de création de la demande d'approbation */
|
||||
@NotNull
|
||||
@Column(name = "created_at", nullable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/** Date d'expiration (timeout) */
|
||||
@Column(name = "expires_at")
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
/** Date de completion (approbation finale ou rejet) */
|
||||
@Column(name = "completed_at")
|
||||
private LocalDateTime completedAt;
|
||||
|
||||
/** Métadonnées additionnelles (JSON) */
|
||||
@Column(name = "metadata", columnDefinition = "TEXT")
|
||||
private String metadata;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (createdAt == null) {
|
||||
createdAt = LocalDateTime.now();
|
||||
}
|
||||
if (currency == null) {
|
||||
currency = "XOF";
|
||||
}
|
||||
if (status == null) {
|
||||
status = "PENDING";
|
||||
}
|
||||
// Expiration par défaut: 7 jours
|
||||
if (expiresAt == null) {
|
||||
expiresAt = createdAt.plusDays(7);
|
||||
}
|
||||
}
|
||||
|
||||
/** Méthode métier pour ajouter une action d'approbateur */
|
||||
public void addApproverAction(ApproverAction action) {
|
||||
approvers.add(action);
|
||||
action.setApproval(this);
|
||||
}
|
||||
|
||||
/** Méthode métier pour compter les approbations */
|
||||
public long countApprovals() {
|
||||
return approvers.stream()
|
||||
.filter(a -> "APPROVED".equals(a.getDecision()))
|
||||
.count();
|
||||
}
|
||||
|
||||
/** Méthode métier pour obtenir le nombre d'approbations requises */
|
||||
public int getRequiredApprovals() {
|
||||
return switch (requiredLevel) {
|
||||
case "NONE" -> 0;
|
||||
case "LEVEL1" -> 1;
|
||||
case "LEVEL2" -> 2;
|
||||
case "LEVEL3" -> 3;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si toutes les approbations sont reçues */
|
||||
public boolean hasAllApprovals() {
|
||||
return countApprovals() >= getRequiredApprovals();
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'approbation est expirée */
|
||||
public boolean isExpired() {
|
||||
return expiresAt != null && LocalDateTime.now().isAfter(expiresAt);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'approbation est en attente */
|
||||
public boolean isPending() {
|
||||
return "PENDING".equals(status);
|
||||
}
|
||||
|
||||
/** Méthode métier pour vérifier si l'approbation est complétée */
|
||||
public boolean isCompleted() {
|
||||
return "VALIDATED".equals(status) || "REJECTED".equals(status) || "CANCELLED".equals(status);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ 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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
@@ -124,6 +125,8 @@ public class TransactionWave extends BaseEntity {
|
||||
@JoinColumn(name = "compte_wave_id", nullable = false)
|
||||
private CompteWave compteWave;
|
||||
|
||||
@JsonIgnore
|
||||
|
||||
@OneToMany(mappedBy = "transactionWave", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Builder.Default
|
||||
private List<WebhookWave> webhooks = new ArrayList<>();
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Donnée de référence paramétrable via le client.
|
||||
*
|
||||
* <p>
|
||||
* Remplace toutes les enums Java et valeurs hardcodées
|
||||
* par une table unique CRUD-able depuis l'interface
|
||||
* d'administration. Chaque ligne appartient à un
|
||||
* {@code domaine} (ex: STATUT_ORGANISATION, DEVISE)
|
||||
* et porte un {@code code} unique dans ce domaine.
|
||||
*
|
||||
* <p>
|
||||
* Le champ {@code organisation} permet une
|
||||
* personnalisation par organisation. Lorsqu'il est
|
||||
* {@code null}, la valeur est globale à la plateforme.
|
||||
*
|
||||
* <p>
|
||||
* Table : {@code types_reference}
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2026-02-21
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "types_reference", indexes = {
|
||||
@Index(name = "idx_typeref_domaine", columnList = "domaine"),
|
||||
@Index(name = "idx_typeref_domaine_actif", columnList = "domaine, actif, ordre_affichage"),
|
||||
@Index(name = "idx_typeref_org", columnList = "organisation_id")
|
||||
}, uniqueConstraints = {
|
||||
@UniqueConstraint(name = "uk_typeref_domaine_code_org", columnNames = {
|
||||
"domaine", "code", "organisation_id"
|
||||
})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TypeReference extends BaseEntity {
|
||||
|
||||
/**
|
||||
* Domaine fonctionnel de cette valeur de référence.
|
||||
*
|
||||
* <p>
|
||||
* Exemples : {@code STATUT_ORGANISATION},
|
||||
* {@code TYPE_ORGANISATION}, {@code DEVISE}.
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 50)
|
||||
@Column(name = "domaine", nullable = false, length = 50)
|
||||
private String domaine;
|
||||
|
||||
/**
|
||||
* Code technique unique au sein du domaine.
|
||||
*
|
||||
* <p>
|
||||
* Exemples : {@code ACTIVE}, {@code XOF},
|
||||
* {@code ASSOCIATION}.
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 50)
|
||||
@Column(name = "code", nullable = false, length = 50)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* Libellé affiché dans l'interface utilisateur.
|
||||
*
|
||||
* <p>
|
||||
* Exemple : {@code "Franc CFA (UEMOA)"}.
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 200)
|
||||
@Column(name = "libelle", nullable = false, length = 200)
|
||||
private String libelle;
|
||||
|
||||
/** Description longue optionnelle. */
|
||||
@Size(max = 1000)
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* Classe d'icône pour le rendu UI.
|
||||
*
|
||||
* <p>
|
||||
* Exemple : {@code "pi-check-circle"}.
|
||||
*/
|
||||
@Size(max = 100)
|
||||
@Column(name = "icone", length = 100)
|
||||
private String icone;
|
||||
|
||||
/**
|
||||
* Code couleur hexadécimal pour le rendu UI.
|
||||
*
|
||||
* <p>
|
||||
* Exemple : {@code "#22C55E"}.
|
||||
*/
|
||||
@Size(max = 50)
|
||||
@Column(name = "couleur", length = 50)
|
||||
private String couleur;
|
||||
|
||||
/**
|
||||
* Niveau de sévérité pour les badges PrimeFaces.
|
||||
*
|
||||
* <p>
|
||||
* Valeurs typiques : {@code success},
|
||||
* {@code warning}, {@code danger}, {@code info}.
|
||||
*/
|
||||
@Size(max = 20)
|
||||
@Column(name = "severity", length = 20)
|
||||
private String severity;
|
||||
|
||||
/**
|
||||
* Ordre d'affichage dans les listes déroulantes.
|
||||
*
|
||||
* <p>
|
||||
* Les valeurs avec un ordre inférieur
|
||||
* apparaissent en premier.
|
||||
*/
|
||||
@Builder.Default
|
||||
@Column(name = "ordre_affichage", nullable = false)
|
||||
private Integer ordreAffichage = 0;
|
||||
|
||||
/**
|
||||
* Indique si cette valeur est la valeur par défaut
|
||||
* pour son domaine. Une seule valeur par défaut
|
||||
* est autorisée par domaine et organisation.
|
||||
*/
|
||||
@Builder.Default
|
||||
@Column(name = "est_defaut", nullable = false)
|
||||
private Boolean estDefaut = false;
|
||||
|
||||
/**
|
||||
* Indique si cette valeur est protégée par le
|
||||
* système. Les valeurs système ne peuvent être
|
||||
* ni supprimées ni désactivées par un
|
||||
* administrateur.
|
||||
*/
|
||||
@Builder.Default
|
||||
@Column(name = "est_systeme", nullable = false)
|
||||
private Boolean estSysteme = false;
|
||||
|
||||
/**
|
||||
* Organisation propriétaire de cette valeur.
|
||||
*
|
||||
* <p>
|
||||
* Lorsque {@code null}, la valeur est globale
|
||||
* à la plateforme et visible par toutes les
|
||||
* organisations.
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id")
|
||||
private Organisation organisation;
|
||||
|
||||
/**
|
||||
* Callback JPA exécuté avant la persistance.
|
||||
*
|
||||
* <p>
|
||||
* Normalise le code et le domaine en
|
||||
* majuscules pour garantir la cohérence.
|
||||
*/
|
||||
@Override
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (domaine != null) {
|
||||
domaine = domaine.toUpperCase();
|
||||
}
|
||||
if (code != null) {
|
||||
code = code.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutValidationEtape;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Historique des validations pour une demande d'aide.
|
||||
*
|
||||
* <p>Chaque ligne représente l'état d'une étape du workflow pour une demande.
|
||||
* La délégation de véto (valideur absent) est tracée avec motif — conformité BCEAO/OHADA.
|
||||
*
|
||||
* <p>Table : {@code validation_etapes_demande}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "validation_etapes_demande",
|
||||
indexes = {
|
||||
@Index(name = "idx_ved_demande", columnList = "demande_aide_id"),
|
||||
@Index(name = "idx_ved_valideur", columnList = "valideur_id"),
|
||||
@Index(name = "idx_ved_statut", columnList = "statut")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ValidationEtapeDemande extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demande_aide_id", nullable = false)
|
||||
private DemandeAide demandeAide;
|
||||
|
||||
@NotNull
|
||||
@Min(1) @Max(3)
|
||||
@Column(name = "etape_numero", nullable = false)
|
||||
private Integer etapeNumero;
|
||||
|
||||
/** Valideur assigné à cette étape */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "valideur_id")
|
||||
private Membre valideur;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut", nullable = false, length = 20)
|
||||
private StatutValidationEtape statut = StatutValidationEtape.EN_ATTENTE;
|
||||
|
||||
@Column(name = "date_validation")
|
||||
private LocalDateTime dateValidation;
|
||||
|
||||
@Column(name = "commentaire", length = 1000)
|
||||
private String commentaire;
|
||||
|
||||
/**
|
||||
* Valideur supérieur qui a désactivé le véto de {@code valideur}.
|
||||
* Renseigné uniquement en cas de délégation.
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "delegue_par_id")
|
||||
private Membre deleguePar;
|
||||
|
||||
/**
|
||||
* Motif et trace de la délégation — obligatoire si {@code deleguePar} est renseigné.
|
||||
* Conservé 10 ans — exigence BCEAO/OHADA/Fiscalité ivoirienne.
|
||||
*/
|
||||
@Column(name = "trace_delegation", columnDefinition = "TEXT")
|
||||
private String traceDelegation;
|
||||
|
||||
// ── Méthodes métier ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean estEnAttente() {
|
||||
return StatutValidationEtape.EN_ATTENTE.equals(statut);
|
||||
}
|
||||
|
||||
public boolean estFinalisee() {
|
||||
return StatutValidationEtape.APPROUVEE.equals(statut)
|
||||
|| StatutValidationEtape.REJETEE.equals(statut)
|
||||
|| StatutValidationEtape.DELEGUEE.equals(statut)
|
||||
|| StatutValidationEtape.EXPIREE.equals(statut);
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statut == null) statut = StatutValidationEtape.EN_ATTENTE;
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,13 @@ import lombok.NoArgsConstructor;
|
||||
* @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")
|
||||
})
|
||||
@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
|
||||
@@ -41,15 +39,13 @@ public class WebhookWave extends BaseEntity {
|
||||
private String waveEventId;
|
||||
|
||||
/** Type d'événement */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_evenement", length = 50)
|
||||
private TypeEvenementWebhook typeEvenement;
|
||||
private String typeEvenement;
|
||||
|
||||
/** Statut de traitement */
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Builder.Default
|
||||
@Column(name = "statut_traitement", nullable = false, length = 30)
|
||||
private StatutWebhook statutTraitement = StatutWebhook.EN_ATTENTE;
|
||||
private String statutTraitement = StatutWebhook.EN_ATTENTE.name();
|
||||
|
||||
/** Payload JSON reçu */
|
||||
@Column(name = "payload", columnDefinition = "TEXT")
|
||||
@@ -91,12 +87,13 @@ public class WebhookWave extends BaseEntity {
|
||||
|
||||
/** Méthode métier pour vérifier si le webhook est traité */
|
||||
public boolean isTraite() {
|
||||
return StatutWebhook.TRAITE.equals(statutTraitement);
|
||||
return StatutWebhook.TRAITE.name().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)
|
||||
return (StatutWebhook.ECHOUE.name().equals(statutTraitement)
|
||||
|| StatutWebhook.EN_ATTENTE.name().equals(statutTraitement))
|
||||
&& (nombreTentatives == null || nombreTentatives < 5);
|
||||
}
|
||||
|
||||
@@ -105,7 +102,7 @@ public class WebhookWave extends BaseEntity {
|
||||
protected void onCreate() {
|
||||
super.onCreate();
|
||||
if (statutTraitement == null) {
|
||||
statutTraitement = StatutWebhook.EN_ATTENTE;
|
||||
statutTraitement = StatutWebhook.EN_ATTENTE.name();
|
||||
}
|
||||
if (dateReception == null) {
|
||||
dateReception = LocalDateTime.now();
|
||||
@@ -115,4 +112,3 @@ public class WebhookWave extends BaseEntity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeWorkflow;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Configuration du workflow de validation pour une organisation.
|
||||
*
|
||||
* <p>Maximum 3 étapes ordonnées. Chaque étape requiert un rôle spécifique.
|
||||
* Exemple Mutuelle Y : Secrétaire (étape 1) → Trésorier (étape 2) → Président (étape 3).
|
||||
*
|
||||
* <p>Table : {@code workflow_validation_config}
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "workflow_validation_config",
|
||||
indexes = {
|
||||
@Index(name = "idx_wf_organisation", columnList = "organisation_id"),
|
||||
@Index(name = "idx_wf_type", columnList = "type_workflow")
|
||||
},
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
name = "uk_wf_org_type_etape",
|
||||
columnNames = {"organisation_id", "type_workflow", "etape_numero"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkflowValidationConfig extends BaseEntity {
|
||||
|
||||
@NotNull
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@NotNull
|
||||
@Builder.Default
|
||||
@Column(name = "type_workflow", nullable = false, length = 30)
|
||||
private TypeWorkflow typeWorkflow = TypeWorkflow.DEMANDE_AIDE;
|
||||
|
||||
/** Numéro d'ordre de l'étape (1, 2 ou 3) */
|
||||
@NotNull
|
||||
@Min(1) @Max(3)
|
||||
@Column(name = "etape_numero", nullable = false)
|
||||
private Integer etapeNumero;
|
||||
|
||||
/** Rôle nécessaire pour valider cette étape */
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "role_requis_id")
|
||||
private Role roleRequis;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "libelle_etape", nullable = false, length = 200)
|
||||
private String libelleEtape;
|
||||
|
||||
/** Délai maximum en heures avant expiration automatique (SLA) */
|
||||
@Builder.Default
|
||||
@Min(1)
|
||||
@Column(name = "delai_max_heures", nullable = false)
|
||||
private Integer delaiMaxHeures = 72;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package dev.lions.unionflow.server.entity.agricole;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.agricole.StatutCampagneAgricole;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "campagnes_agricoles", indexes = {
|
||||
@Index(name = "idx_agricole_organisation", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CampagneAgricole extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "designation", nullable = false, length = 200)
|
||||
private String designation;
|
||||
|
||||
@Column(name = "type_culture", length = 100)
|
||||
private String typeCulturePrincipale;
|
||||
|
||||
@Column(name = "surface_estimee_ha", precision = 19, scale = 4)
|
||||
private BigDecimal surfaceTotaleEstimeeHectares;
|
||||
|
||||
@Column(name = "volume_prev_tonnes", precision = 19, scale = 4)
|
||||
private BigDecimal volumePrevisionnelTonnes;
|
||||
|
||||
@Column(name = "volume_reel_tonnes", precision = 19, scale = 4)
|
||||
private BigDecimal volumeReelTonnes;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutCampagneAgricole statut = StatutCampagneAgricole.PREPARATION;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package dev.lions.unionflow.server.entity.collectefonds;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.collectefonds.StatutCampagneCollecte;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "campagnes_collecte", indexes = {
|
||||
@Index(name = "idx_collecte_organisation", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CampagneCollecte extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "titre", nullable = false, length = 200)
|
||||
private String titre;
|
||||
|
||||
@Column(name = "courte_description", length = 500)
|
||||
private String courteDescription;
|
||||
|
||||
@Column(name = "html_description_complete", columnDefinition = "TEXT")
|
||||
private String htmlDescriptionComplete;
|
||||
|
||||
@Column(name = "image_banniere_url", length = 500)
|
||||
private String imageBanniereUrl;
|
||||
|
||||
@Column(name = "objectif_financier", precision = 19, scale = 4)
|
||||
private BigDecimal objectifFinancier;
|
||||
|
||||
@Column(name = "montant_collecte_actuel", precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal montantCollecteActuel = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "nombre_donateurs")
|
||||
@Builder.Default
|
||||
private Integer nombreDonateurs = 0;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutCampagneCollecte statut = StatutCampagneCollecte.BROUILLON;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_ouverture", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDateTime dateOuverture = LocalDateTime.now();
|
||||
|
||||
@Column(name = "date_cloture_prevue")
|
||||
private LocalDateTime dateCloturePrevue;
|
||||
|
||||
@Column(name = "est_publique", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean estPublique = true;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package dev.lions.unionflow.server.entity.collectefonds;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "contributions_collecte", indexes = {
|
||||
@Index(name = "idx_contribution_campagne", columnList = "campagne_id"),
|
||||
@Index(name = "idx_contribution_membre", columnList = "membre_donateur_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ContributionCollecte extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "campagne_id", nullable = false)
|
||||
private CampagneCollecte campagne;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_donateur_id")
|
||||
private Membre membreDonateur;
|
||||
|
||||
@Column(name = "alias_donateur", length = 150)
|
||||
private String aliasDonateur;
|
||||
|
||||
@Column(name = "est_anonyme", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean estAnonyme = false;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "montant_soutien", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal montantSoutien;
|
||||
|
||||
@Column(name = "message_soutien", length = 500)
|
||||
private String messageSoutien;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_contribution", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDateTime dateContribution = LocalDateTime.now();
|
||||
|
||||
@Column(name = "transaction_paiement_id", length = 100)
|
||||
private String transactionPaiementId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut_paiement", length = 50)
|
||||
private StatutTransactionWave statutPaiement;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package dev.lions.unionflow.server.entity.culte;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.culte.TypeDonReligieux;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "dons_religieux", indexes = {
|
||||
@Index(name = "idx_don_c_organisation", columnList = "institution_id"),
|
||||
@Index(name = "idx_don_c_fidele", columnList = "fidele_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DonReligieux extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "institution_id", nullable = false)
|
||||
private Organisation institution;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "fidele_id")
|
||||
private Membre fidele;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_don", nullable = false, length = 50)
|
||||
private TypeDonReligieux typeDon;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "montant", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal montant;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_encaissement", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDateTime dateEncaissement = LocalDateTime.now();
|
||||
|
||||
@Column(name = "periode_nature", length = 150)
|
||||
private String periodeOuNatureAssociee;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.lions.unionflow.server.entity.gouvernance;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.gouvernance.NiveauEchelon;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "echelons_organigramme", indexes = {
|
||||
@Index(name = "idx_echelon_org", columnList = "organisation_id"),
|
||||
@Index(name = "idx_echelon_parent", columnList = "echelon_parent_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EchelonOrganigramme extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "echelon_parent_id")
|
||||
private Organisation echelonParent;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "niveau_echelon", nullable = false, length = 50)
|
||||
private NiveauEchelon niveau;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "designation", nullable = false, length = 200)
|
||||
private String designation;
|
||||
|
||||
@Column(name = "zone_delegation", length = 200)
|
||||
private String zoneGeographiqueOuDelegation;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package dev.lions.unionflow.server.entity.listener;
|
||||
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.service.KeycloakService;
|
||||
import io.quarkus.arc.Arc;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Listener JPA pour l'alimentation automatique
|
||||
* des champs d'audit.
|
||||
*
|
||||
* <p>
|
||||
* Renseigne automatiquement {@code creePar} lors
|
||||
* de la création et {@code modifiePar} lors de la
|
||||
* mise à jour, en récupérant l'email de
|
||||
* l'utilisateur authentifié via
|
||||
* {@link KeycloakService}.
|
||||
*
|
||||
* <p>
|
||||
* Ce listener est référencé via
|
||||
* {@code @EntityListeners} sur {@link BaseEntity},
|
||||
* garantissant que <strong>toutes</strong> les
|
||||
* entités héritent automatiquement de ce
|
||||
* comportement (WOU strict).
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2026-02-21
|
||||
*/
|
||||
public class AuditEntityListener {
|
||||
|
||||
/**
|
||||
* Utilisateur par défaut pour les opérations
|
||||
* système sans contexte de sécurité.
|
||||
*/
|
||||
private static final String UTILISATEUR_SYSTEME = "system";
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AuditEntityListener.class);
|
||||
|
||||
/**
|
||||
* Callback exécuté avant la persistance.
|
||||
*
|
||||
* <p>
|
||||
* Renseigne {@code creePar} avec l'email
|
||||
* de l'utilisateur authentifié, ou
|
||||
* {@code "system"} si aucun contexte de
|
||||
* sécurité n'est disponible.
|
||||
*
|
||||
* @param entity l'entité en cours de création
|
||||
*/
|
||||
@PrePersist
|
||||
public void avantCreation(BaseEntity entity) {
|
||||
if (entity.getCreePar() == null
|
||||
|| entity.getCreePar().isBlank()) {
|
||||
entity.setCreePar(
|
||||
obtenirUtilisateurCourant());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback exécuté avant la mise à jour.
|
||||
*
|
||||
* <p>
|
||||
* Renseigne {@code modifiePar} avec l'email
|
||||
* de l'utilisateur authentifié.
|
||||
*
|
||||
* @param entity l'entité en cours de modification
|
||||
*/
|
||||
@PreUpdate
|
||||
public void avantModification(BaseEntity entity) {
|
||||
entity.setModifiePar(
|
||||
obtenirUtilisateurCourant());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient l'email de l'utilisateur courant.
|
||||
*
|
||||
* <p>
|
||||
* Utilise {@link Arc#container()} pour
|
||||
* résoudre le {@link KeycloakService} depuis
|
||||
* le conteneur CDI de Quarkus.
|
||||
*
|
||||
* @return l'email ou {@code "system"} en fallback
|
||||
*/
|
||||
private String obtenirUtilisateurCourant() {
|
||||
try {
|
||||
KeycloakService keycloakService = Arc.container()
|
||||
.instance(KeycloakService.class)
|
||||
.get();
|
||||
if (keycloakService != null
|
||||
&& keycloakService.isAuthenticated()) {
|
||||
String email = keycloakService.getCurrentUserEmail();
|
||||
if (email != null && !email.isBlank()) {
|
||||
return email;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debugf(
|
||||
"Contexte de sécurité indisponible: %s",
|
||||
e.getMessage());
|
||||
}
|
||||
return UTILISATEUR_SYSTEME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package dev.lions.unionflow.server.entity.mutuelle.credit;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutDemandeCredit;
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeCredit;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "demandes_credit", indexes = {
|
||||
@Index(name = "idx_credit_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_credit_numero", columnList = "numero_dossier", unique = true)
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DemandeCredit extends BaseEntity {
|
||||
|
||||
@Column(name = "numero_dossier", unique = true, nullable = false, length = 50)
|
||||
private String numeroDossier;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_credit", nullable = false, length = 50)
|
||||
private TypeCredit typeCredit;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "compte_lie_id")
|
||||
private CompteEpargne compteLie;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "montant_demande", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal montantDemande;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "duree_mois_demande", nullable = false)
|
||||
private Integer dureeMoisDemande;
|
||||
|
||||
@Column(name = "justification_detaillee", columnDefinition = "TEXT")
|
||||
private String justificationDetaillee;
|
||||
|
||||
@Column(name = "montant_approuve", precision = 19, scale = 4)
|
||||
private BigDecimal montantApprouve;
|
||||
|
||||
@Column(name = "duree_mois_approuvee")
|
||||
private Integer dureeMoisApprouvee;
|
||||
|
||||
@Column(name = "taux_interet_annuel", precision = 5, scale = 2)
|
||||
private BigDecimal tauxInteretAnnuel;
|
||||
|
||||
@Column(name = "cout_total_credit", precision = 19, scale = 4)
|
||||
private BigDecimal coutTotalCredit;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutDemandeCredit statut = StatutDemandeCredit.SOUMISE;
|
||||
|
||||
@Column(name = "notes_comite", columnDefinition = "TEXT")
|
||||
private String notesComite;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_soumission", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDate dateSoumission = LocalDate.now();
|
||||
|
||||
@Column(name = "date_validation")
|
||||
private LocalDate dateValidation;
|
||||
|
||||
@Column(name = "date_premier_echeance")
|
||||
private LocalDate datePremierEcheance;
|
||||
|
||||
@OneToMany(mappedBy = "demandeCredit", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@Builder.Default
|
||||
private List<GarantieDemande> garanties = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "demandeCredit", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@OrderBy("ordre ASC")
|
||||
@Builder.Default
|
||||
private List<EcheanceCredit> echeancier = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package dev.lions.unionflow.server.entity.mutuelle.credit;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutEcheanceCredit;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "echeances_credit", indexes = {
|
||||
@Index(name = "idx_echeance_demande", columnList = "demande_credit_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(exclude = "demandeCredit")
|
||||
public class EcheanceCredit extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demande_credit_id", nullable = false)
|
||||
private DemandeCredit demandeCredit;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "ordre", nullable = false)
|
||||
private Integer ordre;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_echeance_prevue", nullable = false)
|
||||
private LocalDate dateEcheancePrevue;
|
||||
|
||||
@Column(name = "date_paiement_effectif")
|
||||
private LocalDate datePaiementEffectif;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "capital_amorti", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal capitalAmorti;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "interets_periode", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal interetsDeLaPeriode;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "montant_total_exigible", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal montantTotalExigible;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "capital_restant_du", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal capitalRestantDu;
|
||||
|
||||
@Column(name = "penalites_retard", precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal penalitesRetard = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "montant_regle", precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal montantRegle = BigDecimal.ZERO;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutEcheanceCredit statut = StatutEcheanceCredit.A_VENIR;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package dev.lions.unionflow.server.entity.mutuelle.credit;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeGarantie;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "garanties_demande")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(exclude = "demandeCredit")
|
||||
public class GarantieDemande extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "demande_credit_id", nullable = false)
|
||||
private DemandeCredit demandeCredit;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_garantie", nullable = false, length = 50)
|
||||
private TypeGarantie typeGarantie;
|
||||
|
||||
@Column(name = "valeur_estimee", precision = 19, scale = 4)
|
||||
private BigDecimal valeurEstimee;
|
||||
|
||||
@Column(name = "reference_description", length = 500)
|
||||
private String referenceOuDescription;
|
||||
|
||||
@Column(name = "document_preuve_id", length = 36)
|
||||
private String documentPreuveId;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package dev.lions.unionflow.server.entity.mutuelle.epargne;
|
||||
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.StatutCompteEpargne;
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeCompteEpargne;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "comptes_epargne", indexes = {
|
||||
@Index(name = "idx_compte_epargne_numero", columnList = "numero_compte", unique = true),
|
||||
@Index(name = "idx_compte_epargne_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_compte_epargne_orga", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CompteEpargne extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "numero_compte", unique = true, nullable = false, length = 50)
|
||||
private String numeroCompte;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_compte", nullable = false, length = 50)
|
||||
private TypeCompteEpargne typeCompte;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "solde_actuel", nullable = false, precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal soldeActuel = BigDecimal.ZERO;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "solde_bloque", nullable = false, precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal soldeBloque = BigDecimal.ZERO;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 30)
|
||||
@Builder.Default
|
||||
private StatutCompteEpargne statut = StatutCompteEpargne.ACTIF;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_ouverture", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDate dateOuverture = LocalDate.now();
|
||||
|
||||
@Column(name = "date_derniere_transaction")
|
||||
private LocalDate dateDerniereTransaction;
|
||||
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package dev.lions.unionflow.server.entity.mutuelle.epargne;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne;
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "transactions_epargne", indexes = {
|
||||
@Index(name = "idx_tx_epargne_compte", columnList = "compte_id"),
|
||||
@Index(name = "idx_tx_epargne_reference", columnList = "reference_externe")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TransactionEpargne extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "compte_id", nullable = false)
|
||||
private CompteEpargne compte;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_transaction", nullable = false, length = 50)
|
||||
private TypeTransactionEpargne type;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "montant", nullable = false, precision = 19, scale = 4)
|
||||
private BigDecimal montant;
|
||||
|
||||
@Column(name = "solde_avant", precision = 19, scale = 4)
|
||||
private BigDecimal soldeAvant;
|
||||
|
||||
@Column(name = "solde_apres", precision = 19, scale = 4)
|
||||
private BigDecimal soldeApres;
|
||||
|
||||
@Column(name = "motif", length = 500)
|
||||
private String motif;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_transaction", nullable = false)
|
||||
@Builder.Default
|
||||
private LocalDateTime dateTransaction = LocalDateTime.now();
|
||||
|
||||
@Column(name = "operateur_id", length = 36)
|
||||
private String operateurId;
|
||||
|
||||
@Column(name = "reference_externe", length = 100)
|
||||
private String referenceExterne;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut_execution", length = 50)
|
||||
private StatutTransactionWave statutExecution;
|
||||
|
||||
/** Origine des fonds (LCB-FT) — obligatoire au-dessus du seuil configuré */
|
||||
@Column(name = "origine_fonds", length = 200)
|
||||
private String origineFonds;
|
||||
|
||||
/** Pièce justificative (document) pour opérations au-dessus du seuil LCB-FT */
|
||||
@Column(name = "piece_justificative_id")
|
||||
private java.util.UUID pieceJustificativeId;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package dev.lions.unionflow.server.entity.ong;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.ong.StatutProjetOng;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "projets_ong", indexes = {
|
||||
@Index(name = "idx_projet_ong_organisation", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjetOng extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "nom_projet", nullable = false, length = 200)
|
||||
private String nomProjet;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String descriptionMandat;
|
||||
|
||||
@Column(name = "zone_geographique", length = 200)
|
||||
private String zoneGeographiqueIntervention;
|
||||
|
||||
@Column(name = "budget_previsionnel", precision = 19, scale = 4)
|
||||
private BigDecimal budgetPrevisionnel;
|
||||
|
||||
@Column(name = "depenses_reelles", precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal depensesReelles = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "date_lancement")
|
||||
private LocalDate dateLancement;
|
||||
|
||||
@Column(name = "date_fin_estimee")
|
||||
private LocalDate dateFinEstimee;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutProjetOng statut = StatutProjetOng.EN_ETUDE;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.lions.unionflow.server.entity.registre;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.registre.StatutAgrement;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "agrements_professionnels", indexes = {
|
||||
@Index(name = "idx_agrement_membre", columnList = "membre_id"),
|
||||
@Index(name = "idx_agrement_orga", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AgrementProfessionnel extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_id", nullable = false)
|
||||
private Membre membre;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@Column(name = "secteur_ordre", length = 150)
|
||||
private String secteurOuOrdre;
|
||||
|
||||
@Column(name = "numero_licence", length = 100)
|
||||
private String numeroLicenceOuRegistre;
|
||||
|
||||
@Column(name = "categorie_classement", length = 100)
|
||||
private String categorieClassement;
|
||||
|
||||
@Column(name = "date_delivrance")
|
||||
private LocalDate dateDelivrance;
|
||||
|
||||
@Column(name = "date_expiration")
|
||||
private LocalDate dateExpiration;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutAgrement statut = StatutAgrement.PROVISOIRE;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package dev.lions.unionflow.server.entity.tontine;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.tontine.FrequenceTour;
|
||||
import dev.lions.unionflow.server.api.enums.tontine.StatutTontine;
|
||||
import dev.lions.unionflow.server.api.enums.tontine.TypeTontine;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tontines", indexes = {
|
||||
@Index(name = "idx_tontine_organisation", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Tontine extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "nom", nullable = false, length = 150)
|
||||
private String nom;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_tontine", nullable = false, length = 50)
|
||||
private TypeTontine typeTontine;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "frequence", nullable = false, length = 50)
|
||||
private FrequenceTour frequence;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutTontine statut = StatutTontine.PLANIFIEE;
|
||||
|
||||
@Column(name = "date_debut_effective")
|
||||
private LocalDate dateDebutEffective;
|
||||
|
||||
@Column(name = "date_fin_prevue")
|
||||
private LocalDate dateFinPrevue;
|
||||
|
||||
@Column(name = "montant_mise_tour", precision = 19, scale = 4)
|
||||
private BigDecimal montantMiseParTour;
|
||||
|
||||
@Column(name = "limite_participants")
|
||||
private Integer limiteParticipants;
|
||||
|
||||
@OneToMany(mappedBy = "tontine", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@OrderBy("ordreTour ASC")
|
||||
@Builder.Default
|
||||
private List<TourTontine> calendrierTours = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package dev.lions.unionflow.server.entity.tontine;
|
||||
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tours_tontine", indexes = {
|
||||
@Index(name = "idx_tour_tontine", columnList = "tontine_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(exclude = { "tontine", "membreBeneficiaire" })
|
||||
public class TourTontine extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "tontine_id", nullable = false)
|
||||
private Tontine tontine;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "ordre_tour", nullable = false)
|
||||
private Integer ordreTour;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_ouverture_cotisations", nullable = false)
|
||||
private LocalDate dateOuvertureCotisations;
|
||||
|
||||
@Column(name = "date_tirage_remise")
|
||||
private LocalDate dateTirageOuRemise;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "montant_cible", nullable = false, precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal montantCible = BigDecimal.ZERO;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "cagnotte_collectee", nullable = false, precision = 19, scale = 4)
|
||||
@Builder.Default
|
||||
private BigDecimal cagnotteCollectee = BigDecimal.ZERO;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre_beneficiaire_id")
|
||||
private Membre membreBeneficiaire;
|
||||
|
||||
@Column(name = "statut_interne", length = 30)
|
||||
private String statutInterne;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package dev.lions.unionflow.server.entity.vote;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.vote.ModeScrutin;
|
||||
import dev.lions.unionflow.server.api.enums.vote.StatutVote;
|
||||
import dev.lions.unionflow.server.api.enums.vote.TypeVote;
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "campagnes_vote", indexes = {
|
||||
@Index(name = "idx_vote_orga", columnList = "organisation_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CampagneVote extends BaseEntity {
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "titre", nullable = false, length = 200)
|
||||
private String titre;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String descriptionOuResolution;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "organisation_id", nullable = false)
|
||||
private Organisation organisation;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "type_vote", nullable = false, length = 50)
|
||||
private TypeVote typeVote;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "mode_scrutin", nullable = false, length = 50)
|
||||
private ModeScrutin modeScrutin;
|
||||
|
||||
@NotNull
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "statut", nullable = false, length = 50)
|
||||
@Builder.Default
|
||||
private StatutVote statut = StatutVote.BROUILLON;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_ouverture", nullable = false)
|
||||
private LocalDateTime dateOuverture;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "date_fermeture", nullable = false)
|
||||
private LocalDateTime dateFermeture;
|
||||
|
||||
@Column(name = "restreindre_membres_ajour", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean restreindreMembresAJour = true;
|
||||
|
||||
@Column(name = "autoriser_vote_blanc", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean autoriserVoteBlanc = true;
|
||||
|
||||
@Column(name = "total_electeurs")
|
||||
private Integer totalElecteursInscrits;
|
||||
|
||||
@Column(name = "total_votants")
|
||||
private Integer totalVotantsEffectifs;
|
||||
|
||||
@Column(name = "total_blancs_nuls")
|
||||
private Integer totalVotesBlancsOuNuls;
|
||||
|
||||
@OneToMany(mappedBy = "campagneVote", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@Builder.Default
|
||||
private List<Candidat> candidats = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package dev.lions.unionflow.server.entity.vote;
|
||||
|
||||
import dev.lions.unionflow.server.entity.BaseEntity;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.*;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "candidats", indexes = {
|
||||
@Index(name = "idx_candidat_campagne", columnList = "campagne_vote_id")
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(exclude = "campagneVote")
|
||||
public class Candidat extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "campagne_vote_id", nullable = false)
|
||||
private CampagneVote campagneVote;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "nom_candidature", nullable = false, length = 150)
|
||||
private String nomCandidatureOuChoix;
|
||||
|
||||
@Column(name = "membre_associe_id", length = 36)
|
||||
private String membreIdAssocie;
|
||||
|
||||
@Column(name = "profession_foi", columnDefinition = "TEXT")
|
||||
private String professionDeFoi;
|
||||
|
||||
@Column(name = "photo_url", length = 500)
|
||||
private String photoUrl;
|
||||
|
||||
@Column(name = "nombre_voix")
|
||||
@Builder.Default
|
||||
private Integer nombreDeVoix = 0;
|
||||
|
||||
@Column(name = "pourcentage", precision = 5, scale = 2)
|
||||
@Builder.Default
|
||||
private BigDecimal pourcentageObtenu = BigDecimal.ZERO;
|
||||
}
|
||||
Reference in New Issue
Block a user