Compare commits

...

19 Commits

Author SHA1 Message Date
dahoud
db36727001 feat(sprint-17 api 2026-04-25): bump 1.0.10 + DTO KpiPublicSnapshot pour partage KPI public signé
Sous-ensemble strictement filtré de ComplianceSnapshot — vue read-only pour autorités
externes (BCEAO, ARTCI, CENTIF). N'expose que les indicateurs agrégés, jamais
d'informations sensibles (complianceOfficerId, payloads, IDs membres, PEP details).

Champs : organisationNom, referentielComptable, scoreGlobal, complianceOfficerDesigne,
agAnnuelleStatut, rapportAirmsStatut, dirigeantsAvecCmu, tauxKycAJourPct,
tauxFormationLbcFtPct, couvertureUboPct, commissaireAuxComptesStatut, dateGeneration.

Tests Jacoco 100% (3 tests : builder all-fields, null defaults, equals/hash/toString)

ACTION USER : `mvn clean install` puis `mvn deploy` pour 1.0.10 sur Gitea.
2026-04-25 16:47:54 +00:00
dahoud
7a596f68bd feat(sprint-13.A api 2026-04-25): bump 1.0.9 + OrganisationResponse expose referentielComptable + complianceOfficerId
Pendant pour Sprint 10 — UpdateOrganisationRequest avait déjà ces 2 champs (Sprint 10),
mais OrganisationResponse ne les exposait pas en lecture. Le formulaire d'édition web
ne pouvait donc pas afficher leur valeur initiale lors du load.

