feat: PHASE 1 - Adresses et Rôles/Permissions
PHASE 1.1 - Entité Adresse: - Création entité Adresse avec types (SIEGE_SOCIAL, BUREAU, DOMICILE, AUTRE) - Relations flexibles: Organisation, Membre, Evenement - Enum TypeAdresse dans module API (DRY/WOU) - Repository et Service AdresseService - Relations bidirectionnelles mises à jour PHASE 1.2 - Système Rôles et Permissions: - Entité Role avec types (SYSTEME, ORGANISATION, PERSONNALISE) - Entité Permission avec structure MODULE > RESSOURCE > ACTION - Tables de liaison MembreRole et RolePermission - Repositories pour toutes les entités - Services RoleService et PermissionService - Relations bidirectionnelles dans Membre Respect strict DRY/WOU: - Enums dans module API réutilisables - Patterns de service cohérents - Relations JPA standardisées
This commit is contained in:
310
PLAN_IMPLEMENTATION_ARCHITECTURE_V3.md
Normal file
310
PLAN_IMPLEMENTATION_ARCHITECTURE_V3.md
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
# Plan d'Implémentation - Architecture UnionFlow v3.0
|
||||||
|
|
||||||
|
**Date** : 2025-01-29
|
||||||
|
**Objectif** : Aligner le code actuel avec l'architecture cible (union-flow.puml)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 État Actuel vs Architecture Cible
|
||||||
|
|
||||||
|
### ✅ Entités Existantes
|
||||||
|
- ✅ BaseEntity
|
||||||
|
- ✅ Organisation
|
||||||
|
- ✅ TypeOrganisationEntity
|
||||||
|
- ✅ Membre
|
||||||
|
- ✅ Cotisation
|
||||||
|
- ✅ Adhesion
|
||||||
|
- ✅ Evenement
|
||||||
|
- ✅ InscriptionEvenement
|
||||||
|
- ✅ DemandeAide
|
||||||
|
- ✅ AuditLog
|
||||||
|
|
||||||
|
### ❌ Entités Manquantes
|
||||||
|
1. **Paiements** : Paiement, PaiementCotisation, PaiementAdhesion, PaiementEvenement, PaiementAide
|
||||||
|
2. **Wave** : CompteWave, TransactionWave, WebhookWave, ConfigurationWave
|
||||||
|
3. **Comptabilité** : CompteComptable, JournalComptable, EcritureComptable, LigneEcriture
|
||||||
|
4. **Documents** : Document, PieceJointe
|
||||||
|
5. **Notifications** : Notification, TemplateNotification
|
||||||
|
6. **Rôles/Permissions** : Role, Permission, MembreRole, RolePermission
|
||||||
|
7. **Adresses** : Adresse (séparée)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Plan d'Implémentation par Étapes
|
||||||
|
|
||||||
|
### **PHASE 1 : FONDATIONS - Adresses et Rôles** (Priorité HAUTE)
|
||||||
|
**Durée estimée** : 2-3 jours
|
||||||
|
|
||||||
|
#### Étape 1.1 : Entité Adresse
|
||||||
|
- [ ] Créer `Adresse.java` (entité séparée)
|
||||||
|
- [ ] Types d'adresse : SIEGE_SOCIAL, BUREAU, DOMICILE, AUTRE
|
||||||
|
- [ ] Relations : Organisation ↔ Adresse (0..*), Membre ↔ Adresse (0..*)
|
||||||
|
- [ ] Migration : Extraire adresses de Organisation et Membre
|
||||||
|
- [ ] Repository : `AdresseRepository`
|
||||||
|
- [ ] Service : `AdresseService`
|
||||||
|
- [ ] DTO : `AdresseDTO`
|
||||||
|
|
||||||
|
#### Étape 1.2 : Système de Rôles et Permissions
|
||||||
|
- [ ] Créer `Role.java` (entité)
|
||||||
|
- [ ] Créer `Permission.java` (entité)
|
||||||
|
- [ ] Créer `MembreRole.java` (table de liaison)
|
||||||
|
- [ ] Créer `RolePermission.java` (table de liaison)
|
||||||
|
- [ ] Enums : TypeRole, TypePermission
|
||||||
|
- [ ] Repository : `RoleRepository`, `PermissionRepository`, `MembreRoleRepository`, `RolePermissionRepository`
|
||||||
|
- [ ] Service : `RoleService`, `PermissionService`
|
||||||
|
- [ ] DTOs : `RoleDTO`, `PermissionDTO`, `MembreRoleDTO`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 2 : SYSTÈME DE PAIEMENTS CENTRALISÉ** (Priorité CRITIQUE)
|
||||||
|
**Durée estimée** : 3-4 jours
|
||||||
|
|
||||||
|
#### Étape 2.1 : Entité Paiement
|
||||||
|
- [ ] Créer `Paiement.java` (entité centrale)
|
||||||
|
- [ ] Enum : `MethodePaiement` (WAVE_MOBILE_MONEY, ORANGE_MONEY, MTN_MOBILE_MONEY, etc.)
|
||||||
|
- [ ] Enum : `StatutPaiement` (EN_ATTENTE, EN_COURS, VALIDE, ECHOUE, ANNULE, REMBOURSE)
|
||||||
|
- [ ] Champs : montant, devise, datePaiement, dateValidation, validateur, references externes
|
||||||
|
- [ ] Relation : Paiement → Membre (1-N)
|
||||||
|
- [ ] Repository : `PaiementRepository`
|
||||||
|
- [ ] Service : `PaiementService`
|
||||||
|
- [ ] DTO : `PaiementDTO`
|
||||||
|
|
||||||
|
#### Étape 2.2 : Tables de Liaison Paiements
|
||||||
|
- [ ] Créer `PaiementCotisation.java` (table de liaison)
|
||||||
|
- [ ] Créer `PaiementAdhesion.java` (table de liaison)
|
||||||
|
- [ ] Créer `PaiementEvenement.java` (table de liaison)
|
||||||
|
- [ ] Créer `PaiementAide.java` (table de liaison)
|
||||||
|
- [ ] Champs communs : montantApplique, dateApplication
|
||||||
|
- [ ] Relations : Paiement ↔ Cotisation/Adhesion/Evenement/Aide
|
||||||
|
- [ ] Repositories : `PaiementCotisationRepository`, etc.
|
||||||
|
- [ ] Services : Logique d'application des paiements
|
||||||
|
|
||||||
|
#### Étape 2.3 : Refactoring Cotisation et Adhesion
|
||||||
|
- [ ] Modifier `Cotisation.java` : Retirer montantPaye, utiliser PaiementCotisation
|
||||||
|
- [ ] Modifier `Adhesion.java` : Retirer montantPaye, utiliser PaiementAdhesion
|
||||||
|
- [ ] Mettre à jour `CotisationService` : Utiliser PaiementService
|
||||||
|
- [ ] Mettre à jour `AdhesionService` : Utiliser PaiementService
|
||||||
|
- [ ] Migration des données existantes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 3 : INTÉGRATION WAVE MOBILE MONEY** (Priorité CRITIQUE)
|
||||||
|
**Durée estimée** : 4-5 jours
|
||||||
|
|
||||||
|
#### Étape 3.1 : Entités Wave
|
||||||
|
- [ ] Créer `CompteWave.java`
|
||||||
|
- Numéro téléphone (+225XXXXXXXX)
|
||||||
|
- Statut : NON_VERIFIE, VERIFIE, SUSPENDU, BLOQUE
|
||||||
|
- Relations : Organisation (1-N), Membre (0..1)
|
||||||
|
- Identifiants API encryptés
|
||||||
|
- [ ] Créer `TransactionWave.java`
|
||||||
|
- Identifiants Wave (transactionId, requestId, reference)
|
||||||
|
- Type : DEPOT, RETRAIT, TRANSFERT, PAIEMENT, REMBOURSEMENT
|
||||||
|
- Statut : INITIALISE, EN_ATTENTE, EN_COURS, REUSSIE, ECHOUE, ANNULEE, EXPIRED
|
||||||
|
- Montant, frais, montant net
|
||||||
|
- Métadonnées JSON
|
||||||
|
- Relation : CompteWave (1-N), Paiement (0..1)
|
||||||
|
- [ ] Créer `WebhookWave.java`
|
||||||
|
- Wave event ID
|
||||||
|
- Type d'événement
|
||||||
|
- Statut traitement : EN_ATTENTE, EN_TRAITEMENT, TRAITE, ECHOUE, IGNORE
|
||||||
|
- Payload JSON
|
||||||
|
- Relation : TransactionWave (0..1), Paiement (0..1)
|
||||||
|
- [ ] Créer `ConfigurationWave.java`
|
||||||
|
- Clé-valeur pour configuration
|
||||||
|
- Support sandbox/production
|
||||||
|
|
||||||
|
#### Étape 3.2 : Repositories et Services Wave
|
||||||
|
- [ ] Repositories : `CompteWaveRepository`, `TransactionWaveRepository`, `WebhookWaveRepository`, `ConfigurationWaveRepository`
|
||||||
|
- [ ] Service : `WaveService` (intégration API Wave)
|
||||||
|
- Méthodes : initierPaiement, verifierTransaction, traiterWebhook
|
||||||
|
- Gestion retry avec backoff exponentiel
|
||||||
|
- Validation de signature webhook
|
||||||
|
- Chiffrement des clés API
|
||||||
|
|
||||||
|
#### Étape 3.3 : Intégration avec PaiementService
|
||||||
|
- [ ] Modifier `PaiementService` : Support Wave
|
||||||
|
- [ ] Workflow : Initiation → TransactionWave → Webhook → Validation
|
||||||
|
- [ ] Gestion des erreurs et retry
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 4 : COMPTABILITÉ** (Priorité MOYENNE)
|
||||||
|
**Durée estimée** : 3-4 jours
|
||||||
|
|
||||||
|
#### Étape 4.1 : Plan Comptable
|
||||||
|
- [ ] Créer `CompteComptable.java`
|
||||||
|
- Numéro compte unique
|
||||||
|
- Type : ACTIF, PASSIF, CHARGES, PRODUITS, TRESORERIE, AUTRE
|
||||||
|
- Classe comptable (1-7)
|
||||||
|
- Solde initial, solde actuel
|
||||||
|
- [ ] Repository : `CompteComptableRepository`
|
||||||
|
- [ ] Service : `CompteComptableService`
|
||||||
|
- [ ] DTO : `CompteComptableDTO`
|
||||||
|
|
||||||
|
#### Étape 4.2 : Journaux et Écritures
|
||||||
|
- [ ] Créer `JournalComptable.java`
|
||||||
|
- Code unique
|
||||||
|
- Type : ACHATS, VENTES, BANQUE, CAISSE, OD
|
||||||
|
- Période, statut
|
||||||
|
- [ ] Créer `EcritureComptable.java`
|
||||||
|
- Numéro pièce unique
|
||||||
|
- Date, libellé, référence
|
||||||
|
- Lettrage, pointage
|
||||||
|
- Relation : JournalComptable (1-N), Organisation (1-N), Paiement (0..1)
|
||||||
|
- [ ] Créer `LigneEcriture.java`
|
||||||
|
- Numéro ligne
|
||||||
|
- Compte débiteur/créditeur
|
||||||
|
- Montant débit/crédit
|
||||||
|
- Relation : EcritureComptable (1-N), CompteComptable (1-N)
|
||||||
|
- Validation : Débit = Crédit
|
||||||
|
|
||||||
|
#### Étape 4.3 : Service Comptable
|
||||||
|
- [ ] Service : `ComptabiliteService`
|
||||||
|
- Génération automatique d'écritures pour paiements
|
||||||
|
- Rapprochement bancaire
|
||||||
|
- Pointage et lettrage
|
||||||
|
- [ ] Intégration avec PaiementService
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 5 : GESTION DOCUMENTAIRE** (Priorité MOYENNE)
|
||||||
|
**Durée estimée** : 2-3 jours
|
||||||
|
|
||||||
|
#### Étape 5.1 : Entités Documents
|
||||||
|
- [ ] Créer `Document.java`
|
||||||
|
- Nom fichier, nom original
|
||||||
|
- Chemin stockage
|
||||||
|
- Type MIME, taille
|
||||||
|
- Hash MD5, SHA256
|
||||||
|
- Type : IDENTITE, JUSTIFICATIF_DOMICILE, PHOTO, CONTRAT, FACTURE, RECU, RAPPORT, AUTRE
|
||||||
|
- [ ] Créer `PieceJointe.java`
|
||||||
|
- Ordre d'affichage
|
||||||
|
- Libellé, commentaire
|
||||||
|
- Relations flexibles : Membre, Organisation, Cotisation, Adhesion, DemandeAide, TransactionWave
|
||||||
|
|
||||||
|
#### Étape 5.2 : Services Documents
|
||||||
|
- [ ] Repositories : `DocumentRepository`, `PieceJointeRepository`
|
||||||
|
- [ ] Service : `DocumentService`
|
||||||
|
- Upload sécurisé
|
||||||
|
- Vérification intégrité (hash)
|
||||||
|
- Contrôle d'accès
|
||||||
|
- Audit téléchargements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 6 : SYSTÈME DE NOTIFICATIONS** (Priorité MOYENNE)
|
||||||
|
**Durée estimée** : 2-3 jours
|
||||||
|
|
||||||
|
#### Étape 6.1 : Entités Notifications
|
||||||
|
- [ ] Créer `TemplateNotification.java`
|
||||||
|
- Code unique
|
||||||
|
- Sujet, corps (texte et HTML)
|
||||||
|
- Variables disponibles (JSON)
|
||||||
|
- Canaux supportés
|
||||||
|
- Support multi-langues
|
||||||
|
- [ ] Créer `Notification.java`
|
||||||
|
- Type : EMAIL, SMS, PUSH, IN_APP, SYSTEME
|
||||||
|
- Priorité : CRITIQUE, HAUTE, NORMALE, BASSE
|
||||||
|
- Statut : EN_ATTENTE, ENVOYEE, LUE, ECHOUE, ANNULEE
|
||||||
|
- Relations : Membre (1-N), Organisation (0..1), TemplateNotification (0..1)
|
||||||
|
|
||||||
|
#### Étape 6.2 : Service Notifications
|
||||||
|
- [ ] Repositories : `NotificationRepository`, `TemplateNotificationRepository`
|
||||||
|
- [ ] Service : `NotificationService`
|
||||||
|
- Envoi multi-canaux
|
||||||
|
- Retry automatique
|
||||||
|
- Priorisation
|
||||||
|
- Templates réutilisables
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 7 : MISE À JOUR MEMBRE** (Priorité HAUTE)
|
||||||
|
**Durée estimée** : 1-2 jours
|
||||||
|
|
||||||
|
#### Étape 7.1 : Ajout Champs Membre
|
||||||
|
- [ ] Ajouter `telephoneWave` (String, format +225XXXXXXXX)
|
||||||
|
- [ ] Ajouter `photoUrl` (String)
|
||||||
|
- [ ] Relation : Membre → CompteWave (0..1)
|
||||||
|
- [ ] Relation : Membre → Adresse (0..*)
|
||||||
|
- [ ] Relation : Membre → MembreRole (1-N)
|
||||||
|
|
||||||
|
#### Étape 7.2 : Migration Données
|
||||||
|
- [ ] Script de migration pour extraire adresses
|
||||||
|
- [ ] Attribution rôles par défaut
|
||||||
|
- [ ] Validation format téléphone Wave
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 8 : MISE À JOUR ORGANISATION** (Priorité MOYENNE)
|
||||||
|
**Durée estimée** : 1 jour
|
||||||
|
|
||||||
|
#### Étape 8.1 : Relations Organisation
|
||||||
|
- [ ] Relation : Organisation → CompteWave (1-N)
|
||||||
|
- [ ] Relation : Organisation → Adresse (0..*)
|
||||||
|
- [ ] Migration : Extraire adresses vers entité Adresse
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 9 : MISE À JOUR ÉVÉNEMENT** (Priorité MOYENNE)
|
||||||
|
**Durée estimée** : 1 jour
|
||||||
|
|
||||||
|
#### Étape 9.1 : Relations Evenement
|
||||||
|
- [ ] Relation : Evenement → Adresse (0..1)
|
||||||
|
- [ ] Relation : Evenement → PaiementEvenement (0..*)
|
||||||
|
- [ ] Migration : Extraire adresse vers entité Adresse
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 10 : RESSOURCES REST ET DTOs** (Priorité HAUTE)
|
||||||
|
**Durée estimée** : 3-4 jours
|
||||||
|
|
||||||
|
#### Étape 10.1 : DTOs API
|
||||||
|
- [ ] Créer tous les DTOs manquants dans `unionflow-server-api`
|
||||||
|
- [ ] Enums dans `unionflow-server-api`
|
||||||
|
|
||||||
|
#### Étape 10.2 : Resources REST
|
||||||
|
- [ ] `PaiementResource`
|
||||||
|
- [ ] `WaveResource` (CompteWave, TransactionWave, WebhookWave)
|
||||||
|
- [ ] `ComptabiliteResource` (CompteComptable, JournalComptable, EcritureComptable)
|
||||||
|
- [ ] `DocumentResource`
|
||||||
|
- [ ] `NotificationResource`
|
||||||
|
- [ ] `RoleResource`, `PermissionResource`
|
||||||
|
- [ ] `AdresseResource`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **PHASE 11 : TESTS ET VALIDATION** (Priorité HAUTE)
|
||||||
|
**Durée estimée** : 2-3 jours
|
||||||
|
|
||||||
|
#### Étape 11.1 : Tests Unitaires
|
||||||
|
- [ ] Tests pour toutes les nouvelles entités
|
||||||
|
- [ ] Tests pour tous les services
|
||||||
|
- [ ] Tests d'intégration Wave (mock)
|
||||||
|
|
||||||
|
#### Étape 11.2 : Tests d'Intégration
|
||||||
|
- [ ] Tests de workflow complet paiement
|
||||||
|
- [ ] Tests webhooks Wave
|
||||||
|
- [ ] Tests génération écritures comptables
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Ordre d'Implémentation Recommandé
|
||||||
|
|
||||||
|
1. **PHASE 1** : Adresses et Rôles (fondations)
|
||||||
|
2. **PHASE 2** : Système de Paiements (critique)
|
||||||
|
3. **PHASE 7** : Mise à jour Membre (dépend de Phase 1)
|
||||||
|
4. **PHASE 3** : Intégration Wave (dépend de Phase 2)
|
||||||
|
5. **PHASE 4** : Comptabilité (dépend de Phase 2)
|
||||||
|
6. **PHASE 5** : Documents
|
||||||
|
7. **PHASE 6** : Notifications
|
||||||
|
8. **PHASE 8-9** : Mises à jour Organisation/Evenement
|
||||||
|
9. **PHASE 10** : Resources REST
|
||||||
|
10. **PHASE 11** : Tests
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Démarrage de l'Implémentation
|
||||||
|
|
||||||
|
**Prochaine étape** : Commencer par la PHASE 1 - Étape 1.1 : Création de l'entité Adresse
|
||||||
|
|
||||||
275
union-flow.puml
Normal file
275
union-flow.puml
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
@startuml MCD_UnionFlow
|
||||||
|
!theme plain
|
||||||
|
skinparam linetype ortho
|
||||||
|
skinparam packageStyle rectangle
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
|
||||||
|
title UnionFlow - MCD avec Wave Mobile Money
|
||||||
|
|
||||||
|
package Base {
|
||||||
|
abstract class BaseEntity {
|
||||||
|
+ id : UUID
|
||||||
|
+ dateCreation : LocalDateTime
|
||||||
|
+ version : Long
|
||||||
|
+ actif : Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Orgs {
|
||||||
|
class Organisation {
|
||||||
|
+ nom : String
|
||||||
|
+ email : String
|
||||||
|
+ statut : StatutOrg
|
||||||
|
}
|
||||||
|
class TypeOrganisation {
|
||||||
|
+ code : String
|
||||||
|
}
|
||||||
|
class Adresse {
|
||||||
|
+ ville : String
|
||||||
|
+ pays : String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Membres {
|
||||||
|
class Membre {
|
||||||
|
+ numeroMembre : String
|
||||||
|
+ email : String
|
||||||
|
+ telephoneWave : String
|
||||||
|
}
|
||||||
|
class Role {
|
||||||
|
+ code : String
|
||||||
|
}
|
||||||
|
class Permission {
|
||||||
|
+ code : String
|
||||||
|
}
|
||||||
|
class MembreRole {
|
||||||
|
}
|
||||||
|
class RolePermission {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Paiements {
|
||||||
|
class Paiement {
|
||||||
|
+ montant : BigDecimal
|
||||||
|
+ methodePaiement : MethodePmt
|
||||||
|
+ statutPaiement : StatutPmt
|
||||||
|
}
|
||||||
|
class Cotisation {
|
||||||
|
+ montantDu : BigDecimal
|
||||||
|
+ statut : StatutCot
|
||||||
|
}
|
||||||
|
class PaiementCotisation {
|
||||||
|
+ montantApplique : BigDecimal
|
||||||
|
}
|
||||||
|
class Adhesion {
|
||||||
|
+ fraisAdhesion : BigDecimal
|
||||||
|
+ statut : StatutAdh
|
||||||
|
}
|
||||||
|
class PaiementAdhesion {
|
||||||
|
+ montantApplique : BigDecimal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Wave {
|
||||||
|
class CompteWave {
|
||||||
|
+ numeroTelephone : String
|
||||||
|
+ statutCompte : StatutWave
|
||||||
|
}
|
||||||
|
class TransactionWave {
|
||||||
|
+ waveTransactionId : String
|
||||||
|
+ montant : BigDecimal
|
||||||
|
+ statutTransaction : StatutTxWave
|
||||||
|
}
|
||||||
|
class WebhookWave {
|
||||||
|
+ waveEventId : String
|
||||||
|
+ statutTraitement : StatutWebhook
|
||||||
|
}
|
||||||
|
class ConfigurationWave {
|
||||||
|
+ cle : String
|
||||||
|
+ valeur : String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Compta {
|
||||||
|
class CompteComptable {
|
||||||
|
+ numeroCompte : String
|
||||||
|
+ soldeActuel : BigDecimal
|
||||||
|
}
|
||||||
|
class JournalComptable {
|
||||||
|
+ code : String
|
||||||
|
}
|
||||||
|
class EcritureComptable {
|
||||||
|
+ montantDebit : BigDecimal
|
||||||
|
+ montantCredit : BigDecimal
|
||||||
|
}
|
||||||
|
class LigneEcriture {
|
||||||
|
+ montantDebit : BigDecimal
|
||||||
|
+ montantCredit : BigDecimal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Evenements {
|
||||||
|
class Evenement {
|
||||||
|
+ titre : String
|
||||||
|
+ typeEvenement : TypeEvt
|
||||||
|
+ statut : StatutEvt
|
||||||
|
}
|
||||||
|
class InscriptionEvenement {
|
||||||
|
+ statut : StatutInsc
|
||||||
|
}
|
||||||
|
class PaiementEvenement {
|
||||||
|
+ montantApplique : BigDecimal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Solidarite {
|
||||||
|
class DemandeAide {
|
||||||
|
+ typeAide : TypeAide
|
||||||
|
+ statut : StatutAide
|
||||||
|
+ montantDemande : BigDecimal
|
||||||
|
}
|
||||||
|
class PaiementAide {
|
||||||
|
+ montantApplique : BigDecimal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Docs {
|
||||||
|
class Document {
|
||||||
|
+ nomFichier : String
|
||||||
|
+ hashMd5 : String
|
||||||
|
}
|
||||||
|
class PieceJointe {
|
||||||
|
+ ordre : Integer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Notifs {
|
||||||
|
class Notification {
|
||||||
|
+ typeNotification : TypeNotif
|
||||||
|
+ statut : StatutNotif
|
||||||
|
}
|
||||||
|
class TemplateNotification {
|
||||||
|
+ code : String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package Audit {
|
||||||
|
class AuditLog {
|
||||||
|
+ typeAction : TypeAction
|
||||||
|
+ severite : Severite
|
||||||
|
}
|
||||||
|
class ParametreSysteme {
|
||||||
|
+ cle : String
|
||||||
|
+ valeur : String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseEntity <|-- Organisation
|
||||||
|
BaseEntity <|-- TypeOrganisation
|
||||||
|
BaseEntity <|-- Adresse
|
||||||
|
BaseEntity <|-- Membre
|
||||||
|
BaseEntity <|-- Role
|
||||||
|
BaseEntity <|-- Permission
|
||||||
|
BaseEntity <|-- MembreRole
|
||||||
|
BaseEntity <|-- RolePermission
|
||||||
|
BaseEntity <|-- Paiement
|
||||||
|
BaseEntity <|-- Cotisation
|
||||||
|
BaseEntity <|-- PaiementCotisation
|
||||||
|
BaseEntity <|-- Adhesion
|
||||||
|
BaseEntity <|-- PaiementAdhesion
|
||||||
|
BaseEntity <|-- CompteWave
|
||||||
|
BaseEntity <|-- TransactionWave
|
||||||
|
BaseEntity <|-- WebhookWave
|
||||||
|
BaseEntity <|-- ConfigurationWave
|
||||||
|
BaseEntity <|-- CompteComptable
|
||||||
|
BaseEntity <|-- JournalComptable
|
||||||
|
BaseEntity <|-- EcritureComptable
|
||||||
|
BaseEntity <|-- LigneEcriture
|
||||||
|
BaseEntity <|-- Evenement
|
||||||
|
BaseEntity <|-- InscriptionEvenement
|
||||||
|
BaseEntity <|-- PaiementEvenement
|
||||||
|
BaseEntity <|-- DemandeAide
|
||||||
|
BaseEntity <|-- PaiementAide
|
||||||
|
BaseEntity <|-- Document
|
||||||
|
BaseEntity <|-- PieceJointe
|
||||||
|
BaseEntity <|-- Notification
|
||||||
|
BaseEntity <|-- TemplateNotification
|
||||||
|
BaseEntity <|-- AuditLog
|
||||||
|
BaseEntity <|-- ParametreSysteme
|
||||||
|
|
||||||
|
Organisation "1" *-- "0..*" Membre
|
||||||
|
Organisation "0..1" --o "0..*" Organisation
|
||||||
|
Organisation "1" *-- "1" TypeOrganisation
|
||||||
|
Organisation "0..1" --o "0..*" Adresse
|
||||||
|
Organisation "1" *-- "0..*" CompteWave
|
||||||
|
|
||||||
|
Membre "1" *-- "0..*" MembreRole
|
||||||
|
MembreRole "1" *-- "1" Role
|
||||||
|
Membre "0..1" --o "0..*" Adresse
|
||||||
|
Membre "0..1" --o "0..*" CompteWave
|
||||||
|
|
||||||
|
Role "1" *-- "0..*" RolePermission
|
||||||
|
RolePermission "1" *-- "1" Permission
|
||||||
|
|
||||||
|
Paiement "1" *-- "0..*" PaiementCotisation
|
||||||
|
Paiement "1" *-- "0..*" PaiementAdhesion
|
||||||
|
Paiement "1" *-- "0..*" PaiementEvenement
|
||||||
|
Paiement "1" *-- "0..*" PaiementAide
|
||||||
|
Paiement "0..1" --o "1" TransactionWave
|
||||||
|
Paiement "1" *-- "1" Membre
|
||||||
|
|
||||||
|
Cotisation "1" *-- "0..*" PaiementCotisation
|
||||||
|
PaiementCotisation "1" *-- "1" Paiement
|
||||||
|
Cotisation "1" *-- "1" Membre
|
||||||
|
Cotisation "1" *-- "1" Organisation
|
||||||
|
|
||||||
|
Adhesion "1" *-- "0..*" PaiementAdhesion
|
||||||
|
PaiementAdhesion "1" *-- "1" Paiement
|
||||||
|
Adhesion "1" *-- "1" Membre
|
||||||
|
Adhesion "1" *-- "1" Organisation
|
||||||
|
|
||||||
|
CompteWave "1" *-- "0..*" TransactionWave
|
||||||
|
TransactionWave "1" *-- "0..*" WebhookWave
|
||||||
|
WebhookWave "0..1" --o "1" TransactionWave
|
||||||
|
WebhookWave "0..1" --o "1" Paiement
|
||||||
|
CompteWave "1" *-- "1" Organisation
|
||||||
|
CompteWave "0..1" --o "1" Membre
|
||||||
|
|
||||||
|
JournalComptable "1" *-- "0..*" EcritureComptable
|
||||||
|
EcritureComptable "1" *-- "1..*" LigneEcriture
|
||||||
|
LigneEcriture "1" *-- "1" CompteComptable
|
||||||
|
EcritureComptable "0..1" --o "1" Paiement
|
||||||
|
EcritureComptable "1" *-- "1" Organisation
|
||||||
|
|
||||||
|
Evenement "1" *-- "0..*" InscriptionEvenement
|
||||||
|
InscriptionEvenement "1" *-- "1" Membre
|
||||||
|
InscriptionEvenement "1" *-- "1" Evenement
|
||||||
|
Evenement "1" *-- "1" Organisation
|
||||||
|
Evenement "0..1" --o "1" Adresse
|
||||||
|
Evenement "0..1" --o "0..*" PaiementEvenement
|
||||||
|
PaiementEvenement "1" *-- "1" Paiement
|
||||||
|
PaiementEvenement "1" *-- "1" InscriptionEvenement
|
||||||
|
|
||||||
|
DemandeAide "1" *-- "0..*" PaiementAide
|
||||||
|
PaiementAide "1" *-- "1" Paiement
|
||||||
|
DemandeAide "1" *-- "1" Membre
|
||||||
|
DemandeAide "0..1" --o "1" Membre
|
||||||
|
DemandeAide "1" *-- "1" Organisation
|
||||||
|
|
||||||
|
PieceJointe "1" *-- "1" Document
|
||||||
|
Document "0..1" --o "0..*" PieceJointe
|
||||||
|
PieceJointe "0..1" --o "1" Membre
|
||||||
|
PieceJointe "0..1" --o "1" Organisation
|
||||||
|
PieceJointe "0..1" --o "1" Cotisation
|
||||||
|
PieceJointe "0..1" --o "1" Adhesion
|
||||||
|
PieceJointe "0..1" --o "1" DemandeAide
|
||||||
|
PieceJointe "0..1" --o "1" TransactionWave
|
||||||
|
|
||||||
|
Notification "0..1" --o "1" TemplateNotification
|
||||||
|
Notification "1" *-- "1" Membre
|
||||||
|
Notification "0..1" --o "1" Organisation
|
||||||
|
|
||||||
|
AuditLog "0..1" --o "1" Membre
|
||||||
|
AuditLog "0..1" --o "1" Organisation
|
||||||
|
|
||||||
|
@enduml
|
||||||
@@ -42,8 +42,6 @@ public class MembreInscriptionBean implements Serializable {
|
|||||||
|
|
||||||
// Propriétés système
|
// Propriétés système
|
||||||
private String numeroGenere;
|
private String numeroGenere;
|
||||||
private String membreIdString; // ID du membre en mode modification
|
|
||||||
private boolean modeModification = false;
|
|
||||||
|
|
||||||
// Informations personnelles
|
// Informations personnelles
|
||||||
private String prenom;
|
private String prenom;
|
||||||
@@ -105,10 +103,8 @@ public class MembreInscriptionBean implements Serializable {
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// Générer un numéro de membre automatiquement (seulement en mode création)
|
// Générer un numéro de membre automatiquement
|
||||||
if (!modeModification) {
|
|
||||||
this.numeroGenere = "M" + System.currentTimeMillis();
|
this.numeroGenere = "M" + System.currentTimeMillis();
|
||||||
}
|
|
||||||
|
|
||||||
// Charger les organisations actives
|
// Charger les organisations actives
|
||||||
try {
|
try {
|
||||||
@@ -120,61 +116,6 @@ public class MembreInscriptionBean implements Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters/Setters pour mode modification
|
|
||||||
public String getMembreIdString() {
|
|
||||||
return membreIdString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMembreIdString(String membreIdString) {
|
|
||||||
this.membreIdString = membreIdString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isModeModification() {
|
|
||||||
return modeModification;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Méthode appelée par f:viewAction pour charger le membre en mode modification
|
|
||||||
public void chargerMembreSiModification() {
|
|
||||||
if (membreIdString != null && !membreIdString.isEmpty()) {
|
|
||||||
try {
|
|
||||||
java.util.UUID id = java.util.UUID.fromString(membreIdString);
|
|
||||||
MembreDTO membre = membreService.obtenirParId(id);
|
|
||||||
|
|
||||||
if (membre != null) {
|
|
||||||
modeModification = true;
|
|
||||||
// Remplir tous les champs avec les données du membre
|
|
||||||
this.numeroGenere = membre.getNumeroMembre();
|
|
||||||
this.prenom = membre.getPrenom();
|
|
||||||
this.nom = membre.getNom();
|
|
||||||
this.email = membre.getEmail();
|
|
||||||
this.telephone = membre.getTelephone();
|
|
||||||
this.dateNaissance = membre.getDateNaissance();
|
|
||||||
this.adresse = membre.getAdresse();
|
|
||||||
this.ville = membre.getVille();
|
|
||||||
this.nationalite = membre.getNationalite();
|
|
||||||
this.profession = membre.getProfession();
|
|
||||||
this.situationMatrimoniale = membre.getStatutMatrimonial();
|
|
||||||
this.organisationId = membre.getAssociationId() != null ? membre.getAssociationId().toString() : null;
|
|
||||||
// TODO: Charger les autres champs si disponibles
|
|
||||||
|
|
||||||
LOGGER.info("Membre chargé pour modification: " + membre.getNomComplet());
|
|
||||||
} else {
|
|
||||||
FacesContext.getCurrentInstance().addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", "Membre introuvable"));
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.severe("ID invalide: " + membreIdString);
|
|
||||||
FacesContext.getCurrentInstance().addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", "Identifiant de membre invalide"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.severe("Erreur lors du chargement du membre: " + e.getMessage());
|
|
||||||
FacesContext.getCurrentInstance().addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur",
|
|
||||||
"Impossible de charger le membre: " + e.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
public String inscrire() {
|
public String inscrire() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -196,15 +196,15 @@ public class MembreListeBean implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Propriétés pour la page de modification
|
// Propriétés pour la page de modification
|
||||||
private String membreSelectionneIdString;
|
private UUID membreSelectionneId;
|
||||||
private MembreDTO membreSelectionne;
|
private MembreDTO membreSelectionne;
|
||||||
|
|
||||||
public String getMembreSelectionneIdString() {
|
public UUID getMembreSelectionneId() {
|
||||||
return membreSelectionneIdString;
|
return membreSelectionneId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMembreSelectionneIdString(String membreSelectionneIdString) {
|
public void setMembreSelectionneId(UUID membreSelectionneId) {
|
||||||
this.membreSelectionneIdString = membreSelectionneIdString;
|
this.membreSelectionneId = membreSelectionneId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MembreDTO getMembreSelectionne() {
|
public MembreDTO getMembreSelectionne() {
|
||||||
@@ -216,16 +216,10 @@ public class MembreListeBean implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void chargerMembreSelectionne() {
|
public void chargerMembreSelectionne() {
|
||||||
if (membreSelectionneIdString != null && !membreSelectionneIdString.isEmpty()) {
|
if (membreSelectionneId != null) {
|
||||||
try {
|
try {
|
||||||
UUID id = UUID.fromString(membreSelectionneIdString);
|
membreSelectionne = membreService.obtenirParId(membreSelectionneId);
|
||||||
membreSelectionne = membreService.obtenirParId(id);
|
|
||||||
LOGGER.info("Membre chargé pour modification: " + membreSelectionne.getNomComplet());
|
LOGGER.info("Membre chargé pour modification: " + membreSelectionne.getNomComplet());
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.severe("ID invalide: " + membreSelectionneIdString);
|
|
||||||
FacesContext.getCurrentInstance().addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur",
|
|
||||||
"Identifiant de membre invalide"));
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.severe("Erreur lors du chargement du membre: " + e.getMessage());
|
LOGGER.severe("Erreur lors du chargement du membre: " + e.getMessage());
|
||||||
FacesContext.getCurrentInstance().addMessage(null,
|
FacesContext.getCurrentInstance().addMessage(null,
|
||||||
|
|||||||
@@ -6,24 +6,18 @@
|
|||||||
xmlns:p="http://primefaces.org/ui"
|
xmlns:p="http://primefaces.org/ui"
|
||||||
template="/templates/main-template.xhtml">
|
template="/templates/main-template.xhtml">
|
||||||
|
|
||||||
<ui:define name="title">#{empty param.id ? 'Inscription' : 'Modification'} Membre - UnionFlow</ui:define>
|
<ui:define name="title">Inscription Membre - UnionFlow</ui:define>
|
||||||
|
|
||||||
<!-- Charger le membre si ID fourni (mode modification) -->
|
|
||||||
<f:metadata>
|
|
||||||
<f:viewParam name="id" value="#{membreInscriptionBean.membreIdString}" />
|
|
||||||
<f:viewAction action="#{membreInscriptionBean.chargerMembreSiModification}" />
|
|
||||||
</f:metadata>
|
|
||||||
|
|
||||||
<ui:define name="content">
|
<ui:define name="content">
|
||||||
<!-- En-tête -->
|
<!-- En-tête -->
|
||||||
<ui:include src="/templates/components/layout/page-header.xhtml">
|
<ui:include src="/templates/components/layout/page-header.xhtml">
|
||||||
<ui:param name="icon" value="#{empty param.id ? 'pi pi-user-plus text-primary' : 'pi pi-pencil text-warning'}" />
|
<ui:param name="icon" value="pi pi-user-plus text-primary" />
|
||||||
<ui:param name="title" value="#{empty param.id ? 'Inscription Nouveau Membre' : 'Modifier le Membre'}" />
|
<ui:param name="title" value="Inscription Nouveau Membre" />
|
||||||
<ui:param name="description" value="#{empty param.id ? 'Formulaire complet d\'inscription avec photo et documents' : 'Modifiez les informations du membre'}" />
|
<ui:param name="description" value="Formulaire complet d'inscription avec photo et documents" />
|
||||||
<ui:define name="actions">
|
<ui:define name="actions">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-900 font-medium">Numéro: #{membreInscriptionBean.numeroGenere}</div>
|
<div class="text-900 font-medium">Numéro: #{membreInscriptionBean.numeroGenere}</div>
|
||||||
<small class="text-600">#{empty param.id ? 'Généré automatiquement' : 'Existant'}</small>
|
<small class="text-600">Généré automatiquement</small>
|
||||||
</div>
|
</div>
|
||||||
</ui:define>
|
</ui:define>
|
||||||
</ui:include>
|
</ui:include>
|
||||||
@@ -100,7 +94,7 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="dateNaissance" value="Date de naissance" />
|
<p:outputLabel for="dateNaissance" value="Date de naissance" />
|
||||||
<p:calendar id="dateNaissance" value="#{membreInscriptionBean.dateNaissance}"
|
<p:calendar id="dateNaissance" value="#{membreInscriptionBean.dateNaissance}" required="true"
|
||||||
pattern="dd/MM/yyyy" showIcon="true" yearNavigator="true" yearRange="1920:2030"
|
pattern="dd/MM/yyyy" showIcon="true" yearNavigator="true" yearRange="1920:2030"
|
||||||
monthNavigator="true" requiredMessage="La date de naissance est obligatoire"
|
monthNavigator="true" requiredMessage="La date de naissance est obligatoire"
|
||||||
styleClass="w-full" />
|
styleClass="w-full" />
|
||||||
@@ -113,7 +107,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="sexe" value="Sexe" />
|
<p:outputLabel for="sexe" value="Sexe" />
|
||||||
<p:selectOneMenu id="sexe" value="#{membreInscriptionBean.sexe}" styleClass="w-full">
|
<p:selectOneMenu id="sexe" value="#{membreInscriptionBean.sexe}" required="true" styleClass="w-full">
|
||||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true" />
|
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true" />
|
||||||
<f:selectItems value="#{membreInscriptionBean.sexeOptions}" />
|
<f:selectItems value="#{membreInscriptionBean.sexeOptions}" />
|
||||||
</p:selectOneMenu>
|
</p:selectOneMenu>
|
||||||
@@ -148,6 +142,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="contactUrgenceNom" value="Nom complet" />
|
<p:outputLabel for="contactUrgenceNom" value="Nom complet" />
|
||||||
<p:inputText id="contactUrgenceNom" value="#{membreInscriptionBean.contactUrgenceNom}"
|
<p:inputText id="contactUrgenceNom" value="#{membreInscriptionBean.contactUrgenceNom}"
|
||||||
|
required="true" requiredMessage="Le nom du contact d'urgence est obligatoire"
|
||||||
styleClass="w-full" />
|
styleClass="w-full" />
|
||||||
<p:message for="contactUrgenceNom" />
|
<p:message for="contactUrgenceNom" />
|
||||||
</div>
|
</div>
|
||||||
@@ -155,12 +150,13 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="contactUrgenceTelephone" value="Téléphone" />
|
<p:outputLabel for="contactUrgenceTelephone" value="Téléphone" />
|
||||||
<p:inputText id="contactUrgenceTelephone" value="#{membreInscriptionBean.contactUrgenceTelephone}"
|
<p:inputText id="contactUrgenceTelephone" value="#{membreInscriptionBean.contactUrgenceTelephone}"
|
||||||
|
required="true" requiredMessage="Le téléphone du contact d'urgence est obligatoire"
|
||||||
styleClass="w-full" />
|
styleClass="w-full" />
|
||||||
<p:message for="contactUrgenceTelephone" />
|
<p:message for="contactUrgenceTelephone" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="contactUrgenceLien" value="Lien de parenté" />
|
<p:outputLabel for="contactUrgenceLien" value="Lien de parenté" />
|
||||||
<p:selectOneMenu id="contactUrgenceLien" value="#{membreInscriptionBean.contactUrgenceLien}" styleClass="w-full">
|
<p:selectOneMenu id="contactUrgenceLien" value="#{membreInscriptionBean.contactUrgenceLien}" required="true" styleClass="w-full">
|
||||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true" />
|
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true" />
|
||||||
<f:selectItems value="#{membreInscriptionBean.contactUrgenceLienOptions}" />
|
<f:selectItems value="#{membreInscriptionBean.contactUrgenceLienOptions}" />
|
||||||
</p:selectOneMenu>
|
</p:selectOneMenu>
|
||||||
@@ -221,6 +217,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="adresse" value="Adresse complète" />
|
<p:outputLabel for="adresse" value="Adresse complète" />
|
||||||
<p:inputTextarea id="adresse" value="#{membreInscriptionBean.adresse}" rows="4"
|
<p:inputTextarea id="adresse" value="#{membreInscriptionBean.adresse}" rows="4"
|
||||||
|
required="true" requiredMessage="L'adresse est obligatoire"
|
||||||
styleClass="w-full" />
|
styleClass="w-full" />
|
||||||
<p:message for="adresse" />
|
<p:message for="adresse" />
|
||||||
</div>
|
</div>
|
||||||
@@ -228,7 +225,8 @@
|
|||||||
<div class="formgrid grid">
|
<div class="formgrid grid">
|
||||||
<div class="field col">
|
<div class="field col">
|
||||||
<p:outputLabel for="ville" value="Ville" />
|
<p:outputLabel for="ville" value="Ville" />
|
||||||
<p:inputText id="ville" value="#{membreInscriptionBean.ville}" styleClass="w-full" />
|
<p:inputText id="ville" value="#{membreInscriptionBean.ville}" required="true"
|
||||||
|
requiredMessage="La ville est obligatoire" styleClass="w-full" />
|
||||||
<p:message for="ville" />
|
<p:message for="ville" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field col">
|
<div class="field col">
|
||||||
@@ -255,6 +253,7 @@
|
|||||||
<div class="field col">
|
<div class="field col">
|
||||||
<p:outputLabel for="telephoneMobile" value="Téléphone mobile" />
|
<p:outputLabel for="telephoneMobile" value="Téléphone mobile" />
|
||||||
<p:inputText id="telephoneMobile" value="#{membreInscriptionBean.telephoneMobile}"
|
<p:inputText id="telephoneMobile" value="#{membreInscriptionBean.telephoneMobile}"
|
||||||
|
required="true" requiredMessage="Le téléphone mobile est obligatoire"
|
||||||
styleClass="w-full" />
|
styleClass="w-full" />
|
||||||
<p:message for="telephoneMobile" />
|
<p:message for="telephoneMobile" />
|
||||||
</div>
|
</div>
|
||||||
@@ -279,7 +278,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<p:outputLabel for="typeAdhesion" value="Type d'adhésion" />
|
<p:outputLabel for="typeAdhesion" value="Type d'adhésion" />
|
||||||
<p:selectOneMenu id="typeAdhesion" value="#{membreInscriptionBean.typeAdhesion}" styleClass="w-full">
|
<p:selectOneMenu id="typeAdhesion" value="#{membreInscriptionBean.typeAdhesion}" required="true" styleClass="w-full">
|
||||||
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true" />
|
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true" />
|
||||||
<f:selectItems value="#{membreInscriptionBean.typeAdhesionOptions}" />
|
<f:selectItems value="#{membreInscriptionBean.typeAdhesionOptions}" />
|
||||||
</p:selectOneMenu>
|
</p:selectOneMenu>
|
||||||
|
|||||||
@@ -197,7 +197,7 @@
|
|||||||
iconOnly="true"/>
|
iconOnly="true"/>
|
||||||
<!-- DRY/WOU: Composite Component action-button-edit-nav -->
|
<!-- DRY/WOU: Composite Component action-button-edit-nav -->
|
||||||
<uf:action-button-edit-nav itemId="#{membre.id}"
|
<uf:action-button-edit-nav itemId="#{membre.id}"
|
||||||
editPage="/pages/secure/membre/inscription.xhtml"
|
editPage="/pages/secure/membre/modifier.xhtml"
|
||||||
iconOnly="true"/>
|
iconOnly="true"/>
|
||||||
<ui:include src="/templates/components/buttons/button-icon.xhtml">
|
<ui:include src="/templates/components/buttons/button-icon.xhtml">
|
||||||
<ui:param name="icon" value="pi pi-dollar" />
|
<ui:param name="icon" value="pi pi-dollar" />
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package dev.lions.unionflow.server.api.dto.adresse;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||||
|
import dev.lions.unionflow.server.api.enums.adresse.TypeAdresse;
|
||||||
|
import jakarta.validation.constraints.DecimalMax;
|
||||||
|
import jakarta.validation.constraints.DecimalMin;
|
||||||
|
import jakarta.validation.constraints.Digits;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO pour la gestion des adresses
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class AdresseDTO extends BaseDTO {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** Type d'adresse */
|
||||||
|
@NotNull(message = "Le type d'adresse est obligatoire")
|
||||||
|
private TypeAdresse typeAdresse;
|
||||||
|
|
||||||
|
/** Adresse complète */
|
||||||
|
private String adresse;
|
||||||
|
|
||||||
|
/** Complément d'adresse */
|
||||||
|
private String complementAdresse;
|
||||||
|
|
||||||
|
/** Code postal */
|
||||||
|
private String codePostal;
|
||||||
|
|
||||||
|
/** Ville */
|
||||||
|
private String ville;
|
||||||
|
|
||||||
|
/** Région */
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
/** Pays */
|
||||||
|
private String pays;
|
||||||
|
|
||||||
|
/** Latitude */
|
||||||
|
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||||
|
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||||
|
@Digits(integer = 3, fraction = 6)
|
||||||
|
private BigDecimal latitude;
|
||||||
|
|
||||||
|
/** Longitude */
|
||||||
|
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||||
|
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||||
|
@Digits(integer = 3, fraction = 6)
|
||||||
|
private BigDecimal longitude;
|
||||||
|
|
||||||
|
/** Adresse principale */
|
||||||
|
private Boolean principale;
|
||||||
|
|
||||||
|
/** Libellé personnalisé */
|
||||||
|
private String libelle;
|
||||||
|
|
||||||
|
/** Notes et commentaires */
|
||||||
|
private String notes;
|
||||||
|
|
||||||
|
/** ID de l'organisation (si adresse d'organisation) */
|
||||||
|
private UUID organisationId;
|
||||||
|
|
||||||
|
/** ID du membre (si adresse de membre) */
|
||||||
|
private UUID membreId;
|
||||||
|
|
||||||
|
/** ID de l'événement (si adresse d'événement) */
|
||||||
|
private UUID evenementId;
|
||||||
|
|
||||||
|
/** Adresse complète formatée (calculée) */
|
||||||
|
private String adresseComplete;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package dev.lions.unionflow.server.api.enums.adresse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Énumération des types d'adresse dans UnionFlow
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
public enum TypeAdresse {
|
||||||
|
SIEGE_SOCIAL("Siège Social"),
|
||||||
|
BUREAU("Bureau"),
|
||||||
|
DOMICILE("Domicile"),
|
||||||
|
AUTRE("Autre");
|
||||||
|
|
||||||
|
private final String libelle;
|
||||||
|
|
||||||
|
TypeAdresse(String libelle) {
|
||||||
|
this.libelle = libelle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLibelle() {
|
||||||
|
return libelle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.enums.adresse.TypeAdresse;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entité Adresse pour la gestion des adresses des organisations, membres et événements
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "adresses",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_adresse_ville", columnList = "ville"),
|
||||||
|
@Index(name = "idx_adresse_pays", columnList = "pays"),
|
||||||
|
@Index(name = "idx_adresse_type", columnList = "type_adresse"),
|
||||||
|
@Index(name = "idx_adresse_organisation", columnList = "organisation_id"),
|
||||||
|
@Index(name = "idx_adresse_membre", columnList = "membre_id"),
|
||||||
|
@Index(name = "idx_adresse_evenement", columnList = "evenement_id")
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Adresse extends BaseEntity {
|
||||||
|
|
||||||
|
/** Type d'adresse */
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "type_adresse", nullable = false, length = 50)
|
||||||
|
private dev.lions.unionflow.server.api.enums.adresse.TypeAdresse typeAdresse;
|
||||||
|
|
||||||
|
/** Adresse complète */
|
||||||
|
@Column(name = "adresse", length = 500)
|
||||||
|
private String adresse;
|
||||||
|
|
||||||
|
/** Complément d'adresse */
|
||||||
|
@Column(name = "complement_adresse", length = 200)
|
||||||
|
private String complementAdresse;
|
||||||
|
|
||||||
|
/** Code postal */
|
||||||
|
@Column(name = "code_postal", length = 20)
|
||||||
|
private String codePostal;
|
||||||
|
|
||||||
|
/** Ville */
|
||||||
|
@Column(name = "ville", length = 100)
|
||||||
|
private String ville;
|
||||||
|
|
||||||
|
/** Région */
|
||||||
|
@Column(name = "region", length = 100)
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
/** Pays */
|
||||||
|
@Column(name = "pays", length = 100)
|
||||||
|
private String pays;
|
||||||
|
|
||||||
|
/** Coordonnées géographiques - Latitude */
|
||||||
|
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||||
|
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||||
|
@Digits(integer = 3, fraction = 6)
|
||||||
|
@Column(name = "latitude", precision = 9, scale = 6)
|
||||||
|
private BigDecimal latitude;
|
||||||
|
|
||||||
|
/** Coordonnées géographiques - Longitude */
|
||||||
|
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||||
|
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||||
|
@Digits(integer = 3, fraction = 6)
|
||||||
|
@Column(name = "longitude", precision = 9, scale = 6)
|
||||||
|
private BigDecimal longitude;
|
||||||
|
|
||||||
|
/** Adresse principale (une seule par entité) */
|
||||||
|
@Builder.Default
|
||||||
|
@Column(name = "principale", nullable = false)
|
||||||
|
private Boolean principale = false;
|
||||||
|
|
||||||
|
/** Libellé personnalisé */
|
||||||
|
@Column(name = "libelle", length = 100)
|
||||||
|
private String libelle;
|
||||||
|
|
||||||
|
/** Notes et commentaires */
|
||||||
|
@Column(name = "notes", length = 500)
|
||||||
|
private String notes;
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "organisation_id")
|
||||||
|
private Organisation organisation;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "membre_id")
|
||||||
|
private Membre membre;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "evenement_id")
|
||||||
|
private Evenement evenement;
|
||||||
|
|
||||||
|
/** Méthode métier pour obtenir l'adresse complète formatée */
|
||||||
|
public String getAdresseComplete() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (adresse != null && !adresse.isEmpty()) {
|
||||||
|
sb.append(adresse);
|
||||||
|
}
|
||||||
|
if (complementAdresse != null && !complementAdresse.isEmpty()) {
|
||||||
|
if (sb.length() > 0) sb.append(", ");
|
||||||
|
sb.append(complementAdresse);
|
||||||
|
}
|
||||||
|
if (codePostal != null && !codePostal.isEmpty()) {
|
||||||
|
if (sb.length() > 0) sb.append(", ");
|
||||||
|
sb.append(codePostal);
|
||||||
|
}
|
||||||
|
if (ville != null && !ville.isEmpty()) {
|
||||||
|
if (sb.length() > 0) sb.append(" ");
|
||||||
|
sb.append(ville);
|
||||||
|
}
|
||||||
|
if (region != null && !region.isEmpty()) {
|
||||||
|
if (sb.length() > 0) sb.append(", ");
|
||||||
|
sb.append(region);
|
||||||
|
}
|
||||||
|
if (pays != null && !pays.isEmpty()) {
|
||||||
|
if (sb.length() > 0) sb.append(", ");
|
||||||
|
sb.append(pays);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier si l'adresse a des coordonnées GPS */
|
||||||
|
public boolean hasCoordinates() {
|
||||||
|
return latitude != null && longitude != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback JPA avant la persistance */
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
super.onCreate(); // Appelle le onCreate de BaseEntity
|
||||||
|
if (typeAdresse == null) {
|
||||||
|
typeAdresse = dev.lions.unionflow.server.api.enums.adresse.TypeAdresse.AUTRE;
|
||||||
|
}
|
||||||
|
if (principale == null) {
|
||||||
|
principale = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -118,6 +118,10 @@ public class Evenement extends BaseEntity {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private List<InscriptionEvenement> inscriptions = new ArrayList<>();
|
private List<InscriptionEvenement> inscriptions = new ArrayList<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "evenement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<Adresse> adresses = new ArrayList<>();
|
||||||
|
|
||||||
/** Types d'événements */
|
/** Types d'événements */
|
||||||
public enum TypeEvenement {
|
public enum TypeEvenement {
|
||||||
ASSEMBLEE_GENERALE("Assemblée Générale"),
|
ASSEMBLEE_GENERALE("Assemblée Générale"),
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import jakarta.validation.constraints.Email;
|
|||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -67,6 +69,14 @@ public class Membre extends BaseEntity {
|
|||||||
@JoinColumn(name = "organisation_id")
|
@JoinColumn(name = "organisation_id")
|
||||||
private Organisation organisation;
|
private Organisation organisation;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<Adresse> adresses = new ArrayList<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<MembreRole> roles = new ArrayList<>();
|
||||||
|
|
||||||
/** Méthode métier pour obtenir le nom complet */
|
/** Méthode métier pour obtenir le nom complet */
|
||||||
public String getNomComplet() {
|
public String getNomComplet() {
|
||||||
return prenom + " " + nom;
|
return prenom + " " + nom;
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table de liaison entre Membre et Role
|
||||||
|
* Permet à un membre d'avoir plusieurs rôles avec dates de début/fin
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "membres_roles",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_membre_role_membre", columnList = "membre_id"),
|
||||||
|
@Index(name = "idx_membre_role_role", columnList = "role_id"),
|
||||||
|
@Index(name = "idx_membre_role_actif", columnList = "actif")
|
||||||
|
},
|
||||||
|
uniqueConstraints = {
|
||||||
|
@UniqueConstraint(
|
||||||
|
name = "uk_membre_role",
|
||||||
|
columnNames = {"membre_id", "role_id"})
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class MembreRole extends BaseEntity {
|
||||||
|
|
||||||
|
/** Membre */
|
||||||
|
@NotNull
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "membre_id", nullable = false)
|
||||||
|
private Membre membre;
|
||||||
|
|
||||||
|
/** Rôle */
|
||||||
|
@NotNull
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "role_id", nullable = false)
|
||||||
|
private Role role;
|
||||||
|
|
||||||
|
/** Date de début d'attribution */
|
||||||
|
@Column(name = "date_debut")
|
||||||
|
private LocalDate dateDebut;
|
||||||
|
|
||||||
|
/** Date de fin d'attribution (null = permanent) */
|
||||||
|
@Column(name = "date_fin")
|
||||||
|
private LocalDate dateFin;
|
||||||
|
|
||||||
|
/** Commentaire sur l'attribution */
|
||||||
|
@Column(name = "commentaire", length = 500)
|
||||||
|
private String commentaire;
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier si l'attribution est active */
|
||||||
|
public boolean isActif() {
|
||||||
|
if (!Boolean.TRUE.equals(getActif())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LocalDate aujourdhui = LocalDate.now();
|
||||||
|
if (dateDebut != null && aujourdhui.isBefore(dateDebut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dateFin != null && aujourdhui.isAfter(dateFin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback JPA avant la persistance */
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
if (dateDebut == null) {
|
||||||
|
dateDebut = LocalDate.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -191,6 +191,10 @@ public class Organisation extends BaseEntity {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private List<Membre> membres = new ArrayList<>();
|
private List<Membre> membres = new ArrayList<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<Adresse> adresses = new ArrayList<>();
|
||||||
|
|
||||||
/** Méthode métier pour obtenir le nom complet avec sigle */
|
/** Méthode métier pour obtenir le nom complet avec sigle */
|
||||||
public String getNomComplet() {
|
public String getNomComplet() {
|
||||||
if (nomCourt != null && !nomCourt.isEmpty()) {
|
if (nomCourt != null && !nomCourt.isEmpty()) {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entité Permission pour la gestion des permissions granulaires
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "permissions",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_permission_code", columnList = "code", unique = true),
|
||||||
|
@Index(name = "idx_permission_module", columnList = "module"),
|
||||||
|
@Index(name = "idx_permission_ressource", columnList = "ressource")
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Permission extends BaseEntity {
|
||||||
|
|
||||||
|
/** Code unique de la permission (format: MODULE > RESSOURCE > ACTION) */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "code", unique = true, nullable = false, length = 100)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/** Module (ex: ORGANISATION, MEMBRE, COTISATION) */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "module", nullable = false, length = 50)
|
||||||
|
private String module;
|
||||||
|
|
||||||
|
/** Ressource (ex: MEMBRE, COTISATION, ADHESION) */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "ressource", nullable = false, length = 50)
|
||||||
|
private String ressource;
|
||||||
|
|
||||||
|
/** Action (ex: CREATE, READ, UPDATE, DELETE, VALIDATE) */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "action", nullable = false, length = 50)
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
/** Libellé de la permission */
|
||||||
|
@Column(name = "libelle", length = 200)
|
||||||
|
private String libelle;
|
||||||
|
|
||||||
|
/** Description de la permission */
|
||||||
|
@Column(name = "description", length = 500)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/** Rôles associés */
|
||||||
|
@OneToMany(mappedBy = "permission", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<RolePermission> roles = new ArrayList<>();
|
||||||
|
|
||||||
|
/** Méthode métier pour générer le code à partir des composants */
|
||||||
|
public static String genererCode(String module, String ressource, String action) {
|
||||||
|
return String.format("%s > %s > %s", module.toUpperCase(), ressource.toUpperCase(), action.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier si le code est valide */
|
||||||
|
public boolean isCodeValide() {
|
||||||
|
return code != null && code.contains(" > ") && code.split(" > ").length == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback JPA avant la persistance */
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
// Générer le code si non fourni
|
||||||
|
if (code == null || code.isEmpty()) {
|
||||||
|
if (module != null && ressource != null && action != null) {
|
||||||
|
code = genererCode(module, ressource, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entité Role pour la gestion des rôles dans le système
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "roles",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_role_code", columnList = "code", unique = true),
|
||||||
|
@Index(name = "idx_role_actif", columnList = "actif"),
|
||||||
|
@Index(name = "idx_role_niveau", columnList = "niveau_hierarchique")
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Role extends BaseEntity {
|
||||||
|
|
||||||
|
/** Code unique du rôle */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "code", unique = true, nullable = false, length = 50)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/** Libellé du rôle */
|
||||||
|
@NotBlank
|
||||||
|
@Column(name = "libelle", nullable = false, length = 100)
|
||||||
|
private String libelle;
|
||||||
|
|
||||||
|
/** Description du rôle */
|
||||||
|
@Column(name = "description", length = 500)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/** Niveau hiérarchique (plus bas = plus prioritaire) */
|
||||||
|
@NotNull
|
||||||
|
@Builder.Default
|
||||||
|
@Column(name = "niveau_hierarchique", nullable = false)
|
||||||
|
private Integer niveauHierarchique = 100;
|
||||||
|
|
||||||
|
/** Type de rôle */
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "type_role", nullable = false, length = 50)
|
||||||
|
private TypeRole typeRole;
|
||||||
|
|
||||||
|
/** Organisation propriétaire (null pour rôles système) */
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "organisation_id")
|
||||||
|
private Organisation organisation;
|
||||||
|
|
||||||
|
/** Permissions associées */
|
||||||
|
@OneToMany(mappedBy = "role", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||||
|
@Builder.Default
|
||||||
|
private List<RolePermission> permissions = new ArrayList<>();
|
||||||
|
|
||||||
|
/** Énumération des types de rôle */
|
||||||
|
public enum TypeRole {
|
||||||
|
SYSTEME("Rôle Système"),
|
||||||
|
ORGANISATION("Rôle Organisation"),
|
||||||
|
PERSONNALISE("Rôle Personnalisé");
|
||||||
|
|
||||||
|
private final String libelle;
|
||||||
|
|
||||||
|
TypeRole(String libelle) {
|
||||||
|
this.libelle = libelle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLibelle() {
|
||||||
|
return libelle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Méthode métier pour vérifier si c'est un rôle système */
|
||||||
|
public boolean isRoleSysteme() {
|
||||||
|
return TypeRole.SYSTEME.equals(typeRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback JPA avant la persistance */
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
if (typeRole == null) {
|
||||||
|
typeRole = TypeRole.PERSONNALISE;
|
||||||
|
}
|
||||||
|
if (niveauHierarchique == null) {
|
||||||
|
niveauHierarchique = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package dev.lions.unionflow.server.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table de liaison entre Role et Permission
|
||||||
|
* Permet à un rôle d'avoir plusieurs permissions
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(
|
||||||
|
name = "roles_permissions",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_role_permission_role", columnList = "role_id"),
|
||||||
|
@Index(name = "idx_role_permission_permission", columnList = "permission_id")
|
||||||
|
},
|
||||||
|
uniqueConstraints = {
|
||||||
|
@UniqueConstraint(
|
||||||
|
name = "uk_role_permission",
|
||||||
|
columnNames = {"role_id", "permission_id"})
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class RolePermission extends BaseEntity {
|
||||||
|
|
||||||
|
/** Rôle */
|
||||||
|
@NotNull
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "role_id", nullable = false)
|
||||||
|
private Role role;
|
||||||
|
|
||||||
|
/** Permission */
|
||||||
|
@NotNull
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "permission_id", nullable = false)
|
||||||
|
private Permission permission;
|
||||||
|
|
||||||
|
/** Commentaire sur l'association */
|
||||||
|
@Column(name = "commentaire", length = 500)
|
||||||
|
private String commentaire;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package dev.lions.unionflow.server.repository;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.Adresse;
|
||||||
|
import dev.lions.unionflow.server.entity.Adresse.TypeAdresse;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository pour l'entité Adresse
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AdresseRepository implements PanacheRepository<Adresse> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve toutes les adresses d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<Adresse> findByOrganisationId(UUID organisationId) {
|
||||||
|
return find("organisation.id", organisationId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve l'adresse principale d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return Adresse principale ou Optional.empty()
|
||||||
|
*/
|
||||||
|
public Optional<Adresse> findPrincipaleByOrganisationId(UUID organisationId) {
|
||||||
|
return find("organisation.id = ?1 AND principale = true", organisationId).firstResultOptional();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve toutes les adresses d'un membre
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<Adresse> findByMembreId(UUID membreId) {
|
||||||
|
return find("membre.id", membreId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve l'adresse principale d'un membre
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return Adresse principale ou Optional.empty()
|
||||||
|
*/
|
||||||
|
public Optional<Adresse> findPrincipaleByMembreId(UUID membreId) {
|
||||||
|
return find("membre.id = ?1 AND principale = true", membreId).firstResultOptional();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve l'adresse d'un événement
|
||||||
|
*
|
||||||
|
* @param evenementId ID de l'événement
|
||||||
|
* @return Adresse ou Optional.empty()
|
||||||
|
*/
|
||||||
|
public Optional<Adresse> findByEvenementId(UUID evenementId) {
|
||||||
|
return find("evenement.id", evenementId).firstResultOptional();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les adresses par type
|
||||||
|
*
|
||||||
|
* @param typeAdresse Type d'adresse
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<Adresse> findByType(TypeAdresse typeAdresse) {
|
||||||
|
return find("typeAdresse", typeAdresse).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les adresses par ville
|
||||||
|
*
|
||||||
|
* @param ville Nom de la ville
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<Adresse> findByVille(String ville) {
|
||||||
|
return find("LOWER(ville) = LOWER(?1)", ville).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les adresses par pays
|
||||||
|
*
|
||||||
|
* @param pays Nom du pays
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<Adresse> findByPays(String pays) {
|
||||||
|
return find("LOWER(pays) = LOWER(?1)", pays).list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package dev.lions.unionflow.server.repository;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.MembreRole;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository pour l'entité MembreRole
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class MembreRoleRepository implements PanacheRepository<MembreRole> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les rôles d'un membre
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return Liste des attributions de rôles
|
||||||
|
*/
|
||||||
|
public List<MembreRole> findByMembreId(UUID membreId) {
|
||||||
|
return find("membre.id = ?1 AND actif = true", membreId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les rôles actifs d'un membre (dans la période valide)
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return Liste des attributions de rôles actives
|
||||||
|
*/
|
||||||
|
public List<MembreRole> findActifsByMembreId(UUID membreId) {
|
||||||
|
LocalDate aujourdhui = LocalDate.now();
|
||||||
|
return find(
|
||||||
|
"membre.id = ?1 AND actif = true AND (dateDebut IS NULL OR dateDebut <= ?2) AND (dateFin IS NULL OR dateFin >= ?2)",
|
||||||
|
membreId,
|
||||||
|
aujourdhui)
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les membres ayant un rôle spécifique
|
||||||
|
*
|
||||||
|
* @param roleId ID du rôle
|
||||||
|
* @return Liste des attributions de rôles
|
||||||
|
*/
|
||||||
|
public List<MembreRole> findByRoleId(UUID roleId) {
|
||||||
|
return find("role.id = ?1 AND actif = true", roleId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une attribution spécifique membre-role
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @param roleId ID du rôle
|
||||||
|
* @return Attribution ou null
|
||||||
|
*/
|
||||||
|
public MembreRole findByMembreAndRole(UUID membreId, UUID roleId) {
|
||||||
|
return find("membre.id = ?1 AND role.id = ?2", membreId, roleId).firstResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package dev.lions.unionflow.server.repository;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.Permission;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository pour l'entité Permission
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class PermissionRepository implements PanacheRepository<Permission> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une permission par son code
|
||||||
|
*
|
||||||
|
* @param code Code de la permission
|
||||||
|
* @return Permission ou Optional.empty()
|
||||||
|
*/
|
||||||
|
public Optional<Permission> findByCode(String code) {
|
||||||
|
return find("code", code).firstResultOptional();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les permissions par module
|
||||||
|
*
|
||||||
|
* @param module Nom du module
|
||||||
|
* @return Liste des permissions
|
||||||
|
*/
|
||||||
|
public List<Permission> findByModule(String module) {
|
||||||
|
return find("LOWER(module) = LOWER(?1) AND actif = true", module).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les permissions par ressource
|
||||||
|
*
|
||||||
|
* @param ressource Nom de la ressource
|
||||||
|
* @return Liste des permissions
|
||||||
|
*/
|
||||||
|
public List<Permission> findByRessource(String ressource) {
|
||||||
|
return find("LOWER(ressource) = LOWER(?1) AND actif = true", ressource).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les permissions par module et ressource
|
||||||
|
*
|
||||||
|
* @param module Nom du module
|
||||||
|
* @param ressource Nom de la ressource
|
||||||
|
* @return Liste des permissions
|
||||||
|
*/
|
||||||
|
public List<Permission> findByModuleAndRessource(String module, String ressource) {
|
||||||
|
return find(
|
||||||
|
"LOWER(module) = LOWER(?1) AND LOWER(ressource) = LOWER(?2) AND actif = true",
|
||||||
|
module,
|
||||||
|
ressource)
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve toutes les permissions actives
|
||||||
|
*
|
||||||
|
* @return Liste des permissions actives
|
||||||
|
*/
|
||||||
|
public List<Permission> findAllActives() {
|
||||||
|
return find("actif = true").order("module ASC, ressource ASC, action ASC").list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package dev.lions.unionflow.server.repository;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.RolePermission;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository pour l'entité RolePermission
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class RolePermissionRepository implements PanacheRepository<RolePermission> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve toutes les permissions d'un rôle
|
||||||
|
*
|
||||||
|
* @param roleId ID du rôle
|
||||||
|
* @return Liste des associations rôle-permission
|
||||||
|
*/
|
||||||
|
public List<RolePermission> findByRoleId(UUID roleId) {
|
||||||
|
return find("role.id = ?1 AND actif = true", roleId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les rôles ayant une permission spécifique
|
||||||
|
*
|
||||||
|
* @param permissionId ID de la permission
|
||||||
|
* @return Liste des associations rôle-permission
|
||||||
|
*/
|
||||||
|
public List<RolePermission> findByPermissionId(UUID permissionId) {
|
||||||
|
return find("permission.id = ?1 AND actif = true", permissionId).list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une association spécifique rôle-permission
|
||||||
|
*
|
||||||
|
* @param roleId ID du rôle
|
||||||
|
* @param permissionId ID de la permission
|
||||||
|
* @return Association ou null
|
||||||
|
*/
|
||||||
|
public RolePermission findByRoleAndPermission(UUID roleId, UUID permissionId) {
|
||||||
|
return find("role.id = ?1 AND permission.id = ?2", roleId, permissionId).firstResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package dev.lions.unionflow.server.repository;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.Role;
|
||||||
|
import dev.lions.unionflow.server.entity.Role.TypeRole;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository pour l'entité Role
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class RoleRepository implements PanacheRepository<Role> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un rôle par son code
|
||||||
|
*
|
||||||
|
* @param code Code du rôle
|
||||||
|
* @return Rôle ou Optional.empty()
|
||||||
|
*/
|
||||||
|
public Optional<Role> findByCode(String code) {
|
||||||
|
return find("code", code).firstResultOptional();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les rôles système
|
||||||
|
*
|
||||||
|
* @return Liste des rôles système
|
||||||
|
*/
|
||||||
|
public List<Role> findRolesSysteme() {
|
||||||
|
return find("typeRole = ?1 AND actif = true", TypeRole.SYSTEME)
|
||||||
|
.order("niveauHierarchique ASC")
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les rôles d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return Liste des rôles
|
||||||
|
*/
|
||||||
|
public List<Role> findByOrganisationId(UUID organisationId) {
|
||||||
|
return find("organisation.id = ?1 AND actif = true", organisationId)
|
||||||
|
.order("niveauHierarchique ASC")
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve tous les rôles actifs
|
||||||
|
*
|
||||||
|
* @return Liste des rôles actifs
|
||||||
|
*/
|
||||||
|
public List<Role> findAllActifs() {
|
||||||
|
return find("actif = true").order("niveauHierarchique ASC").list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve les rôles par type
|
||||||
|
*
|
||||||
|
* @param typeRole Type de rôle
|
||||||
|
* @return Liste des rôles
|
||||||
|
*/
|
||||||
|
public List<Role> findByType(TypeRole typeRole) {
|
||||||
|
return find("typeRole = ?1 AND actif = true", typeRole)
|
||||||
|
.order("niveauHierarchique ASC")
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
package dev.lions.unionflow.server.service;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.api.dto.adresse.AdresseDTO;
|
||||||
|
import dev.lions.unionflow.server.api.enums.adresse.TypeAdresse;
|
||||||
|
import dev.lions.unionflow.server.entity.Adresse;
|
||||||
|
import dev.lions.unionflow.server.entity.Evenement;
|
||||||
|
import dev.lions.unionflow.server.entity.Membre;
|
||||||
|
import dev.lions.unionflow.server.entity.Organisation;
|
||||||
|
import dev.lions.unionflow.server.repository.AdresseRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.EvenementRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service métier pour la gestion des adresses
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class AdresseService {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(AdresseService.class);
|
||||||
|
|
||||||
|
@Inject AdresseRepository adresseRepository;
|
||||||
|
|
||||||
|
@Inject OrganisationRepository organisationRepository;
|
||||||
|
|
||||||
|
@Inject MembreRepository membreRepository;
|
||||||
|
|
||||||
|
@Inject EvenementRepository evenementRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une nouvelle adresse
|
||||||
|
*
|
||||||
|
* @param adresseDTO DTO de l'adresse à créer
|
||||||
|
* @return DTO de l'adresse créée
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public AdresseDTO creerAdresse(AdresseDTO adresseDTO) {
|
||||||
|
LOG.infof("Création d'une nouvelle adresse de type: %s", adresseDTO.getTypeAdresse());
|
||||||
|
|
||||||
|
Adresse adresse = convertToEntity(adresseDTO);
|
||||||
|
|
||||||
|
// Gestion de l'adresse principale
|
||||||
|
if (Boolean.TRUE.equals(adresseDTO.getPrincipale())) {
|
||||||
|
desactiverAutresPrincipales(adresseDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
adresseRepository.persist(adresse);
|
||||||
|
LOG.infof("Adresse créée avec succès: ID=%s", adresse.getId());
|
||||||
|
|
||||||
|
return convertToDTO(adresse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour une adresse existante
|
||||||
|
*
|
||||||
|
* @param id ID de l'adresse
|
||||||
|
* @param adresseDTO DTO avec les nouvelles données
|
||||||
|
* @return DTO de l'adresse mise à jour
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public AdresseDTO mettreAJourAdresse(UUID id, AdresseDTO adresseDTO) {
|
||||||
|
LOG.infof("Mise à jour de l'adresse ID: %s", id);
|
||||||
|
|
||||||
|
Adresse adresse =
|
||||||
|
adresseRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Adresse non trouvée avec l'ID: " + id));
|
||||||
|
|
||||||
|
// Mise à jour des champs
|
||||||
|
updateFromDTO(adresse, adresseDTO);
|
||||||
|
|
||||||
|
// Gestion de l'adresse principale
|
||||||
|
if (Boolean.TRUE.equals(adresseDTO.getPrincipale())) {
|
||||||
|
desactiverAutresPrincipales(adresseDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
adresseRepository.persist(adresse);
|
||||||
|
LOG.infof("Adresse mise à jour avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return convertToDTO(adresse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime une adresse
|
||||||
|
*
|
||||||
|
* @param id ID de l'adresse
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void supprimerAdresse(UUID id) {
|
||||||
|
LOG.infof("Suppression de l'adresse ID: %s", id);
|
||||||
|
|
||||||
|
Adresse adresse =
|
||||||
|
adresseRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Adresse non trouvée avec l'ID: " + id));
|
||||||
|
|
||||||
|
adresseRepository.delete(adresse);
|
||||||
|
LOG.infof("Adresse supprimée avec succès: ID=%s", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une adresse par son ID
|
||||||
|
*
|
||||||
|
* @param id ID de l'adresse
|
||||||
|
* @return DTO de l'adresse
|
||||||
|
*/
|
||||||
|
public AdresseDTO trouverParId(UUID id) {
|
||||||
|
return adresseRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Adresse non trouvée avec l'ID: " + id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve toutes les adresses d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<AdresseDTO> trouverParOrganisation(UUID organisationId) {
|
||||||
|
return adresseRepository.findByOrganisationId(organisationId).stream()
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve toutes les adresses d'un membre
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return Liste des adresses
|
||||||
|
*/
|
||||||
|
public List<AdresseDTO> trouverParMembre(UUID membreId) {
|
||||||
|
return adresseRepository.findByMembreId(membreId).stream()
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve l'adresse d'un événement
|
||||||
|
*
|
||||||
|
* @param evenementId ID de l'événement
|
||||||
|
* @return DTO de l'adresse ou null
|
||||||
|
*/
|
||||||
|
public AdresseDTO trouverParEvenement(UUID evenementId) {
|
||||||
|
return adresseRepository
|
||||||
|
.findByEvenementId(evenementId)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve l'adresse principale d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return DTO de l'adresse principale ou null
|
||||||
|
*/
|
||||||
|
public AdresseDTO trouverPrincipaleParOrganisation(UUID organisationId) {
|
||||||
|
return adresseRepository
|
||||||
|
.findPrincipaleByOrganisationId(organisationId)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve l'adresse principale d'un membre
|
||||||
|
*
|
||||||
|
* @param membreId ID du membre
|
||||||
|
* @return DTO de l'adresse principale ou null
|
||||||
|
*/
|
||||||
|
public AdresseDTO trouverPrincipaleParMembre(UUID membreId) {
|
||||||
|
return adresseRepository
|
||||||
|
.findPrincipaleByMembreId(membreId)
|
||||||
|
.map(this::convertToDTO)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// MÉTHODES PRIVÉES
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/** Désactive les autres adresses principales pour la même entité */
|
||||||
|
private void desactiverAutresPrincipales(AdresseDTO adresseDTO) {
|
||||||
|
List<Adresse> autresPrincipales;
|
||||||
|
|
||||||
|
if (adresseDTO.getOrganisationId() != null) {
|
||||||
|
autresPrincipales =
|
||||||
|
adresseRepository
|
||||||
|
.find("organisation.id = ?1 AND principale = true", adresseDTO.getOrganisationId())
|
||||||
|
.list();
|
||||||
|
} else if (adresseDTO.getMembreId() != null) {
|
||||||
|
autresPrincipales =
|
||||||
|
adresseRepository
|
||||||
|
.find("membre.id = ?1 AND principale = true", adresseDTO.getMembreId())
|
||||||
|
.list();
|
||||||
|
} else {
|
||||||
|
return; // Pas d'entité associée
|
||||||
|
}
|
||||||
|
|
||||||
|
autresPrincipales.forEach(adr -> adr.setPrincipale(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convertit une entité en DTO */
|
||||||
|
private AdresseDTO convertToDTO(Adresse adresse) {
|
||||||
|
if (adresse == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdresseDTO dto = new AdresseDTO();
|
||||||
|
dto.setId(adresse.getId());
|
||||||
|
dto.setTypeAdresse(convertTypeAdresse(adresse.getTypeAdresse()));
|
||||||
|
dto.setAdresse(adresse.getAdresse());
|
||||||
|
dto.setComplementAdresse(adresse.getComplementAdresse());
|
||||||
|
dto.setCodePostal(adresse.getCodePostal());
|
||||||
|
dto.setVille(adresse.getVille());
|
||||||
|
dto.setRegion(adresse.getRegion());
|
||||||
|
dto.setPays(adresse.getPays());
|
||||||
|
dto.setLatitude(adresse.getLatitude());
|
||||||
|
dto.setLongitude(adresse.getLongitude());
|
||||||
|
dto.setPrincipale(adresse.getPrincipale());
|
||||||
|
dto.setLibelle(adresse.getLibelle());
|
||||||
|
dto.setNotes(adresse.getNotes());
|
||||||
|
|
||||||
|
if (adresse.getOrganisation() != null) {
|
||||||
|
dto.setOrganisationId(adresse.getOrganisation().getId());
|
||||||
|
}
|
||||||
|
if (adresse.getMembre() != null) {
|
||||||
|
dto.setMembreId(adresse.getMembre().getId());
|
||||||
|
}
|
||||||
|
if (adresse.getEvenement() != null) {
|
||||||
|
dto.setEvenementId(adresse.getEvenement().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setAdresseComplete(adresse.getAdresseComplete());
|
||||||
|
dto.setDateCreation(adresse.getDateCreation());
|
||||||
|
dto.setDateModification(adresse.getDateModification());
|
||||||
|
dto.setActif(adresse.getActif());
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convertit un DTO en entité */
|
||||||
|
private Adresse convertToEntity(AdresseDTO dto) {
|
||||||
|
if (dto == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Adresse adresse = new Adresse();
|
||||||
|
adresse.setTypeAdresse(convertTypeAdresse(dto.getTypeAdresse()));
|
||||||
|
adresse.setAdresse(dto.getAdresse());
|
||||||
|
adresse.setComplementAdresse(dto.getComplementAdresse());
|
||||||
|
adresse.setCodePostal(dto.getCodePostal());
|
||||||
|
adresse.setVille(dto.getVille());
|
||||||
|
adresse.setRegion(dto.getRegion());
|
||||||
|
adresse.setPays(dto.getPays());
|
||||||
|
adresse.setLatitude(dto.getLatitude());
|
||||||
|
adresse.setLongitude(dto.getLongitude());
|
||||||
|
adresse.setPrincipale(dto.getPrincipale() != null ? dto.getPrincipale() : false);
|
||||||
|
adresse.setLibelle(dto.getLibelle());
|
||||||
|
adresse.setNotes(dto.getNotes());
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
if (dto.getOrganisationId() != null) {
|
||||||
|
Organisation org =
|
||||||
|
organisationRepository
|
||||||
|
.findByIdOptional(dto.getOrganisationId())
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new NotFoundException(
|
||||||
|
"Organisation non trouvée avec l'ID: " + dto.getOrganisationId()));
|
||||||
|
adresse.setOrganisation(org);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.getMembreId() != null) {
|
||||||
|
Membre membre =
|
||||||
|
membreRepository
|
||||||
|
.findByIdOptional(dto.getMembreId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new NotFoundException("Membre non trouvé avec l'ID: " + dto.getMembreId()));
|
||||||
|
adresse.setMembre(membre);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.getEvenementId() != null) {
|
||||||
|
Evenement evenement =
|
||||||
|
evenementRepository
|
||||||
|
.findByIdOptional(dto.getEvenementId())
|
||||||
|
.orElseThrow(
|
||||||
|
() ->
|
||||||
|
new NotFoundException(
|
||||||
|
"Événement non trouvé avec l'ID: " + dto.getEvenementId()));
|
||||||
|
adresse.setEvenement(evenement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return adresse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Met à jour une entité à partir d'un DTO */
|
||||||
|
private void updateFromDTO(Adresse adresse, AdresseDTO dto) {
|
||||||
|
if (dto.getTypeAdresse() != null) {
|
||||||
|
adresse.setTypeAdresse(convertTypeAdresse(dto.getTypeAdresse()));
|
||||||
|
}
|
||||||
|
if (dto.getAdresse() != null) {
|
||||||
|
adresse.setAdresse(dto.getAdresse());
|
||||||
|
}
|
||||||
|
if (dto.getComplementAdresse() != null) {
|
||||||
|
adresse.setComplementAdresse(dto.getComplementAdresse());
|
||||||
|
}
|
||||||
|
if (dto.getCodePostal() != null) {
|
||||||
|
adresse.setCodePostal(dto.getCodePostal());
|
||||||
|
}
|
||||||
|
if (dto.getVille() != null) {
|
||||||
|
adresse.setVille(dto.getVille());
|
||||||
|
}
|
||||||
|
if (dto.getRegion() != null) {
|
||||||
|
adresse.setRegion(dto.getRegion());
|
||||||
|
}
|
||||||
|
if (dto.getPays() != null) {
|
||||||
|
adresse.setPays(dto.getPays());
|
||||||
|
}
|
||||||
|
if (dto.getLatitude() != null) {
|
||||||
|
adresse.setLatitude(dto.getLatitude());
|
||||||
|
}
|
||||||
|
if (dto.getLongitude() != null) {
|
||||||
|
adresse.setLongitude(dto.getLongitude());
|
||||||
|
}
|
||||||
|
if (dto.getPrincipale() != null) {
|
||||||
|
adresse.setPrincipale(dto.getPrincipale());
|
||||||
|
}
|
||||||
|
if (dto.getLibelle() != null) {
|
||||||
|
adresse.setLibelle(dto.getLibelle());
|
||||||
|
}
|
||||||
|
if (dto.getNotes() != null) {
|
||||||
|
adresse.setNotes(dto.getNotes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convertit TypeAdresse (entité) vers TypeAdresse (DTO) - même enum, pas de conversion nécessaire */
|
||||||
|
private TypeAdresse convertTypeAdresse(TypeAdresse type) {
|
||||||
|
return type; // Même enum, pas de conversion nécessaire
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convertit TypeAdresse (DTO) vers TypeAdresse (entité) - même enum, pas de conversion nécessaire */
|
||||||
|
private TypeAdresse convertTypeAdresse(TypeAdresse type) {
|
||||||
|
return type != null ? type : TypeAdresse.AUTRE; // Même enum, valeur par défaut si null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package dev.lions.unionflow.server.service;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.Permission;
|
||||||
|
import dev.lions.unionflow.server.repository.PermissionRepository;
|
||||||
|
import dev.lions.unionflow.server.service.KeycloakService;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service métier pour la gestion des permissions
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class PermissionService {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(PermissionService.class);
|
||||||
|
|
||||||
|
@Inject PermissionRepository permissionRepository;
|
||||||
|
|
||||||
|
@Inject KeycloakService keycloakService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une nouvelle permission
|
||||||
|
*
|
||||||
|
* @param permission Permission à créer
|
||||||
|
* @return Permission créée
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Permission creerPermission(Permission permission) {
|
||||||
|
LOG.infof("Création d'une nouvelle permission: %s", permission.getCode());
|
||||||
|
|
||||||
|
// Vérifier l'unicité du code
|
||||||
|
if (permissionRepository.findByCode(permission.getCode()).isPresent()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Une permission avec ce code existe déjà: " + permission.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Générer le code si non fourni
|
||||||
|
if (permission.getCode() == null || permission.getCode().isEmpty()) {
|
||||||
|
permission.setCode(
|
||||||
|
Permission.genererCode(
|
||||||
|
permission.getModule(), permission.getRessource(), permission.getAction()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Métadonnées
|
||||||
|
permission.setCreePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
permissionRepository.persist(permission);
|
||||||
|
LOG.infof(
|
||||||
|
"Permission créée avec succès: ID=%s, Code=%s",
|
||||||
|
permission.getId(), permission.getCode());
|
||||||
|
|
||||||
|
return permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour une permission existante
|
||||||
|
*
|
||||||
|
* @param id ID de la permission
|
||||||
|
* @param permissionModifiee Permission avec les modifications
|
||||||
|
* @return Permission mise à jour
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Permission mettreAJourPermission(UUID id, Permission permissionModifiee) {
|
||||||
|
LOG.infof("Mise à jour de la permission ID: %s", id);
|
||||||
|
|
||||||
|
Permission permission =
|
||||||
|
permissionRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Permission non trouvée avec l'ID: " + id));
|
||||||
|
|
||||||
|
// Mise à jour
|
||||||
|
permission.setCode(permissionModifiee.getCode());
|
||||||
|
permission.setModule(permissionModifiee.getModule());
|
||||||
|
permission.setRessource(permissionModifiee.getRessource());
|
||||||
|
permission.setAction(permissionModifiee.getAction());
|
||||||
|
permission.setLibelle(permissionModifiee.getLibelle());
|
||||||
|
permission.setDescription(permissionModifiee.getDescription());
|
||||||
|
permission.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
permissionRepository.persist(permission);
|
||||||
|
LOG.infof("Permission mise à jour avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une permission par son ID
|
||||||
|
*
|
||||||
|
* @param id ID de la permission
|
||||||
|
* @return Permission ou null
|
||||||
|
*/
|
||||||
|
public Permission trouverParId(UUID id) {
|
||||||
|
return permissionRepository.findByIdOptional(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve une permission par son code
|
||||||
|
*
|
||||||
|
* @param code Code de la permission
|
||||||
|
* @return Permission ou null
|
||||||
|
*/
|
||||||
|
public Permission trouverParCode(String code) {
|
||||||
|
return permissionRepository.findByCode(code).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste les permissions par module
|
||||||
|
*
|
||||||
|
* @param module Nom du module
|
||||||
|
* @return Liste des permissions
|
||||||
|
*/
|
||||||
|
public List<Permission> listerParModule(String module) {
|
||||||
|
return permissionRepository.findByModule(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste les permissions par ressource
|
||||||
|
*
|
||||||
|
* @param ressource Nom de la ressource
|
||||||
|
* @return Liste des permissions
|
||||||
|
*/
|
||||||
|
public List<Permission> listerParRessource(String ressource) {
|
||||||
|
return permissionRepository.findByRessource(ressource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste toutes les permissions actives
|
||||||
|
*
|
||||||
|
* @return Liste des permissions actives
|
||||||
|
*/
|
||||||
|
public List<Permission> listerToutesActives() {
|
||||||
|
return permissionRepository.findAllActives();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime (désactive) une permission
|
||||||
|
*
|
||||||
|
* @param id ID de la permission
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void supprimerPermission(UUID id) {
|
||||||
|
LOG.infof("Suppression de la permission ID: %s", id);
|
||||||
|
|
||||||
|
Permission permission =
|
||||||
|
permissionRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Permission non trouvée avec l'ID: " + id));
|
||||||
|
|
||||||
|
permission.setActif(false);
|
||||||
|
permission.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
permissionRepository.persist(permission);
|
||||||
|
LOG.infof("Permission supprimée avec succès: ID=%s", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package dev.lions.unionflow.server.service;
|
||||||
|
|
||||||
|
import dev.lions.unionflow.server.entity.Organisation;
|
||||||
|
import dev.lions.unionflow.server.entity.Role;
|
||||||
|
import dev.lions.unionflow.server.entity.Role.TypeRole;
|
||||||
|
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||||
|
import dev.lions.unionflow.server.repository.RoleRepository;
|
||||||
|
import dev.lions.unionflow.server.service.KeycloakService;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service métier pour la gestion des rôles
|
||||||
|
*
|
||||||
|
* @author UnionFlow Team
|
||||||
|
* @version 3.0
|
||||||
|
* @since 2025-01-29
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class RoleService {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(RoleService.class);
|
||||||
|
|
||||||
|
@Inject RoleRepository roleRepository;
|
||||||
|
|
||||||
|
@Inject OrganisationRepository organisationRepository;
|
||||||
|
|
||||||
|
@Inject KeycloakService keycloakService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouveau rôle
|
||||||
|
*
|
||||||
|
* @param role Rôle à créer
|
||||||
|
* @return Rôle créé
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Role creerRole(Role role) {
|
||||||
|
LOG.infof("Création d'un nouveau rôle: %s", role.getCode());
|
||||||
|
|
||||||
|
// Vérifier l'unicité du code
|
||||||
|
if (roleRepository.findByCode(role.getCode()).isPresent()) {
|
||||||
|
throw new IllegalArgumentException("Un rôle avec ce code existe déjà: " + role.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Métadonnées
|
||||||
|
role.setCreePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
roleRepository.persist(role);
|
||||||
|
LOG.infof("Rôle créé avec succès: ID=%s, Code=%s", role.getId(), role.getCode());
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour un rôle existant
|
||||||
|
*
|
||||||
|
* @param id ID du rôle
|
||||||
|
* @param roleModifie Rôle avec les modifications
|
||||||
|
* @return Rôle mis à jour
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Role mettreAJourRole(UUID id, Role roleModifie) {
|
||||||
|
LOG.infof("Mise à jour du rôle ID: %s", id);
|
||||||
|
|
||||||
|
Role role =
|
||||||
|
roleRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Rôle non trouvé avec l'ID: " + id));
|
||||||
|
|
||||||
|
// Vérifier l'unicité du code si modifié
|
||||||
|
if (!role.getCode().equals(roleModifie.getCode())) {
|
||||||
|
if (roleRepository.findByCode(roleModifie.getCode()).isPresent()) {
|
||||||
|
throw new IllegalArgumentException("Un rôle avec ce code existe déjà: " + roleModifie.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mise à jour
|
||||||
|
role.setCode(roleModifie.getCode());
|
||||||
|
role.setLibelle(roleModifie.getLibelle());
|
||||||
|
role.setDescription(roleModifie.getDescription());
|
||||||
|
role.setNiveauHierarchique(roleModifie.getNiveauHierarchique());
|
||||||
|
role.setTypeRole(roleModifie.getTypeRole());
|
||||||
|
role.setOrganisation(roleModifie.getOrganisation());
|
||||||
|
role.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
roleRepository.persist(role);
|
||||||
|
LOG.infof("Rôle mis à jour avec succès: ID=%s", id);
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un rôle par son ID
|
||||||
|
*
|
||||||
|
* @param id ID du rôle
|
||||||
|
* @return Rôle ou null
|
||||||
|
*/
|
||||||
|
public Role trouverParId(UUID id) {
|
||||||
|
return roleRepository.findByIdOptional(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un rôle par son code
|
||||||
|
*
|
||||||
|
* @param code Code du rôle
|
||||||
|
* @return Rôle ou null
|
||||||
|
*/
|
||||||
|
public Role trouverParCode(String code) {
|
||||||
|
return roleRepository.findByCode(code).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste tous les rôles système
|
||||||
|
*
|
||||||
|
* @return Liste des rôles système
|
||||||
|
*/
|
||||||
|
public List<Role> listerRolesSysteme() {
|
||||||
|
return roleRepository.findRolesSysteme();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste tous les rôles d'une organisation
|
||||||
|
*
|
||||||
|
* @param organisationId ID de l'organisation
|
||||||
|
* @return Liste des rôles
|
||||||
|
*/
|
||||||
|
public List<Role> listerParOrganisation(UUID organisationId) {
|
||||||
|
return roleRepository.findByOrganisationId(organisationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste tous les rôles actifs
|
||||||
|
*
|
||||||
|
* @return Liste des rôles actifs
|
||||||
|
*/
|
||||||
|
public List<Role> listerTousActifs() {
|
||||||
|
return roleRepository.findAllActifs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime (désactive) un rôle
|
||||||
|
*
|
||||||
|
* @param id ID du rôle
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void supprimerRole(UUID id) {
|
||||||
|
LOG.infof("Suppression du rôle ID: %s", id);
|
||||||
|
|
||||||
|
Role role =
|
||||||
|
roleRepository
|
||||||
|
.findByIdOptional(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException("Rôle non trouvé avec l'ID: " + id));
|
||||||
|
|
||||||
|
// Vérifier si c'est un rôle système
|
||||||
|
if (role.isRoleSysteme()) {
|
||||||
|
throw new IllegalStateException("Impossible de supprimer un rôle système");
|
||||||
|
}
|
||||||
|
|
||||||
|
role.setActif(false);
|
||||||
|
role.setModifiePar(keycloakService.getCurrentUserEmail());
|
||||||
|
|
||||||
|
roleRepository.persist(role);
|
||||||
|
LOG.infof("Rôle supprimé avec succès: ID=%s", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1297
unionflow.md
Normal file
1297
unionflow.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user