Phase 0 : @RolesAllowed SUPER_ADMIN sur POST/DELETE organisations ; AuthenticationFilter pages super-admin Phase 2 : OrganisationModuleService, @RequiresModule, ModuleAccessFilter, RoleService, PermissionChecker Phase 3 : multi-org context switching (OrganisationContextFilter, headers X-Active-Organisation-Id / X-Active-Role) Phase 4 : feature-gating navigation par typeOrganisation (web MenuBean + mobile MorePage) Phase 5 : MemberLifecycleService — 8 transitions (activer/suspendre/radier/archiver/inviter/accepter/expirer/rappels) Phase 6 : FormuleAbonnement Option C (planCommercial, apiAccess, federationAccess, quotas) + SouscriptionOrganisation méthodes quota Phase 7 : DashboardResource SUPER_ADMIN ajouté ; DashboardBean.checkAccessAndRedirect() ; dashboards distincts par rôle Phase 8 : MembreResourceLifecycleRbacTest, SouscriptionQuotaOptionCTest, OrganisationContextHolderTest, OrganisationContextFilterMultiOrgTest, MemberLifecycleServiceTest
142 lines
4.8 KiB
Java
142 lines
4.8 KiB
Java
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.time.LocalDateTime;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
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;
|
|
|
|
// ── Champs d'invitation (StatutMembre.INVITE) ──────────────────────────────
|
|
|
|
/** Date à laquelle l'invitation a été envoyée. */
|
|
@Column(name = "date_invitation")
|
|
private LocalDateTime dateInvitation;
|
|
|
|
/** Date d'expiration de l'invitation (null = pas d'expiration). */
|
|
@Column(name = "date_expiration_invitation")
|
|
private LocalDateTime dateExpirationInvitation;
|
|
|
|
/** Token opaque utilisé dans le lien d'invitation envoyé par email. */
|
|
@Column(name = "token_invitation", length = 64)
|
|
private String tokenInvitation;
|
|
|
|
/** ID de l'administrateur qui a envoyé l'invitation. */
|
|
@Column(name = "invite_par")
|
|
private UUID invitePar;
|
|
|
|
/** Motif d'archivage (pour StatutMembre.ARCHIVE). */
|
|
@Column(name = "motif_archivage", length = 500)
|
|
private String motifArchivage;
|
|
|
|
// ── Rôle fonctionnel dans l'organisation ─────────────────────────────────
|
|
|
|
/** Rôle de ce membre dans l'organisation (ex: PRESIDENT, TRESORIER...). */
|
|
@Column(name = "role_org", length = 50)
|
|
private String roleOrg;
|
|
|
|
// ── 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;
|
|
}
|
|
}
|
|
}
|