Quarkus inchangé (3.27.3 — décision flotte LTS sur 3.27 jusqu'à fin août 2026).

Modifications
- OrganisationResponse : 2 nouveaux champs String referentielComptable + UUID complianceOfficerId
- Tests : 2 tests Jacoco 100% (null par défaut, builder, setter ; pour les 2 nouveaux champs)
- Bump version 1.0.8 → 1.0.9

ACTION USER : `mvn clean install` puis `mvn deploy` pour publier 1.0.9 sur Gitea.
2026-04-25 15:22:26 +00:00
dahoud
f263d4f51a revert(sprint-14 api): rollback 1.0.9 → 1.0.8 — flotte reste sur Quarkus 3.27.3 LTS
Décision stratégique : rester sur Quarkus 3.27.3 pour TOUTE la flotte Lions
jusqu'à fin août 2026 (1 mois avant EOL 3.27 = 24 sept 2026).

Justification :
- 3.27.3 LTS = 5 mois de runway, pas urgent
- Cohérence flotte (10 projets actuellement sur 3.27.3)
- Évite saut imprévu PrimeFaces 14 → 15 (extension quarkus-primefaces 4.15.14
  cible Quarkus 3.32.2 et impose PF15.0.14 — incompat thème Freya 5.0.0 acheté)
- LUM en prod 44 jours — pas de re-déploiement risqué
- 3.39 LTS attendue ~sept 2026 (juste avant EOL 3.27) → migration directe
  3.27 → 3.39 prévue avec écosystème PF15+Freya mature

Restaurer pom.xml 1.0.8 + Quarkus 3.27.3 (état post-Sprint 10).
1.0.9 (commit 6f23319) jamais publié sur Gitea — fix local seulement.
2026-04-25 15:10:55 +00:00
dahoud
6f233193d4 build(sprint-14 api 2026-04-25): bump 1.0.8 → 1.0.9 + Quarkus 3.27.3 → 3.33.0 LTS
Migration LTS dans la fenêtre officielle. Module DTOs records purs — aucun runtime
Quarkus consommé directement, le bump de quarkus.platform.version est documentaire
(aligned-with) pour cohérence flotte.

Aucun breaking change applicable (records + Bean Validation + tests JUnit 5).
Tests existants : 3530 + jacoco 100% — doivent rester verts au build.

ACTION USER : `mvn clean install` puis `mvn deploy` pour publier 1.0.9 sur Gitea.
2026-04-25 15:00:34 +00:00
dahoud
13635e269b test(sprint-10 api): tests DTOs Sprint 10 pour Jacoco 100%
Build api 1.0.8 a échoué sur jacoco:check (97-99% < 100% requis). Ajout des tests
manquants pour les 6 nouveaux DTOs + extension UpdateOrganisationRequestTest.

Tests ajoutés
- CreateBeneficiaireEffectifRequestTest : 8 tests (builder all-fields, validation 5 champs obligatoires, nationalité ISO-3, natureControle regex, pourcentage 0-100, paysResidence vide accepté, valide complet, equals/hash/toString)
- UpdateBeneficiaireEffectifRequestTest : 7 tests (builder, vide valide, pourcentage > 100, nationalité format, natureControle invalide / vide, equals/hash/toString)
- BeneficiaireEffectifResponseTest : 3 tests (all-fields, null defaults, equals)
- AuditTrailOperationResponseTest : 3 tests (all-fields, null defaults, equals)
- CreateRoleDelegationRequestTest : 6 tests (builder, 6 champs obligatoires, role regex, dateFin future, valide, equals)
- RoleDelegationResponseTest : 3 tests (all-fields, null, equals)
- UpdateOrganisationRequestTest : 4 tests ajoutés (referentielComptable valide × 3, vide accepté, invalide, complianceOfficerId)

Total +34 tests, attendus pour rétablir Jacoco 100% bundle.
2026-04-25 12:42:06 +00:00
dahoud
65a9d03c00 feat(sprint-10 api 2026-04-25): bump 1.0.8 + DTOs UBO/audit-trail/délégation + UpdateOrganisationRequest enrichi
Pré-requis pour exposer les features Sprints 1-2 via REST. Architecture stricte :
DTOs en module api (contrat public), aucune logique métier.

Version : 1.0.7 → 1.0.8

DTOs UBO (Instr. BCEAO 003-03-2025)
- CreateBeneficiaireEffectifRequest : validation pays ISO-3, regex natureControle (5 valeurs), pourcentages 0-100
- UpdateBeneficiaireEffectifRequest : tous optionnels (PATCH partiel)
- BeneficiaireEffectifResponse : vue read-only

DTOs audit trail (Sprint 1)
- AuditTrailOperationResponse : payloadAvant/payloadApres/metadata en String JSONB

DTOs délégation rôles (Sprint 2)
- CreateRoleDelegationRequest : regex rôle uppercase + dates futures
- RoleDelegationResponse : statut + estActive (calculé)

Enrich UpdateOrganisationRequest
- referentielComptable (regex SYSCOHADA|SYCEBNL|PCSFD_UMOA)
- complianceOfficerId UUID (Instr. BCEAO 001-03-2025)

À publier sur Gitea via script/publish-api.sh — user action requise.
2026-04-25 12:31:43 +00:00
2d21a580de docs: bump published version ref 2.0.0→1.0.7 (ligne publication Gitea Maven 2026-04-24, module autonome) 2026-04-24 18:06:09 +00:00
3feab6518e chore(quarkus-327): drop parent-pom.xml (all modules autonomous), clean publish-api scripts 2026-04-23 15:50:22 +00:00
7fa862b755 chore(quarkus-327): bump to Quarkus 3.27.3 LTS, make pom autonomous, bump to 1.0.7 2026-04-23 14:42:55 +00:00
dahoud
1496c83b6f docs: ajouter openapi.yaml généré depuis le backend de production (v1.0.5) 2026-04-21 16:28:31 +00:00
dahoud
ee6e945bdb feat: DTOs KYC/messagerie/mutuelle/parts/versement/payment + enrichissement paiement
## Nouveaux packages DTO / enums
- dto/kyc/ : NiveauRisqueKyc, TypePieceIdentite, KycDossier DTOs
- dto/messagerie/ : conversation/message DTOs + enums (TypeConversation, etc.)
- dto/mutuelle/financier/ : ParametresFinanciersMutuelle DTOs
- dto/mutuelle/parts/ : ComptePartsSociales, TransactionPartsSociales DTOs
- dto/versement/ : Versement DTOs
- payment/ : PaymentProvider, PaymentStatus, PaymentException,
  CheckoutRequest, CheckoutSession, PaymentEvent (SPI paiement unifié)

## Paiement enrichi
- CreatePaiementRequest, DeclarerPaiementManuelRequest, InitierPaiementEnLigneRequest
- IntentionStatutResponse, PaiementGatewayResponse, PaiementResponse, PaiementSummaryResponse
- Tests correspondants mis à jour

## Tests nouveaux
- DTOs agricole, ayant-droit, backup tests
- Tous les nouveaux DTOs KYC/messagerie/mutuelle/versement/payment

## Build
- pom.xml : version 1.0.5, parent-pom.xml aligné
- script/publish-api.sh : version bumped

164 fichiers, +8 481 insertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:44:49 +00:00
dahoud
eac66ce25d refactor: supprimer alias doublons typeAssociation/typeLibelle/numeroRegistre dans OrganisationResponse
- Supprimer typeAssociation (doublon mort de typeOrganisation)
- Supprimer typeLibelle (doublon de typeOrganisationLibelle)
- Supprimer alias getNumeroRegistre/setNumeroRegistre (doublon de numeroEnregistrement)
- Supprimer alias getNomOrganisationParente/setNomOrganisationParente (doublon de organisationParenteNom)
- Réécrire OrganisationResponseTest pour tester les vrais champs
2026-04-17 19:53:25 +00:00
dahoud
69f740d6ed test: corriger ADMIN_ENTITE→ADMIN_ORGANISATION dans LoginRequestTest 2026-04-17 19:19:57 +00:00
dahoud
1b7700a368 test: corriger OrganisationSummaryResponseTest pour ville + pays (12 args)
Constructeur passé de 10 à 12 arguments avec l'ajout de ville et pays.
Tests mis à jour + nouveau test testSettersVillePays.
2026-04-16 12:34:05 +00:00
dahoud
c9615f349e fix(dto): ajouter ville + pays à OrganisationSummaryResponse
Le XHTML liste.xhtml affichait #{org.ville} et #{org.pays} mais le DTO
Summary n'avait que id/nom/type/statut/nombreMembres — pas de localisation.
L'EL résolvait null → colonnes vides dans le DataTable.
2026-04-16 12:28:26 +00:00
dahoud
ba31d6802e test(MembreSummaryResponse): cover isActif(false) branch for 100% Jacoco
isActif() had 1 of 4 branches missed: Boolean=false was never tested.
Added explicit test for actif=false (distinct from null).
2026-04-11 02:50:33 +00:00
dahoud
d66c013d0b fix(tests): replace remaining record-style accessors with getters
ErrorResponseTest and CompteAdherentResponseTest were using record
accessors (message(), error(), numeroMembre(), etc.) on @Data classes.
2026-04-11 02:45:24 +00:00
dahoud
ae7ada6b91 fix(tests): replace record-style accessors with JavaBean getters in server-api tests
All response DTOs were converted from records to @Data classes for JSF/EL
compatibility. Updated 8 test files to use getters instead of record
component accessors. Also added isActif() method to MembreSummaryResponse
(Boolean wrapper needs explicit isActif() since Lombok generates getActif()).
Fixed ConversationResponseTest builder calls: .isMuted() → .muted(),
and MessageResponseTest builder calls: .isEdited() → .edited(), .isDeleted() → .deleted().
2026-04-11 02:21:08 +00:00
dahoud
43678c8ae9 fix(api): 1.0.4 — OrganisationSummaryResponse record→@Data (compat JSF/EL), version bump 2026-04-11 02:00:07 +00:00
614 changed files with 75198 additions and 50252 deletions

286
.gitignore vendored
View File

@@ -1,143 +1,143 @@
# ====================================
# GITIGNORE POUR UNIONFLOW SERVER API
# ====================================
# ===== MAVEN =====
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# ===== QUARKUS =====
.quarkus/
quarkus.log
hs_err_pid*
# ===== JAVA COMPILED FILES =====
*.class
*.jar
*.war
*.ear
*.nar
*.zip
*.tar.gz
*.rar
# ===== IDE - ECLIPSE =====
.project
.classpath
.settings/
.factorypath
.metadata/
bin/
.apt_generated
.springBeans
.sts4-cache
# ===== IDE - INTELLIJ IDEA =====
.idea/
*.iml
*.ipr
*.iws
out/
# ===== IDE - NETBEANS =====
nbproject/
nbbuild/
nbdist/
.nb-gradle/
nb-configuration.xml
nbactions.xml
# ===== IDE - VS CODE =====
.vscode/
*.code-workspace
# ===== OS SPECIFIC =====
# Mac
.DS_Store
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
# Linux
*~
# ===== LOGS =====
*.log
*.log.*
logs/
log/
# ===== TEMPORARY FILES =====
*.tmp
*.temp
*.bak
*.backup
*.old
*.swp
*.swo
*.orig
*.rej
*~
# ===== ENVIRONMENT & CONFIGURATION =====
.env
.env.local
.env.*
*.local
application-local.properties
# ===== SECURITY - KEYS & CERTIFICATES =====
*.key
*.pem
*.crt
*.p12
*.jks
*.keystore
.certs/
.certificates/
# ===== TEST COVERAGE =====
.jacoco/
jacoco.exec
coverage/
.nyc_output/
# ===== GENERATED SOURCES =====
src/gen/
generated-sources/
generated-test-sources/
# ===== BUILD ARTIFACTS =====
dist/
build/
out/
# ===== NODE (if used for build tools) =====
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# ===== DOCKER =====
.dockerignore
# ===== MAVEN WRAPPER (if not using project wrapper) =====
# Uncomment if you don't want to commit Maven wrapper
# .mvn/
# mvnw
# mvnw.cmd
# ====================================
# GITIGNORE POUR UNIONFLOW SERVER API
# ====================================
# ===== MAVEN =====
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# ===== QUARKUS =====
.quarkus/
quarkus.log
hs_err_pid*
# ===== JAVA COMPILED FILES =====
*.class
*.jar
*.war
*.ear
*.nar
*.zip
*.tar.gz
*.rar
# ===== IDE - ECLIPSE =====
.project
.classpath
.settings/
.factorypath
.metadata/
bin/
.apt_generated
.springBeans
.sts4-cache
# ===== IDE - INTELLIJ IDEA =====
.idea/
*.iml
*.ipr
*.iws
out/
# ===== IDE - NETBEANS =====
nbproject/
nbbuild/
nbdist/
.nb-gradle/
nb-configuration.xml
nbactions.xml
# ===== IDE - VS CODE =====
.vscode/
*.code-workspace
# ===== OS SPECIFIC =====
# Mac
.DS_Store
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
# Linux
*~
# ===== LOGS =====
*.log
*.log.*
logs/
log/
# ===== TEMPORARY FILES =====
*.tmp
*.temp
*.bak
*.backup
*.old
*.swp
*.swo
*.orig
*.rej
*~
# ===== ENVIRONMENT & CONFIGURATION =====
.env
.env.local
.env.*
*.local
application-local.properties
# ===== SECURITY - KEYS & CERTIFICATES =====
*.key
*.pem
*.crt
*.p12
*.jks
*.keystore
.certs/
.certificates/
# ===== TEST COVERAGE =====
.jacoco/
jacoco.exec
coverage/
.nyc_output/
# ===== GENERATED SOURCES =====
src/gen/
generated-sources/
generated-test-sources/
# ===== BUILD ARTIFACTS =====
dist/
build/
out/
# ===== NODE (if used for build tools) =====
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# ===== DOCKER =====
.dockerignore
# ===== MAVEN WRAPPER (if not using project wrapper) =====
# Uncomment if you don't want to commit Maven wrapper
# .mvn/
# mvnw
# mvnw.cmd

View File

@@ -1,276 +1,276 @@
# Classes sans Tests - UnionFlow Server API
**Date d'analyse**: 2026-03-15
**Couverture globale**: 72% (4635 instructions manquées sur 16697)
**Classes sans tests**: 91 classes (0% de couverture)
---
## 1. DTOs REQUEST (dev.lions.unionflow.server.api.dto)
### 1.1 Solidarité (8 classes - 0%)
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreateCommentaireAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreateDemandeAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreateEvaluationAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreatePropositionAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdateCommentaireAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdateDemandeAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdateEvaluationAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdatePropositionAideRequest`
### 1.2 Comptabilité (8 classes - 0%)
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateCompteComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateEcritureComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateJournalComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateLigneEcritureRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateCompteComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateEcritureComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateJournalComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateLigneEcritureRequest`
### 1.3 Paiement (4 classes - 0%)
- `dev.lions.unionflow.server.api.dto.paiement.request.CreatePaiementRequest`
- `dev.lions.unionflow.server.api.dto.paiement.request.DeclarerPaiementManuelRequest`
- `dev.lions.unionflow.server.api.dto.paiement.request.InitierDepotEpargneRequest`
- `dev.lions.unionflow.server.api.dto.paiement.request.InitierPaiementEnLigneRequest`
### 1.4 Notification (4 classes - 0%)
- `dev.lions.unionflow.server.api.dto.notification.request.CreateNotificationRequest`
- `dev.lions.unionflow.server.api.dto.notification.request.CreateTemplateNotificationRequest`
- `dev.lions.unionflow.server.api.dto.notification.request.UpdateNotificationRequest`
- `dev.lions.unionflow.server.api.dto.notification.request.UpdateTemplateNotificationRequest`
### 1.5 Document (4 classes - 0%)
- `dev.lions.unionflow.server.api.dto.document.request.CreateDocumentRequest`
- `dev.lions.unionflow.server.api.dto.document.request.CreatePieceJointeRequest`
- `dev.lions.unionflow.server.api.dto.document.request.UpdateDocumentRequest`
- `dev.lions.unionflow.server.api.dto.document.request.UpdatePieceJointeRequest`
### 1.6 Abonnement (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.abonnement.request.CreateAbonnementRequest`
- `dev.lions.unionflow.server.api.dto.abonnement.request.UpdateAbonnementRequest`
### 1.7 Événement (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.evenement.request.CreateEvenementRequest`
- `dev.lions.unionflow.server.api.dto.evenement.request.UpdateEvenementRequest`
### 1.8 Formule Abonnement (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.formuleabonnement.request.CreateFormuleAbonnementRequest`
- `dev.lions.unionflow.server.api.dto.formuleabonnement.request.UpdateFormuleAbonnementRequest`
### 1.9 Organisation (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.organisation.request.CreateOrganisationRequest`
- `dev.lions.unionflow.server.api.dto.organisation.request.UpdateOrganisationRequest`
### 1.10 Adresse (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.adresse.request.CreateAdresseRequest`
- `dev.lions.unionflow.server.api.dto.adresse.request.UpdateAdresseRequest`
### 1.11 Membre (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.membre.request.CreateMembreRequest`
- `dev.lions.unionflow.server.api.dto.membre.request.UpdateMembreRequest`
### 1.12 Cotisation (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.cotisation.request.CreateCotisationRequest`
- `dev.lions.unionflow.server.api.dto.cotisation.request.UpdateCotisationRequest`
### 1.13 Référence (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest`
- `dev.lions.unionflow.server.api.dto.reference.request.UpdateTypeReferenceRequest`
### 1.14 Suggestion (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.suggestion.request.CreateSuggestionRequest`
- `dev.lions.unionflow.server.api.dto.suggestion.request.UpdateSuggestionRequest`
### 1.15 Finance (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.finance.request.CreateAdhesionRequest`
- `dev.lions.unionflow.server.api.dto.finance.request.UpdateAdhesionRequest`
### 1.16 User (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.user.request.CreateUserRequest`
- `dev.lions.unionflow.server.api.dto.user.request.UpdateUserRequest`
### 1.17 Configuration (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.config.request.CreateConfigurationRequest`
- `dev.lions.unionflow.server.api.dto.config.request.UpdateConfigurationRequest`
### 1.18 Ticket (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.ticket.request.CreateTicketRequest`
- `dev.lions.unionflow.server.api.dto.ticket.request.UpdateTicketRequest`
### 1.19 Rôle (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.role.request.CreateRoleRequest`
- `dev.lions.unionflow.server.api.dto.role.request.UpdateRoleRequest`
### 1.20 Admin (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.admin.request.CreateAdminRequest`
### 1.21 Favoris (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.favoris.request.CreateFavoriRequest`
---
## 2. DTOs RESPONSE (dev.lions.unionflow.server.api.dto)
### 2.1 Paiement (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.paiement.response.PaiementGatewayResponse`
### 2.2 Cotisation (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.cotisation.response.CotisationSummaryResponse`
### 2.3 Adresse (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.adresse.response.AdresseResponse`
### 2.4 Admin (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.admin.response.AdminResponse`
---
## 3. AUTRES DTOs
### 3.1 Wave (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.wave.CompteWaveDTO`
- `dev.lions.unionflow.server.api.dto.wave.TransactionWaveDTO`
---
## 4. ENUMS (dev.lions.unionflow.server.api.enums)
### 4.1 Mutuelle - Crédit (4 classes - 0%)
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutDemandeCredit`
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutEcheanceCredit`
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeCredit`
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeGarantie`
### 4.2 Mutuelle - Épargne (3 classes - 0%)
- `dev.lions.unionflow.server.api.enums.mutuelle.epargne.StatutCompteEpargne`
- `dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeCompteEpargne`
- `dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne`
### 4.3 Vote (3 classes - 0%)
- `dev.lions.unionflow.server.api.enums.vote.ModeScrutin`
- `dev.lions.unionflow.server.api.enums.vote.StatutVote`
- `dev.lions.unionflow.server.api.enums.vote.TypeVote`
### 4.4 Tontine (3 classes - 0%)
- `dev.lions.unionflow.server.api.enums.tontine.FrequenceTour`
- `dev.lions.unionflow.server.api.enums.tontine.StatutTontine`
- `dev.lions.unionflow.server.api.enums.tontine.TypeTontine`
### 4.5 Ayant Droit (2 classes - 0%)
- `dev.lions.unionflow.server.api.enums.ayantdroit.StatutAyantDroit`
- `dev.lions.unionflow.server.api.enums.ayantdroit.TypeAyantDroit`
### 4.6 Agricole (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.agricole.StatutCampagneAgricole`
### 4.7 Collecte de Fonds (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.collectefonds.StatutCampagneCollecte`
### 4.8 Culte (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.culte.TypeDonReligieux`
### 4.9 ONG (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.ong.StatutProjetOng`
### 4.10 Gouvernance (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.gouvernance.NiveauEchelon`
### 4.11 Registre (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.registre.StatutAgrement`
---
## RÉCAPITULATIF PAR CATÉGORIE
| Catégorie | Nombre de classes | % du total |
|-----------|-------------------|------------|
| **DTO Requests** | **62 classes** | **68%** |
| **Enums** | **21 classes** | **23%** |
| **DTO Responses** | **4 classes** | **4%** |
| **Autres DTOs** | **2 classes** | **2%** |
| **Autres** | **2 classes** | **2%** |
| **TOTAL** | **91 classes** | **100%** |
---
## PRIORITÉS DE TEST
### P0 - Haute Priorité (Core Business)
1. **Solidarité** (8 requests) - Module métier principal
2. **Comptabilité** (8 requests) - Gestion financière critique
3. **Paiement** (4 requests + 1 response) - Transactions financières
4. **Mutuelle Crédit** (4 enums) - Microfinance core
5. **Mutuelle Épargne** (3 enums) - Microfinance core
### P1 - Priorité Moyenne (Features importantes)
6. **Notification** (4 requests) - Communication système
7. **Document** (4 requests) - Gestion documentaire
8. **Événement** (2 requests) - Gestion événementielle
9. **Organisation** (2 requests) - Structure organisationnelle
10. **Membre** (2 requests) - Gestion des utilisateurs
### P2 - Priorité Basse (Features secondaires)
11. **Abonnement** (2 requests) - Gestion des abonnements
12. **Tontine** (3 enums) - Feature spécifique
13. **Vote** (3 enums) - Feature spécifique
14. **Autres requests** (20 requests restants)
15. **Enums divers** (11 enums restants)
---
## PLAN D'ACTION
### Étape 1: Tests DTOs Request (62 classes)
- Tester getters/setters
- Tester validations Jakarta Bean Validation
- Tester méthodes equals/hashCode/toString si présentes
### Étape 2: Tests DTOs Response (4 classes)
- Tester constructeurs et builders
- Tester sérialisation JSON
### Étape 3: Tests Enums (21 classes)
- Tester valueOf() et values()
- Tester getters de valeurs
- Tester méthodes utilitaires (fromString, etc.)
### Étape 4: Tests Autres (4 classes)
- Tests spécifiques selon le type de classe
---
## TEMPLATES DE TEST RECOMMANDÉS
### Pour DTOs Request:
```java
@Test
void testCreateXxxRequest_AllFields() {
var request = new CreateXxxRequest();
// Set all fields
// Assert all fields
}
@Test
void testCreateXxxRequest_Validation() {
var request = new CreateXxxRequest();
// Test @NotNull, @Size, etc.
}
```
### Pour Enums:
```java
@Test
void testEnumValues() {
assertEquals(3, TypeXxx.values().length);
}
@Test
void testEnumValueOf() {
assertEquals(TypeXxx.VALUE1, TypeXxx.valueOf("VALUE1"));
}
```
---
**Objectif**: Atteindre **100% de couverture** sur le module unionflow-server-api.
# Classes sans Tests - UnionFlow Server API
**Date d'analyse**: 2026-03-15
**Couverture globale**: 72% (4635 instructions manquées sur 16697)
**Classes sans tests**: 91 classes (0% de couverture)
---
## 1. DTOs REQUEST (dev.lions.unionflow.server.api.dto)
### 1.1 Solidarité (8 classes - 0%)
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreateCommentaireAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreateDemandeAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreateEvaluationAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.CreatePropositionAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdateCommentaireAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdateDemandeAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdateEvaluationAideRequest`
- `dev.lions.unionflow.server.api.dto.solidarite.request.UpdatePropositionAideRequest`
### 1.2 Comptabilité (8 classes - 0%)
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateCompteComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateEcritureComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateJournalComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.CreateLigneEcritureRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateCompteComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateEcritureComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateJournalComptableRequest`
- `dev.lions.unionflow.server.api.dto.comptabilite.request.UpdateLigneEcritureRequest`
### 1.3 Paiement (4 classes - 0%)
- `dev.lions.unionflow.server.api.dto.paiement.request.CreatePaiementRequest`
- `dev.lions.unionflow.server.api.dto.paiement.request.DeclarerPaiementManuelRequest`
- `dev.lions.unionflow.server.api.dto.paiement.request.InitierDepotEpargneRequest`
- `dev.lions.unionflow.server.api.dto.paiement.request.InitierPaiementEnLigneRequest`
### 1.4 Notification (4 classes - 0%)
- `dev.lions.unionflow.server.api.dto.notification.request.CreateNotificationRequest`
- `dev.lions.unionflow.server.api.dto.notification.request.CreateTemplateNotificationRequest`
- `dev.lions.unionflow.server.api.dto.notification.request.UpdateNotificationRequest`
- `dev.lions.unionflow.server.api.dto.notification.request.UpdateTemplateNotificationRequest`
### 1.5 Document (4 classes - 0%)
- `dev.lions.unionflow.server.api.dto.document.request.CreateDocumentRequest`
- `dev.lions.unionflow.server.api.dto.document.request.CreatePieceJointeRequest`
- `dev.lions.unionflow.server.api.dto.document.request.UpdateDocumentRequest`
- `dev.lions.unionflow.server.api.dto.document.request.UpdatePieceJointeRequest`
### 1.6 Abonnement (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.abonnement.request.CreateAbonnementRequest`
- `dev.lions.unionflow.server.api.dto.abonnement.request.UpdateAbonnementRequest`
### 1.7 Événement (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.evenement.request.CreateEvenementRequest`
- `dev.lions.unionflow.server.api.dto.evenement.request.UpdateEvenementRequest`
### 1.8 Formule Abonnement (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.formuleabonnement.request.CreateFormuleAbonnementRequest`
- `dev.lions.unionflow.server.api.dto.formuleabonnement.request.UpdateFormuleAbonnementRequest`
### 1.9 Organisation (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.organisation.request.CreateOrganisationRequest`
- `dev.lions.unionflow.server.api.dto.organisation.request.UpdateOrganisationRequest`
### 1.10 Adresse (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.adresse.request.CreateAdresseRequest`
- `dev.lions.unionflow.server.api.dto.adresse.request.UpdateAdresseRequest`
### 1.11 Membre (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.membre.request.CreateMembreRequest`
- `dev.lions.unionflow.server.api.dto.membre.request.UpdateMembreRequest`
### 1.12 Cotisation (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.cotisation.request.CreateCotisationRequest`
- `dev.lions.unionflow.server.api.dto.cotisation.request.UpdateCotisationRequest`
### 1.13 Référence (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest`
- `dev.lions.unionflow.server.api.dto.reference.request.UpdateTypeReferenceRequest`
### 1.14 Suggestion (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.suggestion.request.CreateSuggestionRequest`
- `dev.lions.unionflow.server.api.dto.suggestion.request.UpdateSuggestionRequest`
### 1.15 Finance (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.finance.request.CreateAdhesionRequest`
- `dev.lions.unionflow.server.api.dto.finance.request.UpdateAdhesionRequest`
### 1.16 User (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.user.request.CreateUserRequest`
- `dev.lions.unionflow.server.api.dto.user.request.UpdateUserRequest`
### 1.17 Configuration (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.config.request.CreateConfigurationRequest`
- `dev.lions.unionflow.server.api.dto.config.request.UpdateConfigurationRequest`
### 1.18 Ticket (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.ticket.request.CreateTicketRequest`
- `dev.lions.unionflow.server.api.dto.ticket.request.UpdateTicketRequest`
### 1.19 Rôle (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.role.request.CreateRoleRequest`
- `dev.lions.unionflow.server.api.dto.role.request.UpdateRoleRequest`
### 1.20 Admin (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.admin.request.CreateAdminRequest`
### 1.21 Favoris (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.favoris.request.CreateFavoriRequest`
---
## 2. DTOs RESPONSE (dev.lions.unionflow.server.api.dto)
### 2.1 Paiement (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.paiement.response.PaiementGatewayResponse`
### 2.2 Cotisation (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.cotisation.response.CotisationSummaryResponse`
### 2.3 Adresse (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.adresse.response.AdresseResponse`
### 2.4 Admin (1 classe - 0%)
- `dev.lions.unionflow.server.api.dto.admin.response.AdminResponse`
---
## 3. AUTRES DTOs
### 3.1 Wave (2 classes - 0%)
- `dev.lions.unionflow.server.api.dto.wave.CompteWaveDTO`
- `dev.lions.unionflow.server.api.dto.wave.TransactionWaveDTO`
---
## 4. ENUMS (dev.lions.unionflow.server.api.enums)
### 4.1 Mutuelle - Crédit (4 classes - 0%)
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutDemandeCredit`
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutEcheanceCredit`
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeCredit`
- `dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeGarantie`
### 4.2 Mutuelle - Épargne (3 classes - 0%)
- `dev.lions.unionflow.server.api.enums.mutuelle.epargne.StatutCompteEpargne`
- `dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeCompteEpargne`
- `dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne`
### 4.3 Vote (3 classes - 0%)
- `dev.lions.unionflow.server.api.enums.vote.ModeScrutin`
- `dev.lions.unionflow.server.api.enums.vote.StatutVote`
- `dev.lions.unionflow.server.api.enums.vote.TypeVote`
### 4.4 Tontine (3 classes - 0%)
- `dev.lions.unionflow.server.api.enums.tontine.FrequenceTour`
- `dev.lions.unionflow.server.api.enums.tontine.StatutTontine`
- `dev.lions.unionflow.server.api.enums.tontine.TypeTontine`
### 4.5 Ayant Droit (2 classes - 0%)
- `dev.lions.unionflow.server.api.enums.ayantdroit.StatutAyantDroit`
- `dev.lions.unionflow.server.api.enums.ayantdroit.TypeAyantDroit`
### 4.6 Agricole (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.agricole.StatutCampagneAgricole`
### 4.7 Collecte de Fonds (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.collectefonds.StatutCampagneCollecte`
### 4.8 Culte (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.culte.TypeDonReligieux`
### 4.9 ONG (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.ong.StatutProjetOng`
### 4.10 Gouvernance (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.gouvernance.NiveauEchelon`
### 4.11 Registre (1 classe - 0%)
- `dev.lions.unionflow.server.api.enums.registre.StatutAgrement`
---
## RÉCAPITULATIF PAR CATÉGORIE
| Catégorie | Nombre de classes | % du total |
|-----------|-------------------|------------|
| **DTO Requests** | **62 classes** | **68%** |
| **Enums** | **21 classes** | **23%** |
| **DTO Responses** | **4 classes** | **4%** |
| **Autres DTOs** | **2 classes** | **2%** |
| **Autres** | **2 classes** | **2%** |
| **TOTAL** | **91 classes** | **100%** |
---
## PRIORITÉS DE TEST
### P0 - Haute Priorité (Core Business)
1. **Solidarité** (8 requests) - Module métier principal
2. **Comptabilité** (8 requests) - Gestion financière critique
3. **Paiement** (4 requests + 1 response) - Transactions financières
4. **Mutuelle Crédit** (4 enums) - Microfinance core
5. **Mutuelle Épargne** (3 enums) - Microfinance core
### P1 - Priorité Moyenne (Features importantes)
6. **Notification** (4 requests) - Communication système
7. **Document** (4 requests) - Gestion documentaire
8. **Événement** (2 requests) - Gestion événementielle
9. **Organisation** (2 requests) - Structure organisationnelle
10. **Membre** (2 requests) - Gestion des utilisateurs
### P2 - Priorité Basse (Features secondaires)
11. **Abonnement** (2 requests) - Gestion des abonnements
12. **Tontine** (3 enums) - Feature spécifique
13. **Vote** (3 enums) - Feature spécifique
14. **Autres requests** (20 requests restants)
15. **Enums divers** (11 enums restants)
---
## PLAN D'ACTION
### Étape 1: Tests DTOs Request (62 classes)
- Tester getters/setters
- Tester validations Jakarta Bean Validation
- Tester méthodes equals/hashCode/toString si présentes
### Étape 2: Tests DTOs Response (4 classes)
- Tester constructeurs et builders
- Tester sérialisation JSON
### Étape 3: Tests Enums (21 classes)
- Tester valueOf() et values()
- Tester getters de valeurs
- Tester méthodes utilitaires (fromString, etc.)
### Étape 4: Tests Autres (4 classes)
- Tests spécifiques selon le type de classe
---
## TEMPLATES DE TEST RECOMMANDÉS
### Pour DTOs Request:
```java
@Test
void testCreateXxxRequest_AllFields() {
var request = new CreateXxxRequest();
// Set all fields
// Assert all fields
}
@Test
void testCreateXxxRequest_Validation() {
var request = new CreateXxxRequest();
// Test @NotNull, @Size, etc.
}
```
### Pour Enums:
```java
@Test
void testEnumValues() {
assertEquals(3, TypeXxx.values().length);
}
@Test
void testEnumValueOf() {
assertEquals(TypeXxx.VALUE1, TypeXxx.valueOf("VALUE1"));
}
```
---
**Objectif**: Atteindre **100% de couverture** sur le module unionflow-server-api.

1098
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,169 +1,169 @@
#!/usr/bin/env python3
"""Script pour analyser le rapport JaCoCo et extraire les classes sans tests."""
import os
import re
from pathlib import Path
from html.parser import HTMLParser
class JaCoCoParser(HTMLParser):
def __init__(self):
super().__init__()
self.current_package = ""
self.classes = []
self.in_tbody = False
self.current_row = {}
self.current_cell = ""
self.cell_index = 0
def handle_starttag(self, tag, attrs):
if tag == "tbody":
self.in_tbody = True
elif tag == "tr" and self.in_tbody:
self.current_row = {}
self.cell_index = 0
elif tag == "td" and self.in_tbody:
self.current_cell = ""
elif tag == "a" and self.in_tbody:
for attr, value in attrs:
if attr == "href" and value.endswith(".html"):
self.current_row["href"] = value
def handle_endtag(self, tag):
if tag == "tbody":
self.in_tbody = False
elif tag == "tr" and self.in_tbody and self.current_row:
if "coverage" in self.current_row and self.current_row["coverage"] == "0 %":
self.classes.append(self.current_row.copy())
elif tag == "td" and self.in_tbody:
self.cell_index += 1
def handle_data(self, data):
if self.in_tbody:
data = data.strip()
if data and self.cell_index == 0:
self.current_row["name"] = data
elif data and "%" in data:
self.current_row["coverage"] = data
def analyze_package(package_path, package_name):
"""Analyse un fichier index.html de package."""
index_file = os.path.join(package_path, "index.html")
if not os.path.exists(index_file):
return []
with open(index_file, 'r', encoding='utf-8') as f:
content = f.read()
parser = JaCoCoParser()
parser.current_package = package_name
parser.feed(content)
classes = []
for row in parser.classes:
if "name" in row:
classes.append({
"package": package_name,
"class": row["name"],
"coverage": row.get("coverage", "0 %")
})
return classes
def main():
jacoco_dir = Path("target/site/jacoco")
if not jacoco_dir.exists():
print(f"Erreur: Le répertoire {jacoco_dir} n'existe pas.")
print("Veuillez exécuter 'mvn clean test' pour générer le rapport JaCoCo.")
return
# Collecter tous les packages
all_classes = []
for package_dir in jacoco_dir.iterdir():
if package_dir.is_dir() and not package_dir.name.startswith("."):
package_name = package_dir.name.replace("/", ".")
classes = analyze_package(package_dir, package_name)
all_classes.extend(classes)
# Trier par package et type
dto_requests = []
dto_responses = []
dto_other = []
enums = []
others = []
for cls in all_classes:
pkg = cls["package"]
name = cls["class"]
if "enums" in pkg:
enums.append(cls)
elif "dto" in pkg:
if ".request" in pkg:
dto_requests.append(cls)
elif ".response" in pkg:
dto_responses.append(cls)
else:
dto_other.append(cls)
else:
others.append(cls)
# Afficher les résultats
print("=" * 80)
print("CLASSES SANS TESTS (0% de couverture)")
print("=" * 80)
print()
if dto_requests:
print("1. DTOs REQUEST (dev.lions.unionflow.server.api.dto)")
print("-" * 80)
for cls in sorted(dto_requests, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: DTO Request | Couverture: {cls['coverage']}")
print()
if dto_responses:
print("2. DTOs RESPONSE (dev.lions.unionflow.server.api.dto)")
print("-" * 80)
for cls in sorted(dto_responses, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: DTO Response | Couverture: {cls['coverage']}")
print()
if dto_other:
print("3. AUTRES DTOs")
print("-" * 80)
for cls in sorted(dto_other, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: DTO | Couverture: {cls['coverage']}")
print()
if enums:
print("4. ENUMS (dev.lions.unionflow.server.api.enums)")
print("-" * 80)
for cls in sorted(enums, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: Enum | Couverture: {cls['coverage']}")
print()
if others:
print("5. AUTRES")
print("-" * 80)
for cls in sorted(others, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: Autre | Couverture: {cls['coverage']}")
print()
print("=" * 80)
print(f"TOTAL: {len(all_classes)} classes sans tests")
print(f" - DTO Requests: {len(dto_requests)}")
print(f" - DTO Responses: {len(dto_responses)}")
print(f" - Autres DTOs: {len(dto_other)}")
print(f" - Enums: {len(enums)}")
print(f" - Autres: {len(others)}")
print("=" * 80)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Script pour analyser le rapport JaCoCo et extraire les classes sans tests."""
import os
import re
from pathlib import Path
from html.parser import HTMLParser
class JaCoCoParser(HTMLParser):
def __init__(self):
super().__init__()
self.current_package = ""
self.classes = []
self.in_tbody = False
self.current_row = {}
self.current_cell = ""
self.cell_index = 0
def handle_starttag(self, tag, attrs):
if tag == "tbody":
self.in_tbody = True
elif tag == "tr" and self.in_tbody:
self.current_row = {}
self.cell_index = 0
elif tag == "td" and self.in_tbody:
self.current_cell = ""
elif tag == "a" and self.in_tbody:
for attr, value in attrs:
if attr == "href" and value.endswith(".html"):
self.current_row["href"] = value
def handle_endtag(self, tag):
if tag == "tbody":
self.in_tbody = False
elif tag == "tr" and self.in_tbody and self.current_row:
if "coverage" in self.current_row and self.current_row["coverage"] == "0 %":
self.classes.append(self.current_row.copy())
elif tag == "td" and self.in_tbody:
self.cell_index += 1
def handle_data(self, data):
if self.in_tbody:
data = data.strip()
if data and self.cell_index == 0:
self.current_row["name"] = data
elif data and "%" in data:
self.current_row["coverage"] = data
def analyze_package(package_path, package_name):
"""Analyse un fichier index.html de package."""
index_file = os.path.join(package_path, "index.html")
if not os.path.exists(index_file):
return []
with open(index_file, 'r', encoding='utf-8') as f:
content = f.read()
parser = JaCoCoParser()
parser.current_package = package_name
parser.feed(content)
classes = []
for row in parser.classes:
if "name" in row:
classes.append({
"package": package_name,
"class": row["name"],
"coverage": row.get("coverage", "0 %")
})
return classes
def main():
jacoco_dir = Path("target/site/jacoco")
if not jacoco_dir.exists():
print(f"Erreur: Le répertoire {jacoco_dir} n'existe pas.")
print("Veuillez exécuter 'mvn clean test' pour générer le rapport JaCoCo.")
return
# Collecter tous les packages
all_classes = []
for package_dir in jacoco_dir.iterdir():
if package_dir.is_dir() and not package_dir.name.startswith("."):
package_name = package_dir.name.replace("/", ".")
classes = analyze_package(package_dir, package_name)
all_classes.extend(classes)
# Trier par package et type
dto_requests = []
dto_responses = []
dto_other = []
enums = []
others = []
for cls in all_classes:
pkg = cls["package"]
name = cls["class"]
if "enums" in pkg:
enums.append(cls)
elif "dto" in pkg:
if ".request" in pkg:
dto_requests.append(cls)
elif ".response" in pkg:
dto_responses.append(cls)
else:
dto_other.append(cls)
else:
others.append(cls)
# Afficher les résultats
print("=" * 80)
print("CLASSES SANS TESTS (0% de couverture)")
print("=" * 80)
print()
if dto_requests:
print("1. DTOs REQUEST (dev.lions.unionflow.server.api.dto)")
print("-" * 80)
for cls in sorted(dto_requests, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: DTO Request | Couverture: {cls['coverage']}")
print()
if dto_responses:
print("2. DTOs RESPONSE (dev.lions.unionflow.server.api.dto)")
print("-" * 80)
for cls in sorted(dto_responses, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: DTO Response | Couverture: {cls['coverage']}")
print()
if dto_other:
print("3. AUTRES DTOs")
print("-" * 80)
for cls in sorted(dto_other, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: DTO | Couverture: {cls['coverage']}")
print()
if enums:
print("4. ENUMS (dev.lions.unionflow.server.api.enums)")
print("-" * 80)
for cls in sorted(enums, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: Enum | Couverture: {cls['coverage']}")
print()
if others:
print("5. AUTRES")
print("-" * 80)
for cls in sorted(others, key=lambda x: (x["package"], x["class"])):
print(f" {cls['package']}.{cls['class']}")
print(f" Type: Autre | Couverture: {cls['coverage']}")
print()
print("=" * 80)
print(f"TOTAL: {len(all_classes)} classes sans tests")
print(f" - DTO Requests: {len(dto_requests)}")
print(f" - DTO Responses: {len(dto_responses)}")
print(f" - Autres DTOs: {len(dto_other)}")
print(f" - Enums: {len(enums)}")
print(f" - Autres: {len(others)}")
print("=" * 80)
if __name__ == "__main__":
main()

View File

@@ -1,359 +1,359 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Configuration Checkstyle pour UnionFlow
Basée sur Google Java Style Guide avec adaptations pour UnionFlow
Objectif : 100% de conformité (zéro violation)
@author UnionFlow Team
@version 1.0
@since 2025-01-10
-->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Suppressions pour les fichiers générés -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
default="checkstyle-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Vérifications au niveau des fichiers -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="NewlineAtEndOfFile"/>
<!-- Vérifications au niveau des arbres syntaxiques -->
<module name="TreeWalker">
<!-- Annotations -->
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="AnnotationUseStyle"/>
<module name="MissingOverride"/>
<!-- Blocs -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
</module>
<!-- Conception de classe -->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="OneTopLevelClass"/>
<module name="VisibilityModifier">
<property name="protectedAllowed" value="true"/>
<property name="packageAllowed" value="true"/>
</module>
<!-- Codage -->
<module name="ArrayTrailingComma"/>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="CovariantEquals"/>
<module name="DeclarationOrder"/>
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<module name="ExplicitInitialization"/>
<module name="FallThrough"/>
<module name="IllegalInstantiation"/>
<module name="IllegalToken"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format" value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="InnerAssignment"/>
<module name="MagicNumber">
<property name="ignoreNumbers" value="-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000"/>
<property name="ignoreHashCodeMethod" value="true"/>
<property name="ignoreAnnotation" value="true"/>
<property name="ignoreFieldDeclaration" value="true"/>
</module>
<module name="MissingSwitchDefault"/>
<module name="ModifiedControlVariable"/>
<module name="MultipleStringLiterals">
<property name="allowedDuplicates" value="3"/>
</module>
<module name="MultipleVariableDeclarations"/>
<module name="NoClone"/>
<module name="NoFinalizer"/>
<module name="OneStatementPerLine"/>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="PackageDeclaration"/>
<module name="ParameterAssignment"/>
<module name="RequireThis">
<property name="checkFields" value="false"/>
<property name="checkMethods" value="false"/>
</module>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="UnnecessaryParentheses"/>
<module name="VariableDeclarationUsageDistance"/>
<!-- Imports -->
<module name="AvoidStarImport"/>
<module name="AvoidStaticImport">
<property name="excludes" value="org.assertj.core.api.Assertions.*,org.junit.jupiter.api.Assertions.*,org.mockito.Mockito.*"/>
</module>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
</module>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Javadoc -->
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="InvalidJavadocPosition"/>
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="false"/>
<property name="allowMissingReturnTag" value="false"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="JavadocParagraph"/>
<module name="JavadocStyle"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="JavadocType"/>
<module name="MissingJavadocMethod">
<property name="minLineCount" value="2"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="MissingJavadocType"/>
<module name="NonEmptyAtclauseDescription"/>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<!-- Métriques -->
<module name="BooleanExpressionComplexity">
<property name="max" value="7"/>
</module>
<module name="ClassDataAbstractionCoupling">
<property name="max" value="15"/>
</module>
<module name="ClassFanOutComplexity">
<property name="max" value="25"/>
</module>
<module name="CyclomaticComplexity">
<property name="max" value="15"/>
</module>
<module name="JavaNCSS">
<property name="methodMaximum" value="80"/>
<property name="classMaximum" value="2000"/>
</module>
<module name="NPathComplexity">
<property name="max" value="200"/>
</module>
<!-- Divers -->
<module name="ArrayTypeStyle"/>
<module name="CommentsIndentation"/>
<module name="Indentation">
<property name="basicOffset" value="4"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="4"/>
<property name="throwsIndent" value="8"/>
<property name="lineWrappingIndentation" value="8"/>
<property name="arrayInitIndent" value="4"/>
</module>
<module name="OuterTypeFilename"/>
<module name="TodoComment">
<property name="format" value="(TODO)|(FIXME)"/>
</module>
<module name="TrailingComment"/>
<module name="UncommentedMain">
<property name="excludedClasses" value="\.Main$"/>
</module>
<module name="UpperEll"/>
<!-- Modificateurs -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Conventions de nommage -->
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="4"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF, PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF"/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ConstantName"/>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern" value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="StaticVariableName"/>
<module name="TypeName">
<message key="name.invalidPattern" value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<!-- Taille -->
<module name="AnonInnerLength">
<property name="max" value="30"/>
</module>
<module name="ExecutableStatementCount">
<property name="max" value="50"/>
</module>
<module name="LineLength">
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="MethodCount">
<property name="maxTotal" value="50"/>
<property name="maxPrivate" value="30"/>
<property name="maxPackage" value="30"/>
<property name="maxProtected" value="30"/>
<property name="maxPublic" value="30"/>
</module>
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
<property name="max" value="100"/>
</module>
<module name="OuterTypeNumber"/>
<module name="ParameterNumber">
<property name="max" value="8"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
</module>
<!-- Espaces blancs -->
<module name="EmptyForIteratorPad"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="GenericWhitespace">
<message key="ws.followed" value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded" value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow" value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded" value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="MethodParamPad"/>
<module name="NoLineWrap"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<module name="ParenPad"/>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed" value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded" value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
</module>
</module>
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Configuration Checkstyle pour UnionFlow
Basée sur Google Java Style Guide avec adaptations pour UnionFlow
Objectif : 100% de conformité (zéro violation)
@author UnionFlow Team
@version 1.0
@since 2025-01-10
-->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Suppressions pour les fichiers générés -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
default="checkstyle-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Vérifications au niveau des fichiers -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="NewlineAtEndOfFile"/>
<!-- Vérifications au niveau des arbres syntaxiques -->
<module name="TreeWalker">
<!-- Annotations -->
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="AnnotationUseStyle"/>
<module name="MissingOverride"/>
<!-- Blocs -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
</module>
<!-- Conception de classe -->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="OneTopLevelClass"/>
<module name="VisibilityModifier">
<property name="protectedAllowed" value="true"/>
<property name="packageAllowed" value="true"/>
</module>
<!-- Codage -->
<module name="ArrayTrailingComma"/>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="CovariantEquals"/>
<module name="DeclarationOrder"/>
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<module name="ExplicitInitialization"/>
<module name="FallThrough"/>
<module name="IllegalInstantiation"/>
<module name="IllegalToken"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format" value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="InnerAssignment"/>
<module name="MagicNumber">
<property name="ignoreNumbers" value="-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000"/>
<property name="ignoreHashCodeMethod" value="true"/>
<property name="ignoreAnnotation" value="true"/>
<property name="ignoreFieldDeclaration" value="true"/>
</module>
<module name="MissingSwitchDefault"/>
<module name="ModifiedControlVariable"/>
<module name="MultipleStringLiterals">
<property name="allowedDuplicates" value="3"/>
</module>
<module name="MultipleVariableDeclarations"/>
<module name="NoClone"/>
<module name="NoFinalizer"/>
<module name="OneStatementPerLine"/>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="PackageDeclaration"/>
<module name="ParameterAssignment"/>
<module name="RequireThis">
<property name="checkFields" value="false"/>
<property name="checkMethods" value="false"/>
</module>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="UnnecessaryParentheses"/>
<module name="VariableDeclarationUsageDistance"/>
<!-- Imports -->
<module name="AvoidStarImport"/>
<module name="AvoidStaticImport">
<property name="excludes" value="org.assertj.core.api.Assertions.*,org.junit.jupiter.api.Assertions.*,org.mockito.Mockito.*"/>
</module>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
</module>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Javadoc -->
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="InvalidJavadocPosition"/>
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="false"/>
<property name="allowMissingReturnTag" value="false"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="JavadocParagraph"/>
<module name="JavadocStyle"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="JavadocType"/>
<module name="MissingJavadocMethod">
<property name="minLineCount" value="2"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="MissingJavadocType"/>
<module name="NonEmptyAtclauseDescription"/>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<!-- Métriques -->
<module name="BooleanExpressionComplexity">
<property name="max" value="7"/>
</module>
<module name="ClassDataAbstractionCoupling">
<property name="max" value="15"/>
</module>
<module name="ClassFanOutComplexity">
<property name="max" value="25"/>
</module>
<module name="CyclomaticComplexity">
<property name="max" value="15"/>
</module>
<module name="JavaNCSS">
<property name="methodMaximum" value="80"/>
<property name="classMaximum" value="2000"/>
</module>
<module name="NPathComplexity">
<property name="max" value="200"/>
</module>
<!-- Divers -->
<module name="ArrayTypeStyle"/>
<module name="CommentsIndentation"/>
<module name="Indentation">
<property name="basicOffset" value="4"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="4"/>
<property name="throwsIndent" value="8"/>
<property name="lineWrappingIndentation" value="8"/>
<property name="arrayInitIndent" value="4"/>
</module>
<module name="OuterTypeFilename"/>
<module name="TodoComment">
<property name="format" value="(TODO)|(FIXME)"/>
</module>
<module name="TrailingComment"/>
<module name="UncommentedMain">
<property name="excludedClasses" value="\.Main$"/>
</module>
<module name="UpperEll"/>
<!-- Modificateurs -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Conventions de nommage -->
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="4"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF, PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF"/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ConstantName"/>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern" value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="StaticVariableName"/>
<module name="TypeName">
<message key="name.invalidPattern" value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<!-- Taille -->
<module name="AnonInnerLength">
<property name="max" value="30"/>
</module>
<module name="ExecutableStatementCount">
<property name="max" value="50"/>
</module>
<module name="LineLength">
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="MethodCount">
<property name="maxTotal" value="50"/>
<property name="maxPrivate" value="30"/>
<property name="maxPackage" value="30"/>
<property name="maxProtected" value="30"/>
<property name="maxPublic" value="30"/>
</module>
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
<property name="max" value="100"/>
</module>
<module name="OuterTypeNumber"/>
<module name="ParameterNumber">
<property name="max" value="8"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
</module>
<!-- Espaces blancs -->
<module name="EmptyForIteratorPad"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="GenericWhitespace">
<message key="ws.followed" value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded" value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow" value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded" value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="MethodParamPad"/>
<module name="NoLineWrap"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<module name="ParenPad"/>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed" value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded" value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
</module>
</module>

16414
docs/openapi.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

View File

@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>UnionFlow - Parent</name>
<description>Plateforme complète de gestion d'union — POM parent partagé</description>
<distributionManagement>
<repository>
<id>gitea-lionsdev</id>
<url>https://git.lions.dev/api/packages/lionsdev/maven</url>
</repository>
<snapshotRepository>
<id>gitea-lionsdev</id>
<url>https://git.lions.dev/api/packages/lionsdev/maven</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>gitea-lionsdev</id>
<url>https://git.lions.dev/api/packages/lionsdev/maven</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.version>3.15.1</quarkus.platform.version>
<lombok.version>1.18.34</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-server-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
<parameters>true</parameters>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

55
pom.xml
View File

@@ -4,36 +4,50 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-parent</artifactId>
<version>1.0.0</version>
<relativePath>parent-pom.xml</relativePath>
</parent>
<groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-server-api</artifactId>
<version>1.0.10</version>
<packaging>jar</packaging>
<name>UnionFlow Server API</name>
<description>API définitions pour le serveur UnionFlow</description>
<distributionManagement>
<repository>
<id>gitea-lionsdev</id>
<url>https://git.lions.dev/api/packages/lionsdev/maven</url>
</repository>
</distributionManagement>
<repositories>
<repository>
<id>gitea-lionsdev</id>
<url>https://git.lions.dev/api/packages/lionsdev/maven</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>21</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.release>${java.version}</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<quarkus.platform.version>3.15.1</quarkus.platform.version>
<jackson.version>2.17.0</jackson.version>
<quarkus.platform.version>3.27.3</quarkus.platform.version>
<lombok.version>1.18.38</lombok.version>
<jackson.version>2.18.2</jackson.version>
<validation-api.version>3.0.2</validation-api.version>
<microprofile-openapi.version>3.1.1</microprofile-openapi.version>
<!-- Versions des plugins de qualité -->
<jacoco.version>0.8.11</jacoco.version>
<jacoco.version>0.8.12</jacoco.version>
<checkstyle.version>10.12.4</checkstyle.version>
<maven-checkstyle-plugin.version>3.3.1</maven-checkstyle-plugin.version>
<junit.version>5.10.1</junit.version>
<mockito.version>5.7.0</mockito.version>
<assertj.version>3.24.2</assertj.version>
<junit.version>5.11.4</junit.version>
<mockito.version>5.14.2</mockito.version>
<assertj.version>3.27.3</assertj.version>
<!-- Seuils de couverture Jacoco - 100% obligatoire -->
<jacoco.line.coverage.minimum>1.00</jacoco.line.coverage.minimum>
@@ -115,7 +129,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
@@ -126,16 +140,15 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<release>21</release>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<version>1.18.36</version>
</path>
</annotationProcessorPaths>
</configuration>

View File

@@ -1,28 +1,15 @@
@echo off
REM Publie le parent pom + server-api sur le Gitea Package Registry
REM Publie unionflow-server-api sur le Gitea Package Registry
REM Usage : script\publish-api.bat
REM Depuis : n'importe où dans le repo server-api
REM Prérequis: credentials dans %USERPROFILE%\.m2\settings.xml (server id: gitea-lionsdev)
set REGISTRY_URL=https://git.lions.dev/api/packages/lionsdev/maven
set REGISTRY_ID=gitea-lionsdev
REM Depuis : n'importe ou dans le repo server-api
REM Prerequis: credentials dans %USERPROFILE%\.m2\settings.xml (server id: gitea-lionsdev)
REM
REM Note : unionflow-parent n'existe plus (modules autonomes depuis Quarkus 3.27.3).
cd /d "%~dp0\.."
echo.
echo [1/2] Publication du parent pom...
call mvn deploy:deploy-file ^
-DgroupId=dev.lions.unionflow ^
-DartifactId=unionflow-parent ^
-Dversion=1.0.0 ^
-Dpackaging=pom ^
-Dfile=parent-pom.xml ^
-DrepositoryId=%REGISTRY_ID% ^
-Durl=%REGISTRY_URL%
if errorlevel 409 echo [WARN] Parent pom deja publie pour cette version, on continue.
echo.
echo [2/2] Publication du server-api...
echo Publication du server-api...
call mvn deploy -DskipTests
if errorlevel 409 echo [WARN] Server-api deja publie - incrementer la version pour republier.

View File

@@ -1,30 +1,18 @@
#!/bin/bash
# Publie le parent pom + server-api sur le Gitea Package Registry
# Publie unionflow-server-api sur le Gitea Package Registry
# Usage : ./script/publish-api.sh
# Depuis : n'importe où dans le repo server-api
# Prérequis: credentials dans ~/.m2/settings.xml (server id: gitea-lionsdev)
#
# Note : unionflow-parent n'existe plus (les 3 modules sont autonomes
# depuis la migration Quarkus 3.27.3). Ne publier que le module server-api.
set -e
REGISTRY_URL="https://git.lions.dev/api/packages/lionsdev/maven"
REGISTRY_ID="gitea-lionsdev"
cd "$(dirname "$0")/.."
echo ""
echo "[1/2] Publication du parent pom..."
mvn deploy:deploy-file \
-DgroupId=dev.lions.unionflow \
-DartifactId=unionflow-parent \
-Dversion=1.0.0 \
-Dpackaging=pom \
-Dfile=parent-pom.xml \
-DrepositoryId="${REGISTRY_ID}" \
-Durl="${REGISTRY_URL}" \
|| echo "[WARN] Parent pom déjà publié pour cette version (409), on continue."
echo ""
echo "[2/2] Publication du server-api..."
echo "Publication du server-api..."
mvn deploy -DskipTests \
|| echo "[WARN] Server-api déjà publié pour cette version (409) - incrémenter la version pour republier."

View File

@@ -1,82 +1,82 @@
package dev.lions.unionflow.server.api.dto.abonnement.request;
import dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'un abonnement.
*/
@Builder
public record CreateAbonnementRequest(
@NotBlank(message = "Le numéro de référence est obligatoire") @Pattern(regexp = "^ABO-\\d{4}-[A-Z0-9]{8}$", message = "Format de référence invalide (ABO-YYYY-XXXXXXXX)") String numeroReference,
@NotNull(message = "L'identifiant de l'organisation est obligatoire") UUID organisationId,
String nomOrganisation,
@NotNull(message = "L'identifiant de la formule est obligatoire") UUID formulaireId,
String codeFormule,
String nomFormule,
TypeFormule typeFormule,
@NotNull(message = "Le statut est obligatoire") StatutAbonnement statut,
@NotNull(message = "Le type d'abonnement est obligatoire") TypePeriodeAbonnement typeAbonnement,
@NotNull(message = "La date de début est obligatoire") LocalDate dateDebut,
@Future(message = "La date de fin doit être dans le futur") LocalDate dateFin,
LocalDate dateProchainePeriode,
@NotNull(message = "Le montant est obligatoire") @DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montant,
@NotBlank(message = "La devise est obligatoire") @Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres") String devise,
@DecimalMin(value = "0.0", message = "La remise doit être positive") @DecimalMin(value = "100.0", message = "La remise ne peut pas dépasser 100%") BigDecimal remise,
@DecimalMin(value = "0.0", message = "Le montant final doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montantFinal,
Boolean renouvellementAutomatique,
Boolean periodeEssaiUtilisee,
LocalDate dateFinEssai,
Integer maxMembres,
Integer nombreMembresActuels,
BigDecimal espaceStockageGB,
BigDecimal espaceStockageUtilise,
Boolean supportTechnique,
String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
UUID responsableId,
String nomResponsable,
String emailResponsable,
String telephoneResponsable,
@Pattern(regexp = "^(WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|VIREMENT|CHEQUE|AUTRE)$", message = "Mode de paiement invalide") String modePaiementPrefere,
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide") String numeroPaiementMobile,
@Size(max = 5000, message = "L'historique ne peut pas dépasser 5000 caractères") String historiquePaiements,
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères") String notes,
Boolean alertesActivees,
Boolean notificationsEmail,
Boolean notificationsSMS) {
}
package dev.lions.unionflow.server.api.dto.abonnement.request;
import dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'un abonnement.
*/
@Builder
public record CreateAbonnementRequest(
@NotBlank(message = "Le numéro de référence est obligatoire") @Pattern(regexp = "^ABO-\\d{4}-[A-Z0-9]{8}$", message = "Format de référence invalide (ABO-YYYY-XXXXXXXX)") String numeroReference,
@NotNull(message = "L'identifiant de l'organisation est obligatoire") UUID organisationId,
String nomOrganisation,
@NotNull(message = "L'identifiant de la formule est obligatoire") UUID formulaireId,
String codeFormule,
String nomFormule,
TypeFormule typeFormule,
@NotNull(message = "Le statut est obligatoire") StatutAbonnement statut,
@NotNull(message = "Le type d'abonnement est obligatoire") TypePeriodeAbonnement typeAbonnement,
@NotNull(message = "La date de début est obligatoire") LocalDate dateDebut,
@Future(message = "La date de fin doit être dans le futur") LocalDate dateFin,
LocalDate dateProchainePeriode,
@NotNull(message = "Le montant est obligatoire") @DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montant,
@NotBlank(message = "La devise est obligatoire") @Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres") String devise,
@DecimalMin(value = "0.0", message = "La remise doit être positive") @DecimalMin(value = "100.0", message = "La remise ne peut pas dépasser 100%") BigDecimal remise,
@DecimalMin(value = "0.0", message = "Le montant final doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montantFinal,
Boolean renouvellementAutomatique,
Boolean periodeEssaiUtilisee,
LocalDate dateFinEssai,
Integer maxMembres,
Integer nombreMembresActuels,
BigDecimal espaceStockageGB,
BigDecimal espaceStockageUtilise,
Boolean supportTechnique,
String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
UUID responsableId,
String nomResponsable,
String emailResponsable,
String telephoneResponsable,
@Pattern(regexp = "^(WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|VIREMENT|CHEQUE|AUTRE)$", message = "Mode de paiement invalide") String modePaiementPrefere,
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide") String numeroPaiementMobile,
@Size(max = 5000, message = "L'historique ne peut pas dépasser 5000 caractères") String historiquePaiements,
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères") String notes,
Boolean alertesActivees,
Boolean notificationsEmail,
Boolean notificationsSMS) {
}

View File

@@ -1,77 +1,77 @@
package dev.lions.unionflow.server.api.dto.abonnement.request;
import dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de mise à jour d'un abonnement.
*/
@Builder
public record UpdateAbonnementRequest(
@Pattern(regexp = "^ABO-\\d{4}-[A-Z0-9]{8}$", message = "Format de référence invalide (ABO-YYYY-XXXXXXXX)") String numeroReference,
UUID organisationId,
String nomOrganisation,
UUID formulaireId,
String codeFormule,
String nomFormule,
TypeFormule typeFormule,
StatutAbonnement statut,
TypePeriodeAbonnement typeAbonnement,
LocalDate dateDebut,
@Future(message = "La date de fin doit être dans le futur") LocalDate dateFin,
LocalDate dateProchainePeriode,
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montant,
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres") String devise,
@DecimalMin(value = "0.0", message = "La remise doit être positive") @DecimalMin(value = "100.0", message = "La remise ne peut pas dépasser 100%") BigDecimal remise,
@DecimalMin(value = "0.0", message = "Le montant final doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montantFinal,
Boolean renouvellementAutomatique,
Boolean periodeEssaiUtilisee,
LocalDate dateFinEssai,
Integer maxMembres,
Integer nombreMembresActuels,
BigDecimal espaceStockageGB,
BigDecimal espaceStockageUtilise,
Boolean supportTechnique,
String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
UUID responsableId,
String nomResponsable,
String emailResponsable,
String telephoneResponsable,
@Pattern(regexp = "^(WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|VIREMENT|CHEQUE|AUTRE)$", message = "Mode de paiement invalide") String modePaiementPrefere,
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide") String numeroPaiementMobile,
@Size(max = 5000, message = "L'historique ne peut pas dépasser 5000 caractères") String historiquePaiements,
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères") String notes,
Boolean alertesActivees,
Boolean notificationsEmail,
Boolean notificationsSMS) {
}
package dev.lions.unionflow.server.api.dto.abonnement.request;
import dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de mise à jour d'un abonnement.
*/
@Builder
public record UpdateAbonnementRequest(
@Pattern(regexp = "^ABO-\\d{4}-[A-Z0-9]{8}$", message = "Format de référence invalide (ABO-YYYY-XXXXXXXX)") String numeroReference,
UUID organisationId,
String nomOrganisation,
UUID formulaireId,
String codeFormule,
String nomFormule,
TypeFormule typeFormule,
StatutAbonnement statut,
TypePeriodeAbonnement typeAbonnement,
LocalDate dateDebut,
@Future(message = "La date de fin doit être dans le futur") LocalDate dateFin,
LocalDate dateProchainePeriode,
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montant,
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres") String devise,
@DecimalMin(value = "0.0", message = "La remise doit être positive") @DecimalMin(value = "100.0", message = "La remise ne peut pas dépasser 100%") BigDecimal remise,
@DecimalMin(value = "0.0", message = "Le montant final doit être positif") @Digits(integer = 10, fraction = 2, message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales") BigDecimal montantFinal,
Boolean renouvellementAutomatique,
Boolean periodeEssaiUtilisee,
LocalDate dateFinEssai,
Integer maxMembres,
Integer nombreMembresActuels,
BigDecimal espaceStockageGB,
BigDecimal espaceStockageUtilise,
Boolean supportTechnique,
String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
UUID responsableId,
String nomResponsable,
String emailResponsable,
String telephoneResponsable,
@Pattern(regexp = "^(WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|VIREMENT|CHEQUE|AUTRE)$", message = "Mode de paiement invalide") String modePaiementPrefere,
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide") String numeroPaiementMobile,
@Size(max = 5000, message = "L'historique ne peut pas dépasser 5000 caractères") String historiquePaiements,
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères") String notes,
Boolean alertesActivees,
Boolean notificationsEmail,
Boolean notificationsSMS) {
}

View File

@@ -1,133 +1,133 @@
package dev.lions.unionflow.server.api.dto.abonnement.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée pour un abonnement.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AbonnementResponse extends BaseResponse {
private String numeroReference;
private UUID organisationId;
private String nomOrganisation;
private UUID formulaireId;
private String codeFormule;
private String nomFormule;
private TypeFormule typeFormule;
private StatutAbonnement statut;
private TypePeriodeAbonnement typeAbonnement;
private LocalDate dateDebut;
private LocalDate dateFin;
private LocalDate dateProchainePeriode;
private BigDecimal montant;
private String devise;
private BigDecimal remise;
private BigDecimal montantFinal;
private Boolean renouvellementAutomatique;
private Boolean periodeEssaiUtilisee;
private LocalDate dateFinEssai;
private Integer maxMembres;
private Integer nombreMembresActuels;
private BigDecimal espaceStockageGB;
private BigDecimal espaceStockageUtilise;
private Boolean supportTechnique;
private String niveauSupport;
private Boolean fonctionnalitesAvancees;
private Boolean apiAccess;
private Boolean rapportsPersonnalises;
private Boolean integrationsTierces;
private LocalDateTime dateDerniereUtilisation;
private Integer connexionsCeMois;
private UUID responsableId;
private String nomResponsable;
private String emailResponsable;
private String telephoneResponsable;
private String modePaiementPrefere;
private String numeroPaiementMobile;
private String historiquePaiements;
private String notes;
private Boolean alertesActivees;
private Boolean notificationsEmail;
private Boolean notificationsSMS;
private LocalDateTime dateSuspension;
private String raisonSuspension;
private LocalDateTime dateAnnulation;
private String raisonAnnulation;
// === MÉTHODES UTILITAIRES ===
public boolean isActive() {
return StatutAbonnement.ACTIF.equals(statut);
}
public boolean isExpire() {
return StatutAbonnement.EXPIRE.equals(statut) ||
(dateFin != null && dateFin.isBefore(LocalDate.now()));
}
public boolean isSuspendu() {
return StatutAbonnement.SUSPENDU.equals(statut);
}
public int getMembresRestants() {
if (maxMembres == null) return 0;
int actuels = nombreMembresActuels != null ? nombreMembresActuels : 0;
return Math.max(0, maxMembres - actuels);
}
public boolean isQuotaAtteint() {
if (maxMembres == null) return false;
int actuels = nombreMembresActuels != null ? nombreMembresActuels : 0;
return actuels >= maxMembres;
}
public int getPourcentageUtilisation() {
if (maxMembres == null || maxMembres == 0) return 0;
int actuels = nombreMembresActuels != null ? nombreMembresActuels : 0;
return (actuels * 100) / maxMembres;
}
public long getJoursRestants() {
if (dateFin == null) return -1;
LocalDate maintenant = LocalDate.now();
if (maintenant.isAfter(dateFin)) return 0;
return java.time.temporal.ChronoUnit.DAYS.between(maintenant, dateFin);
}
public boolean isExpirationProche() {
long joursRestants = getJoursRestants();
return joursRestants >= 0 && joursRestants <= 30;
}
public boolean peutEtreRenouvele() {
return Boolean.TRUE.equals(renouvellementAutomatique) && !isExpire();
}
public String getStatutLibelle() {
return statut != null ? statut.name() : "INCONNU";
}
}
package dev.lions.unionflow.server.api.dto.abonnement.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.abonnement.StatutAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée pour un abonnement.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AbonnementResponse extends BaseResponse {
private String numeroReference;
private UUID organisationId;
private String nomOrganisation;
private UUID formulaireId;
private String codeFormule;
private String nomFormule;
private TypeFormule typeFormule;
private StatutAbonnement statut;
private TypePeriodeAbonnement typeAbonnement;
private LocalDate dateDebut;
private LocalDate dateFin;
private LocalDate dateProchainePeriode;
private BigDecimal montant;
private String devise;
private BigDecimal remise;
private BigDecimal montantFinal;
private Boolean renouvellementAutomatique;
private Boolean periodeEssaiUtilisee;
private LocalDate dateFinEssai;
private Integer maxMembres;
private Integer nombreMembresActuels;
private BigDecimal espaceStockageGB;
private BigDecimal espaceStockageUtilise;
private Boolean supportTechnique;
private String niveauSupport;
private Boolean fonctionnalitesAvancees;
private Boolean apiAccess;
private Boolean rapportsPersonnalises;
private Boolean integrationsTierces;
private LocalDateTime dateDerniereUtilisation;
private Integer connexionsCeMois;
private UUID responsableId;
private String nomResponsable;
private String emailResponsable;
private String telephoneResponsable;
private String modePaiementPrefere;
private String numeroPaiementMobile;
private String historiquePaiements;
private String notes;
private Boolean alertesActivees;
private Boolean notificationsEmail;
private Boolean notificationsSMS;
private LocalDateTime dateSuspension;
private String raisonSuspension;
private LocalDateTime dateAnnulation;
private String raisonAnnulation;
// === MÉTHODES UTILITAIRES ===
public boolean isActive() {
return StatutAbonnement.ACTIF.equals(statut);
}
public boolean isExpire() {
return StatutAbonnement.EXPIRE.equals(statut) ||
(dateFin != null && dateFin.isBefore(LocalDate.now()));
}
public boolean isSuspendu() {
return StatutAbonnement.SUSPENDU.equals(statut);
}
public int getMembresRestants() {
if (maxMembres == null) return 0;
int actuels = nombreMembresActuels != null ? nombreMembresActuels : 0;
return Math.max(0, maxMembres - actuels);
}
public boolean isQuotaAtteint() {
if (maxMembres == null) return false;
int actuels = nombreMembresActuels != null ? nombreMembresActuels : 0;
return actuels >= maxMembres;
}
public int getPourcentageUtilisation() {
if (maxMembres == null || maxMembres == 0) return 0;
int actuels = nombreMembresActuels != null ? nombreMembresActuels : 0;
return (actuels * 100) / maxMembres;
}
public long getJoursRestants() {
if (dateFin == null) return -1;
LocalDate maintenant = LocalDate.now();
if (maintenant.isAfter(dateFin)) return 0;
return java.time.temporal.ChronoUnit.DAYS.between(maintenant, dateFin);
}
public boolean isExpirationProche() {
long joursRestants = getJoursRestants();
return joursRestants >= 0 && joursRestants <= 30;
}
public boolean peutEtreRenouvele() {
return Boolean.TRUE.equals(renouvellementAutomatique) && !isExpire();
}
public String getStatutLibelle() {
return statut != null ? statut.name() : "INCONNU";
}
}

View File

@@ -1,29 +1,29 @@
package dev.lions.unionflow.server.api.dto.admin.request;
import java.time.LocalDateTime;
import lombok.Builder;
/**
* Requête de création d'un log d'audit.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateAuditLogRequest(
String typeAction,
String severite,
String utilisateur,
String role,
String module,
String description,
String details,
String ipAddress,
String userAgent,
String sessionId,
LocalDateTime dateHeure,
String donneesAvant,
String donneesApres,
String entiteId,
String entiteType) {
}
package dev.lions.unionflow.server.api.dto.admin.request;
import java.time.LocalDateTime;
import lombok.Builder;
/**
* Requête de création d'un log d'audit.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateAuditLogRequest(
String typeAction,
String severite,
String utilisateur,
String role,
String module,
String description,
String details,
String ipAddress,
String userAgent,
String sessionId,
LocalDateTime dateHeure,
String donneesAvant,
String donneesApres,
String entiteId,
String entiteType) {
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.admin.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
/**
* Réponse pour les logs d'audit.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
public class AuditLogResponse extends BaseResponse {
private String typeAction;
private String severite;
private String utilisateur;
private String role;
private String module;
private String description;
private String details;
private String ipAddress;
private String userAgent;
private String sessionId;
private LocalDateTime dateHeure;
private String donneesAvant;
private String donneesApres;
private String entiteId;
private String entiteType;
}
package dev.lions.unionflow.server.api.dto.admin.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
/**
* Réponse pour les logs d'audit.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
public class AuditLogResponse extends BaseResponse {
private String typeAction;
private String severite;
private String utilisateur;
private String role;
private String module;
private String description;
private String details;
private String ipAddress;
private String userAgent;
private String sessionId;
private LocalDateTime dateHeure;
private String donneesAvant;
private String donneesApres;
private String entiteId;
private String entiteType;
}

View File

@@ -1,43 +1,43 @@
package dev.lions.unionflow.server.api.dto.adresse.request;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.UUID;
/**
* Requete de création d'une adresse.
*
* @author UnionFlow Team
* @version 3.0
*/
public record CreateAdresseRequest(
@NotBlank(message = "Le type d'adresse est obligatoire") String typeAdresse, // Code depuis types_reference
@NotBlank(message = "L'adresse est obligatoire") String adresse,
String complementAdresse,
String codePostal,
@NotBlank(message = "La ville est obligatoire") String ville,
String region,
@NotBlank(message = "Le pays est obligatoire") String pays,
@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) BigDecimal latitude, // Optionnel
@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) BigDecimal longitude, // Optionnel
@NotNull(message = "L'indicateur principale est obligatoire") Boolean principale,
String libelle,
String notes,
UUID organisationId, // Exclusive: soit organisationId, soit membreId, soit evenementId
UUID membreId,
UUID evenementId) {
}
package dev.lions.unionflow.server.api.dto.adresse.request;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.UUID;
/**
* Requete de création d'une adresse.
*
* @author UnionFlow Team
* @version 3.0
*/
public record CreateAdresseRequest(
@NotBlank(message = "Le type d'adresse est obligatoire") String typeAdresse, // Code depuis types_reference
@NotBlank(message = "L'adresse est obligatoire") String adresse,
String complementAdresse,
String codePostal,
@NotBlank(message = "La ville est obligatoire") String ville,
String region,
@NotBlank(message = "Le pays est obligatoire") String pays,
@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) BigDecimal latitude, // Optionnel
@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) BigDecimal longitude, // Optionnel
@NotNull(message = "L'indicateur principale est obligatoire") Boolean principale,
String libelle,
String notes,
UUID organisationId, // Exclusive: soit organisationId, soit membreId, soit evenementId
UUID membreId,
UUID evenementId) {
}

View File

@@ -1,36 +1,36 @@
package dev.lions.unionflow.server.api.dto.adresse.request;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import java.math.BigDecimal;
import java.util.UUID;
/**
* Requete de mise à jour d'une adresse.
* Tous les champs sont optionnels pour permettre des mises à jour partielles.
*
* @author UnionFlow Team
* @version 3.0
*/
public record UpdateAdresseRequest(
String typeAdresse, // Code depuis types_reference
String adresse,
String complementAdresse,
String codePostal,
String ville,
String region,
String pays,
@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) BigDecimal latitude,
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180") @DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180") @Digits(integer = 3, fraction = 6) BigDecimal longitude,
Boolean principale,
String libelle,
String notes,
UUID organisationId,
UUID membreId,
UUID evenementId) {
}
package dev.lions.unionflow.server.api.dto.adresse.request;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import java.math.BigDecimal;
import java.util.UUID;
/**
* Requete de mise à jour d'une adresse.
* Tous les champs sont optionnels pour permettre des mises à jour partielles.
*
* @author UnionFlow Team
* @version 3.0
*/
public record UpdateAdresseRequest(
String typeAdresse, // Code depuis types_reference
String adresse,
String complementAdresse,
String codePostal,
String ville,
String region,
String pays,
@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) BigDecimal latitude,
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180") @DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180") @Digits(integer = 3, fraction = 6) BigDecimal longitude,
Boolean principale,
String libelle,
String notes,
UUID organisationId,
UUID membreId,
UUID evenementId) {
}

View File

@@ -1,42 +1,42 @@
package dev.lions.unionflow.server.api.dto.adresse.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
/**
* DTO de réponse détaillée pour une adresse.
*
* @author UnionFlow Team
* @version 3.0
*/
@Getter
@Setter
public class AdresseResponse extends BaseResponse {
private String typeAdresse; // Code
private String typeAdresseLibelle; // Depuis types_reference
private String typeAdresseIcone;
private String adresse;
private String complementAdresse;
private String codePostal;
private String ville;
private String region;
private String pays;
private BigDecimal latitude;
private BigDecimal longitude;
private Boolean principale;
private String libelle;
private String notes;
private UUID organisationId;
private UUID membreId;
private UUID evenementId;
private String adresseComplete;
}
package dev.lions.unionflow.server.api.dto.adresse.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
/**
* DTO de réponse détaillée pour une adresse.
*
* @author UnionFlow Team
* @version 3.0
*/
@Getter
@Setter
public class AdresseResponse extends BaseResponse {
private String typeAdresse; // Code
private String typeAdresseLibelle; // Depuis types_reference
private String typeAdresseIcone;
private String adresse;
private String complementAdresse;
private String codePostal;
private String ville;
private String region;
private String pays;
private BigDecimal latitude;
private BigDecimal longitude;
private Boolean principale;
private String libelle;
private String notes;
private UUID organisationId;
private UUID membreId;
private UUID evenementId;
private String adresseComplete;
}

View File

@@ -1,35 +1,35 @@
package dev.lions.unionflow.server.api.dto.agricole;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.agricole.StatutCampagneAgricole;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CampagneAgricoleDTO extends BaseDTO {
private String organisationCoopId;
// Exemple : "Campagne d'Arachide 2025/2026"
private String designation;
private String typeCulturePrincipale;
// Nombre d'hectares au total couvert par les membres de la coop
private BigDecimal surfaceTotaleEstimeeHectares;
// Tonnes récoltées attendues vs réelles
private BigDecimal volumePrevisionnelTonnes;
private BigDecimal volumeReelTonnes;
private StatutCampagneAgricole statut;
}
package dev.lions.unionflow.server.api.dto.agricole;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.agricole.StatutCampagneAgricole;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CampagneAgricoleDTO extends BaseDTO {
private String organisationCoopId;
// Exemple : "Campagne d'Arachide 2025/2026"
private String designation;
private String typeCulturePrincipale;
// Nombre d'hectares au total couvert par les membres de la coop
private BigDecimal surfaceTotaleEstimeeHectares;
// Tonnes récoltées attendues vs réelles
private BigDecimal volumePrevisionnelTonnes;
private BigDecimal volumeReelTonnes;
private StatutCampagneAgricole statut;
}

View File

@@ -1,265 +1,265 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour les données analytics UnionFlow
*
* <p>
* Représente une donnée analytique avec sa valeur, sa métrique associée, sa
* période d'analyse et
* ses métadonnées.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AnalyticsDataResponse extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Type de métrique analysée */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Période d'analyse */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Valeur numérique de la métrique */
@NotNull(message = "La valeur est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur invalide")
private BigDecimal valeur;
/** Valeur précédente pour comparaison */
@DecimalMin(value = "0.0", message = "La valeur précédente doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur précédente invalide")
private BigDecimal valeurPrecedente;
/** Pourcentage d'évolution par rapport à la période précédente */
@Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
private BigDecimal pourcentageEvolution;
/** Date de début de la période analysée */
@NotNull(message = "La date de début est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebut;
/** Date de fin de la période analysée */
@NotNull(message = "La date de fin est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFin;
/** Date de calcul de la métrique */
@NotNull(message = "La date de calcul est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateCalcul;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur qui a demandé le calcul */
private UUID utilisateurId;
/** Nom de l'utilisateur qui a demandé le calcul */
@Size(max = 200, message = "Le nom de l'utilisateur ne peut pas dépasser 200 caractères")
private String nomUtilisateur;
/** Libellé personnalisé de la métrique */
@Size(max = 300, message = "Le libellé personnalisé ne peut pas dépasser 300 caractères")
private String libellePersonnalise;
/** Description ou commentaire sur la métrique */
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
/** Données détaillées pour les graphiques (format JSON) */
@Size(max = 10000, message = "Les données détaillées ne peuvent pas dépasser 10000 caractères")
private String donneesDetaillees;
/** Configuration du graphique (couleurs, type, etc.) */
@Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
private String configurationGraphique;
/** Métadonnées additionnelles */
private Map<String, Object> metadonnees;
/** Indicateur de fiabilité des données (0-100) */
@DecimalMin(value = "0.0", message = "L'indicateur de fiabilité doit être positif")
@DecimalMax(value = "100.0", message = "L'indicateur de fiabilité ne peut pas dépasser 100")
@Digits(integer = 3, fraction = 1, message = "Format d'indicateur de fiabilité invalide")
private BigDecimal indicateurFiabilite;
/** Nombre d'éléments analysés pour calculer cette métrique */
@DecimalMin(value = "0", message = "Le nombre d'éléments doit être positif")
private Integer nombreElementsAnalyses;
/** Temps de calcul en millisecondes */
@DecimalMin(value = "0", message = "Le temps de calcul doit être positif")
private Long tempsCalculMs;
/** Indicateur si la métrique est en temps réel */
@Builder.Default
private Boolean tempsReel = false;
/** Indicateur si la métrique nécessite une mise à jour */
@Builder.Default
private Boolean necessiteMiseAJour = false;
/** Niveau de priorité de la métrique (1=faible, 5=critique) */
@DecimalMin(value = "1", message = "Le niveau de priorité minimum est 1")
@DecimalMax(value = "5", message = "Le niveau de priorité maximum est 5")
private Integer niveauPriorite;
/** Tags pour catégoriser la métrique */
private List<String> tags;
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé à afficher (personnalisé ou par défaut)
*
* @return Le libellé à afficher
*/
public String getLibelleAffichage() {
return libellePersonnalise != null && !libellePersonnalise.trim().isEmpty()
? libellePersonnalise
: typeMetrique.getLibelle();
}
/**
* Retourne l'unité de mesure de la métrique
*
* @return L'unité de mesure
*/
public String getUnite() {
return typeMetrique.getUnite();
}
/**
* Retourne l'icône de la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return typeMetrique.getIcone();
}
/**
* Retourne la couleur de la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return typeMetrique.getCouleur();
}
/**
* Vérifie si la métrique a évolué positivement
*
* @return true si l'évolution est positive
*/
public boolean hasEvolutionPositive() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) > 0;
}
/**
* Vérifie si la métrique a évolué négativement
*
* @return true si l'évolution est négative
*/
public boolean hasEvolutionNegative() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) < 0;
}
/**
* Vérifie si la métrique est stable (pas d'évolution)
*
* @return true si l'évolution est nulle
*/
public boolean isStable() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Retourne la tendance sous forme de texte
*
* @return "hausse", "baisse" ou "stable"
*/
public String getTendance() {
if (hasEvolutionPositive())
return "hausse";
if (hasEvolutionNegative())
return "baisse";
return "stable";
}
/**
* Vérifie si les données sont fiables (indicateur > 80)
*
* @return true si les données sont considérées comme fiables
*/
public boolean isDonneesFiables() {
return indicateurFiabilite != null
&& indicateurFiabilite.compareTo(new BigDecimal("80.0")) >= 0;
}
/**
* Vérifie si la métrique est critique (priorité >= 4)
*
* @return true si la métrique est critique
*/
public boolean isCritique() {
return niveauPriorite != null && niveauPriorite >= 4;
}
/**
* Constructeur avec les champs essentiels
*
* @param typeMetrique Le type de métrique
* @param periodeAnalyse La période d'analyse
* @param valeur La valeur de la métrique
*/
public AnalyticsDataResponse(
TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, BigDecimal valeur) {
super();
this.typeMetrique = typeMetrique;
this.periodeAnalyse = periodeAnalyse;
this.valeur = valeur;
this.dateCalcul = LocalDateTime.now();
this.dateDebut = periodeAnalyse.getDateDebut();
this.dateFin = periodeAnalyse.getDateFin();
this.tempsReel = false;
this.necessiteMiseAJour = false;
this.niveauPriorite = 3; // Priorité normale par défaut
this.indicateurFiabilite = new BigDecimal("95.0"); // Fiabilité élevée par défaut
}
}
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour les données analytics UnionFlow
*
* <p>
* Représente une donnée analytique avec sa valeur, sa métrique associée, sa
* période d'analyse et
* ses métadonnées.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AnalyticsDataResponse extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Type de métrique analysée */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Période d'analyse */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Valeur numérique de la métrique */
@NotNull(message = "La valeur est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur invalide")
private BigDecimal valeur;
/** Valeur précédente pour comparaison */
@DecimalMin(value = "0.0", message = "La valeur précédente doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur précédente invalide")
private BigDecimal valeurPrecedente;
/** Pourcentage d'évolution par rapport à la période précédente */
@Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
private BigDecimal pourcentageEvolution;
/** Date de début de la période analysée */
@NotNull(message = "La date de début est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebut;
/** Date de fin de la période analysée */
@NotNull(message = "La date de fin est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFin;
/** Date de calcul de la métrique */
@NotNull(message = "La date de calcul est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateCalcul;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur qui a demandé le calcul */
private UUID utilisateurId;
/** Nom de l'utilisateur qui a demandé le calcul */
@Size(max = 200, message = "Le nom de l'utilisateur ne peut pas dépasser 200 caractères")
private String nomUtilisateur;
/** Libellé personnalisé de la métrique */
@Size(max = 300, message = "Le libellé personnalisé ne peut pas dépasser 300 caractères")
private String libellePersonnalise;
/** Description ou commentaire sur la métrique */
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
/** Données détaillées pour les graphiques (format JSON) */
@Size(max = 10000, message = "Les données détaillées ne peuvent pas dépasser 10000 caractères")
private String donneesDetaillees;
/** Configuration du graphique (couleurs, type, etc.) */
@Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
private String configurationGraphique;
/** Métadonnées additionnelles */
private Map<String, Object> metadonnees;
/** Indicateur de fiabilité des données (0-100) */
@DecimalMin(value = "0.0", message = "L'indicateur de fiabilité doit être positif")
@DecimalMax(value = "100.0", message = "L'indicateur de fiabilité ne peut pas dépasser 100")
@Digits(integer = 3, fraction = 1, message = "Format d'indicateur de fiabilité invalide")
private BigDecimal indicateurFiabilite;
/** Nombre d'éléments analysés pour calculer cette métrique */
@DecimalMin(value = "0", message = "Le nombre d'éléments doit être positif")
private Integer nombreElementsAnalyses;
/** Temps de calcul en millisecondes */
@DecimalMin(value = "0", message = "Le temps de calcul doit être positif")
private Long tempsCalculMs;
/** Indicateur si la métrique est en temps réel */
@Builder.Default
private Boolean tempsReel = false;
/** Indicateur si la métrique nécessite une mise à jour */
@Builder.Default
private Boolean necessiteMiseAJour = false;
/** Niveau de priorité de la métrique (1=faible, 5=critique) */
@DecimalMin(value = "1", message = "Le niveau de priorité minimum est 1")
@DecimalMax(value = "5", message = "Le niveau de priorité maximum est 5")
private Integer niveauPriorite;
/** Tags pour catégoriser la métrique */
private List<String> tags;
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé à afficher (personnalisé ou par défaut)
*
* @return Le libellé à afficher
*/
public String getLibelleAffichage() {
return libellePersonnalise != null && !libellePersonnalise.trim().isEmpty()
? libellePersonnalise
: typeMetrique.getLibelle();
}
/**
* Retourne l'unité de mesure de la métrique
*
* @return L'unité de mesure
*/
public String getUnite() {
return typeMetrique.getUnite();
}
/**
* Retourne l'icône de la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return typeMetrique.getIcone();
}
/**
* Retourne la couleur de la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return typeMetrique.getCouleur();
}
/**
* Vérifie si la métrique a évolué positivement
*
* @return true si l'évolution est positive
*/
public boolean hasEvolutionPositive() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) > 0;
}
/**
* Vérifie si la métrique a évolué négativement
*
* @return true si l'évolution est négative
*/
public boolean hasEvolutionNegative() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) < 0;
}
/**
* Vérifie si la métrique est stable (pas d'évolution)
*
* @return true si l'évolution est nulle
*/
public boolean isStable() {
return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Retourne la tendance sous forme de texte
*
* @return "hausse", "baisse" ou "stable"
*/
public String getTendance() {
if (hasEvolutionPositive())
return "hausse";
if (hasEvolutionNegative())
return "baisse";
return "stable";
}
/**
* Vérifie si les données sont fiables (indicateur > 80)
*
* @return true si les données sont considérées comme fiables
*/
public boolean isDonneesFiables() {
return indicateurFiabilite != null
&& indicateurFiabilite.compareTo(new BigDecimal("80.0")) >= 0;
}
/**
* Vérifie si la métrique est critique (priorité >= 4)
*
* @return true si la métrique est critique
*/
public boolean isCritique() {
return niveauPriorite != null && niveauPriorite >= 4;
}
/**
* Constructeur avec les champs essentiels
*
* @param typeMetrique Le type de métrique
* @param periodeAnalyse La période d'analyse
* @param valeur La valeur de la métrique
*/
public AnalyticsDataResponse(
TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, BigDecimal valeur) {
super();
this.typeMetrique = typeMetrique;
this.periodeAnalyse = periodeAnalyse;
this.valeur = valeur;
this.dateCalcul = LocalDateTime.now();
this.dateDebut = periodeAnalyse.getDateDebut();
this.dateFin = periodeAnalyse.getDateFin();
this.tempsReel = false;
this.necessiteMiseAJour = false;
this.niveauPriorite = 3; // Priorité normale par défaut
this.indicateurFiabilite = new BigDecimal("95.0"); // Fiabilité élevée par défaut
}
}

View File

@@ -1,350 +1,350 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour les widgets de tableau de bord analytics UnionFlow
*
* <p>Représente un widget personnalisable affiché sur le tableau de bord avec sa configuration, sa
* position et ses données.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardWidgetResponse extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Titre du widget */
@NotBlank(message = "Le titre du widget est obligatoire")
@Size(min = 3, max = 200, message = "Le titre du widget doit contenir entre 3 et 200 caractères")
private String titre;
/** Description du widget */
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
private String description;
/** Type de widget (kpi, chart, table, gauge, progress, text) */
@NotBlank(message = "Le type de widget est obligatoire")
@Size(max = 50, message = "Le type de widget ne peut pas dépasser 50 caractères")
private String typeWidget;
/** Type de métrique affiché */
private TypeMetrique typeMetrique;
/** Période d'analyse */
private PeriodeAnalyse periodeAnalyse;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur propriétaire */
@NotNull(message = "L'identifiant de l'utilisateur propriétaire est obligatoire")
private UUID utilisateurProprietaireId;
/** Nom de l'utilisateur propriétaire */
@Size(
max = 200,
message = "Le nom de l'utilisateur propriétaire ne peut pas dépasser 200 caractères")
private String nomUtilisateurProprietaire;
/** Position X du widget sur la grille */
@NotNull(message = "La position X est obligatoire")
@DecimalMin(value = "0", message = "La position X doit être positive ou nulle")
private Integer positionX;
/** Position Y du widget sur la grille */
@NotNull(message = "La position Y est obligatoire")
@DecimalMin(value = "0", message = "La position Y doit être positive ou nulle")
private Integer positionY;
/** Largeur du widget (en unités de grille) */
@NotNull(message = "La largeur est obligatoire")
@DecimalMin(value = "1", message = "La largeur minimum est 1")
@DecimalMax(value = "12", message = "La largeur maximum est 12")
private Integer largeur;
/** Hauteur du widget (en unités de grille) */
@NotNull(message = "La hauteur est obligatoire")
@DecimalMin(value = "1", message = "La hauteur minimum est 1")
@DecimalMax(value = "12", message = "La hauteur maximum est 12")
private Integer hauteur;
/** Ordre d'affichage (z-index) */
@DecimalMin(value = "0", message = "L'ordre d'affichage doit être positif ou nul")
@Builder.Default
private Integer ordreAffichage = 0;
/** Configuration visuelle du widget */
@Size(max = 5000, message = "La configuration visuelle ne peut pas dépasser 5000 caractères")
private String configurationVisuelle;
/** Couleur principale du widget */
@Size(max = 7, message = "La couleur doit être au format #RRGGBB")
private String couleurPrincipale;
/** Couleur secondaire du widget */
@Size(max = 7, message = "La couleur secondaire doit être au format #RRGGBB")
private String couleurSecondaire;
/** Icône du widget */
@Size(max = 50, message = "L'icône ne peut pas dépasser 50 caractères")
private String icone;
/** Indicateur si le widget est visible */
@Builder.Default private Boolean visible = true;
/** Indicateur si le widget est redimensionnable */
@Builder.Default private Boolean redimensionnable = true;
/** Indicateur si le widget est déplaçable */
@Builder.Default private Boolean deplacable = true;
/** Indicateur si le widget peut être supprimé */
@Builder.Default private Boolean supprimable = true;
/** Indicateur si le widget se met à jour automatiquement */
@Builder.Default private Boolean miseAJourAutomatique = true;
/** Fréquence de mise à jour en secondes */
@DecimalMin(value = "30", message = "La fréquence minimum est 30 secondes")
@Builder.Default
private Integer frequenceMiseAJourSecondes = 300; // 5 minutes par défaut
/** Date de dernière mise à jour des données */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereMiseAJour;
/** Prochaine mise à jour programmée */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime prochaineMiseAJour;
/** Données du widget (format JSON) */
@Size(max = 50000, message = "Les données du widget ne peuvent pas dépasser 50000 caractères")
private String donneesWidget;
/** Configuration des filtres */
private Map<String, Object> configurationFiltres;
/** Configuration des alertes */
private Map<String, Object> configurationAlertes;
/** Seuil d'alerte bas */
private Double seuilAlerteBas;
/** Seuil d'alerte haut */
private Double seuilAlerteHaut;
/** Indicateur si une alerte est active */
@Builder.Default private Boolean alerteActive = false;
/** Message d'alerte actuel */
@Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
private String messageAlerte;
/** Type d'alerte (info, warning, error, success) */
@Size(max = 20, message = "Le type d'alerte ne peut pas dépasser 20 caractères")
private String typeAlerte;
/** Permissions d'accès au widget */
@Size(max = 1000, message = "Les permissions ne peuvent pas dépasser 1000 caractères")
private String permissions;
/** Rôles autorisés à voir le widget */
@Size(max = 500, message = "Les rôles autorisés ne peuvent pas dépasser 500 caractères")
private String rolesAutorises;
/** Template personnalisé du widget */
@Size(max = 10000, message = "Le template personnalisé ne peut pas dépasser 10000 caractères")
private String templatePersonnalise;
/** CSS personnalisé du widget */
@Size(max = 5000, message = "Le CSS personnalisé ne peut pas dépasser 5000 caractères")
private String cssPersonnalise;
/** JavaScript personnalisé du widget */
@Size(max = 10000, message = "Le JavaScript personnalisé ne peut pas dépasser 10000 caractères")
private String javascriptPersonnalise;
/** Métadonnées additionnelles */
private Map<String, Object> metadonnees;
/** Nombre de vues du widget */
@DecimalMin(value = "0", message = "Le nombre de vues doit être positif")
@Builder.Default
private Long nombreVues = 0L;
/** Nombre d'interactions avec le widget */
@DecimalMin(value = "0", message = "Le nombre d'interactions doit être positif")
@Builder.Default
private Long nombreInteractions = 0L;
/** Temps moyen passé sur le widget (en secondes) */
@DecimalMin(value = "0", message = "Le temps moyen doit être positif")
private Integer tempsMoyenSecondes;
/** Taux d'erreur du widget (en pourcentage) */
@DecimalMin(value = "0.0", message = "Le taux d'erreur doit être positif")
@DecimalMax(value = "100.0", message = "Le taux d'erreur ne peut pas dépasser 100%")
@Builder.Default
private Double tauxErreur = 0.0;
/** Date de dernière erreur */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereErreur;
/** Message de dernière erreur */
@Size(max = 1000, message = "Le message d'erreur ne peut pas dépasser 1000 caractères")
private String messageDerniereErreur;
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé de la métrique si définie
*
* @return Le libellé de la métrique ou null
*/
public String getLibelleMetrique() {
return typeMetrique != null ? typeMetrique.getLibelle() : null;
}
/**
* Retourne l'unité de mesure si métrique définie
*
* @return L'unité de mesure ou chaîne vide
*/
public String getUnite() {
return typeMetrique != null ? typeMetrique.getUnite() : "";
}
/**
* Retourne l'icône de la métrique ou l'icône personnalisée
*
* @return L'icône à afficher
*/
public String getIconeAffichage() {
if (icone != null && !icone.trim().isEmpty()) {
return icone;
}
return typeMetrique != null ? typeMetrique.getIcone() : "dashboard";
}
/**
* Retourne la couleur de la métrique ou la couleur personnalisée
*
* @return La couleur à utiliser
*/
public String getCouleurAffichage() {
if (couleurPrincipale != null && !couleurPrincipale.trim().isEmpty()) {
return couleurPrincipale;
}
return typeMetrique != null ? typeMetrique.getCouleur() : "#757575";
}
/**
* Vérifie si le widget nécessite une mise à jour
*
* @return true si une mise à jour est nécessaire
*/
public boolean necessiteMiseAJour() {
return miseAJourAutomatique
&& prochaineMiseAJour != null
&& prochaineMiseAJour.isBefore(LocalDateTime.now());
}
/**
* Vérifie si le widget est interactif
*
* @return true si le widget permet des interactions
*/
public boolean isInteractif() {
return "chart".equals(typeWidget) || "table".equals(typeWidget) || "gauge".equals(typeWidget);
}
/**
* Vérifie si le widget affiche des données temps réel
*
* @return true si le widget est en temps réel
*/
/** Indique si la fréquence est en temps réel (pour couverture branches). */
private boolean isFrequenceTempsReel() {
if (frequenceMiseAJourSecondes == null) return false;
return frequenceMiseAJourSecondes <= 60;
}
public boolean isTempsReel() {
return isFrequenceTempsReel();
}
/**
* Retourne la taille du widget (surface occupée)
*
* @return La surface en unités de grille
*/
public int getTailleWidget() {
return largeur * hauteur;
}
/**
* Vérifie si le widget est grand (surface > 6)
*
* @return true si le widget est considéré comme grand
*/
public boolean isWidgetGrand() {
return getTailleWidget() > 6;
}
/**
* Vérifie si le widget a des erreurs récentes (< 24h)
*
* @return true si des erreurs récentes sont détectées
*/
public boolean hasErreursRecentes() {
return dateDerniereErreur != null
&& dateDerniereErreur.isAfter(LocalDateTime.now().minusHours(24));
}
/**
* Retourne le statut du widget
*
* @return "actif", "erreur", "inactif" ou "maintenance"
*/
/** Indique si le taux d'erreur déclenche le statut maintenance (pour couverture branches). */
private boolean isTauxErreurMaintenance() {
if (tauxErreur == null) return false;
return tauxErreur > 10.0;
}
public String getStatutWidget() {
if (hasErreursRecentes()) return "erreur";
if (!visible) return "inactif";
if (isTauxErreurMaintenance()) return "maintenance";
return "actif";
}
}
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour les widgets de tableau de bord analytics UnionFlow
*
* <p>Représente un widget personnalisable affiché sur le tableau de bord avec sa configuration, sa
* position et ses données.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardWidgetResponse extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Titre du widget */
@NotBlank(message = "Le titre du widget est obligatoire")
@Size(min = 3, max = 200, message = "Le titre du widget doit contenir entre 3 et 200 caractères")
private String titre;
/** Description du widget */
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
private String description;
/** Type de widget (kpi, chart, table, gauge, progress, text) */
@NotBlank(message = "Le type de widget est obligatoire")
@Size(max = 50, message = "Le type de widget ne peut pas dépasser 50 caractères")
private String typeWidget;
/** Type de métrique affiché */
private TypeMetrique typeMetrique;
/** Période d'analyse */
private PeriodeAnalyse periodeAnalyse;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur propriétaire */
@NotNull(message = "L'identifiant de l'utilisateur propriétaire est obligatoire")
private UUID utilisateurProprietaireId;
/** Nom de l'utilisateur propriétaire */
@Size(
max = 200,
message = "Le nom de l'utilisateur propriétaire ne peut pas dépasser 200 caractères")
private String nomUtilisateurProprietaire;
/** Position X du widget sur la grille */
@NotNull(message = "La position X est obligatoire")
@DecimalMin(value = "0", message = "La position X doit être positive ou nulle")
private Integer positionX;
/** Position Y du widget sur la grille */
@NotNull(message = "La position Y est obligatoire")
@DecimalMin(value = "0", message = "La position Y doit être positive ou nulle")
private Integer positionY;
/** Largeur du widget (en unités de grille) */
@NotNull(message = "La largeur est obligatoire")
@DecimalMin(value = "1", message = "La largeur minimum est 1")
@DecimalMax(value = "12", message = "La largeur maximum est 12")
private Integer largeur;
/** Hauteur du widget (en unités de grille) */
@NotNull(message = "La hauteur est obligatoire")
@DecimalMin(value = "1", message = "La hauteur minimum est 1")
@DecimalMax(value = "12", message = "La hauteur maximum est 12")
private Integer hauteur;
/** Ordre d'affichage (z-index) */
@DecimalMin(value = "0", message = "L'ordre d'affichage doit être positif ou nul")
@Builder.Default
private Integer ordreAffichage = 0;
/** Configuration visuelle du widget */
@Size(max = 5000, message = "La configuration visuelle ne peut pas dépasser 5000 caractères")
private String configurationVisuelle;
/** Couleur principale du widget */
@Size(max = 7, message = "La couleur doit être au format #RRGGBB")
private String couleurPrincipale;
/** Couleur secondaire du widget */
@Size(max = 7, message = "La couleur secondaire doit être au format #RRGGBB")
private String couleurSecondaire;
/** Icône du widget */
@Size(max = 50, message = "L'icône ne peut pas dépasser 50 caractères")
private String icone;
/** Indicateur si le widget est visible */
@Builder.Default private Boolean visible = true;
/** Indicateur si le widget est redimensionnable */
@Builder.Default private Boolean redimensionnable = true;
/** Indicateur si le widget est déplaçable */
@Builder.Default private Boolean deplacable = true;
/** Indicateur si le widget peut être supprimé */
@Builder.Default private Boolean supprimable = true;
/** Indicateur si le widget se met à jour automatiquement */
@Builder.Default private Boolean miseAJourAutomatique = true;
/** Fréquence de mise à jour en secondes */
@DecimalMin(value = "30", message = "La fréquence minimum est 30 secondes")
@Builder.Default
private Integer frequenceMiseAJourSecondes = 300; // 5 minutes par défaut
/** Date de dernière mise à jour des données */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereMiseAJour;
/** Prochaine mise à jour programmée */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime prochaineMiseAJour;
/** Données du widget (format JSON) */
@Size(max = 50000, message = "Les données du widget ne peuvent pas dépasser 50000 caractères")
private String donneesWidget;
/** Configuration des filtres */
private Map<String, Object> configurationFiltres;
/** Configuration des alertes */
private Map<String, Object> configurationAlertes;
/** Seuil d'alerte bas */
private Double seuilAlerteBas;
/** Seuil d'alerte haut */
private Double seuilAlerteHaut;
/** Indicateur si une alerte est active */
@Builder.Default private Boolean alerteActive = false;
/** Message d'alerte actuel */
@Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
private String messageAlerte;
/** Type d'alerte (info, warning, error, success) */
@Size(max = 20, message = "Le type d'alerte ne peut pas dépasser 20 caractères")
private String typeAlerte;
/** Permissions d'accès au widget */
@Size(max = 1000, message = "Les permissions ne peuvent pas dépasser 1000 caractères")
private String permissions;
/** Rôles autorisés à voir le widget */
@Size(max = 500, message = "Les rôles autorisés ne peuvent pas dépasser 500 caractères")
private String rolesAutorises;
/** Template personnalisé du widget */
@Size(max = 10000, message = "Le template personnalisé ne peut pas dépasser 10000 caractères")
private String templatePersonnalise;
/** CSS personnalisé du widget */
@Size(max = 5000, message = "Le CSS personnalisé ne peut pas dépasser 5000 caractères")
private String cssPersonnalise;
/** JavaScript personnalisé du widget */
@Size(max = 10000, message = "Le JavaScript personnalisé ne peut pas dépasser 10000 caractères")
private String javascriptPersonnalise;
/** Métadonnées additionnelles */
private Map<String, Object> metadonnees;
/** Nombre de vues du widget */
@DecimalMin(value = "0", message = "Le nombre de vues doit être positif")
@Builder.Default
private Long nombreVues = 0L;
/** Nombre d'interactions avec le widget */
@DecimalMin(value = "0", message = "Le nombre d'interactions doit être positif")
@Builder.Default
private Long nombreInteractions = 0L;
/** Temps moyen passé sur le widget (en secondes) */
@DecimalMin(value = "0", message = "Le temps moyen doit être positif")
private Integer tempsMoyenSecondes;
/** Taux d'erreur du widget (en pourcentage) */
@DecimalMin(value = "0.0", message = "Le taux d'erreur doit être positif")
@DecimalMax(value = "100.0", message = "Le taux d'erreur ne peut pas dépasser 100%")
@Builder.Default
private Double tauxErreur = 0.0;
/** Date de dernière erreur */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereErreur;
/** Message de dernière erreur */
@Size(max = 1000, message = "Le message d'erreur ne peut pas dépasser 1000 caractères")
private String messageDerniereErreur;
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé de la métrique si définie
*
* @return Le libellé de la métrique ou null
*/
public String getLibelleMetrique() {
return typeMetrique != null ? typeMetrique.getLibelle() : null;
}
/**
* Retourne l'unité de mesure si métrique définie
*
* @return L'unité de mesure ou chaîne vide
*/
public String getUnite() {
return typeMetrique != null ? typeMetrique.getUnite() : "";
}
/**
* Retourne l'icône de la métrique ou l'icône personnalisée
*
* @return L'icône à afficher
*/
public String getIconeAffichage() {
if (icone != null && !icone.trim().isEmpty()) {
return icone;
}
return typeMetrique != null ? typeMetrique.getIcone() : "dashboard";
}
/**
* Retourne la couleur de la métrique ou la couleur personnalisée
*
* @return La couleur à utiliser
*/
public String getCouleurAffichage() {
if (couleurPrincipale != null && !couleurPrincipale.trim().isEmpty()) {
return couleurPrincipale;
}
return typeMetrique != null ? typeMetrique.getCouleur() : "#757575";
}
/**
* Vérifie si le widget nécessite une mise à jour
*
* @return true si une mise à jour est nécessaire
*/
public boolean necessiteMiseAJour() {
return miseAJourAutomatique
&& prochaineMiseAJour != null
&& prochaineMiseAJour.isBefore(LocalDateTime.now());
}
/**
* Vérifie si le widget est interactif
*
* @return true si le widget permet des interactions
*/
public boolean isInteractif() {
return "chart".equals(typeWidget) || "table".equals(typeWidget) || "gauge".equals(typeWidget);
}
/**
* Vérifie si le widget affiche des données temps réel
*
* @return true si le widget est en temps réel
*/
/** Indique si la fréquence est en temps réel (pour couverture branches). */
private boolean isFrequenceTempsReel() {
if (frequenceMiseAJourSecondes == null) return false;
return frequenceMiseAJourSecondes <= 60;
}
public boolean isTempsReel() {
return isFrequenceTempsReel();
}
/**
* Retourne la taille du widget (surface occupée)
*
* @return La surface en unités de grille
*/
public int getTailleWidget() {
return largeur * hauteur;
}
/**
* Vérifie si le widget est grand (surface > 6)
*
* @return true si le widget est considéré comme grand
*/
public boolean isWidgetGrand() {
return getTailleWidget() > 6;
}
/**
* Vérifie si le widget a des erreurs récentes (< 24h)
*
* @return true si des erreurs récentes sont détectées
*/
public boolean hasErreursRecentes() {
return dateDerniereErreur != null
&& dateDerniereErreur.isAfter(LocalDateTime.now().minusHours(24));
}
/**
* Retourne le statut du widget
*
* @return "actif", "erreur", "inactif" ou "maintenance"
*/
/** Indique si le taux d'erreur déclenche le statut maintenance (pour couverture branches). */
private boolean isTauxErreurMaintenance() {
if (tauxErreur == null) return false;
return tauxErreur > 10.0;
}
public String getStatutWidget() {
if (hasErreursRecentes()) return "erreur";
if (!visible) return "inactif";
if (isTauxErreurMaintenance()) return "maintenance";
return "actif";
}
}

View File

@@ -1,309 +1,309 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour les tendances et évolutions des KPI UnionFlow
*
* <p>Représente l'évolution d'un KPI dans le temps avec les points de données historiques pour
* générer des graphiques de tendance.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class KPITrendResponse extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Type de métrique pour cette tendance */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Période d'analyse globale */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Identifiant de l'organisation (optionnel) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Date de début de la période analysée */
@NotNull(message = "La date de début est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebut;
/** Date de fin de la période analysée */
@NotNull(message = "La date de fin est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFin;
/** Points de données pour la tendance */
@NotNull(message = "Les points de données sont obligatoires")
private List<PointDonneeDTO> pointsDonnees;
/** Valeur actuelle du KPI */
@NotNull(message = "La valeur actuelle est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur actuelle doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur actuelle invalide")
private BigDecimal valeurActuelle;
/** Valeur minimale sur la période */
@DecimalMin(value = "0.0", message = "La valeur minimale doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur minimale invalide")
private BigDecimal valeurMinimale;
/** Valeur maximale sur la période */
@DecimalMin(value = "0.0", message = "La valeur maximale doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur maximale invalide")
private BigDecimal valeurMaximale;
/** Valeur moyenne sur la période */
@DecimalMin(value = "0.0", message = "La valeur moyenne doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur moyenne invalide")
private BigDecimal valeurMoyenne;
/** Écart-type des valeurs */
@DecimalMin(value = "0.0", message = "L'écart-type doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format d'écart-type invalide")
private BigDecimal ecartType;
/** Coefficient de variation (écart-type / moyenne) */
@DecimalMin(value = "0.0", message = "Le coefficient de variation doit être positif ou nul")
@Digits(integer = 6, fraction = 4, message = "Format de coefficient de variation invalide")
private BigDecimal coefficientVariation;
/** Tendance générale (pente de la régression linéaire) */
@Digits(integer = 10, fraction = 6, message = "Format de tendance invalide")
private BigDecimal tendanceGenerale;
/** Coefficient de corrélation R² */
@DecimalMin(value = "0.0", message = "Le coefficient de corrélation doit être positif ou nul")
@DecimalMax(value = "1.0", message = "Le coefficient de corrélation ne peut pas dépasser 1")
@Digits(integer = 1, fraction = 6, message = "Format de coefficient de corrélation invalide")
private BigDecimal coefficientCorrelation;
/** Pourcentage d'évolution depuis le début de la période */
@Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
private BigDecimal pourcentageEvolutionGlobale;
/** Prédiction pour la prochaine période */
@DecimalMin(value = "0.0", message = "La prédiction doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de prédiction invalide")
private BigDecimal predictionProchainePeriode;
/** Marge d'erreur de la prédiction (en pourcentage) */
@DecimalMin(value = "0.0", message = "La marge d'erreur doit être positive ou nulle")
@DecimalMax(value = "100.0", message = "La marge d'erreur ne peut pas dépasser 100%")
@Digits(integer = 3, fraction = 2, message = "Format de marge d'erreur invalide")
private BigDecimal margeErreurPrediction;
/** Seuil d'alerte bas */
@DecimalMin(value = "0.0", message = "Le seuil d'alerte bas doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format de seuil d'alerte bas invalide")
private BigDecimal seuilAlerteBas;
/** Seuil d'alerte haut */
@DecimalMin(value = "0.0", message = "Le seuil d'alerte haut doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format de seuil d'alerte haut invalide")
private BigDecimal seuilAlerteHaut;
/** Indicateur si une alerte est active */
@Builder.Default private Boolean alerteActive = false;
/** Type d'alerte (bas, haut, anomalie) */
@Size(max = 50, message = "Le type d'alerte ne peut pas dépasser 50 caractères")
private String typeAlerte;
/** Message d'alerte */
@Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
private String messageAlerte;
/** Configuration du graphique (couleurs, style, etc.) */
@Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
private String configurationGraphique;
/** Intervalle de regroupement des données */
@Size(max = 20, message = "L'intervalle de regroupement ne peut pas dépasser 20 caractères")
private String intervalleRegroupement;
/** Format d'affichage des dates */
@Size(max = 20, message = "Le format de date ne peut pas dépasser 20 caractères")
private String formatDate;
/** Date de dernière mise à jour */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereMiseAJour;
/** Fréquence de mise à jour en minutes */
@DecimalMin(value = "1", message = "La fréquence de mise à jour minimum est 1 minute")
private Integer frequenceMiseAJourMinutes;
// === CLASSES INTERNES ===
/** Classe interne représentant un point de données dans la tendance */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PointDonneeDTO {
/** Date du point de données */
@NotNull(message = "La date du point de données est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime date;
/** Valeur du point de données */
@NotNull(message = "La valeur du point de données est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur du point doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur du point invalide")
private BigDecimal valeur;
/** Libellé du point (optionnel) */
@Size(max = 100, message = "Le libellé du point ne peut pas dépasser 100 caractères")
private String libelle;
/** Indicateur si le point est une anomalie */
@Builder.Default private Boolean anomalie = false;
/** Indicateur si le point est une prédiction */
@Builder.Default private Boolean prediction = false;
/** Métadonnées additionnelles du point */
private String metadonnees;
}
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé de la métrique
*
* @return Le libellé de la métrique
*/
public String getLibelleMetrique() {
return typeMetrique.getLibelle();
}
/**
* Retourne l'unité de mesure
*
* @return L'unité de mesure
*/
public String getUnite() {
return typeMetrique.getUnite();
}
/**
* Retourne l'icône de la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return typeMetrique.getIcone();
}
/**
* Retourne la couleur de la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return typeMetrique.getCouleur();
}
/**
* Vérifie si la tendance est positive
*
* @return true si la tendance générale est positive
*/
public boolean isTendancePositive() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) > 0;
}
/**
* Vérifie si la tendance est négative
*
* @return true si la tendance générale est négative
*/
public boolean isTendanceNegative() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) < 0;
}
/**
* Vérifie si la tendance est stable
*
* @return true si la tendance générale est stable
*/
public boolean isTendanceStable() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Retourne la volatilité du KPI (basée sur le coefficient de variation)
*
* @return "faible", "moyenne" ou "élevée"
*/
public String getVolatilite() {
if (coefficientVariation == null) return "inconnue";
BigDecimal cv = coefficientVariation;
if (cv.compareTo(new BigDecimal("0.1")) <= 0) return "faible";
if (cv.compareTo(new BigDecimal("0.3")) <= 0) return "moyenne";
return "élevée";
}
/**
* Vérifie si la prédiction est fiable (R² > 0.7)
*
* @return true si la prédiction est considérée comme fiable
*/
public boolean isPredictionFiable() {
return coefficientCorrelation != null
&& coefficientCorrelation.compareTo(new BigDecimal("0.7")) >= 0;
}
/**
* Retourne le nombre de points de données
*
* @return Le nombre de points de données
*/
public int getNombrePointsDonnees() {
return pointsDonnees != null ? pointsDonnees.size() : 0;
}
/**
* Vérifie si des anomalies ont été détectées
*
* @return true si au moins un point est marqué comme anomalie
*/
public boolean hasAnomalies() {
return pointsDonnees != null
&& pointsDonnees.stream().anyMatch(point -> Boolean.TRUE.equals(point.getAnomalie()));
}
}
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour les tendances et évolutions des KPI UnionFlow
*
* <p>Représente l'évolution d'un KPI dans le temps avec les points de données historiques pour
* générer des graphiques de tendance.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class KPITrendResponse extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Type de métrique pour cette tendance */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Période d'analyse globale */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Identifiant de l'organisation (optionnel) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Date de début de la période analysée */
@NotNull(message = "La date de début est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebut;
/** Date de fin de la période analysée */
@NotNull(message = "La date de fin est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFin;
/** Points de données pour la tendance */
@NotNull(message = "Les points de données sont obligatoires")
private List<PointDonneeDTO> pointsDonnees;
/** Valeur actuelle du KPI */
@NotNull(message = "La valeur actuelle est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur actuelle doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur actuelle invalide")
private BigDecimal valeurActuelle;
/** Valeur minimale sur la période */
@DecimalMin(value = "0.0", message = "La valeur minimale doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur minimale invalide")
private BigDecimal valeurMinimale;
/** Valeur maximale sur la période */
@DecimalMin(value = "0.0", message = "La valeur maximale doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur maximale invalide")
private BigDecimal valeurMaximale;
/** Valeur moyenne sur la période */
@DecimalMin(value = "0.0", message = "La valeur moyenne doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur moyenne invalide")
private BigDecimal valeurMoyenne;
/** Écart-type des valeurs */
@DecimalMin(value = "0.0", message = "L'écart-type doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format d'écart-type invalide")
private BigDecimal ecartType;
/** Coefficient de variation (écart-type / moyenne) */
@DecimalMin(value = "0.0", message = "Le coefficient de variation doit être positif ou nul")
@Digits(integer = 6, fraction = 4, message = "Format de coefficient de variation invalide")
private BigDecimal coefficientVariation;
/** Tendance générale (pente de la régression linéaire) */
@Digits(integer = 10, fraction = 6, message = "Format de tendance invalide")
private BigDecimal tendanceGenerale;
/** Coefficient de corrélation R² */
@DecimalMin(value = "0.0", message = "Le coefficient de corrélation doit être positif ou nul")
@DecimalMax(value = "1.0", message = "Le coefficient de corrélation ne peut pas dépasser 1")
@Digits(integer = 1, fraction = 6, message = "Format de coefficient de corrélation invalide")
private BigDecimal coefficientCorrelation;
/** Pourcentage d'évolution depuis le début de la période */
@Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
private BigDecimal pourcentageEvolutionGlobale;
/** Prédiction pour la prochaine période */
@DecimalMin(value = "0.0", message = "La prédiction doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de prédiction invalide")
private BigDecimal predictionProchainePeriode;
/** Marge d'erreur de la prédiction (en pourcentage) */
@DecimalMin(value = "0.0", message = "La marge d'erreur doit être positive ou nulle")
@DecimalMax(value = "100.0", message = "La marge d'erreur ne peut pas dépasser 100%")
@Digits(integer = 3, fraction = 2, message = "Format de marge d'erreur invalide")
private BigDecimal margeErreurPrediction;
/** Seuil d'alerte bas */
@DecimalMin(value = "0.0", message = "Le seuil d'alerte bas doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format de seuil d'alerte bas invalide")
private BigDecimal seuilAlerteBas;
/** Seuil d'alerte haut */
@DecimalMin(value = "0.0", message = "Le seuil d'alerte haut doit être positif ou nul")
@Digits(integer = 15, fraction = 4, message = "Format de seuil d'alerte haut invalide")
private BigDecimal seuilAlerteHaut;
/** Indicateur si une alerte est active */
@Builder.Default private Boolean alerteActive = false;
/** Type d'alerte (bas, haut, anomalie) */
@Size(max = 50, message = "Le type d'alerte ne peut pas dépasser 50 caractères")
private String typeAlerte;
/** Message d'alerte */
@Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
private String messageAlerte;
/** Configuration du graphique (couleurs, style, etc.) */
@Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
private String configurationGraphique;
/** Intervalle de regroupement des données */
@Size(max = 20, message = "L'intervalle de regroupement ne peut pas dépasser 20 caractères")
private String intervalleRegroupement;
/** Format d'affichage des dates */
@Size(max = 20, message = "Le format de date ne peut pas dépasser 20 caractères")
private String formatDate;
/** Date de dernière mise à jour */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereMiseAJour;
/** Fréquence de mise à jour en minutes */
@DecimalMin(value = "1", message = "La fréquence de mise à jour minimum est 1 minute")
private Integer frequenceMiseAJourMinutes;
// === CLASSES INTERNES ===
/** Classe interne représentant un point de données dans la tendance */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class PointDonneeDTO {
/** Date du point de données */
@NotNull(message = "La date du point de données est obligatoire")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime date;
/** Valeur du point de données */
@NotNull(message = "La valeur du point de données est obligatoire")
@DecimalMin(value = "0.0", message = "La valeur du point doit être positive ou nulle")
@Digits(integer = 15, fraction = 4, message = "Format de valeur du point invalide")
private BigDecimal valeur;
/** Libellé du point (optionnel) */
@Size(max = 100, message = "Le libellé du point ne peut pas dépasser 100 caractères")
private String libelle;
/** Indicateur si le point est une anomalie */
@Builder.Default private Boolean anomalie = false;
/** Indicateur si le point est une prédiction */
@Builder.Default private Boolean prediction = false;
/** Métadonnées additionnelles du point */
private String metadonnees;
}
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le libellé de la métrique
*
* @return Le libellé de la métrique
*/
public String getLibelleMetrique() {
return typeMetrique.getLibelle();
}
/**
* Retourne l'unité de mesure
*
* @return L'unité de mesure
*/
public String getUnite() {
return typeMetrique.getUnite();
}
/**
* Retourne l'icône de la métrique
*
* @return L'icône Material Design
*/
public String getIcone() {
return typeMetrique.getIcone();
}
/**
* Retourne la couleur de la métrique
*
* @return Le code couleur hexadécimal
*/
public String getCouleur() {
return typeMetrique.getCouleur();
}
/**
* Vérifie si la tendance est positive
*
* @return true si la tendance générale est positive
*/
public boolean isTendancePositive() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) > 0;
}
/**
* Vérifie si la tendance est négative
*
* @return true si la tendance générale est négative
*/
public boolean isTendanceNegative() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) < 0;
}
/**
* Vérifie si la tendance est stable
*
* @return true si la tendance générale est stable
*/
public boolean isTendanceStable() {
return tendanceGenerale != null && tendanceGenerale.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Retourne la volatilité du KPI (basée sur le coefficient de variation)
*
* @return "faible", "moyenne" ou "élevée"
*/
public String getVolatilite() {
if (coefficientVariation == null) return "inconnue";
BigDecimal cv = coefficientVariation;
if (cv.compareTo(new BigDecimal("0.1")) <= 0) return "faible";
if (cv.compareTo(new BigDecimal("0.3")) <= 0) return "moyenne";
return "élevée";
}
/**
* Vérifie si la prédiction est fiable (R² > 0.7)
*
* @return true si la prédiction est considérée comme fiable
*/
public boolean isPredictionFiable() {
return coefficientCorrelation != null
&& coefficientCorrelation.compareTo(new BigDecimal("0.7")) >= 0;
}
/**
* Retourne le nombre de points de données
*
* @return Le nombre de points de données
*/
public int getNombrePointsDonnees() {
return pointsDonnees != null ? pointsDonnees.size() : 0;
}
/**
* Vérifie si des anomalies ont été détectées
*
* @return true si au moins un point est marqué comme anomalie
*/
public boolean hasAnomalies() {
return pointsDonnees != null
&& pointsDonnees.stream().anyMatch(point -> Boolean.TRUE.equals(point.getAnomalie()));
}
}

View File

@@ -1,333 +1,333 @@
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.FormatExport;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour la configuration des rapports analytics UnionFlow
*
* <p>Représente la configuration d'un rapport personnalisé avec ses métriques, sa mise en forme et
* ses paramètres d'export.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportConfigDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Nom du rapport */
@NotBlank(message = "Le nom du rapport est obligatoire")
@Size(min = 3, max = 200, message = "Le nom du rapport doit contenir entre 3 et 200 caractères")
private String nom;
/** Description du rapport */
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
/** Type de rapport (executif, analytique, technique, operationnel) */
@NotBlank(message = "Le type de rapport est obligatoire")
@Size(max = 50, message = "Le type de rapport ne peut pas dépasser 50 caractères")
private String typeRapport;
/** Période d'analyse par défaut */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Date de début personnalisée (si période personnalisée) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebutPersonnalisee;
/** Date de fin personnalisée (si période personnalisée) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFinPersonnalisee;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur créateur */
@NotNull(message = "L'identifiant de l'utilisateur créateur est obligatoire")
private UUID utilisateurCreateurId;
/** Nom de l'utilisateur créateur */
@Size(max = 200, message = "Le nom de l'utilisateur créateur ne peut pas dépasser 200 caractères")
private String nomUtilisateurCreateur;
/** Métriques incluses dans le rapport */
@NotNull(message = "Les métriques sont obligatoires")
@Valid
private List<MetriqueConfigDTO> metriques;
/** Sections du rapport */
@Valid private List<SectionRapportDTO> sections;
/** Format d'export par défaut */
@NotNull(message = "Le format d'export est obligatoire")
private FormatExport formatExport;
/** Formats d'export autorisés */
private List<FormatExport> formatsExportAutorises;
/** Modèle de rapport à utiliser */
@Size(max = 100, message = "Le modèle de rapport ne peut pas dépasser 100 caractères")
private String modeleRapport;
/** Configuration de la mise en page */
@Size(
max = 2000,
message = "La configuration de mise en page ne peut pas dépasser 2000 caractères")
private String configurationMiseEnPage;
/** Logo personnalisé (URL ou base64) */
@Size(max = 5000, message = "Le logo personnalisé ne peut pas dépasser 5000 caractères")
private String logoPersonnalise;
/** Couleurs personnalisées du rapport */
private Map<String, String> couleursPersonnalisees;
/** Indicateur si le rapport est public */
@Builder.Default private Boolean rapportPublic = false;
/** Indicateur si le rapport est automatique */
@Builder.Default private Boolean rapportAutomatique = false;
/** Fréquence de génération automatique (en heures) */
@DecimalMin(value = "1", message = "La fréquence minimum est 1 heure")
private Integer frequenceGenerationHeures;
/** Prochaine génération automatique */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime prochaineGeneration;
/** Liste des destinataires pour l'envoi automatique */
private List<String> destinatairesEmail;
/** Objet de l'email pour l'envoi automatique */
@Size(max = 200, message = "L'objet de l'email ne peut pas dépasser 200 caractères")
private String objetEmail;
/** Corps de l'email pour l'envoi automatique */
@Size(max = 2000, message = "Le corps de l'email ne peut pas dépasser 2000 caractères")
private String corpsEmail;
/** Paramètres de filtrage avancé */
private Map<String, Object> parametresFiltrage;
/** Tags pour catégoriser le rapport */
private List<String> tags;
/** Niveau de confidentialité (1=public, 5=confidentiel) */
@DecimalMin(value = "1", message = "Le niveau de confidentialité minimum est 1")
@DecimalMax(value = "5", message = "Le niveau de confidentialité maximum est 5")
@Builder.Default
private Integer niveauConfidentialite = 1;
/** Date de dernière génération */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereGeneration;
/** Nombre de générations effectuées */
@DecimalMin(value = "0", message = "Le nombre de générations doit être positif")
@Builder.Default
private Integer nombreGenerations = 0;
/** Taille moyenne des rapports générés (en KB) */
@DecimalMin(value = "0", message = "La taille moyenne doit être positive")
private Long tailleMoyenneKB;
/** Temps moyen de génération (en secondes) */
@DecimalMin(value = "0", message = "Le temps moyen de génération doit être positif")
private Integer tempsMoyenGenerationSecondes;
// === CLASSES INTERNES ===
/** Configuration d'une métrique dans le rapport */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class MetriqueConfigDTO {
/** Type de métrique */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Libellé personnalisé */
@Size(max = 200, message = "Le libellé personnalisé ne peut pas dépasser 200 caractères")
private String libellePersonnalise;
/** Position dans le rapport (ordre d'affichage) */
@DecimalMin(value = "1", message = "La position minimum est 1")
private Integer position;
/** Taille d'affichage (1=petit, 2=moyen, 3=grand) */
@DecimalMin(value = "1", message = "La taille minimum est 1")
@DecimalMax(value = "3", message = "La taille maximum est 3")
@Builder.Default
private Integer tailleAffichage = 2;
/** Couleur personnalisée */
@Size(max = 7, message = "La couleur doit être au format #RRGGBB")
private String couleurPersonnalisee;
/** Indicateur si la métrique inclut un graphique */
@Builder.Default private Boolean inclureGraphique = true;
/** Type de graphique (line, bar, pie, area) */
@Size(max = 20, message = "Le type de graphique ne peut pas dépasser 20 caractères")
@Builder.Default
private String typeGraphique = "line";
/** Indicateur si la métrique inclut la tendance */
@Builder.Default private Boolean inclureTendance = true;
/** Indicateur si la métrique inclut la comparaison */
@Builder.Default private Boolean inclureComparaison = true;
/** Seuils d'alerte personnalisés */
private Map<String, Object> seuilsAlerte;
/** Filtres spécifiques à cette métrique */
private Map<String, Object> filtresSpecifiques;
}
/** Configuration d'une section du rapport */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SectionRapportDTO {
/** Nom de la section */
@NotBlank(message = "Le nom de la section est obligatoire")
@Size(max = 200, message = "Le nom de la section ne peut pas dépasser 200 caractères")
private String nom;
/** Description de la section */
@Size(max = 500, message = "La description de la section ne peut pas dépasser 500 caractères")
private String description;
/** Position de la section dans le rapport */
@DecimalMin(value = "1", message = "La position minimum est 1")
private Integer position;
/** Type de section (resume, metriques, graphiques, tableaux, analyse) */
@NotBlank(message = "Le type de section est obligatoire")
@Size(max = 50, message = "Le type de section ne peut pas dépasser 50 caractères")
private String typeSection;
/** Métriques incluses dans cette section */
private List<TypeMetrique> metriquesIncluses;
/** Configuration spécifique de la section */
private Map<String, Object> configurationSection;
/** Indicateur si la section est visible */
@Builder.Default private Boolean visible = true;
/** Indicateur si la section peut être réduite */
@Builder.Default private Boolean pliable = false;
}
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le nombre de métriques configurées
*
* @return Le nombre de métriques
*/
public int getNombreMetriques() {
return metriques != null ? metriques.size() : 0;
}
/**
* Retourne le nombre de sections configurées
*
* @return Le nombre de sections
*/
public int getNombreSections() {
return sections != null ? sections.size() : 0;
}
/**
* Vérifie si le rapport utilise une période personnalisée
*
* @return true si la période est personnalisée
*/
public boolean isPeriodePersonnalisee() {
return periodeAnalyse == PeriodeAnalyse.PERIODE_PERSONNALISEE;
}
/**
* Vérifie si le rapport est confidentiel (niveau >= 4)
*
* @return true si le rapport est confidentiel
*/
/** Indique si le niveau de confidentialité est élevé (pour couverture branches). */
private boolean isNiveauConfidentiel() {
if (niveauConfidentialite == null) return false;
return niveauConfidentialite >= 4;
}
public boolean isConfidentiel() {
return isNiveauConfidentiel();
}
/**
* Vérifie si le rapport nécessite une génération
*
* @return true si la prochaine génération est due
*/
public boolean necessiteGeneration() {
return rapportAutomatique
&& prochaineGeneration != null
&& prochaineGeneration.isBefore(LocalDateTime.now());
}
/**
* Retourne la fréquence de génération en texte
*
* @return La fréquence sous forme de texte
*/
public String getFrequenceTexte() {
if (frequenceGenerationHeures == null) return "Manuelle";
return switch (frequenceGenerationHeures) {
case 1 -> "Toutes les heures";
case 24 -> "Quotidienne";
case 168 -> "Hebdomadaire"; // 24 * 7
case 720 -> "Mensuelle"; // 24 * 30
default -> "Toutes les " + frequenceGenerationHeures + " heures";
};
}
}
package dev.lions.unionflow.server.api.dto.analytics;
import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.FormatExport;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* DTO pour la configuration des rapports analytics UnionFlow
*
* <p>Représente la configuration d'un rapport personnalisé avec ses métriques, sa mise en forme et
* ses paramètres d'export.
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-16
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportConfigDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
/** Nom du rapport */
@NotBlank(message = "Le nom du rapport est obligatoire")
@Size(min = 3, max = 200, message = "Le nom du rapport doit contenir entre 3 et 200 caractères")
private String nom;
/** Description du rapport */
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
/** Type de rapport (executif, analytique, technique, operationnel) */
@NotBlank(message = "Le type de rapport est obligatoire")
@Size(max = 50, message = "Le type de rapport ne peut pas dépasser 50 caractères")
private String typeRapport;
/** Période d'analyse par défaut */
@NotNull(message = "La période d'analyse est obligatoire")
private PeriodeAnalyse periodeAnalyse;
/** Date de début personnalisée (si période personnalisée) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDebutPersonnalisee;
/** Date de fin personnalisée (si période personnalisée) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateFinPersonnalisee;
/** Identifiant de l'organisation (optionnel pour filtrage) */
private UUID organisationId;
/** Nom de l'organisation */
@Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
private String nomOrganisation;
/** Identifiant de l'utilisateur créateur */
@NotNull(message = "L'identifiant de l'utilisateur créateur est obligatoire")
private UUID utilisateurCreateurId;
/** Nom de l'utilisateur créateur */
@Size(max = 200, message = "Le nom de l'utilisateur créateur ne peut pas dépasser 200 caractères")
private String nomUtilisateurCreateur;
/** Métriques incluses dans le rapport */
@NotNull(message = "Les métriques sont obligatoires")
@Valid
private List<MetriqueConfigDTO> metriques;
/** Sections du rapport */
@Valid private List<SectionRapportDTO> sections;
/** Format d'export par défaut */
@NotNull(message = "Le format d'export est obligatoire")
private FormatExport formatExport;
/** Formats d'export autorisés */
private List<FormatExport> formatsExportAutorises;
/** Modèle de rapport à utiliser */
@Size(max = 100, message = "Le modèle de rapport ne peut pas dépasser 100 caractères")
private String modeleRapport;
/** Configuration de la mise en page */
@Size(
max = 2000,
message = "La configuration de mise en page ne peut pas dépasser 2000 caractères")
private String configurationMiseEnPage;
/** Logo personnalisé (URL ou base64) */
@Size(max = 5000, message = "Le logo personnalisé ne peut pas dépasser 5000 caractères")
private String logoPersonnalise;
/** Couleurs personnalisées du rapport */
private Map<String, String> couleursPersonnalisees;
/** Indicateur si le rapport est public */
@Builder.Default private Boolean rapportPublic = false;
/** Indicateur si le rapport est automatique */
@Builder.Default private Boolean rapportAutomatique = false;
/** Fréquence de génération automatique (en heures) */
@DecimalMin(value = "1", message = "La fréquence minimum est 1 heure")
private Integer frequenceGenerationHeures;
/** Prochaine génération automatique */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime prochaineGeneration;
/** Liste des destinataires pour l'envoi automatique */
private List<String> destinatairesEmail;
/** Objet de l'email pour l'envoi automatique */
@Size(max = 200, message = "L'objet de l'email ne peut pas dépasser 200 caractères")
private String objetEmail;
/** Corps de l'email pour l'envoi automatique */
@Size(max = 2000, message = "Le corps de l'email ne peut pas dépasser 2000 caractères")
private String corpsEmail;
/** Paramètres de filtrage avancé */
private Map<String, Object> parametresFiltrage;
/** Tags pour catégoriser le rapport */
private List<String> tags;
/** Niveau de confidentialité (1=public, 5=confidentiel) */
@DecimalMin(value = "1", message = "Le niveau de confidentialité minimum est 1")
@DecimalMax(value = "5", message = "Le niveau de confidentialité maximum est 5")
@Builder.Default
private Integer niveauConfidentialite = 1;
/** Date de dernière génération */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateDerniereGeneration;
/** Nombre de générations effectuées */
@DecimalMin(value = "0", message = "Le nombre de générations doit être positif")
@Builder.Default
private Integer nombreGenerations = 0;
/** Taille moyenne des rapports générés (en KB) */
@DecimalMin(value = "0", message = "La taille moyenne doit être positive")
private Long tailleMoyenneKB;
/** Temps moyen de génération (en secondes) */
@DecimalMin(value = "0", message = "Le temps moyen de génération doit être positif")
private Integer tempsMoyenGenerationSecondes;
// === CLASSES INTERNES ===
/** Configuration d'une métrique dans le rapport */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class MetriqueConfigDTO {
/** Type de métrique */
@NotNull(message = "Le type de métrique est obligatoire")
private TypeMetrique typeMetrique;
/** Libellé personnalisé */
@Size(max = 200, message = "Le libellé personnalisé ne peut pas dépasser 200 caractères")
private String libellePersonnalise;
/** Position dans le rapport (ordre d'affichage) */
@DecimalMin(value = "1", message = "La position minimum est 1")
private Integer position;
/** Taille d'affichage (1=petit, 2=moyen, 3=grand) */
@DecimalMin(value = "1", message = "La taille minimum est 1")
@DecimalMax(value = "3", message = "La taille maximum est 3")
@Builder.Default
private Integer tailleAffichage = 2;
/** Couleur personnalisée */
@Size(max = 7, message = "La couleur doit être au format #RRGGBB")
private String couleurPersonnalisee;
/** Indicateur si la métrique inclut un graphique */
@Builder.Default private Boolean inclureGraphique = true;
/** Type de graphique (line, bar, pie, area) */
@Size(max = 20, message = "Le type de graphique ne peut pas dépasser 20 caractères")
@Builder.Default
private String typeGraphique = "line";
/** Indicateur si la métrique inclut la tendance */
@Builder.Default private Boolean inclureTendance = true;
/** Indicateur si la métrique inclut la comparaison */
@Builder.Default private Boolean inclureComparaison = true;
/** Seuils d'alerte personnalisés */
private Map<String, Object> seuilsAlerte;
/** Filtres spécifiques à cette métrique */
private Map<String, Object> filtresSpecifiques;
}
/** Configuration d'une section du rapport */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class SectionRapportDTO {
/** Nom de la section */
@NotBlank(message = "Le nom de la section est obligatoire")
@Size(max = 200, message = "Le nom de la section ne peut pas dépasser 200 caractères")
private String nom;
/** Description de la section */
@Size(max = 500, message = "La description de la section ne peut pas dépasser 500 caractères")
private String description;
/** Position de la section dans le rapport */
@DecimalMin(value = "1", message = "La position minimum est 1")
private Integer position;
/** Type de section (resume, metriques, graphiques, tableaux, analyse) */
@NotBlank(message = "Le type de section est obligatoire")
@Size(max = 50, message = "Le type de section ne peut pas dépasser 50 caractères")
private String typeSection;
/** Métriques incluses dans cette section */
private List<TypeMetrique> metriquesIncluses;
/** Configuration spécifique de la section */
private Map<String, Object> configurationSection;
/** Indicateur si la section est visible */
@Builder.Default private Boolean visible = true;
/** Indicateur si la section peut être réduite */
@Builder.Default private Boolean pliable = false;
}
// === MÉTHODES UTILITAIRES ===
/**
* Retourne le nombre de métriques configurées
*
* @return Le nombre de métriques
*/
public int getNombreMetriques() {
return metriques != null ? metriques.size() : 0;
}
/**
* Retourne le nombre de sections configurées
*
* @return Le nombre de sections
*/
public int getNombreSections() {
return sections != null ? sections.size() : 0;
}
/**
* Vérifie si le rapport utilise une période personnalisée
*
* @return true si la période est personnalisée
*/
public boolean isPeriodePersonnalisee() {
return periodeAnalyse == PeriodeAnalyse.PERIODE_PERSONNALISEE;
}
/**
* Vérifie si le rapport est confidentiel (niveau >= 4)
*
* @return true si le rapport est confidentiel
*/
/** Indique si le niveau de confidentialité est élevé (pour couverture branches). */
private boolean isNiveauConfidentiel() {
if (niveauConfidentialite == null) return false;
return niveauConfidentialite >= 4;
}
public boolean isConfidentiel() {
return isNiveauConfidentiel();
}
/**
* Vérifie si le rapport nécessite une génération
*
* @return true si la prochaine génération est due
*/
public boolean necessiteGeneration() {
return rapportAutomatique
&& prochaineGeneration != null
&& prochaineGeneration.isBefore(LocalDateTime.now());
}
/**
* Retourne la fréquence de génération en texte
*
* @return La fréquence sous forme de texte
*/
public String getFrequenceTexte() {
if (frequenceGenerationHeures == null) return "Manuelle";
return switch (frequenceGenerationHeures) {
case 1 -> "Toutes les heures";
case 24 -> "Quotidienne";
case 168 -> "Hebdomadaire"; // 24 * 7
case 720 -> "Mensuelle"; // 24 * 30
default -> "Toutes les " + frequenceGenerationHeures + " heures";
};
}
}

View File

@@ -0,0 +1,36 @@
package dev.lions.unionflow.server.api.dto.audit.response;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Builder;
/**
* Vue d'une opération d'audit trail enrichi (Sprint 1, exposée Sprint 10).
*
* <p>Les payloads JSONB ({@code payloadAvant}, {@code payloadApres}, {@code metadata})
* sont sérialisés en string ; le frontend les parse selon les besoins.
*
* @since 2026-04-25 (Sprint 10)
*/
@Builder
public record AuditTrailOperationResponse(
UUID id,
UUID userId,
String userEmail,
String roleActif,
UUID organisationActiveId,
String actionType,
String entityType,
UUID entityId,
String description,
String ipAddress,
String userAgent,
UUID requestId,
String payloadAvant,
String payloadApres,
String metadata,
Boolean sodCheckPassed,
String sodViolations,
LocalDateTime operationAt
) {
}

View File

@@ -1,32 +1,32 @@
package dev.lions.unionflow.server.api.dto.auth.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Builder;
/**
* Requête d'authentification utilisateur.
*
* @param username Email ou nom d'utilisateur
* @param password Mot de passe
* @param typeCompte Type de compte (MEMBRE, ADMIN, etc.)
* @param rememberMe Se souvenir de moi
* @author UnionFlow Team
* @version 2.0
* @since 2026-02-28
*/
@Builder
public record LoginRequest(
@NotBlank(message = "L'email ou nom d'utilisateur est requis")
@Size(min = 3, max = 100, message = "L'email ou nom d'utilisateur doit contenir entre 3 et 100 caractères")
String username,
@NotBlank(message = "Le mot de passe est requis")
@Size(min = 6, message = "Le mot de passe doit contenir au moins 6 caractères")
String password,
@NotBlank(message = "Le type de compte est requis")
String typeCompte,
Boolean rememberMe) {
}
package dev.lions.unionflow.server.api.dto.auth.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Builder;
/**
* Requête d'authentification utilisateur.
*
* @param username Email ou nom d'utilisateur
* @param password Mot de passe
* @param typeCompte Type de compte (MEMBRE, ADMIN, etc.)
* @param rememberMe Se souvenir de moi
* @author UnionFlow Team
* @version 2.0
* @since 2026-02-28
*/
@Builder
public record LoginRequest(
@NotBlank(message = "L'email ou nom d'utilisateur est requis")
@Size(min = 3, max = 100, message = "L'email ou nom d'utilisateur doit contenir entre 3 et 100 caractères")
String username,
@NotBlank(message = "Le mot de passe est requis")
@Size(min = 6, message = "Le mot de passe doit contenir au moins 6 caractères")
String password,
@NotBlank(message = "Le type de compte est requis")
String typeCompte,
Boolean rememberMe) {
}

View File

@@ -1,90 +1,90 @@
package dev.lions.unionflow.server.api.dto.auth.response;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse d'authentification contenant le token JWT et les informations utilisateur.
*
* @author UnionFlow Team
* @version 2.0
* @since 2026-02-28
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LoginResponse {
private String accessToken;
private String refreshToken;
@Builder.Default
private String tokenType = "Bearer";
private Long expiresIn;
private LocalDateTime expirationDate;
private UserInfo user;
/**
* Vérifie si le token est expiré.
*
* @return true si le token est expiré
*/
public boolean isExpired() {
return expirationDate != null && LocalDateTime.now().isAfter(expirationDate);
}
/**
* Informations de l'utilisateur connecté.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class UserInfo {
private UUID id;
private String nom;
private String prenom;
private String email;
private String username;
private String typeCompte;
private List<String> roles;
private List<String> permissions;
private EntiteInfo entite;
/**
* Retourne le nom complet de l'utilisateur.
*
* @return Prénom + Nom ou nom d'utilisateur si absent
*/
public String getNomComplet() {
if (prenom != null && nom != null) {
return prenom + " " + nom;
}
return nom != null ? nom : username;
}
}
/**
* Informations sur l'entité (organisation/membre) de l'utilisateur.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class EntiteInfo {
private UUID id;
private String nom;
private String type;
private String pays;
private String ville;
}
}
package dev.lions.unionflow.server.api.dto.auth.response;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse d'authentification contenant le token JWT et les informations utilisateur.
*
* @author UnionFlow Team
* @version 2.0
* @since 2026-02-28
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LoginResponse {
private String accessToken;
private String refreshToken;
@Builder.Default
private String tokenType = "Bearer";
private Long expiresIn;
private LocalDateTime expirationDate;
private UserInfo user;
/**
* Vérifie si le token est expiré.
*
* @return true si le token est expiré
*/
public boolean isExpired() {
return expirationDate != null && LocalDateTime.now().isAfter(expirationDate);
}
/**
* Informations de l'utilisateur connecté.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class UserInfo {
private UUID id;
private String nom;
private String prenom;
private String email;
private String username;
private String typeCompte;
private List<String> roles;
private List<String> permissions;
private EntiteInfo entite;
/**
* Retourne le nom complet de l'utilisateur.
*
* @return Prénom + Nom ou nom d'utilisateur si absent
*/
public String getNomComplet() {
if (prenom != null && nom != null) {
return prenom + " " + nom;
}
return nom != null ? nom : username;
}
}
/**
* Informations sur l'entité (organisation/membre) de l'utilisateur.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class EntiteInfo {
private UUID id;
private String nom;
private String type;
private String pays;
private String ville;
}
}

View File

@@ -1,42 +1,42 @@
package dev.lions.unionflow.server.api.dto.ayantdroit;
import dev.lions.unionflow.server.api.enums.ayantdroit.LienParente;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Past;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AyantDroitRequest {
@NotBlank(message = "L'Id du membre principal rattaché est obligatoire")
private String membrePrincipalId;
@NotBlank(message = "Le prénom est obligatoire")
private String prenom;
@NotBlank(message = "Le nom est obligatoire")
private String nom;
@NotNull(message = "La date de naissance est obligatoire pour l'âge limite")
@Past(message = "La date de naissance doit être dans le passé")
private LocalDate dateNaissance;
private String sexe;
private String pieceIdentite;
@NotNull(message = "Le lien de parenté / bénéfice est requis")
private LienParente lienParente;
// Id document du livret de famille ou certificat médical / scolaire
private String justificatifLienId;
}
package dev.lions.unionflow.server.api.dto.ayantdroit;
import dev.lions.unionflow.server.api.enums.ayantdroit.LienParente;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Past;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AyantDroitRequest {
@NotBlank(message = "L'Id du membre principal rattaché est obligatoire")
private String membrePrincipalId;
@NotBlank(message = "Le prénom est obligatoire")
private String prenom;
@NotBlank(message = "Le nom est obligatoire")
private String nom;
@NotNull(message = "La date de naissance est obligatoire pour l'âge limite")
@Past(message = "La date de naissance doit être dans le passé")
private LocalDate dateNaissance;
private String sexe;
private String pieceIdentite;
@NotNull(message = "Le lien de parenté / bénéfice est requis")
private LienParente lienParente;
// Id document du livret de famille ou certificat médical / scolaire
private String justificatifLienId;
}

View File

@@ -1,40 +1,40 @@
package dev.lions.unionflow.server.api.dto.ayantdroit;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.ayantdroit.LienParente;
import dev.lions.unionflow.server.api.enums.ayantdroit.StatutAyantDroit;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.math.BigDecimal;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AyantDroitResponse extends BaseDTO {
private String membrePrincipalId;
private String numeroCarteBeneficiaire;
private String prenom;
private String nom;
private LocalDate dateNaissance;
private Integer ageActuel;
private String sexe;
private String pieceIdentite;
private LienParente lienParente;
// Prise en charge (%) par rapport à la couverture du Membre Principal
private BigDecimal pourcentageCouvertureSante;
private StatutAyantDroit statut;
}
package dev.lions.unionflow.server.api.dto.ayantdroit;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.ayantdroit.LienParente;
import dev.lions.unionflow.server.api.enums.ayantdroit.StatutAyantDroit;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.math.BigDecimal;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AyantDroitResponse extends BaseDTO {
private String membrePrincipalId;
private String numeroCarteBeneficiaire;
private String prenom;
private String nom;
private LocalDate dateNaissance;
private Integer ageActuel;
private String sexe;
private String pieceIdentite;
private LienParente lienParente;
// Prise en charge (%) par rapport à la couverture du Membre Principal
private BigDecimal pourcentageCouvertureSante;
private StatutAyantDroit statut;
}

View File

@@ -1,28 +1,28 @@
package dev.lions.unionflow.server.api.dto.backup.request;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Request pour créer une sauvegarde
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CreateBackupRequest {
@NotBlank(message = "Le nom de la sauvegarde est requis")
private String name;
private String description;
private String type; // AUTO, MANUAL, RESTORE_POINT
private Boolean includeDatabase;
private Boolean includeFiles;
private Boolean includeConfiguration;
}
package dev.lions.unionflow.server.api.dto.backup.request;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Request pour créer une sauvegarde
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CreateBackupRequest {
@NotBlank(message = "Le nom de la sauvegarde est requis")
private String name;
private String description;
private String type; // AUTO, MANUAL, RESTORE_POINT
private Boolean includeDatabase;
private Boolean includeFiles;
private Boolean includeConfiguration;
}

View File

@@ -1,28 +1,28 @@
package dev.lions.unionflow.server.api.dto.backup.request;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.UUID;
/**
* Request pour restaurer une sauvegarde
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RestoreBackupRequest {
@NotNull(message = "L'ID de la sauvegarde est requis")
private UUID backupId;
private Boolean restoreDatabase;
private Boolean restoreFiles;
private Boolean restoreConfiguration;
private Boolean createRestorePoint; // Créer un point de restauration avant
}
package dev.lions.unionflow.server.api.dto.backup.request;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.UUID;
/**
* Request pour restaurer une sauvegarde
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RestoreBackupRequest {
@NotNull(message = "L'ID de la sauvegarde est requis")
private UUID backupId;
private Boolean restoreDatabase;
private Boolean restoreFiles;
private Boolean restoreConfiguration;
private Boolean createRestorePoint; // Créer un point de restauration avant
}

View File

@@ -1,25 +1,25 @@
package dev.lions.unionflow.server.api.dto.backup.request;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Request pour mettre à jour la configuration des sauvegardes automatiques
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpdateBackupConfigRequest {
private Boolean autoBackupEnabled;
private String frequency; // HOURLY, DAILY, WEEKLY
private String retention; // "7 jours", "30 jours", "90 jours", "1 an"
private Integer retentionDays;
private String backupTime; // Format HH:mm pour sauvegarde quotidienne
private Boolean includeDatabase;
private Boolean includeFiles;
private Boolean includeConfiguration;
}
package dev.lions.unionflow.server.api.dto.backup.request;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Request pour mettre à jour la configuration des sauvegardes automatiques
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpdateBackupConfigRequest {
private Boolean autoBackupEnabled;
private String frequency; // HOURLY, DAILY, WEEKLY
private String retention; // "7 jours", "30 jours", "90 jours", "1 an"
private Integer retentionDays;
private String backupTime; // Format HH:mm pour sauvegarde quotidienne
private Boolean includeDatabase;
private Boolean includeFiles;
private Boolean includeConfiguration;
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.backup.response;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* Response contenant la configuration des sauvegardes automatiques
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BackupConfigResponse {
private Boolean autoBackupEnabled;
private String frequency; // HOURLY, DAILY, WEEKLY
private String retention; // "7 jours", "30 jours", etc.
private Integer retentionDays;
private String backupTime; // HH:mm
private Boolean includeDatabase;
private Boolean includeFiles;
private Boolean includeConfiguration;
private LocalDateTime lastBackup;
private LocalDateTime nextScheduledBackup;
private Integer totalBackups;
private Long totalSizeBytes;
private String totalSizeFormatted;
}
package dev.lions.unionflow.server.api.dto.backup.response;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* Response contenant la configuration des sauvegardes automatiques
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BackupConfigResponse {
private Boolean autoBackupEnabled;
private String frequency; // HOURLY, DAILY, WEEKLY
private String retention; // "7 jours", "30 jours", etc.
private Integer retentionDays;
private String backupTime; // HH:mm
private Boolean includeDatabase;
private Boolean includeFiles;
private Boolean includeConfiguration;
private LocalDateTime lastBackup;
private LocalDateTime nextScheduledBackup;
private Integer totalBackups;
private Long totalSizeBytes;
private String totalSizeFormatted;
}

View File

@@ -1,37 +1,37 @@
package dev.lions.unionflow.server.api.dto.backup.response;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* Response représentant une sauvegarde
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BackupResponse {
private UUID id;
private String name;
private String description;
private String type; // AUTO, MANUAL, RESTORE_POINT
private Long sizeBytes;
private String sizeFormatted; // ex: "2.3 GB"
private String status; // PENDING, IN_PROGRESS, COMPLETED, FAILED
private LocalDateTime createdAt;
private LocalDateTime completedAt;
private String createdBy;
private Boolean includesDatabase;
private Boolean includesFiles;
private Boolean includesConfiguration;
private String filePath;
private String errorMessage; // Si status = FAILED
}
package dev.lions.unionflow.server.api.dto.backup.response;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* Response représentant une sauvegarde
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BackupResponse {
private UUID id;
private String name;
private String description;
private String type; // AUTO, MANUAL, RESTORE_POINT
private Long sizeBytes;
private String sizeFormatted; // ex: "2.3 GB"
private String status; // PENDING, IN_PROGRESS, COMPLETED, FAILED
private LocalDateTime createdAt;
private LocalDateTime completedAt;
private String createdBy;
private Boolean includesDatabase;
private Boolean includesFiles;
private Boolean includesConfiguration;
private String filePath;
private String errorMessage; // Si status = FAILED
}

View File

@@ -1,164 +1,164 @@
package dev.lions.unionflow.server.api.dto.base;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
/**
* Classe de base pour tous les DTOs UnionFlow Fournit les propriétés communes
* d'audit et de gestion
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-10
*/
@Getter
@Setter
public abstract class BaseDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** Identifiant unique UUID */
private UUID id;
/** Date de création de l'enregistrement */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public LocalDateTime dateCreation;
/** Date de dernière modification */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public LocalDateTime dateModification;
/** Utilisateur qui a créé l'enregistrement */
private String creePar;
/** Utilisateur qui a modifié l'enregistrement en dernier */
private String modifiePar;
/** Version pour gestion de la concurrence optimiste */
private Long version;
/** Indicateur si l'enregistrement est actif */
private Boolean actif;
// Constructeur par défaut
public BaseDTO() {
this.dateCreation = LocalDateTime.now();
this.actif = true;
this.version = 0L;
}
// Getters et Setters générés automatiquement par Lombok @Getter/@Setter
// Méthodes utilitaires
/**
* Marque l'entité comme nouvellement créée
*
* @param utilisateur L'utilisateur qui crée l'entité
*/
public void marquerCommeNouveau(String utilisateur) {
LocalDateTime maintenant = LocalDateTime.now();
this.dateCreation = maintenant;
this.dateModification = maintenant;
this.creePar = utilisateur;
this.modifiePar = utilisateur;
this.version = 0L;
this.actif = true;
}
/**
* Marque l'entité comme modifiée
*
* @param utilisateur L'utilisateur qui modifie l'entité
*/
public void marquerCommeModifie(String utilisateur) {
this.dateModification = LocalDateTime.now();
this.modifiePar = utilisateur;
if (this.version != null) {
this.version++;
}
}
/**
* Désactive l'entité (soft delete)
*
* @param utilisateur L'utilisateur qui désactive l'entité
*/
public void desactiver(String utilisateur) {
this.actif = false;
marquerCommeModifie(utilisateur);
}
/**
* Réactive l'entité
*
* @param utilisateur L'utilisateur qui réactive l'entité
*/
public void reactiver(String utilisateur) {
this.actif = true;
marquerCommeModifie(utilisateur);
}
/**
* Vérifie si l'entité est nouvelle (pas encore persistée)
*
* @return true si l'entité est nouvelle
*/
public boolean isNouveau() {
return id == null;
}
/**
* Vérifie si l'entité est active
*
* @return true si l'entité est active
*/
public boolean isActif() {
return Boolean.TRUE.equals(actif);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
BaseDTO baseDTO = (BaseDTO) obj;
return id != null && id.equals(baseDTO.id);
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
@Override
public String toString() {
return getClass().getSimpleName()
+ "{"
+ "id="
+ id
+ ", dateCreation="
+ dateCreation
+ ", dateModification="
+ dateModification
+ ", creePar='"
+ creePar
+ '\''
+ ", modifiePar='"
+ modifiePar
+ '\''
+ ", version="
+ version
+ ", actif="
+ actif
+ '}';
}
}
package dev.lions.unionflow.server.api.dto.base;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
/**
* Classe de base pour tous les DTOs UnionFlow Fournit les propriétés communes
* d'audit et de gestion
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-10
*/
@Getter
@Setter
public abstract class BaseDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** Identifiant unique UUID */
private UUID id;
/** Date de création de l'enregistrement */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public LocalDateTime dateCreation;
/** Date de dernière modification */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public LocalDateTime dateModification;
/** Utilisateur qui a créé l'enregistrement */
private String creePar;
/** Utilisateur qui a modifié l'enregistrement en dernier */
private String modifiePar;
/** Version pour gestion de la concurrence optimiste */
private Long version;
/** Indicateur si l'enregistrement est actif */
private Boolean actif;
// Constructeur par défaut
public BaseDTO() {
this.dateCreation = LocalDateTime.now();
this.actif = true;
this.version = 0L;
}
// Getters et Setters générés automatiquement par Lombok @Getter/@Setter
// Méthodes utilitaires
/**
* Marque l'entité comme nouvellement créée
*
* @param utilisateur L'utilisateur qui crée l'entité
*/
public void marquerCommeNouveau(String utilisateur) {
LocalDateTime maintenant = LocalDateTime.now();
this.dateCreation = maintenant;
this.dateModification = maintenant;
this.creePar = utilisateur;
this.modifiePar = utilisateur;
this.version = 0L;
this.actif = true;
}
/**
* Marque l'entité comme modifiée
*
* @param utilisateur L'utilisateur qui modifie l'entité
*/
public void marquerCommeModifie(String utilisateur) {
this.dateModification = LocalDateTime.now();
this.modifiePar = utilisateur;
if (this.version != null) {
this.version++;
}
}
/**
* Désactive l'entité (soft delete)
*
* @param utilisateur L'utilisateur qui désactive l'entité
*/
public void desactiver(String utilisateur) {
this.actif = false;
marquerCommeModifie(utilisateur);
}
/**
* Réactive l'entité
*
* @param utilisateur L'utilisateur qui réactive l'entité
*/
public void reactiver(String utilisateur) {
this.actif = true;
marquerCommeModifie(utilisateur);
}
/**
* Vérifie si l'entité est nouvelle (pas encore persistée)
*
* @return true si l'entité est nouvelle
*/
public boolean isNouveau() {
return id == null;
}
/**
* Vérifie si l'entité est active
*
* @return true si l'entité est active
*/
public boolean isActif() {
return Boolean.TRUE.equals(actif);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
BaseDTO baseDTO = (BaseDTO) obj;
return id != null && id.equals(baseDTO.id);
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
@Override
public String toString() {
return getClass().getSimpleName()
+ "{"
+ "id="
+ id
+ ", dateCreation="
+ dateCreation
+ ", dateModification="
+ dateModification
+ ", creePar='"
+ creePar
+ '\''
+ ", modifiePar='"
+ modifiePar
+ '\''
+ ", version="
+ version
+ ", actif="
+ actif
+ '}';
}
}

View File

@@ -1,79 +1,79 @@
package dev.lions.unionflow.server.api.dto.base;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
/**
* Classe de base pour les DTOs de réponse.
*
* <p>
* Contient les champs d'audit communs à toutes
* les réponses : identifiant, dates de
* création/modification, créateur, et version.
*
* <p>
* Les DTOs de type Request ne doivent
* <b>pas</b> hériter de cette classe (utiliser
* des {@code record} sans héritage).
*
* @author UnionFlow Team
* @version 3.0
* @since 2026-02-21
*/
@Getter
@Setter
public abstract class BaseResponse {
/** Identifiant unique de l'entité. */
private UUID id;
/** Date de création de l'entité. */
private LocalDateTime dateCreation;
/** Date de dernière modification. */
private LocalDateTime dateModification;
/** Email du créateur. */
private String creePar;
/** Email du dernier modificateur. */
private String modifiePar;
/** Version pour l'optimistic locking. */
private Long version;
/** État actif/inactif (soft-delete). */
private Boolean actif;
/**
* Comparaison basée sur l'ID.
* Deux BaseResponse sont égaux si leurs IDs sont égaux et non null.
*
* @param obj Objet à comparer
* @return true si les objets ont le même ID
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BaseResponse that = (BaseResponse) obj;
return id != null && id.equals(that.id);
}
/**
* Hash code basé sur l'ID.
*
* @return Hash code de l'ID, ou 0 si ID null
*/
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
package dev.lions.unionflow.server.api.dto.base;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
/**
* Classe de base pour les DTOs de réponse.
*
* <p>
* Contient les champs d'audit communs à toutes
* les réponses : identifiant, dates de
* création/modification, créateur, et version.
*
* <p>
* Les DTOs de type Request ne doivent
* <b>pas</b> hériter de cette classe (utiliser
* des {@code record} sans héritage).
*
* @author UnionFlow Team
* @version 3.0
* @since 2026-02-21
*/
@Getter
@Setter
public abstract class BaseResponse {
/** Identifiant unique de l'entité. */
private UUID id;
/** Date de création de l'entité. */
private LocalDateTime dateCreation;
/** Date de dernière modification. */
private LocalDateTime dateModification;
/** Email du créateur. */
private String creePar;
/** Email du dernier modificateur. */
private String modifiePar;
/** Version pour l'optimistic locking. */
private Long version;
/** État actif/inactif (soft-delete). */
private Boolean actif;
/**
* Comparaison basée sur l'ID.
* Deux BaseResponse sont égaux si leurs IDs sont égaux et non null.
*
* @param obj Objet à comparer
* @return true si les objets ont le même ID
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BaseResponse that = (BaseResponse) obj;
return id != null && id.equals(that.id);
}
/**
* Hash code basé sur l'ID.
*
* @return Hash code de l'ID, ou 0 si ID null
*/
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}

View File

@@ -1,59 +1,59 @@
package dev.lions.unionflow.server.api.dto.base;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Réponse paginée générique.
*
* <p>
* Encapsule une liste d'éléments avec les
* métadonnées de pagination (page, taille,
* total). Utilisable pour tout type de réponse
* paginée.
*
* @param <T> type des éléments de la page
* @author UnionFlow Team
* @version 3.0
* @since 2026-02-21
*/
@Getter
@AllArgsConstructor
public class PageResponse<T> {
/** Éléments de la page courante. */
private final List<T> contenu;
/** Numéro de page courant (0-indexed). */
private final int page;
/** Taille de la page demandée. */
private final int taille;
/** Nombre total d'éléments. */
private final long totalElements;
/** Nombre total de pages. */
private final int totalPages;
/**
* Indique s'il existe une page suivante.
*
* @return {@code true} si une page suivante
* existe
*/
public boolean hasNext() {
return page < totalPages - 1;
}
/**
* Indique s'il existe une page précédente.
*
* @return {@code true} si une page précédente
* existe
*/
public boolean hasPrevious() {
return page > 0;
}
}
package dev.lions.unionflow.server.api.dto.base;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Réponse paginée générique.
*
* <p>
* Encapsule une liste d'éléments avec les
* métadonnées de pagination (page, taille,
* total). Utilisable pour tout type de réponse
* paginée.
*
* @param <T> type des éléments de la page
* @author UnionFlow Team
* @version 3.0
* @since 2026-02-21
*/
@Getter
@AllArgsConstructor
public class PageResponse<T> {
/** Éléments de la page courante. */
private final List<T> contenu;
/** Numéro de page courant (0-indexed). */
private final int page;
/** Taille de la page demandée. */
private final int taille;
/** Nombre total d'éléments. */
private final long totalElements;
/** Nombre total de pages. */
private final int totalPages;
/**
* Indique s'il existe une page suivante.
*
* @return {@code true} si une page suivante
* existe
*/
public boolean hasNext() {
return page < totalPages - 1;
}
/**
* Indique s'il existe une page précédente.
*
* @return {@code true} si une page précédente
* existe
*/
public boolean hasPrevious() {
return page > 0;
}
}

View File

@@ -1,45 +1,45 @@
package dev.lions.unionflow.server.api.dto.collectefonds;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.collectefonds.StatutCampagneCollecte;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Campagne de levée de fonds (Crowdfunding / ONG)")
public class CampagneCollecteResponse extends BaseDTO {
private String organisationId;
private String titre;
private String courteDescription;
private String htmlDescriptionComplete;
private String imageBanniereUrl;
@Schema(description = "Objectif monétaire escompté")
private BigDecimal objectifFinancier;
@Schema(description = "Somme totale déjà récoltée sur cette campagne")
private BigDecimal montantCollecteActuel;
private Integer nombreDonateurs;
private StatutCampagneCollecte statut;
private LocalDateTime dateOuverture;
private LocalDateTime dateCloturePrevue;
// Si la page est visible pour les non-membres (donateurs externes)
private Boolean estPublique;
}
package dev.lions.unionflow.server.api.dto.collectefonds;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.collectefonds.StatutCampagneCollecte;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Campagne de levée de fonds (Crowdfunding / ONG)")
public class CampagneCollecteResponse extends BaseDTO {
private String organisationId;
private String titre;
private String courteDescription;
private String htmlDescriptionComplete;
private String imageBanniereUrl;
@Schema(description = "Objectif monétaire escompté")
private BigDecimal objectifFinancier;
@Schema(description = "Somme totale déjà récoltée sur cette campagne")
private BigDecimal montantCollecteActuel;
private Integer nombreDonateurs;
private StatutCampagneCollecte statut;
private LocalDateTime dateOuverture;
private LocalDateTime dateCloturePrevue;
// Si la page est visible pour les non-membres (donateurs externes)
private Boolean estPublique;
}

View File

@@ -1,42 +1,42 @@
package dev.lions.unionflow.server.api.dto.collectefonds;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Don transactionnel reçu pour une campagne de collecte")
public class ContributionCollecteDTO extends BaseDTO {
private String campagneId;
@Schema(description = "Id du membre (Null si le don est public/externe)")
private String membreDonateurId;
@Schema(description = "Nom affiché si don public ou pour le mur de contributeurs")
private String aliasDonateur;
private Boolean estAnonyme;
private BigDecimal montantSoutien;
private String messageSoutien;
private LocalDateTime dateContribution;
// Lien avec la passerelle de paiement
private String transactionPaiementId;
private StatutTransactionWave statutPaiement;
}
package dev.lions.unionflow.server.api.dto.collectefonds;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Don transactionnel reçu pour une campagne de collecte")
public class ContributionCollecteDTO extends BaseDTO {
private String campagneId;
@Schema(description = "Id du membre (Null si le don est public/externe)")
private String membreDonateurId;
@Schema(description = "Nom affiché si don public ou pour le mur de contributeurs")
private String aliasDonateur;
private Boolean estAnonyme;
private BigDecimal montantSoutien;
private String messageSoutien;
private LocalDateTime dateContribution;
// Lien avec la passerelle de paiement
private String transactionPaiementId;
private StatutTransactionWave statutPaiement;
}

View File

@@ -1,13 +1,22 @@
package dev.lions.unionflow.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO d'erreur unifié retourné par tous les endpoints REST.
* Remplace les ErrorResponse locales dupliquées dans chaque Resource.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ErrorResponse(String message, String error) {
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorResponse {
private String message;
private String error;
/** Constructeur pratique avec message uniquement (cas le plus courant). */
public static ErrorResponse of(String message) {

View File

@@ -1,125 +1,125 @@
package dev.lions.unionflow.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* DTO générique pour les réponses paginées de l'API.
* Utilisé par tous les endpoints REST qui renvoient des données paginées.
*
* @param <T> Type des éléments dans la liste
* @author UnionFlow Team
* @version 2.0
*/
public class PagedResponse<T> {
@JsonProperty("data")
private List<T> data;
@JsonProperty("total")
private Long total;
@JsonProperty("page")
private Integer page;
@JsonProperty("size")
private Integer size;
@JsonProperty("totalPages")
private Integer totalPages;
// Constructeurs
public PagedResponse() {
}
public PagedResponse(List<T> data, Long total, Integer page, Integer size) {
this.data = data;
this.total = total;
this.page = page;
this.size = size;
this.totalPages = calculateTotalPages(total, size);
}
public PagedResponse(List<T> data, Long total, Integer page, Integer size, Integer totalPages) {
this.data = data;
this.total = total;
this.page = page;
this.size = size;
this.totalPages = totalPages;
}
// Méthode utilitaire pour calculer le nombre de pages
private Integer calculateTotalPages(Long total, Integer size) {
if (size == null || size == 0 || total == null) {
return 0;
}
return (int) Math.ceil((double) total / size);
}
// Getters et setters
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
this.totalPages = calculateTotalPages(total, this.size);
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
this.totalPages = calculateTotalPages(this.total, size);
}
public Integer getTotalPages() {
return totalPages;
}
public void setTotalPages(Integer totalPages) {
this.totalPages = totalPages;
}
// Méthodes utilitaires
public boolean hasNext() {
return page != null && totalPages != null && page < totalPages - 1;
}
public boolean hasPrevious() {
return page != null && page > 0;
}
public boolean isEmpty() {
return data == null || data.isEmpty();
}
@Override
public String toString() {
return "PagedResponse{" +
"total=" + total +
", page=" + page +
", size=" + size +
", totalPages=" + totalPages +
", itemsCount=" + (data != null ? data.size() : 0) +
'}';
}
}
package dev.lions.unionflow.server.api.dto.common;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* DTO générique pour les réponses paginées de l'API.
* Utilisé par tous les endpoints REST qui renvoient des données paginées.
*
* @param <T> Type des éléments dans la liste
* @author UnionFlow Team
* @version 2.0
*/
public class PagedResponse<T> {
@JsonProperty("data")
private List<T> data;
@JsonProperty("total")
private Long total;
@JsonProperty("page")
private Integer page;
@JsonProperty("size")
private Integer size;
@JsonProperty("totalPages")
private Integer totalPages;
// Constructeurs
public PagedResponse() {
}
public PagedResponse(List<T> data, Long total, Integer page, Integer size) {
this.data = data;
this.total = total;
this.page = page;
this.size = size;
this.totalPages = calculateTotalPages(total, size);
}
public PagedResponse(List<T> data, Long total, Integer page, Integer size, Integer totalPages) {
this.data = data;
this.total = total;
this.page = page;
this.size = size;
this.totalPages = totalPages;
}
// Méthode utilitaire pour calculer le nombre de pages
private Integer calculateTotalPages(Long total, Integer size) {
if (size == null || size == 0 || total == null) {
return 0;
}
return (int) Math.ceil((double) total / size);
}
// Getters et setters
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
this.totalPages = calculateTotalPages(total, this.size);
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
this.totalPages = calculateTotalPages(this.total, size);
}
public Integer getTotalPages() {
return totalPages;
}
public void setTotalPages(Integer totalPages) {
this.totalPages = totalPages;
}
// Méthodes utilitaires
public boolean hasNext() {
return page != null && totalPages != null && page < totalPages - 1;
}
public boolean hasPrevious() {
return page != null && page > 0;
}
public boolean isEmpty() {
return data == null || data.isEmpty();
}
@Override
public String toString() {
return "PagedResponse{" +
"total=" + total +
", page=" + page +
", size=" + size +
", totalPages=" + totalPages +
", itemsCount=" + (data != null ? data.size() : 0) +
'}';
}
}

View File

@@ -1,27 +1,27 @@
package dev.lions.unionflow.server.api.dto.communication.request;
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;
import java.util.List;
import java.util.UUID;
/**
* Request DTO pour créer une conversation
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-16
*/
@Builder
public record CreateConversationRequest(
@NotBlank @Size(max = 255) String name,
@Size(max = 1000) String description,
@NotNull ConversationType type,
@NotEmpty List<UUID> participantIds,
UUID organisationId
) {}
package dev.lions.unionflow.server.api.dto.communication.request;
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;
import java.util.List;
import java.util.UUID;
/**
* Request DTO pour créer une conversation
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-16
*/
@Builder
public record CreateConversationRequest(
@NotBlank @Size(max = 255) String name,
@Size(max = 1000) String description,
@NotNull ConversationType type,
@NotEmpty List<UUID> participantIds,
UUID organisationId
) {}

View File

@@ -1,29 +1,29 @@
package dev.lions.unionflow.server.api.dto.communication.request;
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
import dev.lions.unionflow.server.api.enums.communication.MessageType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;
import java.util.List;
import java.util.UUID;
/**
* Request DTO pour envoyer un message
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-16
*/
@Builder
public record SendMessageRequest(
@NotNull UUID conversationId,
@NotBlank @Size(max = 10000) String content,
MessageType type,
MessagePriority priority,
List<UUID> recipientIds,
List<String> recipientRoles,
List<String> attachments
) {}
package dev.lions.unionflow.server.api.dto.communication.request;
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
import dev.lions.unionflow.server.api.enums.communication.MessageType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;
import java.util.List;
import java.util.UUID;
/**
* Request DTO pour envoyer un message
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-16
*/
@Builder
public record SendMessageRequest(
@NotNull UUID conversationId,
@NotBlank @Size(max = 10000) String content,
MessageType type,
MessagePriority priority,
List<UUID> recipientIds,
List<String> recipientRoles,
List<String> attachments
) {}

View File

@@ -1,7 +1,10 @@
package dev.lions.unionflow.server.api.dto.communication.response;
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
@@ -14,20 +17,23 @@ import java.util.UUID;
* @version 1.0
* @since 2026-03-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public record ConversationResponse(
UUID id,
String name,
String description,
ConversationType type,
List<UUID> participantIds,
UUID organisationId,
MessageResponse lastMessage,
int unreadCount,
boolean isMuted,
boolean isPinned,
boolean isArchived,
LocalDateTime createdAt,
LocalDateTime updatedAt,
String avatarUrl
) {}
public class ConversationResponse {
private UUID id;
private String name;
private String description;
private ConversationType type;
private List<UUID> participantIds;
private UUID organisationId;
private MessageResponse lastMessage;
private int unreadCount;
private boolean muted;
private boolean pinned;
private boolean archived;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String avatarUrl;
}

View File

@@ -3,7 +3,10 @@ package dev.lions.unionflow.server.api.dto.communication.response;
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
import dev.lions.unionflow.server.api.enums.communication.MessageType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
@@ -16,24 +19,27 @@ import java.util.UUID;
* @version 1.0
* @since 2026-03-16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public record MessageResponse(
UUID id,
UUID conversationId,
UUID senderId,
String senderName,
String senderAvatar,
String content,
MessageType type,
MessageStatus status,
MessagePriority priority,
List<UUID> recipientIds,
List<String> recipientRoles,
UUID organisationId,
LocalDateTime createdAt,
LocalDateTime readAt,
List<String> attachments,
boolean isEdited,
LocalDateTime editedAt,
boolean isDeleted
) {}
public class MessageResponse {
private UUID id;
private UUID conversationId;
private UUID senderId;
private String senderName;
private String senderAvatar;
private String content;
private MessageType type;
private MessageStatus status;
private MessagePriority priority;
private List<UUID> recipientIds;
private List<String> recipientRoles;
private UUID organisationId;
private LocalDateTime createdAt;
private LocalDateTime readAt;
private List<String> attachments;
private boolean edited;
private LocalDateTime editedAt;
private boolean deleted;
}

View File

@@ -0,0 +1,57 @@
package dev.lions.unionflow.server.api.dto.compliance.response;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Builder;
/**
* Snapshot KPI public (Sprint 17) — vue read-only pour autorités externes (BCEAO, ARTCI, CENTIF).
*
* <p>Sous-ensemble strictement filtré de la conformité : indicateurs agrégés uniquement,
* jamais de données sensibles ({@code complianceOfficerId}, payloads détaillés, IDs membres,
* informations PEP). L'autorité voit le score, l'état des obligations, les pourcentages
* de couverture — pas l'intimité opérationnelle.
*
* <p>Diffusé via URL signée temporairement (HMAC-SHA256, expiry stricte).
*
* @since 2026-04-25 (Sprint 17 — transparency réglementaire publique)
*/
@Builder
public record KpiPublicSnapshot(
/** Nom de l'organisation. */
String organisationNom,
/** Référentiel comptable applicable (SYSCOHADA / SYCEBNL / PCSFD_UMOA). */
String referentielComptable,
/** Score conformité agrégé 0-100. */
int scoreGlobal,
/** Compliance Officer désigné (présence uniquement, pas l'identité). */
boolean complianceOfficerDesigne,
/** Statut AG annuelle : OK / EN_ATTENTE / RETARD. */
String agAnnuelleStatut,
/** Statut rapport AIRMS : OK / EN_ATTENTE. */
String rapportAirmsStatut,
/** Nombre de dirigeants enrôlés CMU (sans liste). */
int dirigeantsAvecCmu,
/** Pourcentage KYC à jour (membres). */
BigDecimal tauxKycAJourPct,
/** Pourcentage formation LBC/FT (membres formés dans les 12 derniers mois). */
BigDecimal tauxFormationLbcFtPct,
/** Pourcentage couverture UBO (Bénéficiaires Effectifs documentés). */
BigDecimal couvertureUboPct,
/** Statut commissaire aux comptes (OPTIONNEL / OBLIGATOIRE). */
String commissaireAuxComptesStatut,
/** Date de génération du snapshot. */
LocalDateTime dateGeneration
) {
}

View File

@@ -1,28 +1,28 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import lombok.Builder;
/**
* Requête de création d'un compte comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateCompteComptableRequest(
@NotBlank(message = "Le numéro de compte est obligatoire") String numeroCompte,
@NotBlank(message = "Le libellé est obligatoire") String libelle,
@NotNull(message = "Le type de compte est obligatoire") TypeCompteComptable typeCompte,
@NotNull(message = "La classe comptable est obligatoire") @Min(value = 1, message = "La classe comptable doit être entre 1 et 7") @Max(value = 7, message = "La classe comptable doit être entre 1 et 7") Integer classeComptable,
BigDecimal soldeInitial,
BigDecimal soldeActuel,
Boolean compteCollectif,
Boolean compteAnalytique,
String description) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import lombok.Builder;
/**
* Requête de création d'un compte comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateCompteComptableRequest(
@NotBlank(message = "Le numéro de compte est obligatoire") String numeroCompte,
@NotBlank(message = "Le libellé est obligatoire") String libelle,
@NotNull(message = "Le type de compte est obligatoire") TypeCompteComptable typeCompte,
@NotNull(message = "La classe comptable est obligatoire") @Min(value = 1, message = "La classe comptable doit être entre 1 et 7") @Max(value = 7, message = "La classe comptable doit être entre 1 et 7") Integer classeComptable,
BigDecimal soldeInitial,
BigDecimal soldeActuel,
Boolean compteCollectif,
Boolean compteAnalytique,
String description) {
}

View File

@@ -1,41 +1,41 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateEcritureComptableRequest(
@NotBlank(message = "Le numéro de pièce est obligatoire") String numeroPiece,
@NotNull(message = "La date de l'écriture est obligatoire") LocalDate dateEcriture,
@NotBlank(message = "Le libellé est obligatoire") String libelle,
String reference,
String lettrage,
Boolean pointe,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String commentaire,
@NotNull(message = "Le journal est obligatoire") UUID journalId,
UUID organisationId,
UUID paiementId,
List<CreateLigneEcritureRequest> lignes) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateEcritureComptableRequest(
@NotBlank(message = "Le numéro de pièce est obligatoire") String numeroPiece,
@NotNull(message = "La date de l'écriture est obligatoire") LocalDate dateEcriture,
@NotBlank(message = "Le libellé est obligatoire") String libelle,
String reference,
String lettrage,
Boolean pointe,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String commentaire,
@NotNull(message = "Le journal est obligatoire") UUID journalId,
UUID organisationId,
UUID paiementId,
List<CreateLigneEcritureRequest> lignes) {
}

View File

@@ -1,24 +1,24 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de création d'un journal comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateJournalComptableRequest(
@NotBlank(message = "Le code du journal est obligatoire") String code,
@NotBlank(message = "Le libellé est obligatoire") String libelle,
@NotNull(message = "Le type de journal est obligatoire") TypeJournalComptable typeJournal,
LocalDate dateDebut,
LocalDate dateFin,
String statut,
String description) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de création d'un journal comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateJournalComptableRequest(
@NotBlank(message = "Le code du journal est obligatoire") String code,
@NotBlank(message = "Le libellé est obligatoire") String libelle,
@NotNull(message = "Le type de journal est obligatoire") TypeJournalComptable typeJournal,
LocalDate dateDebut,
LocalDate dateFin,
String statut,
String description) {
}

View File

@@ -1,30 +1,30 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une ligne d'écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateLigneEcritureRequest(
@NotNull(message = "Le numéro de ligne est obligatoire") @Min(value = 1, message = "Le numéro de ligne doit être positif") Integer numeroLigne,
@DecimalMin(value = "0.0", message = "Le montant débit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0", message = "Le montant crédit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String libelle,
String reference,
@NotNull(message = "L'écriture est obligatoire") UUID ecritureId,
@NotNull(message = "Le compte comptable est obligatoire") UUID compteComptableId) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une ligne d'écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateLigneEcritureRequest(
@NotNull(message = "Le numéro de ligne est obligatoire") @Min(value = 1, message = "Le numéro de ligne doit être positif") Integer numeroLigne,
@DecimalMin(value = "0.0", message = "Le montant débit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0", message = "Le montant crédit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String libelle,
String reference,
@NotNull(message = "L'écriture est obligatoire") UUID ecritureId,
@NotNull(message = "Le compte comptable est obligatoire") UUID compteComptableId) {
}

View File

@@ -1,26 +1,26 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import java.math.BigDecimal;
import lombok.Builder;
/**
* Requête de mise à jour d'un compte comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateCompteComptableRequest(
String numeroCompte,
String libelle,
TypeCompteComptable typeCompte,
@Min(value = 1, message = "La classe comptable doit être entre 1 et 7") @Max(value = 7, message = "La classe comptable doit être entre 1 et 7") Integer classeComptable,
BigDecimal soldeInitial,
BigDecimal soldeActuel,
Boolean compteCollectif,
Boolean compteAnalytique,
String description) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import java.math.BigDecimal;
import lombok.Builder;
/**
* Requête de mise à jour d'un compte comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateCompteComptableRequest(
String numeroCompte,
String libelle,
TypeCompteComptable typeCompte,
@Min(value = 1, message = "La classe comptable doit être entre 1 et 7") @Max(value = 7, message = "La classe comptable doit être entre 1 et 7") Integer classeComptable,
BigDecimal soldeInitial,
BigDecimal soldeActuel,
Boolean compteCollectif,
Boolean compteAnalytique,
String description) {
}

View File

@@ -1,34 +1,34 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de mise à jour d'une écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateEcritureComptableRequest(
String numeroPiece,
LocalDate dateEcriture,
String libelle,
String reference,
String lettrage,
Boolean pointe,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String commentaire,
UUID journalId,
UUID paiementId,
List<UpdateLigneEcritureRequest> lignes) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de mise à jour d'une écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateEcritureComptableRequest(
String numeroPiece,
LocalDate dateEcriture,
String libelle,
String reference,
String lettrage,
Boolean pointe,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String commentaire,
UUID journalId,
UUID paiementId,
List<UpdateLigneEcritureRequest> lignes) {
}

View File

@@ -1,21 +1,21 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de mise à jour d'un journal comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateJournalComptableRequest(
String libelle,
TypeJournalComptable typeJournal,
LocalDate dateDebut,
LocalDate dateFin,
String statut,
String description) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de mise à jour d'un journal comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateJournalComptableRequest(
String libelle,
TypeJournalComptable typeJournal,
LocalDate dateDebut,
LocalDate dateFin,
String statut,
String description) {
}

View File

@@ -1,24 +1,24 @@
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Min;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de mise à jour d'une ligne d'écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateLigneEcritureRequest(
@Min(value = 1, message = "Le numéro de ligne doit être positif") Integer numeroLigne,
@DecimalMin(value = "0.0", message = "Le montant débit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0", message = "Le montant crédit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String libelle,
String reference,
UUID compteComptableId) {
}
package dev.lions.unionflow.server.api.dto.comptabilite.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Min;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de mise à jour d'une ligne d'écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateLigneEcritureRequest(
@Min(value = 1, message = "Le numéro de ligne doit être positif") Integer numeroLigne,
@DecimalMin(value = "0.0", message = "Le montant débit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantDebit,
@DecimalMin(value = "0.0", message = "Le montant crédit doit être positif ou nul") @Digits(integer = 12, fraction = 2) BigDecimal montantCredit,
String libelle,
String reference,
UUID compteComptableId) {
}

View File

@@ -1,35 +1,35 @@
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'un compte comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CompteComptableResponse extends BaseResponse {
private String numeroCompte;
private String libelle;
private TypeCompteComptable typeCompte;
private Integer classeComptable;
private BigDecimal soldeInitial;
private BigDecimal soldeActuel;
private Boolean compteCollectif;
private Boolean compteAnalytique;
private String description;
}
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'un compte comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CompteComptableResponse extends BaseResponse {
private String numeroCompte;
private String libelle;
private TypeCompteComptable typeCompte;
private Integer classeComptable;
private BigDecimal soldeInitial;
private BigDecimal soldeActuel;
private Boolean compteCollectif;
private Boolean compteAnalytique;
private String description;
}

View File

@@ -1,41 +1,41 @@
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'une écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EcritureComptableResponse extends BaseResponse {
private String numeroPiece;
private LocalDate dateEcriture;
private String libelle;
private String reference;
private String lettrage;
private Boolean pointe;
private BigDecimal montantDebit;
private BigDecimal montantCredit;
private String commentaire;
private UUID journalId;
private UUID organisationId;
private UUID paiementId;
private List<LigneEcritureResponse> lignes;
}
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'une écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EcritureComptableResponse extends BaseResponse {
private String numeroPiece;
private LocalDate dateEcriture;
private String libelle;
private String reference;
private String lettrage;
private Boolean pointe;
private BigDecimal montantDebit;
private BigDecimal montantCredit;
private String commentaire;
private UUID journalId;
private UUID organisationId;
private UUID paiementId;
private List<LigneEcritureResponse> lignes;
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'un journal comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JournalComptableResponse extends BaseResponse {
private String code;
private String libelle;
private TypeJournalComptable typeJournal;
private LocalDate dateDebut;
private LocalDate dateFin;
private String statut;
private String description;
}
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'un journal comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JournalComptableResponse extends BaseResponse {
private String code;
private String libelle;
private TypeJournalComptable typeJournal;
private LocalDate dateDebut;
private LocalDate dateFin;
private String statut;
private String description;
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'une ligne d'écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LigneEcritureResponse extends BaseResponse {
private Integer numeroLigne;
private BigDecimal montantDebit;
private BigDecimal montantCredit;
private String libelle;
private String reference;
private UUID ecritureId;
private UUID compteComptableId;
}
package dev.lions.unionflow.server.api.dto.comptabilite.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'une ligne d'écriture comptable.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LigneEcritureResponse extends BaseResponse {
private Integer numeroLigne;
private BigDecimal montantDebit;
private BigDecimal montantCredit;
private String libelle;
private String reference;
private UUID ecritureId;
private UUID compteComptableId;
}

View File

@@ -1,22 +1,22 @@
package dev.lions.unionflow.server.api.dto.config.request;
import java.util.Map;
import lombok.Builder;
/**
* Requête de création d'une configuration.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateConfigurationRequest(
String cle,
String valeur,
String type,
String categorie,
String description,
Boolean modifiable,
Boolean visible,
Map<String, Object> metadonnees) {
}
package dev.lions.unionflow.server.api.dto.config.request;
import java.util.Map;
import lombok.Builder;
/**
* Requête de création d'une configuration.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateConfigurationRequest(
String cle,
String valeur,
String type,
String categorie,
String description,
Boolean modifiable,
Boolean visible,
Map<String, Object> metadonnees) {
}

View File

@@ -1,50 +1,50 @@
package dev.lions.unionflow.server.api.dto.config.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
/**
* Requête de création/mise à jour des paramètres LCB-FT (Lutte contre le Blanchiment et le Financement du Terrorisme).
* Définit les seuils au-dessus desquels les justifications d'origine des fonds sont obligatoires.
*
* @author lions dev Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Paramètres LCB-FT (seuils de vigilance)")
public class ParametresLcbFtRequest {
@Schema(description = "ID de l'organisation (null pour paramètres plateforme)")
private String organisationId;
@NotNull(message = "Le montant seuil de justification est obligatoire")
@DecimalMin(value = "0", message = "Le montant doit être positif ou nul")
@Schema(description = "Montant au-dessus duquel l'origine des fonds est obligatoire (ex. 500000 XOF)", example = "500000")
private BigDecimal montantSeuilJustification;
@NotNull(message = "Le montant seuil de validation manuelle est obligatoire")
@DecimalMin(value = "0", message = "Le montant doit être positif ou nul")
@Schema(description = "Montant au-dessus duquel une validation manuelle est requise (ex. 1000000 XOF)", example = "1000000")
private BigDecimal montantSeuilValidationManuelle;
@NotBlank(message = "Le code devise est obligatoire")
@Size(max = 3, message = "Le code devise doit faire 3 caractères (ISO 4217)")
@Schema(description = "Code devise ISO 4217 (ex. XOF, EUR, USD)", example = "XOF")
private String codeDevise;
@Schema(description = "Notes ou commentaires sur la configuration")
private String notes;
}
package dev.lions.unionflow.server.api.dto.config.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
/**
* Requête de création/mise à jour des paramètres LCB-FT (Lutte contre le Blanchiment et le Financement du Terrorisme).
* Définit les seuils au-dessus desquels les justifications d'origine des fonds sont obligatoires.
*
* @author lions dev Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Paramètres LCB-FT (seuils de vigilance)")
public class ParametresLcbFtRequest {
@Schema(description = "ID de l'organisation (null pour paramètres plateforme)")
private String organisationId;
@NotNull(message = "Le montant seuil de justification est obligatoire")
@DecimalMin(value = "0", message = "Le montant doit être positif ou nul")
@Schema(description = "Montant au-dessus duquel l'origine des fonds est obligatoire (ex. 500000 XOF)", example = "500000")
private BigDecimal montantSeuilJustification;
@NotNull(message = "Le montant seuil de validation manuelle est obligatoire")
@DecimalMin(value = "0", message = "Le montant doit être positif ou nul")
@Schema(description = "Montant au-dessus duquel une validation manuelle est requise (ex. 1000000 XOF)", example = "1000000")
private BigDecimal montantSeuilValidationManuelle;
@NotBlank(message = "Le code devise est obligatoire")
@Size(max = 3, message = "Le code devise doit faire 3 caractères (ISO 4217)")
@Schema(description = "Code devise ISO 4217 (ex. XOF, EUR, USD)", example = "XOF")
private String codeDevise;
@Schema(description = "Notes ou commentaires sur la configuration")
private String notes;
}

View File

@@ -1,22 +1,22 @@
package dev.lions.unionflow.server.api.dto.config.request;
import java.util.Map;
import lombok.Builder;
/**
* Requête de mise à jour d'une configuration.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateConfigurationRequest(
String cle,
String valeur,
String type,
String categorie,
String description,
Boolean modifiable,
Boolean visible,
Map<String, Object> metadonnees) {
}
package dev.lions.unionflow.server.api.dto.config.request;
import java.util.Map;
import lombok.Builder;
/**
* Requête de mise à jour d'une configuration.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateConfigurationRequest(
String cle,
String valeur,
String type,
String categorie,
String description,
Boolean modifiable,
Boolean visible,
Map<String, Object> metadonnees) {
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.config.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse contenant les données d'une configuration.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ConfigurationResponse extends BaseResponse {
private String cle;
private String valeur;
private String type;
private String categorie;
private String description;
private Boolean modifiable;
private Boolean visible;
private Map<String, Object> metadonnees;
}
package dev.lions.unionflow.server.api.dto.config.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse contenant les données d'une configuration.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ConfigurationResponse extends BaseResponse {
private String cle;
private String valeur;
private String type;
private String categorie;
private String description;
private Boolean modifiable;
private Boolean visible;
private Map<String, Object> metadonnees;
}

View File

@@ -1,49 +1,49 @@
package dev.lions.unionflow.server.api.dto.config.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
/**
* Réponse contenant les paramètres LCB-FT (Lutte contre le Blanchiment et le Financement du Terrorisme).
* Retourne les seuils configurés pour une organisation ou la plateforme.
*
* @author lions dev Team
* @version 1.0
* @since 2026-03-13
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Paramètres LCB-FT avec seuils de vigilance")
public class ParametresLcbFtResponse extends BaseResponse {
@Schema(description = "ID de l'organisation (null si paramètres plateforme)")
private String organisationId;
@Schema(description = "Nom de l'organisation (null si paramètres plateforme)")
private String organisationNom;
@Schema(description = "Montant au-dessus duquel l'origine des fonds est obligatoire", example = "500000")
private BigDecimal montantSeuilJustification;
@Schema(description = "Montant au-dessus duquel une validation manuelle est requise", example = "1000000")
private BigDecimal montantSeuilValidationManuelle;
@Schema(description = "Code devise ISO 4217", example = "XOF")
private String codeDevise;
@Schema(description = "Notes ou commentaires sur la configuration")
private String notes;
@Schema(description = "Indique si ces paramètres s'appliquent à toute la plateforme")
private Boolean estParametrePlateforme;
}
package dev.lions.unionflow.server.api.dto.config.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.math.BigDecimal;
/**
* Réponse contenant les paramètres LCB-FT (Lutte contre le Blanchiment et le Financement du Terrorisme).
* Retourne les seuils configurés pour une organisation ou la plateforme.
*
* @author lions dev Team
* @version 1.0
* @since 2026-03-13
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Paramètres LCB-FT avec seuils de vigilance")
public class ParametresLcbFtResponse extends BaseResponse {
@Schema(description = "ID de l'organisation (null si paramètres plateforme)")
private String organisationId;
@Schema(description = "Nom de l'organisation (null si paramètres plateforme)")
private String organisationNom;
@Schema(description = "Montant au-dessus duquel l'origine des fonds est obligatoire", example = "500000")
private BigDecimal montantSeuilJustification;
@Schema(description = "Montant au-dessus duquel une validation manuelle est requise", example = "1000000")
private BigDecimal montantSeuilValidationManuelle;
@Schema(description = "Code devise ISO 4217", example = "XOF")
private String codeDevise;
@Schema(description = "Notes ou commentaires sur la configuration")
private String notes;
@Schema(description = "Indique si ces paramètres s'appliquent à toute la plateforme")
private Boolean estParametrePlateforme;
}

View File

@@ -1,62 +1,62 @@
package dev.lions.unionflow.server.api.dto.cotisation.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une nouvelle cotisation.
*
* @param membreId Identifiant du membre concerné.
* @param typeCotisation Type de cotisation (MENSUELLE, etc.).
* @param libelle Libellé de la cotisation.
* @param description Description détaillée.
* @param montantDu Montant total à payer.
* @param codeDevise Code ISO de la devise (par défaut XOF).
* @param dateEcheance Date limite de paiement.
* @param periode Période concernée (ex: Janvier 2025).
* @param annee Année de référence.
* @param mois Mois de référence (1-12, optionnel).
* @param recurrente Indique si la cotisation est récurrente.
* @param observations Commentaires libres.
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
@Builder
public record CreateCotisationRequest(
@NotNull(message = "L'identifiant du membre est obligatoire") UUID membreId,
@NotNull(message = "L'identifiant de l'organisation est obligatoire") UUID organisationId,
@NotBlank(message = "Le type de cotisation est obligatoire") String typeCotisation,
@NotBlank(message = "Le libellé est obligatoire") @Size(max = 100) String libelle,
@Size(max = 500) String description,
@NotNull(message = "Le montant dû est obligatoire") @DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal montantDu,
@Size(min = 3, max = 3) String codeDevise,
@NotNull(message = "La date d'échéance est obligatoire") LocalDate dateEcheance,
@Size(max = 50) String periode,
@Min(2020) @Max(2100) Integer annee,
@Min(1) @Max(12) Integer mois,
Boolean recurrente,
@Size(max = 1000) String observations) {
}
package dev.lions.unionflow.server.api.dto.cotisation.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une nouvelle cotisation.
*
* @param membreId Identifiant du membre concerné.
* @param typeCotisation Type de cotisation (MENSUELLE, etc.).
* @param libelle Libellé de la cotisation.
* @param description Description détaillée.
* @param montantDu Montant total à payer.
* @param codeDevise Code ISO de la devise (par défaut XOF).
* @param dateEcheance Date limite de paiement.
* @param periode Période concernée (ex: Janvier 2025).
* @param annee Année de référence.
* @param mois Mois de référence (1-12, optionnel).
* @param recurrente Indique si la cotisation est récurrente.
* @param observations Commentaires libres.
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
@Builder
public record CreateCotisationRequest(
@NotNull(message = "L'identifiant du membre est obligatoire") UUID membreId,
@NotNull(message = "L'identifiant de l'organisation est obligatoire") UUID organisationId,
@NotBlank(message = "Le type de cotisation est obligatoire") String typeCotisation,
@NotBlank(message = "Le libellé est obligatoire") @Size(max = 100) String libelle,
@Size(max = 500) String description,
@NotNull(message = "Le montant dû est obligatoire") @DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal montantDu,
@Size(min = 3, max = 3) String codeDevise,
@NotNull(message = "La date d'échéance est obligatoire") LocalDate dateEcheance,
@Size(max = 50) String periode,
@Min(2020) @Max(2100) Integer annee,
@Min(1) @Max(12) Integer mois,
Boolean recurrente,
@Size(max = 1000) String observations) {
}

View File

@@ -1,48 +1,48 @@
package dev.lions.unionflow.server.api.dto.cotisation.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de mise à jour d'une cotisation existante.
*
* @param libelle Nouveau libellé.
* @param description Nouvelle description.
* @param montantDu Nouveau montant dû (si non payé).
* @param dateEcheance Nouvelle date d'échéance.
* @param observations Nouvelles observations.
* @param statut Nouveau statut (validation métier requise).
* @param annee Année de référence.
* @param mois Mois de référence.
* @param recurrente État de récurrence.
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
@Builder
public record UpdateCotisationRequest(
@Size(max = 100) String libelle,
@Size(max = 500) String description,
@DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal montantDu,
LocalDate dateEcheance,
@Size(max = 1000) String observations,
String statut,
@Min(2020) @Max(2100) Integer annee,
@Min(1) @Max(12) Integer mois,
Boolean recurrente) {
}
package dev.lions.unionflow.server.api.dto.cotisation.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de mise à jour d'une cotisation existante.
*
* @param libelle Nouveau libellé.
* @param description Nouvelle description.
* @param montantDu Nouveau montant dû (si non payé).
* @param dateEcheance Nouvelle date d'échéance.
* @param observations Nouvelles observations.
* @param statut Nouveau statut (validation métier requise).
* @param annee Année de référence.
* @param mois Mois de référence.
* @param recurrente État de récurrence.
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
@Builder
public record UpdateCotisationRequest(
@Size(max = 100) String libelle,
@Size(max = 500) String description,
@DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal montantDu,
LocalDate dateEcheance,
@Size(max = 1000) String observations,
String statut,
@Min(2020) @Max(2100) Integer annee,
@Min(1) @Max(12) Integer mois,
Boolean recurrente) {
}

View File

@@ -1,129 +1,129 @@
package dev.lions.unionflow.server.api.dto.cotisation.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée pour une cotisation.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CotisationResponse extends BaseResponse {
private String numeroReference;
private UUID membreId;
private String nomMembre;
/** Nom complet (prénom + nom) pour affichage */
private String nomCompletMembre;
private String numeroMembre;
/** Initiales du membre (ex: JD pour Jean Dupont) */
private String initialesMembre;
/** Type / statut du membre (ex: Actif, En attente) */
private String typeMembre;
private UUID organisationId;
private String nomOrganisation;
/** Région de l'organisation (affichage liste) */
private String regionOrganisation;
/** Classe CSS icône PrimeFaces pour l'organisation (ex: pi-building) */
private String iconeOrganisation;
private String typeCotisation;
/** Alias pour tri/filtre (type de cotisation) */
private String type;
private String typeCotisationLibelle;
/** Libellé du type pour affichage (alias typeCotisationLibelle) */
private String typeLibelle;
/** Sévérité PrimeFaces pour le tag type (info, success, warn, error, secondary) */
private String typeSeverity;
/** Classe icône PrimeFaces pour le type (ex: pi-calendar) */
private String typeIcon;
private String libelle;
private String description;
private BigDecimal montantDu;
/** Alias pour tri/filtre (montant du) */
private BigDecimal montant;
/** Montant formaté pour affichage (ex: "5 000") */
private String montantFormatte;
private BigDecimal montantPaye;
private BigDecimal montantRestant;
private String codeDevise;
private String statut;
private String statutLibelle;
/** Sévérité PrimeFaces pour le tag statut */
private String statutSeverity;
/** Classe icône PrimeFaces pour le statut (ex: pi-check) */
private String statutIcon;
private LocalDate dateEcheance;
/** Date d'échéance formatée pour affichage */
private String dateEcheanceFormattee;
/** Classe CSS couleur pour le retard (ex: text-red-500) */
private String retardCouleur;
/** Texte affiché pour le retard (ex: "X jours de retard") */
private String retardTexte;
/** Date de paiement formatée pour affichage */
private String datePaiementFormattee;
/** Icône PrimeFaces pour le mode de paiement */
private String modePaiementIcon;
/** Libellé du mode de paiement */
private String modePaiementLibelle;
private LocalDateTime datePaiement;
private String periode;
private Integer annee;
private Integer mois;
private String observations;
private Boolean recurrente;
private Integer nombreRappels;
private LocalDateTime dateDernierRappel;
private UUID valideParId;
private String nomValidateur;
private LocalDateTime dateValidation;
private Integer pourcentagePaiement;
private Long joursRetard;
private Boolean enRetard;
// === MÉTHODES DE FORMATAGE ===
public String getMontantDuFormatte() {
if (montantDu == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantDu, codeDevise != null ? codeDevise : "FCFA");
}
public String getMontantPayeFormatte() {
if (montantPaye == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantPaye, codeDevise != null ? codeDevise : "FCFA");
}
public String getMontantRestantFormatte() {
if (montantRestant == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantRestant, codeDevise != null ? codeDevise : "FCFA");
}
public boolean isMontantRestantPositif() {
return montantRestant != null && montantRestant.signum() > 0;
}
/** Alias de {@link #modePaiementLibelle} pour #{cotisation.methodePaiementLibelle}. */
public String getMethodePaiementLibelle() {
return modePaiementLibelle;
}
// Informations de paiement
private String methodePaiement; // WAVE_MONEY, VIREMENT, ESPECES, CARTE, MOBILE_MONEY
private String referencePaiement; // Référence externe du paiement
private String waveSessionId; // ID de session Wave Money pour prélèvements
}
package dev.lions.unionflow.server.api.dto.cotisation.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée pour une cotisation.
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CotisationResponse extends BaseResponse {
private String numeroReference;
private UUID membreId;
private String nomMembre;
/** Nom complet (prénom + nom) pour affichage */
private String nomCompletMembre;
private String numeroMembre;
/** Initiales du membre (ex: JD pour Jean Dupont) */
private String initialesMembre;
/** Type / statut du membre (ex: Actif, En attente) */
private String typeMembre;
private UUID organisationId;
private String nomOrganisation;
/** Région de l'organisation (affichage liste) */
private String regionOrganisation;
/** Classe CSS icône PrimeFaces pour l'organisation (ex: pi-building) */
private String iconeOrganisation;
private String typeCotisation;
/** Alias pour tri/filtre (type de cotisation) */
private String type;
private String typeCotisationLibelle;
/** Libellé du type pour affichage (alias typeCotisationLibelle) */
private String typeLibelle;
/** Sévérité PrimeFaces pour le tag type (info, success, warn, error, secondary) */
private String typeSeverity;
/** Classe icône PrimeFaces pour le type (ex: pi-calendar) */
private String typeIcon;
private String libelle;
private String description;
private BigDecimal montantDu;
/** Alias pour tri/filtre (montant du) */
private BigDecimal montant;
/** Montant formaté pour affichage (ex: "5 000") */
private String montantFormatte;
private BigDecimal montantPaye;
private BigDecimal montantRestant;
private String codeDevise;
private String statut;
private String statutLibelle;
/** Sévérité PrimeFaces pour le tag statut */
private String statutSeverity;
/** Classe icône PrimeFaces pour le statut (ex: pi-check) */
private String statutIcon;
private LocalDate dateEcheance;
/** Date d'échéance formatée pour affichage */
private String dateEcheanceFormattee;
/** Classe CSS couleur pour le retard (ex: text-red-500) */
private String retardCouleur;
/** Texte affiché pour le retard (ex: "X jours de retard") */
private String retardTexte;
/** Date de paiement formatée pour affichage */
private String datePaiementFormattee;
/** Icône PrimeFaces pour le mode de paiement */
private String modePaiementIcon;
/** Libellé du mode de paiement */
private String modePaiementLibelle;
private LocalDateTime datePaiement;
private String periode;
private Integer annee;
private Integer mois;
private String observations;
private Boolean recurrente;
private Integer nombreRappels;
private LocalDateTime dateDernierRappel;
private UUID valideParId;
private String nomValidateur;
private LocalDateTime dateValidation;
private Integer pourcentagePaiement;
private Long joursRetard;
private Boolean enRetard;
// === MÉTHODES DE FORMATAGE ===
public String getMontantDuFormatte() {
if (montantDu == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantDu, codeDevise != null ? codeDevise : "FCFA");
}
public String getMontantPayeFormatte() {
if (montantPaye == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantPaye, codeDevise != null ? codeDevise : "FCFA");
}
public String getMontantRestantFormatte() {
if (montantRestant == null) return "0 FCFA";
return String.format(java.util.Locale.US, "%,.0f %s", montantRestant, codeDevise != null ? codeDevise : "FCFA");
}
public boolean isMontantRestantPositif() {
return montantRestant != null && montantRestant.signum() > 0;
}
/** Alias de {@link #modePaiementLibelle} pour #{cotisation.methodePaiementLibelle}. */
public String getMethodePaiementLibelle() {
return modePaiementLibelle;
}
// Informations de paiement
private String methodePaiement; // WAVE_MONEY, VIREMENT, ESPECES, CARTE, MOBILE_MONEY
private String referencePaiement; // Référence externe du paiement
private String waveSessionId; // ID de session Wave Money pour prélèvements
}

View File

@@ -1,5 +1,9 @@
package dev.lions.unionflow.server.api.dto.cotisation.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
@@ -7,29 +11,32 @@ import java.util.UUID;
/**
* Réponse simplifiée pour les listes de cotisations.
*
* @param id Identifiant unique.
* @param numeroReference Référence unique.
* @param nomMembre Nom du membre.
* @param montantDu Montant total dû.
* @param montantPaye Montant déjà réglé.
* @param statut Statut actuel (code).
* @param statutLibelle Libellé du statut pour l'UI.
* @param dateEcheance Date limite.
* @param annee Année concernée.
* @param actif Indique si l'entité est active.
* @author UnionFlow Team
* @version 1.0
* @since 2026-02-22
*/
public record CotisationSummaryResponse(
UUID id,
String numeroReference,
String nomMembre,
BigDecimal montantDu,
BigDecimal montantPaye,
String statut,
String statutLibelle,
LocalDate dateEcheance,
Integer annee,
Boolean actif) {
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CotisationSummaryResponse {
/** Identifiant unique. */
private UUID id;
/** Référence unique. */
private String numeroReference;
/** Nom du membre. */
private String nomMembre;
/** Montant total dû. */
private BigDecimal montantDu;
/** Montant déjà réglé. */
private BigDecimal montantPaye;
/** Statut actuel (code). */
private String statut;
/** Libellé du statut pour l'UI. */
private String statutLibelle;
/** Date limite. */
private LocalDate dateEcheance;
/** Année concernée. */
private Integer annee;
/** Indique si l'entité est active. */
private Boolean actif;
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.culte;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.culte.TypeDonReligieux;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DonReligieuxDTO extends BaseDTO {
private String institutionId; // Mosquée, Église, Paroisse...
// Si relié spécifiquement à un fidèle enregistré
private String fideleId;
private TypeDonReligieux typeDon;
private BigDecimal montant;
private LocalDateTime dateEncaissement;
// Utile pour la zakat (Nissab de l'année concernée) ou la dîme périodique
private String periodeOuNatureAssociee;
}
package dev.lions.unionflow.server.api.dto.culte;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.culte.TypeDonReligieux;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DonReligieuxDTO extends BaseDTO {
private String institutionId; // Mosquée, Église, Paroisse...
// Si relié spécifiquement à un fidèle enregistré
private String fideleId;
private TypeDonReligieux typeDon;
private BigDecimal montant;
private LocalDateTime dateEncaissement;
// Utile pour la zakat (Nissab de l'année concernée) ou la dîme périodique
private String periodeOuNatureAssociee;
}

View File

@@ -1,122 +1,122 @@
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* DTO principal pour toutes les données du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardDataResponse {
@JsonProperty("stats")
private DashboardStatsResponse stats;
@JsonProperty("recentActivities")
private List<RecentActivityResponse> recentActivities;
@JsonProperty("upcomingEvents")
private List<UpcomingEventResponse> upcomingEvents;
@JsonProperty("userPreferences")
private Map<String, Object> userPreferences;
@JsonProperty("organizationId")
private String organizationId;
@JsonProperty("userId")
private String userId;
// Méthodes utilitaires
public Integer getTodayEventsCount() {
if (upcomingEvents == null) {
return 0;
}
return (int) upcomingEvents.stream()
.filter(event -> event.getIsToday() != null && event.getIsToday())
.count();
}
public Integer getTomorrowEventsCount() {
if (upcomingEvents == null) {
return 0;
}
return (int) upcomingEvents.stream()
.filter(event -> event.getIsTomorrow() != null && event.getIsTomorrow())
.count();
}
public Integer getRecentActivitiesCount() {
if (recentActivities == null) {
return 0;
}
return (int) recentActivities.stream()
.filter(activity -> activity.getIsRecent() != null && activity.getIsRecent())
.count();
}
public Integer getTodayActivitiesCount() {
if (recentActivities == null) {
return 0;
}
return (int) recentActivities.stream()
.filter(activity -> activity.getIsToday() != null && activity.getIsToday())
.count();
}
public Boolean getHasUpcomingEvents() {
return upcomingEvents != null && !upcomingEvents.isEmpty();
}
public Boolean getHasRecentActivities() {
return recentActivities != null && !recentActivities.isEmpty();
}
public String getThemePreference() {
if (userPreferences == null) {
return "royal_teal";
}
return (String) userPreferences.getOrDefault("theme", "royal_teal");
}
public String getLanguagePreference() {
if (userPreferences == null) {
return "fr";
}
return (String) userPreferences.getOrDefault("language", "fr");
}
public Boolean getNotificationsEnabled() {
if (userPreferences == null) {
return true;
}
return (Boolean) userPreferences.getOrDefault("notifications", true);
}
public Boolean getAutoRefreshEnabled() {
if (userPreferences == null) {
return true;
}
return (Boolean) userPreferences.getOrDefault("autoRefresh", true);
}
public Integer getRefreshInterval() {
if (userPreferences == null) {
return 300; // 5 minutes par défaut
}
return (Integer) userPreferences.getOrDefault("refreshInterval", 300);
}
}
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* DTO principal pour toutes les données du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardDataResponse {
@JsonProperty("stats")
private DashboardStatsResponse stats;
@JsonProperty("recentActivities")
private List<RecentActivityResponse> recentActivities;
@JsonProperty("upcomingEvents")
private List<UpcomingEventResponse> upcomingEvents;
@JsonProperty("userPreferences")
private Map<String, Object> userPreferences;
@JsonProperty("organizationId")
private String organizationId;
@JsonProperty("userId")
private String userId;
// Méthodes utilitaires
public Integer getTodayEventsCount() {
if (upcomingEvents == null) {
return 0;
}
return (int) upcomingEvents.stream()
.filter(event -> event.getIsToday() != null && event.getIsToday())
.count();
}
public Integer getTomorrowEventsCount() {
if (upcomingEvents == null) {
return 0;
}
return (int) upcomingEvents.stream()
.filter(event -> event.getIsTomorrow() != null && event.getIsTomorrow())
.count();
}
public Integer getRecentActivitiesCount() {
if (recentActivities == null) {
return 0;
}
return (int) recentActivities.stream()
.filter(activity -> activity.getIsRecent() != null && activity.getIsRecent())
.count();
}
public Integer getTodayActivitiesCount() {
if (recentActivities == null) {
return 0;
}
return (int) recentActivities.stream()
.filter(activity -> activity.getIsToday() != null && activity.getIsToday())
.count();
}
public Boolean getHasUpcomingEvents() {
return upcomingEvents != null && !upcomingEvents.isEmpty();
}
public Boolean getHasRecentActivities() {
return recentActivities != null && !recentActivities.isEmpty();
}
public String getThemePreference() {
if (userPreferences == null) {
return "royal_teal";
}
return (String) userPreferences.getOrDefault("theme", "royal_teal");
}
public String getLanguagePreference() {
if (userPreferences == null) {
return "fr";
}
return (String) userPreferences.getOrDefault("language", "fr");
}
public Boolean getNotificationsEnabled() {
if (userPreferences == null) {
return true;
}
return (Boolean) userPreferences.getOrDefault("notifications", true);
}
public Boolean getAutoRefreshEnabled() {
if (userPreferences == null) {
return true;
}
return (Boolean) userPreferences.getOrDefault("autoRefresh", true);
}
public Integer getRefreshInterval() {
if (userPreferences == null) {
return 300; // 5 minutes par défaut
}
return (Integer) userPreferences.getOrDefault("refreshInterval", 300);
}
}

View File

@@ -1,119 +1,119 @@
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* DTO pour les statistiques du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardStatsResponse {
@JsonProperty("totalMembers")
private Integer totalMembers;
@JsonProperty("activeMembers")
private Integer activeMembers;
@JsonProperty("totalEvents")
private Integer totalEvents;
@JsonProperty("upcomingEvents")
private Integer upcomingEvents;
@JsonProperty("totalContributions")
private Integer totalContributions;
@JsonProperty("totalContributionAmount")
private Double totalContributionAmount;
@JsonProperty("pendingRequests")
private Integer pendingRequests;
@JsonProperty("completedProjects")
private Integer completedProjects;
@JsonProperty("monthlyGrowth")
private Double monthlyGrowth;
@JsonProperty("engagementRate")
private Double engagementRate;
@JsonProperty("lastUpdated")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime lastUpdated;
/**
* Nombre total d'organisations dans le système (SuperAdmin uniquement)
*/
@JsonProperty("totalOrganizations")
private Integer totalOrganizations;
/**
* Répartition des organisations par type
* Exemple: {"Mutuelle": 15, "Coopérative": 8, "Tontine": 5, "Autre": 3}
*/
@JsonProperty("organizationTypeDistribution")
private Map<String, Integer> organizationTypeDistribution;
/**
* Données historiques mensuelles pour les graphiques (12 derniers mois)
*/
@JsonProperty("monthlyHistoricalData")
private List<MonthlyStatDTO> monthlyHistoricalData;
// Méthodes utilitaires
public String getFormattedContributionAmount() {
if (totalContributionAmount == null) {
return "0";
}
if (totalContributionAmount >= 1_000_000) {
return String.format("%.1fM", totalContributionAmount / 1_000_000);
} else if (totalContributionAmount >= 1_000) {
return String.format("%.0fK", totalContributionAmount / 1_000);
} else {
return String.format("%.0f", totalContributionAmount);
}
}
public Boolean getHasGrowth() {
return monthlyGrowth != null && monthlyGrowth > 0;
}
public Boolean getIsHighEngagement() {
return engagementRate != null && engagementRate > 0.7;
}
public Double getInactiveMembers() {
if (totalMembers == null || activeMembers == null) {
return 0.0;
}
return (double) (totalMembers - activeMembers);
}
public Double getActiveMemberPercentage() {
if (totalMembers == null || activeMembers == null || totalMembers == 0) {
return 0.0;
}
return (double) activeMembers / totalMembers * 100;
}
public Double getEngagementPercentage() {
if (engagementRate == null) {
return 0.0;
}
return engagementRate * 100;
}
}
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* DTO pour les statistiques du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardStatsResponse {
@JsonProperty("totalMembers")
private Integer totalMembers;
@JsonProperty("activeMembers")
private Integer activeMembers;
@JsonProperty("totalEvents")
private Integer totalEvents;
@JsonProperty("upcomingEvents")
private Integer upcomingEvents;
@JsonProperty("totalContributions")
private Integer totalContributions;
@JsonProperty("totalContributionAmount")
private Double totalContributionAmount;
@JsonProperty("pendingRequests")
private Integer pendingRequests;
@JsonProperty("completedProjects")
private Integer completedProjects;
@JsonProperty("monthlyGrowth")
private Double monthlyGrowth;
@JsonProperty("engagementRate")
private Double engagementRate;
@JsonProperty("lastUpdated")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime lastUpdated;
/**
* Nombre total d'organisations dans le système (SuperAdmin uniquement)
*/
@JsonProperty("totalOrganizations")
private Integer totalOrganizations;
/**
* Répartition des organisations par type
* Exemple: {"Mutuelle": 15, "Coopérative": 8, "Tontine": 5, "Autre": 3}
*/
@JsonProperty("organizationTypeDistribution")
private Map<String, Integer> organizationTypeDistribution;
/**
* Données historiques mensuelles pour les graphiques (12 derniers mois)
*/
@JsonProperty("monthlyHistoricalData")
private List<MonthlyStatDTO> monthlyHistoricalData;
// Méthodes utilitaires
public String getFormattedContributionAmount() {
if (totalContributionAmount == null) {
return "0";
}
if (totalContributionAmount >= 1_000_000) {
return String.format("%.1fM", totalContributionAmount / 1_000_000);
} else if (totalContributionAmount >= 1_000) {
return String.format("%.0fK", totalContributionAmount / 1_000);
} else {
return String.format("%.0f", totalContributionAmount);
}
}
public Boolean getHasGrowth() {
return monthlyGrowth != null && monthlyGrowth > 0;
}
public Boolean getIsHighEngagement() {
return engagementRate != null && engagementRate > 0.7;
}
public Double getInactiveMembers() {
if (totalMembers == null || activeMembers == null) {
return 0.0;
}
return (double) (totalMembers - activeMembers);
}
public Double getActiveMemberPercentage() {
if (totalMembers == null || activeMembers == null || totalMembers == 0) {
return 0.0;
}
return (double) activeMembers / totalMembers * 100;
}
public Double getEngagementPercentage() {
if (engagementRate == null) {
return 0.0;
}
return engagementRate * 100;
}
}

View File

@@ -1,5 +1,9 @@
package dev.lions.unionflow.server.api.dto.dashboard;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -10,39 +14,42 @@ import java.time.LocalDate;
* @author UnionFlow Team
* @version 1.0
*/
public record MembreDashboardSyntheseResponse(
String prenom,
String nom,
LocalDate dateInscription,
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MembreDashboardSyntheseResponse implements Serializable {
// Cotisations
BigDecimal mesCotisationsPaiement,
/** Total des cotisations payées sur l'année en cours (pour affichage dashboard). */
BigDecimal totalCotisationsPayeesAnnee,
/** Total des cotisations payées tout temps (pour la carte « Contribution Totale »). */
BigDecimal totalCotisationsPayeesToutTemps,
/** Nombre de cotisations PAYÉES (pour la carte « Cotisations »). */
Integer nombreCotisationsPayees,
String statutCotisations,
/** Taux de cotisation en % (0-100). Calculé sur l'année courante ou toutes années si pas de cotisation 2026. */
Integer tauxCotisationsPerso,
/** Nombre TOTAL de cotisations (toutes années, tous statuts) — pour calcul du taux d'engagement. */
Integer nombreCotisationsTotal,
private String prenom;
private String nom;
private LocalDate dateInscription;
// Epargne
BigDecimal monSoldeEpargne,
BigDecimal evolutionEpargneNombre,
String evolutionEpargne,
Integer objectifEpargne,
// Cotisations
private BigDecimal mesCotisationsPaiement;
/** Total des cotisations payées sur l'année en cours (pour affichage dashboard). */
private BigDecimal totalCotisationsPayeesAnnee;
/** Total des cotisations payées tout temps (pour la carte « Contribution Totale »). */
private BigDecimal totalCotisationsPayeesToutTemps;
/** Nombre de cotisations PAYÉES (pour la carte « Cotisations »). */
private Integer nombreCotisationsPayees;
private String statutCotisations;
/** Taux de cotisation en % (0-100). Calculé sur l'année courante ou toutes années si pas de cotisation 2026. */
private Integer tauxCotisationsPerso;
/** Nombre TOTAL de cotisations (toutes années, tous statuts) — pour calcul du taux d'engagement. */
private Integer nombreCotisationsTotal;
// Evenements
Integer mesEvenementsInscrits,
Integer evenementsAVenir,
Integer tauxParticipationPerso,
// Epargne
private BigDecimal monSoldeEpargne;
private BigDecimal evolutionEpargneNombre;
private String evolutionEpargne;
private Integer objectifEpargne;
// Aides
Integer mesDemandesAide,
Integer aidesEnCours,
Integer tauxAidesApprouvees) implements Serializable {
// Evenements
private Integer mesEvenementsInscrits;
private Integer evenementsAVenir;
private Integer tauxParticipationPerso;
// Aides
private Integer mesDemandesAide;
private Integer aidesEnCours;
private Integer tauxAidesApprouvees;
}

View File

@@ -1,70 +1,70 @@
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO pour les statistiques mensuelles historiques
* Utilisé pour générer des graphiques de croissance sur 12 mois
*
* @author UnionFlow Team
* @version 2.0
* @since 2026-03-07
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MonthlyStatDTO {
/**
* Mois au format "2026-01", "2026-02", etc.
*/
@JsonProperty("month")
private String month;
/**
* Nombre de membres ce mois-là
*/
@JsonProperty("totalMembers")
private Integer totalMembers;
/**
* Nombre de membres actifs ce mois-là
*/
@JsonProperty("activeMembers")
private Integer activeMembers;
/**
* Montant total des contributions ce mois-là
*/
@JsonProperty("contributionAmount")
private Double contributionAmount;
/**
* Nombre d'événements organisés ce mois-là
*/
@JsonProperty("eventsCount")
private Integer eventsCount;
/**
* Taux d'engagement ce mois-là
*/
@JsonProperty("engagementRate")
private Double engagementRate;
/**
* Nombre de nouveaux membres ce mois-là
*/
@JsonProperty("newMembers")
private Integer newMembers;
/**
* Nombre de cotisations payées ce mois-là
*/
@JsonProperty("contributionsCount")
private Integer contributionsCount;
}
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO pour les statistiques mensuelles historiques
* Utilisé pour générer des graphiques de croissance sur 12 mois
*
* @author UnionFlow Team
* @version 2.0
* @since 2026-03-07
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MonthlyStatDTO {
/**
* Mois au format "2026-01", "2026-02", etc.
*/
@JsonProperty("month")
private String month;
/**
* Nombre de membres ce mois-là
*/
@JsonProperty("totalMembers")
private Integer totalMembers;
/**
* Nombre de membres actifs ce mois-là
*/
@JsonProperty("activeMembers")
private Integer activeMembers;
/**
* Montant total des contributions ce mois-là
*/
@JsonProperty("contributionAmount")
private Double contributionAmount;
/**
* Nombre d'événements organisés ce mois-là
*/
@JsonProperty("eventsCount")
private Integer eventsCount;
/**
* Taux d'engagement ce mois-là
*/
@JsonProperty("engagementRate")
private Double engagementRate;
/**
* Nombre de nouveaux membres ce mois-là
*/
@JsonProperty("newMembers")
private Integer newMembers;
/**
* Nombre de cotisations payées ce mois-là
*/
@JsonProperty("contributionsCount")
private Integer contributionsCount;
}

View File

@@ -1,130 +1,130 @@
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
/**
* DTO pour les activités récentes du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecentActivityResponse {
@JsonProperty("id")
private String id;
@JsonProperty("type")
private String type;
@JsonProperty("title")
private String title;
@JsonProperty("description")
private String description;
@JsonProperty("userName")
private String userName;
@JsonProperty("timestamp")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime timestamp;
@JsonProperty("userAvatar")
private String userAvatar;
@JsonProperty("actionUrl")
private String actionUrl;
// Méthodes utilitaires
public String getTimeAgo() {
if (timestamp == null) {
return "";
}
LocalDateTime now = LocalDateTime.now();
long minutes = ChronoUnit.MINUTES.between(timestamp, now);
long hours = ChronoUnit.HOURS.between(timestamp, now);
long days = ChronoUnit.DAYS.between(timestamp, now);
if (minutes < 60) {
return minutes + "min";
} else if (hours < 24) {
return hours + "h";
} else if (days < 7) {
return days + "j";
} else {
long weeks = days / 7;
return weeks + "sem";
}
}
public String getActivityIcon() {
if (type == null) {
return "help_outline";
}
switch (type.toLowerCase()) {
case "member":
return "person";
case "event":
return "event";
case "contribution":
return "payment";
case "organization":
return "business";
case "system":
return "settings";
default:
return "info";
}
}
public String getActivityColor() {
if (type == null) {
return "#6B7280"; // grey
}
switch (type.toLowerCase()) {
case "member":
return "#10B981"; // success
case "event":
return "#3B82F6"; // info
case "contribution":
return "#008B8B"; // teal blue
case "organization":
return "#4169E1"; // royal blue
case "system":
return "#6B7280"; // grey
default:
return "#6B7280"; // grey
}
}
public Boolean getIsRecent() {
if (timestamp == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(timestamp, now);
return hours < 24;
}
public Boolean getIsToday() {
if (timestamp == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
return timestamp.toLocalDate().equals(now.toLocalDate());
}
}
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
/**
* DTO pour les activités récentes du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecentActivityResponse {
@JsonProperty("id")
private String id;
@JsonProperty("type")
private String type;
@JsonProperty("title")
private String title;
@JsonProperty("description")
private String description;
@JsonProperty("userName")
private String userName;
@JsonProperty("timestamp")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime timestamp;
@JsonProperty("userAvatar")
private String userAvatar;
@JsonProperty("actionUrl")
private String actionUrl;
// Méthodes utilitaires
public String getTimeAgo() {
if (timestamp == null) {
return "";
}
LocalDateTime now = LocalDateTime.now();
long minutes = ChronoUnit.MINUTES.between(timestamp, now);
long hours = ChronoUnit.HOURS.between(timestamp, now);
long days = ChronoUnit.DAYS.between(timestamp, now);
if (minutes < 60) {
return minutes + "min";
} else if (hours < 24) {
return hours + "h";
} else if (days < 7) {
return days + "j";
} else {
long weeks = days / 7;
return weeks + "sem";
}
}
public String getActivityIcon() {
if (type == null) {
return "help_outline";
}
switch (type.toLowerCase()) {
case "member":
return "person";
case "event":
return "event";
case "contribution":
return "payment";
case "organization":
return "business";
case "system":
return "settings";
default:
return "info";
}
}
public String getActivityColor() {
if (type == null) {
return "#6B7280"; // grey
}
switch (type.toLowerCase()) {
case "member":
return "#10B981"; // success
case "event":
return "#3B82F6"; // info
case "contribution":
return "#008B8B"; // teal blue
case "organization":
return "#4169E1"; // royal blue
case "system":
return "#6B7280"; // grey
default:
return "#6B7280"; // grey
}
}
public Boolean getIsRecent() {
if (timestamp == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(timestamp, now);
return hours < 24;
}
public Boolean getIsToday() {
if (timestamp == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
return timestamp.toLocalDate().equals(now.toLocalDate());
}
}

View File

@@ -1,183 +1,183 @@
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
/**
* DTO pour les événements à venir du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpcomingEventResponse {
@JsonProperty("id")
private String id;
@JsonProperty("title")
private String title;
@JsonProperty("description")
private String description;
@JsonProperty("startDate")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime startDate;
@JsonProperty("endDate")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime endDate;
@JsonProperty("location")
private String location;
@JsonProperty("maxParticipants")
private Integer maxParticipants;
@JsonProperty("currentParticipants")
private Integer currentParticipants;
@JsonProperty("status")
private String status;
@JsonProperty("imageUrl")
private String imageUrl;
@JsonProperty("tags")
private List<String> tags;
// Méthodes utilitaires
public String getDaysUntilEvent() {
return getDaysUntilEvent(LocalDateTime.now());
}
/**
* Version testable avec une date de référence fixe (même package).
*/
String getDaysUntilEvent(LocalDateTime now) {
if (startDate == null) {
return "";
}
long days = ChronoUnit.DAYS.between(now.toLocalDate(), startDate.toLocalDate());
long hours = ChronoUnit.HOURS.between(now, startDate);
if (days < 0) {
return "En cours";
}
if (days == 0) {
// Vérifier si l'événement est déjà passé (même si moins d'1h)
if (startDate.isBefore(now)) {
return "En cours";
} else if (hours < 2) {
return "Bientôt";
} else {
return "Aujourd'hui";
}
} else if (days == 1) {
return "Demain";
} else if (days < 7) {
return "Dans " + days + " jours";
} else {
long weeks = days / 7;
return "Dans " + weeks + " semaine" + (weeks > 1 ? "s" : "");
}
}
public Double getFillPercentage() {
if (maxParticipants == null || currentParticipants == null || maxParticipants == 0) {
return 0.0;
}
return (double) currentParticipants / maxParticipants * 100;
}
public Boolean getIsFull() {
if (maxParticipants == null || currentParticipants == null) {
return false;
}
return currentParticipants >= maxParticipants;
}
public Boolean getIsAlmostFull() {
Double fillPercentage = getFillPercentage();
return fillPercentage >= 80.0 && fillPercentage < 100.0;
}
public Boolean getIsToday() {
if (startDate == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
return startDate.toLocalDate().equals(now.toLocalDate());
}
public Boolean getIsTomorrow() {
if (startDate == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
return startDate.toLocalDate().equals(now.toLocalDate().plusDays(1));
}
public String getStatusColor() {
if (status == null) {
return "#6B7280"; // grey
}
switch (status.toLowerCase()) {
case "confirmed":
return "#10B981"; // success
case "open":
return "#3B82F6"; // info
case "cancelled":
return "#EF4444"; // error
case "postponed":
return "#F59E0B"; // warning
default:
return "#6B7280"; // grey
}
}
public String getStatusLabel() {
if (status == null) {
return "Inconnu";
}
switch (status.toLowerCase()) {
case "confirmed":
return "Confirmé";
case "open":
return "Ouvert";
case "cancelled":
return "Annulé";
case "postponed":
return "Reporté";
default:
return status;
}
}
public Integer getAvailableSpots() {
if (maxParticipants == null || currentParticipants == null) {
return 0;
}
return Math.max(0, maxParticipants - currentParticipants);
}
public String getParticipationSummary() {
if (maxParticipants == null || currentParticipants == null) {
return "0/0 participants";
}
return currentParticipants + "/" + maxParticipants + " participants";
}
}
package dev.lions.unionflow.server.api.dto.dashboard;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
/**
* DTO pour les événements à venir du dashboard
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpcomingEventResponse {
@JsonProperty("id")
private String id;
@JsonProperty("title")
private String title;
@JsonProperty("description")
private String description;
@JsonProperty("startDate")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime startDate;
@JsonProperty("endDate")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime endDate;
@JsonProperty("location")
private String location;
@JsonProperty("maxParticipants")
private Integer maxParticipants;
@JsonProperty("currentParticipants")
private Integer currentParticipants;
@JsonProperty("status")
private String status;
@JsonProperty("imageUrl")
private String imageUrl;
@JsonProperty("tags")
private List<String> tags;
// Méthodes utilitaires
public String getDaysUntilEvent() {
return getDaysUntilEvent(LocalDateTime.now());
}
/**
* Version testable avec une date de référence fixe (même package).
*/
String getDaysUntilEvent(LocalDateTime now) {
if (startDate == null) {
return "";
}
long days = ChronoUnit.DAYS.between(now.toLocalDate(), startDate.toLocalDate());
long hours = ChronoUnit.HOURS.between(now, startDate);
if (days < 0) {
return "En cours";
}
if (days == 0) {
// Vérifier si l'événement est déjà passé (même si moins d'1h)
if (startDate.isBefore(now)) {
return "En cours";
} else if (hours < 2) {
return "Bientôt";
} else {
return "Aujourd'hui";
}
} else if (days == 1) {
return "Demain";
} else if (days < 7) {
return "Dans " + days + " jours";
} else {
long weeks = days / 7;
return "Dans " + weeks + " semaine" + (weeks > 1 ? "s" : "");
}
}
public Double getFillPercentage() {
if (maxParticipants == null || currentParticipants == null || maxParticipants == 0) {
return 0.0;
}
return (double) currentParticipants / maxParticipants * 100;
}
public Boolean getIsFull() {
if (maxParticipants == null || currentParticipants == null) {
return false;
}
return currentParticipants >= maxParticipants;
}
public Boolean getIsAlmostFull() {
Double fillPercentage = getFillPercentage();
return fillPercentage >= 80.0 && fillPercentage < 100.0;
}
public Boolean getIsToday() {
if (startDate == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
return startDate.toLocalDate().equals(now.toLocalDate());
}
public Boolean getIsTomorrow() {
if (startDate == null) {
return false;
}
LocalDateTime now = LocalDateTime.now();
return startDate.toLocalDate().equals(now.toLocalDate().plusDays(1));
}
public String getStatusColor() {
if (status == null) {
return "#6B7280"; // grey
}
switch (status.toLowerCase()) {
case "confirmed":
return "#10B981"; // success
case "open":
return "#3B82F6"; // info
case "cancelled":
return "#EF4444"; // error
case "postponed":
return "#F59E0B"; // warning
default:
return "#6B7280"; // grey
}
}
public String getStatusLabel() {
if (status == null) {
return "Inconnu";
}
switch (status.toLowerCase()) {
case "confirmed":
return "Confirmé";
case "open":
return "Ouvert";
case "cancelled":
return "Annulé";
case "postponed":
return "Reporté";
default:
return status;
}
}
public Integer getAvailableSpots() {
if (maxParticipants == null || currentParticipants == null) {
return 0;
}
return Math.max(0, maxParticipants - currentParticipants);
}
public String getParticipationSummary() {
if (maxParticipants == null || currentParticipants == null) {
return "0/0 participants";
}
return currentParticipants + "/" + maxParticipants + " participants";
}
}

View File

@@ -0,0 +1,42 @@
package dev.lions.unionflow.server.api.dto.delegation.request;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une délégation de rôle temporaire.
*
* @since 2026-04-25 (Sprint 10 — service existant Sprint 2)
*/
@Builder
public record CreateRoleDelegationRequest(
@NotNull(message = "L'organisation est obligatoire")
UUID organisationId,
@NotNull(message = "Le déléguant est obligatoire")
UUID delegantUserId,
@NotNull(message = "Le délégataire est obligatoire")
UUID delegataireUserId,
@NotBlank(message = "Le rôle délégué est obligatoire")
@Pattern(regexp = "^[A-Z_]{3,50}$", message = "Format de rôle invalide (uppercase + underscore)")
String roleDelegue,
@NotNull(message = "La date de début est obligatoire")
LocalDateTime dateDebut,
@NotNull(message = "La date de fin est obligatoire")
@Future(message = "La date de fin doit être future")
LocalDateTime dateFin,
@Size(max = 500)
String motif
) {
}

View File

@@ -0,0 +1,27 @@
package dev.lions.unionflow.server.api.dto.delegation.response;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Builder;
/**
* Vue d'une délégation de rôle.
*
* @since 2026-04-25 (Sprint 10)
*/
@Builder
public record RoleDelegationResponse(
UUID id,
UUID organisationId,
UUID delegantUserId,
UUID delegataireUserId,
String roleDelegue,
LocalDateTime dateDebut,
LocalDateTime dateFin,
String motif,
String statut,
LocalDateTime dateRevocation,
LocalDateTime dateCreation,
boolean estActive
) {
}

View File

@@ -1,26 +1,26 @@
package dev.lions.unionflow.server.api.dto.document.request;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
/**
* Requête de création d'un document.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateDocumentRequest(
@NotBlank(message = "Le nom du fichier est obligatoire") String nomFichier,
String nomOriginal,
@NotBlank(message = "Le chemin de stockage est obligatoire") String cheminStockage,
String typeMime,
@NotNull(message = "La taille est obligatoire") @Min(value = 0, message = "La taille doit être positive") Long tailleOctets,
TypeDocument typeDocument,
String hashMd5,
String hashSha256,
String description) {
}
package dev.lions.unionflow.server.api.dto.document.request;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
/**
* Requête de création d'un document.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateDocumentRequest(
@NotBlank(message = "Le nom du fichier est obligatoire") String nomFichier,
String nomOriginal,
@NotBlank(message = "Le chemin de stockage est obligatoire") String cheminStockage,
String typeMime,
@NotNull(message = "La taille est obligatoire") @Min(value = 0, message = "La taille doit être positive") Long tailleOctets,
TypeDocument typeDocument,
String hashMd5,
String hashSha256,
String description) {
}

View File

@@ -1,22 +1,22 @@
package dev.lions.unionflow.server.api.dto.document.request;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une pièce jointe.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreatePieceJointeRequest(
@NotNull(message = "L'ordre est obligatoire") @Min(value = 1, message = "L'ordre doit être positif") Integer ordre,
String libelle,
String commentaire,
@NotNull(message = "Le document est obligatoire") UUID documentId,
@NotNull(message = "Le type entité est obligatoire") String typeEntiteRattachee,
@NotNull(message = "L'ID entité est obligatoire") UUID entiteRattacheeId) {
}
package dev.lions.unionflow.server.api.dto.document.request;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une pièce jointe.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreatePieceJointeRequest(
@NotNull(message = "L'ordre est obligatoire") @Min(value = 1, message = "L'ordre doit être positif") Integer ordre,
String libelle,
String commentaire,
@NotNull(message = "Le document est obligatoire") UUID documentId,
@NotNull(message = "Le type entité est obligatoire") String typeEntiteRattachee,
@NotNull(message = "L'ID entité est obligatoire") UUID entiteRattacheeId) {
}

View File

@@ -1,18 +1,18 @@
package dev.lions.unionflow.server.api.dto.document.request;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import lombok.Builder;
/**
* Requête de mise à jour d'un document.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateDocumentRequest(
String nomFichier,
String nomOriginal,
TypeDocument typeDocument,
String description) {
}
package dev.lions.unionflow.server.api.dto.document.request;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import lombok.Builder;
/**
* Requête de mise à jour d'un document.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateDocumentRequest(
String nomFichier,
String nomOriginal,
TypeDocument typeDocument,
String description) {
}

View File

@@ -1,17 +1,17 @@
package dev.lions.unionflow.server.api.dto.document.request;
import jakarta.validation.constraints.Min;
import lombok.Builder;
/**
* Requête de mise à jour d'une pièce jointe.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdatePieceJointeRequest(
@Min(value = 1, message = "L'ordre doit être positif") Integer ordre,
String libelle,
String commentaire) {
}
package dev.lions.unionflow.server.api.dto.document.request;
import jakarta.validation.constraints.Min;
import lombok.Builder;
/**
* Requête de mise à jour d'une pièce jointe.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdatePieceJointeRequest(
@Min(value = 1, message = "L'ordre doit être positif") Integer ordre,
String libelle,
String commentaire) {
}

View File

@@ -1,36 +1,36 @@
package dev.lions.unionflow.server.api.dto.document.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'un document.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DocumentResponse extends BaseResponse {
private String nomFichier;
private String nomOriginal;
private String cheminStockage;
private String typeMime;
private Long tailleOctets;
private TypeDocument typeDocument;
private String hashMd5;
private String hashSha256;
private String description;
private Integer nombreTelechargements;
private String tailleFormatee;
}
package dev.lions.unionflow.server.api.dto.document.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée d'un document.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DocumentResponse extends BaseResponse {
private String nomFichier;
private String nomOriginal;
private String cheminStockage;
private String typeMime;
private Long tailleOctets;
private TypeDocument typeDocument;
private String hashMd5;
private String hashSha256;
private String description;
private Integer nombreTelechargements;
private String tailleFormatee;
}

View File

@@ -1,31 +1,31 @@
package dev.lions.unionflow.server.api.dto.document.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse contenant les données d'une pièce jointe.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PieceJointeResponse extends BaseResponse {
private Integer ordre;
private String libelle;
private String commentaire;
private UUID documentId;
private String typeEntiteRattachee;
private UUID entiteRattacheeId;
}
package dev.lions.unionflow.server.api.dto.document.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse contenant les données d'une pièce jointe.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PieceJointeResponse extends BaseResponse {
private Integer ordre;
private String libelle;
private String commentaire;
private UUID documentId;
private String typeEntiteRattachee;
private UUID entiteRattacheeId;
}

View File

@@ -1,70 +1,70 @@
package dev.lions.unionflow.server.api.dto.evenement.request;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.FutureOrPresent;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'un événement.
*/
@Builder
public record CreateEvenementRequest(
@NotBlank(message = "Le titre"
+ ValidationConstants.OBLIGATOIRE_MESSAGE) @Size(min = ValidationConstants.TITRE_MIN_LENGTH, max = ValidationConstants.TITRE_MAX_LENGTH, message = ValidationConstants.TITRE_SIZE_MESSAGE) String titre,
@Size(max = ValidationConstants.DESCRIPTION_COURTE_MAX_LENGTH, message = ValidationConstants.DESCRIPTION_COURTE_SIZE_MESSAGE) String description,
@NotNull(message = "Le type d'événement est obligatoire") TypeEvenementMetier typeEvenement,
@NotNull(message = "Le statut est obligatoire") StatutEvenement statut,
PrioriteEvenement priorite,
@NotNull(message = "La date de début est obligatoire") @FutureOrPresent(message = "La date de début ne peut pas être dans le passé") LocalDate dateDebut,
LocalDate dateFin,
LocalTime heureDebut,
LocalTime heureFin,
@NotBlank(message = "Le lieu est obligatoire") @Size(max = 100) String lieu,
@Size(max = 200) String adresse,
@Size(max = 50) String ville,
@Size(max = 50) String region,
@DecimalMin(value = "-90.0") @DecimalMax(value = "90.0") BigDecimal latitude,
@DecimalMin(value = "-180.0") @DecimalMax(value = "180.0") BigDecimal longitude,
@NotNull(message = "L'association organisatrice est obligatoire") UUID associationId,
@Size(max = 100) String organisateur,
@Email(message = "Format d'email invalide") @Size(max = 100) String emailOrganisateur,
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$") String telephoneOrganisateur,
@Min(value = 1) @Max(value = 10000) Integer capaciteMax,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal budget,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal coutReel,
@Pattern(regexp = ValidationConstants.DEVISE_PATTERN, message = ValidationConstants.DEVISE_MESSAGE) String codeDevise,
Boolean inscriptionObligatoire,
LocalDate dateLimiteInscription,
Boolean evenementPublic,
Boolean recurrent,
String frequenceRecurrence,
@Size(max = 500) String instructions,
@Size(max = 500) String materielNecessaire,
@Size(max = 100) String conditionsMeteo,
@Size(max = 255) String imageUrl,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$") String couleurTheme) {
}
package dev.lions.unionflow.server.api.dto.evenement.request;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.FutureOrPresent;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'un événement.
*/
@Builder
public record CreateEvenementRequest(
@NotBlank(message = "Le titre"
+ ValidationConstants.OBLIGATOIRE_MESSAGE) @Size(min = ValidationConstants.TITRE_MIN_LENGTH, max = ValidationConstants.TITRE_MAX_LENGTH, message = ValidationConstants.TITRE_SIZE_MESSAGE) String titre,
@Size(max = ValidationConstants.DESCRIPTION_COURTE_MAX_LENGTH, message = ValidationConstants.DESCRIPTION_COURTE_SIZE_MESSAGE) String description,
@NotNull(message = "Le type d'événement est obligatoire") TypeEvenementMetier typeEvenement,
@NotNull(message = "Le statut est obligatoire") StatutEvenement statut,
PrioriteEvenement priorite,
@NotNull(message = "La date de début est obligatoire") @FutureOrPresent(message = "La date de début ne peut pas être dans le passé") LocalDate dateDebut,
LocalDate dateFin,
LocalTime heureDebut,
LocalTime heureFin,
@NotBlank(message = "Le lieu est obligatoire") @Size(max = 100) String lieu,
@Size(max = 200) String adresse,
@Size(max = 50) String ville,
@Size(max = 50) String region,
@DecimalMin(value = "-90.0") @DecimalMax(value = "90.0") BigDecimal latitude,
@DecimalMin(value = "-180.0") @DecimalMax(value = "180.0") BigDecimal longitude,
@NotNull(message = "L'association organisatrice est obligatoire") UUID associationId,
@Size(max = 100) String organisateur,
@Email(message = "Format d'email invalide") @Size(max = 100) String emailOrganisateur,
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$") String telephoneOrganisateur,
@Min(value = 1) @Max(value = 10000) Integer capaciteMax,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal budget,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal coutReel,
@Pattern(regexp = ValidationConstants.DEVISE_PATTERN, message = ValidationConstants.DEVISE_MESSAGE) String codeDevise,
Boolean inscriptionObligatoire,
LocalDate dateLimiteInscription,
Boolean evenementPublic,
Boolean recurrent,
String frequenceRecurrence,
@Size(max = 500) String instructions,
@Size(max = 500) String materielNecessaire,
@Size(max = 100) String conditionsMeteo,
@Size(max = 255) String imageUrl,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$") String couleurTheme) {
}

View File

@@ -1,65 +1,65 @@
package dev.lions.unionflow.server.api.dto.evenement.request;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import lombok.Builder;
/**
* Requête de mise à jour d'un événement.
*/
@Builder
public record UpdateEvenementRequest(
@Size(min = ValidationConstants.TITRE_MIN_LENGTH, max = ValidationConstants.TITRE_MAX_LENGTH, message = ValidationConstants.TITRE_SIZE_MESSAGE) String titre,
@Size(max = ValidationConstants.DESCRIPTION_COURTE_MAX_LENGTH, message = ValidationConstants.DESCRIPTION_COURTE_SIZE_MESSAGE) String description,
TypeEvenementMetier typeEvenement,
StatutEvenement statut,
PrioriteEvenement priorite,
LocalDate dateDebut,
LocalDate dateFin,
LocalTime heureDebut,
LocalTime heureFin,
@NotBlank(message = "Le lieu est obligatoire") @Size(max = 100) String lieu,
@Size(max = 200) String adresse,
@Size(max = 50) String ville,
@Size(max = 50) String region,
@DecimalMin(value = "-90.0") @DecimalMax(value = "90.0") BigDecimal latitude,
@DecimalMin(value = "-180.0") @DecimalMax(value = "180.0") BigDecimal longitude,
@Size(max = 100) String organisateur,
@Email(message = "Format d'email invalide") @Size(max = 100) String emailOrganisateur,
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$") String telephoneOrganisateur,
@Min(value = 1) @Max(value = 10000) Integer capaciteMax,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal budget,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal coutReel,
@Pattern(regexp = ValidationConstants.DEVISE_PATTERN, message = ValidationConstants.DEVISE_MESSAGE) String codeDevise,
Boolean inscriptionObligatoire,
LocalDate dateLimiteInscription,
Boolean evenementPublic,
Boolean recurrent,
String frequenceRecurrence,
@Size(max = 500) String instructions,
@Size(max = 500) String materielNecessaire,
@Size(max = 100) String conditionsMeteo,
@Size(max = 255) String imageUrl,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$") String couleurTheme) {
}
package dev.lions.unionflow.server.api.dto.evenement.request;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import lombok.Builder;
/**
* Requête de mise à jour d'un événement.
*/
@Builder
public record UpdateEvenementRequest(
@Size(min = ValidationConstants.TITRE_MIN_LENGTH, max = ValidationConstants.TITRE_MAX_LENGTH, message = ValidationConstants.TITRE_SIZE_MESSAGE) String titre,
@Size(max = ValidationConstants.DESCRIPTION_COURTE_MAX_LENGTH, message = ValidationConstants.DESCRIPTION_COURTE_SIZE_MESSAGE) String description,
TypeEvenementMetier typeEvenement,
StatutEvenement statut,
PrioriteEvenement priorite,
LocalDate dateDebut,
LocalDate dateFin,
LocalTime heureDebut,
LocalTime heureFin,
@NotBlank(message = "Le lieu est obligatoire") @Size(max = 100) String lieu,
@Size(max = 200) String adresse,
@Size(max = 50) String ville,
@Size(max = 50) String region,
@DecimalMin(value = "-90.0") @DecimalMax(value = "90.0") BigDecimal latitude,
@DecimalMin(value = "-180.0") @DecimalMax(value = "180.0") BigDecimal longitude,
@Size(max = 100) String organisateur,
@Email(message = "Format d'email invalide") @Size(max = 100) String emailOrganisateur,
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$") String telephoneOrganisateur,
@Min(value = 1) @Max(value = 10000) Integer capaciteMax,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal budget,
@DecimalMin(value = ValidationConstants.MONTANT_MIN_VALUE, message = ValidationConstants.MONTANT_POSITIF_MESSAGE) @Digits(integer = ValidationConstants.MONTANT_INTEGER_DIGITS, fraction = ValidationConstants.MONTANT_FRACTION_DIGITS, message = ValidationConstants.MONTANT_DIGITS_MESSAGE) BigDecimal coutReel,
@Pattern(regexp = ValidationConstants.DEVISE_PATTERN, message = ValidationConstants.DEVISE_MESSAGE) String codeDevise,
Boolean inscriptionObligatoire,
LocalDate dateLimiteInscription,
Boolean evenementPublic,
Boolean recurrent,
String frequenceRecurrence,
@Size(max = 500) String instructions,
@Size(max = 500) String materielNecessaire,
@Size(max = 100) String conditionsMeteo,
@Size(max = 255) String imageUrl,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$") String couleurTheme) {
}

View File

@@ -1,277 +1,277 @@
package dev.lions.unionflow.server.api.dto.evenement.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée pour un événement.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EvenementResponse extends BaseResponse {
private String titre;
private String description;
private TypeEvenementMetier typeEvenement;
private StatutEvenement statut;
private PrioriteEvenement priorite;
// Décommenter si l'on a besoin du @JsonFormat, sinon on garde le standard
// @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateDebut;
// @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateFin;
// @JsonFormat(pattern = "HH:mm")
private LocalTime heureDebut;
// @JsonFormat(pattern = "HH:mm")
private LocalTime heureFin;
private String lieu;
private String adresse;
private String ville;
private String region;
private BigDecimal latitude;
private BigDecimal longitude;
private UUID associationId;
private String nomAssociation;
private String organisateur;
private String emailOrganisateur;
private String telephoneOrganisateur;
private Integer capaciteMax;
private Integer participantsInscrits;
private Integer participantsPresents;
private BigDecimal budget;
private BigDecimal coutReel;
private String codeDevise;
private Boolean inscriptionObligatoire;
// @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateLimiteInscription;
private Boolean evenementPublic;
private Boolean recurrent;
private String frequenceRecurrence;
private String instructions;
private String materielNecessaire;
private String conditionsMeteo;
private String imageUrl;
private String couleurTheme;
// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateAnnulation;
private String raisonAnnulation;
private String nomAnnulateur;
private Long annulePar;
// === METHODES UTILITAIRES ===
public boolean estEnCours() {
return StatutEvenement.EN_COURS.equals(statut);
}
public boolean estTermine() {
return StatutEvenement.TERMINE.equals(statut);
}
public boolean estAnnule() {
return StatutEvenement.ANNULE.equals(statut);
}
public boolean estComplet() {
return capaciteMax != null
&& participantsInscrits != null
&& participantsInscrits >= capaciteMax;
}
public int getPlacesDisponibles() {
if (capaciteMax == null || participantsInscrits == null) {
return 0;
}
return Math.max(0, capaciteMax - participantsInscrits);
}
public int getTauxRemplissage() {
if (capaciteMax == null || capaciteMax == 0 || participantsInscrits == null) {
return 0;
}
return (participantsInscrits * 100) / capaciteMax;
}
public int getTauxPresence() {
if (participantsInscrits == null || participantsInscrits == 0 || participantsPresents == null) {
return 0;
}
return (participantsPresents * 100) / participantsInscrits;
}
public boolean sontInscriptionsOuvertes() {
if (estAnnule() || estTermine()) {
return false;
}
if (dateLimiteInscription != null && LocalDate.now().isAfter(dateLimiteInscription)) {
return false;
}
return !estComplet();
}
public long getDureeEnHeures() {
if (heureDebut == null || heureFin == null) {
return 0;
}
return heureDebut.until(heureFin, java.time.temporal.ChronoUnit.HOURS);
}
public boolean estEvenementMultiJours() {
return dateFin != null && !dateDebut.equals(dateFin);
}
public String getTypeEvenementLibelle() {
return typeEvenement != null ? typeEvenement.getLibelle() : "Non défini";
}
public String getTypeEvenementIcon() {
if (typeEvenement == null) return "pi pi-calendar";
return switch (typeEvenement) {
case ASSEMBLEE_GENERALE -> "pi pi-building";
case FORMATION -> "pi pi-book";
case REUNION_BUREAU -> "pi pi-users";
case CONFERENCE -> "pi pi-microphone";
case ATELIER -> "pi pi-wrench";
case CEREMONIE -> "pi pi-flag";
case ACTIVITE_SOCIALE, ACTION_CARITATIVE, AUTRE -> "pi pi-calendar";
};
}
public String getTypeEvenementSeverity() {
if (typeEvenement == null) return "secondary";
return switch (typeEvenement) {
case ASSEMBLEE_GENERALE -> "warning";
case FORMATION -> "info";
case ACTIVITE_SOCIALE, ACTION_CARITATIVE, REUNION_BUREAU, CONFERENCE, ATELIER, CEREMONIE, AUTRE -> "secondary";
};
}
public String getStatutLibelle() {
return statut != null ? statut.getLibelle() : "Non défini";
}
public String getStatutSeverity() {
if (statut == null) return "secondary";
return switch (statut) {
case PLANIFIE -> "info";
case EN_COURS -> "success";
case TERMINE, CONFIRME -> "secondary";
case ANNULE -> "danger";
case REPORTE -> "warning";
};
}
public String getDateDebutFormatee() {
return dateDebut != null ? dateDebut.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) : "";
}
public String getHeureDebutFormatee() {
return heureDebut != null ? heureDebut.format(DateTimeFormatter.ofPattern("HH:mm")) : "";
}
public String getHeureFinFormatee() {
return heureFin != null ? heureFin.format(DateTimeFormatter.ofPattern("HH:mm")) : "";
}
public String getStatutIcon() {
if (statut == null) return "pi pi-question";
return switch (statut) {
case PLANIFIE -> "pi pi-clock";
case CONFIRME -> "pi pi-check-circle";
case EN_COURS -> "pi pi-play";
case TERMINE -> "pi pi-check";
case ANNULE -> "pi pi-times";
case REPORTE -> "pi pi-refresh";
};
}
public String getPrioriteSeverity() {
if (priorite == null) return "secondary";
return switch (priorite) {
case CRITIQUE -> "danger";
case HAUTE -> "warning";
case NORMALE -> "info";
case BASSE -> "secondary";
};
}
public String getBudgetFormate() {
if (budget == null) return "";
return String.format(java.util.Locale.US, "%,.0f %s", budget, codeDevise != null ? codeDevise : "FCFA");
}
public String getPrioriteLibelle() {
return priorite != null ? priorite.getLibelle() : "Normale";
}
public String getAdresseComplete() {
StringBuilder adresseComplete = new StringBuilder();
if (lieu != null && !lieu.trim().isEmpty()) {
adresseComplete.append(lieu);
}
if (adresse != null && !adresse.trim().isEmpty()) {
if (adresseComplete.length() > 0)
adresseComplete.append(", ");
adresseComplete.append(adresse);
}
if (ville != null && !ville.trim().isEmpty()) {
if (adresseComplete.length() > 0)
adresseComplete.append(", ");
adresseComplete.append(ville);
}
if (region != null && !region.trim().isEmpty()) {
if (adresseComplete.length() > 0)
adresseComplete.append(", ");
adresseComplete.append(region);
}
return adresseComplete.toString();
}
public boolean hasCoordonnees() {
return latitude != null && longitude != null;
}
public BigDecimal getEcartBudgetaire() {
if (budget == null || coutReel == null) {
return BigDecimal.ZERO;
}
return budget.subtract(coutReel);
}
public boolean estBudgetDepasse() {
return getEcartBudgetaire().compareTo(BigDecimal.ZERO) < 0;
}
}
package dev.lions.unionflow.server.api.dto.evenement.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import dev.lions.unionflow.server.api.enums.evenement.PrioriteEvenement;
import dev.lions.unionflow.server.api.enums.evenement.StatutEvenement;
import dev.lions.unionflow.server.api.enums.evenement.TypeEvenementMetier;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse détaillée pour un événement.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EvenementResponse extends BaseResponse {
private String titre;
private String description;
private TypeEvenementMetier typeEvenement;
private StatutEvenement statut;
private PrioriteEvenement priorite;
// Décommenter si l'on a besoin du @JsonFormat, sinon on garde le standard
// @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateDebut;
// @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateFin;
// @JsonFormat(pattern = "HH:mm")
private LocalTime heureDebut;
// @JsonFormat(pattern = "HH:mm")
private LocalTime heureFin;
private String lieu;
private String adresse;
private String ville;
private String region;
private BigDecimal latitude;
private BigDecimal longitude;
private UUID associationId;
private String nomAssociation;
private String organisateur;
private String emailOrganisateur;
private String telephoneOrganisateur;
private Integer capaciteMax;
private Integer participantsInscrits;
private Integer participantsPresents;
private BigDecimal budget;
private BigDecimal coutReel;
private String codeDevise;
private Boolean inscriptionObligatoire;
// @JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateLimiteInscription;
private Boolean evenementPublic;
private Boolean recurrent;
private String frequenceRecurrence;
private String instructions;
private String materielNecessaire;
private String conditionsMeteo;
private String imageUrl;
private String couleurTheme;
// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime dateAnnulation;
private String raisonAnnulation;
private String nomAnnulateur;
private Long annulePar;
// === METHODES UTILITAIRES ===
public boolean estEnCours() {
return StatutEvenement.EN_COURS.equals(statut);
}
public boolean estTermine() {
return StatutEvenement.TERMINE.equals(statut);
}
public boolean estAnnule() {
return StatutEvenement.ANNULE.equals(statut);
}
public boolean estComplet() {
return capaciteMax != null
&& participantsInscrits != null
&& participantsInscrits >= capaciteMax;
}
public int getPlacesDisponibles() {
if (capaciteMax == null || participantsInscrits == null) {
return 0;
}
return Math.max(0, capaciteMax - participantsInscrits);
}
public int getTauxRemplissage() {
if (capaciteMax == null || capaciteMax == 0 || participantsInscrits == null) {
return 0;
}
return (participantsInscrits * 100) / capaciteMax;
}
public int getTauxPresence() {
if (participantsInscrits == null || participantsInscrits == 0 || participantsPresents == null) {
return 0;
}
return (participantsPresents * 100) / participantsInscrits;
}
public boolean sontInscriptionsOuvertes() {
if (estAnnule() || estTermine()) {
return false;
}
if (dateLimiteInscription != null && LocalDate.now().isAfter(dateLimiteInscription)) {
return false;
}
return !estComplet();
}
public long getDureeEnHeures() {
if (heureDebut == null || heureFin == null) {
return 0;
}
return heureDebut.until(heureFin, java.time.temporal.ChronoUnit.HOURS);
}
public boolean estEvenementMultiJours() {
return dateFin != null && !dateDebut.equals(dateFin);
}
public String getTypeEvenementLibelle() {
return typeEvenement != null ? typeEvenement.getLibelle() : "Non défini";
}
public String getTypeEvenementIcon() {
if (typeEvenement == null) return "pi pi-calendar";
return switch (typeEvenement) {
case ASSEMBLEE_GENERALE -> "pi pi-building";
case FORMATION -> "pi pi-book";
case REUNION_BUREAU -> "pi pi-users";
case CONFERENCE -> "pi pi-microphone";
case ATELIER -> "pi pi-wrench";
case CEREMONIE -> "pi pi-flag";
case ACTIVITE_SOCIALE, ACTION_CARITATIVE, AUTRE -> "pi pi-calendar";
};
}
public String getTypeEvenementSeverity() {
if (typeEvenement == null) return "secondary";
return switch (typeEvenement) {
case ASSEMBLEE_GENERALE -> "warning";
case FORMATION -> "info";
case ACTIVITE_SOCIALE, ACTION_CARITATIVE, REUNION_BUREAU, CONFERENCE, ATELIER, CEREMONIE, AUTRE -> "secondary";
};
}
public String getStatutLibelle() {
return statut != null ? statut.getLibelle() : "Non défini";
}
public String getStatutSeverity() {
if (statut == null) return "secondary";
return switch (statut) {
case PLANIFIE -> "info";
case EN_COURS -> "success";
case TERMINE, CONFIRME -> "secondary";
case ANNULE -> "danger";
case REPORTE -> "warning";
};
}
public String getDateDebutFormatee() {
return dateDebut != null ? dateDebut.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) : "";
}
public String getHeureDebutFormatee() {
return heureDebut != null ? heureDebut.format(DateTimeFormatter.ofPattern("HH:mm")) : "";
}
public String getHeureFinFormatee() {
return heureFin != null ? heureFin.format(DateTimeFormatter.ofPattern("HH:mm")) : "";
}
public String getStatutIcon() {
if (statut == null) return "pi pi-question";
return switch (statut) {
case PLANIFIE -> "pi pi-clock";
case CONFIRME -> "pi pi-check-circle";
case EN_COURS -> "pi pi-play";
case TERMINE -> "pi pi-check";
case ANNULE -> "pi pi-times";
case REPORTE -> "pi pi-refresh";
};
}
public String getPrioriteSeverity() {
if (priorite == null) return "secondary";
return switch (priorite) {
case CRITIQUE -> "danger";
case HAUTE -> "warning";
case NORMALE -> "info";
case BASSE -> "secondary";
};
}
public String getBudgetFormate() {
if (budget == null) return "";
return String.format(java.util.Locale.US, "%,.0f %s", budget, codeDevise != null ? codeDevise : "FCFA");
}
public String getPrioriteLibelle() {
return priorite != null ? priorite.getLibelle() : "Normale";
}
public String getAdresseComplete() {
StringBuilder adresseComplete = new StringBuilder();
if (lieu != null && !lieu.trim().isEmpty()) {
adresseComplete.append(lieu);
}
if (adresse != null && !adresse.trim().isEmpty()) {
if (adresseComplete.length() > 0)
adresseComplete.append(", ");
adresseComplete.append(adresse);
}
if (ville != null && !ville.trim().isEmpty()) {
if (adresseComplete.length() > 0)
adresseComplete.append(", ");
adresseComplete.append(ville);
}
if (region != null && !region.trim().isEmpty()) {
if (adresseComplete.length() > 0)
adresseComplete.append(", ");
adresseComplete.append(region);
}
return adresseComplete.toString();
}
public boolean hasCoordonnees() {
return latitude != null && longitude != null;
}
public BigDecimal getEcartBudgetaire() {
if (budget == null || coutReel == null) {
return BigDecimal.ZERO;
}
return budget.subtract(coutReel);
}
public boolean estBudgetDepasse() {
return getEcartBudgetaire().compareTo(BigDecimal.ZERO) < 0;
}
}

View File

@@ -1,26 +1,26 @@
package dev.lions.unionflow.server.api.dto.favoris.request;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'un favori.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateFavoriRequest(
UUID utilisateurId,
String typeFavori,
String titre,
String description,
String url,
String icon,
String couleur,
String categorie,
Integer ordre,
Integer nbVisites,
String derniereVisite,
Boolean estPlusUtilise) {
}
package dev.lions.unionflow.server.api.dto.favoris.request;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'un favori.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateFavoriRequest(
UUID utilisateurId,
String typeFavori,
String titre,
String description,
String url,
String icon,
String couleur,
String categorie,
Integer ordre,
Integer nbVisites,
String derniereVisite,
Boolean estPlusUtilise) {
}

View File

@@ -1,37 +1,37 @@
package dev.lions.unionflow.server.api.dto.favoris.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
/**
* Réponse pour un favori utilisateur.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FavoriResponse extends BaseResponse {
private UUID utilisateurId;
private String typeFavori;
private String titre;
private String description;
private String url;
private String icon;
private String couleur;
private String categorie;
private Integer ordre;
private Integer nbVisites;
private String derniereVisite;
private Boolean estPlusUtilise;
}
package dev.lions.unionflow.server.api.dto.favoris.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
/**
* Réponse pour un favori utilisateur.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FavoriResponse extends BaseResponse {
private UUID utilisateurId;
private String typeFavori;
private String titre;
private String description;
private String url;
private String icon;
private String couleur;
private String categorie;
private Integer ordre;
private Integer nbVisites;
private String derniereVisite;
private Boolean estPlusUtilise;
}

View File

@@ -1,33 +1,33 @@
package dev.lions.unionflow.server.api.dto.finance.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une adhésion.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateAdhesionRequest(
@NotBlank(message = "Le numéro de référence est obligatoire") @Size(max = 50, message = "Le numéro de référence ne peut pas dépasser 50 caractères") String numeroReference,
@NotNull(message = "L'identifiant du membre est obligatoire") UUID membreId,
@NotNull(message = "L'identifiant de l'organisation est obligatoire") UUID organisationId,
@NotNull(message = "La date de demande est obligatoire") LocalDate dateDemande,
@NotNull(message = "Les frais d'adhésion sont obligatoires") @DecimalMin(value = "0.0", inclusive = false, message = "Les frais d'adhésion doivent être positifs") @Digits(integer = 10, fraction = 2, message = "Format de montant invalide") BigDecimal fraisAdhesion,
@NotBlank(message = "Le code devise est obligatoire") @Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres") String codeDevise,
@Size(max = 1000, message = "Les observations ne peuvent pas dépasser 1000 caractères") String observations) {
}
package dev.lions.unionflow.server.api.dto.finance.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.Builder;
/**
* Requête de création d'une adhésion.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record CreateAdhesionRequest(
@NotBlank(message = "Le numéro de référence est obligatoire") @Size(max = 50, message = "Le numéro de référence ne peut pas dépasser 50 caractères") String numeroReference,
@NotNull(message = "L'identifiant du membre est obligatoire") UUID membreId,
@NotNull(message = "L'identifiant de l'organisation est obligatoire") UUID organisationId,
@NotNull(message = "La date de demande est obligatoire") LocalDate dateDemande,
@NotNull(message = "Les frais d'adhésion sont obligatoires") @DecimalMin(value = "0.0", inclusive = false, message = "Les frais d'adhésion doivent être positifs") @Digits(integer = 10, fraction = 2, message = "Format de montant invalide") BigDecimal fraisAdhesion,
@NotBlank(message = "Le code devise est obligatoire") @Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres") String codeDevise,
@Size(max = 1000, message = "Les observations ne peuvent pas dépasser 1000 caractères") String observations) {
}

View File

@@ -1,38 +1,38 @@
package dev.lions.unionflow.server.api.dto.finance.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Builder;
/**
* Requête de mise à jour d'une adhésion.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateAdhesionRequest(
@DecimalMin(value = "0.0", message = "Le montant payé ne peut pas être négatif") @Digits(integer = 10, fraction = 2, message = "Format de montant invalide") BigDecimal montantPaye,
@Pattern(regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE|EN_PAIEMENT|PAYEE)$", message = "Statut invalide") String statut,
LocalDate dateApprobation,
LocalDateTime datePaiement,
@Pattern(regexp = "^(ESPECES|VIREMENT|CHEQUE|WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|CARTE_BANCAIRE)$", message = "Méthode de paiement invalide") String methodePaiement,
@Size(max = 100, message = "La référence de paiement ne peut pas dépasser 100 caractères") String referencePaiement,
@Size(max = 1000, message = "Le motif de rejet ne peut pas dépasser 1000 caractères") String motifRejet,
@Size(max = 1000, message = "Les observations ne peuvent pas dépasser 1000 caractères") String observations,
@Size(max = 255, message = "Le nom de l'approbateur ne peut pas dépasser 255 caractères") String approuvePar,
LocalDate dateValidation) {
}
package dev.lions.unionflow.server.api.dto.finance.request;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.Builder;
/**
* Requête de mise à jour d'une adhésion.
*
* @author UnionFlow Team
* @version 1.0
*/
@Builder
public record UpdateAdhesionRequest(
@DecimalMin(value = "0.0", message = "Le montant payé ne peut pas être négatif") @Digits(integer = 10, fraction = 2, message = "Format de montant invalide") BigDecimal montantPaye,
@Pattern(regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE|EN_PAIEMENT|PAYEE)$", message = "Statut invalide") String statut,
LocalDate dateApprobation,
LocalDateTime datePaiement,
@Pattern(regexp = "^(ESPECES|VIREMENT|CHEQUE|WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|CARTE_BANCAIRE)$", message = "Méthode de paiement invalide") String methodePaiement,
@Size(max = 100, message = "La référence de paiement ne peut pas dépasser 100 caractères") String referencePaiement,
@Size(max = 1000, message = "Le motif de rejet ne peut pas dépasser 1000 caractères") String motifRejet,
@Size(max = 1000, message = "Les observations ne peuvent pas dépasser 1000 caractères") String observations,
@Size(max = 255, message = "Le nom de l'approbateur ne peut pas dépasser 255 caractères") String approuvePar,
LocalDate dateValidation) {
}

View File

@@ -1,171 +1,171 @@
package dev.lions.unionflow.server.api.dto.finance.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse contenant les données d'une adhésion.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AdhesionResponse extends BaseResponse {
private String numeroReference;
private UUID membreId;
private String numeroMembre;
private String nomMembre;
private String emailMembre;
private UUID organisationId;
private String nomOrganisation;
private LocalDate dateDemande;
private BigDecimal fraisAdhesion;
private BigDecimal montantPaye;
private String codeDevise;
private String statut;
private LocalDate dateApprobation;
private LocalDateTime datePaiement;
private String methodePaiement;
private String referencePaiement;
private String motifRejet;
private String observations;
private String approuvePar;
private LocalDate dateValidation;
// Méthodes utilitaires héritées de l'ancien DTO
public boolean isPayeeIntegralement() {
return montantPaye != null && fraisAdhesion != null && montantPaye.compareTo(fraisAdhesion) >= 0;
}
public boolean isEnAttentePaiement() {
return "APPROUVEE".equals(statut) && !isPayeeIntegralement();
}
public BigDecimal getMontantRestant() {
if (fraisAdhesion == null)
return BigDecimal.ZERO;
if (montantPaye == null)
return fraisAdhesion;
BigDecimal restant = fraisAdhesion.subtract(montantPaye);
return restant.compareTo(BigDecimal.ZERO) > 0 ? restant : BigDecimal.ZERO;
}
public int getPourcentagePaiement() {
if (fraisAdhesion == null || fraisAdhesion.compareTo(BigDecimal.ZERO) == 0)
return 0;
if (montantPaye == null)
return 0;
return montantPaye.multiply(BigDecimal.valueOf(100)).divide(fraisAdhesion, 0, java.math.RoundingMode.HALF_UP)
.intValue();
}
public long getJoursDepuisDemande() {
if (dateDemande == null)
return 0;
return ChronoUnit.DAYS.between(dateDemande, LocalDate.now());
}
public String getStatutLibelle() {
if (statut == null)
return "Non défini";
return switch (statut) {
case "EN_ATTENTE" -> "En attente";
case "APPROUVEE" -> "Approuvée";
case "REJETEE" -> "Rejetée";
case "ANNULEE" -> "Annulée";
case "EN_PAIEMENT" -> "En paiement";
case "PAYEE" -> "Payée";
default -> statut;
};
}
public String getStatutSeverity() {
if (statut == null)
return "secondary";
return switch (statut) {
case "APPROUVEE", "PAYEE" -> "success";
case "EN_ATTENTE", "EN_PAIEMENT" -> "warning";
case "REJETEE" -> "danger";
case "ANNULEE" -> "secondary";
default -> "secondary";
};
}
public String getStatutIcon() {
if (statut == null)
return "pi-circle";
return switch (statut) {
case "APPROUVEE", "PAYEE" -> "pi-check";
case "EN_ATTENTE" -> "pi-clock";
case "EN_PAIEMENT" -> "pi-credit-card";
case "REJETEE" -> "pi-times";
case "ANNULEE" -> "pi-ban";
default -> "pi-circle";
};
}
public String getMethodePaiementLibelle() {
if (methodePaiement == null)
return "Non défini";
return switch (methodePaiement) {
case "ESPECES" -> "Espèces";
case "VIREMENT" -> "Virement bancaire";
case "CHEQUE" -> "Chèque";
case "WAVE_MONEY" -> "Wave Money";
case "ORANGE_MONEY" -> "Orange Money";
case "FREE_MONEY" -> "Free Money";
case "CARTE_BANCAIRE" -> "Carte bancaire";
default -> methodePaiement;
};
}
public String getDateDemandeFormatee() {
if (dateDemande == null)
return "";
return dateDemande.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
public String getDateApprobationFormatee() {
if (dateApprobation == null)
return "";
return dateApprobation.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
public String getDatePaiementFormatee() {
if (datePaiement == null)
return "";
return datePaiement.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
}
public String getFraisAdhesionFormatte() {
if (fraisAdhesion == null)
return "0 FCFA";
return String.format("%,.0f FCFA", fraisAdhesion.doubleValue());
}
public String getMontantPayeFormatte() {
if (montantPaye == null)
return "0 FCFA";
return String.format("%,.0f FCFA", montantPaye.doubleValue());
}
public String getMontantRestantFormatte() {
return String.format("%,.0f FCFA", getMontantRestant().doubleValue());
}
}
package dev.lions.unionflow.server.api.dto.finance.response;
import dev.lions.unionflow.server.api.dto.base.BaseResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Réponse contenant les données d'une adhésion.
*
* @author UnionFlow Team
* @version 1.0
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AdhesionResponse extends BaseResponse {
private String numeroReference;
private UUID membreId;
private String numeroMembre;
private String nomMembre;
private String emailMembre;
private UUID organisationId;
private String nomOrganisation;
private LocalDate dateDemande;
private BigDecimal fraisAdhesion;
private BigDecimal montantPaye;
private String codeDevise;
private String statut;
private LocalDate dateApprobation;
private LocalDateTime datePaiement;
private String methodePaiement;
private String referencePaiement;
private String motifRejet;
private String observations;
private String approuvePar;
private LocalDate dateValidation;
// Méthodes utilitaires héritées de l'ancien DTO
public boolean isPayeeIntegralement() {
return montantPaye != null && fraisAdhesion != null && montantPaye.compareTo(fraisAdhesion) >= 0;
}
public boolean isEnAttentePaiement() {
return "APPROUVEE".equals(statut) && !isPayeeIntegralement();
}
public BigDecimal getMontantRestant() {
if (fraisAdhesion == null)
return BigDecimal.ZERO;
if (montantPaye == null)
return fraisAdhesion;
BigDecimal restant = fraisAdhesion.subtract(montantPaye);
return restant.compareTo(BigDecimal.ZERO) > 0 ? restant : BigDecimal.ZERO;
}
public int getPourcentagePaiement() {
if (fraisAdhesion == null || fraisAdhesion.compareTo(BigDecimal.ZERO) == 0)
return 0;
if (montantPaye == null)
return 0;
return montantPaye.multiply(BigDecimal.valueOf(100)).divide(fraisAdhesion, 0, java.math.RoundingMode.HALF_UP)
.intValue();
}
public long getJoursDepuisDemande() {
if (dateDemande == null)
return 0;
return ChronoUnit.DAYS.between(dateDemande, LocalDate.now());
}
public String getStatutLibelle() {
if (statut == null)
return "Non défini";
return switch (statut) {
case "EN_ATTENTE" -> "En attente";
case "APPROUVEE" -> "Approuvée";
case "REJETEE" -> "Rejetée";
case "ANNULEE" -> "Annulée";
case "EN_PAIEMENT" -> "En paiement";
case "PAYEE" -> "Payée";
default -> statut;
};
}
public String getStatutSeverity() {
if (statut == null)
return "secondary";
return switch (statut) {
case "APPROUVEE", "PAYEE" -> "success";
case "EN_ATTENTE", "EN_PAIEMENT" -> "warning";
case "REJETEE" -> "danger";
case "ANNULEE" -> "secondary";
default -> "secondary";
};
}
public String getStatutIcon() {
if (statut == null)
return "pi-circle";
return switch (statut) {
case "APPROUVEE", "PAYEE" -> "pi-check";
case "EN_ATTENTE" -> "pi-clock";
case "EN_PAIEMENT" -> "pi-credit-card";
case "REJETEE" -> "pi-times";
case "ANNULEE" -> "pi-ban";
default -> "pi-circle";
};
}
public String getMethodePaiementLibelle() {
if (methodePaiement == null)
return "Non défini";
return switch (methodePaiement) {
case "ESPECES" -> "Espèces";
case "VIREMENT" -> "Virement bancaire";
case "CHEQUE" -> "Chèque";
case "WAVE_MONEY" -> "Wave Money";
case "ORANGE_MONEY" -> "Orange Money";
case "FREE_MONEY" -> "Free Money";
case "CARTE_BANCAIRE" -> "Carte bancaire";
default -> methodePaiement;
};
}
public String getDateDemandeFormatee() {
if (dateDemande == null)
return "";
return dateDemande.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
public String getDateApprobationFormatee() {
if (dateApprobation == null)
return "";
return dateApprobation.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
}
public String getDatePaiementFormatee() {
if (datePaiement == null)
return "";
return datePaiement.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
}
public String getFraisAdhesionFormatte() {
if (fraisAdhesion == null)
return "0 FCFA";
return String.format("%,.0f FCFA", fraisAdhesion.doubleValue());
}
public String getMontantPayeFormatte() {
if (montantPaye == null)
return "0 FCFA";
return String.format("%,.0f FCFA", montantPaye.doubleValue());
}
public String getMontantRestantFormatte() {
return String.format("%,.0f FCFA", getMontantRestant().doubleValue());
}
}

View File

@@ -1,24 +1,24 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour approuver une transaction
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApproveTransactionRequest {
@Size(max = 1000, message = "Le commentaire ne peut pas dépasser 1000 caractères")
private String comment;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour approuver une transaction
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApproveTransactionRequest {
@Size(max = 1000, message = "Le commentaire ne peut pas dépasser 1000 caractères")
private String comment;
}

View File

@@ -1,42 +1,42 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour créer une ligne budgétaire
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreateBudgetLineRequest {
@NotBlank(message = "La catégorie est requise")
@Pattern(regexp = "^(CONTRIBUTIONS|SAVINGS|SOLIDARITY|EVENTS|OPERATIONAL|INVESTMENTS|OTHER)$",
message = "Catégorie invalide")
private String category;
@NotBlank(message = "Le nom est requis")
@Size(max = 200, message = "Le nom ne peut pas dépasser 200 caractères")
private String name;
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
private String description;
@NotNull(message = "Le montant prévu est requis")
@DecimalMin(value = "0.0", message = "Le montant prévu doit être positif")
@Digits(integer = 14, fraction = 2, message = "Format du montant invalide")
private BigDecimal amountPlanned;
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
private String notes;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour créer une ligne budgétaire
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreateBudgetLineRequest {
@NotBlank(message = "La catégorie est requise")
@Pattern(regexp = "^(CONTRIBUTIONS|SAVINGS|SOLIDARITY|EVENTS|OPERATIONAL|INVESTMENTS|OTHER)$",
message = "Catégorie invalide")
private String category;
@NotBlank(message = "Le nom est requis")
@Size(max = 200, message = "Le nom ne peut pas dépasser 200 caractères")
private String name;
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
private String description;
@NotNull(message = "Le montant prévu est requis")
@DecimalMin(value = "0.0", message = "Le montant prévu doit être positif")
@Digits(integer = 14, fraction = 2, message = "Format du montant invalide")
private BigDecimal amountPlanned;
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
private String notes;
}

View File

@@ -1,58 +1,58 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour créer un budget
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreateBudgetRequest {
@NotBlank(message = "Le nom est requis")
@Size(max = 200, message = "Le nom ne peut pas dépasser 200 caractères")
private String name;
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
@NotNull(message = "L'ID de l'organisation est requis")
private UUID organizationId;
@NotBlank(message = "La période est requise")
@Pattern(regexp = "^(MONTHLY|QUARTERLY|SEMIANNUAL|ANNUAL)$",
message = "Période invalide")
private String period;
@NotNull(message = "L'année est requise")
@Min(value = 2020, message = "L'année doit être >= 2020")
@Max(value = 2100, message = "L'année doit être <= 2100")
private Integer year;
@Min(value = 1, message = "Le mois doit être entre 1 et 12")
@Max(value = 12, message = "Le mois doit être entre 1 et 12")
private Integer month;
@NotNull(message = "Au moins une ligne budgétaire est requise")
@Size(min = 1, message = "Au moins une ligne budgétaire est requise")
@Valid
@Builder.Default
private List<CreateBudgetLineRequest> lines = new ArrayList<>();
@Pattern(regexp = "^[A-Z]{3}$", message = "Code devise invalide (doit être ISO 3 lettres)")
private String currency; // Optionnel, défaut XOF
}
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour créer un budget
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreateBudgetRequest {
@NotBlank(message = "Le nom est requis")
@Size(max = 200, message = "Le nom ne peut pas dépasser 200 caractères")
private String name;
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
private String description;
@NotNull(message = "L'ID de l'organisation est requis")
private UUID organizationId;
@NotBlank(message = "La période est requise")
@Pattern(regexp = "^(MONTHLY|QUARTERLY|SEMIANNUAL|ANNUAL)$",
message = "Période invalide")
private String period;
@NotNull(message = "L'année est requise")
@Min(value = 2020, message = "L'année doit être >= 2020")
@Max(value = 2100, message = "L'année doit être <= 2100")
private Integer year;
@Min(value = 1, message = "Le mois doit être entre 1 et 12")
@Max(value = 12, message = "Le mois doit être entre 1 et 12")
private Integer month;
@NotNull(message = "Au moins une ligne budgétaire est requise")
@Size(min = 1, message = "Au moins une ligne budgétaire est requise")
@Valid
@Builder.Default
private List<CreateBudgetLineRequest> lines = new ArrayList<>();
@Pattern(regexp = "^[A-Z]{3}$", message = "Code devise invalide (doit être ISO 3 lettres)")
private String currency; // Optionnel, défaut XOF
}

View File

@@ -1,26 +1,26 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour rejeter une transaction
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class RejectTransactionRequest {
@NotBlank(message = "La raison du rejet est requise")
@Size(min = 10, max = 1000, message = "La raison doit contenir entre 10 et 1000 caractères")
private String reason;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de requête pour rejeter une transaction
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class RejectTransactionRequest {
@NotBlank(message = "La raison du rejet est requise")
@Size(min = 10, max = 1000, message = "La raison doit contenir entre 10 et 1000 caractères")
private String reason;
}

View File

@@ -1,44 +1,44 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour une action d'approbateur
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApproverActionResponse {
private UUID id;
@NotNull
private UUID approverId;
@NotBlank
private String approverName;
@NotBlank
private String approverRole;
@NotBlank
private String decision; // PENDING, APPROVED, REJECTED
private String comment;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime decidedAt;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour une action d'approbateur
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApproverActionResponse {
private UUID id;
@NotNull
private UUID approverId;
@NotBlank
private String approverName;
@NotBlank
private String approverRole;
@NotBlank
private String decision; // PENDING, APPROVED, REJECTED
private String comment;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime decidedAt;
}

View File

@@ -1,47 +1,47 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour une ligne budgétaire
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BudgetLineResponse {
private UUID id;
@NotBlank
private String category; // CONTRIBUTIONS, SAVINGS, SOLIDARITY, etc.
@NotBlank
private String name;
private String description;
@NotNull
private BigDecimal amountPlanned;
@NotNull
private BigDecimal amountRealized;
private String notes;
// Champs calculés
private Double realizationRate;
private BigDecimal variance;
private Boolean isOverBudget;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour une ligne budgétaire
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BudgetLineResponse {
private UUID id;
@NotBlank
private String category; // CONTRIBUTIONS, SAVINGS, SOLIDARITY, etc.
@NotBlank
private String name;
private String description;
@NotNull
private BigDecimal amountPlanned;
@NotNull
private BigDecimal amountRealized;
private String notes;
// Champs calculés
private Double realizationRate;
private BigDecimal variance;
private Boolean isOverBudget;
}

View File

@@ -1,92 +1,92 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour un budget
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BudgetResponse {
private UUID id;
@NotBlank
private String name;
private String description;
@NotNull
private UUID organizationId;
@NotBlank
private String period; // MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL
@NotNull
private Integer year;
private Integer month;
@NotBlank
private String status; // DRAFT, ACTIVE, CLOSED, CANCELLED
@Builder.Default
private List<BudgetLineResponse> lines = new ArrayList<>();
@NotNull
private BigDecimal totalPlanned;
@NotNull
private BigDecimal totalRealized;
@NotBlank
private String currency;
@NotNull
private UUID createdById;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime approvedAt;
private UUID approvedById;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
private String metadata;
// Champs calculés
private Double realizationRate;
private BigDecimal variance;
private Double varianceRate;
private Boolean isOverBudget;
private Boolean isActive;
private Boolean isCurrentPeriod;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour un budget
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BudgetResponse {
private UUID id;
@NotBlank
private String name;
private String description;
@NotNull
private UUID organizationId;
@NotBlank
private String period; // MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL
@NotNull
private Integer year;
private Integer month;
@NotBlank
private String status; // DRAFT, ACTIVE, CLOSED, CANCELLED
@Builder.Default
private List<BudgetLineResponse> lines = new ArrayList<>();
@NotNull
private BigDecimal totalPlanned;
@NotNull
private BigDecimal totalRealized;
@NotBlank
private String currency;
@NotNull
private UUID createdById;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime approvedAt;
private UUID approvedById;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
private String metadata;
// Champs calculés
private Double realizationRate;
private BigDecimal variance;
private Double varianceRate;
private Boolean isOverBudget;
private Boolean isActive;
private Boolean isCurrentPeriod;
}

View File

@@ -1,81 +1,81 @@
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour une approbation de transaction
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TransactionApprovalResponse {
private UUID id;
@NotNull
private UUID transactionId;
@NotBlank
private String transactionType; // CONTRIBUTION, DEPOSIT, WITHDRAWAL, etc.
@NotNull
private BigDecimal amount;
@NotBlank
private String currency;
@NotNull
private UUID requesterId;
@NotBlank
private String requesterName;
private UUID organizationId;
@NotBlank
private String requiredLevel; // NONE, LEVEL1, LEVEL2, LEVEL3
@NotBlank
private String status; // PENDING, APPROVED, VALIDATED, REJECTED, EXPIRED, CANCELLED
@Builder.Default
private List<ApproverActionResponse> approvers = new ArrayList<>();
private String rejectionReason;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime expiresAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime completedAt;
private String metadata;
// Champs calculés
private Integer approvalCount;
private Integer requiredApprovals;
private Boolean hasAllApprovals;
private Boolean isExpired;
private Boolean isPending;
private Boolean isCompleted;
}
package dev.lions.unionflow.server.api.dto.finance_workflow.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* DTO de réponse pour une approbation de transaction
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TransactionApprovalResponse {
private UUID id;
@NotNull
private UUID transactionId;
@NotBlank
private String transactionType; // CONTRIBUTION, DEPOSIT, WITHDRAWAL, etc.
@NotNull
private BigDecimal amount;
@NotBlank
private String currency;
@NotNull
private UUID requesterId;
@NotBlank
private String requesterName;
private UUID organizationId;
@NotBlank
private String requiredLevel; // NONE, LEVEL1, LEVEL2, LEVEL3
@NotBlank
private String status; // PENDING, APPROVED, VALIDATED, REJECTED, EXPIRED, CANCELLED
@Builder.Default
private List<ApproverActionResponse> approvers = new ArrayList<>();
private String rejectionReason;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime expiresAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime completedAt;
private String metadata;
// Champs calculés
private Integer approvalCount;
private Integer requiredApprovals;
private Boolean hasAllApprovals;
private Boolean isExpired;
private Boolean isPending;
private Boolean isCompleted;
}

View File

@@ -1,51 +1,51 @@
package dev.lions.unionflow.server.api.dto.formuleabonnement.request;
import dev.lions.unionflow.server.api.enums.formuleabonnement.StatutFormule;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de création d'une formule d'abonnement.
*/
@Builder
public record CreateFormuleAbonnementRequest(
@NotBlank(message = "Le nom de la formule est obligatoire") @Size(min = 2, max = 100, message = "Le nom de la formule doit contenir entre 2 et 100 caractères") String nom,
@NotBlank(message = "Le code de la formule est obligatoire") @Pattern(regexp = "^[A-Z_]{2,20}$", message = "Le code doit contenir uniquement des lettres majuscules et underscores") String code,
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères") String description,
@NotNull(message = "Le type de formule est obligatoire") TypeFormule type,
@NotNull(message = "Le statut est obligatoire") StatutFormule statut,
@NotNull(message = "Le prix mensuel est obligatoire") @DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal prixMensuel,
@DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal prixAnnuel,
@NotBlank(message = "La devise est obligatoire") @Pattern(regexp = "^[A-Z]{3}$") String devise,
@NotNull(message = "Le nombre maximum de membres est obligatoire") Integer maxMembres,
@NotNull(message = "Le nombre maximum d'administrateurs est obligatoire") Integer maxAdministrateurs,
@NotNull(message = "L'espace de stockage est obligatoire") @DecimalMin(value = "0.1", message = "L'espace de stockage doit être d'au moins 0.1 GB") BigDecimal espaceStockageGB,
@NotNull(message = "Le support technique doit être spécifié") Boolean supportTechnique,
@Pattern(regexp = "^(EMAIL|CHAT|TELEPHONE|PREMIUM)$", message = "Le niveau de support doit être EMAIL, CHAT, TELEPHONE ou PREMIUM") String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
Boolean sauvegardeAutomatique,
Boolean multiLangues,
Boolean personnalisationInterface,
Boolean formationIncluse,
Integer heuresFormation,
Boolean populaire,
Boolean recommandee,
Integer periodeEssaiJours,
LocalDate dateDebutValidite,
LocalDate dateFinValidite,
Integer ordreAffichage,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$", message = "La couleur doit être un code hexadécimal valide") String couleur,
@Size(max = 50, message = "Le nom de l'icône ne peut pas dépasser 50 caractères") String icone,
@Size(max = 500, message = "Les notes ne peuvent pas dépasser 500 caractères") String notes) {
}
package dev.lions.unionflow.server.api.dto.formuleabonnement.request;
import dev.lions.unionflow.server.api.enums.formuleabonnement.StatutFormule;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de création d'une formule d'abonnement.
*/
@Builder
public record CreateFormuleAbonnementRequest(
@NotBlank(message = "Le nom de la formule est obligatoire") @Size(min = 2, max = 100, message = "Le nom de la formule doit contenir entre 2 et 100 caractères") String nom,
@NotBlank(message = "Le code de la formule est obligatoire") @Pattern(regexp = "^[A-Z_]{2,20}$", message = "Le code doit contenir uniquement des lettres majuscules et underscores") String code,
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères") String description,
@NotNull(message = "Le type de formule est obligatoire") TypeFormule type,
@NotNull(message = "Le statut est obligatoire") StatutFormule statut,
@NotNull(message = "Le prix mensuel est obligatoire") @DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal prixMensuel,
@DecimalMin(value = "0.0", inclusive = false) @Digits(integer = 10, fraction = 2) BigDecimal prixAnnuel,
@NotBlank(message = "La devise est obligatoire") @Pattern(regexp = "^[A-Z]{3}$") String devise,
@NotNull(message = "Le nombre maximum de membres est obligatoire") Integer maxMembres,
@NotNull(message = "Le nombre maximum d'administrateurs est obligatoire") Integer maxAdministrateurs,
@NotNull(message = "L'espace de stockage est obligatoire") @DecimalMin(value = "0.1", message = "L'espace de stockage doit être d'au moins 0.1 GB") BigDecimal espaceStockageGB,
@NotNull(message = "Le support technique doit être spécifié") Boolean supportTechnique,
@Pattern(regexp = "^(EMAIL|CHAT|TELEPHONE|PREMIUM)$", message = "Le niveau de support doit être EMAIL, CHAT, TELEPHONE ou PREMIUM") String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
Boolean sauvegardeAutomatique,
Boolean multiLangues,
Boolean personnalisationInterface,
Boolean formationIncluse,
Integer heuresFormation,
Boolean populaire,
Boolean recommandee,
Integer periodeEssaiJours,
LocalDate dateDebutValidite,
LocalDate dateFinValidite,
Integer ordreAffichage,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$", message = "La couleur doit être un code hexadécimal valide") String couleur,
@Size(max = 50, message = "Le nom de l'icône ne peut pas dépasser 50 caractères") String icone,
@Size(max = 500, message = "Les notes ne peuvent pas dépasser 500 caractères") String notes) {
}

View File

@@ -1,49 +1,49 @@
package dev.lions.unionflow.server.api.dto.formuleabonnement.request;
import dev.lions.unionflow.server.api.enums.formuleabonnement.StatutFormule;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de mise à jour d'une formule d'abonnement.
*/
@Builder
public record UpdateFormuleAbonnementRequest(
@Size(min = 2, max = 100, message = "Le nom de la formule doit contenir entre 2 et 100 caractères") String nom,
@Pattern(regexp = "^[A-Z_]{2,20}$", message = "Le code doit contenir uniquement des lettres majuscules et underscores") String code,
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères") String description,
TypeFormule type,
StatutFormule statut,
@DecimalMin(value = "0.0", inclusive = false, message = "Le prix mensuel doit être positif") @Digits(integer = 10, fraction = 2) BigDecimal prixMensuel,
@DecimalMin(value = "0.0", inclusive = false, message = "Le prix annuel doit être positif") @Digits(integer = 10, fraction = 2) BigDecimal prixAnnuel,
@Pattern(regexp = "^[A-Z]{3}$") String devise,
Integer maxMembres,
Integer maxAdministrateurs,
@DecimalMin(value = "0.1", message = "L'espace de stockage doit être d'au moins 0.1 GB") BigDecimal espaceStockageGB,
Boolean supportTechnique,
@Pattern(regexp = "^(EMAIL|CHAT|TELEPHONE|PREMIUM)$", message = "Le niveau de support doit être EMAIL, CHAT, TELEPHONE ou PREMIUM") String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
Boolean sauvegardeAutomatique,
Boolean multiLangues,
Boolean personnalisationInterface,
Boolean formationIncluse,
Integer heuresFormation,
Boolean populaire,
Boolean recommandee,
Integer periodeEssaiJours,
LocalDate dateDebutValidite,
LocalDate dateFinValidite,
Integer ordreAffichage,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$", message = "La couleur doit être un code hexadécimal valide") String couleur,
@Size(max = 50, message = "Le nom de l'icône ne peut pas dépasser 50 caractères") String icone,
@Size(max = 500, message = "Les notes ne peuvent pas dépasser 500 caractères") String notes) {
}
package dev.lions.unionflow.server.api.dto.formuleabonnement.request;
import dev.lions.unionflow.server.api.enums.formuleabonnement.StatutFormule;
import dev.lions.unionflow.server.api.enums.formuleabonnement.TypeFormule;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Builder;
/**
* Requête de mise à jour d'une formule d'abonnement.
*/
@Builder
public record UpdateFormuleAbonnementRequest(
@Size(min = 2, max = 100, message = "Le nom de la formule doit contenir entre 2 et 100 caractères") String nom,
@Pattern(regexp = "^[A-Z_]{2,20}$", message = "Le code doit contenir uniquement des lettres majuscules et underscores") String code,
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères") String description,
TypeFormule type,
StatutFormule statut,
@DecimalMin(value = "0.0", inclusive = false, message = "Le prix mensuel doit être positif") @Digits(integer = 10, fraction = 2) BigDecimal prixMensuel,
@DecimalMin(value = "0.0", inclusive = false, message = "Le prix annuel doit être positif") @Digits(integer = 10, fraction = 2) BigDecimal prixAnnuel,
@Pattern(regexp = "^[A-Z]{3}$") String devise,
Integer maxMembres,
Integer maxAdministrateurs,
@DecimalMin(value = "0.1", message = "L'espace de stockage doit être d'au moins 0.1 GB") BigDecimal espaceStockageGB,
Boolean supportTechnique,
@Pattern(regexp = "^(EMAIL|CHAT|TELEPHONE|PREMIUM)$", message = "Le niveau de support doit être EMAIL, CHAT, TELEPHONE ou PREMIUM") String niveauSupport,
Boolean fonctionnalitesAvancees,
Boolean apiAccess,
Boolean rapportsPersonnalises,
Boolean integrationsTierces,
Boolean sauvegardeAutomatique,
Boolean multiLangues,
Boolean personnalisationInterface,
Boolean formationIncluse,
Integer heuresFormation,
Boolean populaire,
Boolean recommandee,
Integer periodeEssaiJours,
LocalDate dateDebutValidite,
LocalDate dateFinValidite,
Integer ordreAffichage,
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$", message = "La couleur doit être un code hexadécimal valide") String couleur,
@Size(max = 50, message = "Le nom de l'icône ne peut pas dépasser 50 caractères") String icone,
@Size(max = 500, message = "Les notes ne peuvent pas dépasser 500 caractères") String notes) {
}

Some files were not shown because too many files have changed in this diff Show More