Initial commit: unionflow-mobile-apps
Application Flutter complète (sans build artifacts). Signed-off-by: lions dev Team
This commit is contained in:
143
unionflow-server-api/.gitignore
vendored
Normal file
143
unionflow-server-api/.gitignore
vendored
Normal file
@@ -0,0 +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
|
||||
|
||||
359
unionflow-server-api/checkstyle-unionflow.xml
Normal file
359
unionflow-server-api/checkstyle-unionflow.xml
Normal file
@@ -0,0 +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>
|
||||
236
unionflow-server-api/pom.xml
Normal file
236
unionflow-server-api/pom.xml
Normal file
@@ -0,0 +1,236 @@
|
||||
<?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-server-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>UnionFlow Server API</name>
|
||||
<description>API définitions pour le serveur UnionFlow</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<quarkus.platform.version>3.15.1</quarkus.platform.version>
|
||||
<jackson.version>2.17.0</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>
|
||||
<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>
|
||||
|
||||
<!-- Seuils de couverture Jacoco - 100% obligatoire -->
|
||||
<jacoco.line.coverage.minimum>1.00</jacoco.line.coverage.minimum>
|
||||
<jacoco.branch.coverage.minimum>1.00</jacoco.branch.coverage.minimum>
|
||||
<jacoco.instruction.coverage.minimum>1.00</jacoco.instruction.coverage.minimum>
|
||||
<jacoco.method.coverage.minimum>1.00</jacoco.method.coverage.minimum>
|
||||
<jacoco.class.coverage.minimum>1.00</jacoco.class.coverage.minimum>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Jackson pour la sérialisation JSON -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Bean Validation -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>${validation-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MicroProfile OpenAPI pour la documentation -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.openapi</groupId>
|
||||
<artifactId>microprofile-openapi-api</artifactId>
|
||||
<version>${microprofile-openapi.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- Dépendances de test -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>8.0.1.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
<version>4.0.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok pour génération automatique des getters/setters -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Plugin Jacoco pour la couverture de code -->
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${jacoco.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>check</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<rule>
|
||||
<element>BUNDLE</element>
|
||||
<limits>
|
||||
<limit>
|
||||
<counter>LINE</counter>
|
||||
<value>COVEREDRATIO</value>
|
||||
<minimum>${jacoco.line.coverage.minimum}</minimum>
|
||||
</limit>
|
||||
<limit>
|
||||
<counter>BRANCH</counter>
|
||||
<value>COVEREDRATIO</value>
|
||||
<minimum>${jacoco.branch.coverage.minimum}</minimum>
|
||||
</limit>
|
||||
<limit>
|
||||
<counter>INSTRUCTION</counter>
|
||||
<value>COVEREDRATIO</value>
|
||||
<minimum>${jacoco.instruction.coverage.minimum}</minimum>
|
||||
</limit>
|
||||
<limit>
|
||||
<counter>METHOD</counter>
|
||||
<value>COVEREDRATIO</value>
|
||||
<minimum>${jacoco.method.coverage.minimum}</minimum>
|
||||
</limit>
|
||||
<limit>
|
||||
<counter>CLASS</counter>
|
||||
<value>COVEREDRATIO</value>
|
||||
<minimum>${jacoco.class.coverage.minimum}</minimum>
|
||||
</limit>
|
||||
</limits>
|
||||
</rule>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Plugin Checkstyle pour la qualité du code -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>${maven-checkstyle-plugin.version}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>${checkstyle.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<configLocation>google_checks.xml</configLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
<excludes>**/target/**/*</excludes>
|
||||
</configuration>
|
||||
<!-- Executions désactivées temporairement pour les tests
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
-->
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,622 @@
|
||||
package dev.lions.unionflow.server.api.dto.abonnement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des abonnements UnionFlow Représente un abonnement d'une organisation à une
|
||||
* formule
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AbonnementDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de référence unique de l'abonnement */
|
||||
@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)")
|
||||
private String numeroReference;
|
||||
|
||||
/** Identifiant de l'organisation abonnée */
|
||||
@NotNull(message = "L'identifiant de l'organisation est obligatoire")
|
||||
private UUID organisationId;
|
||||
|
||||
/** Nom de l'organisation abonnée */
|
||||
private String nomOrganisation;
|
||||
|
||||
/** Identifiant de la formule d'abonnement */
|
||||
@NotNull(message = "L'identifiant de la formule est obligatoire")
|
||||
private UUID formulaireId;
|
||||
|
||||
/** Code de la formule */
|
||||
private String codeFormule;
|
||||
|
||||
/** Nom de la formule */
|
||||
private String nomFormule;
|
||||
|
||||
/** Type de formule (BASIC, STANDARD, PREMIUM, ENTERPRISE) */
|
||||
private String typeFormule;
|
||||
|
||||
/** Statut de l'abonnement ACTIF, SUSPENDU, EXPIRE, ANNULE, EN_ATTENTE_PAIEMENT */
|
||||
@NotBlank(message = "Le statut est obligatoire")
|
||||
@Pattern(
|
||||
regexp = "^(ACTIF|SUSPENDU|EXPIRE|ANNULE|EN_ATTENTE_PAIEMENT)$",
|
||||
message = "Statut invalide")
|
||||
private String statut;
|
||||
|
||||
/** Type d'abonnement (MENSUEL, ANNUEL) */
|
||||
@NotBlank(message = "Le type d'abonnement est obligatoire")
|
||||
@Pattern(regexp = "^(MENSUEL|ANNUEL)$", message = "Le type doit être MENSUEL ou ANNUEL")
|
||||
private String typeAbonnement;
|
||||
|
||||
/** Date de début de l'abonnement */
|
||||
@NotNull(message = "La date de début est obligatoire")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateDebut;
|
||||
|
||||
/** Date de fin de l'abonnement */
|
||||
@Future(message = "La date de fin doit être dans le futur")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateFin;
|
||||
|
||||
/** Date de la prochaine facturation */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateProchainePeriode;
|
||||
|
||||
/** Montant de l'abonnement */
|
||||
@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")
|
||||
private BigDecimal montant;
|
||||
|
||||
/** Devise */
|
||||
@NotBlank(message = "La devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
|
||||
private String devise;
|
||||
|
||||
/** Remise appliquée (pourcentage) */
|
||||
@DecimalMin(value = "0.0", message = "La remise doit être positive")
|
||||
@DecimalMin(value = "100.0", message = "La remise ne peut pas dépasser 100%")
|
||||
private BigDecimal remise;
|
||||
|
||||
/** Montant après 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")
|
||||
private BigDecimal montantFinal;
|
||||
|
||||
/** Renouvellement automatique */
|
||||
private Boolean renouvellementAutomatique;
|
||||
|
||||
/** Période d'essai utilisée */
|
||||
private Boolean periodeEssaiUtilisee;
|
||||
|
||||
/** Date de fin de la période d'essai */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateFinEssai;
|
||||
|
||||
/** Nombre de membres autorisés */
|
||||
private Integer maxMembres;
|
||||
|
||||
/** Nombre de membres actuels */
|
||||
private Integer nombreMembresActuels;
|
||||
|
||||
/** Espace de stockage alloué (GB) */
|
||||
private BigDecimal espaceStockageGB;
|
||||
|
||||
/** Espace de stockage utilisé (GB) */
|
||||
private BigDecimal espaceStockageUtilise;
|
||||
|
||||
/** Support technique inclus */
|
||||
private Boolean supportTechnique;
|
||||
|
||||
/** Niveau de support */
|
||||
private String niveauSupport;
|
||||
|
||||
/** Fonctionnalités avancées activées */
|
||||
private Boolean fonctionnalitesAvancees;
|
||||
|
||||
/** Accès API activé */
|
||||
private Boolean apiAccess;
|
||||
|
||||
/** Rapports personnalisés activés */
|
||||
private Boolean rapportsPersonnalises;
|
||||
|
||||
/** Intégrations tierces activées */
|
||||
private Boolean integrationsTierces;
|
||||
|
||||
/** Date de dernière utilisation */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateDerniereUtilisation;
|
||||
|
||||
/** Nombre de connexions ce mois */
|
||||
private Integer connexionsCeMois;
|
||||
|
||||
/** Identifiant du responsable de l'abonnement */
|
||||
private UUID responsableId;
|
||||
|
||||
/** Nom du responsable */
|
||||
private String nomResponsable;
|
||||
|
||||
/** Email du responsable */
|
||||
private String emailResponsable;
|
||||
|
||||
/** Téléphone du responsable */
|
||||
private String telephoneResponsable;
|
||||
|
||||
/** Mode de paiement préféré */
|
||||
@Pattern(
|
||||
regexp = "^(WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|VIREMENT|CHEQUE|AUTRE)$",
|
||||
message = "Mode de paiement invalide")
|
||||
private String modePaiementPrefere;
|
||||
|
||||
/** Numéro de téléphone pour paiement mobile */
|
||||
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide")
|
||||
private String numeroPaiementMobile;
|
||||
|
||||
/** Historique des paiements (JSON) */
|
||||
@Size(max = 5000, message = "L'historique ne peut pas dépasser 5000 caractères")
|
||||
private String historiquePaiements;
|
||||
|
||||
/** Notes sur l'abonnement */
|
||||
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
|
||||
private String notes;
|
||||
|
||||
/** Alertes activées */
|
||||
private Boolean alertesActivees;
|
||||
|
||||
/** Notifications par email */
|
||||
private Boolean notificationsEmail;
|
||||
|
||||
/** Notifications par SMS */
|
||||
private Boolean notificationsSMS;
|
||||
|
||||
/** Date de suspension (si applicable) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateSuspension;
|
||||
|
||||
/** Raison de la suspension */
|
||||
@Size(max = 500, message = "La raison ne peut pas dépasser 500 caractères")
|
||||
private String raisonSuspension;
|
||||
|
||||
/** Date d'annulation (si applicable) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateAnnulation;
|
||||
|
||||
/** Raison de l'annulation */
|
||||
@Size(max = 500, message = "La raison ne peut pas dépasser 500 caractères")
|
||||
private String raisonAnnulation;
|
||||
|
||||
// Constructeurs
|
||||
public AbonnementDTO() {
|
||||
super();
|
||||
this.statut = "EN_ATTENTE_PAIEMENT";
|
||||
this.devise = "XOF";
|
||||
this.renouvellementAutomatique = true;
|
||||
this.periodeEssaiUtilisee = false;
|
||||
this.supportTechnique = true;
|
||||
this.fonctionnalitesAvancees = false;
|
||||
this.apiAccess = false;
|
||||
this.rapportsPersonnalises = false;
|
||||
this.integrationsTierces = false;
|
||||
this.connexionsCeMois = 0;
|
||||
this.alertesActivees = true;
|
||||
this.notificationsEmail = true;
|
||||
this.notificationsSMS = false;
|
||||
this.numeroReference = genererNumeroReference();
|
||||
}
|
||||
|
||||
public AbonnementDTO(UUID organisationId, String nomOrganisation, String typeFormule) {
|
||||
this();
|
||||
this.organisationId = organisationId;
|
||||
this.nomOrganisation = nomOrganisation;
|
||||
this.typeFormule = typeFormule;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getNumeroReference() {
|
||||
return numeroReference;
|
||||
}
|
||||
|
||||
public void setNumeroReference(String numeroReference) {
|
||||
this.numeroReference = numeroReference;
|
||||
}
|
||||
|
||||
public UUID getOrganisationId() {
|
||||
return organisationId;
|
||||
}
|
||||
|
||||
public void setOrganisationId(UUID organisationId) {
|
||||
this.organisationId = organisationId;
|
||||
}
|
||||
|
||||
public String getNomOrganisation() {
|
||||
return nomOrganisation;
|
||||
}
|
||||
|
||||
public void setNomOrganisation(String nomOrganisation) {
|
||||
this.nomOrganisation = nomOrganisation;
|
||||
}
|
||||
|
||||
public UUID getFormulaireId() {
|
||||
return formulaireId;
|
||||
}
|
||||
|
||||
public void setFormulaireId(UUID formulaireId) {
|
||||
this.formulaireId = formulaireId;
|
||||
}
|
||||
|
||||
public String getCodeFormule() {
|
||||
return codeFormule;
|
||||
}
|
||||
|
||||
public void setCodeFormule(String codeFormule) {
|
||||
this.codeFormule = codeFormule;
|
||||
}
|
||||
|
||||
public String getNomFormule() {
|
||||
return nomFormule;
|
||||
}
|
||||
|
||||
public void setNomFormule(String nomFormule) {
|
||||
this.nomFormule = nomFormule;
|
||||
}
|
||||
|
||||
public String getTypeFormule() {
|
||||
return typeFormule;
|
||||
}
|
||||
|
||||
public void setTypeFormule(String typeFormule) {
|
||||
this.typeFormule = typeFormule;
|
||||
}
|
||||
|
||||
public String getStatut() {
|
||||
return statut;
|
||||
}
|
||||
|
||||
public void setStatut(String statut) {
|
||||
this.statut = statut;
|
||||
}
|
||||
|
||||
public String getTypeAbonnement() {
|
||||
return typeAbonnement;
|
||||
}
|
||||
|
||||
public void setTypeAbonnement(String typeAbonnement) {
|
||||
this.typeAbonnement = typeAbonnement;
|
||||
}
|
||||
|
||||
public LocalDate getDateDebut() {
|
||||
return dateDebut;
|
||||
}
|
||||
|
||||
public void setDateDebut(LocalDate dateDebut) {
|
||||
this.dateDebut = dateDebut;
|
||||
}
|
||||
|
||||
public LocalDate getDateFin() {
|
||||
return dateFin;
|
||||
}
|
||||
|
||||
public void setDateFin(LocalDate dateFin) {
|
||||
this.dateFin = dateFin;
|
||||
}
|
||||
|
||||
public LocalDate getDateProchainePeriode() {
|
||||
return dateProchainePeriode;
|
||||
}
|
||||
|
||||
public void setDateProchainePeriode(LocalDate dateProchainePeriode) {
|
||||
this.dateProchainePeriode = dateProchainePeriode;
|
||||
}
|
||||
|
||||
public BigDecimal getMontant() {
|
||||
return montant;
|
||||
}
|
||||
|
||||
public void setMontant(BigDecimal montant) {
|
||||
this.montant = montant;
|
||||
}
|
||||
|
||||
public String getDevise() {
|
||||
return devise;
|
||||
}
|
||||
|
||||
public void setDevise(String devise) {
|
||||
this.devise = devise;
|
||||
}
|
||||
|
||||
// Getters et setters restants (suite)
|
||||
public BigDecimal getRemise() {
|
||||
return remise;
|
||||
}
|
||||
|
||||
public void setRemise(BigDecimal remise) {
|
||||
this.remise = remise;
|
||||
}
|
||||
|
||||
public BigDecimal getMontantFinal() {
|
||||
return montantFinal;
|
||||
}
|
||||
|
||||
public void setMontantFinal(BigDecimal montantFinal) {
|
||||
this.montantFinal = montantFinal;
|
||||
}
|
||||
|
||||
public Boolean getRenouvellementAutomatique() {
|
||||
return renouvellementAutomatique;
|
||||
}
|
||||
|
||||
public void setRenouvellementAutomatique(Boolean renouvellementAutomatique) {
|
||||
this.renouvellementAutomatique = renouvellementAutomatique;
|
||||
}
|
||||
|
||||
public Boolean getPeriodeEssaiUtilisee() {
|
||||
return periodeEssaiUtilisee;
|
||||
}
|
||||
|
||||
public void setPeriodeEssaiUtilisee(Boolean periodeEssaiUtilisee) {
|
||||
this.periodeEssaiUtilisee = periodeEssaiUtilisee;
|
||||
}
|
||||
|
||||
public LocalDate getDateFinEssai() {
|
||||
return dateFinEssai;
|
||||
}
|
||||
|
||||
public void setDateFinEssai(LocalDate dateFinEssai) {
|
||||
this.dateFinEssai = dateFinEssai;
|
||||
}
|
||||
|
||||
public Integer getMaxMembres() {
|
||||
return maxMembres;
|
||||
}
|
||||
|
||||
public void setMaxMembres(Integer maxMembres) {
|
||||
this.maxMembres = maxMembres;
|
||||
}
|
||||
|
||||
public Integer getNombreMembresActuels() {
|
||||
return nombreMembresActuels;
|
||||
}
|
||||
|
||||
public void setNombreMembresActuels(Integer nombreMembresActuels) {
|
||||
this.nombreMembresActuels = nombreMembresActuels;
|
||||
}
|
||||
|
||||
public BigDecimal getEspaceStockageGB() {
|
||||
return espaceStockageGB;
|
||||
}
|
||||
|
||||
public void setEspaceStockageGB(BigDecimal espaceStockageGB) {
|
||||
this.espaceStockageGB = espaceStockageGB;
|
||||
}
|
||||
|
||||
public BigDecimal getEspaceStockageUtilise() {
|
||||
return espaceStockageUtilise;
|
||||
}
|
||||
|
||||
public void setEspaceStockageUtilise(BigDecimal espaceStockageUtilise) {
|
||||
this.espaceStockageUtilise = espaceStockageUtilise;
|
||||
}
|
||||
|
||||
public Boolean getSupportTechnique() {
|
||||
return supportTechnique;
|
||||
}
|
||||
|
||||
public void setSupportTechnique(Boolean supportTechnique) {
|
||||
this.supportTechnique = supportTechnique;
|
||||
}
|
||||
|
||||
public String getNiveauSupport() {
|
||||
return niveauSupport;
|
||||
}
|
||||
|
||||
public void setNiveauSupport(String niveauSupport) {
|
||||
this.niveauSupport = niveauSupport;
|
||||
}
|
||||
|
||||
public Boolean getFonctionnalitesAvancees() {
|
||||
return fonctionnalitesAvancees;
|
||||
}
|
||||
|
||||
public void setFonctionnalitesAvancees(Boolean fonctionnalitesAvancees) {
|
||||
this.fonctionnalitesAvancees = fonctionnalitesAvancees;
|
||||
}
|
||||
|
||||
public Boolean getApiAccess() {
|
||||
return apiAccess;
|
||||
}
|
||||
|
||||
public void setApiAccess(Boolean apiAccess) {
|
||||
this.apiAccess = apiAccess;
|
||||
}
|
||||
|
||||
public Boolean getRapportsPersonnalises() {
|
||||
return rapportsPersonnalises;
|
||||
}
|
||||
|
||||
public void setRapportsPersonnalises(Boolean rapportsPersonnalises) {
|
||||
this.rapportsPersonnalises = rapportsPersonnalises;
|
||||
}
|
||||
|
||||
public Boolean getIntegrationsTierces() {
|
||||
return integrationsTierces;
|
||||
}
|
||||
|
||||
public void setIntegrationsTierces(Boolean integrationsTierces) {
|
||||
this.integrationsTierces = integrationsTierces;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateDerniereUtilisation() {
|
||||
return dateDerniereUtilisation;
|
||||
}
|
||||
|
||||
public void setDateDerniereUtilisation(LocalDateTime dateDerniereUtilisation) {
|
||||
this.dateDerniereUtilisation = dateDerniereUtilisation;
|
||||
}
|
||||
|
||||
public Integer getConnexionsCeMois() {
|
||||
return connexionsCeMois;
|
||||
}
|
||||
|
||||
public void setConnexionsCeMois(Integer connexionsCeMois) {
|
||||
this.connexionsCeMois = connexionsCeMois;
|
||||
}
|
||||
|
||||
public UUID getResponsableId() {
|
||||
return responsableId;
|
||||
}
|
||||
|
||||
public void setResponsableId(UUID responsableId) {
|
||||
this.responsableId = responsableId;
|
||||
}
|
||||
|
||||
public String getNomResponsable() {
|
||||
return nomResponsable;
|
||||
}
|
||||
|
||||
public void setNomResponsable(String nomResponsable) {
|
||||
this.nomResponsable = nomResponsable;
|
||||
}
|
||||
|
||||
public String getEmailResponsable() {
|
||||
return emailResponsable;
|
||||
}
|
||||
|
||||
public void setEmailResponsable(String emailResponsable) {
|
||||
this.emailResponsable = emailResponsable;
|
||||
}
|
||||
|
||||
public String getTelephoneResponsable() {
|
||||
return telephoneResponsable;
|
||||
}
|
||||
|
||||
public void setTelephoneResponsable(String telephoneResponsable) {
|
||||
this.telephoneResponsable = telephoneResponsable;
|
||||
}
|
||||
|
||||
public String getModePaiementPrefere() {
|
||||
return modePaiementPrefere;
|
||||
}
|
||||
|
||||
public void setModePaiementPrefere(String modePaiementPrefere) {
|
||||
this.modePaiementPrefere = modePaiementPrefere;
|
||||
}
|
||||
|
||||
public String getNumeroPaiementMobile() {
|
||||
return numeroPaiementMobile;
|
||||
}
|
||||
|
||||
public void setNumeroPaiementMobile(String numeroPaiementMobile) {
|
||||
this.numeroPaiementMobile = numeroPaiementMobile;
|
||||
}
|
||||
|
||||
public String getHistoriquePaiements() {
|
||||
return historiquePaiements;
|
||||
}
|
||||
|
||||
public void setHistoriquePaiements(String historiquePaiements) {
|
||||
this.historiquePaiements = historiquePaiements;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(String notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public Boolean getAlertesActivees() {
|
||||
return alertesActivees;
|
||||
}
|
||||
|
||||
public void setAlertesActivees(Boolean alertesActivees) {
|
||||
this.alertesActivees = alertesActivees;
|
||||
}
|
||||
|
||||
public Boolean getNotificationsEmail() {
|
||||
return notificationsEmail;
|
||||
}
|
||||
|
||||
public void setNotificationsEmail(Boolean notificationsEmail) {
|
||||
this.notificationsEmail = notificationsEmail;
|
||||
}
|
||||
|
||||
public Boolean getNotificationsSMS() {
|
||||
return notificationsSMS;
|
||||
}
|
||||
|
||||
public void setNotificationsSMS(Boolean notificationsSMS) {
|
||||
this.notificationsSMS = notificationsSMS;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateSuspension() {
|
||||
return dateSuspension;
|
||||
}
|
||||
|
||||
public void setDateSuspension(LocalDateTime dateSuspension) {
|
||||
this.dateSuspension = dateSuspension;
|
||||
}
|
||||
|
||||
public String getRaisonSuspension() {
|
||||
return raisonSuspension;
|
||||
}
|
||||
|
||||
public void setRaisonSuspension(String raisonSuspension) {
|
||||
this.raisonSuspension = raisonSuspension;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateAnnulation() {
|
||||
return dateAnnulation;
|
||||
}
|
||||
|
||||
public void setDateAnnulation(LocalDateTime dateAnnulation) {
|
||||
this.dateAnnulation = dateAnnulation;
|
||||
}
|
||||
|
||||
public String getRaisonAnnulation() {
|
||||
return raisonAnnulation;
|
||||
}
|
||||
|
||||
public void setRaisonAnnulation(String raisonAnnulation) {
|
||||
this.raisonAnnulation = raisonAnnulation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un numéro de référence unique
|
||||
*
|
||||
* @return Le numéro de référence généré
|
||||
*/
|
||||
private String genererNumeroReference() {
|
||||
return "ABO-"
|
||||
+ LocalDate.now().getYear()
|
||||
+ "-"
|
||||
+ String.format("%08d", (int) (Math.random() * 100000000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package dev.lions.unionflow.server.api.dto.admin;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour les logs d'audit
|
||||
* Représente une entrée dans le journal d'audit du système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AuditLogDTO extends BaseDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Type d'action effectuée */
|
||||
private String typeAction;
|
||||
|
||||
/** Sévérité de l'événement (SUCCESS, INFO, WARNING, ERROR, CRITICAL) */
|
||||
private String severite;
|
||||
|
||||
/** Nom de l'utilisateur qui a effectué l'action */
|
||||
private String utilisateur;
|
||||
|
||||
/** Rôle de l'utilisateur */
|
||||
private String role;
|
||||
|
||||
/** Module concerné (AUTH, MEMBRES, COTISATIONS, EVENTS, etc.) */
|
||||
private String module;
|
||||
|
||||
/** Description de l'action */
|
||||
private String description;
|
||||
|
||||
/** Détails supplémentaires */
|
||||
private String details;
|
||||
|
||||
/** Adresse IP de l'utilisateur */
|
||||
private String ipAddress;
|
||||
|
||||
/** User-Agent du navigateur */
|
||||
private String userAgent;
|
||||
|
||||
/** ID de session */
|
||||
private String sessionId;
|
||||
|
||||
/** Date et heure de l'événement */
|
||||
private LocalDateTime dateHeure;
|
||||
|
||||
/** Données avant modification (pour les actions de modification) */
|
||||
private String donneesAvant;
|
||||
|
||||
/** Données après modification (pour les actions de modification) */
|
||||
private String donneesApres;
|
||||
|
||||
/** ID de l'entité concernée (membre, cotisation, etc.) */
|
||||
private String entiteId;
|
||||
|
||||
/** Type d'entité concernée */
|
||||
private String entiteType;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package dev.lions.unionflow.server.api.dto.adresse;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.adresse.TypeAdresse;
|
||||
import jakarta.validation.constraints.DecimalMax;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des adresses
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AdresseDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Type d'adresse */
|
||||
@NotNull(message = "Le type d'adresse est obligatoire")
|
||||
private TypeAdresse typeAdresse;
|
||||
|
||||
/** Adresse complète */
|
||||
private String adresse;
|
||||
|
||||
/** Complément d'adresse */
|
||||
private String complementAdresse;
|
||||
|
||||
/** Code postal */
|
||||
private String codePostal;
|
||||
|
||||
/** Ville */
|
||||
private String ville;
|
||||
|
||||
/** Région */
|
||||
private String region;
|
||||
|
||||
/** Pays */
|
||||
private String pays;
|
||||
|
||||
/** Latitude */
|
||||
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@Digits(integer = 3, fraction = 6)
|
||||
private BigDecimal latitude;
|
||||
|
||||
/** Longitude */
|
||||
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@Digits(integer = 3, fraction = 6)
|
||||
private BigDecimal longitude;
|
||||
|
||||
/** Adresse principale */
|
||||
private Boolean principale;
|
||||
|
||||
/** Libellé personnalisé */
|
||||
private String libelle;
|
||||
|
||||
/** Notes et commentaires */
|
||||
private String notes;
|
||||
|
||||
/** ID de l'organisation (si adresse d'organisation) */
|
||||
private UUID organisationId;
|
||||
|
||||
/** ID du membre (si adresse de membre) */
|
||||
private UUID membreId;
|
||||
|
||||
/** ID de l'événement (si adresse d'événement) */
|
||||
private UUID evenementId;
|
||||
|
||||
/** Adresse complète formatée (calculée) */
|
||||
private String adresseComplete;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
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 AnalyticsDataDTO 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 AnalyticsDataDTO(
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
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 DashboardWidgetDTO 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
|
||||
*/
|
||||
public boolean isTempsReel() {
|
||||
return frequenceMiseAJourSecondes != null && frequenceMiseAJourSecondes <= 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"
|
||||
*/
|
||||
public String getStatutWidget() {
|
||||
if (hasErreursRecentes()) return "erreur";
|
||||
if (!visible) return "inactif";
|
||||
if (tauxErreur != null && tauxErreur > 10.0) return "maintenance";
|
||||
return "actif";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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 KPITrendDTO 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()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
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
|
||||
*/
|
||||
public boolean isConfidentiel() {
|
||||
return niveauConfidentialite != null && niveauConfidentialite >= 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
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.id = UUID.randomUUID();
|
||||
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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package dev.lions.unionflow.server.api.dto.comptabilite;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des comptes comptables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class CompteComptableDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de compte */
|
||||
@NotBlank(message = "Le numéro de compte est obligatoire")
|
||||
private String numeroCompte;
|
||||
|
||||
/** Libellé du compte */
|
||||
@NotBlank(message = "Le libellé est obligatoire")
|
||||
private String libelle;
|
||||
|
||||
/** Type de compte */
|
||||
@NotNull(message = "Le type de compte est obligatoire")
|
||||
private TypeCompteComptable typeCompte;
|
||||
|
||||
/** Classe comptable (1-7) */
|
||||
@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")
|
||||
private Integer classeComptable;
|
||||
|
||||
/** Solde initial */
|
||||
private BigDecimal soldeInitial;
|
||||
|
||||
/** Solde actuel */
|
||||
private BigDecimal soldeActuel;
|
||||
|
||||
/** Compte collectif */
|
||||
private Boolean compteCollectif;
|
||||
|
||||
/** Compte analytique */
|
||||
private Boolean compteAnalytique;
|
||||
|
||||
/** Description */
|
||||
private String description;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package dev.lions.unionflow.server.api.dto.comptabilite;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des écritures comptables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class EcritureComptableDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de pièce */
|
||||
@NotBlank(message = "Le numéro de pièce est obligatoire")
|
||||
private String numeroPiece;
|
||||
|
||||
/** Date de l'écriture */
|
||||
@NotNull(message = "La date de l'écriture est obligatoire")
|
||||
private LocalDate dateEcriture;
|
||||
|
||||
/** Libellé de l'écriture */
|
||||
@NotBlank(message = "Le libellé est obligatoire")
|
||||
private String libelle;
|
||||
|
||||
/** Référence externe */
|
||||
private String reference;
|
||||
|
||||
/** Lettrage */
|
||||
private String lettrage;
|
||||
|
||||
/** Pointage */
|
||||
private Boolean pointe;
|
||||
|
||||
/** Montant total débit */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montantDebit;
|
||||
|
||||
/** Montant total crédit */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montantCredit;
|
||||
|
||||
/** Commentaires */
|
||||
private String commentaire;
|
||||
|
||||
/** ID du journal */
|
||||
@NotNull(message = "Le journal est obligatoire")
|
||||
private UUID journalId;
|
||||
|
||||
/** ID de l'organisation */
|
||||
private UUID organisationId;
|
||||
|
||||
/** ID du paiement */
|
||||
private UUID paiementId;
|
||||
|
||||
/** Lignes d'écriture */
|
||||
private List<LigneEcritureDTO> lignes;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package dev.lions.unionflow.server.api.dto.comptabilite;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des journaux comptables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class JournalComptableDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Code unique du journal */
|
||||
@NotBlank(message = "Le code du journal est obligatoire")
|
||||
private String code;
|
||||
|
||||
/** Libellé du journal */
|
||||
@NotBlank(message = "Le libellé est obligatoire")
|
||||
private String libelle;
|
||||
|
||||
/** Type de journal */
|
||||
@NotNull(message = "Le type de journal est obligatoire")
|
||||
private TypeJournalComptable typeJournal;
|
||||
|
||||
/** Date de début de la période */
|
||||
private LocalDate dateDebut;
|
||||
|
||||
/** Date de fin de la période */
|
||||
private LocalDate dateFin;
|
||||
|
||||
/** Statut du journal */
|
||||
private String statut;
|
||||
|
||||
/** Description */
|
||||
private String description;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package dev.lions.unionflow.server.api.dto.comptabilite;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des lignes d'écriture
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class LigneEcritureDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de ligne */
|
||||
@NotNull(message = "Le numéro de ligne est obligatoire")
|
||||
@Min(value = 1, message = "Le numéro de ligne doit être positif")
|
||||
private Integer numeroLigne;
|
||||
|
||||
/** Montant débit */
|
||||
@DecimalMin(value = "0.0", message = "Le montant débit doit être positif ou nul")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montantDebit;
|
||||
|
||||
/** Montant crédit */
|
||||
@DecimalMin(value = "0.0", message = "Le montant crédit doit être positif ou nul")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montantCredit;
|
||||
|
||||
/** Libellé de la ligne */
|
||||
private String libelle;
|
||||
|
||||
/** Référence */
|
||||
private String reference;
|
||||
|
||||
/** ID de l'écriture */
|
||||
@NotNull(message = "L'écriture est obligatoire")
|
||||
private UUID ecritureId;
|
||||
|
||||
/** ID du compte comptable */
|
||||
@NotNull(message = "Le compte comptable est obligatoire")
|
||||
private UUID compteComptableId;
|
||||
}
|
||||
|
||||
@@ -0,0 +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 DashboardDataDTO {
|
||||
|
||||
@JsonProperty("stats")
|
||||
private DashboardStatsDTO stats;
|
||||
|
||||
@JsonProperty("recentActivities")
|
||||
private List<RecentActivityDTO> recentActivities;
|
||||
|
||||
@JsonProperty("upcomingEvents")
|
||||
private List<UpcomingEventDTO> 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* DTO pour les statistiques du dashboard
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DashboardStatsDTO {
|
||||
|
||||
@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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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 RecentActivityDTO {
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
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 UpcomingEventDTO {
|
||||
|
||||
@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() {
|
||||
if (startDate == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long days = ChronoUnit.DAYS.between(now, startDate);
|
||||
long hours = ChronoUnit.HOURS.between(now, startDate);
|
||||
|
||||
if (days == 0) {
|
||||
if (hours < 0) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package dev.lions.unionflow.server.api.dto.document;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des documents
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DocumentDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Nom du fichier */
|
||||
@NotBlank(message = "Le nom du fichier est obligatoire")
|
||||
private String nomFichier;
|
||||
|
||||
/** Nom original */
|
||||
private String nomOriginal;
|
||||
|
||||
/** Chemin de stockage */
|
||||
@NotBlank(message = "Le chemin de stockage est obligatoire")
|
||||
private String cheminStockage;
|
||||
|
||||
/** Type MIME */
|
||||
private String typeMime;
|
||||
|
||||
/** Taille en octets */
|
||||
@NotNull(message = "La taille est obligatoire")
|
||||
@Min(value = 0, message = "La taille doit être positive")
|
||||
private Long tailleOctets;
|
||||
|
||||
/** Type de document */
|
||||
private TypeDocument typeDocument;
|
||||
|
||||
/** Hash MD5 */
|
||||
private String hashMd5;
|
||||
|
||||
/** Hash SHA256 */
|
||||
private String hashSha256;
|
||||
|
||||
/** Description */
|
||||
private String description;
|
||||
|
||||
/** Nombre de téléchargements */
|
||||
private Integer nombreTelechargements;
|
||||
|
||||
/** Taille formatée (calculée) */
|
||||
private String tailleFormatee;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package dev.lions.unionflow.server.api.dto.document;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des pièces jointes
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class PieceJointeDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Ordre d'affichage */
|
||||
@NotNull(message = "L'ordre est obligatoire")
|
||||
@Min(value = 1, message = "L'ordre doit être positif")
|
||||
private Integer ordre;
|
||||
|
||||
/** Libellé */
|
||||
private String libelle;
|
||||
|
||||
/** Commentaire */
|
||||
private String commentaire;
|
||||
|
||||
/** ID du document */
|
||||
@NotNull(message = "Le document est obligatoire")
|
||||
private UUID documentId;
|
||||
|
||||
/** ID du membre (optionnel) */
|
||||
private UUID membreId;
|
||||
|
||||
/** ID de l'organisation (optionnel) */
|
||||
private UUID organisationId;
|
||||
|
||||
/** ID de la cotisation (optionnel) */
|
||||
private UUID cotisationId;
|
||||
|
||||
/** ID de l'adhésion (optionnel) */
|
||||
private UUID adhesionId;
|
||||
|
||||
/** ID de la demande d'aide (optionnel) */
|
||||
private UUID demandeAideId;
|
||||
|
||||
/** ID de la transaction Wave (optionnel) */
|
||||
private UUID transactionWaveId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,793 @@
|
||||
package dev.lions.unionflow.server.api.dto.evenement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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.Future;
|
||||
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.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des événements dans l'API UnionFlow Représente un événement organisé par une
|
||||
* association
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class EvenementDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Titre de l'événement */
|
||||
@NotBlank(message = "Le titre" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Size(
|
||||
min = ValidationConstants.TITRE_MIN_LENGTH,
|
||||
max = ValidationConstants.TITRE_MAX_LENGTH,
|
||||
message = ValidationConstants.TITRE_SIZE_MESSAGE)
|
||||
private String titre;
|
||||
|
||||
/** Description détaillée de l'événement */
|
||||
@Size(
|
||||
max = ValidationConstants.DESCRIPTION_COURTE_MAX_LENGTH,
|
||||
message = ValidationConstants.DESCRIPTION_COURTE_SIZE_MESSAGE)
|
||||
private String description;
|
||||
|
||||
/** Type d'événement */
|
||||
@NotNull(message = "Le type d'événement est obligatoire")
|
||||
private TypeEvenementMetier typeEvenement;
|
||||
|
||||
/** Statut de l'événement */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
private StatutEvenement statut;
|
||||
|
||||
/** Priorité de l'événement */
|
||||
private PrioriteEvenement priorite;
|
||||
|
||||
/** Date de début de l'événement */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@NotNull(message = "La date de début est obligatoire")
|
||||
@Future(message = "La date de début doit être dans le futur")
|
||||
private LocalDate dateDebut;
|
||||
|
||||
/** Date de fin de l'événement */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateFin;
|
||||
|
||||
/** Heure de début */
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime heureDebut;
|
||||
|
||||
/** Heure de fin */
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime heureFin;
|
||||
|
||||
/** Lieu de l'événement */
|
||||
@NotBlank(message = "Le lieu est obligatoire")
|
||||
@Size(max = 100, message = "Le lieu ne peut pas dépasser 100 caractères")
|
||||
private String lieu;
|
||||
|
||||
/** Adresse complète du lieu */
|
||||
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
|
||||
private String adresse;
|
||||
|
||||
/** Ville */
|
||||
@Size(max = 50, message = "La ville ne peut pas dépasser 50 caractères")
|
||||
private String ville;
|
||||
|
||||
/** Région */
|
||||
@Size(max = 50, message = "La région ne peut pas dépasser 50 caractères")
|
||||
private String region;
|
||||
|
||||
/** Coordonnées GPS (latitude) */
|
||||
@DecimalMin(value = "-90.0", message = "La latitude doit être entre -90 et 90")
|
||||
@DecimalMax(value = "90.0", message = "La latitude doit être entre -90 et 90")
|
||||
private BigDecimal latitude;
|
||||
|
||||
/** Coordonnées GPS (longitude) */
|
||||
@DecimalMin(value = "-180.0", message = "La longitude doit être entre -180 et 180")
|
||||
@DecimalMax(value = "180.0", message = "La longitude doit être entre -180 et 180")
|
||||
private BigDecimal longitude;
|
||||
|
||||
/** Identifiant de l'association organisatrice */
|
||||
@NotNull(message = "L'association organisatrice est obligatoire")
|
||||
private UUID associationId;
|
||||
|
||||
/** Nom de l'association organisatrice (lecture seule) */
|
||||
private String nomAssociation;
|
||||
|
||||
/** Nom de l'organisateur principal */
|
||||
@Size(max = 100, message = "Le nom de l'organisateur ne peut pas dépasser 100 caractères")
|
||||
private String organisateur;
|
||||
|
||||
/** Email de l'organisateur */
|
||||
@Email(message = "Format d'email invalide")
|
||||
@Size(max = 100, message = "L'email ne peut pas dépasser 100 caractères")
|
||||
private String emailOrganisateur;
|
||||
|
||||
/** Téléphone de l'organisateur */
|
||||
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$", message = "Format de téléphone invalide")
|
||||
private String telephoneOrganisateur;
|
||||
|
||||
/** Capacité maximale de participants */
|
||||
@Min(value = 1, message = "La capacité doit être d'au moins 1 personne")
|
||||
@Max(value = 10000, message = "La capacité ne peut pas dépasser 10000 personnes")
|
||||
private Integer capaciteMax;
|
||||
|
||||
/** Nombre de participants inscrits */
|
||||
@Min(value = 0, message = "Le nombre de participants ne peut pas être négatif")
|
||||
private Integer participantsInscrits;
|
||||
|
||||
/** Nombre de participants présents */
|
||||
@Min(value = 0, message = "Le nombre de participants présents ne peut pas être négatif")
|
||||
private Integer participantsPresents;
|
||||
|
||||
/** Budget prévu pour l'événement */
|
||||
@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)
|
||||
private BigDecimal budget;
|
||||
|
||||
/** Coût réel de l'événement */
|
||||
@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)
|
||||
private BigDecimal coutReel;
|
||||
|
||||
/** Code de la devise */
|
||||
@Pattern(
|
||||
regexp = ValidationConstants.DEVISE_PATTERN,
|
||||
message = ValidationConstants.DEVISE_MESSAGE)
|
||||
private String codeDevise;
|
||||
|
||||
/** Indique si l'inscription est obligatoire */
|
||||
private Boolean inscriptionObligatoire = false;
|
||||
|
||||
/** Date limite d'inscription */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateLimiteInscription;
|
||||
|
||||
/** Indique si l'événement est public */
|
||||
private Boolean evenementPublic = true;
|
||||
|
||||
/** Indique si l'événement est récurrent */
|
||||
private Boolean recurrent = false;
|
||||
|
||||
/** Fréquence de récurrence (si récurrent) */
|
||||
@Pattern(
|
||||
regexp = "^(HEBDOMADAIRE|MENSUELLE|TRIMESTRIELLE|ANNUELLE)$",
|
||||
message = "Fréquence de récurrence invalide")
|
||||
private String frequenceRecurrence;
|
||||
|
||||
/** Instructions spéciales pour les participants */
|
||||
@Size(max = 500, message = "Les instructions ne peuvent pas dépasser 500 caractères")
|
||||
private String instructions;
|
||||
|
||||
/** Matériel nécessaire */
|
||||
@Size(max = 500, message = "La liste du matériel ne peut pas dépasser 500 caractères")
|
||||
private String materielNecessaire;
|
||||
|
||||
/** Conditions météorologiques requises */
|
||||
@Size(max = 100, message = "Les conditions météo ne peuvent pas dépasser 100 caractères")
|
||||
private String conditionsMeteo;
|
||||
|
||||
/** URL de l'image de l'événement */
|
||||
@Size(max = 255, message = "L'URL de l'image ne peut pas dépasser 255 caractères")
|
||||
private String imageUrl;
|
||||
|
||||
/** Couleur thème de l'événement */
|
||||
@Pattern(regexp = "^#[0-9A-Fa-f]{6}$", message = "Format de couleur invalide (format: #RRGGBB)")
|
||||
private String couleurTheme;
|
||||
|
||||
/** Date d'annulation (si annulé) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateAnnulation;
|
||||
|
||||
/** Raison de l'annulation */
|
||||
@Size(max = 500, message = "La raison d'annulation ne peut pas dépasser 500 caractères")
|
||||
private String raisonAnnulation;
|
||||
|
||||
/** Identifiant de l'utilisateur qui a annulé */
|
||||
private Long annulePar;
|
||||
|
||||
/** Nom de l'utilisateur qui a annulé */
|
||||
private String nomAnnulateur;
|
||||
|
||||
// Constructeurs
|
||||
public EvenementDTO() {
|
||||
super();
|
||||
this.statut = StatutEvenement.PLANIFIE;
|
||||
this.priorite = PrioriteEvenement.NORMALE;
|
||||
this.participantsInscrits = 0;
|
||||
this.participantsPresents = 0;
|
||||
this.inscriptionObligatoire = false;
|
||||
this.evenementPublic = true;
|
||||
this.recurrent = false;
|
||||
this.codeDevise = "XOF"; // Franc CFA par défaut
|
||||
}
|
||||
|
||||
public EvenementDTO(
|
||||
String titre, TypeEvenementMetier typeEvenement, LocalDate dateDebut, String lieu) {
|
||||
this();
|
||||
this.titre = titre;
|
||||
this.typeEvenement = typeEvenement;
|
||||
this.dateDebut = dateDebut;
|
||||
this.lieu = lieu;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getTitre() {
|
||||
return titre;
|
||||
}
|
||||
|
||||
public void setTitre(String titre) {
|
||||
this.titre = titre;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public TypeEvenementMetier getTypeEvenement() {
|
||||
return typeEvenement;
|
||||
}
|
||||
|
||||
public void setTypeEvenement(TypeEvenementMetier typeEvenement) {
|
||||
this.typeEvenement = typeEvenement;
|
||||
}
|
||||
|
||||
public StatutEvenement getStatut() {
|
||||
return statut;
|
||||
}
|
||||
|
||||
public void setStatut(StatutEvenement statut) {
|
||||
this.statut = statut;
|
||||
}
|
||||
|
||||
public PrioriteEvenement getPriorite() {
|
||||
return priorite;
|
||||
}
|
||||
|
||||
public void setPriorite(PrioriteEvenement priorite) {
|
||||
this.priorite = priorite;
|
||||
}
|
||||
|
||||
public LocalDate getDateDebut() {
|
||||
return dateDebut;
|
||||
}
|
||||
|
||||
public void setDateDebut(LocalDate dateDebut) {
|
||||
this.dateDebut = dateDebut;
|
||||
}
|
||||
|
||||
public LocalDate getDateFin() {
|
||||
return dateFin;
|
||||
}
|
||||
|
||||
public void setDateFin(LocalDate dateFin) {
|
||||
this.dateFin = dateFin;
|
||||
}
|
||||
|
||||
public LocalTime getHeureDebut() {
|
||||
return heureDebut;
|
||||
}
|
||||
|
||||
public void setHeureDebut(LocalTime heureDebut) {
|
||||
this.heureDebut = heureDebut;
|
||||
}
|
||||
|
||||
public LocalTime getHeureFin() {
|
||||
return heureFin;
|
||||
}
|
||||
|
||||
public void setHeureFin(LocalTime heureFin) {
|
||||
this.heureFin = heureFin;
|
||||
}
|
||||
|
||||
public String getLieu() {
|
||||
return lieu;
|
||||
}
|
||||
|
||||
public void setLieu(String lieu) {
|
||||
this.lieu = lieu;
|
||||
}
|
||||
|
||||
public String getAdresse() {
|
||||
return adresse;
|
||||
}
|
||||
|
||||
public void setAdresse(String adresse) {
|
||||
this.adresse = adresse;
|
||||
}
|
||||
|
||||
public String getVille() {
|
||||
return ville;
|
||||
}
|
||||
|
||||
public void setVille(String ville) {
|
||||
this.ville = ville;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public BigDecimal getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public void setLatitude(BigDecimal latitude) {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public void setLongitude(BigDecimal longitude) {
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public UUID getAssociationId() {
|
||||
return associationId;
|
||||
}
|
||||
|
||||
public void setAssociationId(UUID associationId) {
|
||||
this.associationId = associationId;
|
||||
}
|
||||
|
||||
public String getNomAssociation() {
|
||||
return nomAssociation;
|
||||
}
|
||||
|
||||
public void setNomAssociation(String nomAssociation) {
|
||||
this.nomAssociation = nomAssociation;
|
||||
}
|
||||
|
||||
public String getOrganisateur() {
|
||||
return organisateur;
|
||||
}
|
||||
|
||||
public void setOrganisateur(String organisateur) {
|
||||
this.organisateur = organisateur;
|
||||
}
|
||||
|
||||
public String getEmailOrganisateur() {
|
||||
return emailOrganisateur;
|
||||
}
|
||||
|
||||
public void setEmailOrganisateur(String emailOrganisateur) {
|
||||
this.emailOrganisateur = emailOrganisateur;
|
||||
}
|
||||
|
||||
public String getTelephoneOrganisateur() {
|
||||
return telephoneOrganisateur;
|
||||
}
|
||||
|
||||
public void setTelephoneOrganisateur(String telephoneOrganisateur) {
|
||||
this.telephoneOrganisateur = telephoneOrganisateur;
|
||||
}
|
||||
|
||||
public Integer getCapaciteMax() {
|
||||
return capaciteMax;
|
||||
}
|
||||
|
||||
public void setCapaciteMax(Integer capaciteMax) {
|
||||
this.capaciteMax = capaciteMax;
|
||||
}
|
||||
|
||||
public Integer getParticipantsInscrits() {
|
||||
return participantsInscrits;
|
||||
}
|
||||
|
||||
public void setParticipantsInscrits(Integer participantsInscrits) {
|
||||
this.participantsInscrits = participantsInscrits;
|
||||
}
|
||||
|
||||
public Integer getParticipantsPresents() {
|
||||
return participantsPresents;
|
||||
}
|
||||
|
||||
public void setParticipantsPresents(Integer participantsPresents) {
|
||||
this.participantsPresents = participantsPresents;
|
||||
}
|
||||
|
||||
public BigDecimal getBudget() {
|
||||
return budget;
|
||||
}
|
||||
|
||||
public void setBudget(BigDecimal budget) {
|
||||
this.budget = budget;
|
||||
}
|
||||
|
||||
public BigDecimal getCoutReel() {
|
||||
return coutReel;
|
||||
}
|
||||
|
||||
public void setCoutReel(BigDecimal coutReel) {
|
||||
this.coutReel = coutReel;
|
||||
}
|
||||
|
||||
public String getCodeDevise() {
|
||||
return codeDevise;
|
||||
}
|
||||
|
||||
public void setCodeDevise(String codeDevise) {
|
||||
this.codeDevise = codeDevise;
|
||||
}
|
||||
|
||||
public Boolean getInscriptionObligatoire() {
|
||||
return inscriptionObligatoire;
|
||||
}
|
||||
|
||||
public void setInscriptionObligatoire(Boolean inscriptionObligatoire) {
|
||||
this.inscriptionObligatoire = inscriptionObligatoire;
|
||||
}
|
||||
|
||||
public LocalDate getDateLimiteInscription() {
|
||||
return dateLimiteInscription;
|
||||
}
|
||||
|
||||
public void setDateLimiteInscription(LocalDate dateLimiteInscription) {
|
||||
this.dateLimiteInscription = dateLimiteInscription;
|
||||
}
|
||||
|
||||
public Boolean getEvenementPublic() {
|
||||
return evenementPublic;
|
||||
}
|
||||
|
||||
public void setEvenementPublic(Boolean evenementPublic) {
|
||||
this.evenementPublic = evenementPublic;
|
||||
}
|
||||
|
||||
public Boolean getRecurrent() {
|
||||
return recurrent;
|
||||
}
|
||||
|
||||
public void setRecurrent(Boolean recurrent) {
|
||||
this.recurrent = recurrent;
|
||||
}
|
||||
|
||||
public String getFrequenceRecurrence() {
|
||||
return frequenceRecurrence;
|
||||
}
|
||||
|
||||
public void setFrequenceRecurrence(String frequenceRecurrence) {
|
||||
this.frequenceRecurrence = frequenceRecurrence;
|
||||
}
|
||||
|
||||
public String getInstructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public void setInstructions(String instructions) {
|
||||
this.instructions = instructions;
|
||||
}
|
||||
|
||||
public String getMaterielNecessaire() {
|
||||
return materielNecessaire;
|
||||
}
|
||||
|
||||
public void setMaterielNecessaire(String materielNecessaire) {
|
||||
this.materielNecessaire = materielNecessaire;
|
||||
}
|
||||
|
||||
public String getConditionsMeteo() {
|
||||
return conditionsMeteo;
|
||||
}
|
||||
|
||||
public void setConditionsMeteo(String conditionsMeteo) {
|
||||
this.conditionsMeteo = conditionsMeteo;
|
||||
}
|
||||
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
public void setImageUrl(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
public String getCouleurTheme() {
|
||||
return couleurTheme;
|
||||
}
|
||||
|
||||
public void setCouleurTheme(String couleurTheme) {
|
||||
this.couleurTheme = couleurTheme;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateAnnulation() {
|
||||
return dateAnnulation;
|
||||
}
|
||||
|
||||
public void setDateAnnulation(LocalDateTime dateAnnulation) {
|
||||
this.dateAnnulation = dateAnnulation;
|
||||
}
|
||||
|
||||
public String getRaisonAnnulation() {
|
||||
return raisonAnnulation;
|
||||
}
|
||||
|
||||
public void setRaisonAnnulation(String raisonAnnulation) {
|
||||
this.raisonAnnulation = raisonAnnulation;
|
||||
}
|
||||
|
||||
public Long getAnnulePar() {
|
||||
return annulePar;
|
||||
}
|
||||
|
||||
public void setAnnulePar(Long annulePar) {
|
||||
this.annulePar = annulePar;
|
||||
}
|
||||
|
||||
public String getNomAnnulateur() {
|
||||
return nomAnnulateur;
|
||||
}
|
||||
|
||||
public void setNomAnnulateur(String nomAnnulateur) {
|
||||
this.nomAnnulateur = nomAnnulateur;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Vérifie si l'événement est en cours
|
||||
*
|
||||
* @return true si l'événement est actuellement en cours
|
||||
*/
|
||||
public boolean estEnCours() {
|
||||
return StatutEvenement.EN_COURS.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'événement est terminé
|
||||
*
|
||||
* @return true si l'événement est terminé
|
||||
*/
|
||||
public boolean estTermine() {
|
||||
return StatutEvenement.TERMINE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'événement est annulé
|
||||
*
|
||||
* @return true si l'événement est annulé
|
||||
*/
|
||||
public boolean estAnnule() {
|
||||
return StatutEvenement.ANNULE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'événement est complet (capacité atteinte)
|
||||
*
|
||||
* @return true si le nombre d'inscrits atteint la capacité maximale
|
||||
*/
|
||||
public boolean estComplet() {
|
||||
return capaciteMax != null
|
||||
&& participantsInscrits != null
|
||||
&& participantsInscrits >= capaciteMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le nombre de places disponibles
|
||||
*
|
||||
* @return Le nombre de places restantes
|
||||
*/
|
||||
public int getPlacesDisponibles() {
|
||||
if (capaciteMax == null || participantsInscrits == null) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(0, capaciteMax - participantsInscrits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le taux de remplissage en pourcentage
|
||||
*
|
||||
* @return Le pourcentage de remplissage (0-100)
|
||||
*/
|
||||
public int getTauxRemplissage() {
|
||||
if (capaciteMax == null || capaciteMax == 0 || participantsInscrits == null) {
|
||||
return 0;
|
||||
}
|
||||
return (participantsInscrits * 100) / capaciteMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le taux de présence en pourcentage
|
||||
*
|
||||
* @return Le pourcentage de présence (0-100)
|
||||
*/
|
||||
public int getTauxPresence() {
|
||||
if (participantsInscrits == null || participantsInscrits == 0 || participantsPresents == null) {
|
||||
return 0;
|
||||
}
|
||||
return (participantsPresents * 100) / participantsInscrits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si les inscriptions sont encore ouvertes
|
||||
*
|
||||
* @return true si les inscriptions sont ouvertes
|
||||
*/
|
||||
public boolean sontInscriptionsOuvertes() {
|
||||
if (estAnnule() || estTermine()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dateLimiteInscription != null && LocalDate.now().isAfter(dateLimiteInscription)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !estComplet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la durée de l'événement en heures
|
||||
*
|
||||
* @return La durée en heures, ou 0 si non calculable
|
||||
*/
|
||||
public long getDureeEnHeures() {
|
||||
if (heureDebut == null || heureFin == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return heureDebut.until(heureFin, java.time.temporal.ChronoUnit.HOURS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'événement dure plusieurs jours
|
||||
*
|
||||
* @return true si l'événement s'étend sur plusieurs jours
|
||||
*/
|
||||
public boolean estEvenementMultiJours() {
|
||||
return dateFin != null && !dateDebut.equals(dateFin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé du type d'événement
|
||||
*
|
||||
* @return Le libellé du type
|
||||
*/
|
||||
public String getTypeEvenementLibelle() {
|
||||
return typeEvenement != null ? typeEvenement.getLibelle() : "Non défini";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé du statut
|
||||
*
|
||||
* @return Le libellé du statut
|
||||
*/
|
||||
public String getStatutLibelle() {
|
||||
return statut != null ? statut.getLibelle() : "Non défini";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé de la priorité
|
||||
*
|
||||
* @return Le libellé de la priorité
|
||||
*/
|
||||
public String getPrioriteLibelle() {
|
||||
return priorite != null ? priorite.getLibelle() : "Normale";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'adresse complète du lieu
|
||||
*
|
||||
* @return L'adresse complète formatée
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'événement a des coordonnées GPS
|
||||
*
|
||||
* @return true si latitude et longitude sont définies
|
||||
*/
|
||||
public boolean hasCoordonnees() {
|
||||
return latitude != null && longitude != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'écart budgétaire
|
||||
*
|
||||
* @return La différence entre budget et coût réel (positif = économie, négatif = dépassement)
|
||||
*/
|
||||
public BigDecimal getEcartBudgetaire() {
|
||||
if (budget == null || coutReel == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return budget.subtract(coutReel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le budget a été dépassé
|
||||
*
|
||||
* @return true si le coût réel dépasse le budget
|
||||
*/
|
||||
public boolean estBudgetDepasse() {
|
||||
return getEcartBudgetaire().compareTo(BigDecimal.ZERO) < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EvenementDTO{"
|
||||
+ "titre='"
|
||||
+ titre
|
||||
+ '\''
|
||||
+ ", typeEvenement='"
|
||||
+ typeEvenement
|
||||
+ '\''
|
||||
+ ", statut='"
|
||||
+ statut
|
||||
+ '\''
|
||||
+ ", dateDebut="
|
||||
+ dateDebut
|
||||
+ ", lieu='"
|
||||
+ lieu
|
||||
+ '\''
|
||||
+ ", participantsInscrits="
|
||||
+ participantsInscrits
|
||||
+ ", capaciteMax="
|
||||
+ capaciteMax
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package dev.lions.unionflow.server.api.dto.finance;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des adhésions dans l'API UnionFlow
|
||||
* Représente une demande d'adhésion d'un membre à une organisation
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AdhesionDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de référence unique de l'adhésion */
|
||||
@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")
|
||||
private String numeroReference;
|
||||
|
||||
/** Identifiant du membre */
|
||||
@NotNull(message = "L'identifiant du membre est obligatoire")
|
||||
private UUID membreId;
|
||||
|
||||
/** Numéro du membre (lecture seule) */
|
||||
private String numeroMembre;
|
||||
|
||||
/** Nom complet du membre (lecture seule) */
|
||||
private String nomMembre;
|
||||
|
||||
/** Email du membre (lecture seule) */
|
||||
private String emailMembre;
|
||||
|
||||
/** Identifiant de l'organisation */
|
||||
@NotNull(message = "L'identifiant de l'organisation est obligatoire")
|
||||
private UUID organisationId;
|
||||
|
||||
/** Nom de l'organisation (lecture seule) */
|
||||
private String nomOrganisation;
|
||||
|
||||
/** Date de la demande d'adhésion */
|
||||
@NotNull(message = "La date de demande est obligatoire")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateDemande;
|
||||
|
||||
/** Frais d'adhésion */
|
||||
@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")
|
||||
private BigDecimal fraisAdhesion;
|
||||
|
||||
/** Montant payé */
|
||||
@DecimalMin(value = "0.0", message = "Le montant payé ne peut pas être négatif")
|
||||
@Digits(integer = 10, fraction = 2, message = "Format de montant invalide")
|
||||
private BigDecimal montantPaye;
|
||||
|
||||
/** Code devise (ISO 4217) */
|
||||
@NotBlank(message = "Le code devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres")
|
||||
private String codeDevise;
|
||||
|
||||
/** Statut de l'adhésion */
|
||||
@NotBlank(message = "Le statut est obligatoire")
|
||||
@Pattern(
|
||||
regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE|EN_PAIEMENT|PAYEE)$",
|
||||
message = "Statut invalide")
|
||||
private String statut;
|
||||
|
||||
/** Date d'approbation */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateApprobation;
|
||||
|
||||
/** Date de paiement */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime datePaiement;
|
||||
|
||||
/** Méthode de paiement */
|
||||
@Pattern(
|
||||
regexp = "^(ESPECES|VIREMENT|CHEQUE|WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|CARTE_BANCAIRE)$",
|
||||
message = "Méthode de paiement invalide")
|
||||
private String methodePaiement;
|
||||
|
||||
/** Référence de paiement */
|
||||
@Size(max = 100, message = "La référence de paiement ne peut pas dépasser 100 caractères")
|
||||
private String referencePaiement;
|
||||
|
||||
/** Motif de rejet */
|
||||
@Size(max = 1000, message = "Le motif de rejet ne peut pas dépasser 1000 caractères")
|
||||
private String motifRejet;
|
||||
|
||||
/** Observations */
|
||||
@Size(max = 1000, message = "Les observations ne peuvent pas dépasser 1000 caractères")
|
||||
private String observations;
|
||||
|
||||
/** Utilisateur ayant approuvé l'adhésion */
|
||||
@Size(max = 255, message = "Le nom de l'approbateur ne peut pas dépasser 255 caractères")
|
||||
private String approuvePar;
|
||||
|
||||
/** Date de validation */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateValidation;
|
||||
|
||||
// Méthodes utilitaires pour l'affichage
|
||||
|
||||
/** Vérifie si l'adhésion est payée intégralement */
|
||||
public boolean isPayeeIntegralement() {
|
||||
return montantPaye != null
|
||||
&& fraisAdhesion != null
|
||||
&& montantPaye.compareTo(fraisAdhesion) >= 0;
|
||||
}
|
||||
|
||||
/** Vérifie si l'adhésion est en attente de paiement */
|
||||
public boolean isEnAttentePaiement() {
|
||||
return "APPROUVEE".equals(statut) && !isPayeeIntegralement();
|
||||
}
|
||||
|
||||
/** Calcule le montant restant à payer */
|
||||
public BigDecimal getMontantRestant() {
|
||||
if (fraisAdhesion == null) return BigDecimal.ZERO;
|
||||
if (montantPaye == null) return fraisAdhesion;
|
||||
BigDecimal restant = fraisAdhesion.subtract(montantPaye);
|
||||
return restant.compareTo(BigDecimal.ZERO) > 0 ? restant : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/** Calcule le pourcentage de paiement */
|
||||
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();
|
||||
}
|
||||
|
||||
/** Calcule le nombre de jours depuis la demande */
|
||||
public long getJoursDepuisDemande() {
|
||||
if (dateDemande == null) return 0;
|
||||
return ChronoUnit.DAYS.between(dateDemande, LocalDate.now());
|
||||
}
|
||||
|
||||
/** Retourne le libellé du statut */
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne la sévérité du statut pour PrimeFaces */
|
||||
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";
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne l'icône du statut pour PrimeFaces */
|
||||
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";
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne le libellé de la méthode de paiement */
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
/** Formate la date de demande */
|
||||
public String getDateDemandeFormatee() {
|
||||
if (dateDemande == null) return "";
|
||||
return dateDemande.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
|
||||
}
|
||||
|
||||
/** Formate la date d'approbation */
|
||||
public String getDateApprobationFormatee() {
|
||||
if (dateApprobation == null) return "";
|
||||
return dateApprobation.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
|
||||
}
|
||||
|
||||
/** Formate la date de paiement */
|
||||
public String getDatePaiementFormatee() {
|
||||
if (datePaiement == null) return "";
|
||||
return datePaiement.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
|
||||
}
|
||||
|
||||
/** Formate les frais d'adhésion */
|
||||
public String getFraisAdhesionFormatte() {
|
||||
if (fraisAdhesion == null) return "0 FCFA";
|
||||
return String.format("%,.0f FCFA", fraisAdhesion.doubleValue());
|
||||
}
|
||||
|
||||
/** Formate le montant payé */
|
||||
public String getMontantPayeFormatte() {
|
||||
if (montantPaye == null) return "0 FCFA";
|
||||
return String.format("%,.0f FCFA", montantPaye.doubleValue());
|
||||
}
|
||||
|
||||
/** Formate le montant restant */
|
||||
public String getMontantRestantFormatte() {
|
||||
return String.format("%,.0f FCFA", getMontantRestant().doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,574 @@
|
||||
package dev.lions.unionflow.server.api.dto.finance;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des cotisations dans l'API UnionFlow Représente une cotisation d'un membre à
|
||||
* son organisation
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class CotisationDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de référence unique de la cotisation */
|
||||
@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")
|
||||
private String numeroReference;
|
||||
|
||||
/** Identifiant du membre */
|
||||
@NotNull(message = "L'identifiant du membre est obligatoire")
|
||||
private UUID membreId;
|
||||
|
||||
/** Numéro du membre (lecture seule) */
|
||||
private String numeroMembre;
|
||||
|
||||
/** Nom complet du membre (lecture seule) */
|
||||
private String nomMembre;
|
||||
|
||||
/** Identifiant de l'association */
|
||||
@NotNull(message = "L'identifiant de l'association est obligatoire")
|
||||
private UUID associationId;
|
||||
|
||||
/** Nom de l'association (lecture seule) */
|
||||
private String nomAssociation;
|
||||
|
||||
/** Type de cotisation */
|
||||
@NotNull(message = "Le type de cotisation est obligatoire")
|
||||
@Pattern(
|
||||
regexp = "^(MENSUELLE|TRIMESTRIELLE|SEMESTRIELLE|ANNUELLE|EXCEPTIONNELLE|ADHESION)$",
|
||||
message = "Type de cotisation invalide")
|
||||
private String typeCotisation;
|
||||
|
||||
/** Libellé de la cotisation */
|
||||
@NotBlank(message = "Le libellé est obligatoire")
|
||||
@Size(max = 100, message = "Le libellé ne peut pas dépasser 100 caractères")
|
||||
private String libelle;
|
||||
|
||||
/** Description détaillée */
|
||||
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
|
||||
private String description;
|
||||
|
||||
/** Montant dû */
|
||||
@NotNull(message = "Le montant dû est obligatoire")
|
||||
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant dû doit être positif")
|
||||
@Digits(integer = 10, fraction = 2, message = "Format de montant invalide")
|
||||
private BigDecimal montantDu;
|
||||
|
||||
/** Montant payé */
|
||||
@DecimalMin(value = "0.0", message = "Le montant payé ne peut pas être négatif")
|
||||
@Digits(integer = 10, fraction = 2, message = "Format de montant invalide")
|
||||
private BigDecimal montantPaye;
|
||||
|
||||
/** Code de la devise */
|
||||
@NotBlank(message = "Le code devise est obligatoire")
|
||||
@Size(min = 3, max = 3, message = "Le code devise doit faire exactement 3 caractères")
|
||||
private String codeDevise;
|
||||
|
||||
/** Statut de la cotisation */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
@Pattern(
|
||||
regexp = "^(EN_ATTENTE|PAYEE|PARTIELLEMENT_PAYEE|EN_RETARD|ANNULEE|REMBOURSEE)$",
|
||||
message = "Statut invalide")
|
||||
private String statut;
|
||||
|
||||
/** Date d'échéance */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@NotNull(message = "La date d'échéance est obligatoire")
|
||||
private LocalDate dateEcheance;
|
||||
|
||||
/** Date de paiement */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime datePaiement;
|
||||
|
||||
/** Méthode de paiement */
|
||||
@Pattern(
|
||||
regexp = "^(ESPECES|VIREMENT|CHEQUE|WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|CARTE_BANCAIRE)$",
|
||||
message = "Méthode de paiement invalide")
|
||||
private String methodePaiement;
|
||||
|
||||
/** Référence du paiement (numéro de transaction, chèque, etc.) */
|
||||
@Size(max = 100, message = "La référence de paiement ne peut pas dépasser 100 caractères")
|
||||
private String referencePaiement;
|
||||
|
||||
/** Période concernée (ex: "Janvier 2025", "Q1 2025") */
|
||||
@Size(max = 50, message = "La période ne peut pas dépasser 50 caractères")
|
||||
private String periode;
|
||||
|
||||
/** Année de la cotisation */
|
||||
@Min(value = 2020, message = "L'année doit être supérieure à 2020")
|
||||
@Max(value = 2050, message = "L'année doit être inférieure à 2050")
|
||||
private Integer annee;
|
||||
|
||||
/** Mois de la cotisation (1-12) */
|
||||
@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 mois;
|
||||
|
||||
/** Observations ou commentaires */
|
||||
@Size(max = 500, message = "Les observations ne peuvent pas dépasser 500 caractères")
|
||||
private String observations;
|
||||
|
||||
/** Indique si la cotisation est récurrente */
|
||||
private Boolean recurrente = false;
|
||||
|
||||
/** Nombre de rappels envoyés */
|
||||
@Min(value = 0, message = "Le nombre de rappels ne peut pas être négatif")
|
||||
private Integer nombreRappels = 0;
|
||||
|
||||
/** Date du dernier rappel */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateDernierRappel;
|
||||
|
||||
/** Identifiant de l'utilisateur qui a validé le paiement */
|
||||
private UUID validePar;
|
||||
|
||||
/** Nom de l'utilisateur qui a validé le paiement */
|
||||
private String nomValidateur;
|
||||
|
||||
// Constructeurs
|
||||
public CotisationDTO() {
|
||||
super();
|
||||
this.montantPaye = BigDecimal.ZERO;
|
||||
this.codeDevise = "XOF"; // Franc CFA par défaut
|
||||
this.statut = "EN_ATTENTE";
|
||||
this.recurrente = false;
|
||||
this.nombreRappels = 0;
|
||||
this.annee = LocalDate.now().getYear();
|
||||
}
|
||||
|
||||
public CotisationDTO(
|
||||
UUID membreId, String typeCotisation, BigDecimal montantDu, LocalDate dateEcheance) {
|
||||
this();
|
||||
this.membreId = membreId;
|
||||
this.typeCotisation = typeCotisation;
|
||||
this.montantDu = montantDu;
|
||||
this.dateEcheance = dateEcheance;
|
||||
this.numeroReference = genererNumeroReference();
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getNumeroReference() {
|
||||
return numeroReference;
|
||||
}
|
||||
|
||||
public void setNumeroReference(String numeroReference) {
|
||||
this.numeroReference = numeroReference;
|
||||
}
|
||||
|
||||
public UUID getMembreId() {
|
||||
return membreId;
|
||||
}
|
||||
|
||||
public void setMembreId(UUID membreId) {
|
||||
this.membreId = membreId;
|
||||
}
|
||||
|
||||
public String getNumeroMembre() {
|
||||
return numeroMembre;
|
||||
}
|
||||
|
||||
public void setNumeroMembre(String numeroMembre) {
|
||||
this.numeroMembre = numeroMembre;
|
||||
}
|
||||
|
||||
public String getNomMembre() {
|
||||
return nomMembre;
|
||||
}
|
||||
|
||||
public void setNomMembre(String nomMembre) {
|
||||
this.nomMembre = nomMembre;
|
||||
}
|
||||
|
||||
public UUID getAssociationId() {
|
||||
return associationId;
|
||||
}
|
||||
|
||||
public void setAssociationId(UUID associationId) {
|
||||
this.associationId = associationId;
|
||||
}
|
||||
|
||||
public String getNomAssociation() {
|
||||
return nomAssociation;
|
||||
}
|
||||
|
||||
public void setNomAssociation(String nomAssociation) {
|
||||
this.nomAssociation = nomAssociation;
|
||||
}
|
||||
|
||||
public String getTypeCotisation() {
|
||||
return typeCotisation;
|
||||
}
|
||||
|
||||
public void setTypeCotisation(String typeCotisation) {
|
||||
this.typeCotisation = typeCotisation;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public void setLibelle(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public BigDecimal getMontantDu() {
|
||||
return montantDu;
|
||||
}
|
||||
|
||||
public void setMontantDu(BigDecimal montantDu) {
|
||||
this.montantDu = montantDu;
|
||||
}
|
||||
|
||||
public BigDecimal getMontantPaye() {
|
||||
return montantPaye;
|
||||
}
|
||||
|
||||
public void setMontantPaye(BigDecimal montantPaye) {
|
||||
this.montantPaye = montantPaye;
|
||||
}
|
||||
|
||||
public String getCodeDevise() {
|
||||
return codeDevise;
|
||||
}
|
||||
|
||||
public void setCodeDevise(String codeDevise) {
|
||||
this.codeDevise = codeDevise;
|
||||
}
|
||||
|
||||
public String getStatut() {
|
||||
return statut;
|
||||
}
|
||||
|
||||
public void setStatut(String statut) {
|
||||
this.statut = statut;
|
||||
}
|
||||
|
||||
public LocalDate getDateEcheance() {
|
||||
return dateEcheance;
|
||||
}
|
||||
|
||||
public void setDateEcheance(LocalDate dateEcheance) {
|
||||
this.dateEcheance = dateEcheance;
|
||||
}
|
||||
|
||||
public LocalDateTime getDatePaiement() {
|
||||
return datePaiement;
|
||||
}
|
||||
|
||||
public void setDatePaiement(LocalDateTime datePaiement) {
|
||||
this.datePaiement = datePaiement;
|
||||
}
|
||||
|
||||
public String getMethodePaiement() {
|
||||
return methodePaiement;
|
||||
}
|
||||
|
||||
public void setMethodePaiement(String methodePaiement) {
|
||||
this.methodePaiement = methodePaiement;
|
||||
}
|
||||
|
||||
public String getReferencePaiement() {
|
||||
return referencePaiement;
|
||||
}
|
||||
|
||||
public void setReferencePaiement(String referencePaiement) {
|
||||
this.referencePaiement = referencePaiement;
|
||||
}
|
||||
|
||||
public String getPeriode() {
|
||||
return periode;
|
||||
}
|
||||
|
||||
public void setPeriode(String periode) {
|
||||
this.periode = periode;
|
||||
}
|
||||
|
||||
public Integer getAnnee() {
|
||||
return annee;
|
||||
}
|
||||
|
||||
public void setAnnee(Integer annee) {
|
||||
this.annee = annee;
|
||||
}
|
||||
|
||||
public Integer getMois() {
|
||||
return mois;
|
||||
}
|
||||
|
||||
public void setMois(Integer mois) {
|
||||
this.mois = mois;
|
||||
}
|
||||
|
||||
public String getObservations() {
|
||||
return observations;
|
||||
}
|
||||
|
||||
public void setObservations(String observations) {
|
||||
this.observations = observations;
|
||||
}
|
||||
|
||||
public Boolean getRecurrente() {
|
||||
return recurrente;
|
||||
}
|
||||
|
||||
public void setRecurrente(Boolean recurrente) {
|
||||
this.recurrente = recurrente;
|
||||
}
|
||||
|
||||
public Integer getNombreRappels() {
|
||||
return nombreRappels;
|
||||
}
|
||||
|
||||
public void setNombreRappels(Integer nombreRappels) {
|
||||
this.nombreRappels = nombreRappels;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateDernierRappel() {
|
||||
return dateDernierRappel;
|
||||
}
|
||||
|
||||
public void setDateDernierRappel(LocalDateTime dateDernierRappel) {
|
||||
this.dateDernierRappel = dateDernierRappel;
|
||||
}
|
||||
|
||||
public UUID getValidePar() {
|
||||
return validePar;
|
||||
}
|
||||
|
||||
public void setValidePar(UUID validePar) {
|
||||
this.validePar = validePar;
|
||||
}
|
||||
|
||||
public String getNomValidateur() {
|
||||
return nomValidateur;
|
||||
}
|
||||
|
||||
public void setNomValidateur(String nomValidateur) {
|
||||
this.nomValidateur = nomValidateur;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Génère un numéro de référence unique pour la cotisation
|
||||
*
|
||||
* @return Le numéro de référence généré
|
||||
*/
|
||||
private String genererNumeroReference() {
|
||||
return "COT-"
|
||||
+ LocalDate.now().getYear()
|
||||
+ "-"
|
||||
+ String.format("%06d", System.currentTimeMillis() % 1000000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la cotisation est payée intégralement
|
||||
*
|
||||
* @return true si le montant payé égale le montant dû
|
||||
*/
|
||||
public boolean isPayeeIntegralement() {
|
||||
return montantPaye != null && montantDu != null && montantPaye.compareTo(montantDu) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la cotisation est en retard
|
||||
*
|
||||
* @return true si la date d'échéance est dépassée et non payée
|
||||
*/
|
||||
public boolean isEnRetard() {
|
||||
return dateEcheance != null && LocalDate.now().isAfter(dateEcheance) && !isPayeeIntegralement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le montant restant à payer
|
||||
*
|
||||
* @return Le montant restant
|
||||
*/
|
||||
public BigDecimal getMontantRestant() {
|
||||
if (montantDu == null) return BigDecimal.ZERO;
|
||||
if (montantPaye == null) return montantDu;
|
||||
|
||||
BigDecimal restant = montantDu.subtract(montantPaye);
|
||||
return restant.compareTo(BigDecimal.ZERO) > 0 ? restant : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le pourcentage de paiement
|
||||
*
|
||||
* @return Le pourcentage payé (0-100)
|
||||
*/
|
||||
public int getPourcentagePaiement() {
|
||||
if (montantDu == null || montantDu.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (montantPaye == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return montantPaye
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.divide(montantDu, 0, java.math.RoundingMode.HALF_UP)
|
||||
.intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le nombre de jours de retard
|
||||
*
|
||||
* @return Le nombre de jours de retard, 0 si pas en retard
|
||||
*/
|
||||
public long getJoursRetard() {
|
||||
if (dateEcheance == null || !isEnRetard()) {
|
||||
return 0;
|
||||
}
|
||||
return ChronoUnit.DAYS.between(dateEcheance, LocalDate.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé du type de cotisation
|
||||
*
|
||||
* @return Le libellé du type
|
||||
*/
|
||||
public String getTypeCotisationLibelle() {
|
||||
if (typeCotisation == null) return "Non défini";
|
||||
|
||||
return switch (typeCotisation) {
|
||||
case "MENSUELLE" -> "Mensuelle";
|
||||
case "TRIMESTRIELLE" -> "Trimestrielle";
|
||||
case "SEMESTRIELLE" -> "Semestrielle";
|
||||
case "ANNUELLE" -> "Annuelle";
|
||||
case "EXCEPTIONNELLE" -> "Exceptionnelle";
|
||||
case "ADHESION" -> "Adhésion";
|
||||
default -> typeCotisation;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé du statut
|
||||
*
|
||||
* @return Le libellé du statut
|
||||
*/
|
||||
public String getStatutLibelle() {
|
||||
if (statut == null) return "Non défini";
|
||||
|
||||
return switch (statut) {
|
||||
case "EN_ATTENTE" -> "En attente";
|
||||
case "PAYEE" -> "Payée";
|
||||
case "PARTIELLEMENT_PAYEE" -> "Partiellement payée";
|
||||
case "EN_RETARD" -> "En retard";
|
||||
case "ANNULEE" -> "Annulée";
|
||||
case "REMBOURSEE" -> "Remboursée";
|
||||
default -> statut;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé de la méthode de paiement
|
||||
*
|
||||
* @return Le libellé de la méthode
|
||||
*/
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
/** Met à jour le statut en fonction du montant payé */
|
||||
public void mettreAJourStatut() {
|
||||
if (montantPaye == null || montantPaye.compareTo(BigDecimal.ZERO) == 0) {
|
||||
if (isEnRetard()) {
|
||||
this.statut = "EN_RETARD";
|
||||
} else {
|
||||
this.statut = "EN_ATTENTE";
|
||||
}
|
||||
} else if (isPayeeIntegralement()) {
|
||||
this.statut = "PAYEE";
|
||||
if (this.datePaiement == null) {
|
||||
this.datePaiement = LocalDateTime.now();
|
||||
}
|
||||
} else {
|
||||
this.statut = "PARTIELLEMENT_PAYEE";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque la cotisation comme payée
|
||||
*
|
||||
* @param montant Le montant payé
|
||||
* @param methode La méthode de paiement
|
||||
* @param reference La référence du paiement
|
||||
*/
|
||||
public void marquerCommePaye(BigDecimal montant, String methode, String reference) {
|
||||
this.montantPaye = montant;
|
||||
this.methodePaiement = methode;
|
||||
this.referencePaiement = reference;
|
||||
this.datePaiement = LocalDateTime.now();
|
||||
mettreAJourStatut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CotisationDTO{"
|
||||
+ "numeroReference='"
|
||||
+ numeroReference
|
||||
+ '\''
|
||||
+ ", nomMembre='"
|
||||
+ nomMembre
|
||||
+ '\''
|
||||
+ ", typeCotisation='"
|
||||
+ typeCotisation
|
||||
+ '\''
|
||||
+ ", montantDu="
|
||||
+ montantDu
|
||||
+ ", montantPaye="
|
||||
+ montantPaye
|
||||
+ ", statut='"
|
||||
+ statut
|
||||
+ '\''
|
||||
+ ", dateEcheance="
|
||||
+ dateEcheance
|
||||
+ ", periode='"
|
||||
+ periode
|
||||
+ '\''
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
package dev.lions.unionflow.server.api.dto.formuleabonnement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des formules d'abonnement UnionFlow Représente les différents plans/formules
|
||||
* d'abonnement disponibles dans le catalogue
|
||||
*
|
||||
* <p>Distinction importante : - FormuleAbonnementDTO = Plans disponibles (BASIC, PREMIUM, etc.) -
|
||||
* CATALOGUE - AbonnementDTO = Souscription effective d'une organisation - INSTANCE
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class FormuleAbonnementDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Types de formules disponibles */
|
||||
public enum TypeFormule {
|
||||
BASIC("Formule Basique"),
|
||||
STANDARD("Formule Standard"),
|
||||
PREMIUM("Formule Premium"),
|
||||
ENTERPRISE("Formule Entreprise");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeFormule(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
/** Statuts des formules */
|
||||
public enum StatutFormule {
|
||||
ACTIVE("Active"),
|
||||
INACTIVE("Inactive"),
|
||||
ARCHIVEE("Archivée"),
|
||||
BIENTOT_DISPONIBLE("Bientôt Disponible");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutFormule(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
/** Nom de la formule */
|
||||
@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")
|
||||
private String nom;
|
||||
|
||||
/** Code unique de la formule */
|
||||
@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")
|
||||
private String code;
|
||||
|
||||
/** Description détaillée de la formule */
|
||||
@Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
|
||||
private String description;
|
||||
|
||||
/** Type de formule (enum) */
|
||||
@NotNull(message = "Le type de formule est obligatoire")
|
||||
private TypeFormule type;
|
||||
|
||||
/** Statut de la formule (enum) */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
private StatutFormule statut;
|
||||
|
||||
/** Prix mensuel de la formule */
|
||||
@NotNull(message = "Le prix mensuel est obligatoire")
|
||||
@DecimalMin(value = "0.0", inclusive = false, message = "Le prix mensuel doit être positif")
|
||||
@Digits(
|
||||
integer = 10,
|
||||
fraction = 2,
|
||||
message = "Le prix mensuel ne peut avoir plus de 10 chiffres entiers et 2 décimales")
|
||||
private BigDecimal prixMensuel;
|
||||
|
||||
/** Prix annuel de la formule (avec remise éventuelle) */
|
||||
@DecimalMin(value = "0.0", inclusive = false, message = "Le prix annuel doit être positif")
|
||||
@Digits(
|
||||
integer = 10,
|
||||
fraction = 2,
|
||||
message = "Le prix annuel ne peut avoir plus de 10 chiffres entiers et 2 décimales")
|
||||
private BigDecimal prixAnnuel;
|
||||
|
||||
/** Devise utilisée */
|
||||
@NotBlank(message = "La devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
|
||||
private String devise = "XOF";
|
||||
|
||||
/** Nombre maximum de membres autorisés */
|
||||
@NotNull(message = "Le nombre maximum de membres est obligatoire")
|
||||
private Integer maxMembres;
|
||||
|
||||
/** Nombre maximum d'administrateurs autorisés */
|
||||
@NotNull(message = "Le nombre maximum d'administrateurs est obligatoire")
|
||||
private Integer maxAdministrateurs;
|
||||
|
||||
/** Espace de stockage alloué en GB */
|
||||
@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")
|
||||
private BigDecimal espaceStockageGB;
|
||||
|
||||
/** Support technique inclus */
|
||||
@NotNull(message = "Le support technique doit être spécifié")
|
||||
private Boolean supportTechnique;
|
||||
|
||||
/** Niveau de support */
|
||||
@Pattern(
|
||||
regexp = "^(EMAIL|CHAT|TELEPHONE|PREMIUM)$",
|
||||
message = "Le niveau de support doit être EMAIL, CHAT, TELEPHONE ou PREMIUM")
|
||||
private String niveauSupport;
|
||||
|
||||
/** Fonctionnalités avancées incluses */
|
||||
private Boolean fonctionnalitesAvancees;
|
||||
|
||||
/** Accès API autorisé */
|
||||
private Boolean apiAccess;
|
||||
|
||||
/** Rapports personnalisés autorisés */
|
||||
private Boolean rapportsPersonnalises;
|
||||
|
||||
/** Intégrations tierces autorisées */
|
||||
private Boolean integrationsTierces;
|
||||
|
||||
/** Sauvegarde automatique incluse */
|
||||
private Boolean sauvegardeAutomatique;
|
||||
|
||||
/** Support multi-langues */
|
||||
private Boolean multiLangues;
|
||||
|
||||
/** Personnalisation de l'interface */
|
||||
private Boolean personnalisationInterface;
|
||||
|
||||
/** Formation incluse */
|
||||
private Boolean formationIncluse;
|
||||
|
||||
/** Nombre d'heures de formation incluses */
|
||||
private Integer heuresFormation;
|
||||
|
||||
/** Formule populaire (mise en avant) */
|
||||
private Boolean populaire;
|
||||
|
||||
/** Formule recommandée */
|
||||
private Boolean recommandee;
|
||||
|
||||
/** Période d'essai gratuite en jours */
|
||||
private Integer periodeEssaiJours;
|
||||
|
||||
/** Date de début de validité de la formule */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateDebutValidite;
|
||||
|
||||
/** Date de fin de validité de la formule */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateFinValidite;
|
||||
|
||||
/** Ordre d'affichage dans le catalogue */
|
||||
private Integer ordreAffichage;
|
||||
|
||||
/** Couleur associée à la formule (code hexadécimal) */
|
||||
@Pattern(
|
||||
regexp = "^#[0-9A-Fa-f]{6}$",
|
||||
message = "La couleur doit être un code hexadécimal valide")
|
||||
private String couleur;
|
||||
|
||||
/** Icône associée à la formule */
|
||||
@Size(max = 50, message = "Le nom de l'icône ne peut pas dépasser 50 caractères")
|
||||
private String icone;
|
||||
|
||||
/** Notes administratives */
|
||||
@Size(max = 500, message = "Les notes ne peuvent pas dépasser 500 caractères")
|
||||
private String notes;
|
||||
|
||||
// Constructeurs
|
||||
public FormuleAbonnementDTO() {
|
||||
super();
|
||||
this.devise = "XOF";
|
||||
this.statut = StatutFormule.ACTIVE;
|
||||
this.type = TypeFormule.BASIC;
|
||||
this.supportTechnique = true;
|
||||
this.fonctionnalitesAvancees = false;
|
||||
this.apiAccess = false;
|
||||
this.rapportsPersonnalises = false;
|
||||
this.integrationsTierces = false;
|
||||
this.sauvegardeAutomatique = true;
|
||||
this.multiLangues = false;
|
||||
this.personnalisationInterface = false;
|
||||
this.formationIncluse = false;
|
||||
this.populaire = false;
|
||||
this.recommandee = false;
|
||||
this.periodeEssaiJours = 0;
|
||||
this.heuresFormation = 0;
|
||||
this.ordreAffichage = 1;
|
||||
}
|
||||
|
||||
public FormuleAbonnementDTO(String nom, String code, TypeFormule type, BigDecimal prixMensuel) {
|
||||
this();
|
||||
this.nom = nom;
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
this.prixMensuel = prixMensuel;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getNom() {
|
||||
return nom;
|
||||
}
|
||||
|
||||
public void setNom(String nom) {
|
||||
this.nom = nom;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public TypeFormule getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(TypeFormule type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public StatutFormule getStatut() {
|
||||
return statut;
|
||||
}
|
||||
|
||||
public void setStatut(StatutFormule statut) {
|
||||
this.statut = statut;
|
||||
}
|
||||
|
||||
public BigDecimal getPrixMensuel() {
|
||||
return prixMensuel;
|
||||
}
|
||||
|
||||
public void setPrixMensuel(BigDecimal prixMensuel) {
|
||||
this.prixMensuel = prixMensuel;
|
||||
}
|
||||
|
||||
public BigDecimal getPrixAnnuel() {
|
||||
return prixAnnuel;
|
||||
}
|
||||
|
||||
public void setPrixAnnuel(BigDecimal prixAnnuel) {
|
||||
this.prixAnnuel = prixAnnuel;
|
||||
}
|
||||
|
||||
public String getDevise() {
|
||||
return devise;
|
||||
}
|
||||
|
||||
public void setDevise(String devise) {
|
||||
this.devise = devise;
|
||||
}
|
||||
|
||||
public Integer getMaxMembres() {
|
||||
return maxMembres;
|
||||
}
|
||||
|
||||
public void setMaxMembres(Integer maxMembres) {
|
||||
this.maxMembres = maxMembres;
|
||||
}
|
||||
|
||||
public Integer getMaxAdministrateurs() {
|
||||
return maxAdministrateurs;
|
||||
}
|
||||
|
||||
public void setMaxAdministrateurs(Integer maxAdministrateurs) {
|
||||
this.maxAdministrateurs = maxAdministrateurs;
|
||||
}
|
||||
|
||||
public BigDecimal getEspaceStockageGB() {
|
||||
return espaceStockageGB;
|
||||
}
|
||||
|
||||
public void setEspaceStockageGB(BigDecimal espaceStockageGB) {
|
||||
this.espaceStockageGB = espaceStockageGB;
|
||||
}
|
||||
|
||||
public Boolean getSupportTechnique() {
|
||||
return supportTechnique;
|
||||
}
|
||||
|
||||
public void setSupportTechnique(Boolean supportTechnique) {
|
||||
this.supportTechnique = supportTechnique;
|
||||
}
|
||||
|
||||
public String getNiveauSupport() {
|
||||
return niveauSupport;
|
||||
}
|
||||
|
||||
public void setNiveauSupport(String niveauSupport) {
|
||||
this.niveauSupport = niveauSupport;
|
||||
}
|
||||
|
||||
public Boolean getFonctionnalitesAvancees() {
|
||||
return fonctionnalitesAvancees;
|
||||
}
|
||||
|
||||
public void setFonctionnalitesAvancees(Boolean fonctionnalitesAvancees) {
|
||||
this.fonctionnalitesAvancees = fonctionnalitesAvancees;
|
||||
}
|
||||
|
||||
public Boolean getApiAccess() {
|
||||
return apiAccess;
|
||||
}
|
||||
|
||||
public void setApiAccess(Boolean apiAccess) {
|
||||
this.apiAccess = apiAccess;
|
||||
}
|
||||
|
||||
public Boolean getRapportsPersonnalises() {
|
||||
return rapportsPersonnalises;
|
||||
}
|
||||
|
||||
public void setRapportsPersonnalises(Boolean rapportsPersonnalises) {
|
||||
this.rapportsPersonnalises = rapportsPersonnalises;
|
||||
}
|
||||
|
||||
public Boolean getIntegrationsTierces() {
|
||||
return integrationsTierces;
|
||||
}
|
||||
|
||||
public void setIntegrationsTierces(Boolean integrationsTierces) {
|
||||
this.integrationsTierces = integrationsTierces;
|
||||
}
|
||||
|
||||
public Boolean getSauvegardeAutomatique() {
|
||||
return sauvegardeAutomatique;
|
||||
}
|
||||
|
||||
public void setSauvegardeAutomatique(Boolean sauvegardeAutomatique) {
|
||||
this.sauvegardeAutomatique = sauvegardeAutomatique;
|
||||
}
|
||||
|
||||
public Boolean getMultiLangues() {
|
||||
return multiLangues;
|
||||
}
|
||||
|
||||
public void setMultiLangues(Boolean multiLangues) {
|
||||
this.multiLangues = multiLangues;
|
||||
}
|
||||
|
||||
public Boolean getPersonnalisationInterface() {
|
||||
return personnalisationInterface;
|
||||
}
|
||||
|
||||
public void setPersonnalisationInterface(Boolean personnalisationInterface) {
|
||||
this.personnalisationInterface = personnalisationInterface;
|
||||
}
|
||||
|
||||
public Boolean getFormationIncluse() {
|
||||
return formationIncluse;
|
||||
}
|
||||
|
||||
public void setFormationIncluse(Boolean formationIncluse) {
|
||||
this.formationIncluse = formationIncluse;
|
||||
}
|
||||
|
||||
public Integer getHeuresFormation() {
|
||||
return heuresFormation;
|
||||
}
|
||||
|
||||
public void setHeuresFormation(Integer heuresFormation) {
|
||||
this.heuresFormation = heuresFormation;
|
||||
}
|
||||
|
||||
public Boolean getPopulaire() {
|
||||
return populaire;
|
||||
}
|
||||
|
||||
public void setPopulaire(Boolean populaire) {
|
||||
this.populaire = populaire;
|
||||
}
|
||||
|
||||
public Boolean getRecommandee() {
|
||||
return recommandee;
|
||||
}
|
||||
|
||||
public void setRecommandee(Boolean recommandee) {
|
||||
this.recommandee = recommandee;
|
||||
}
|
||||
|
||||
public Integer getPeriodeEssaiJours() {
|
||||
return periodeEssaiJours;
|
||||
}
|
||||
|
||||
public void setPeriodeEssaiJours(Integer periodeEssaiJours) {
|
||||
this.periodeEssaiJours = periodeEssaiJours;
|
||||
}
|
||||
|
||||
public LocalDate getDateDebutValidite() {
|
||||
return dateDebutValidite;
|
||||
}
|
||||
|
||||
public void setDateDebutValidite(LocalDate dateDebutValidite) {
|
||||
this.dateDebutValidite = dateDebutValidite;
|
||||
}
|
||||
|
||||
public LocalDate getDateFinValidite() {
|
||||
return dateFinValidite;
|
||||
}
|
||||
|
||||
public void setDateFinValidite(LocalDate dateFinValidite) {
|
||||
this.dateFinValidite = dateFinValidite;
|
||||
}
|
||||
|
||||
public Integer getOrdreAffichage() {
|
||||
return ordreAffichage;
|
||||
}
|
||||
|
||||
public void setOrdreAffichage(Integer ordreAffichage) {
|
||||
this.ordreAffichage = ordreAffichage;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public void setCouleur(String couleur) {
|
||||
this.couleur = couleur;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public void setIcone(String icone) {
|
||||
this.icone = icone;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(String notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Vérifie si la formule est active
|
||||
*
|
||||
* @return true si la formule est active
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return StatutFormule.ACTIVE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la formule est inactive
|
||||
*
|
||||
* @return true si la formule est inactive
|
||||
*/
|
||||
public boolean isInactive() {
|
||||
return StatutFormule.INACTIVE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la formule est archivée
|
||||
*
|
||||
* @return true si la formule est archivée
|
||||
*/
|
||||
public boolean isArchivee() {
|
||||
return StatutFormule.ARCHIVEE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la formule est actuellement valide
|
||||
*
|
||||
* @return true si la formule est dans sa période de validité
|
||||
*/
|
||||
public boolean isValide() {
|
||||
if (!isActive()) return false;
|
||||
|
||||
LocalDate aujourd = LocalDate.now();
|
||||
|
||||
if (dateDebutValidite != null && aujourd.isBefore(dateDebutValidite)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dateFinValidite != null && aujourd.isAfter(dateFinValidite)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'économie annuelle par rapport au paiement mensuel
|
||||
*
|
||||
* @return L'économie réalisée en payant annuellement
|
||||
*/
|
||||
public BigDecimal getEconomieAnnuelle() {
|
||||
if (prixMensuel == null || prixAnnuel == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
BigDecimal coutMensuelAnnuel = prixMensuel.multiply(BigDecimal.valueOf(12));
|
||||
return coutMensuelAnnuel.subtract(prixAnnuel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le pourcentage d'économie annuelle
|
||||
*
|
||||
* @return Le pourcentage d'économie (0-100)
|
||||
*/
|
||||
public int getPourcentageEconomieAnnuelle() {
|
||||
if (prixMensuel == null || prixAnnuel == null) return 0;
|
||||
|
||||
BigDecimal coutMensuelAnnuel = prixMensuel.multiply(BigDecimal.valueOf(12));
|
||||
if (coutMensuelAnnuel.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal economie = getEconomieAnnuelle();
|
||||
return economie
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.divide(coutMensuelAnnuel, 0, java.math.RoundingMode.HALF_UP)
|
||||
.intValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la formule a une période d'essai
|
||||
*
|
||||
* @return true si une période d'essai est disponible
|
||||
*/
|
||||
public boolean hasPeriodeEssai() {
|
||||
return periodeEssaiJours != null && periodeEssaiJours > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la formule inclut la formation
|
||||
*
|
||||
* @return true si la formation est incluse
|
||||
*/
|
||||
public boolean hasFormation() {
|
||||
return Boolean.TRUE.equals(formationIncluse) && heuresFormation != null && heuresFormation > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la formule est recommandée ou populaire
|
||||
*
|
||||
* @return true si la formule est mise en avant
|
||||
*/
|
||||
public boolean isMiseEnAvant() {
|
||||
return Boolean.TRUE.equals(populaire) || Boolean.TRUE.equals(recommandee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le badge à afficher pour la formule
|
||||
*
|
||||
* @return Le texte du badge ou null
|
||||
*/
|
||||
public String getBadge() {
|
||||
if (Boolean.TRUE.equals(populaire)) return "POPULAIRE";
|
||||
if (Boolean.TRUE.equals(recommandee)) return "RECOMMANDÉE";
|
||||
if (hasPeriodeEssai()) return "ESSAI GRATUIT";
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le score de fonctionnalités (0-100)
|
||||
*
|
||||
* @return Le score basé sur les fonctionnalités incluses
|
||||
*/
|
||||
public int getScoreFonctionnalites() {
|
||||
int score = 0;
|
||||
int total = 0;
|
||||
|
||||
// Fonctionnalités de base (poids 1)
|
||||
if (Boolean.TRUE.equals(supportTechnique)) score += 10;
|
||||
total += 10;
|
||||
|
||||
if (Boolean.TRUE.equals(sauvegardeAutomatique)) score += 10;
|
||||
total += 10;
|
||||
|
||||
// Fonctionnalités avancées (poids 2)
|
||||
if (Boolean.TRUE.equals(fonctionnalitesAvancees)) score += 15;
|
||||
total += 15;
|
||||
|
||||
if (Boolean.TRUE.equals(apiAccess)) score += 15;
|
||||
total += 15;
|
||||
|
||||
if (Boolean.TRUE.equals(rapportsPersonnalises)) score += 15;
|
||||
total += 15;
|
||||
|
||||
if (Boolean.TRUE.equals(integrationsTierces)) score += 15;
|
||||
total += 15;
|
||||
|
||||
// Fonctionnalités premium (poids 3)
|
||||
if (Boolean.TRUE.equals(multiLangues)) score += 10;
|
||||
total += 10;
|
||||
|
||||
if (Boolean.TRUE.equals(personnalisationInterface)) score += 10;
|
||||
total += 10;
|
||||
|
||||
// total est toujours > 0 car il est toujours incrémenté (minimum 100)
|
||||
return (score * 100) / total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine la classe CSS pour la couleur de la formule
|
||||
*
|
||||
* @return La classe CSS appropriée
|
||||
*/
|
||||
public String getCssClass() {
|
||||
if (type == null) return "formule-default";
|
||||
|
||||
return switch (type) {
|
||||
case BASIC -> "formule-basic";
|
||||
case STANDARD -> "formule-standard";
|
||||
case PREMIUM -> "formule-premium";
|
||||
case ENTERPRISE -> "formule-enterprise";
|
||||
};
|
||||
}
|
||||
|
||||
/** Active la formule */
|
||||
public void activer() {
|
||||
this.statut = StatutFormule.ACTIVE;
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
/** Désactive la formule */
|
||||
public void desactiver() {
|
||||
this.statut = StatutFormule.INACTIVE;
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
/** Archive la formule */
|
||||
public void archiver() {
|
||||
this.statut = StatutFormule.ARCHIVEE;
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FormuleAbonnementDTO{"
|
||||
+ "nom='"
|
||||
+ nom
|
||||
+ '\''
|
||||
+ ", code='"
|
||||
+ code
|
||||
+ '\''
|
||||
+ ", type="
|
||||
+ type
|
||||
+ ", prixMensuel="
|
||||
+ prixMensuel
|
||||
+ ", prixAnnuel="
|
||||
+ prixAnnuel
|
||||
+ ", devise='"
|
||||
+ devise
|
||||
+ '\''
|
||||
+ ", statut="
|
||||
+ statut
|
||||
+ ", populaire="
|
||||
+ populaire
|
||||
+ ", recommandee="
|
||||
+ recommandee
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
package dev.lions.unionflow.server.api.dto.membre;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
|
||||
import dev.lions.unionflow.server.api.validation.ValidationConstants;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Past;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des membres dans l'API UnionFlow Contient toutes les informations relatives à
|
||||
* un membre d'une organisation
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class MembreDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Numéro unique du membre (format: UF-YYYY-XXXXXXXX)
|
||||
* AUTO-GÉNÉRÉ si non fourni - Ne pas afficher dans le formulaire de création
|
||||
*/
|
||||
@Size(max = 50, message = "Le numéro de membre ne peut pas dépasser 50 caractères")
|
||||
private String numeroMembre;
|
||||
|
||||
/** Nom de famille du membre - OBLIGATOIRE */
|
||||
@NotBlank(message = "Le nom" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Size(
|
||||
min = ValidationConstants.NOM_PRENOM_MIN_LENGTH,
|
||||
max = ValidationConstants.NOM_PRENOM_MAX_LENGTH,
|
||||
message = ValidationConstants.NOM_SIZE_MESSAGE)
|
||||
@Pattern(
|
||||
regexp = "^[a-zA-ZÀ-ÿ\\s\\-']+$",
|
||||
message = "Le nom ne peut contenir que des lettres, espaces, tirets et apostrophes")
|
||||
private String nom;
|
||||
|
||||
/** Prénom du membre - OBLIGATOIRE */
|
||||
@NotBlank(message = "Le prénom" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Size(
|
||||
min = ValidationConstants.NOM_PRENOM_MIN_LENGTH,
|
||||
max = ValidationConstants.NOM_PRENOM_MAX_LENGTH,
|
||||
message = ValidationConstants.PRENOM_SIZE_MESSAGE)
|
||||
@Pattern(
|
||||
regexp = "^[a-zA-ZÀ-ÿ\\s\\-']+$",
|
||||
message = "Le prénom ne peut contenir que des lettres, espaces, tirets et apostrophes")
|
||||
private String prenom;
|
||||
|
||||
/** Adresse email du membre - OBLIGATOIRE */
|
||||
@NotBlank(message = "L'email" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Email(message = ValidationConstants.EMAIL_FORMAT_MESSAGE)
|
||||
@Size(
|
||||
max = ValidationConstants.EMAIL_MAX_LENGTH,
|
||||
message = ValidationConstants.EMAIL_SIZE_MESSAGE)
|
||||
private String email;
|
||||
|
||||
/** Numéro de téléphone du membre - Optionnel, format flexible */
|
||||
@Size(max = 20, message = "Le téléphone ne peut pas dépasser 20 caractères")
|
||||
private String telephone;
|
||||
|
||||
/**
|
||||
* Date de naissance du membre - OPTIONNELLE
|
||||
* AUTO-GÉNÉRÉE à il y a 18 ans si non fournie - Peut être laissée vide dans le formulaire
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Past(message = "La date de naissance doit être dans le passé")
|
||||
private LocalDate dateNaissance;
|
||||
|
||||
/** Adresse physique du membre */
|
||||
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
|
||||
private String adresse;
|
||||
|
||||
/** Profession du membre */
|
||||
@Size(max = 100, message = "La profession ne peut pas dépasser 100 caractères")
|
||||
private String profession;
|
||||
|
||||
/** Statut matrimonial du membre */
|
||||
@Size(max = 20, message = "Le statut matrimonial ne peut pas dépasser 20 caractères")
|
||||
private String statutMatrimonial;
|
||||
|
||||
/** Nationalité du membre */
|
||||
@Size(max = 50, message = "La nationalité ne peut pas dépasser 50 caractères")
|
||||
private String nationalite;
|
||||
|
||||
/** Numéro de pièce d'identité */
|
||||
@Size(max = 50, message = "Le numéro d'identité ne peut pas dépasser 50 caractères")
|
||||
private String numeroIdentite;
|
||||
|
||||
/** Type de pièce d'identité (CNI, Passeport, etc.) */
|
||||
@Size(max = 20, message = "Le type d'identité ne peut pas dépasser 20 caractères")
|
||||
private String typeIdentite;
|
||||
|
||||
/** Statut du membre - OBLIGATOIRE */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
private StatutMembre statut;
|
||||
|
||||
/** Identifiant de l'association à laquelle appartient le membre - OBLIGATOIRE */
|
||||
@NotNull(message = "L'association est obligatoire")
|
||||
private UUID associationId;
|
||||
|
||||
/** Nom de l'association (lecture seule) */
|
||||
private String associationNom;
|
||||
|
||||
/**
|
||||
* Date d'adhésion du membre - OPTIONNELLE
|
||||
* AUTO-GÉNÉRÉE à LocalDate.now() si non fournie - Ne pas afficher dans le formulaire de création
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateAdhesion;
|
||||
|
||||
/** Région géographique */
|
||||
@Size(max = 50, message = "La région ne peut pas dépasser 50 caractères")
|
||||
private String region;
|
||||
|
||||
/** Ville de résidence */
|
||||
@Size(max = 50, message = "La ville ne peut pas dépasser 50 caractères")
|
||||
private String ville;
|
||||
|
||||
/** Quartier de résidence */
|
||||
@Size(max = 50, message = "Le quartier ne peut pas dépasser 50 caractères")
|
||||
private String quartier;
|
||||
|
||||
/** Rôle du membre dans l'organisation */
|
||||
@Size(max = 50, message = "Le rôle ne peut pas dépasser 50 caractères")
|
||||
private String role;
|
||||
|
||||
/** Indique si le membre fait partie du bureau */
|
||||
private Boolean membreBureau = false;
|
||||
|
||||
/** Indique si le membre est responsable */
|
||||
private Boolean responsable = false;
|
||||
|
||||
/** Photo de profil (URL ou chemin) */
|
||||
@Size(max = 255, message = "L'URL de la photo ne peut pas dépasser 255 caractères")
|
||||
private String photoUrl;
|
||||
|
||||
// Constructeurs
|
||||
public MembreDTO() {
|
||||
super();
|
||||
this.statut = StatutMembre.ACTIF;
|
||||
this.dateAdhesion = LocalDate.now();
|
||||
this.membreBureau = false;
|
||||
this.responsable = false;
|
||||
}
|
||||
|
||||
public MembreDTO(String numeroMembre, String nom, String prenom, String email) {
|
||||
this();
|
||||
this.numeroMembre = numeroMembre;
|
||||
this.nom = nom;
|
||||
this.prenom = prenom;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getNumeroMembre() {
|
||||
return numeroMembre;
|
||||
}
|
||||
|
||||
public void setNumeroMembre(String numeroMembre) {
|
||||
this.numeroMembre = numeroMembre;
|
||||
}
|
||||
|
||||
public String getNom() {
|
||||
return nom;
|
||||
}
|
||||
|
||||
public void setNom(String nom) {
|
||||
this.nom = nom;
|
||||
}
|
||||
|
||||
public String getPrenom() {
|
||||
return prenom;
|
||||
}
|
||||
|
||||
public void setPrenom(String prenom) {
|
||||
this.prenom = prenom;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getTelephone() {
|
||||
return telephone;
|
||||
}
|
||||
|
||||
public void setTelephone(String telephone) {
|
||||
this.telephone = telephone;
|
||||
}
|
||||
|
||||
public LocalDate getDateNaissance() {
|
||||
return dateNaissance;
|
||||
}
|
||||
|
||||
public void setDateNaissance(LocalDate dateNaissance) {
|
||||
this.dateNaissance = dateNaissance;
|
||||
}
|
||||
|
||||
public String getAdresse() {
|
||||
return adresse;
|
||||
}
|
||||
|
||||
public void setAdresse(String adresse) {
|
||||
this.adresse = adresse;
|
||||
}
|
||||
|
||||
public String getProfession() {
|
||||
return profession;
|
||||
}
|
||||
|
||||
public void setProfession(String profession) {
|
||||
this.profession = profession;
|
||||
}
|
||||
|
||||
public String getStatutMatrimonial() {
|
||||
return statutMatrimonial;
|
||||
}
|
||||
|
||||
public void setStatutMatrimonial(String statutMatrimonial) {
|
||||
this.statutMatrimonial = statutMatrimonial;
|
||||
}
|
||||
|
||||
public String getNationalite() {
|
||||
return nationalite;
|
||||
}
|
||||
|
||||
public void setNationalite(String nationalite) {
|
||||
this.nationalite = nationalite;
|
||||
}
|
||||
|
||||
public String getNumeroIdentite() {
|
||||
return numeroIdentite;
|
||||
}
|
||||
|
||||
public void setNumeroIdentite(String numeroIdentite) {
|
||||
this.numeroIdentite = numeroIdentite;
|
||||
}
|
||||
|
||||
public String getTypeIdentite() {
|
||||
return typeIdentite;
|
||||
}
|
||||
|
||||
public void setTypeIdentite(String typeIdentite) {
|
||||
this.typeIdentite = typeIdentite;
|
||||
}
|
||||
|
||||
public StatutMembre getStatut() {
|
||||
return statut;
|
||||
}
|
||||
|
||||
public void setStatut(StatutMembre statut) {
|
||||
this.statut = statut;
|
||||
}
|
||||
|
||||
public UUID getAssociationId() {
|
||||
return associationId;
|
||||
}
|
||||
|
||||
public void setAssociationId(UUID associationId) {
|
||||
this.associationId = associationId;
|
||||
}
|
||||
|
||||
public String getAssociationNom() {
|
||||
return associationNom;
|
||||
}
|
||||
|
||||
public void setAssociationNom(String associationNom) {
|
||||
this.associationNom = associationNom;
|
||||
}
|
||||
|
||||
public LocalDate getDateAdhesion() {
|
||||
return dateAdhesion;
|
||||
}
|
||||
|
||||
public void setDateAdhesion(LocalDate dateAdhesion) {
|
||||
this.dateAdhesion = dateAdhesion;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getVille() {
|
||||
return ville;
|
||||
}
|
||||
|
||||
public void setVille(String ville) {
|
||||
this.ville = ville;
|
||||
}
|
||||
|
||||
public String getQuartier() {
|
||||
return quartier;
|
||||
}
|
||||
|
||||
public void setQuartier(String quartier) {
|
||||
this.quartier = quartier;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Boolean getMembreBureau() {
|
||||
return membreBureau;
|
||||
}
|
||||
|
||||
public void setMembreBureau(Boolean membreBureau) {
|
||||
this.membreBureau = membreBureau;
|
||||
}
|
||||
|
||||
public Boolean getResponsable() {
|
||||
return responsable;
|
||||
}
|
||||
|
||||
public void setResponsable(Boolean responsable) {
|
||||
this.responsable = responsable;
|
||||
}
|
||||
|
||||
public String getPhotoUrl() {
|
||||
return photoUrl;
|
||||
}
|
||||
|
||||
public void setPhotoUrl(String photoUrl) {
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Retourne le nom complet du membre (prénom + nom)
|
||||
*
|
||||
* @return Le nom complet
|
||||
*/
|
||||
public String getNomComplet() {
|
||||
if (prenom != null && nom != null) {
|
||||
return prenom + " " + nom;
|
||||
} else if (nom != null) {
|
||||
return nom;
|
||||
} else if (prenom != null) {
|
||||
return prenom;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le membre est majeur (18 ans ou plus)
|
||||
*
|
||||
* @return true si le membre est majeur, false sinon
|
||||
*/
|
||||
public boolean estMajeur() {
|
||||
if (dateNaissance == null) {
|
||||
return false;
|
||||
}
|
||||
return LocalDate.now().minusYears(18).isAfter(dateNaissance)
|
||||
|| LocalDate.now().minusYears(18).isEqual(dateNaissance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'âge du membre
|
||||
*
|
||||
* @return L'âge en années, ou -1 si la date de naissance n'est pas définie
|
||||
*/
|
||||
public int getAge() {
|
||||
if (dateNaissance == null) {
|
||||
return -1;
|
||||
}
|
||||
return LocalDate.now().getYear() - dateNaissance.getYear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le membre est actif
|
||||
*
|
||||
* @return true si le statut est ACTIF
|
||||
*/
|
||||
public boolean estActif() {
|
||||
return StatutMembre.ACTIF.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le membre a un rôle de direction
|
||||
*
|
||||
* @return true si le membre fait partie du bureau ou est responsable
|
||||
*/
|
||||
public boolean hasRoleDirection() {
|
||||
return Boolean.TRUE.equals(membreBureau) || Boolean.TRUE.equals(responsable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne une représentation textuelle du statut
|
||||
*
|
||||
* @return Le libellé du statut
|
||||
*/
|
||||
public String getStatutLibelle() {
|
||||
return statut != null ? statut.getLibelle() : "Non défini";
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide les données essentielles du membre
|
||||
*
|
||||
* @return true si les données sont valides
|
||||
*/
|
||||
public boolean sontDonneesValides() {
|
||||
return numeroMembre != null
|
||||
&& !numeroMembre.trim().isEmpty()
|
||||
&& nom != null
|
||||
&& !nom.trim().isEmpty()
|
||||
&& prenom != null
|
||||
&& !prenom.trim().isEmpty()
|
||||
&& statut != null
|
||||
&& associationId != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MembreDTO{"
|
||||
+ "numeroMembre='"
|
||||
+ numeroMembre
|
||||
+ '\''
|
||||
+ ", nom='"
|
||||
+ nom
|
||||
+ '\''
|
||||
+ ", prenom='"
|
||||
+ prenom
|
||||
+ '\''
|
||||
+ ", email='"
|
||||
+ email
|
||||
+ '\''
|
||||
+ ", statut='"
|
||||
+ statut
|
||||
+ '\''
|
||||
+ ", associationId="
|
||||
+ associationId
|
||||
+ ", dateAdhesion="
|
||||
+ dateAdhesion
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package dev.lions.unionflow.server.api.dto.membre;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* DTO pour les critères de recherche avancée des membres Permet de filtrer les membres selon de
|
||||
* multiples critères
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-19
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "Critères de recherche avancée pour les membres")
|
||||
public class MembreSearchCriteria {
|
||||
|
||||
/** Terme de recherche général (nom, prénom, email) */
|
||||
@Schema(description = "Terme de recherche général dans nom, prénom ou email", example = "marie")
|
||||
@Size(max = 100, message = "Le terme de recherche ne peut pas dépasser 100 caractères")
|
||||
private String query;
|
||||
|
||||
/** Recherche par nom exact ou partiel */
|
||||
@Schema(description = "Filtre par nom (recherche partielle)", example = "Dupont")
|
||||
@Size(max = 50, message = "Le nom ne peut pas dépasser 50 caractères")
|
||||
private String nom;
|
||||
|
||||
/** Recherche par prénom exact ou partiel */
|
||||
@Schema(description = "Filtre par prénom (recherche partielle)", example = "Marie")
|
||||
@Size(max = 50, message = "Le prénom ne peut pas dépasser 50 caractères")
|
||||
private String prenom;
|
||||
|
||||
/** Recherche par email exact ou partiel */
|
||||
@Schema(description = "Filtre par email (recherche partielle)", example = "@unionflow.com")
|
||||
@Size(max = 100, message = "L'email ne peut pas dépasser 100 caractères")
|
||||
private String email;
|
||||
|
||||
/** Filtre par numéro de téléphone */
|
||||
@Schema(description = "Filtre par numéro de téléphone", example = "+221")
|
||||
@Size(max = 20, message = "Le téléphone ne peut pas dépasser 20 caractères")
|
||||
private String telephone;
|
||||
|
||||
/** Liste des IDs d'organisations */
|
||||
@Schema(description = "Liste des IDs d'organisations à inclure")
|
||||
private List<UUID> organisationIds;
|
||||
|
||||
/** Liste des rôles à rechercher */
|
||||
@Schema(description = "Liste des rôles à rechercher", example = "[\"PRESIDENT\", \"SECRETAIRE\"]")
|
||||
private List<String> roles;
|
||||
|
||||
/** Filtre par statut d'activité */
|
||||
@Schema(description = "Filtre par statut d'activité", example = "ACTIF")
|
||||
@Pattern(regexp = "^(ACTIF|INACTIF|SUSPENDU|RADIE)$", message = "Statut invalide")
|
||||
private String statut;
|
||||
|
||||
/** Date d'adhésion minimum */
|
||||
@Schema(description = "Date d'adhésion minimum", example = "2020-01-01")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateAdhesionMin;
|
||||
|
||||
/** Date d'adhésion maximum */
|
||||
@Schema(description = "Date d'adhésion maximum", example = "2025-12-31")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateAdhesionMax;
|
||||
|
||||
/** Âge minimum */
|
||||
@Schema(description = "Âge minimum", example = "18")
|
||||
@Min(value = 0, message = "L'âge minimum doit être positif")
|
||||
@Max(value = 120, message = "L'âge minimum ne peut pas dépasser 120 ans")
|
||||
private Integer ageMin;
|
||||
|
||||
/** Âge maximum */
|
||||
@Schema(description = "Âge maximum", example = "65")
|
||||
@Min(value = 0, message = "L'âge maximum doit être positif")
|
||||
@Max(value = 120, message = "L'âge maximum ne peut pas dépasser 120 ans")
|
||||
private Integer ageMax;
|
||||
|
||||
/** Filtre par région */
|
||||
@Schema(description = "Filtre par région", example = "Dakar")
|
||||
@Size(max = 50, message = "La région ne peut pas dépasser 50 caractères")
|
||||
private String region;
|
||||
|
||||
/** Filtre par ville */
|
||||
@Schema(description = "Filtre par ville", example = "Dakar")
|
||||
@Size(max = 50, message = "La ville ne peut pas dépasser 50 caractères")
|
||||
private String ville;
|
||||
|
||||
/** Filtre par profession */
|
||||
@Schema(description = "Filtre par profession", example = "Ingénieur")
|
||||
@Size(max = 100, message = "La profession ne peut pas dépasser 100 caractères")
|
||||
private String profession;
|
||||
|
||||
/** Filtre par nationalité */
|
||||
@Schema(description = "Filtre par nationalité", example = "Sénégalaise")
|
||||
@Size(max = 50, message = "La nationalité ne peut pas dépasser 50 caractères")
|
||||
private String nationalite;
|
||||
|
||||
/** Filtre membres du bureau uniquement */
|
||||
@Schema(description = "Filtre pour les membres du bureau uniquement")
|
||||
private Boolean membreBureau;
|
||||
|
||||
/** Filtre responsables uniquement */
|
||||
@Schema(description = "Filtre pour les responsables uniquement")
|
||||
private Boolean responsable;
|
||||
|
||||
/** Inclure les membres inactifs dans la recherche */
|
||||
@Schema(description = "Inclure les membres inactifs", defaultValue = "false")
|
||||
@Builder.Default
|
||||
private Boolean includeInactifs = false;
|
||||
|
||||
/**
|
||||
* Vérifie si au moins un critère de recherche est défini
|
||||
*
|
||||
* @return true si au moins un critère est défini
|
||||
*/
|
||||
public boolean hasAnyCriteria() {
|
||||
return query != null && !query.trim().isEmpty()
|
||||
|| nom != null && !nom.trim().isEmpty()
|
||||
|| prenom != null && !prenom.trim().isEmpty()
|
||||
|| email != null && !email.trim().isEmpty()
|
||||
|| telephone != null && !telephone.trim().isEmpty()
|
||||
|| organisationIds != null && !organisationIds.isEmpty()
|
||||
|| roles != null && !roles.isEmpty()
|
||||
|| statut != null && !statut.trim().isEmpty()
|
||||
|| dateAdhesionMin != null
|
||||
|| dateAdhesionMax != null
|
||||
|| ageMin != null
|
||||
|| ageMax != null
|
||||
|| region != null && !region.trim().isEmpty()
|
||||
|| ville != null && !ville.trim().isEmpty()
|
||||
|| profession != null && !profession.trim().isEmpty()
|
||||
|| nationalite != null && !nationalite.trim().isEmpty()
|
||||
|| membreBureau != null
|
||||
|| responsable != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide la cohérence des critères de recherche
|
||||
*
|
||||
* @return true si les critères sont cohérents
|
||||
*/
|
||||
public boolean isValid() {
|
||||
// Validation des dates
|
||||
if (dateAdhesionMin != null && dateAdhesionMax != null) {
|
||||
if (dateAdhesionMin.isAfter(dateAdhesionMax)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation des âges
|
||||
if (ageMin != null && ageMax != null) {
|
||||
if (ageMin > ageMax) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Nettoie les chaînes de caractères (trim et null si vide) */
|
||||
public void sanitize() {
|
||||
query = sanitizeString(query);
|
||||
nom = sanitizeString(nom);
|
||||
prenom = sanitizeString(prenom);
|
||||
email = sanitizeString(email);
|
||||
telephone = sanitizeString(telephone);
|
||||
statut = sanitizeString(statut);
|
||||
region = sanitizeString(region);
|
||||
ville = sanitizeString(ville);
|
||||
profession = sanitizeString(profession);
|
||||
nationalite = sanitizeString(nationalite);
|
||||
}
|
||||
|
||||
private String sanitizeString(String str) {
|
||||
if (str == null) return null;
|
||||
str = str.trim();
|
||||
return str.isEmpty() ? null : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne une description textuelle des critères actifs
|
||||
*
|
||||
* @return Description des critères
|
||||
*/
|
||||
public String getDescription() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (query != null) sb.append("Recherche: '").append(query).append("' ");
|
||||
if (nom != null) sb.append("Nom: '").append(nom).append("' ");
|
||||
if (prenom != null) sb.append("Prénom: '").append(prenom).append("' ");
|
||||
if (email != null) sb.append("Email: '").append(email).append("' ");
|
||||
if (statut != null) sb.append("Statut: ").append(statut).append(" ");
|
||||
if (organisationIds != null && !organisationIds.isEmpty()) {
|
||||
sb.append("Organisations: ").append(organisationIds.size()).append(" ");
|
||||
}
|
||||
if (roles != null && !roles.isEmpty()) {
|
||||
sb.append("Rôles: ").append(String.join(", ", roles)).append(" ");
|
||||
}
|
||||
if (dateAdhesionMin != null) sb.append("Adhésion >= ").append(dateAdhesionMin).append(" ");
|
||||
if (dateAdhesionMax != null) sb.append("Adhésion <= ").append(dateAdhesionMax).append(" ");
|
||||
if (ageMin != null) sb.append("Âge >= ").append(ageMin).append(" ");
|
||||
if (ageMax != null) sb.append("Âge <= ").append(ageMax).append(" ");
|
||||
if (region != null) sb.append("Région: '").append(region).append("' ");
|
||||
if (ville != null) sb.append("Ville: '").append(ville).append("' ");
|
||||
if (profession != null) sb.append("Profession: '").append(profession).append("' ");
|
||||
if (nationalite != null) sb.append("Nationalité: '").append(nationalite).append("' ");
|
||||
if (Boolean.TRUE.equals(membreBureau)) sb.append("Membre bureau ");
|
||||
if (Boolean.TRUE.equals(responsable)) sb.append("Responsable ");
|
||||
|
||||
return sb.toString().trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package dev.lions.unionflow.server.api.dto.membre;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* DTO pour les résultats de recherche avancée des membres Contient les résultats paginés et les
|
||||
* métadonnées de recherche
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-19
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "Résultats de recherche avancée des membres avec pagination")
|
||||
public class MembreSearchResultDTO {
|
||||
|
||||
/** Liste des membres trouvés */
|
||||
@Schema(description = "Liste des membres correspondant aux critères")
|
||||
private List<MembreDTO> membres;
|
||||
|
||||
/** Nombre total de résultats (toutes pages confondues) */
|
||||
@Schema(description = "Nombre total de résultats trouvés", example = "247")
|
||||
private long totalElements;
|
||||
|
||||
/** Nombre total de pages */
|
||||
@Schema(description = "Nombre total de pages", example = "13")
|
||||
private int totalPages;
|
||||
|
||||
/** Numéro de la page actuelle (0-based) */
|
||||
@Schema(description = "Numéro de la page actuelle", example = "0")
|
||||
private int currentPage;
|
||||
|
||||
/** Taille de la page */
|
||||
@Schema(description = "Nombre d'éléments par page", example = "20")
|
||||
private int pageSize;
|
||||
|
||||
/** Nombre d'éléments sur la page actuelle */
|
||||
@Schema(description = "Nombre d'éléments sur cette page", example = "20")
|
||||
private int numberOfElements;
|
||||
|
||||
/** Indique s'il y a une page suivante */
|
||||
@Schema(description = "Indique s'il y a une page suivante")
|
||||
private boolean hasNext;
|
||||
|
||||
/** Indique s'il y a une page précédente */
|
||||
@Schema(description = "Indique s'il y a une page précédente")
|
||||
private boolean hasPrevious;
|
||||
|
||||
/** Indique si c'est la première page */
|
||||
@Schema(description = "Indique si c'est la première page")
|
||||
private boolean isFirst;
|
||||
|
||||
/** Indique si c'est la dernière page */
|
||||
@Schema(description = "Indique si c'est la dernière page")
|
||||
private boolean isLast;
|
||||
|
||||
/** Critères de recherche utilisés */
|
||||
@Schema(description = "Critères de recherche qui ont été appliqués")
|
||||
private MembreSearchCriteria criteria;
|
||||
|
||||
/** Temps d'exécution de la recherche en millisecondes */
|
||||
@Schema(description = "Temps d'exécution de la recherche en ms", example = "45")
|
||||
private long executionTimeMs;
|
||||
|
||||
/** Statistiques de recherche */
|
||||
@Schema(description = "Statistiques sur les résultats de recherche")
|
||||
private SearchStatistics statistics;
|
||||
|
||||
/** Statistiques sur les résultats de recherche */
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "Statistiques sur les résultats de recherche")
|
||||
public static class SearchStatistics {
|
||||
|
||||
/** Répartition par statut */
|
||||
@Schema(description = "Nombre de membres actifs dans les résultats")
|
||||
private long membresActifs;
|
||||
|
||||
@Schema(description = "Nombre de membres inactifs dans les résultats")
|
||||
private long membresInactifs;
|
||||
|
||||
/** Répartition par âge */
|
||||
@Schema(description = "Âge moyen des membres trouvés")
|
||||
private double ageMoyen;
|
||||
|
||||
@Schema(description = "Âge minimum des membres trouvés")
|
||||
private int ageMin;
|
||||
|
||||
@Schema(description = "Âge maximum des membres trouvés")
|
||||
private int ageMax;
|
||||
|
||||
/** Répartition par organisation */
|
||||
@Schema(description = "Nombre d'organisations représentées")
|
||||
private long nombreOrganisations;
|
||||
|
||||
/** Répartition par région */
|
||||
@Schema(description = "Nombre de régions représentées")
|
||||
private long nombreRegions;
|
||||
|
||||
/** Ancienneté moyenne */
|
||||
@Schema(description = "Ancienneté moyenne en années")
|
||||
private double ancienneteMoyenne;
|
||||
}
|
||||
|
||||
/** Calcule et met à jour les indicateurs de pagination */
|
||||
public void calculatePaginationFlags() {
|
||||
this.isFirst = currentPage == 0;
|
||||
this.isLast = currentPage >= totalPages - 1;
|
||||
this.hasPrevious = currentPage > 0;
|
||||
this.hasNext = currentPage < totalPages - 1;
|
||||
this.numberOfElements = membres != null ? membres.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si les résultats sont vides
|
||||
*
|
||||
* @return true si aucun résultat
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return membres == null || membres.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le numéro de la page suivante (1-based pour affichage)
|
||||
*
|
||||
* @return Numéro de page suivante ou -1 si pas de page suivante
|
||||
*/
|
||||
public int getNextPageNumber() {
|
||||
return hasNext ? currentPage + 2 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le numéro de la page précédente (1-based pour affichage)
|
||||
*
|
||||
* @return Numéro de page précédente ou -1 si pas de page précédente
|
||||
*/
|
||||
public int getPreviousPageNumber() {
|
||||
return hasPrevious ? currentPage : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne une description textuelle des résultats
|
||||
*
|
||||
* @return Description des résultats
|
||||
*/
|
||||
public String getResultDescription() {
|
||||
if (isEmpty()) {
|
||||
return "Aucun membre trouvé";
|
||||
}
|
||||
|
||||
if (totalElements == 1) {
|
||||
return "1 membre trouvé";
|
||||
}
|
||||
|
||||
if (totalPages == 1) {
|
||||
return String.format("%d membres trouvés", totalElements);
|
||||
}
|
||||
|
||||
int startElement = currentPage * pageSize + 1;
|
||||
int endElement = Math.min(startElement + numberOfElements - 1, (int) totalElements);
|
||||
|
||||
return String.format(
|
||||
"Membres %d-%d sur %d (page %d/%d)",
|
||||
startElement, endElement, totalElements, currentPage + 1, totalPages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method pour créer un résultat vide
|
||||
*
|
||||
* @param criteria Critères de recherche
|
||||
* @return Résultat vide
|
||||
*/
|
||||
public static MembreSearchResultDTO empty(MembreSearchCriteria criteria) {
|
||||
MembreSearchResultDTO result = new MembreSearchResultDTO();
|
||||
result.setMembres(List.of());
|
||||
result.setTotalElements(0L);
|
||||
result.setTotalPages(0);
|
||||
result.setCurrentPage(0);
|
||||
result.setPageSize(20);
|
||||
result.setNumberOfElements(0);
|
||||
result.setHasNext(false);
|
||||
result.setHasPrevious(false);
|
||||
result.setFirst(true);
|
||||
result.setLast(true);
|
||||
result.setCriteria(criteria);
|
||||
result.setExecutionTimeMs(0L);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
package dev.lions.unionflow.server.api.dto.notification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* DTO pour les actions rapides des notifications UnionFlow
|
||||
*
|
||||
* <p>Ce DTO représente une action que l'utilisateur peut exécuter directement depuis la
|
||||
* notification sans ouvrir l'application.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ActionNotificationDTO {
|
||||
|
||||
/** Identifiant unique de l'action */
|
||||
@NotBlank(message = "L'identifiant de l'action est obligatoire")
|
||||
private String id;
|
||||
|
||||
/** Libellé affiché sur le bouton d'action */
|
||||
@NotBlank(message = "Le libellé de l'action est obligatoire")
|
||||
@Size(max = 30, message = "Le libellé ne peut pas dépasser 30 caractères")
|
||||
private String libelle;
|
||||
|
||||
/** Description de l'action (tooltip) */
|
||||
@Size(max = 100, message = "La description ne peut pas dépasser 100 caractères")
|
||||
private String description;
|
||||
|
||||
/** Type d'action à exécuter */
|
||||
@NotBlank(message = "Le type d'action est obligatoire")
|
||||
private String typeAction;
|
||||
|
||||
/** Icône de l'action (Material Design) */
|
||||
private String icone;
|
||||
|
||||
/** Couleur de l'action (hexadécimal) */
|
||||
private String couleur;
|
||||
|
||||
/** URL à ouvrir (pour les actions de type "url") */
|
||||
private String url;
|
||||
|
||||
/** Route de l'application à ouvrir (pour les actions de type "route") */
|
||||
private String route;
|
||||
|
||||
/** Paramètres de l'action */
|
||||
private Map<String, String> parametres;
|
||||
|
||||
/** Indique si l'action ferme la notification */
|
||||
private Boolean fermeNotification;
|
||||
|
||||
/** Indique si l'action nécessite une confirmation */
|
||||
private Boolean necessiteConfirmation;
|
||||
|
||||
/** Message de confirmation à afficher */
|
||||
private String messageConfirmation;
|
||||
|
||||
/** Indique si l'action est destructive (suppression, etc.) */
|
||||
private Boolean estDestructive;
|
||||
|
||||
/** Ordre d'affichage de l'action */
|
||||
private Integer ordre;
|
||||
|
||||
/** Indique si l'action est activée */
|
||||
private Boolean estActivee;
|
||||
|
||||
/** Condition d'affichage de l'action (expression) */
|
||||
private String conditionAffichage;
|
||||
|
||||
/** Rôles autorisés à exécuter cette action */
|
||||
private String[] rolesAutorises;
|
||||
|
||||
/** Permissions requises pour exécuter cette action */
|
||||
private String[] permissionsRequises;
|
||||
|
||||
/** Délai d'expiration de l'action en minutes */
|
||||
private Integer delaiExpirationMinutes;
|
||||
|
||||
/** Nombre maximum d'exécutions autorisées */
|
||||
private Integer maxExecutions;
|
||||
|
||||
/** Nombre d'exécutions actuelles */
|
||||
private Integer nombreExecutions;
|
||||
|
||||
/** Indique si l'action peut être exécutée plusieurs fois */
|
||||
private Boolean peutEtreRepetee;
|
||||
|
||||
/** Style du bouton (primary, secondary, outline, text) */
|
||||
private String styleBouton;
|
||||
|
||||
/** Taille du bouton (small, medium, large) */
|
||||
private String tailleBouton;
|
||||
|
||||
/** Position du bouton (left, center, right) */
|
||||
private String positionBouton;
|
||||
|
||||
/** Données personnalisées de l'action */
|
||||
private Map<String, Object> donneesPersonnalisees;
|
||||
|
||||
// === CONSTRUCTEURS ===
|
||||
|
||||
/** Constructeur par défaut */
|
||||
public ActionNotificationDTO() {
|
||||
this.fermeNotification = true;
|
||||
this.necessiteConfirmation = false;
|
||||
this.estDestructive = false;
|
||||
this.ordre = 0;
|
||||
this.estActivee = true;
|
||||
this.maxExecutions = 1;
|
||||
this.nombreExecutions = 0;
|
||||
this.peutEtreRepetee = false;
|
||||
this.styleBouton = "primary";
|
||||
this.tailleBouton = "medium";
|
||||
this.positionBouton = "right";
|
||||
}
|
||||
|
||||
/** Constructeur avec paramètres essentiels */
|
||||
public ActionNotificationDTO(String id, String libelle, String typeAction) {
|
||||
this();
|
||||
this.id = id;
|
||||
this.libelle = libelle;
|
||||
this.typeAction = typeAction;
|
||||
}
|
||||
|
||||
/** Constructeur pour action URL */
|
||||
public ActionNotificationDTO(String id, String libelle, String url, String icone) {
|
||||
this(id, libelle, "url");
|
||||
this.url = url;
|
||||
this.icone = icone;
|
||||
}
|
||||
|
||||
/** Constructeur pour action de route */
|
||||
public ActionNotificationDTO(
|
||||
String id, String libelle, String route, String icone, Map<String, String> parametres) {
|
||||
this(id, libelle, "route");
|
||||
this.route = route;
|
||||
this.icone = icone;
|
||||
this.parametres = parametres;
|
||||
}
|
||||
|
||||
// === GETTERS ET SETTERS ===
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public void setLibelle(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getTypeAction() {
|
||||
return typeAction;
|
||||
}
|
||||
|
||||
public void setTypeAction(String typeAction) {
|
||||
this.typeAction = typeAction;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public void setIcone(String icone) {
|
||||
this.icone = icone;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public void setCouleur(String couleur) {
|
||||
this.couleur = couleur;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
public void setRoute(String route) {
|
||||
this.route = route;
|
||||
}
|
||||
|
||||
public Map<String, String> getParametres() {
|
||||
return parametres;
|
||||
}
|
||||
|
||||
public void setParametres(Map<String, String> parametres) {
|
||||
this.parametres = parametres;
|
||||
}
|
||||
|
||||
public Boolean getFermeNotification() {
|
||||
return fermeNotification;
|
||||
}
|
||||
|
||||
public void setFermeNotification(Boolean fermeNotification) {
|
||||
this.fermeNotification = fermeNotification;
|
||||
}
|
||||
|
||||
public Boolean getNecessiteConfirmation() {
|
||||
return necessiteConfirmation;
|
||||
}
|
||||
|
||||
public void setNecessiteConfirmation(Boolean necessiteConfirmation) {
|
||||
this.necessiteConfirmation = necessiteConfirmation;
|
||||
}
|
||||
|
||||
public String getMessageConfirmation() {
|
||||
return messageConfirmation;
|
||||
}
|
||||
|
||||
public void setMessageConfirmation(String messageConfirmation) {
|
||||
this.messageConfirmation = messageConfirmation;
|
||||
}
|
||||
|
||||
public Boolean getEstDestructive() {
|
||||
return estDestructive;
|
||||
}
|
||||
|
||||
public void setEstDestructive(Boolean estDestructive) {
|
||||
this.estDestructive = estDestructive;
|
||||
}
|
||||
|
||||
public Integer getOrdre() {
|
||||
return ordre;
|
||||
}
|
||||
|
||||
public void setOrdre(Integer ordre) {
|
||||
this.ordre = ordre;
|
||||
}
|
||||
|
||||
public Boolean getEstActivee() {
|
||||
return estActivee;
|
||||
}
|
||||
|
||||
public void setEstActivee(Boolean estActivee) {
|
||||
this.estActivee = estActivee;
|
||||
}
|
||||
|
||||
public String getConditionAffichage() {
|
||||
return conditionAffichage;
|
||||
}
|
||||
|
||||
public void setConditionAffichage(String conditionAffichage) {
|
||||
this.conditionAffichage = conditionAffichage;
|
||||
}
|
||||
|
||||
public String[] getRolesAutorises() {
|
||||
return rolesAutorises;
|
||||
}
|
||||
|
||||
public void setRolesAutorises(String[] rolesAutorises) {
|
||||
this.rolesAutorises = rolesAutorises;
|
||||
}
|
||||
|
||||
public String[] getPermissionsRequises() {
|
||||
return permissionsRequises;
|
||||
}
|
||||
|
||||
public void setPermissionsRequises(String[] permissionsRequises) {
|
||||
this.permissionsRequises = permissionsRequises;
|
||||
}
|
||||
|
||||
public Integer getDelaiExpirationMinutes() {
|
||||
return delaiExpirationMinutes;
|
||||
}
|
||||
|
||||
public void setDelaiExpirationMinutes(Integer delaiExpirationMinutes) {
|
||||
this.delaiExpirationMinutes = delaiExpirationMinutes;
|
||||
}
|
||||
|
||||
public Integer getMaxExecutions() {
|
||||
return maxExecutions;
|
||||
}
|
||||
|
||||
public void setMaxExecutions(Integer maxExecutions) {
|
||||
this.maxExecutions = maxExecutions;
|
||||
}
|
||||
|
||||
public Integer getNombreExecutions() {
|
||||
return nombreExecutions;
|
||||
}
|
||||
|
||||
public void setNombreExecutions(Integer nombreExecutions) {
|
||||
this.nombreExecutions = nombreExecutions;
|
||||
}
|
||||
|
||||
public Boolean getPeutEtreRepetee() {
|
||||
return peutEtreRepetee;
|
||||
}
|
||||
|
||||
public void setPeutEtreRepetee(Boolean peutEtreRepetee) {
|
||||
this.peutEtreRepetee = peutEtreRepetee;
|
||||
}
|
||||
|
||||
public String getStyleBouton() {
|
||||
return styleBouton;
|
||||
}
|
||||
|
||||
public void setStyleBouton(String styleBouton) {
|
||||
this.styleBouton = styleBouton;
|
||||
}
|
||||
|
||||
public String getTailleBouton() {
|
||||
return tailleBouton;
|
||||
}
|
||||
|
||||
public void setTailleBouton(String tailleBouton) {
|
||||
this.tailleBouton = tailleBouton;
|
||||
}
|
||||
|
||||
public String getPositionBouton() {
|
||||
return positionBouton;
|
||||
}
|
||||
|
||||
public void setPositionBouton(String positionBouton) {
|
||||
this.positionBouton = positionBouton;
|
||||
}
|
||||
|
||||
public Map<String, Object> getDonneesPersonnalisees() {
|
||||
return donneesPersonnalisees;
|
||||
}
|
||||
|
||||
public void setDonneesPersonnalisees(Map<String, Object> donneesPersonnalisees) {
|
||||
this.donneesPersonnalisees = donneesPersonnalisees;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si l'action peut être exécutée */
|
||||
public boolean peutEtreExecutee() {
|
||||
return estActivee && (nombreExecutions < maxExecutions || peutEtreRepetee);
|
||||
}
|
||||
|
||||
/** Vérifie si l'action est expirée */
|
||||
public boolean isExpiree() {
|
||||
// Implémentation basée sur delaiExpirationMinutes et date de création de la notification
|
||||
return false; // À implémenter selon la logique métier
|
||||
}
|
||||
|
||||
/** Incrémente le nombre d'exécutions */
|
||||
public void incrementerExecutions() {
|
||||
if (nombreExecutions == null) {
|
||||
nombreExecutions = 0;
|
||||
}
|
||||
nombreExecutions++;
|
||||
}
|
||||
|
||||
/** Vérifie si l'utilisateur a les permissions requises */
|
||||
public boolean utilisateurAutorise(String[] rolesUtilisateur, String[] permissionsUtilisateur) {
|
||||
// Vérification des rôles
|
||||
if (rolesAutorises != null && rolesAutorises.length > 0) {
|
||||
boolean roleAutorise = false;
|
||||
for (String roleRequis : rolesAutorises) {
|
||||
for (String roleUtilisateur : rolesUtilisateur) {
|
||||
if (roleRequis.equals(roleUtilisateur)) {
|
||||
roleAutorise = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (roleAutorise) break;
|
||||
}
|
||||
if (!roleAutorise) return false;
|
||||
}
|
||||
|
||||
// Vérification des permissions
|
||||
if (permissionsRequises != null && permissionsRequises.length > 0) {
|
||||
boolean permissionAutorisee = false;
|
||||
for (String permissionRequise : permissionsRequises) {
|
||||
for (String permissionUtilisateur : permissionsUtilisateur) {
|
||||
if (permissionRequise.equals(permissionUtilisateur)) {
|
||||
permissionAutorisee = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (permissionAutorisee) break;
|
||||
}
|
||||
if (!permissionAutorisee) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Retourne la couleur par défaut selon le type d'action */
|
||||
public String getCouleurParDefaut() {
|
||||
if (couleur != null) return couleur;
|
||||
|
||||
return switch (typeAction) {
|
||||
case "confirm" -> "#4CAF50"; // Vert pour confirmation
|
||||
case "cancel" -> "#F44336"; // Rouge pour annulation
|
||||
case "info" -> "#2196F3"; // Bleu pour information
|
||||
case "warning" -> "#FF9800"; // Orange pour avertissement
|
||||
case "url", "route" -> "#2196F3"; // Bleu pour navigation
|
||||
default -> "#9E9E9E"; // Gris par défaut
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne l'icône par défaut selon le type d'action */
|
||||
public String getIconeParDefaut() {
|
||||
if (icone != null) return icone;
|
||||
|
||||
return switch (typeAction) {
|
||||
case "confirm" -> "check";
|
||||
case "cancel" -> "close";
|
||||
case "info" -> "info";
|
||||
case "warning" -> "warning";
|
||||
case "url" -> "open_in_new";
|
||||
case "route" -> "arrow_forward";
|
||||
case "call" -> "phone";
|
||||
case "message" -> "message";
|
||||
case "email" -> "email";
|
||||
case "share" -> "share";
|
||||
default -> "touch_app";
|
||||
};
|
||||
}
|
||||
|
||||
/** Crée une action de confirmation */
|
||||
public static ActionNotificationDTO creerActionConfirmation(String id, String libelle) {
|
||||
ActionNotificationDTO action = new ActionNotificationDTO(id, libelle, "confirm");
|
||||
action.setCouleur("#4CAF50");
|
||||
action.setIcone("check");
|
||||
action.setStyleBouton("primary");
|
||||
return action;
|
||||
}
|
||||
|
||||
/** Crée une action d'annulation */
|
||||
public static ActionNotificationDTO creerActionAnnulation(String id, String libelle) {
|
||||
ActionNotificationDTO action = new ActionNotificationDTO(id, libelle, "cancel");
|
||||
action.setCouleur("#F44336");
|
||||
action.setIcone("close");
|
||||
action.setStyleBouton("outline");
|
||||
action.setEstDestructive(true);
|
||||
return action;
|
||||
}
|
||||
|
||||
/** Crée une action de navigation */
|
||||
public static ActionNotificationDTO creerActionNavigation(
|
||||
String id, String libelle, String route) {
|
||||
ActionNotificationDTO action = new ActionNotificationDTO(id, libelle, "route");
|
||||
action.setRoute(route);
|
||||
action.setCouleur("#2196F3");
|
||||
action.setIcone("arrow_forward");
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"ActionNotificationDTO{id='%s', libelle='%s', type='%s'}", id, libelle, typeAction);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package dev.lions.unionflow.server.api.dto.notification;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.notification.PrioriteNotification;
|
||||
import dev.lions.unionflow.server.api.enums.notification.StatutNotification;
|
||||
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des notifications
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class NotificationDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Type de notification */
|
||||
@NotNull(message = "Le type de notification est obligatoire")
|
||||
private TypeNotification typeNotification;
|
||||
|
||||
/** Priorité */
|
||||
private PrioriteNotification priorite;
|
||||
|
||||
/** Statut */
|
||||
private StatutNotification statut;
|
||||
|
||||
/** Sujet */
|
||||
private String sujet;
|
||||
|
||||
/** Corps du message */
|
||||
private String corps;
|
||||
|
||||
/** Date d'envoi prévue */
|
||||
private LocalDateTime dateEnvoiPrevue;
|
||||
|
||||
/** Date d'envoi réelle */
|
||||
private LocalDateTime dateEnvoi;
|
||||
|
||||
/** Date de lecture */
|
||||
private LocalDateTime dateLecture;
|
||||
|
||||
/** Nombre de tentatives */
|
||||
private Integer nombreTentatives;
|
||||
|
||||
/** Message d'erreur */
|
||||
private String messageErreur;
|
||||
|
||||
/** Données additionnelles (JSON) */
|
||||
private String donneesAdditionnelles;
|
||||
|
||||
/** ID du membre */
|
||||
private UUID membreId;
|
||||
|
||||
/** ID de l'organisation */
|
||||
private UUID organisationId;
|
||||
|
||||
/** ID du template */
|
||||
private UUID templateId;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package dev.lions.unionflow.server.api.dto.notification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* DTO pour les préférences spécifiques à un canal de notification
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class PreferenceCanalNotificationDTO {
|
||||
|
||||
/** Indique si ce canal est activé */
|
||||
private Boolean active;
|
||||
|
||||
/** Niveau d'importance personnalisé (1-5) */
|
||||
@Min(value = 1, message = "L'importance doit être comprise entre 1 et 5")
|
||||
@Max(value = 5, message = "L'importance doit être comprise entre 1 et 5")
|
||||
private Integer importance;
|
||||
|
||||
/** Son personnalisé pour ce canal */
|
||||
private String sonPersonnalise;
|
||||
|
||||
/** Pattern de vibration personnalisé */
|
||||
private long[] patternVibration;
|
||||
|
||||
/** Couleur LED personnalisée */
|
||||
private String couleurLED;
|
||||
|
||||
/** Indique si le son est activé pour ce canal */
|
||||
private Boolean sonActive;
|
||||
|
||||
/** Indique si la vibration est activée pour ce canal */
|
||||
private Boolean vibrationActive;
|
||||
|
||||
/** Indique si la LED est activée pour ce canal */
|
||||
private Boolean ledActive;
|
||||
|
||||
/** Indique si ce canal peut être désactivé par l'utilisateur */
|
||||
private Boolean peutEtreDesactive;
|
||||
|
||||
// Constructeurs, getters et setters
|
||||
public PreferenceCanalNotificationDTO() {}
|
||||
|
||||
public Boolean getActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(Boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public Integer getImportance() {
|
||||
return importance;
|
||||
}
|
||||
|
||||
public void setImportance(Integer importance) {
|
||||
this.importance = importance;
|
||||
}
|
||||
|
||||
public String getSonPersonnalise() {
|
||||
return sonPersonnalise;
|
||||
}
|
||||
|
||||
public void setSonPersonnalise(String sonPersonnalise) {
|
||||
this.sonPersonnalise = sonPersonnalise;
|
||||
}
|
||||
|
||||
public long[] getPatternVibration() {
|
||||
return patternVibration;
|
||||
}
|
||||
|
||||
public void setPatternVibration(long[] patternVibration) {
|
||||
this.patternVibration = patternVibration;
|
||||
}
|
||||
|
||||
public String getCouleurLED() {
|
||||
return couleurLED;
|
||||
}
|
||||
|
||||
public void setCouleurLED(String couleurLED) {
|
||||
this.couleurLED = couleurLED;
|
||||
}
|
||||
|
||||
public Boolean getSonActive() {
|
||||
return sonActive;
|
||||
}
|
||||
|
||||
public void setSonActive(Boolean sonActive) {
|
||||
this.sonActive = sonActive;
|
||||
}
|
||||
|
||||
public Boolean getVibrationActive() {
|
||||
return vibrationActive;
|
||||
}
|
||||
|
||||
public void setVibrationActive(Boolean vibrationActive) {
|
||||
this.vibrationActive = vibrationActive;
|
||||
}
|
||||
|
||||
public Boolean getLedActive() {
|
||||
return ledActive;
|
||||
}
|
||||
|
||||
public void setLedActive(Boolean ledActive) {
|
||||
this.ledActive = ledActive;
|
||||
}
|
||||
|
||||
public Boolean getPeutEtreDesactive() {
|
||||
return peutEtreDesactive;
|
||||
}
|
||||
|
||||
public void setPeutEtreDesactive(Boolean peutEtreDesactive) {
|
||||
this.peutEtreDesactive = peutEtreDesactive;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package dev.lions.unionflow.server.api.dto.notification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* DTO pour les préférences spécifiques à un type de notification
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class PreferenceTypeNotificationDTO {
|
||||
|
||||
/** Indique si ce type de notification est activé */
|
||||
private Boolean active;
|
||||
|
||||
/** Priorité personnalisée (1-5) */
|
||||
@Min(value = 1, message = "La priorité doit être comprise entre 1 et 5")
|
||||
@Max(value = 5, message = "La priorité doit être comprise entre 1 et 5")
|
||||
private Integer priorite;
|
||||
|
||||
/** Son personnalisé pour ce type */
|
||||
private String sonPersonnalise;
|
||||
|
||||
/** Pattern de vibration personnalisé */
|
||||
private long[] patternVibration;
|
||||
|
||||
/** Couleur LED personnalisée */
|
||||
private String couleurLED;
|
||||
|
||||
/** Durée d'affichage personnalisée (secondes) */
|
||||
@Min(value = 1, message = "La durée d'affichage doit être au moins 1 seconde")
|
||||
@Max(value = 300, message = "La durée d'affichage ne peut pas dépasser 5 minutes")
|
||||
private Integer dureeAffichageSecondes;
|
||||
|
||||
/** Indique si les notifications de ce type doivent vibrer */
|
||||
private Boolean doitVibrer;
|
||||
|
||||
/** Indique si les notifications de ce type doivent émettre un son */
|
||||
private Boolean doitEmettreSon;
|
||||
|
||||
/** Indique si les notifications de ce type doivent allumer la LED */
|
||||
private Boolean doitAllumerLED;
|
||||
|
||||
/** Indique si ce type ignore le mode silencieux */
|
||||
private Boolean ignoreModesilencieux;
|
||||
|
||||
// Constructeurs, getters et setters
|
||||
public PreferenceTypeNotificationDTO() {}
|
||||
|
||||
public Boolean getActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(Boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public Integer getPriorite() {
|
||||
return priorite;
|
||||
}
|
||||
|
||||
public void setPriorite(Integer priorite) {
|
||||
this.priorite = priorite;
|
||||
}
|
||||
|
||||
public String getSonPersonnalise() {
|
||||
return sonPersonnalise;
|
||||
}
|
||||
|
||||
public void setSonPersonnalise(String sonPersonnalise) {
|
||||
this.sonPersonnalise = sonPersonnalise;
|
||||
}
|
||||
|
||||
public long[] getPatternVibration() {
|
||||
return patternVibration;
|
||||
}
|
||||
|
||||
public void setPatternVibration(long[] patternVibration) {
|
||||
this.patternVibration = patternVibration;
|
||||
}
|
||||
|
||||
public String getCouleurLED() {
|
||||
return couleurLED;
|
||||
}
|
||||
|
||||
public void setCouleurLED(String couleurLED) {
|
||||
this.couleurLED = couleurLED;
|
||||
}
|
||||
|
||||
public Integer getDureeAffichageSecondes() {
|
||||
return dureeAffichageSecondes;
|
||||
}
|
||||
|
||||
public void setDureeAffichageSecondes(Integer dureeAffichageSecondes) {
|
||||
this.dureeAffichageSecondes = dureeAffichageSecondes;
|
||||
}
|
||||
|
||||
public Boolean getDoitVibrer() {
|
||||
return doitVibrer;
|
||||
}
|
||||
|
||||
public void setDoitVibrer(Boolean doitVibrer) {
|
||||
this.doitVibrer = doitVibrer;
|
||||
}
|
||||
|
||||
public Boolean getDoitEmettreSon() {
|
||||
return doitEmettreSon;
|
||||
}
|
||||
|
||||
public void setDoitEmettreSon(Boolean doitEmettreSon) {
|
||||
this.doitEmettreSon = doitEmettreSon;
|
||||
}
|
||||
|
||||
public Boolean getDoitAllumerLED() {
|
||||
return doitAllumerLED;
|
||||
}
|
||||
|
||||
public void setDoitAllumerLED(Boolean doitAllumerLED) {
|
||||
this.doitAllumerLED = doitAllumerLED;
|
||||
}
|
||||
|
||||
public Boolean getIgnoreModeSilencieux() {
|
||||
return ignoreModesilencieux;
|
||||
}
|
||||
|
||||
public void setIgnoreModeSilencieux(Boolean ignoreModesilencieux) {
|
||||
this.ignoreModesilencieux = ignoreModesilencieux;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
package dev.lions.unionflow.server.api.dto.notification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import dev.lions.unionflow.server.api.enums.notification.CanalNotification;
|
||||
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* DTO pour les préférences de notification d'un utilisateur
|
||||
*
|
||||
* <p>Ce DTO représente les préférences personnalisées d'un utilisateur concernant la réception et
|
||||
* l'affichage des notifications.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class PreferencesNotificationDTO {
|
||||
|
||||
/** Identifiant unique des préférences */
|
||||
private String id;
|
||||
|
||||
/** Identifiant de l'utilisateur */
|
||||
@NotBlank(message = "L'identifiant utilisateur est obligatoire")
|
||||
private String utilisateurId;
|
||||
|
||||
/** Identifiant de l'organisation */
|
||||
private String organisationId;
|
||||
|
||||
/** Indique si les notifications sont activées globalement */
|
||||
@NotNull(message = "L'activation globale des notifications est obligatoire")
|
||||
private Boolean notificationsActivees;
|
||||
|
||||
/** Indique si les notifications push sont activées */
|
||||
private Boolean pushActivees;
|
||||
|
||||
/** Indique si les notifications par email sont activées */
|
||||
private Boolean emailActivees;
|
||||
|
||||
/** Indique si les notifications SMS sont activées */
|
||||
private Boolean smsActivees;
|
||||
|
||||
/** Indique si les notifications in-app sont activées */
|
||||
private Boolean inAppActivees;
|
||||
|
||||
/** Types de notifications activés */
|
||||
private Set<TypeNotification> typesActives;
|
||||
|
||||
/** Types de notifications désactivés */
|
||||
private Set<TypeNotification> typesDesactivees;
|
||||
|
||||
/** Canaux de notification activés */
|
||||
private Set<CanalNotification> canauxActifs;
|
||||
|
||||
/** Canaux de notification désactivés */
|
||||
private Set<CanalNotification> canauxDesactives;
|
||||
|
||||
/** Mode Ne Pas Déranger activé */
|
||||
private Boolean modeSilencieux;
|
||||
|
||||
/** Heure de début du mode silencieux */
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime heureDebutSilencieux;
|
||||
|
||||
/** Heure de fin du mode silencieux */
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
private LocalTime heureFinSilencieux;
|
||||
|
||||
/** Jours de la semaine pour le mode silencieux (1=Lundi, 7=Dimanche) */
|
||||
private Set<Integer> joursSilencieux;
|
||||
|
||||
/** Indique si les notifications urgentes passent outre le mode silencieux */
|
||||
private Boolean urgentesIgnorentSilencieux;
|
||||
|
||||
/** Fréquence de regroupement des notifications (minutes) */
|
||||
@Min(value = 0, message = "La fréquence de regroupement doit être positive")
|
||||
@Max(value = 1440, message = "La fréquence de regroupement ne peut pas dépasser 24h")
|
||||
private Integer frequenceRegroupementMinutes;
|
||||
|
||||
/** Nombre maximum de notifications affichées simultanément */
|
||||
@Min(value = 1, message = "Le nombre maximum de notifications doit être au moins 1")
|
||||
@Max(value = 50, message = "Le nombre maximum de notifications ne peut pas dépasser 50")
|
||||
private Integer maxNotificationsSimultanees;
|
||||
|
||||
/** Durée d'affichage par défaut des notifications (secondes) */
|
||||
@Min(value = 1, message = "La durée d'affichage doit être au moins 1 seconde")
|
||||
@Max(value = 300, message = "La durée d'affichage ne peut pas dépasser 5 minutes")
|
||||
private Integer dureeAffichageSecondes;
|
||||
|
||||
/** Indique si les notifications doivent vibrer */
|
||||
private Boolean vibrationActivee;
|
||||
|
||||
/** Indique si les notifications doivent émettre un son */
|
||||
private Boolean sonActive;
|
||||
|
||||
/** Indique si la LED doit s'allumer */
|
||||
private Boolean ledActivee;
|
||||
|
||||
/** Son personnalisé pour les notifications */
|
||||
private String sonPersonnalise;
|
||||
|
||||
/** Pattern de vibration personnalisé */
|
||||
private long[] patternVibrationPersonnalise;
|
||||
|
||||
/** Couleur de LED personnalisée */
|
||||
private String couleurLEDPersonnalisee;
|
||||
|
||||
/** Indique si les aperçus de contenu sont affichés sur l'écran de verrouillage */
|
||||
private Boolean apercuEcranVerrouillage;
|
||||
|
||||
/** Indique si les notifications sont affichées dans l'historique */
|
||||
private Boolean affichageHistorique;
|
||||
|
||||
/** Durée de conservation dans l'historique (jours) */
|
||||
@Min(value = 1, message = "La durée de conservation doit être au moins 1 jour")
|
||||
@Max(value = 365, message = "La durée de conservation ne peut pas dépasser 1 an")
|
||||
private Integer dureeConservationJours;
|
||||
|
||||
/** Indique si les notifications sont automatiquement marquées comme lues */
|
||||
private Boolean marquageLectureAutomatique;
|
||||
|
||||
/** Délai avant marquage automatique comme lu (secondes) */
|
||||
private Integer delaiMarquageLectureSecondes;
|
||||
|
||||
/** Indique si les notifications sont automatiquement archivées */
|
||||
private Boolean archivageAutomatique;
|
||||
|
||||
/** Délai avant archivage automatique (heures) */
|
||||
private Integer delaiArchivageHeures;
|
||||
|
||||
/** Préférences par type de notification */
|
||||
private Map<TypeNotification, PreferenceTypeNotificationDTO> preferencesParType;
|
||||
|
||||
/** Préférences par canal de notification */
|
||||
private Map<CanalNotification, PreferenceCanalNotificationDTO> preferencesParCanal;
|
||||
|
||||
/** Mots-clés pour filtrage automatique */
|
||||
private Set<String> motsClesFiltre;
|
||||
|
||||
/** Expéditeurs bloqués */
|
||||
private Set<String> expediteursBloqués;
|
||||
|
||||
/** Expéditeurs prioritaires */
|
||||
private Set<String> expediteursPrioritaires;
|
||||
|
||||
/** Indique si les notifications de test sont activées */
|
||||
private Boolean notificationsTestActivees;
|
||||
|
||||
/** Niveau de log pour les notifications (DEBUG, INFO, WARN, ERROR) */
|
||||
private String niveauLog;
|
||||
|
||||
/** Token FCM pour les notifications push */
|
||||
private String tokenFCM;
|
||||
|
||||
/** Plateforme de l'appareil (android, ios, web) */
|
||||
private String plateforme;
|
||||
|
||||
/** Version de l'application */
|
||||
private String versionApp;
|
||||
|
||||
/** Langue préférée pour les notifications */
|
||||
private String langue;
|
||||
|
||||
/** Fuseau horaire de l'utilisateur */
|
||||
private String fuseauHoraire;
|
||||
|
||||
/** Métadonnées personnalisées */
|
||||
private Map<String, Object> metadonnees;
|
||||
|
||||
// === CONSTRUCTEURS ===
|
||||
|
||||
/** Constructeur par défaut avec valeurs par défaut */
|
||||
public PreferencesNotificationDTO() {
|
||||
this.notificationsActivees = true;
|
||||
this.pushActivees = true;
|
||||
this.emailActivees = true;
|
||||
this.smsActivees = false;
|
||||
this.inAppActivees = true;
|
||||
this.modeSilencieux = false;
|
||||
this.urgentesIgnorentSilencieux = true;
|
||||
this.frequenceRegroupementMinutes = 5;
|
||||
this.maxNotificationsSimultanees = 10;
|
||||
this.dureeAffichageSecondes = 10;
|
||||
this.vibrationActivee = true;
|
||||
this.sonActive = true;
|
||||
this.ledActivee = true;
|
||||
this.apercuEcranVerrouillage = true;
|
||||
this.affichageHistorique = true;
|
||||
this.dureeConservationJours = 30;
|
||||
this.marquageLectureAutomatique = false;
|
||||
this.archivageAutomatique = true;
|
||||
this.delaiArchivageHeures = 168; // 1 semaine
|
||||
this.notificationsTestActivees = false;
|
||||
this.niveauLog = "INFO";
|
||||
this.langue = "fr";
|
||||
}
|
||||
|
||||
/** Constructeur avec utilisateur */
|
||||
public PreferencesNotificationDTO(String utilisateurId) {
|
||||
this();
|
||||
this.utilisateurId = utilisateurId;
|
||||
}
|
||||
|
||||
// === GETTERS ET SETTERS ===
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUtilisateurId() {
|
||||
return utilisateurId;
|
||||
}
|
||||
|
||||
public void setUtilisateurId(String utilisateurId) {
|
||||
this.utilisateurId = utilisateurId;
|
||||
}
|
||||
|
||||
public String getOrganisationId() {
|
||||
return organisationId;
|
||||
}
|
||||
|
||||
public void setOrganisationId(String organisationId) {
|
||||
this.organisationId = organisationId;
|
||||
}
|
||||
|
||||
public Boolean getNotificationsActivees() {
|
||||
return notificationsActivees;
|
||||
}
|
||||
|
||||
public void setNotificationsActivees(Boolean notificationsActivees) {
|
||||
this.notificationsActivees = notificationsActivees;
|
||||
}
|
||||
|
||||
public Boolean getPushActivees() {
|
||||
return pushActivees;
|
||||
}
|
||||
|
||||
public void setPushActivees(Boolean pushActivees) {
|
||||
this.pushActivees = pushActivees;
|
||||
}
|
||||
|
||||
public Boolean getEmailActivees() {
|
||||
return emailActivees;
|
||||
}
|
||||
|
||||
public void setEmailActivees(Boolean emailActivees) {
|
||||
this.emailActivees = emailActivees;
|
||||
}
|
||||
|
||||
public Boolean getSmsActivees() {
|
||||
return smsActivees;
|
||||
}
|
||||
|
||||
public void setSmsActivees(Boolean smsActivees) {
|
||||
this.smsActivees = smsActivees;
|
||||
}
|
||||
|
||||
public Boolean getInAppActivees() {
|
||||
return inAppActivees;
|
||||
}
|
||||
|
||||
public void setInAppActivees(Boolean inAppActivees) {
|
||||
this.inAppActivees = inAppActivees;
|
||||
}
|
||||
|
||||
public Set<TypeNotification> getTypesActives() {
|
||||
return typesActives;
|
||||
}
|
||||
|
||||
public void setTypesActives(Set<TypeNotification> typesActives) {
|
||||
this.typesActives = typesActives;
|
||||
}
|
||||
|
||||
public Set<TypeNotification> getTypesDesactivees() {
|
||||
return typesDesactivees;
|
||||
}
|
||||
|
||||
public void setTypesDesactivees(Set<TypeNotification> typesDesactivees) {
|
||||
this.typesDesactivees = typesDesactivees;
|
||||
}
|
||||
|
||||
public Set<CanalNotification> getCanauxActifs() {
|
||||
return canauxActifs;
|
||||
}
|
||||
|
||||
public void setCanauxActifs(Set<CanalNotification> canauxActifs) {
|
||||
this.canauxActifs = canauxActifs;
|
||||
}
|
||||
|
||||
public Set<CanalNotification> getCanauxDesactives() {
|
||||
return canauxDesactives;
|
||||
}
|
||||
|
||||
public void setCanauxDesactives(Set<CanalNotification> canauxDesactives) {
|
||||
this.canauxDesactives = canauxDesactives;
|
||||
}
|
||||
|
||||
public Boolean getModeSilencieux() {
|
||||
return modeSilencieux;
|
||||
}
|
||||
|
||||
public void setModeSilencieux(Boolean modeSilencieux) {
|
||||
this.modeSilencieux = modeSilencieux;
|
||||
}
|
||||
|
||||
public LocalTime getHeureDebutSilencieux() {
|
||||
return heureDebutSilencieux;
|
||||
}
|
||||
|
||||
public void setHeureDebutSilencieux(LocalTime heureDebutSilencieux) {
|
||||
this.heureDebutSilencieux = heureDebutSilencieux;
|
||||
}
|
||||
|
||||
public LocalTime getHeureFinSilencieux() {
|
||||
return heureFinSilencieux;
|
||||
}
|
||||
|
||||
public void setHeureFinSilencieux(LocalTime heureFinSilencieux) {
|
||||
this.heureFinSilencieux = heureFinSilencieux;
|
||||
}
|
||||
|
||||
public Set<Integer> getJoursSilencieux() {
|
||||
return joursSilencieux;
|
||||
}
|
||||
|
||||
public void setJoursSilencieux(Set<Integer> joursSilencieux) {
|
||||
this.joursSilencieux = joursSilencieux;
|
||||
}
|
||||
|
||||
public Boolean getUrgentesIgnorentSilencieux() {
|
||||
return urgentesIgnorentSilencieux;
|
||||
}
|
||||
|
||||
public void setUrgentesIgnorentSilencieux(Boolean urgentesIgnorentSilencieux) {
|
||||
this.urgentesIgnorentSilencieux = urgentesIgnorentSilencieux;
|
||||
}
|
||||
|
||||
public Integer getFrequenceRegroupementMinutes() {
|
||||
return frequenceRegroupementMinutes;
|
||||
}
|
||||
|
||||
public void setFrequenceRegroupementMinutes(Integer frequenceRegroupementMinutes) {
|
||||
this.frequenceRegroupementMinutes = frequenceRegroupementMinutes;
|
||||
}
|
||||
|
||||
public Integer getMaxNotificationsSimultanees() {
|
||||
return maxNotificationsSimultanees;
|
||||
}
|
||||
|
||||
public void setMaxNotificationsSimultanees(Integer maxNotificationsSimultanees) {
|
||||
this.maxNotificationsSimultanees = maxNotificationsSimultanees;
|
||||
}
|
||||
|
||||
public Integer getDureeAffichageSecondes() {
|
||||
return dureeAffichageSecondes;
|
||||
}
|
||||
|
||||
public void setDureeAffichageSecondes(Integer dureeAffichageSecondes) {
|
||||
this.dureeAffichageSecondes = dureeAffichageSecondes;
|
||||
}
|
||||
|
||||
public Boolean getVibrationActivee() {
|
||||
return vibrationActivee;
|
||||
}
|
||||
|
||||
public void setVibrationActivee(Boolean vibrationActivee) {
|
||||
this.vibrationActivee = vibrationActivee;
|
||||
}
|
||||
|
||||
public Boolean getSonActive() {
|
||||
return sonActive;
|
||||
}
|
||||
|
||||
public void setSonActive(Boolean sonActive) {
|
||||
this.sonActive = sonActive;
|
||||
}
|
||||
|
||||
public Boolean getLedActivee() {
|
||||
return ledActivee;
|
||||
}
|
||||
|
||||
public void setLedActivee(Boolean ledActivee) {
|
||||
this.ledActivee = ledActivee;
|
||||
}
|
||||
|
||||
public String getSonPersonnalise() {
|
||||
return sonPersonnalise;
|
||||
}
|
||||
|
||||
public void setSonPersonnalise(String sonPersonnalise) {
|
||||
this.sonPersonnalise = sonPersonnalise;
|
||||
}
|
||||
|
||||
public long[] getPatternVibrationPersonnalise() {
|
||||
return patternVibrationPersonnalise;
|
||||
}
|
||||
|
||||
public void setPatternVibrationPersonnalise(long[] patternVibrationPersonnalise) {
|
||||
this.patternVibrationPersonnalise = patternVibrationPersonnalise;
|
||||
}
|
||||
|
||||
public String getCouleurLEDPersonnalisee() {
|
||||
return couleurLEDPersonnalisee;
|
||||
}
|
||||
|
||||
public void setCouleurLEDPersonnalisee(String couleurLEDPersonnalisee) {
|
||||
this.couleurLEDPersonnalisee = couleurLEDPersonnalisee;
|
||||
}
|
||||
|
||||
public Boolean getApercuEcranVerrouillage() {
|
||||
return apercuEcranVerrouillage;
|
||||
}
|
||||
|
||||
public void setApercuEcranVerrouillage(Boolean apercuEcranVerrouillage) {
|
||||
this.apercuEcranVerrouillage = apercuEcranVerrouillage;
|
||||
}
|
||||
|
||||
public Boolean getAffichageHistorique() {
|
||||
return affichageHistorique;
|
||||
}
|
||||
|
||||
public void setAffichageHistorique(Boolean affichageHistorique) {
|
||||
this.affichageHistorique = affichageHistorique;
|
||||
}
|
||||
|
||||
public Integer getDureeConservationJours() {
|
||||
return dureeConservationJours;
|
||||
}
|
||||
|
||||
public void setDureeConservationJours(Integer dureeConservationJours) {
|
||||
this.dureeConservationJours = dureeConservationJours;
|
||||
}
|
||||
|
||||
public Boolean getMarquageLectureAutomatique() {
|
||||
return marquageLectureAutomatique;
|
||||
}
|
||||
|
||||
public void setMarquageLectureAutomatique(Boolean marquageLectureAutomatique) {
|
||||
this.marquageLectureAutomatique = marquageLectureAutomatique;
|
||||
}
|
||||
|
||||
public Integer getDelaiMarquageLectureSecondes() {
|
||||
return delaiMarquageLectureSecondes;
|
||||
}
|
||||
|
||||
public void setDelaiMarquageLectureSecondes(Integer delaiMarquageLectureSecondes) {
|
||||
this.delaiMarquageLectureSecondes = delaiMarquageLectureSecondes;
|
||||
}
|
||||
|
||||
public Boolean getArchivageAutomatique() {
|
||||
return archivageAutomatique;
|
||||
}
|
||||
|
||||
public void setArchivageAutomatique(Boolean archivageAutomatique) {
|
||||
this.archivageAutomatique = archivageAutomatique;
|
||||
}
|
||||
|
||||
public Integer getDelaiArchivageHeures() {
|
||||
return delaiArchivageHeures;
|
||||
}
|
||||
|
||||
public void setDelaiArchivageHeures(Integer delaiArchivageHeures) {
|
||||
this.delaiArchivageHeures = delaiArchivageHeures;
|
||||
}
|
||||
|
||||
public Map<TypeNotification, PreferenceTypeNotificationDTO> getPreferencesParType() {
|
||||
return preferencesParType;
|
||||
}
|
||||
|
||||
public void setPreferencesParType(
|
||||
Map<TypeNotification, PreferenceTypeNotificationDTO> preferencesParType) {
|
||||
this.preferencesParType = preferencesParType;
|
||||
}
|
||||
|
||||
public Map<CanalNotification, PreferenceCanalNotificationDTO> getPreferencesParCanal() {
|
||||
return preferencesParCanal;
|
||||
}
|
||||
|
||||
public void setPreferencesParCanal(
|
||||
Map<CanalNotification, PreferenceCanalNotificationDTO> preferencesParCanal) {
|
||||
this.preferencesParCanal = preferencesParCanal;
|
||||
}
|
||||
|
||||
public Set<String> getMotsClesFiltre() {
|
||||
return motsClesFiltre;
|
||||
}
|
||||
|
||||
public void setMotsClesFiltre(Set<String> motsClesFiltre) {
|
||||
this.motsClesFiltre = motsClesFiltre;
|
||||
}
|
||||
|
||||
public Set<String> getExpediteursBloques() {
|
||||
return expediteursBloqués;
|
||||
}
|
||||
|
||||
public void setExpediteursBloques(Set<String> expediteursBloqués) {
|
||||
this.expediteursBloqués = expediteursBloqués;
|
||||
}
|
||||
|
||||
public Set<String> getExpediteursPrioritaires() {
|
||||
return expediteursPrioritaires;
|
||||
}
|
||||
|
||||
public void setExpediteursPrioritaires(Set<String> expediteursPrioritaires) {
|
||||
this.expediteursPrioritaires = expediteursPrioritaires;
|
||||
}
|
||||
|
||||
public Boolean getNotificationsTestActivees() {
|
||||
return notificationsTestActivees;
|
||||
}
|
||||
|
||||
public void setNotificationsTestActivees(Boolean notificationsTestActivees) {
|
||||
this.notificationsTestActivees = notificationsTestActivees;
|
||||
}
|
||||
|
||||
public String getNiveauLog() {
|
||||
return niveauLog;
|
||||
}
|
||||
|
||||
public void setNiveauLog(String niveauLog) {
|
||||
this.niveauLog = niveauLog;
|
||||
}
|
||||
|
||||
public String getTokenFCM() {
|
||||
return tokenFCM;
|
||||
}
|
||||
|
||||
public void setTokenFCM(String tokenFCM) {
|
||||
this.tokenFCM = tokenFCM;
|
||||
}
|
||||
|
||||
public String getPlateforme() {
|
||||
return plateforme;
|
||||
}
|
||||
|
||||
public void setPlateforme(String plateforme) {
|
||||
this.plateforme = plateforme;
|
||||
}
|
||||
|
||||
public String getVersionApp() {
|
||||
return versionApp;
|
||||
}
|
||||
|
||||
public void setVersionApp(String versionApp) {
|
||||
this.versionApp = versionApp;
|
||||
}
|
||||
|
||||
public String getLangue() {
|
||||
return langue;
|
||||
}
|
||||
|
||||
public void setLangue(String langue) {
|
||||
this.langue = langue;
|
||||
}
|
||||
|
||||
public String getFuseauHoraire() {
|
||||
return fuseauHoraire;
|
||||
}
|
||||
|
||||
public void setFuseauHoraire(String fuseauHoraire) {
|
||||
this.fuseauHoraire = fuseauHoraire;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMetadonnees() {
|
||||
return metadonnees;
|
||||
}
|
||||
|
||||
public void setMetadonnees(Map<String, Object> metadonnees) {
|
||||
this.metadonnees = metadonnees;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si un type de notification est activé */
|
||||
public boolean isTypeActive(TypeNotification type) {
|
||||
if (!notificationsActivees) return false;
|
||||
if (typesDesactivees != null && typesDesactivees.contains(type)) return false;
|
||||
if (typesActives != null) return typesActives.contains(type);
|
||||
return type.isActiveeParDefaut();
|
||||
}
|
||||
|
||||
/** Vérifie si un canal de notification est activé */
|
||||
public boolean isCanalActif(CanalNotification canal) {
|
||||
if (!notificationsActivees) return false;
|
||||
if (canauxDesactives != null && canauxDesactives.contains(canal)) return false;
|
||||
if (canauxActifs != null) return canauxActifs.contains(canal);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Version de test de isEnModeSilencieux avec une heure fixe
|
||||
* Permet de tester toutes les branches sans dépendre de l'heure actuelle
|
||||
*/
|
||||
boolean isEnModeSilencieux(LocalTime heureActuelle) {
|
||||
if (!modeSilencieux) return false;
|
||||
if (heureDebutSilencieux == null || heureFinSilencieux == null) return false;
|
||||
|
||||
// Gestion du cas où la période traverse minuit
|
||||
if (heureDebutSilencieux.isAfter(heureFinSilencieux)) {
|
||||
return heureActuelle.isAfter(heureDebutSilencieux) || heureActuelle.isBefore(heureFinSilencieux);
|
||||
} else {
|
||||
return heureActuelle.isAfter(heureDebutSilencieux) && heureActuelle.isBefore(heureFinSilencieux);
|
||||
}
|
||||
}
|
||||
|
||||
/** Vérifie si on est en mode silencieux actuellement */
|
||||
public boolean isEnModeSilencieux() {
|
||||
return isEnModeSilencieux(LocalTime.now());
|
||||
}
|
||||
|
||||
/** Vérifie si un expéditeur est bloqué */
|
||||
public boolean isExpediteurBloque(String expediteurId) {
|
||||
return expediteursBloqués != null && expediteursBloqués.contains(expediteurId);
|
||||
}
|
||||
|
||||
/** Vérifie si un expéditeur est prioritaire */
|
||||
public boolean isExpediteurPrioritaire(String expediteurId) {
|
||||
return expediteursPrioritaires != null && expediteursPrioritaires.contains(expediteurId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"PreferencesNotificationDTO{utilisateurId='%s', notificationsActivees=%s}",
|
||||
utilisateurId, notificationsActivees);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package dev.lions.unionflow.server.api.dto.notification;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des templates de notifications
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TemplateNotificationDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Code unique du template */
|
||||
@NotBlank(message = "Le code est obligatoire")
|
||||
private String code;
|
||||
|
||||
/** Sujet du template */
|
||||
private String sujet;
|
||||
|
||||
/** Corps du template (texte) */
|
||||
private String corpsTexte;
|
||||
|
||||
/** Corps du template (HTML) */
|
||||
private String corpsHtml;
|
||||
|
||||
/** Variables disponibles (JSON) */
|
||||
private String variablesDisponibles;
|
||||
|
||||
/** Canaux supportés (JSON array) */
|
||||
private String canauxSupportes;
|
||||
|
||||
/** Langue du template */
|
||||
private String langue;
|
||||
|
||||
/** Description */
|
||||
private String description;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,518 @@
|
||||
package dev.lions.unionflow.server.api.dto.organisation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.organisation.StatutOrganisation;
|
||||
import dev.lions.unionflow.server.api.enums.organisation.TypeOrganisation;
|
||||
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.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.Period;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des organisations (Lions Club, Associations, Coopératives, etc.) Représente
|
||||
* une organisation avec ses informations complètes et sa hiérarchie
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class OrganisationDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Nom de l'organisation */
|
||||
@NotBlank(message = "Le nom de l'organisation" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Size(
|
||||
min = ValidationConstants.NOM_ORGANISATION_MIN_LENGTH,
|
||||
max = ValidationConstants.NOM_ORGANISATION_MAX_LENGTH,
|
||||
message = ValidationConstants.NOM_ORGANISATION_SIZE_MESSAGE)
|
||||
private String nom;
|
||||
|
||||
/** Nom court ou sigle */
|
||||
@Size(
|
||||
max = ValidationConstants.NOM_COURT_MAX_LENGTH,
|
||||
message = ValidationConstants.NOM_COURT_SIZE_MESSAGE)
|
||||
private String nomCourt;
|
||||
|
||||
/** Type d'organisation */
|
||||
@NotNull(message = "Le type d'organisation est obligatoire")
|
||||
private TypeOrganisation typeOrganisation;
|
||||
|
||||
/** Statut de l'organisation */
|
||||
@NotNull(message = "Le statut de l'organisation est obligatoire")
|
||||
private StatutOrganisation statut;
|
||||
|
||||
/** Description de l'organisation */
|
||||
@Size(
|
||||
max = ValidationConstants.DESCRIPTION_MAX_LENGTH,
|
||||
message = ValidationConstants.DESCRIPTION_SIZE_MESSAGE)
|
||||
private String description;
|
||||
|
||||
/** Date de fondation */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateFondation;
|
||||
|
||||
/** Numéro d'enregistrement officiel */
|
||||
@Size(max = 100, message = "Le numéro d'enregistrement ne peut pas dépasser 100 caractères")
|
||||
private String numeroEnregistrement;
|
||||
|
||||
/** Adresse complète */
|
||||
@Size(max = 500, message = "L'adresse ne peut pas dépasser 500 caractères")
|
||||
private String adresse;
|
||||
|
||||
/** Ville */
|
||||
@Size(max = 100, message = "La ville ne peut pas dépasser 100 caractères")
|
||||
private String ville;
|
||||
|
||||
/** Région/Province */
|
||||
@Size(max = 100, message = "La région ne peut pas dépasser 100 caractères")
|
||||
private String region;
|
||||
|
||||
/** Pays */
|
||||
@Size(max = 100, message = "Le pays ne peut pas dépasser 100 caractères")
|
||||
private String pays;
|
||||
|
||||
/** Code postal */
|
||||
@Pattern(regexp = "^[0-9A-Z\\-\\s]{3,10}$", message = "Format de code postal invalide")
|
||||
private String codePostal;
|
||||
|
||||
/** Latitude pour géolocalisation */
|
||||
@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 = 2,
|
||||
fraction = 8,
|
||||
message = "La latitude ne peut avoir plus de 2 chiffres entiers et 8 décimales")
|
||||
private BigDecimal latitude;
|
||||
|
||||
/** Longitude pour géolocalisation */
|
||||
@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 = 8,
|
||||
message = "La longitude ne peut avoir plus de 3 chiffres entiers et 8 décimales")
|
||||
private BigDecimal longitude;
|
||||
|
||||
/** Téléphone principal */
|
||||
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$", message = "Format de téléphone invalide")
|
||||
private String telephone;
|
||||
|
||||
/** Téléphone secondaire */
|
||||
@Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$", message = "Format de téléphone invalide")
|
||||
private String telephoneSecondaire;
|
||||
|
||||
/** Email principal */
|
||||
@Email(message = "Format d'email invalide")
|
||||
@Size(max = 200, message = "L'email ne peut pas dépasser 200 caractères")
|
||||
private String email;
|
||||
|
||||
/** Email secondaire */
|
||||
@Email(message = "Format d'email invalide")
|
||||
@Size(max = 200, message = "L'email ne peut pas dépasser 200 caractères")
|
||||
private String emailSecondaire;
|
||||
|
||||
/** Site web */
|
||||
@Pattern(
|
||||
regexp =
|
||||
"^https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?$",
|
||||
message = "Format d'URL invalide")
|
||||
@Size(max = 500, message = "L'URL ne peut pas dépasser 500 caractères")
|
||||
private String siteWeb;
|
||||
|
||||
/** Logo de l'organisation (URL ou nom de fichier) */
|
||||
@Size(max = 500, message = "Le logo ne peut pas dépasser 500 caractères")
|
||||
private String logo;
|
||||
|
||||
/** Organisation parente (pour hiérarchie) */
|
||||
private UUID organisationParenteId;
|
||||
|
||||
/** Nom de l'organisation parente */
|
||||
private String nomOrganisationParente;
|
||||
|
||||
/** Niveau hiérarchique (0 = racine) */
|
||||
private Integer niveauHierarchique;
|
||||
|
||||
/** Nombre de membres actifs */
|
||||
private Integer nombreMembres;
|
||||
|
||||
/** Nombre d'administrateurs */
|
||||
private Integer nombreAdministrateurs;
|
||||
|
||||
/** Budget annuel */
|
||||
@DecimalMin(value = "0.0", message = "Le budget doit être positif")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "Le budget ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal budgetAnnuel;
|
||||
|
||||
/** Devise du budget */
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
|
||||
private String devise;
|
||||
|
||||
/** Objectifs de l'organisation */
|
||||
@Size(max = 2000, message = "Les objectifs ne peuvent pas dépasser 2000 caractères")
|
||||
private String objectifs;
|
||||
|
||||
/** Activités principales */
|
||||
@Size(max = 2000, message = "Les activités ne peuvent pas dépasser 2000 caractères")
|
||||
private String activitesPrincipales;
|
||||
|
||||
/** Réseaux sociaux (JSON) */
|
||||
@Size(max = 1000, message = "Les réseaux sociaux ne peuvent pas dépasser 1000 caractères")
|
||||
private String reseauxSociaux;
|
||||
|
||||
/** Certifications ou labels */
|
||||
@Size(max = 500, message = "Les certifications ne peuvent pas dépasser 500 caractères")
|
||||
private String certifications;
|
||||
|
||||
/** Partenaires principaux */
|
||||
@Size(max = 1000, message = "Les partenaires ne peuvent pas dépasser 1000 caractères")
|
||||
private String partenaires;
|
||||
|
||||
/** Notes administratives */
|
||||
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
|
||||
private String notes;
|
||||
|
||||
/** Organisation publique (visible dans l'annuaire) */
|
||||
private Boolean organisationPublique;
|
||||
|
||||
/** Accepte nouveaux membres */
|
||||
private Boolean accepteNouveauxMembres;
|
||||
|
||||
/** Cotisation obligatoire */
|
||||
private Boolean cotisationObligatoire;
|
||||
|
||||
/** Montant cotisation annuelle */
|
||||
@DecimalMin(value = "0.0", message = "Le montant de cotisation doit être positif")
|
||||
@Digits(
|
||||
integer = 10,
|
||||
fraction = 2,
|
||||
message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales")
|
||||
private BigDecimal montantCotisationAnnuelle;
|
||||
|
||||
// Constructeurs
|
||||
public OrganisationDTO() {
|
||||
super();
|
||||
this.statut = StatutOrganisation.ACTIVE;
|
||||
this.typeOrganisation = TypeOrganisation.ASSOCIATION;
|
||||
this.devise = "XOF";
|
||||
this.niveauHierarchique = 0;
|
||||
this.nombreMembres = 0;
|
||||
this.nombreAdministrateurs = 0;
|
||||
this.organisationPublique = true;
|
||||
this.accepteNouveauxMembres = true;
|
||||
this.cotisationObligatoire = false;
|
||||
}
|
||||
|
||||
public OrganisationDTO(String nom, TypeOrganisation type) {
|
||||
this();
|
||||
this.nom = nom;
|
||||
this.typeOrganisation = type;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation est active
|
||||
*
|
||||
* @return true si l'organisation est active
|
||||
*/
|
||||
public boolean estActive() {
|
||||
return StatutOrganisation.ACTIVE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation est inactive
|
||||
*
|
||||
* @return true si l'organisation est inactive
|
||||
*/
|
||||
public boolean estInactive() {
|
||||
return StatutOrganisation.INACTIVE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation est suspendue
|
||||
*
|
||||
* @return true si l'organisation est suspendue
|
||||
*/
|
||||
public boolean estSuspendue() {
|
||||
return StatutOrganisation.SUSPENDUE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation est en cours de création
|
||||
*
|
||||
* @return true si l'organisation est en création
|
||||
*/
|
||||
public boolean estEnCreation() {
|
||||
return StatutOrganisation.EN_CREATION.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation est dissoute
|
||||
*
|
||||
* @return true si l'organisation est dissoute
|
||||
*/
|
||||
public boolean estDissoute() {
|
||||
return StatutOrganisation.DISSOUTE.equals(statut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'ancienneté de l'organisation en années
|
||||
*
|
||||
* @return L'ancienneté en années
|
||||
*/
|
||||
public int getAncienneteAnnees() {
|
||||
if (dateFondation == null) return 0;
|
||||
return Period.between(dateFondation, LocalDate.now()).getYears();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule l'ancienneté de l'organisation en mois
|
||||
*
|
||||
* @return L'ancienneté en mois
|
||||
*/
|
||||
public int getAncienneteMois() {
|
||||
if (dateFondation == null) return 0;
|
||||
Period periode = Period.between(dateFondation, LocalDate.now());
|
||||
return periode.getYears() * 12 + periode.getMonths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation a une géolocalisation
|
||||
*
|
||||
* @return true si latitude et longitude sont définies
|
||||
*/
|
||||
public boolean possedGeolocalisation() {
|
||||
return latitude != null && longitude != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation est une organisation racine (sans parent)
|
||||
*
|
||||
* @return true si l'organisation n'a pas de parent
|
||||
*/
|
||||
public boolean estOrganisationRacine() {
|
||||
return organisationParenteId == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation a des sous-organisations
|
||||
*
|
||||
* @return true si le niveau hiérarchique est supérieur à 0
|
||||
*/
|
||||
public boolean possedeSousOrganisations() {
|
||||
return niveauHierarchique != null && niveauHierarchique > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nom d'affichage (nom court si disponible, sinon nom complet)
|
||||
*
|
||||
* @return Le nom d'affichage
|
||||
*/
|
||||
public String getNomAffichage() {
|
||||
return (nomCourt != null && !nomCourt.trim().isEmpty()) ? nomCourt : nom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'adresse complète formatée
|
||||
*
|
||||
* @return L'adresse complète
|
||||
*/
|
||||
public String getAdresseComplete() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (adresse != null && !adresse.trim().isEmpty()) {
|
||||
sb.append(adresse);
|
||||
}
|
||||
|
||||
if (ville != null && !ville.trim().isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(ville);
|
||||
}
|
||||
|
||||
if (codePostal != null && !codePostal.trim().isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(" ");
|
||||
sb.append(codePostal);
|
||||
}
|
||||
|
||||
if (region != null && !region.trim().isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(region);
|
||||
}
|
||||
|
||||
if (pays != null && !pays.trim().isEmpty()) {
|
||||
if (sb.length() > 0) sb.append(", ");
|
||||
sb.append(pays);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le ratio administrateurs/membres
|
||||
*
|
||||
* @return Le pourcentage d'administrateurs
|
||||
*/
|
||||
public double getRatioAdministrateurs() {
|
||||
if (nombreMembres == null || nombreMembres == 0) return 0.0;
|
||||
if (nombreAdministrateurs == null) return 0.0;
|
||||
return (nombreAdministrateurs.doubleValue() / nombreMembres.doubleValue()) * 100.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'organisation a un budget défini
|
||||
*
|
||||
* @return true si un budget annuel est défini
|
||||
*/
|
||||
public boolean hasBudget() {
|
||||
return budgetAnnuel != null && budgetAnnuel.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Active l'organisation
|
||||
*
|
||||
* @param utilisateur L'utilisateur qui active l'organisation
|
||||
*/
|
||||
public void activer(String utilisateur) {
|
||||
this.statut = StatutOrganisation.ACTIVE;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend l'organisation
|
||||
*
|
||||
* @param utilisateur L'utilisateur qui suspend l'organisation
|
||||
*/
|
||||
public void suspendre(String utilisateur) {
|
||||
this.statut = StatutOrganisation.SUSPENDUE;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dissout l'organisation
|
||||
*
|
||||
* @param utilisateur L'utilisateur qui dissout l'organisation
|
||||
*/
|
||||
public void dissoudre(String utilisateur) {
|
||||
this.statut = StatutOrganisation.DISSOUTE;
|
||||
this.accepteNouveauxMembres = false;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Désactive l'organisation
|
||||
*
|
||||
* @param utilisateur L'utilisateur qui désactive l'organisation
|
||||
*/
|
||||
public void desactiver(String utilisateur) {
|
||||
this.statut = StatutOrganisation.INACTIVE;
|
||||
this.accepteNouveauxMembres = false;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le nombre de membres
|
||||
*
|
||||
* @param nouveauNombre Le nouveau nombre de membres
|
||||
* @param utilisateur L'utilisateur qui fait la mise à jour
|
||||
*/
|
||||
public void mettreAJourNombreMembres(int nouveauNombre, String utilisateur) {
|
||||
this.nombreMembres = nouveauNombre;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un membre
|
||||
*
|
||||
* @param utilisateur L'utilisateur qui ajoute le membre
|
||||
*/
|
||||
public void ajouterMembre(String utilisateur) {
|
||||
if (nombreMembres == null) nombreMembres = 0;
|
||||
nombreMembres++;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire un membre
|
||||
*
|
||||
* @param utilisateur L'utilisateur qui retire le membre
|
||||
*/
|
||||
public void retirerMembre(String utilisateur) {
|
||||
if (nombreMembres != null && nombreMembres > 0) {
|
||||
nombreMembres--;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OrganisationDTO{"
|
||||
+ "nom='"
|
||||
+ nom
|
||||
+ '\''
|
||||
+ ", nomCourt='"
|
||||
+ nomCourt
|
||||
+ '\''
|
||||
+ ", typeOrganisation="
|
||||
+ typeOrganisation
|
||||
+ ", statut="
|
||||
+ statut
|
||||
+ ", ville='"
|
||||
+ ville
|
||||
+ '\''
|
||||
+ ", pays='"
|
||||
+ pays
|
||||
+ '\''
|
||||
+ ", nombreMembres="
|
||||
+ nombreMembres
|
||||
+ ", anciennete="
|
||||
+ getAncienneteAnnees()
|
||||
+ " ans"
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Retourne le libellé du statut */
|
||||
public String getStatutLibelle() {
|
||||
return statut != null ? statut.getLibelle() : "Non défini";
|
||||
}
|
||||
|
||||
/** Retourne le libellé du type d'organisation */
|
||||
public String getTypeLibelle() {
|
||||
return typeOrganisation != null ? typeOrganisation.getLibelle() : "Non défini";
|
||||
}
|
||||
|
||||
/** Ajoute un administrateur */
|
||||
public void ajouterAdministrateur(String utilisateur) {
|
||||
if (nombreAdministrateurs == null) nombreAdministrateurs = 0;
|
||||
nombreAdministrateurs++;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
|
||||
/** Retire un administrateur */
|
||||
public void retirerAdministrateur(String utilisateur) {
|
||||
if (nombreAdministrateurs != null && nombreAdministrateurs > 0) {
|
||||
nombreAdministrateurs--;
|
||||
marquerCommeModifie(utilisateur);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package dev.lions.unionflow.server.api.dto.organisation;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour le catalogue des types d'organisation.
|
||||
*
|
||||
* <p>Permet de gérer dynamiquement la liste des types utilisables par les organisations
|
||||
* (codes, libellés, ordre d'affichage, activation/désactivation dans l'UI).
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TypeOrganisationDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Code fonctionnel unique du type (ex: LIONS_CLUB, ASSOCIATION, COOPERATIVE, ...) */
|
||||
@NotBlank(message = "Le code du type d'organisation est obligatoire")
|
||||
@Size(max = 50, message = "Le code du type d'organisation ne peut pas dépasser 50 caractères")
|
||||
private String code;
|
||||
|
||||
/** Libellé affiché dans l'interface utilisateur */
|
||||
@NotBlank(message = "Le libellé du type d'organisation est obligatoire")
|
||||
@Size(max = 150, message = "Le libellé du type d'organisation ne peut pas dépasser 150 caractères")
|
||||
private String libelle;
|
||||
|
||||
/** Description fonctionnelle (optionnelle) */
|
||||
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
|
||||
private String description;
|
||||
|
||||
/** Ordre d'affichage dans les listes déroulantes */
|
||||
private Integer ordreAffichage;
|
||||
|
||||
/** Indique si le type est actif dans l'application */
|
||||
private Boolean actif = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package dev.lions.unionflow.server.api.dto.paiement;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.MethodePaiement;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.StatutPaiement;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des paiements
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class PaiementDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de référence unique */
|
||||
@NotBlank(message = "Le numéro de référence est obligatoire")
|
||||
private String numeroReference;
|
||||
|
||||
/** Montant du paiement */
|
||||
@NotNull(message = "Le montant est obligatoire")
|
||||
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montant;
|
||||
|
||||
/** Code devise (ISO 3 lettres) */
|
||||
@NotBlank(message = "Le code devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres")
|
||||
private String codeDevise;
|
||||
|
||||
/** Méthode de paiement */
|
||||
@NotNull(message = "La méthode de paiement est obligatoire")
|
||||
private MethodePaiement methodePaiement;
|
||||
|
||||
/** Statut du paiement */
|
||||
@NotNull(message = "Le statut du paiement est obligatoire")
|
||||
private StatutPaiement statutPaiement;
|
||||
|
||||
/** Date de paiement */
|
||||
private LocalDateTime datePaiement;
|
||||
|
||||
/** Date de validation */
|
||||
private LocalDateTime dateValidation;
|
||||
|
||||
/** Validateur (email de l'administrateur) */
|
||||
private String validateur;
|
||||
|
||||
/** Référence externe */
|
||||
private String referenceExterne;
|
||||
|
||||
/** URL de preuve de paiement */
|
||||
private String urlPreuve;
|
||||
|
||||
/** Commentaires et notes */
|
||||
private String commentaire;
|
||||
|
||||
/** Adresse IP de l'initiateur */
|
||||
private String ipAddress;
|
||||
|
||||
/** User-Agent de l'initiateur */
|
||||
private String userAgent;
|
||||
|
||||
/** ID du membre payeur */
|
||||
@NotNull(message = "Le membre payeur est obligatoire")
|
||||
private UUID membreId;
|
||||
|
||||
/** ID de la transaction Wave (si applicable) */
|
||||
private UUID transactionWaveId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
package dev.lions.unionflow.server.api.dto.paiement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
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 java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la consultation du solde Wave Money (Balance API) Représente le solde d'un wallet Wave
|
||||
* Business
|
||||
*
|
||||
* <p>Basé sur l'API officielle Wave : https://docs.wave.com/business#balance-api
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class WaveBalanceDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Solde disponible */
|
||||
@NotNull(message = "Le solde disponible est obligatoire")
|
||||
@DecimalMin(value = "0.0", message = "Le solde doit être positif ou nul")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "Le solde ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal soldeDisponible;
|
||||
|
||||
/** Solde en attente (transactions en cours) */
|
||||
@DecimalMin(value = "0.0", message = "Le solde en attente doit être positif ou nul")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "Le solde ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal soldeEnAttente;
|
||||
|
||||
/** Solde total (disponible + en attente) */
|
||||
@DecimalMin(value = "0.0", message = "Le solde total doit être positif ou nul")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "Le solde ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal soldeTotal;
|
||||
|
||||
/** Devise du wallet */
|
||||
@NotBlank(message = "La devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
|
||||
private String devise = "XOF";
|
||||
|
||||
/** Numéro du wallet Wave */
|
||||
@NotBlank(message = "Le numéro de wallet est obligatoire")
|
||||
private String numeroWallet;
|
||||
|
||||
/** Nom du business associé au wallet */
|
||||
private String nomBusiness;
|
||||
|
||||
/** Date de dernière mise à jour du solde */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateDerniereMiseAJour;
|
||||
|
||||
/** Date de dernière synchronisation avec Wave */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateDerniereSynchronisation;
|
||||
|
||||
/** Statut du wallet */
|
||||
@Pattern(
|
||||
regexp = "^(ACTIVE|INACTIVE|SUSPENDED|BLOCKED)$",
|
||||
message = "Le statut doit être ACTIVE, INACTIVE, SUSPENDED ou BLOCKED")
|
||||
private String statutWallet;
|
||||
|
||||
/** Limite de transaction quotidienne */
|
||||
@DecimalMin(value = "0.0", message = "La limite doit être positive ou nulle")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "La limite ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal limiteQuotidienne;
|
||||
|
||||
/** Montant déjà utilisé aujourd'hui */
|
||||
@DecimalMin(value = "0.0", message = "Le montant utilisé doit être positif ou nul")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "Le montant ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal montantUtiliseAujourdhui;
|
||||
|
||||
/** Limite mensuelle */
|
||||
@DecimalMin(value = "0.0", message = "La limite doit être positive ou nulle")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "La limite ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal limiteMensuelle;
|
||||
|
||||
/** Montant utilisé ce mois */
|
||||
@DecimalMin(value = "0.0", message = "Le montant utilisé doit être positif ou nul")
|
||||
@Digits(
|
||||
integer = 12,
|
||||
fraction = 2,
|
||||
message = "Le montant ne peut avoir plus de 12 chiffres entiers et 2 décimales")
|
||||
private BigDecimal montantUtiliseCeMois;
|
||||
|
||||
/** Nombre de transactions aujourd'hui */
|
||||
private Integer nombreTransactionsAujourdhui;
|
||||
|
||||
/** Nombre de transactions ce mois */
|
||||
private Integer nombreTransactionsCeMois;
|
||||
|
||||
/** Dernière erreur de synchronisation */
|
||||
private String derniereErreur;
|
||||
|
||||
/** Code de la dernière erreur */
|
||||
private String codeDerniereErreur;
|
||||
|
||||
// Constructeurs
|
||||
public WaveBalanceDTO() {
|
||||
super();
|
||||
this.devise = "XOF";
|
||||
this.statutWallet = "ACTIVE";
|
||||
this.soldeEnAttente = BigDecimal.ZERO;
|
||||
this.montantUtiliseAujourdhui = BigDecimal.ZERO;
|
||||
this.montantUtiliseCeMois = BigDecimal.ZERO;
|
||||
this.nombreTransactionsAujourdhui = 0;
|
||||
this.nombreTransactionsCeMois = 0;
|
||||
}
|
||||
|
||||
public WaveBalanceDTO(String numeroWallet, BigDecimal soldeDisponible) {
|
||||
this();
|
||||
this.numeroWallet = numeroWallet;
|
||||
this.soldeDisponible = soldeDisponible;
|
||||
this.soldeTotal = soldeDisponible;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public BigDecimal getSoldeDisponible() {
|
||||
return soldeDisponible;
|
||||
}
|
||||
|
||||
public void setSoldeDisponible(BigDecimal soldeDisponible) {
|
||||
this.soldeDisponible = soldeDisponible;
|
||||
calculerSoldeTotal();
|
||||
}
|
||||
|
||||
public BigDecimal getSoldeEnAttente() {
|
||||
return soldeEnAttente;
|
||||
}
|
||||
|
||||
public void setSoldeEnAttente(BigDecimal soldeEnAttente) {
|
||||
this.soldeEnAttente = soldeEnAttente;
|
||||
calculerSoldeTotal();
|
||||
}
|
||||
|
||||
public BigDecimal getSoldeTotal() {
|
||||
return soldeTotal;
|
||||
}
|
||||
|
||||
public void setSoldeTotal(BigDecimal soldeTotal) {
|
||||
this.soldeTotal = soldeTotal;
|
||||
}
|
||||
|
||||
public String getDevise() {
|
||||
return devise;
|
||||
}
|
||||
|
||||
public void setDevise(String devise) {
|
||||
this.devise = devise;
|
||||
}
|
||||
|
||||
public String getNumeroWallet() {
|
||||
return numeroWallet;
|
||||
}
|
||||
|
||||
public void setNumeroWallet(String numeroWallet) {
|
||||
this.numeroWallet = numeroWallet;
|
||||
}
|
||||
|
||||
public String getNomBusiness() {
|
||||
return nomBusiness;
|
||||
}
|
||||
|
||||
public void setNomBusiness(String nomBusiness) {
|
||||
this.nomBusiness = nomBusiness;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateDerniereMiseAJour() {
|
||||
return dateDerniereMiseAJour;
|
||||
}
|
||||
|
||||
public void setDateDerniereMiseAJour(LocalDateTime dateDerniereMiseAJour) {
|
||||
this.dateDerniereMiseAJour = dateDerniereMiseAJour;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateDerniereSynchronisation() {
|
||||
return dateDerniereSynchronisation;
|
||||
}
|
||||
|
||||
public void setDateDerniereSynchronisation(LocalDateTime dateDerniereSynchronisation) {
|
||||
this.dateDerniereSynchronisation = dateDerniereSynchronisation;
|
||||
}
|
||||
|
||||
public String getStatutWallet() {
|
||||
return statutWallet;
|
||||
}
|
||||
|
||||
public void setStatutWallet(String statutWallet) {
|
||||
this.statutWallet = statutWallet;
|
||||
}
|
||||
|
||||
public BigDecimal getLimiteQuotidienne() {
|
||||
return limiteQuotidienne;
|
||||
}
|
||||
|
||||
public void setLimiteQuotidienne(BigDecimal limiteQuotidienne) {
|
||||
this.limiteQuotidienne = limiteQuotidienne;
|
||||
}
|
||||
|
||||
public BigDecimal getMontantUtiliseAujourdhui() {
|
||||
return montantUtiliseAujourdhui;
|
||||
}
|
||||
|
||||
public void setMontantUtiliseAujourdhui(BigDecimal montantUtiliseAujourdhui) {
|
||||
this.montantUtiliseAujourdhui = montantUtiliseAujourdhui;
|
||||
}
|
||||
|
||||
public BigDecimal getLimiteMensuelle() {
|
||||
return limiteMensuelle;
|
||||
}
|
||||
|
||||
public void setLimiteMensuelle(BigDecimal limiteMensuelle) {
|
||||
this.limiteMensuelle = limiteMensuelle;
|
||||
}
|
||||
|
||||
public BigDecimal getMontantUtiliseCeMois() {
|
||||
return montantUtiliseCeMois;
|
||||
}
|
||||
|
||||
public void setMontantUtiliseCeMois(BigDecimal montantUtiliseCeMois) {
|
||||
this.montantUtiliseCeMois = montantUtiliseCeMois;
|
||||
}
|
||||
|
||||
public Integer getNombreTransactionsAujourdhui() {
|
||||
return nombreTransactionsAujourdhui;
|
||||
}
|
||||
|
||||
public void setNombreTransactionsAujourdhui(Integer nombreTransactionsAujourdhui) {
|
||||
this.nombreTransactionsAujourdhui = nombreTransactionsAujourdhui;
|
||||
}
|
||||
|
||||
public Integer getNombreTransactionsCeMois() {
|
||||
return nombreTransactionsCeMois;
|
||||
}
|
||||
|
||||
public void setNombreTransactionsCeMois(Integer nombreTransactionsCeMois) {
|
||||
this.nombreTransactionsCeMois = nombreTransactionsCeMois;
|
||||
}
|
||||
|
||||
public String getDerniereErreur() {
|
||||
return derniereErreur;
|
||||
}
|
||||
|
||||
public void setDerniereErreur(String derniereErreur) {
|
||||
this.derniereErreur = derniereErreur;
|
||||
}
|
||||
|
||||
public String getCodeDerniereErreur() {
|
||||
return codeDerniereErreur;
|
||||
}
|
||||
|
||||
public void setCodeDerniereErreur(String codeDerniereErreur) {
|
||||
this.codeDerniereErreur = codeDerniereErreur;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/** Calcule le solde total */
|
||||
private void calculerSoldeTotal() {
|
||||
if (soldeDisponible != null && soldeEnAttente != null) {
|
||||
this.soldeTotal = soldeDisponible.add(soldeEnAttente);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le wallet est actif
|
||||
*
|
||||
* @return true si le wallet est actif
|
||||
*/
|
||||
public boolean isWalletActif() {
|
||||
return "ACTIVE".equals(statutWallet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le solde est suffisant pour un montant donné
|
||||
*
|
||||
* @param montant Le montant à vérifier
|
||||
* @return true si le solde est suffisant
|
||||
*/
|
||||
public boolean isSoldeSuffisant(BigDecimal montant) {
|
||||
return soldeDisponible != null && soldeDisponible.compareTo(montant) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le solde disponible restant pour aujourd'hui
|
||||
*
|
||||
* @return Le montant encore disponible aujourd'hui
|
||||
*/
|
||||
public BigDecimal getSoldeDisponibleAujourdhui() {
|
||||
if (soldeDisponible == null || limiteQuotidienne == null || montantUtiliseAujourdhui == null) {
|
||||
return soldeDisponible;
|
||||
}
|
||||
|
||||
BigDecimal limiteRestante = limiteQuotidienne.subtract(montantUtiliseAujourdhui);
|
||||
return soldeDisponible.min(limiteRestante);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour les statistiques après une transaction
|
||||
*
|
||||
* @param montant Le montant de la transaction
|
||||
*/
|
||||
public void mettreAJourApresTransaction(BigDecimal montant) {
|
||||
if (montantUtiliseAujourdhui == null) montantUtiliseAujourdhui = BigDecimal.ZERO;
|
||||
if (montantUtiliseCeMois == null) montantUtiliseCeMois = BigDecimal.ZERO;
|
||||
if (nombreTransactionsAujourdhui == null) nombreTransactionsAujourdhui = 0;
|
||||
if (nombreTransactionsCeMois == null) nombreTransactionsCeMois = 0;
|
||||
|
||||
this.montantUtiliseAujourdhui = montantUtiliseAujourdhui.add(montant);
|
||||
this.montantUtiliseCeMois = montantUtiliseCeMois.add(montant);
|
||||
this.nombreTransactionsAujourdhui++;
|
||||
this.nombreTransactionsCeMois++;
|
||||
this.dateDerniereMiseAJour = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WaveBalanceDTO{"
|
||||
+ "numeroWallet='"
|
||||
+ numeroWallet
|
||||
+ '\''
|
||||
+ ", soldeDisponible="
|
||||
+ soldeDisponible
|
||||
+ ", soldeTotal="
|
||||
+ soldeTotal
|
||||
+ ", devise='"
|
||||
+ devise
|
||||
+ '\''
|
||||
+ ", statutWallet='"
|
||||
+ statutWallet
|
||||
+ '\''
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package dev.lions.unionflow.server.api.dto.paiement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.StatutSession;
|
||||
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.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour les sessions de paiement Wave Money (Checkout API) Représente une session de paiement
|
||||
* créée via l'API Wave Checkout
|
||||
*
|
||||
* <p>Basé sur l'API officielle Wave : https://docs.wave.com/business#checkout-api
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class WaveCheckoutSessionDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** ID de la session Wave (retourné par l'API) */
|
||||
@NotBlank(message = "L'ID de session Wave est obligatoire")
|
||||
private String waveSessionId;
|
||||
|
||||
/** URL de la session de paiement Wave (générée par l'API Wave) */
|
||||
@Size(max = 500, message = "L'URL ne peut pas dépasser 500 caractères")
|
||||
private String waveUrl;
|
||||
|
||||
/** Montant du paiement */
|
||||
@NotNull(message = "Le montant est obligatoire")
|
||||
@DecimalMin(value = "0.01", 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")
|
||||
private BigDecimal montant;
|
||||
|
||||
/** Devise (XOF pour le Sénégal) */
|
||||
@NotBlank(message = "La devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
|
||||
private String devise = "XOF";
|
||||
|
||||
/** URL de succès (redirection après paiement réussi) */
|
||||
@NotBlank(message = "L'URL de succès est obligatoire")
|
||||
@Size(max = 500, message = "L'URL de succès ne peut pas dépasser 500 caractères")
|
||||
private String successUrl;
|
||||
|
||||
/** URL d'erreur (redirection après échec) */
|
||||
@NotBlank(message = "L'URL d'erreur est obligatoire")
|
||||
@Size(max = 500, message = "L'URL d'erreur ne peut pas dépasser 500 caractères")
|
||||
private String errorUrl;
|
||||
|
||||
/** Statut de la session */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
private StatutSession statut;
|
||||
|
||||
/** ID de l'organisation qui effectue le paiement */
|
||||
private UUID organisationId;
|
||||
|
||||
/** Nom de l'organisation */
|
||||
private String nomOrganisation;
|
||||
|
||||
/** ID du membre qui effectue le paiement */
|
||||
private UUID membreId;
|
||||
|
||||
/** Nom du membre */
|
||||
private String nomMembre;
|
||||
|
||||
/** Type de paiement (COTISATION, ABONNEMENT, DON, AUTRE) */
|
||||
@Pattern(
|
||||
regexp = "^(COTISATION|ABONNEMENT|DON|EVENEMENT|FORMATION|AUTRE)$",
|
||||
message = "Type de paiement invalide")
|
||||
private String typePaiement;
|
||||
|
||||
/** Référence du paiement dans UnionFlow */
|
||||
@Size(max = 100, message = "La référence ne peut pas dépasser 100 caractères")
|
||||
private String referenceUnionFlow;
|
||||
|
||||
/** Description du paiement */
|
||||
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
|
||||
private String description;
|
||||
|
||||
/** Nom du business affiché (override_business_name) */
|
||||
@Size(max = 100, message = "Le nom du business ne peut pas dépasser 100 caractères")
|
||||
private String nomBusinessAffiche;
|
||||
|
||||
/** ID du marchand agrégé (si applicable) */
|
||||
private String aggregatedMerchantId;
|
||||
|
||||
/** Date d'expiration de la session */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateExpiration;
|
||||
|
||||
/** Date de completion du paiement */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateCompletion;
|
||||
|
||||
/** Numéro de téléphone du payeur (si fourni) */
|
||||
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide")
|
||||
private String telephonePayeur;
|
||||
|
||||
/** Email du payeur (si fourni) */
|
||||
@Pattern(regexp = "^[A-Za-z0-9+_.-]+@(.+)$", message = "Format d'email invalide")
|
||||
private String emailPayeur;
|
||||
|
||||
/** Adresse IP du client */
|
||||
private String adresseIpClient;
|
||||
|
||||
/** User Agent du navigateur */
|
||||
@Size(max = 500, message = "Le User Agent ne peut pas dépasser 500 caractères")
|
||||
private String userAgent;
|
||||
|
||||
/** Données de callback reçues de Wave */
|
||||
@Size(max = 2000, message = "Les données callback ne peuvent pas dépasser 2000 caractères")
|
||||
private String callbackData;
|
||||
|
||||
/** Code d'erreur Wave (si échec) */
|
||||
private String codeErreurWave;
|
||||
|
||||
/** Message d'erreur Wave (si échec) */
|
||||
@Size(max = 500, message = "Le message d'erreur ne peut pas dépasser 500 caractères")
|
||||
private String messageErreurWave;
|
||||
|
||||
/** Nombre de tentatives de paiement */
|
||||
private Integer nombreTentatives;
|
||||
|
||||
/** Session liée à un webhook */
|
||||
private Boolean webhookRecu;
|
||||
|
||||
/** Date de réception du webhook */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateWebhook;
|
||||
|
||||
/** Données du webhook reçu */
|
||||
@Size(max = 2000, message = "Les données webhook ne peuvent pas dépasser 2000 caractères")
|
||||
private String donneesWebhook;
|
||||
|
||||
// Constructeurs
|
||||
public WaveCheckoutSessionDTO() {
|
||||
super();
|
||||
this.devise = "XOF";
|
||||
this.statut = StatutSession.PENDING;
|
||||
this.nombreTentatives = 0;
|
||||
this.webhookRecu = false;
|
||||
}
|
||||
|
||||
public WaveCheckoutSessionDTO(BigDecimal montant, String successUrl, String errorUrl) {
|
||||
this();
|
||||
this.montant = montant;
|
||||
this.successUrl = successUrl;
|
||||
this.errorUrl = errorUrl;
|
||||
}
|
||||
|
||||
// Getters et Setters générés automatiquement par Lombok @Getter/@Setter
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
package dev.lions.unionflow.server.api.dto.paiement;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.StatutTraitement;
|
||||
import dev.lions.unionflow.server.api.enums.paiement.TypeEvenement;
|
||||
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.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour les webhooks Wave Money Représente les notifications reçues de Wave lors d'événements
|
||||
*
|
||||
* <p>Basé sur l'API officielle Wave : https://docs.wave.com/business#webhooks
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class WaveWebhookDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** ID unique du webhook Wave */
|
||||
@NotBlank(message = "L'ID du webhook est obligatoire")
|
||||
private String webhookId;
|
||||
|
||||
/** Type d'événement */
|
||||
@NotNull(message = "Le type d'événement est obligatoire")
|
||||
private TypeEvenement typeEvenement;
|
||||
|
||||
/** Code de l'événement tel que reçu de Wave */
|
||||
@NotBlank(message = "Le code événement est obligatoire")
|
||||
private String codeEvenement;
|
||||
|
||||
/** Statut de traitement du webhook */
|
||||
@NotNull(message = "Le statut de traitement est obligatoire")
|
||||
private StatutTraitement statutTraitement;
|
||||
|
||||
/** Payload JSON complet reçu de Wave */
|
||||
@NotBlank(message = "Le payload est obligatoire")
|
||||
@Size(max = 5000, message = "Le payload ne peut pas dépasser 5000 caractères")
|
||||
private String payloadJson;
|
||||
|
||||
/** Headers HTTP reçus */
|
||||
@Size(max = 2000, message = "Les headers ne peuvent pas dépasser 2000 caractères")
|
||||
private String headersHttp;
|
||||
|
||||
/** Signature Wave pour vérification */
|
||||
private String signatureWave;
|
||||
|
||||
/** Date de réception du webhook */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateReception;
|
||||
|
||||
/** Date de traitement du webhook */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateTraitement;
|
||||
|
||||
/** ID de la session checkout concernée (si applicable) */
|
||||
private String sessionCheckoutId;
|
||||
|
||||
/** ID de la transaction Wave concernée */
|
||||
private String transactionWaveId;
|
||||
|
||||
/** Montant de la transaction (si applicable) */
|
||||
private BigDecimal montantTransaction;
|
||||
|
||||
/** Devise de la transaction */
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
|
||||
private String deviseTransaction;
|
||||
|
||||
/** Statut de la transaction Wave */
|
||||
private String statutTransactionWave;
|
||||
|
||||
/** ID de l'organisation UnionFlow concernée */
|
||||
private UUID organisationId;
|
||||
|
||||
/** ID du membre UnionFlow concerné */
|
||||
private UUID membreId;
|
||||
|
||||
/** Référence UnionFlow liée */
|
||||
private String referenceUnionFlow;
|
||||
|
||||
/** Type de paiement UnionFlow */
|
||||
@Pattern(
|
||||
regexp = "^(COTISATION|ABONNEMENT|DON|EVENEMENT|FORMATION|AUTRE)$",
|
||||
message = "Type de paiement invalide")
|
||||
private String typePaiementUnionFlow;
|
||||
|
||||
/** Adresse IP source du webhook */
|
||||
private String adresseIpSource;
|
||||
|
||||
/** User Agent du webhook */
|
||||
@Size(max = 500, message = "Le User Agent ne peut pas dépasser 500 caractères")
|
||||
private String userAgentSource;
|
||||
|
||||
/** Nombre de tentatives de traitement */
|
||||
private Integer nombreTentativesTraitement;
|
||||
|
||||
/** Message d'erreur de traitement (si échec) */
|
||||
@Size(max = 1000, message = "Le message d'erreur ne peut pas dépasser 1000 caractères")
|
||||
private String messageErreurTraitement;
|
||||
|
||||
/** Code d'erreur de traitement (si échec) */
|
||||
private String codeErreurTraitement;
|
||||
|
||||
/** Stack trace de l'erreur (si échec) */
|
||||
@Size(max = 3000, message = "La stack trace ne peut pas dépasser 3000 caractères")
|
||||
private String stackTraceErreur;
|
||||
|
||||
/** Webhook traité automatiquement */
|
||||
private Boolean traitementAutomatique;
|
||||
|
||||
/** Webhook nécessitant une intervention manuelle */
|
||||
private Boolean interventionManuelleRequise;
|
||||
|
||||
/** Notes de traitement manuel */
|
||||
@Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
|
||||
private String notesTraitementManuel;
|
||||
|
||||
/** Utilisateur ayant traité manuellement */
|
||||
private String utilisateurTraitementManuel;
|
||||
|
||||
/** Date du traitement manuel */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateTraitementManuel;
|
||||
|
||||
// Constructeurs
|
||||
public WaveWebhookDTO() {
|
||||
super();
|
||||
this.statutTraitement = StatutTraitement.RECU;
|
||||
this.dateReception = LocalDateTime.now();
|
||||
this.nombreTentativesTraitement = 0;
|
||||
this.traitementAutomatique = true;
|
||||
this.interventionManuelleRequise = false;
|
||||
}
|
||||
|
||||
public WaveWebhookDTO(String webhookId, TypeEvenement typeEvenement, String payloadJson) {
|
||||
this();
|
||||
this.webhookId = webhookId;
|
||||
this.typeEvenement = typeEvenement;
|
||||
this.codeEvenement = typeEvenement.getCodeWave();
|
||||
this.payloadJson = payloadJson;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
public String getWebhookId() {
|
||||
return webhookId;
|
||||
}
|
||||
|
||||
public void setWebhookId(String webhookId) {
|
||||
this.webhookId = webhookId;
|
||||
}
|
||||
|
||||
public TypeEvenement getTypeEvenement() {
|
||||
return typeEvenement;
|
||||
}
|
||||
|
||||
public void setTypeEvenement(TypeEvenement typeEvenement) {
|
||||
this.typeEvenement = typeEvenement;
|
||||
if (typeEvenement != null) {
|
||||
this.codeEvenement = typeEvenement.getCodeWave();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCodeEvenement() {
|
||||
return codeEvenement;
|
||||
}
|
||||
|
||||
public void setCodeEvenement(String codeEvenement) {
|
||||
this.codeEvenement = codeEvenement;
|
||||
this.typeEvenement = TypeEvenement.fromCode(codeEvenement);
|
||||
}
|
||||
|
||||
public StatutTraitement getStatutTraitement() {
|
||||
return statutTraitement;
|
||||
}
|
||||
|
||||
public void setStatutTraitement(StatutTraitement statutTraitement) {
|
||||
this.statutTraitement = statutTraitement;
|
||||
}
|
||||
|
||||
public String getPayloadJson() {
|
||||
return payloadJson;
|
||||
}
|
||||
|
||||
public void setPayloadJson(String payloadJson) {
|
||||
this.payloadJson = payloadJson;
|
||||
}
|
||||
|
||||
public String getHeadersHttp() {
|
||||
return headersHttp;
|
||||
}
|
||||
|
||||
public void setHeadersHttp(String headersHttp) {
|
||||
this.headersHttp = headersHttp;
|
||||
}
|
||||
|
||||
public String getSignatureWave() {
|
||||
return signatureWave;
|
||||
}
|
||||
|
||||
public void setSignatureWave(String signatureWave) {
|
||||
this.signatureWave = signatureWave;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateReception() {
|
||||
return dateReception;
|
||||
}
|
||||
|
||||
public void setDateReception(LocalDateTime dateReception) {
|
||||
this.dateReception = dateReception;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateTraitement() {
|
||||
return dateTraitement;
|
||||
}
|
||||
|
||||
public void setDateTraitement(LocalDateTime dateTraitement) {
|
||||
this.dateTraitement = dateTraitement;
|
||||
}
|
||||
|
||||
public String getSessionCheckoutId() {
|
||||
return sessionCheckoutId;
|
||||
}
|
||||
|
||||
public void setSessionCheckoutId(String sessionCheckoutId) {
|
||||
this.sessionCheckoutId = sessionCheckoutId;
|
||||
}
|
||||
|
||||
public String getTransactionWaveId() {
|
||||
return transactionWaveId;
|
||||
}
|
||||
|
||||
public void setTransactionWaveId(String transactionWaveId) {
|
||||
this.transactionWaveId = transactionWaveId;
|
||||
}
|
||||
|
||||
public BigDecimal getMontantTransaction() {
|
||||
return montantTransaction;
|
||||
}
|
||||
|
||||
public void setMontantTransaction(BigDecimal montantTransaction) {
|
||||
this.montantTransaction = montantTransaction;
|
||||
}
|
||||
|
||||
public String getDeviseTransaction() {
|
||||
return deviseTransaction;
|
||||
}
|
||||
|
||||
public void setDeviseTransaction(String deviseTransaction) {
|
||||
this.deviseTransaction = deviseTransaction;
|
||||
}
|
||||
|
||||
public String getStatutTransactionWave() {
|
||||
return statutTransactionWave;
|
||||
}
|
||||
|
||||
public void setStatutTransactionWave(String statutTransactionWave) {
|
||||
this.statutTransactionWave = statutTransactionWave;
|
||||
}
|
||||
|
||||
public UUID getOrganisationId() {
|
||||
return organisationId;
|
||||
}
|
||||
|
||||
public void setOrganisationId(UUID organisationId) {
|
||||
this.organisationId = organisationId;
|
||||
}
|
||||
|
||||
public UUID getMembreId() {
|
||||
return membreId;
|
||||
}
|
||||
|
||||
public void setMembreId(UUID membreId) {
|
||||
this.membreId = membreId;
|
||||
}
|
||||
|
||||
public String getReferenceUnionFlow() {
|
||||
return referenceUnionFlow;
|
||||
}
|
||||
|
||||
public void setReferenceUnionFlow(String referenceUnionFlow) {
|
||||
this.referenceUnionFlow = referenceUnionFlow;
|
||||
}
|
||||
|
||||
public String getTypePaiementUnionFlow() {
|
||||
return typePaiementUnionFlow;
|
||||
}
|
||||
|
||||
public void setTypePaiementUnionFlow(String typePaiementUnionFlow) {
|
||||
this.typePaiementUnionFlow = typePaiementUnionFlow;
|
||||
}
|
||||
|
||||
public String getAdresseIpSource() {
|
||||
return adresseIpSource;
|
||||
}
|
||||
|
||||
public void setAdresseIpSource(String adresseIpSource) {
|
||||
this.adresseIpSource = adresseIpSource;
|
||||
}
|
||||
|
||||
public String getUserAgentSource() {
|
||||
return userAgentSource;
|
||||
}
|
||||
|
||||
public void setUserAgentSource(String userAgentSource) {
|
||||
this.userAgentSource = userAgentSource;
|
||||
}
|
||||
|
||||
public Integer getNombreTentativesTraitement() {
|
||||
return nombreTentativesTraitement;
|
||||
}
|
||||
|
||||
public void setNombreTentativesTraitement(Integer nombreTentativesTraitement) {
|
||||
this.nombreTentativesTraitement = nombreTentativesTraitement;
|
||||
}
|
||||
|
||||
public String getMessageErreurTraitement() {
|
||||
return messageErreurTraitement;
|
||||
}
|
||||
|
||||
public void setMessageErreurTraitement(String messageErreurTraitement) {
|
||||
this.messageErreurTraitement = messageErreurTraitement;
|
||||
}
|
||||
|
||||
public String getCodeErreurTraitement() {
|
||||
return codeErreurTraitement;
|
||||
}
|
||||
|
||||
public void setCodeErreurTraitement(String codeErreurTraitement) {
|
||||
this.codeErreurTraitement = codeErreurTraitement;
|
||||
}
|
||||
|
||||
public String getStackTraceErreur() {
|
||||
return stackTraceErreur;
|
||||
}
|
||||
|
||||
public void setStackTraceErreur(String stackTraceErreur) {
|
||||
this.stackTraceErreur = stackTraceErreur;
|
||||
}
|
||||
|
||||
public Boolean getTraitementAutomatique() {
|
||||
return traitementAutomatique;
|
||||
}
|
||||
|
||||
public void setTraitementAutomatique(Boolean traitementAutomatique) {
|
||||
this.traitementAutomatique = traitementAutomatique;
|
||||
}
|
||||
|
||||
public Boolean getInterventionManuelleRequise() {
|
||||
return interventionManuelleRequise;
|
||||
}
|
||||
|
||||
public void setInterventionManuelleRequise(Boolean interventionManuelleRequise) {
|
||||
this.interventionManuelleRequise = interventionManuelleRequise;
|
||||
}
|
||||
|
||||
public String getNotesTraitementManuel() {
|
||||
return notesTraitementManuel;
|
||||
}
|
||||
|
||||
public void setNotesTraitementManuel(String notesTraitementManuel) {
|
||||
this.notesTraitementManuel = notesTraitementManuel;
|
||||
}
|
||||
|
||||
public String getUtilisateurTraitementManuel() {
|
||||
return utilisateurTraitementManuel;
|
||||
}
|
||||
|
||||
public void setUtilisateurTraitementManuel(String utilisateurTraitementManuel) {
|
||||
this.utilisateurTraitementManuel = utilisateurTraitementManuel;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateTraitementManuel() {
|
||||
return dateTraitementManuel;
|
||||
}
|
||||
|
||||
public void setDateTraitementManuel(LocalDateTime dateTraitementManuel) {
|
||||
this.dateTraitementManuel = dateTraitementManuel;
|
||||
}
|
||||
|
||||
// Méthodes utilitaires
|
||||
|
||||
/**
|
||||
* Vérifie si le webhook concerne un checkout
|
||||
*
|
||||
* @return true si c'est un événement de checkout
|
||||
*/
|
||||
public boolean isEvenementCheckout() {
|
||||
return typeEvenement == TypeEvenement.CHECKOUT_COMPLETE
|
||||
|| typeEvenement == TypeEvenement.CHECKOUT_CANCELLED
|
||||
|| typeEvenement == TypeEvenement.CHECKOUT_EXPIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le webhook concerne un payout
|
||||
*
|
||||
* @return true si c'est un événement de payout
|
||||
*/
|
||||
public boolean isEvenementPayout() {
|
||||
return typeEvenement == TypeEvenement.PAYOUT_COMPLETE
|
||||
|| typeEvenement == TypeEvenement.PAYOUT_FAILED;
|
||||
}
|
||||
|
||||
/** Marque le webhook comme traité avec succès */
|
||||
public void marquerCommeTraite() {
|
||||
this.statutTraitement = StatutTraitement.TRAITE;
|
||||
this.dateTraitement = LocalDateTime.now();
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque le webhook comme échoué
|
||||
*
|
||||
* @param messageErreur Le message d'erreur
|
||||
* @param codeErreur Le code d'erreur
|
||||
*/
|
||||
public void marquerCommeEchec(String messageErreur, String codeErreur) {
|
||||
this.statutTraitement = StatutTraitement.ECHEC;
|
||||
this.messageErreurTraitement = messageErreur;
|
||||
this.codeErreurTraitement = codeErreur;
|
||||
this.nombreTentativesTraitement++;
|
||||
this.dateTraitement = LocalDateTime.now();
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
/** Démarre le traitement du webhook */
|
||||
public void demarrerTraitement() {
|
||||
this.statutTraitement = StatutTraitement.EN_COURS;
|
||||
this.nombreTentativesTraitement++;
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WaveWebhookDTO{"
|
||||
+ "webhookId='"
|
||||
+ webhookId
|
||||
+ '\''
|
||||
+ ", typeEvenement="
|
||||
+ typeEvenement
|
||||
+ ", statutTraitement="
|
||||
+ statutTraitement
|
||||
+ ", dateReception="
|
||||
+ dateReception
|
||||
+ ", sessionCheckoutId='"
|
||||
+ sessionCheckoutId
|
||||
+ '\''
|
||||
+ ", montantTransaction="
|
||||
+ montantTransaction
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les bénéficiaires d'une aide
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class BeneficiaireAideDTO {
|
||||
|
||||
/** Identifiant unique du bénéficiaire */
|
||||
private String id;
|
||||
|
||||
/** Nom complet du bénéficiaire */
|
||||
@NotBlank(message = "Le nom du bénéficiaire est obligatoire")
|
||||
@Size(max = 100, message = "Le nom ne peut pas dépasser 100 caractères")
|
||||
private String nomComplet;
|
||||
|
||||
/** Relation avec le demandeur */
|
||||
@NotBlank(message = "La relation avec le demandeur est obligatoire")
|
||||
private String relationDemandeur;
|
||||
|
||||
/** Date de naissance */
|
||||
private LocalDate dateNaissance;
|
||||
|
||||
/** Âge calculé */
|
||||
private Integer age;
|
||||
|
||||
/** Genre */
|
||||
private String genre;
|
||||
|
||||
/** Numéro de téléphone */
|
||||
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone n'est pas valide")
|
||||
private String telephone;
|
||||
|
||||
/** Adresse email */
|
||||
@Email(message = "L'adresse email n'est pas valide")
|
||||
private String email;
|
||||
|
||||
/** Adresse physique */
|
||||
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
|
||||
private String adresse;
|
||||
|
||||
/** Situation particulière (handicap, maladie, etc.) */
|
||||
@Size(max = 500, message = "La situation particulière ne peut pas dépasser 500 caractères")
|
||||
private String situationParticuliere;
|
||||
|
||||
/** Indique si le bénéficiaire est le demandeur principal */
|
||||
@Builder.Default private Boolean estDemandeurPrincipal = false;
|
||||
|
||||
/** Pourcentage de l'aide destiné à ce bénéficiaire */
|
||||
@DecimalMin(value = "0.0", message = "Le pourcentage doit être positif")
|
||||
@DecimalMax(value = "100.0", message = "Le pourcentage ne peut pas dépasser 100%")
|
||||
private Double pourcentageAide;
|
||||
|
||||
/** Montant spécifique pour ce bénéficiaire */
|
||||
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||
private Double montantSpecifique;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les commentaires sur une demande d'aide
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CommentaireAideDTO {
|
||||
|
||||
/** Identifiant unique du commentaire */
|
||||
private String id;
|
||||
|
||||
/** Contenu du commentaire */
|
||||
@NotBlank(message = "Le contenu du commentaire est obligatoire")
|
||||
@Size(min = 5, max = 2000, message = "Le commentaire doit contenir entre 5 et 2000 caractères")
|
||||
private String contenu;
|
||||
|
||||
/** Type de commentaire */
|
||||
@NotBlank(message = "Le type de commentaire est obligatoire")
|
||||
private String typeCommentaire;
|
||||
|
||||
/** Date de création du commentaire */
|
||||
@NotNull(message = "La date de création est obligatoire")
|
||||
@Builder.Default
|
||||
private LocalDateTime dateCreation = LocalDateTime.now();
|
||||
|
||||
/** Date de dernière modification */
|
||||
private LocalDateTime dateModification;
|
||||
|
||||
/** Identifiant de l'auteur du commentaire */
|
||||
@NotBlank(message = "L'identifiant de l'auteur est obligatoire")
|
||||
private String auteurId;
|
||||
|
||||
/** Nom de l'auteur du commentaire */
|
||||
private String auteurNom;
|
||||
|
||||
/** Rôle de l'auteur */
|
||||
private String auteurRole;
|
||||
|
||||
/** Indique si le commentaire est privé (visible seulement aux évaluateurs) */
|
||||
@Builder.Default private Boolean estPrive = false;
|
||||
|
||||
/** Indique si le commentaire est important */
|
||||
@Builder.Default private Boolean estImportant = false;
|
||||
|
||||
/** Identifiant du commentaire parent (pour les réponses) */
|
||||
private String commentaireParentId;
|
||||
|
||||
/** Réponses à ce commentaire */
|
||||
private List<CommentaireAideDTO> reponses;
|
||||
|
||||
/** Pièces jointes au commentaire */
|
||||
private List<PieceJustificativeDTO> piecesJointes;
|
||||
|
||||
/** Mentions d'utilisateurs dans le commentaire */
|
||||
private List<String> mentionsUtilisateurs;
|
||||
|
||||
/** Indique si le commentaire a été modifié */
|
||||
@Builder.Default private Boolean estModifie = false;
|
||||
|
||||
/** Nombre de likes/réactions */
|
||||
@Builder.Default private Integer nombreReactions = 0;
|
||||
|
||||
/** Indique si le commentaire est résolu (pour les questions) */
|
||||
@Builder.Default private Boolean estResolu = false;
|
||||
|
||||
/** Date de résolution */
|
||||
private LocalDateTime dateResolution;
|
||||
|
||||
/** Identifiant de la personne qui a marqué comme résolu */
|
||||
private String resoluteurId;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les informations de contact du proposant d'aide
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ContactProposantDTO {
|
||||
|
||||
/** Numéro de téléphone principal */
|
||||
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone n'est pas valide")
|
||||
private String telephonePrincipal;
|
||||
|
||||
/** Numéro de téléphone secondaire */
|
||||
@Pattern(
|
||||
regexp = "^\\+?[0-9]{8,15}$",
|
||||
message = "Le numéro de téléphone secondaire n'est pas valide")
|
||||
private String telephoneSecondaire;
|
||||
|
||||
/** Adresse email */
|
||||
@Email(message = "L'adresse email n'est pas valide")
|
||||
private String email;
|
||||
|
||||
/** Adresse email secondaire */
|
||||
@Email(message = "L'adresse email secondaire n'est pas valide")
|
||||
private String emailSecondaire;
|
||||
|
||||
/** Identifiant WhatsApp */
|
||||
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro WhatsApp n'est pas valide")
|
||||
private String whatsapp;
|
||||
|
||||
/** Identifiant Telegram */
|
||||
@Size(max = 50, message = "L'identifiant Telegram ne peut pas dépasser 50 caractères")
|
||||
private String telegram;
|
||||
|
||||
/** Autres moyens de contact (réseaux sociaux, etc.) */
|
||||
private java.util.Map<String, String> autresContacts;
|
||||
|
||||
/** Adresse physique pour rencontres */
|
||||
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
|
||||
private String adressePhysique;
|
||||
|
||||
/** Indique si les rencontres physiques sont possibles */
|
||||
@Builder.Default private Boolean rencontresPhysiquesPossibles = false;
|
||||
|
||||
/** Indique si les appels téléphoniques sont acceptés */
|
||||
@Builder.Default private Boolean appelsAcceptes = true;
|
||||
|
||||
/** Indique si les SMS sont acceptés */
|
||||
@Builder.Default private Boolean smsAcceptes = true;
|
||||
|
||||
/** Indique si les emails sont acceptés */
|
||||
@Builder.Default private Boolean emailsAcceptes = true;
|
||||
|
||||
/** Horaires de disponibilité pour contact */
|
||||
@Size(max = 200, message = "Les horaires ne peuvent pas dépasser 200 caractères")
|
||||
private String horairesDisponibilite;
|
||||
|
||||
/** Langue(s) de communication préférée(s) */
|
||||
private java.util.List<String> languesPreferees;
|
||||
|
||||
/** Instructions spéciales pour le contact */
|
||||
@Size(max = 300, message = "Les instructions ne peuvent pas dépasser 300 caractères")
|
||||
private String instructionsSpeciales;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les informations de contact d'urgence
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ContactUrgenceDTO {
|
||||
|
||||
/** Nom complet du contact d'urgence */
|
||||
@NotBlank(message = "Le nom du contact d'urgence est obligatoire")
|
||||
@Size(max = 100, message = "Le nom ne peut pas dépasser 100 caractères")
|
||||
private String nomComplet;
|
||||
|
||||
/** Relation avec le demandeur */
|
||||
@NotBlank(message = "La relation avec le demandeur est obligatoire")
|
||||
@Size(max = 50, message = "La relation ne peut pas dépasser 50 caractères")
|
||||
private String relation;
|
||||
|
||||
/** Numéro de téléphone principal */
|
||||
@NotBlank(message = "Le numéro de téléphone est obligatoire")
|
||||
@Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Le numéro de téléphone n'est pas valide")
|
||||
private String telephonePrincipal;
|
||||
|
||||
/** Numéro de téléphone secondaire */
|
||||
@Pattern(
|
||||
regexp = "^\\+?[0-9]{8,15}$",
|
||||
message = "Le numéro de téléphone secondaire n'est pas valide")
|
||||
private String telephoneSecondaire;
|
||||
|
||||
/** Adresse email */
|
||||
@Email(message = "L'adresse email n'est pas valide")
|
||||
private String email;
|
||||
|
||||
/** Adresse physique */
|
||||
@Size(max = 200, message = "L'adresse ne peut pas dépasser 200 caractères")
|
||||
private String adresse;
|
||||
|
||||
/** Disponibilité (horaires) */
|
||||
@Size(max = 100, message = "La disponibilité ne peut pas dépasser 100 caractères")
|
||||
private String disponibilite;
|
||||
|
||||
/** Indique si ce contact peut prendre des décisions pour le demandeur */
|
||||
@Builder.Default private Boolean peutPrendreDecisions = false;
|
||||
|
||||
/** Indique si ce contact doit être notifié automatiquement */
|
||||
@Builder.Default private Boolean notificationAutomatique = true;
|
||||
|
||||
/** Commentaires additionnels */
|
||||
@Size(max = 300, message = "Les commentaires ne peuvent pas dépasser 300 caractères")
|
||||
private String commentaires;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les créneaux de disponibilité du proposant
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CreneauDisponibiliteDTO {
|
||||
|
||||
/** Identifiant unique du créneau */
|
||||
private String id;
|
||||
|
||||
/** Jour de la semaine (pour créneaux récurrents) */
|
||||
private DayOfWeek jourSemaine;
|
||||
|
||||
/** Date spécifique (pour créneaux ponctuels) */
|
||||
private LocalDate dateSpecifique;
|
||||
|
||||
/** Heure de début */
|
||||
@NotNull(message = "L'heure de début est obligatoire")
|
||||
private LocalTime heureDebut;
|
||||
|
||||
/** Heure de fin */
|
||||
@NotNull(message = "L'heure de fin est obligatoire")
|
||||
private LocalTime heureFin;
|
||||
|
||||
/** Type de créneau */
|
||||
@NotNull(message = "Le type de créneau est obligatoire")
|
||||
@Builder.Default
|
||||
private TypeCreneau type = TypeCreneau.RECURRENT;
|
||||
|
||||
/** Indique si le créneau est actif */
|
||||
@Builder.Default private Boolean estActif = true;
|
||||
|
||||
/** Fuseau horaire */
|
||||
@Builder.Default private String fuseauHoraire = "Africa/Abidjan";
|
||||
|
||||
/** Commentaires sur le créneau */
|
||||
@Size(max = 200, message = "Les commentaires ne peuvent pas dépasser 200 caractères")
|
||||
private String commentaires;
|
||||
|
||||
/** Priorité du créneau (1 = haute, 5 = basse) */
|
||||
@Min(value = 1, message = "La priorité doit être au moins 1")
|
||||
@Max(value = 5, message = "La priorité ne peut pas dépasser 5")
|
||||
@Builder.Default
|
||||
private Integer priorite = 3;
|
||||
|
||||
/** Durée maximale d'intervention en minutes */
|
||||
@Min(value = 15, message = "La durée doit être au moins 15 minutes")
|
||||
@Max(value = 480, message = "La durée ne peut pas dépasser 8 heures")
|
||||
private Integer dureeMaxMinutes;
|
||||
|
||||
/** Indique si des pauses sont nécessaires */
|
||||
@Builder.Default private Boolean pausesNecessaires = false;
|
||||
|
||||
/** Durée des pauses en minutes */
|
||||
@Min(value = 5, message = "La durée de pause doit être au moins 5 minutes")
|
||||
private Integer dureePauseMinutes;
|
||||
|
||||
/** Énumération des types de créneaux */
|
||||
public enum TypeCreneau {
|
||||
RECURRENT("Récurrent"),
|
||||
PONCTUEL("Ponctuel"),
|
||||
URGENCE("Urgence"),
|
||||
FLEXIBLE("Flexible");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeCreneau(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si le créneau est valide (heure fin > heure début) */
|
||||
public boolean isValide() {
|
||||
return heureDebut != null && heureFin != null && heureFin.isAfter(heureDebut);
|
||||
}
|
||||
|
||||
/** Calcule la durée du créneau en minutes */
|
||||
public long getDureeMinutes() {
|
||||
if (!isValide()) return 0;
|
||||
return java.time.Duration.between(heureDebut, heureFin).toMinutes();
|
||||
}
|
||||
|
||||
/** Vérifie si le créneau est disponible à une date donnée */
|
||||
public boolean isDisponibleLe(LocalDate date) {
|
||||
if (!estActif) return false;
|
||||
|
||||
return switch (type) {
|
||||
case PONCTUEL -> dateSpecifique != null && dateSpecifique.equals(date);
|
||||
case RECURRENT -> jourSemaine != null && date.getDayOfWeek() == jourSemaine;
|
||||
case URGENCE, FLEXIBLE -> true;
|
||||
};
|
||||
}
|
||||
|
||||
/** Vérifie si une heure est dans le créneau */
|
||||
public boolean contientHeure(LocalTime heure) {
|
||||
if (!isValide()) return false;
|
||||
return !heure.isBefore(heureDebut) && !heure.isAfter(heureFin);
|
||||
}
|
||||
|
||||
/** Retourne le libellé du créneau */
|
||||
public String getLibelle() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (type == TypeCreneau.RECURRENT && jourSemaine != null) {
|
||||
sb.append(jourSemaine.name()).append(" ");
|
||||
} else if (type == TypeCreneau.PONCTUEL && dateSpecifique != null) {
|
||||
sb.append(dateSpecifique.toString()).append(" ");
|
||||
}
|
||||
|
||||
sb.append(heureDebut.toString()).append(" - ").append(heureFin.toString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les critères de sélection des bénéficiaires
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CritereSelectionDTO {
|
||||
|
||||
/** Nom du critère */
|
||||
@NotBlank(message = "Le nom du critère est obligatoire")
|
||||
@Size(max = 100, message = "Le nom ne peut pas dépasser 100 caractères")
|
||||
private String nom;
|
||||
|
||||
/** Type de critère (age, situation, localisation, etc.) */
|
||||
@NotBlank(message = "Le type de critère est obligatoire")
|
||||
private String type;
|
||||
|
||||
/** Opérateur de comparaison (equals, greater_than, less_than, contains, etc.) */
|
||||
@NotBlank(message = "L'opérateur est obligatoire")
|
||||
private String operateur;
|
||||
|
||||
/** Valeur de référence pour la comparaison */
|
||||
@NotBlank(message = "La valeur est obligatoire")
|
||||
private String valeur;
|
||||
|
||||
/** Valeur maximale (pour les plages) */
|
||||
private String valeurMax;
|
||||
|
||||
/** Indique si le critère est obligatoire */
|
||||
@Builder.Default private Boolean estObligatoire = false;
|
||||
|
||||
/** Poids du critère dans la sélection (1-10) */
|
||||
@Min(value = 1, message = "Le poids doit être au moins 1")
|
||||
@Max(value = 10, message = "Le poids ne peut pas dépasser 10")
|
||||
@Builder.Default
|
||||
private Integer poids = 5;
|
||||
|
||||
/** Description du critère */
|
||||
@Size(max = 200, message = "La description ne peut pas dépasser 200 caractères")
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import dev.lions.unionflow.server.api.validation.ValidationConstants;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO unifié pour les demandes d'aide dans le système de solidarité
|
||||
*
|
||||
* <p>Ce DTO représente une demande d'aide complète avec toutes les informations nécessaires pour le
|
||||
* traitement, l'évaluation et le suivi. Remplace l'ancien AideDTO pour une approche unifiée.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DemandeAideDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// === IDENTIFICATION ===
|
||||
|
||||
// ID hérité de BaseDTO
|
||||
|
||||
/** Numéro de référence de la demande (généré automatiquement) */
|
||||
@Pattern(
|
||||
regexp = ValidationConstants.REFERENCE_AIDE_PATTERN,
|
||||
message = ValidationConstants.REFERENCE_AIDE_MESSAGE)
|
||||
private String numeroReference;
|
||||
|
||||
// === INFORMATIONS DE BASE ===
|
||||
|
||||
/** Type d'aide demandée */
|
||||
@NotNull(message = "Le type d'aide est obligatoire.")
|
||||
private TypeAide typeAide;
|
||||
|
||||
/** Titre court de la demande */
|
||||
@NotBlank(message = "Le titre" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Size(
|
||||
min = ValidationConstants.TITRE_MIN_LENGTH,
|
||||
max = ValidationConstants.TITRE_MAX_LENGTH,
|
||||
message = ValidationConstants.TITRE_SIZE_MESSAGE)
|
||||
private String titre;
|
||||
|
||||
/** Description détaillée de la demande */
|
||||
@NotBlank(message = "La description" + ValidationConstants.OBLIGATOIRE_MESSAGE)
|
||||
@Size(
|
||||
min = ValidationConstants.DESCRIPTION_MIN_LENGTH,
|
||||
max = ValidationConstants.DESCRIPTION_MAX_LENGTH,
|
||||
message = ValidationConstants.DESCRIPTION_SIZE_MESSAGE)
|
||||
private String description;
|
||||
|
||||
/** Justification de la demande */
|
||||
@Size(
|
||||
max = ValidationConstants.JUSTIFICATION_MAX_LENGTH,
|
||||
message = ValidationConstants.JUSTIFICATION_SIZE_MESSAGE)
|
||||
private String justification;
|
||||
|
||||
// === MONTANT ET FINANCES ===
|
||||
|
||||
/** Montant demandé (si applicable) */
|
||||
@DecimalMin(
|
||||
value = ValidationConstants.MONTANT_MIN_VALUE,
|
||||
inclusive = false,
|
||||
message = ValidationConstants.MONTANT_POSITIF_MESSAGE)
|
||||
@Digits(
|
||||
integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
|
||||
fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
|
||||
message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
|
||||
private BigDecimal montantDemande;
|
||||
|
||||
/** Montant approuvé (si différent du montant demandé) */
|
||||
@DecimalMin(
|
||||
value = ValidationConstants.MONTANT_MIN_VALUE,
|
||||
inclusive = false,
|
||||
message = ValidationConstants.MONTANT_POSITIF_MESSAGE)
|
||||
@Digits(
|
||||
integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
|
||||
fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
|
||||
message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
|
||||
private BigDecimal montantApprouve;
|
||||
|
||||
/** Montant versé effectivement */
|
||||
@DecimalMin(
|
||||
value = ValidationConstants.MONTANT_MIN_VALUE,
|
||||
inclusive = false,
|
||||
message = ValidationConstants.MONTANT_POSITIF_MESSAGE)
|
||||
@Digits(
|
||||
integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
|
||||
fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
|
||||
message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
|
||||
private BigDecimal montantVerse;
|
||||
|
||||
/** Devise du montant (code ISO 3 lettres) */
|
||||
@Pattern(
|
||||
regexp = ValidationConstants.DEVISE_PATTERN,
|
||||
message = ValidationConstants.DEVISE_MESSAGE)
|
||||
private String devise = "XOF";
|
||||
|
||||
// === ACTEURS ===
|
||||
|
||||
/** Identifiant du demandeur (UUID) */
|
||||
@NotNull(message = "L'identifiant du demandeur est obligatoire")
|
||||
private UUID membreDemandeurId;
|
||||
|
||||
/** Nom complet du demandeur */
|
||||
private String nomDemandeur;
|
||||
|
||||
/** Numéro de membre du demandeur */
|
||||
private String numeroMembreDemandeur;
|
||||
|
||||
/** Identifiant de l'évaluateur assigné */
|
||||
private String evaluateurId;
|
||||
|
||||
/** Nom de l'évaluateur */
|
||||
private String evaluateurNom;
|
||||
|
||||
/** Identifiant de l'approbateur */
|
||||
private String approvateurId;
|
||||
|
||||
/** Nom de l'approbateur */
|
||||
private String approvateurNom;
|
||||
|
||||
/** Identifiant de l'organisation (UUID) */
|
||||
@NotNull(message = "L'identifiant de l'organisation est obligatoire")
|
||||
private UUID associationId;
|
||||
|
||||
/** Nom de l'association */
|
||||
private String nomAssociation;
|
||||
|
||||
// === STATUT ET PRIORITÉ ===
|
||||
|
||||
/** Statut actuel de la demande */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
private StatutAide statut = StatutAide.BROUILLON;
|
||||
|
||||
/** Priorité de la demande */
|
||||
@NotNull(message = "La priorité est obligatoire")
|
||||
private PrioriteAide priorite = PrioriteAide.NORMALE;
|
||||
|
||||
/** Motif de rejet (si applicable) */
|
||||
@Size(max = 500, message = "Le motif de rejet ne peut pas dépasser 500 caractères")
|
||||
private String motifRejet;
|
||||
|
||||
/** Commentaires de l'évaluateur */
|
||||
@Size(max = 1000, message = "Les commentaires ne peuvent pas dépasser 1000 caractères")
|
||||
private String commentairesEvaluateur;
|
||||
|
||||
// === DATES ===
|
||||
|
||||
// Date de création héritée de BaseDTO
|
||||
|
||||
/** Date de soumission de la demande */
|
||||
private LocalDateTime dateSoumission;
|
||||
|
||||
/** Date limite de traitement */
|
||||
private LocalDateTime dateLimiteTraitement;
|
||||
|
||||
/** Date d'évaluation */
|
||||
private LocalDateTime dateEvaluation;
|
||||
|
||||
/** Date d'approbation */
|
||||
private LocalDateTime dateApprobation;
|
||||
|
||||
/** Date de versement */
|
||||
private LocalDateTime dateVersement;
|
||||
|
||||
/** Date de clôture */
|
||||
private LocalDateTime dateCloture;
|
||||
|
||||
// Date de modification héritée de BaseDTO
|
||||
|
||||
// === INFORMATIONS COMPLÉMENTAIRES ===
|
||||
|
||||
/** Pièces justificatives attachées */
|
||||
private List<PieceJustificativeDTO> piecesJustificatives;
|
||||
|
||||
/** Bénéficiaires de l'aide (si différents du demandeur) */
|
||||
private List<BeneficiaireAideDTO> beneficiaires;
|
||||
|
||||
/** Historique des changements de statut */
|
||||
private List<HistoriqueStatutDTO> historiqueStatuts;
|
||||
|
||||
/** Commentaires et échanges */
|
||||
private List<CommentaireAideDTO> commentaires;
|
||||
|
||||
/** Données personnalisées spécifiques au type d'aide */
|
||||
private Map<String, Object> donneesPersonnalisees;
|
||||
|
||||
/** Tags pour catégorisation */
|
||||
private List<String> tags;
|
||||
|
||||
// === MÉTADONNÉES ===
|
||||
|
||||
/** Indique si la demande est confidentielle */
|
||||
private Boolean estConfidentielle = false;
|
||||
|
||||
/** Indique si la demande nécessite un suivi */
|
||||
private Boolean necessiteSuivi = false;
|
||||
|
||||
/** Score de priorité calculé automatiquement */
|
||||
private Double scorePriorite;
|
||||
|
||||
/** Nombre de vues de la demande */
|
||||
private Integer nombreVues = 0;
|
||||
|
||||
// Version héritée de BaseDTO
|
||||
|
||||
/** Informations de géolocalisation (si pertinent) */
|
||||
private LocalisationDTO localisation;
|
||||
|
||||
/** Informations de contact d'urgence */
|
||||
private ContactUrgenceDTO contactUrgence;
|
||||
|
||||
// === CHAMPS ADDITIONNELS D'AIDE ===
|
||||
|
||||
/** Date limite pour l'aide */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateLimite;
|
||||
|
||||
/** Justificatifs fournis */
|
||||
private Boolean justificatifsFournis = false;
|
||||
|
||||
/** Liste des documents joints (noms de fichiers) */
|
||||
@Size(max = 1000, message = "La liste des documents ne peut pas dépasser 1000 caractères")
|
||||
private String documentsJoints;
|
||||
|
||||
/** Date de début de l'aide */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateDebutAide;
|
||||
|
||||
/** Date de fin de l'aide */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate dateFinAide;
|
||||
|
||||
/** Identifiant du membre aidant */
|
||||
private UUID membreAidantId;
|
||||
|
||||
/** Nom du membre aidant */
|
||||
private String nomAidant;
|
||||
|
||||
/** Mode de versement */
|
||||
@Size(max = 50, message = "Le mode de versement ne peut pas dépasser 50 caractères")
|
||||
private String modeVersement;
|
||||
|
||||
/** Numéro de transaction */
|
||||
@Size(max = 100, message = "Le numéro de transaction ne peut pas dépasser 100 caractères")
|
||||
private String numeroTransaction;
|
||||
|
||||
/** Identifiant de celui qui a rejeté */
|
||||
private UUID rejeteParId;
|
||||
|
||||
/** Nom de celui qui a rejeté */
|
||||
private String rejetePar;
|
||||
|
||||
/** Date de rejet */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime dateRejet;
|
||||
|
||||
/** Raison du rejet (si applicable) */
|
||||
@Size(max = 500, message = "La raison du rejet ne peut pas dépasser 500 caractères")
|
||||
private String raisonRejet;
|
||||
|
||||
// === CONSTRUCTEURS ===
|
||||
|
||||
/** Constructeur par défaut */
|
||||
public DemandeAideDTO() {
|
||||
super(); // Appelle le constructeur de BaseDTO qui génère l'UUID
|
||||
this.statut = StatutAide.EN_ATTENTE;
|
||||
this.priorite = PrioriteAide.NORMALE;
|
||||
this.devise = "XOF";
|
||||
this.nombreVues = 0;
|
||||
this.numeroReference = genererNumeroReference();
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si la demande est modifiable */
|
||||
public boolean estModifiable() {
|
||||
return statut != null && statut.permetModification();
|
||||
}
|
||||
|
||||
/** Vérifie si la demande peut être annulée */
|
||||
public boolean peutEtreAnnulee() {
|
||||
return statut != null && statut.permetAnnulation();
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est urgente */
|
||||
public boolean estUrgente() {
|
||||
return priorite != null && priorite.isUrgente();
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est terminée */
|
||||
public boolean estTerminee() {
|
||||
return statut != null && statut.isEstFinal();
|
||||
}
|
||||
|
||||
/** Vérifie si la demande est en succès */
|
||||
public boolean estEnSucces() {
|
||||
return statut != null && statut.isSucces();
|
||||
}
|
||||
|
||||
/** Calcule le pourcentage d'avancement */
|
||||
public double getPourcentageAvancement() {
|
||||
if (statut == null) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return switch (statut) {
|
||||
case BROUILLON -> 5.0;
|
||||
case SOUMISE -> 10.0;
|
||||
case EN_ATTENTE -> 20.0;
|
||||
case EN_COURS_EVALUATION -> 40.0;
|
||||
case INFORMATIONS_REQUISES -> 35.0;
|
||||
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> 60.0;
|
||||
case EN_COURS_TRAITEMENT -> 70.0;
|
||||
case EN_COURS_VERSEMENT -> 85.0;
|
||||
case VERSEE, LIVREE, TERMINEE -> 100.0;
|
||||
case REJETEE, ANNULEE, EXPIREE -> 100.0;
|
||||
case SUSPENDUE -> 50.0;
|
||||
case EN_SUIVI -> 95.0;
|
||||
case CLOTUREE -> 100.0;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne le délai restant en heures */
|
||||
public long getDelaiRestantHeures() {
|
||||
if (dateLimiteTraitement == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
if (maintenant.isAfter(dateLimiteTraitement)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return java.time.Duration.between(maintenant, dateLimiteTraitement).toHours();
|
||||
}
|
||||
|
||||
/** Vérifie si le délai est dépassé */
|
||||
public boolean estDelaiDepasse() {
|
||||
return getDelaiRestantHeures() == 0;
|
||||
}
|
||||
|
||||
/** Retourne la durée de traitement en jours */
|
||||
public long getDureeTraitementJours() {
|
||||
if (dateCreation == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LocalDateTime dateFin = dateCloture != null ? dateCloture : LocalDateTime.now();
|
||||
return java.time.Duration.between(dateCreation, dateFin).toDays();
|
||||
}
|
||||
|
||||
// === MÉTHODES MÉTIER D'AIDE ===
|
||||
|
||||
/** Retourne le libellé du statut */
|
||||
public String getStatutLibelle() {
|
||||
return statut != null ? statut.getLibelle() : "Non défini";
|
||||
}
|
||||
|
||||
/** Retourne le libellé de la priorité */
|
||||
public String getPrioriteLibelle() {
|
||||
return priorite != null ? priorite.getLibelle() : "Normale";
|
||||
}
|
||||
|
||||
/** Approuve la demande d'aide */
|
||||
public void approuver(
|
||||
UUID evaluateurId, String nomEvaluateur, BigDecimal montantApprouve, String commentaires) {
|
||||
this.statut = StatutAide.APPROUVEE;
|
||||
this.evaluateurId = evaluateurId.toString();
|
||||
this.evaluateurNom = nomEvaluateur;
|
||||
this.montantApprouve = montantApprouve;
|
||||
this.commentairesEvaluateur = commentaires;
|
||||
this.dateEvaluation = LocalDateTime.now();
|
||||
this.dateApprobation = LocalDateTime.now();
|
||||
marquerCommeModifie(nomEvaluateur);
|
||||
}
|
||||
|
||||
/** Rejette la demande d'aide */
|
||||
public void rejeter(UUID evaluateurId, String nomEvaluateur, String raison) {
|
||||
this.statut = StatutAide.REJETEE;
|
||||
this.rejeteParId = evaluateurId;
|
||||
this.rejetePar = nomEvaluateur;
|
||||
this.raisonRejet = raison;
|
||||
this.dateRejet = LocalDateTime.now();
|
||||
this.dateEvaluation = LocalDateTime.now();
|
||||
marquerCommeModifie(nomEvaluateur);
|
||||
}
|
||||
|
||||
/** Démarre l'aide */
|
||||
public void demarrerAide(UUID aidantId, String nomAidant) {
|
||||
this.statut = StatutAide.EN_COURS_TRAITEMENT;
|
||||
this.membreAidantId = aidantId;
|
||||
this.nomAidant = nomAidant;
|
||||
this.dateDebutAide = LocalDate.now();
|
||||
marquerCommeModifie(nomAidant);
|
||||
}
|
||||
|
||||
/** Termine l'aide avec versement */
|
||||
public void terminerAvecVersement(
|
||||
BigDecimal montantVerse, String modeVersement, String numeroTransaction) {
|
||||
this.statut = StatutAide.TERMINEE;
|
||||
this.montantVerse = montantVerse;
|
||||
this.modeVersement = modeVersement;
|
||||
this.numeroTransaction = numeroTransaction;
|
||||
this.dateVersement = LocalDateTime.now();
|
||||
this.dateFinAide = LocalDate.now();
|
||||
marquerCommeModifie("SYSTEM");
|
||||
}
|
||||
|
||||
/** Incrémente le nombre de vues */
|
||||
public void incrementerVues() {
|
||||
if (nombreVues == null) {
|
||||
nombreVues = 1;
|
||||
} else {
|
||||
nombreVues++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Génère un numéro de référence unique */
|
||||
public static String genererNumeroReference() {
|
||||
return "DA-"
|
||||
+ LocalDate.now().getYear()
|
||||
+ "-"
|
||||
+ String.format("%06d", (int) (Math.random() * 1000000));
|
||||
}
|
||||
|
||||
// === GETTERS EXPLICITES POUR COMPATIBILITÉ ===
|
||||
|
||||
/** Retourne le type d'aide demandée */
|
||||
public TypeAide getTypeAide() {
|
||||
return typeAide;
|
||||
}
|
||||
|
||||
/** Retourne le montant demandé */
|
||||
public BigDecimal getMontantDemande() {
|
||||
return montantDemande;
|
||||
}
|
||||
|
||||
/** Marque comme modifié */
|
||||
public void marquerCommeModifie(String utilisateur) {
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
this.dateModification = maintenant;
|
||||
super.marquerCommeModifie(utilisateur);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour l'évaluation d'une aide reçue ou fournie
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class EvaluationAideDTO {
|
||||
|
||||
/** Identifiant unique de l'évaluation */
|
||||
private String id;
|
||||
|
||||
/** Identifiant de la demande d'aide évaluée */
|
||||
@NotBlank(message = "L'identifiant de la demande d'aide est obligatoire")
|
||||
private String demandeAideId;
|
||||
|
||||
/** Identifiant de la proposition d'aide évaluée (si applicable) */
|
||||
private String propositionAideId;
|
||||
|
||||
/** Identifiant de l'évaluateur */
|
||||
@NotBlank(message = "L'identifiant de l'évaluateur est obligatoire")
|
||||
private String evaluateurId;
|
||||
|
||||
/** Nom de l'évaluateur */
|
||||
private String evaluateurNom;
|
||||
|
||||
/** Rôle de l'évaluateur (beneficiaire, proposant, evaluateur_externe) */
|
||||
@NotBlank(message = "Le rôle de l'évaluateur est obligatoire")
|
||||
private String roleEvaluateur;
|
||||
|
||||
/** Type d'évaluation */
|
||||
@NotNull(message = "Le type d'évaluation est obligatoire")
|
||||
@Builder.Default
|
||||
private TypeEvaluation typeEvaluation = TypeEvaluation.SATISFACTION_BENEFICIAIRE;
|
||||
|
||||
/** Note globale (1-5) */
|
||||
@NotNull(message = "La note globale est obligatoire")
|
||||
@DecimalMin(value = "1.0", message = "La note doit être au moins 1")
|
||||
@DecimalMax(value = "5.0", message = "La note ne peut pas dépasser 5")
|
||||
private Double noteGlobale;
|
||||
|
||||
/** Notes détaillées par critère */
|
||||
private Map<String, Double> notesDetaillees;
|
||||
|
||||
/** Commentaire principal */
|
||||
@Size(min = 10, max = 1000, message = "Le commentaire doit contenir entre 10 et 1000 caractères")
|
||||
private String commentairePrincipal;
|
||||
|
||||
/** Points positifs */
|
||||
@Size(max = 500, message = "Les points positifs ne peuvent pas dépasser 500 caractères")
|
||||
private String pointsPositifs;
|
||||
|
||||
/** Points d'amélioration */
|
||||
@Size(max = 500, message = "Les points d'amélioration ne peuvent pas dépasser 500 caractères")
|
||||
private String pointsAmelioration;
|
||||
|
||||
/** Recommandations */
|
||||
@Size(max = 500, message = "Les recommandations ne peuvent pas dépasser 500 caractères")
|
||||
private String recommandations;
|
||||
|
||||
/** Indique si l'évaluateur recommande cette aide/proposant */
|
||||
@Builder.Default private Boolean recommande = true;
|
||||
|
||||
/** Indique si l'aide a été utile */
|
||||
@Builder.Default private Boolean aideUtile = true;
|
||||
|
||||
/** Indique si l'aide a résolu le problème */
|
||||
@Builder.Default private Boolean problemeResolu = true;
|
||||
|
||||
/** Délai de réponse perçu (1=très lent, 5=très rapide) */
|
||||
@DecimalMin(value = "1.0", message = "La note délai doit être au moins 1")
|
||||
@DecimalMax(value = "5.0", message = "La note délai ne peut pas dépasser 5")
|
||||
private Double noteDelaiReponse;
|
||||
|
||||
/** Qualité de la communication (1=très mauvaise, 5=excellente) */
|
||||
@DecimalMin(value = "1.0", message = "La note communication doit être au moins 1")
|
||||
@DecimalMax(value = "5.0", message = "La note communication ne peut pas dépasser 5")
|
||||
private Double noteCommunication;
|
||||
|
||||
/** Professionnalisme (1=très mauvais, 5=excellent) */
|
||||
@DecimalMin(value = "1.0", message = "La note professionnalisme doit être au moins 1")
|
||||
@DecimalMax(value = "5.0", message = "La note professionnalisme ne peut pas dépasser 5")
|
||||
private Double noteProfessionnalisme;
|
||||
|
||||
/** Respect des engagements (1=très mauvais, 5=excellent) */
|
||||
@DecimalMin(value = "1.0", message = "La note engagement doit être au moins 1")
|
||||
@DecimalMax(value = "5.0", message = "La note engagement ne peut pas dépasser 5")
|
||||
private Double noteRespectEngagements;
|
||||
|
||||
/** Date de création de l'évaluation */
|
||||
@NotNull(message = "La date de création est obligatoire")
|
||||
@Builder.Default
|
||||
private LocalDateTime dateCreation = LocalDateTime.now();
|
||||
|
||||
/** Date de dernière modification */
|
||||
@Builder.Default private LocalDateTime dateModification = LocalDateTime.now();
|
||||
|
||||
/** Indique si l'évaluation est publique */
|
||||
@Builder.Default private Boolean estPublique = true;
|
||||
|
||||
/** Indique si l'évaluation est anonyme */
|
||||
@Builder.Default private Boolean estAnonyme = false;
|
||||
|
||||
/** Indique si l'évaluation a été vérifiée */
|
||||
@Builder.Default private Boolean estVerifiee = false;
|
||||
|
||||
/** Date de vérification */
|
||||
private LocalDateTime dateVerification;
|
||||
|
||||
/** Identifiant du vérificateur */
|
||||
private String verificateurId;
|
||||
|
||||
/** Pièces jointes à l'évaluation (photos, documents) */
|
||||
private List<PieceJustificativeDTO> piecesJointes;
|
||||
|
||||
/** Tags associés à l'évaluation */
|
||||
private List<String> tags;
|
||||
|
||||
/** Données additionnelles */
|
||||
private Map<String, Object> donneesAdditionnelles;
|
||||
|
||||
/** Nombre de personnes qui ont trouvé cette évaluation utile */
|
||||
@Builder.Default private Integer nombreUtile = 0;
|
||||
|
||||
/** Nombre de signalements de cette évaluation */
|
||||
@Builder.Default private Integer nombreSignalements = 0;
|
||||
|
||||
/** Statut de l'évaluation */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
@Builder.Default
|
||||
private StatutEvaluation statut = StatutEvaluation.ACTIVE;
|
||||
|
||||
/** Énumération des types d'évaluation */
|
||||
public enum TypeEvaluation {
|
||||
SATISFACTION_BENEFICIAIRE("Satisfaction du bénéficiaire"),
|
||||
EVALUATION_PROPOSANT("Évaluation du proposant"),
|
||||
EVALUATION_PROCESSUS("Évaluation du processus"),
|
||||
SUIVI_POST_AIDE("Suivi post-aide"),
|
||||
EVALUATION_IMPACT("Évaluation d'impact"),
|
||||
RETOUR_EXPERIENCE("Retour d'expérience");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeEvaluation(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
/** Énumération des statuts d'évaluation */
|
||||
public enum StatutEvaluation {
|
||||
BROUILLON("Brouillon"),
|
||||
ACTIVE("Active"),
|
||||
MASQUEE("Masquée"),
|
||||
SIGNALEE("Signalée"),
|
||||
SUPPRIMEE("Supprimée");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutEvaluation(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Calcule la note moyenne des critères détaillés */
|
||||
public Double getNoteMoyenneDetaillees() {
|
||||
if (notesDetaillees == null || notesDetaillees.isEmpty()) {
|
||||
return noteGlobale;
|
||||
}
|
||||
|
||||
return notesDetaillees.values().stream()
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.average()
|
||||
.orElse(noteGlobale);
|
||||
}
|
||||
|
||||
/** Vérifie si l'évaluation est positive (note >= 4) */
|
||||
public boolean isPositive() {
|
||||
return noteGlobale != null && noteGlobale >= 4.0;
|
||||
}
|
||||
|
||||
/** Vérifie si l'évaluation est négative (note <= 2) */
|
||||
public boolean isNegative() {
|
||||
return noteGlobale != null && noteGlobale <= 2.0;
|
||||
}
|
||||
|
||||
/** Calcule un score de qualité global */
|
||||
public double getScoreQualite() {
|
||||
double score = noteGlobale != null ? noteGlobale : 0.0;
|
||||
|
||||
// Bonus pour les notes détaillées
|
||||
if (noteDelaiReponse != null) score += noteDelaiReponse * 0.1;
|
||||
if (noteCommunication != null) score += noteCommunication * 0.1;
|
||||
if (noteProfessionnalisme != null) score += noteProfessionnalisme * 0.1;
|
||||
if (noteRespectEngagements != null) score += noteRespectEngagements * 0.1;
|
||||
|
||||
// Bonus pour recommandation
|
||||
if (recommande != null && recommande) score += 0.2;
|
||||
|
||||
// Bonus pour résolution du problème
|
||||
if (problemeResolu != null && problemeResolu) score += 0.3;
|
||||
|
||||
// Malus pour signalements
|
||||
if (nombreSignalements > 0) score -= nombreSignalements * 0.1;
|
||||
|
||||
return Math.min(5.0, Math.max(0.0, score));
|
||||
}
|
||||
|
||||
/** Vérifie si l'évaluation est complète */
|
||||
public boolean isComplete() {
|
||||
return noteGlobale != null
|
||||
&& commentairePrincipal != null
|
||||
&& !commentairePrincipal.trim().isEmpty()
|
||||
&& recommande != null
|
||||
&& aideUtile != null
|
||||
&& problemeResolu != null;
|
||||
}
|
||||
|
||||
/** Retourne le niveau de satisfaction */
|
||||
public String getNiveauSatisfaction() {
|
||||
if (noteGlobale == null) return "Non évalué";
|
||||
|
||||
return switch (noteGlobale.intValue()) {
|
||||
case 5 -> "Excellent";
|
||||
case 4 -> "Très bien";
|
||||
case 3 -> "Bien";
|
||||
case 2 -> "Passable";
|
||||
case 1 -> "Insuffisant";
|
||||
default -> "Non évalué";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour l'historique des changements de statut d'une demande d'aide
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class HistoriqueStatutDTO {
|
||||
|
||||
/** Identifiant unique de l'entrée d'historique */
|
||||
private String id;
|
||||
|
||||
/** Ancien statut */
|
||||
private StatutAide ancienStatut;
|
||||
|
||||
/** Nouveau statut */
|
||||
@NotNull(message = "Le nouveau statut est obligatoire")
|
||||
private StatutAide nouveauStatut;
|
||||
|
||||
/** Date du changement de statut */
|
||||
@NotNull(message = "La date de changement est obligatoire")
|
||||
@Builder.Default
|
||||
private LocalDateTime dateChangement = LocalDateTime.now();
|
||||
|
||||
/** Identifiant de la personne qui a effectué le changement */
|
||||
@NotBlank(message = "L'identifiant de l'auteur est obligatoire")
|
||||
private String auteurId;
|
||||
|
||||
/** Nom de la personne qui a effectué le changement */
|
||||
private String auteurNom;
|
||||
|
||||
/** Motif du changement de statut */
|
||||
@Size(max = 500, message = "Le motif ne peut pas dépasser 500 caractères")
|
||||
private String motif;
|
||||
|
||||
/** Commentaires additionnels */
|
||||
@Size(max = 1000, message = "Les commentaires ne peuvent pas dépasser 1000 caractères")
|
||||
private String commentaires;
|
||||
|
||||
/** Indique si le changement est automatique (système) */
|
||||
@Builder.Default private Boolean estAutomatique = false;
|
||||
|
||||
/** Durée en minutes depuis le statut précédent */
|
||||
private Long dureeDepuisPrecedent;
|
||||
|
||||
/** Données additionnelles liées au changement */
|
||||
private java.util.Map<String, Object> donneesAdditionnelles;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les informations de géolocalisation
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class LocalisationDTO {
|
||||
|
||||
/** Latitude */
|
||||
@DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
@DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
|
||||
private Double latitude;
|
||||
|
||||
/** Longitude */
|
||||
@DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
@DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
|
||||
private Double longitude;
|
||||
|
||||
/** Adresse complète */
|
||||
@Size(max = 300, message = "L'adresse ne peut pas dépasser 300 caractères")
|
||||
private String adresseComplete;
|
||||
|
||||
/** Ville */
|
||||
@Size(max = 100, message = "La ville ne peut pas dépasser 100 caractères")
|
||||
private String ville;
|
||||
|
||||
/** Région/Province */
|
||||
@Size(max = 100, message = "La région ne peut pas dépasser 100 caractères")
|
||||
private String region;
|
||||
|
||||
/** Pays */
|
||||
@Size(max = 100, message = "Le pays ne peut pas dépasser 100 caractères")
|
||||
private String pays;
|
||||
|
||||
/** Code postal */
|
||||
@Size(max = 20, message = "Le code postal ne peut pas dépasser 20 caractères")
|
||||
private String codePostal;
|
||||
|
||||
/** Précision de la localisation en mètres */
|
||||
@Min(value = 0, message = "La précision doit être positive")
|
||||
private Double precision;
|
||||
|
||||
/** Indique si la localisation est approximative */
|
||||
@Builder.Default private Boolean estApproximative = false;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les pièces justificatives d'une demande d'aide
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class PieceJustificativeDTO {
|
||||
|
||||
/** Identifiant unique de la pièce justificative */
|
||||
private String id;
|
||||
|
||||
/** Nom du fichier */
|
||||
@NotBlank(message = "Le nom du fichier est obligatoire")
|
||||
@Size(max = 255, message = "Le nom du fichier ne peut pas dépasser 255 caractères")
|
||||
private String nomFichier;
|
||||
|
||||
/** Type de pièce justificative */
|
||||
@NotBlank(message = "Le type de pièce est obligatoire")
|
||||
private String typePiece;
|
||||
|
||||
/** Description de la pièce */
|
||||
@Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
|
||||
private String description;
|
||||
|
||||
/** URL ou chemin d'accès au fichier */
|
||||
@NotBlank(message = "L'URL du fichier est obligatoire")
|
||||
private String urlFichier;
|
||||
|
||||
/** Type MIME du fichier */
|
||||
private String typeMime;
|
||||
|
||||
/** Taille du fichier en octets */
|
||||
@Min(value = 1, message = "La taille du fichier doit être positive")
|
||||
private Long tailleFichier;
|
||||
|
||||
/** Indique si la pièce est obligatoire */
|
||||
@Builder.Default private Boolean estObligatoire = false;
|
||||
|
||||
/** Indique si la pièce a été vérifiée */
|
||||
@Builder.Default private Boolean estVerifiee = false;
|
||||
|
||||
/** Date d'ajout de la pièce */
|
||||
@Builder.Default private LocalDateTime dateAjout = LocalDateTime.now();
|
||||
|
||||
/** Date de vérification */
|
||||
private LocalDateTime dateVerification;
|
||||
|
||||
/** Identifiant de la personne qui a vérifié */
|
||||
private String verificateurId;
|
||||
|
||||
/** Commentaires sur la vérification */
|
||||
@Size(max = 500, message = "Les commentaires ne peuvent pas dépasser 500 caractères")
|
||||
private String commentairesVerification;
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package dev.lions.unionflow.server.api.dto.solidarite;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import dev.lions.unionflow.server.api.validation.ValidationConstants;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO pour les propositions d'aide dans le système de solidarité
|
||||
*
|
||||
* <p>Ce DTO représente une proposition d'aide faite par un membre pour aider soit une demande
|
||||
* spécifique, soit de manière générale.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class PropositionAideDTO {
|
||||
|
||||
// === IDENTIFICATION ===
|
||||
|
||||
/** Identifiant unique de la proposition d'aide */
|
||||
private String id;
|
||||
|
||||
/** Numéro de référence de la proposition (généré automatiquement) */
|
||||
@Pattern(
|
||||
regexp = "^PA-\\d{4}-\\d{6}$",
|
||||
message = "Le numéro de référence doit suivre le format PA-YYYY-NNNNNN")
|
||||
private String numeroReference;
|
||||
|
||||
// === INFORMATIONS DE BASE ===
|
||||
|
||||
/** Type d'aide proposée */
|
||||
@NotNull(message = "Le type d'aide proposée est obligatoire")
|
||||
private TypeAide typeAide;
|
||||
|
||||
/** Titre de la proposition */
|
||||
@NotBlank(message = "Le titre est obligatoire")
|
||||
@Size(min = 10, max = 100, message = "Le titre doit contenir entre 10 et 100 caractères")
|
||||
private String titre;
|
||||
|
||||
/** Description détaillée de l'aide proposée */
|
||||
@NotBlank(message = "La description est obligatoire")
|
||||
@Size(min = 20, max = 1000, message = "La description doit contenir entre 20 et 1000 caractères")
|
||||
private String description;
|
||||
|
||||
/** Conditions ou critères pour bénéficier de l'aide */
|
||||
@Size(max = 500, message = "Les conditions ne peuvent pas dépasser 500 caractères")
|
||||
private String conditions;
|
||||
|
||||
// === MONTANT ET CAPACITÉ ===
|
||||
|
||||
/** Montant maximum que le proposant peut offrir (si applicable) */
|
||||
@DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif")
|
||||
@DecimalMax(value = "1000000.0", message = "Le montant ne peut pas dépasser 1 000 000 FCFA")
|
||||
@Digits(
|
||||
integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
|
||||
fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
|
||||
message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
|
||||
private BigDecimal montantMaximum;
|
||||
|
||||
/** Nombre maximum de bénéficiaires */
|
||||
@Min(value = 1, message = "Le nombre de bénéficiaires doit être au moins 1")
|
||||
@Max(value = 100, message = "Le nombre de bénéficiaires ne peut pas dépasser 100")
|
||||
@Builder.Default
|
||||
private Integer nombreMaxBeneficiaires = 1;
|
||||
|
||||
/** Devise du montant */
|
||||
@Builder.Default private String devise = "FCFA";
|
||||
|
||||
// === ACTEURS ===
|
||||
|
||||
/** Identifiant du proposant */
|
||||
@NotBlank(message = "L'identifiant du proposant est obligatoire")
|
||||
private String proposantId;
|
||||
|
||||
/** Nom complet du proposant */
|
||||
private String proposantNom;
|
||||
|
||||
/** Identifiant de l'organisation du proposant */
|
||||
@NotBlank(message = "L'identifiant de l'organisation est obligatoire")
|
||||
private String organisationId;
|
||||
|
||||
/** Identifiant de la demande d'aide liée (si proposition spécifique) */
|
||||
private String demandeAideId;
|
||||
|
||||
// === STATUT ET DISPONIBILITÉ ===
|
||||
|
||||
/** Statut de la proposition */
|
||||
@NotNull(message = "Le statut est obligatoire")
|
||||
@Builder.Default
|
||||
private StatutProposition statut = StatutProposition.ACTIVE;
|
||||
|
||||
/** Indique si la proposition est disponible */
|
||||
@Builder.Default private Boolean estDisponible = true;
|
||||
|
||||
/** Indique si la proposition est récurrente */
|
||||
@Builder.Default private Boolean estRecurrente = false;
|
||||
|
||||
/** Fréquence de récurrence (si applicable) */
|
||||
private String frequenceRecurrence;
|
||||
|
||||
// === DATES ET DÉLAIS ===
|
||||
|
||||
/** Date de création de la proposition */
|
||||
@NotNull(message = "La date de création est obligatoire")
|
||||
@Builder.Default
|
||||
private LocalDateTime dateCreation = LocalDateTime.now();
|
||||
|
||||
/** Date d'expiration de la proposition */
|
||||
private LocalDateTime dateExpiration;
|
||||
|
||||
/** Date de dernière modification */
|
||||
@Builder.Default private LocalDateTime dateModification = LocalDateTime.now();
|
||||
|
||||
/** Délai de réponse souhaité en heures */
|
||||
@Min(value = 1, message = "Le délai de réponse doit être au moins 1 heure")
|
||||
@Max(value = 8760, message = "Le délai de réponse ne peut pas dépasser 1 an")
|
||||
@Builder.Default
|
||||
private Integer delaiReponseHeures = 72;
|
||||
|
||||
// === CRITÈRES ET PRÉFÉRENCES ===
|
||||
|
||||
/** Critères de sélection des bénéficiaires */
|
||||
private List<CritereSelectionDTO> criteresSelection;
|
||||
|
||||
/** Zones géographiques couvertes */
|
||||
private List<String> zonesGeographiques;
|
||||
|
||||
/** Groupes cibles (âge, situation, etc.) */
|
||||
private List<String> groupesCibles;
|
||||
|
||||
/** Compétences ou ressources disponibles */
|
||||
private List<String> competencesRessources;
|
||||
|
||||
// === CONTACT ET DISPONIBILITÉ ===
|
||||
|
||||
/** Informations de contact préférées */
|
||||
private ContactProposantDTO contactProposant;
|
||||
|
||||
/** Créneaux de disponibilité */
|
||||
private List<CreneauDisponibiliteDTO> creneauxDisponibilite;
|
||||
|
||||
/** Mode de contact préféré */
|
||||
private String modeContactPrefere;
|
||||
|
||||
// === HISTORIQUE ET SUIVI ===
|
||||
|
||||
/** Nombre de demandes traitées avec cette proposition */
|
||||
@Builder.Default private Integer nombreDemandesTraitees = 0;
|
||||
|
||||
/** Nombre de bénéficiaires aidés */
|
||||
@Builder.Default private Integer nombreBeneficiairesAides = 0;
|
||||
|
||||
/** Montant total versé */
|
||||
@Builder.Default private Double montantTotalVerse = 0.0;
|
||||
|
||||
/** Note moyenne des bénéficiaires */
|
||||
@DecimalMin(value = "0.0", message = "La note doit être positive")
|
||||
@DecimalMax(value = "5.0", message = "La note ne peut pas dépasser 5")
|
||||
private Double noteMoyenne;
|
||||
|
||||
/** Nombre d'évaluations reçues */
|
||||
@Builder.Default private Integer nombreEvaluations = 0;
|
||||
|
||||
// === MÉTADONNÉES ===
|
||||
|
||||
/** Tags pour catégorisation */
|
||||
private List<String> tags;
|
||||
|
||||
/** Données personnalisées */
|
||||
private Map<String, Object> donneesPersonnalisees;
|
||||
|
||||
/** Indique si la proposition est mise en avant */
|
||||
@Builder.Default private Boolean estMiseEnAvant = false;
|
||||
|
||||
/** Score de pertinence calculé automatiquement */
|
||||
private Double scorePertinence;
|
||||
|
||||
/** Nombre de vues de la proposition */
|
||||
@Builder.Default private Integer nombreVues = 0;
|
||||
|
||||
/** Nombre de candidatures reçues */
|
||||
@Builder.Default private Integer nombreCandidatures = 0;
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si la proposition est active et disponible */
|
||||
public boolean isActiveEtDisponible() {
|
||||
return statut == StatutProposition.ACTIVE && estDisponible && !isExpiree();
|
||||
}
|
||||
|
||||
/** Vérifie si la proposition est expirée */
|
||||
public boolean isExpiree() {
|
||||
return dateExpiration != null && LocalDateTime.now().isAfter(dateExpiration);
|
||||
}
|
||||
|
||||
/** Vérifie si la proposition peut encore accepter des bénéficiaires */
|
||||
public boolean peutAccepterBeneficiaires() {
|
||||
return isActiveEtDisponible() && nombreBeneficiairesAides < nombreMaxBeneficiaires;
|
||||
}
|
||||
|
||||
/** Calcule le pourcentage de capacité utilisée */
|
||||
public double getPourcentageCapaciteUtilisee() {
|
||||
if (nombreMaxBeneficiaires == 0) return 100.0;
|
||||
return (nombreBeneficiairesAides * 100.0) / nombreMaxBeneficiaires;
|
||||
}
|
||||
|
||||
/** Retourne le nombre de places restantes */
|
||||
public int getPlacesRestantes() {
|
||||
return Math.max(0, nombreMaxBeneficiaires - nombreBeneficiairesAides);
|
||||
}
|
||||
|
||||
/** Vérifie si la proposition correspond à un type d'aide */
|
||||
public boolean correspondAuType(TypeAide type) {
|
||||
return typeAide == type
|
||||
|| (typeAide.getCategorie().equals(type.getCategorie()) && typeAide != TypeAide.AUTRE);
|
||||
}
|
||||
|
||||
/** Calcule le score de compatibilité avec une demande */
|
||||
public double getScoreCompatibilite(DemandeAideDTO demande) {
|
||||
double score = 0.0;
|
||||
|
||||
// Correspondance exacte du type
|
||||
if (typeAide == demande.getTypeAide()) {
|
||||
score += 50.0;
|
||||
} else if (typeAide.getCategorie().equals(demande.getTypeAide().getCategorie())) {
|
||||
score += 30.0;
|
||||
}
|
||||
|
||||
// Montant compatible
|
||||
if (montantMaximum != null && demande.getMontantDemande() != null) {
|
||||
if (demande.getMontantDemande().compareTo(montantMaximum) <= 0) {
|
||||
score += 20.0;
|
||||
} else {
|
||||
score -= 10.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disponibilité
|
||||
if (peutAccepterBeneficiaires()) {
|
||||
score += 15.0;
|
||||
}
|
||||
|
||||
// Réputation
|
||||
if (noteMoyenne != null && noteMoyenne >= 4.0) {
|
||||
score += 10.0;
|
||||
}
|
||||
|
||||
// Récence
|
||||
long joursDepuisCreation =
|
||||
java.time.Duration.between(dateCreation, LocalDateTime.now()).toDays();
|
||||
if (joursDepuisCreation <= 30) {
|
||||
score += 5.0;
|
||||
}
|
||||
|
||||
return Math.min(100.0, Math.max(0.0, score));
|
||||
}
|
||||
|
||||
/** Énumération des statuts de proposition */
|
||||
public enum StatutProposition {
|
||||
BROUILLON("Brouillon"),
|
||||
ACTIVE("Active"),
|
||||
SUSPENDUE("Suspendue"),
|
||||
EXPIREE("Expirée"),
|
||||
TERMINEE("Terminée"),
|
||||
ANNULEE("Annulée");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutProposition(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package dev.lions.unionflow.server.api.dto.wave;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des comptes Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class CompteWaveDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Numéro de téléphone Wave */
|
||||
@NotBlank(message = "Le numéro de téléphone est obligatoire")
|
||||
@Pattern(
|
||||
regexp = "^\\+225[0-9]{8}$",
|
||||
message = "Le numéro de téléphone Wave doit être au format +225XXXXXXXX")
|
||||
private String numeroTelephone;
|
||||
|
||||
/** Statut du compte */
|
||||
private StatutCompteWave statutCompte;
|
||||
|
||||
/** Identifiant Wave API (encrypté) */
|
||||
private String waveAccountId;
|
||||
|
||||
/** Environnement (SANDBOX ou PRODUCTION) */
|
||||
private String environnement;
|
||||
|
||||
/** Date de dernière vérification */
|
||||
private LocalDateTime dateDerniereVerification;
|
||||
|
||||
/** Commentaires */
|
||||
private String commentaire;
|
||||
|
||||
/** ID de l'organisation (si compte d'organisation) */
|
||||
private UUID organisationId;
|
||||
|
||||
/** ID du membre (si compte de membre) */
|
||||
private UUID membreId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package dev.lions.unionflow.server.api.dto.wave;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la gestion des transactions Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TransactionWaveDTO extends BaseDTO {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Identifiant Wave de la transaction */
|
||||
@NotBlank(message = "L'identifiant Wave est obligatoire")
|
||||
private String waveTransactionId;
|
||||
|
||||
/** Identifiant de requête Wave */
|
||||
private String waveRequestId;
|
||||
|
||||
/** Référence Wave */
|
||||
private String waveReference;
|
||||
|
||||
/** Type de transaction */
|
||||
@NotNull(message = "Le type de transaction est obligatoire")
|
||||
private TypeTransactionWave typeTransaction;
|
||||
|
||||
/** Statut de la transaction */
|
||||
@NotNull(message = "Le statut de la transaction est obligatoire")
|
||||
private StatutTransactionWave statutTransaction;
|
||||
|
||||
/** Montant de la transaction */
|
||||
@NotNull(message = "Le montant est obligatoire")
|
||||
@DecimalMin(value = "0.0", message = "Le montant doit être positif")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montant;
|
||||
|
||||
/** Frais de transaction */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 10, fraction = 2)
|
||||
private BigDecimal frais;
|
||||
|
||||
/** Montant net */
|
||||
@DecimalMin(value = "0.0")
|
||||
@Digits(integer = 12, fraction = 2)
|
||||
private BigDecimal montantNet;
|
||||
|
||||
/** Code devise */
|
||||
@NotBlank(message = "Le code devise est obligatoire")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres")
|
||||
private String codeDevise;
|
||||
|
||||
/** Numéro téléphone payeur */
|
||||
private String telephonePayeur;
|
||||
|
||||
/** Numéro téléphone bénéficiaire */
|
||||
private String telephoneBeneficiaire;
|
||||
|
||||
/** Métadonnées JSON */
|
||||
private String metadonnees;
|
||||
|
||||
/** Nombre de tentatives */
|
||||
private Integer nombreTentatives;
|
||||
|
||||
/** Date de dernière tentative */
|
||||
private LocalDateTime dateDerniereTentative;
|
||||
|
||||
/** Message d'erreur */
|
||||
private String messageErreur;
|
||||
|
||||
/** ID du compte Wave */
|
||||
@NotNull(message = "Le compte Wave est obligatoire")
|
||||
private UUID compteWaveId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.abonnement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts d'abonnements UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutAbonnement {
|
||||
ACTIF("Actif"),
|
||||
SUSPENDU("Suspendu"),
|
||||
EXPIRE("Expiré"),
|
||||
ANNULE("Annulé"),
|
||||
EN_ATTENTE_PAIEMENT("En attente de paiement");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutAbonnement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dev.lions.unionflow.server.api.enums.abonnement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de formules d'abonnement UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutFormule {
|
||||
ACTIVE("Active"),
|
||||
INACTIVE("Inactive"),
|
||||
ARCHIVEE("Archivée"),
|
||||
BIENTOT_DISPONIBLE("Bientôt Disponible");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutFormule(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dev.lions.unionflow.server.api.enums.abonnement;
|
||||
|
||||
/**
|
||||
* Énumération des types de formules d'abonnement UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum TypeFormule {
|
||||
BASIC("Formule Basique"),
|
||||
STANDARD("Formule Standard"),
|
||||
PREMIUM("Formule Premium"),
|
||||
ENTERPRISE("Formule Entreprise");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeFormule(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.adresse;
|
||||
|
||||
/**
|
||||
* Énumération des types d'adresse dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeAdresse {
|
||||
SIEGE_SOCIAL("Siège Social"),
|
||||
BUREAU("Bureau"),
|
||||
DOMICILE("Domicile"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeAdresse(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
package dev.lions.unionflow.server.api.enums.analytics;
|
||||
|
||||
/**
|
||||
* Énumération des formats d'export disponibles pour les rapports et données analytics
|
||||
*
|
||||
* <p>Cette énumération définit les différents formats dans lesquels les données peuvent être
|
||||
* exportées depuis l'application UnionFlow.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum FormatExport {
|
||||
|
||||
// === FORMATS DOCUMENTS ===
|
||||
PDF("PDF", "pdf", "application/pdf", "Portable Document Format", true, true),
|
||||
WORD(
|
||||
"Word",
|
||||
"docx",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"Microsoft Word",
|
||||
true,
|
||||
false),
|
||||
|
||||
// === FORMATS TABLEURS ===
|
||||
EXCEL(
|
||||
"Excel",
|
||||
"xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"Microsoft Excel",
|
||||
true,
|
||||
true),
|
||||
CSV("CSV", "csv", "text/csv", "Comma Separated Values", false, true),
|
||||
|
||||
// === FORMATS DONNÉES ===
|
||||
JSON("JSON", "json", "application/json", "JavaScript Object Notation", false, true),
|
||||
XML("XML", "xml", "application/xml", "eXtensible Markup Language", false, false),
|
||||
|
||||
// === FORMATS IMAGES ===
|
||||
PNG("PNG", "png", "image/png", "Portable Network Graphics", true, false),
|
||||
JPEG("JPEG", "jpg", "image/jpeg", "Joint Photographic Experts Group", true, false),
|
||||
SVG("SVG", "svg", "image/svg+xml", "Scalable Vector Graphics", true, false),
|
||||
|
||||
// === FORMATS SPÉCIALISÉS ===
|
||||
POWERPOINT(
|
||||
"PowerPoint",
|
||||
"pptx",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"Microsoft PowerPoint",
|
||||
true,
|
||||
false),
|
||||
HTML("HTML", "html", "text/html", "HyperText Markup Language", true, false);
|
||||
|
||||
private final String libelle;
|
||||
private final String extension;
|
||||
private final String mimeType;
|
||||
private final String description;
|
||||
private final boolean supporteGraphiques;
|
||||
private final boolean supporteGrandesQuantitesDonnees;
|
||||
|
||||
/**
|
||||
* Constructeur de l'énumération FormatExport
|
||||
*
|
||||
* @param libelle Le libellé affiché à l'utilisateur
|
||||
* @param extension L'extension de fichier
|
||||
* @param mimeType Le type MIME du format
|
||||
* @param description La description du format
|
||||
* @param supporteGraphiques true si le format supporte les graphiques
|
||||
* @param supporteGrandesQuantitesDonnees true si le format supporte de grandes quantités de
|
||||
* données
|
||||
*/
|
||||
FormatExport(
|
||||
String libelle,
|
||||
String extension,
|
||||
String mimeType,
|
||||
String description,
|
||||
boolean supporteGraphiques,
|
||||
boolean supporteGrandesQuantitesDonnees) {
|
||||
this.libelle = libelle;
|
||||
this.extension = extension;
|
||||
this.mimeType = mimeType;
|
||||
this.description = description;
|
||||
this.supporteGraphiques = supporteGraphiques;
|
||||
this.supporteGrandesQuantitesDonnees = supporteGrandesQuantitesDonnees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé du format
|
||||
*
|
||||
* @return Le libellé affiché à l'utilisateur
|
||||
*/
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'extension de fichier
|
||||
*
|
||||
* @return L'extension sans le point (ex: "pdf", "xlsx")
|
||||
*/
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le type MIME du format
|
||||
*
|
||||
* @return Le type MIME complet
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la description du format
|
||||
*
|
||||
* @return La description complète du format
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le format supporte les graphiques
|
||||
*
|
||||
* @return true si le format peut inclure des graphiques
|
||||
*/
|
||||
public boolean supporteGraphiques() {
|
||||
return supporteGraphiques;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le format supporte de grandes quantités de données
|
||||
*
|
||||
* @return true si le format peut gérer de gros volumes de données
|
||||
*/
|
||||
public boolean supporteGrandesQuantitesDonnees() {
|
||||
return supporteGrandesQuantitesDonnees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le format est adapté aux rapports exécutifs
|
||||
*
|
||||
* @return true si le format convient aux rapports de direction
|
||||
*/
|
||||
public boolean isFormatExecutif() {
|
||||
return this == PDF || this == POWERPOINT || this == WORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le format est adapté à l'analyse de données
|
||||
*
|
||||
* @return true si le format convient à l'analyse de données
|
||||
*/
|
||||
public boolean isFormatAnalyse() {
|
||||
return this == EXCEL || this == CSV || this == JSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le format est adapté au partage web
|
||||
*
|
||||
* @return true si le format convient au partage sur le web
|
||||
*/
|
||||
public boolean isFormatWeb() {
|
||||
return this == HTML || this == PNG || this == SVG || this == JSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'icône appropriée pour le format
|
||||
*
|
||||
* @return L'icône Material Design
|
||||
*/
|
||||
public String getIcone() {
|
||||
return switch (this) {
|
||||
case PDF -> "picture_as_pdf";
|
||||
case WORD -> "description";
|
||||
case EXCEL -> "table_chart";
|
||||
case CSV -> "grid_on";
|
||||
case JSON -> "code";
|
||||
case XML -> "code";
|
||||
case PNG, JPEG -> "image";
|
||||
case SVG -> "vector_image";
|
||||
case POWERPOINT -> "slideshow";
|
||||
case HTML -> "web";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la couleur appropriée pour le format
|
||||
*
|
||||
* @return Le code couleur hexadécimal
|
||||
*/
|
||||
public String getCouleur() {
|
||||
return switch (this) {
|
||||
case PDF -> "#FF5722"; // Rouge-orange
|
||||
case WORD -> "#2196F3"; // Bleu
|
||||
case EXCEL -> "#4CAF50"; // Vert
|
||||
case CSV -> "#607D8B"; // Bleu gris
|
||||
case JSON -> "#FF9800"; // Orange
|
||||
case XML -> "#795548"; // Marron
|
||||
case PNG, JPEG -> "#E91E63"; // Rose
|
||||
case SVG -> "#9C27B0"; // Violet
|
||||
case POWERPOINT -> "#FF5722"; // Rouge-orange
|
||||
case HTML -> "#00BCD4"; // Cyan
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un nom de fichier avec l'extension appropriée
|
||||
*
|
||||
* @param nomBase Le nom de base du fichier
|
||||
* @return Le nom de fichier complet avec extension
|
||||
*/
|
||||
public String genererNomFichier(String nomBase) {
|
||||
return nomBase + "." + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la taille maximale recommandée pour ce format (en MB)
|
||||
*
|
||||
* @return La taille maximale en mégaoctets
|
||||
*/
|
||||
public int getTailleMaximaleRecommandee() {
|
||||
return switch (this) {
|
||||
case PDF, WORD, POWERPOINT -> 50; // 50 MB pour les documents
|
||||
case EXCEL -> 100; // 100 MB pour Excel
|
||||
case CSV, JSON, XML -> 200; // 200 MB pour les données
|
||||
case PNG, JPEG -> 10; // 10 MB pour les images
|
||||
case SVG, HTML -> 5; // 5 MB pour les formats légers
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le format nécessite un traitement spécial
|
||||
*
|
||||
* @return true si le format nécessite un traitement particulier
|
||||
*/
|
||||
public boolean necessiteTraitementSpecial() {
|
||||
return this == PDF || this == EXCEL || this == POWERPOINT || this == WORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les formats recommandés pour un type de rapport donné
|
||||
*
|
||||
* @param typeRapport Le type de rapport (executif, analytique, technique)
|
||||
* @return Un tableau des formats recommandés
|
||||
*/
|
||||
public static FormatExport[] getFormatsRecommandes(String typeRapport) {
|
||||
return switch (typeRapport.toLowerCase()) {
|
||||
case "executif" -> new FormatExport[] {PDF, POWERPOINT, WORD};
|
||||
case "analytique" -> new FormatExport[] {EXCEL, CSV, JSON, PDF};
|
||||
case "technique" -> new FormatExport[] {JSON, XML, CSV, HTML};
|
||||
case "partage" -> new FormatExport[] {PDF, PNG, HTML};
|
||||
default -> new FormatExport[] {PDF, EXCEL, CSV};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package dev.lions.unionflow.server.api.enums.analytics;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* Énumération des périodes d'analyse disponibles pour les métriques et rapports
|
||||
*
|
||||
* <p>Cette énumération définit les différentes périodes temporelles qui peuvent être utilisées pour
|
||||
* analyser les données et générer des rapports.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum PeriodeAnalyse {
|
||||
|
||||
// === PÉRIODES COURTES ===
|
||||
AUJOURD_HUI("Aujourd'hui", "today", 1, ChronoUnit.DAYS),
|
||||
HIER("Hier", "yesterday", 1, ChronoUnit.DAYS),
|
||||
CETTE_SEMAINE("Cette semaine", "this_week", 7, ChronoUnit.DAYS),
|
||||
SEMAINE_DERNIERE("Semaine dernière", "last_week", 7, ChronoUnit.DAYS),
|
||||
|
||||
// === PÉRIODES MENSUELLES ===
|
||||
CE_MOIS("Ce mois", "this_month", 1, ChronoUnit.MONTHS),
|
||||
MOIS_DERNIER("Mois dernier", "last_month", 1, ChronoUnit.MONTHS),
|
||||
TROIS_DERNIERS_MOIS("3 derniers mois", "last_3_months", 3, ChronoUnit.MONTHS),
|
||||
SIX_DERNIERS_MOIS("6 derniers mois", "last_6_months", 6, ChronoUnit.MONTHS),
|
||||
|
||||
// === PÉRIODES ANNUELLES ===
|
||||
CETTE_ANNEE("Cette année", "this_year", 1, ChronoUnit.YEARS),
|
||||
ANNEE_DERNIERE("Année dernière", "last_year", 1, ChronoUnit.YEARS),
|
||||
DEUX_DERNIERES_ANNEES("2 dernières années", "last_2_years", 2, ChronoUnit.YEARS),
|
||||
|
||||
// === PÉRIODES PERSONNALISÉES ===
|
||||
SEPT_DERNIERS_JOURS("7 derniers jours", "last_7_days", 7, ChronoUnit.DAYS),
|
||||
TRENTE_DERNIERS_JOURS("30 derniers jours", "last_30_days", 30, ChronoUnit.DAYS),
|
||||
QUATRE_VINGT_DIX_DERNIERS_JOURS("90 derniers jours", "last_90_days", 90, ChronoUnit.DAYS),
|
||||
|
||||
// === PÉRIODES SPÉCIALES ===
|
||||
DEPUIS_CREATION("Depuis la création", "since_creation", 0, ChronoUnit.FOREVER),
|
||||
PERIODE_PERSONNALISEE("Période personnalisée", "custom", 0, ChronoUnit.DAYS);
|
||||
|
||||
private final String libelle;
|
||||
private final String code;
|
||||
private final int duree;
|
||||
private final ChronoUnit unite;
|
||||
|
||||
/**
|
||||
* Constructeur de l'énumération PeriodeAnalyse
|
||||
*
|
||||
* @param libelle Le libellé affiché à l'utilisateur
|
||||
* @param code Le code technique de la période
|
||||
* @param duree La durée de la période
|
||||
* @param unite L'unité de temps (jours, mois, années)
|
||||
*/
|
||||
PeriodeAnalyse(String libelle, String code, int duree, ChronoUnit unite) {
|
||||
this.libelle = libelle;
|
||||
this.code = code;
|
||||
this.duree = duree;
|
||||
this.unite = unite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé de la période
|
||||
*
|
||||
* @return Le libellé affiché à l'utilisateur
|
||||
*/
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le code technique de la période
|
||||
*
|
||||
* @return Le code technique
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la durée de la période
|
||||
*
|
||||
* @return La durée numérique
|
||||
*/
|
||||
public int getDuree() {
|
||||
return duree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'unité de temps de la période
|
||||
*
|
||||
* @return L'unité de temps (ChronoUnit)
|
||||
*/
|
||||
public ChronoUnit getUnite() {
|
||||
return unite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la date de début pour cette période
|
||||
*
|
||||
* @return La date de début de la période
|
||||
*/
|
||||
public LocalDateTime getDateDebut() {
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
|
||||
return switch (this) {
|
||||
case AUJOURD_HUI -> maintenant.toLocalDate().atStartOfDay();
|
||||
case HIER -> maintenant.minusDays(1).toLocalDate().atStartOfDay();
|
||||
case CETTE_SEMAINE ->
|
||||
maintenant
|
||||
.minusDays(maintenant.getDayOfWeek().getValue() - 1)
|
||||
.toLocalDate()
|
||||
.atStartOfDay();
|
||||
case SEMAINE_DERNIERE ->
|
||||
maintenant
|
||||
.minusDays(maintenant.getDayOfWeek().getValue() + 6)
|
||||
.toLocalDate()
|
||||
.atStartOfDay();
|
||||
case CE_MOIS -> maintenant.withDayOfMonth(1).toLocalDate().atStartOfDay();
|
||||
case MOIS_DERNIER -> maintenant.minusMonths(1).withDayOfMonth(1).toLocalDate().atStartOfDay();
|
||||
case CETTE_ANNEE -> maintenant.withDayOfYear(1).toLocalDate().atStartOfDay();
|
||||
case ANNEE_DERNIERE -> maintenant.minusYears(1).withDayOfYear(1).toLocalDate().atStartOfDay();
|
||||
case DEPUIS_CREATION -> LocalDateTime.of(2020, 1, 1, 0, 0); // Date de création d'UnionFlow
|
||||
case PERIODE_PERSONNALISEE -> maintenant; // À définir par l'utilisateur
|
||||
default -> maintenant.minus(duree, unite).toLocalDate().atStartOfDay();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule la date de fin pour cette période
|
||||
*
|
||||
* @return La date de fin de la période
|
||||
*/
|
||||
public LocalDateTime getDateFin() {
|
||||
LocalDateTime maintenant = LocalDateTime.now();
|
||||
|
||||
return switch (this) {
|
||||
case AUJOURD_HUI -> maintenant.toLocalDate().atTime(23, 59, 59);
|
||||
case HIER -> maintenant.minusDays(1).toLocalDate().atTime(23, 59, 59);
|
||||
case CETTE_SEMAINE -> maintenant.toLocalDate().atTime(23, 59, 59);
|
||||
case SEMAINE_DERNIERE ->
|
||||
maintenant
|
||||
.minusDays(maintenant.getDayOfWeek().getValue())
|
||||
.toLocalDate()
|
||||
.atTime(23, 59, 59);
|
||||
case CE_MOIS -> maintenant.toLocalDate().atTime(23, 59, 59);
|
||||
case MOIS_DERNIER ->
|
||||
maintenant.withDayOfMonth(1).minusDays(1).toLocalDate().atTime(23, 59, 59);
|
||||
case CETTE_ANNEE -> maintenant.toLocalDate().atTime(23, 59, 59);
|
||||
case ANNEE_DERNIERE ->
|
||||
maintenant.withDayOfYear(1).minusDays(1).toLocalDate().atTime(23, 59, 59);
|
||||
case DEPUIS_CREATION, PERIODE_PERSONNALISEE -> maintenant;
|
||||
default -> maintenant.toLocalDate().atTime(23, 59, 59);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la période est une période courte (moins d'un mois)
|
||||
*
|
||||
* @return true si la période est courte
|
||||
*/
|
||||
public boolean isPeriodeCourte() {
|
||||
return this == AUJOURD_HUI
|
||||
|| this == HIER
|
||||
|| this == CETTE_SEMAINE
|
||||
|| this == SEMAINE_DERNIERE
|
||||
|| this == SEPT_DERNIERS_JOURS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la période est une période longue (plus d'un an)
|
||||
*
|
||||
* @return true si la période est longue
|
||||
*/
|
||||
public boolean isPeriodeLongue() {
|
||||
return this == CETTE_ANNEE
|
||||
|| this == ANNEE_DERNIERE
|
||||
|| this == DEUX_DERNIERES_ANNEES
|
||||
|| this == DEPUIS_CREATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la période est personnalisable
|
||||
*
|
||||
* @return true si la période peut être personnalisée
|
||||
*/
|
||||
public boolean isPersonnalisable() {
|
||||
return this == PERIODE_PERSONNALISEE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'intervalle de regroupement recommandé pour cette période
|
||||
*
|
||||
* @return L'intervalle de regroupement (jour, semaine, mois)
|
||||
*/
|
||||
public String getIntervalleRegroupement() {
|
||||
return switch (this) {
|
||||
case AUJOURD_HUI, HIER -> "heure";
|
||||
case CETTE_SEMAINE, SEMAINE_DERNIERE, SEPT_DERNIERS_JOURS -> "jour";
|
||||
case CE_MOIS, MOIS_DERNIER, TRENTE_DERNIERS_JOURS -> "jour";
|
||||
case TROIS_DERNIERS_MOIS, SIX_DERNIERS_MOIS, QUATRE_VINGT_DIX_DERNIERS_JOURS -> "semaine";
|
||||
case CETTE_ANNEE, ANNEE_DERNIERE, DEUX_DERNIERES_ANNEES -> "mois";
|
||||
case DEPUIS_CREATION -> "annee";
|
||||
default -> "jour";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le format de date approprié pour cette période
|
||||
*
|
||||
* @return Le format de date (dd/MM, MM/yyyy, etc.)
|
||||
*/
|
||||
public String getFormatDate() {
|
||||
return switch (this) {
|
||||
case AUJOURD_HUI, HIER -> "HH:mm";
|
||||
case CETTE_SEMAINE, SEMAINE_DERNIERE, SEPT_DERNIERS_JOURS -> "dd/MM";
|
||||
case CE_MOIS, MOIS_DERNIER, TRENTE_DERNIERS_JOURS -> "dd/MM";
|
||||
case TROIS_DERNIERS_MOIS, SIX_DERNIERS_MOIS, QUATRE_VINGT_DIX_DERNIERS_JOURS -> "dd/MM";
|
||||
case CETTE_ANNEE, ANNEE_DERNIERE -> "MM/yyyy";
|
||||
case DEUX_DERNIERES_ANNEES, DEPUIS_CREATION -> "yyyy";
|
||||
default -> "dd/MM/yyyy";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package dev.lions.unionflow.server.api.enums.analytics;
|
||||
|
||||
/**
|
||||
* Énumération des types de métriques disponibles dans le système analytics UnionFlow
|
||||
*
|
||||
* <p>Cette énumération définit les différents types de métriques qui peuvent être calculées et
|
||||
* affichées dans les tableaux de bord et rapports.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum TypeMetrique {
|
||||
|
||||
// === MÉTRIQUES MEMBRES ===
|
||||
NOMBRE_MEMBRES_ACTIFS("Nombre de membres actifs", "membres", "count"),
|
||||
NOMBRE_MEMBRES_INACTIFS("Nombre de membres inactifs", "membres", "count"),
|
||||
TAUX_CROISSANCE_MEMBRES("Taux de croissance des membres", "membres", "percentage"),
|
||||
MOYENNE_AGE_MEMBRES("Âge moyen des membres", "membres", "average"),
|
||||
REPARTITION_GENRE_MEMBRES("Répartition par genre", "membres", "distribution"),
|
||||
|
||||
// === MÉTRIQUES FINANCIÈRES ===
|
||||
TOTAL_COTISATIONS_COLLECTEES("Total des cotisations collectées", "finance", "amount"),
|
||||
COTISATIONS_EN_ATTENTE("Cotisations en attente", "finance", "amount"),
|
||||
TAUX_RECOUVREMENT_COTISATIONS("Taux de recouvrement", "finance", "percentage"),
|
||||
MOYENNE_COTISATION_MEMBRE("Cotisation moyenne par membre", "finance", "average"),
|
||||
EVOLUTION_REVENUS_MENSUELLE("Évolution des revenus mensuels", "finance", "trend"),
|
||||
|
||||
// === MÉTRIQUES ÉVÉNEMENTS ===
|
||||
NOMBRE_EVENEMENTS_ORGANISES("Nombre d'événements organisés", "evenements", "count"),
|
||||
TAUX_PARTICIPATION_EVENEMENTS("Taux de participation aux événements", "evenements", "percentage"),
|
||||
MOYENNE_PARTICIPANTS_EVENEMENT("Moyenne de participants par événement", "evenements", "average"),
|
||||
EVENEMENTS_ANNULES("Événements annulés", "evenements", "count"),
|
||||
SATISFACTION_EVENEMENTS("Satisfaction des événements", "evenements", "rating"),
|
||||
|
||||
// === MÉTRIQUES SOLIDARITÉ ===
|
||||
NOMBRE_DEMANDES_AIDE("Nombre de demandes d'aide", "solidarite", "count"),
|
||||
MONTANT_AIDES_ACCORDEES("Montant des aides accordées", "solidarite", "amount"),
|
||||
TAUX_APPROBATION_AIDES("Taux d'approbation des aides", "solidarite", "percentage"),
|
||||
DELAI_TRAITEMENT_DEMANDES("Délai moyen de traitement", "solidarite", "duration"),
|
||||
IMPACT_SOCIAL_MESURE("Impact social mesuré", "solidarite", "score"),
|
||||
|
||||
// === MÉTRIQUES ENGAGEMENT ===
|
||||
TAUX_CONNEXION_MOBILE("Taux de connexion mobile", "engagement", "percentage"),
|
||||
FREQUENCE_UTILISATION_APP("Fréquence d'utilisation de l'app", "engagement", "frequency"),
|
||||
ACTIONS_UTILISATEUR_JOUR("Actions utilisateur par jour", "engagement", "count"),
|
||||
RETENTION_UTILISATEURS("Rétention des utilisateurs", "engagement", "percentage"),
|
||||
NPS_SATISFACTION("Net Promoter Score", "engagement", "score"),
|
||||
|
||||
// === MÉTRIQUES ORGANISATIONNELLES ===
|
||||
NOMBRE_ORGANISATIONS_ACTIVES("Organisations actives", "organisation", "count"),
|
||||
TAUX_CROISSANCE_ORGANISATIONS("Croissance des organisations", "organisation", "percentage"),
|
||||
MOYENNE_MEMBRES_PAR_ORGANISATION("Membres moyens par organisation", "organisation", "average"),
|
||||
ORGANISATIONS_PREMIUM("Organisations premium", "organisation", "count"),
|
||||
CHURN_RATE_ORGANISATIONS("Taux de désabonnement", "organisation", "percentage"),
|
||||
|
||||
// === MÉTRIQUES TECHNIQUES ===
|
||||
TEMPS_REPONSE_API("Temps de réponse API", "technique", "duration"),
|
||||
TAUX_DISPONIBILITE_SYSTEME("Taux de disponibilité", "technique", "percentage"),
|
||||
NOMBRE_ERREURS_SYSTEME("Nombre d'erreurs système", "technique", "count"),
|
||||
UTILISATION_STOCKAGE("Utilisation du stockage", "technique", "size"),
|
||||
PERFORMANCE_MOBILE("Performance mobile", "technique", "score");
|
||||
|
||||
private final String libelle;
|
||||
private final String categorie;
|
||||
private final String typeValeur;
|
||||
|
||||
/**
|
||||
* Constructeur de l'énumération TypeMetrique
|
||||
*
|
||||
* @param libelle Le libellé affiché à l'utilisateur
|
||||
* @param categorie La catégorie de la métrique
|
||||
* @param typeValeur Le type de valeur (count, percentage, amount, etc.)
|
||||
*/
|
||||
TypeMetrique(String libelle, String categorie, String typeValeur) {
|
||||
this.libelle = libelle;
|
||||
this.categorie = categorie;
|
||||
this.typeValeur = typeValeur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé de la métrique
|
||||
*
|
||||
* @return Le libellé affiché à l'utilisateur
|
||||
*/
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la catégorie de la métrique
|
||||
*
|
||||
* @return La catégorie (membres, finance, evenements, etc.)
|
||||
*/
|
||||
public String getCategorie() {
|
||||
return categorie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le type de valeur de la métrique
|
||||
*
|
||||
* @return Le type de valeur (count, percentage, amount, etc.)
|
||||
*/
|
||||
public String getTypeValeur() {
|
||||
return typeValeur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la métrique est de type financier
|
||||
*
|
||||
* @return true si la métrique concerne les finances
|
||||
*/
|
||||
public boolean isFinanciere() {
|
||||
return "finance".equals(this.categorie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la métrique est de type pourcentage
|
||||
*
|
||||
* @return true si la métrique est un pourcentage
|
||||
*/
|
||||
public boolean isPourcentage() {
|
||||
return "percentage".equals(this.typeValeur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la métrique est de type compteur
|
||||
*
|
||||
* @return true si la métrique est un compteur
|
||||
*/
|
||||
public boolean isCompteur() {
|
||||
return "count".equals(this.typeValeur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'unité de mesure appropriée pour la métrique
|
||||
*
|
||||
* @return L'unité de mesure (%, XOF, jours, etc.)
|
||||
*/
|
||||
public String getUnite() {
|
||||
return switch (this.typeValeur) {
|
||||
case "percentage" -> "%";
|
||||
case "amount" -> "XOF";
|
||||
case "duration" -> "jours";
|
||||
case "size" -> "MB";
|
||||
case "frequency" -> "/jour";
|
||||
case "rating", "score" -> "/10";
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'icône appropriée pour la métrique
|
||||
*
|
||||
* @return L'icône Material Design
|
||||
*/
|
||||
public String getIcone() {
|
||||
return switch (this.categorie) {
|
||||
case "membres" -> "people";
|
||||
case "finance" -> "attach_money";
|
||||
case "evenements" -> "event";
|
||||
case "solidarite" -> "favorite";
|
||||
case "engagement" -> "trending_up";
|
||||
case "organisation" -> "business";
|
||||
case "technique" -> "settings";
|
||||
default -> "analytics";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la couleur appropriée pour la métrique
|
||||
*
|
||||
* @return Le code couleur hexadécimal
|
||||
*/
|
||||
public String getCouleur() {
|
||||
return switch (this.categorie) {
|
||||
case "membres" -> "#2196F3"; // Bleu
|
||||
case "finance" -> "#4CAF50"; // Vert
|
||||
case "evenements" -> "#FF9800"; // Orange
|
||||
case "solidarite" -> "#E91E63"; // Rose
|
||||
case "engagement" -> "#9C27B0"; // Violet
|
||||
case "organisation" -> "#607D8B"; // Bleu gris
|
||||
case "technique" -> "#795548"; // Marron
|
||||
default -> "#757575"; // Gris
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package dev.lions.unionflow.server.api.enums.comptabilite;
|
||||
|
||||
/**
|
||||
* Énumération des types de comptes comptables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeCompteComptable {
|
||||
ACTIF("Actif"),
|
||||
PASSIF("Passif"),
|
||||
CHARGES("Charges"),
|
||||
PRODUITS("Produits"),
|
||||
TRESORERIE("Trésorerie"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeCompteComptable(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.lions.unionflow.server.api.enums.comptabilite;
|
||||
|
||||
/**
|
||||
* Énumération des types de journaux comptables
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeJournalComptable {
|
||||
ACHATS("Achats"),
|
||||
VENTES("Ventes"),
|
||||
BANQUE("Banque"),
|
||||
CAISSE("Caisse"),
|
||||
OD("Opérations Diverses");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeJournalComptable(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.lions.unionflow.server.api.enums.document;
|
||||
|
||||
/**
|
||||
* Énumération des types de documents
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeDocument {
|
||||
IDENTITE("Pièce d'Identité"),
|
||||
JUSTIFICATIF_DOMICILE("Justificatif de Domicile"),
|
||||
PHOTO("Photo"),
|
||||
CONTRAT("Contrat"),
|
||||
FACTURE("Facture"),
|
||||
RECU("Reçu"),
|
||||
RAPPORT("Rapport"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeDocument(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package dev.lions.unionflow.server.api.enums.evenement;
|
||||
|
||||
/**
|
||||
* Énumération des priorités d'événements dans UnionFlow
|
||||
*
|
||||
* <p>Cette énumération définit les niveaux de priorité pour les événements, permettant de prioriser
|
||||
* l'affichage et les notifications selon l'importance.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-09-21
|
||||
*/
|
||||
public enum PrioriteEvenement {
|
||||
CRITIQUE(
|
||||
"Critique",
|
||||
"critical",
|
||||
1,
|
||||
"Événement critique nécessitant une attention immédiate",
|
||||
"#F44336",
|
||||
"priority_high",
|
||||
true,
|
||||
true),
|
||||
|
||||
HAUTE(
|
||||
"Haute",
|
||||
"high",
|
||||
2,
|
||||
"Événement de haute priorité",
|
||||
"#FF9800",
|
||||
"keyboard_arrow_up",
|
||||
true,
|
||||
false),
|
||||
|
||||
NORMALE(
|
||||
"Normale", "normal", 3, "Événement de priorité normale", "#2196F3", "remove", false, false),
|
||||
|
||||
BASSE(
|
||||
"Basse",
|
||||
"low",
|
||||
4,
|
||||
"Événement de priorité basse",
|
||||
"#4CAF50",
|
||||
"keyboard_arrow_down",
|
||||
false,
|
||||
false);
|
||||
|
||||
private final String libelle;
|
||||
private final String code;
|
||||
private final int niveau;
|
||||
private final String description;
|
||||
private final String couleur;
|
||||
private final String icone;
|
||||
private final boolean notificationImmediate;
|
||||
private final boolean escaladeAutomatique;
|
||||
|
||||
PrioriteEvenement(
|
||||
String libelle,
|
||||
String code,
|
||||
int niveau,
|
||||
String description,
|
||||
String couleur,
|
||||
String icone,
|
||||
boolean notificationImmediate,
|
||||
boolean escaladeAutomatique) {
|
||||
this.libelle = libelle;
|
||||
this.code = code;
|
||||
this.niveau = niveau;
|
||||
this.description = description;
|
||||
this.couleur = couleur;
|
||||
this.icone = icone;
|
||||
this.notificationImmediate = notificationImmediate;
|
||||
this.escaladeAutomatique = escaladeAutomatique;
|
||||
}
|
||||
|
||||
// === GETTERS ===
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getNiveau() {
|
||||
return niveau;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public boolean isNotificationImmediate() {
|
||||
return notificationImmediate;
|
||||
}
|
||||
|
||||
public boolean isEscaladeAutomatique() {
|
||||
return escaladeAutomatique;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si la priorité est élevée (critique ou haute) */
|
||||
public boolean isElevee() {
|
||||
return this == CRITIQUE || this == HAUTE;
|
||||
}
|
||||
|
||||
/** Vérifie si la priorité nécessite une attention immédiate */
|
||||
public boolean isUrgente() {
|
||||
return this == CRITIQUE || this == HAUTE;
|
||||
}
|
||||
|
||||
/** Compare deux priorités */
|
||||
public boolean isSuperieurA(PrioriteEvenement autre) {
|
||||
return this.niveau < autre.niveau; // Plus le niveau est bas, plus la priorité est haute
|
||||
}
|
||||
|
||||
/** Retourne les priorités élevées */
|
||||
public static java.util.List<PrioriteEvenement> getPrioritesElevees() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(PrioriteEvenement::isElevee)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les priorités urgentes */
|
||||
public static java.util.List<PrioriteEvenement> getPrioritesUrgentes() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(PrioriteEvenement::isUrgente)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Détermine la priorité basée sur le type d'événement */
|
||||
public static PrioriteEvenement determinerPriorite(TypeEvenementMetier typeEvenement) {
|
||||
return switch (typeEvenement) {
|
||||
case ASSEMBLEE_GENERALE -> HAUTE;
|
||||
case REUNION_BUREAU -> HAUTE;
|
||||
case ACTION_CARITATIVE -> NORMALE;
|
||||
case FORMATION -> NORMALE;
|
||||
case CONFERENCE -> NORMALE;
|
||||
case ACTIVITE_SOCIALE -> BASSE;
|
||||
case ATELIER -> BASSE;
|
||||
case CEREMONIE -> NORMALE;
|
||||
case AUTRE -> NORMALE;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne la priorité par défaut */
|
||||
public static PrioriteEvenement getDefaut() {
|
||||
return NORMALE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package dev.lions.unionflow.server.api.enums.evenement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts d'événements dans UnionFlow
|
||||
*
|
||||
* <p>Cette énumération définit les différents états qu'un événement peut avoir tout au long de son
|
||||
* cycle de vie, de la planification à la clôture.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-09-21
|
||||
*/
|
||||
public enum StatutEvenement {
|
||||
|
||||
// === STATUTS DE PLANIFICATION ===
|
||||
PLANIFIE(
|
||||
"Planifié",
|
||||
"planned",
|
||||
"L'événement est planifié et en préparation",
|
||||
"#2196F3",
|
||||
"event",
|
||||
false,
|
||||
false),
|
||||
|
||||
CONFIRME(
|
||||
"Confirmé",
|
||||
"confirmed",
|
||||
"L'événement est confirmé et les inscriptions sont ouvertes",
|
||||
"#4CAF50",
|
||||
"event_available",
|
||||
false,
|
||||
false),
|
||||
|
||||
// === STATUTS D'EXÉCUTION ===
|
||||
EN_COURS(
|
||||
"En cours",
|
||||
"ongoing",
|
||||
"L'événement est actuellement en cours",
|
||||
"#FF9800",
|
||||
"play_circle",
|
||||
false,
|
||||
false),
|
||||
|
||||
// === STATUTS FINAUX ===
|
||||
TERMINE(
|
||||
"Terminé",
|
||||
"completed",
|
||||
"L'événement s'est terminé avec succès",
|
||||
"#4CAF50",
|
||||
"check_circle",
|
||||
true,
|
||||
false),
|
||||
|
||||
ANNULE("Annulé", "cancelled", "L'événement a été annulé", "#F44336", "cancel", true, true),
|
||||
|
||||
REPORTE(
|
||||
"Reporté",
|
||||
"postponed",
|
||||
"L'événement a été reporté à une date ultérieure",
|
||||
"#FF5722",
|
||||
"schedule",
|
||||
false,
|
||||
false);
|
||||
|
||||
private final String libelle;
|
||||
private final String code;
|
||||
private final String description;
|
||||
private final String couleur;
|
||||
private final String icone;
|
||||
private final boolean estFinal;
|
||||
private final boolean estEchec;
|
||||
|
||||
StatutEvenement(
|
||||
String libelle,
|
||||
String code,
|
||||
String description,
|
||||
String couleur,
|
||||
String icone,
|
||||
boolean estFinal,
|
||||
boolean estEchec) {
|
||||
this.libelle = libelle;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
this.couleur = couleur;
|
||||
this.icone = icone;
|
||||
this.estFinal = estFinal;
|
||||
this.estEchec = estEchec;
|
||||
}
|
||||
|
||||
// === GETTERS ===
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public boolean isEstFinal() {
|
||||
return estFinal;
|
||||
}
|
||||
|
||||
public boolean isEstEchec() {
|
||||
return estEchec;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si l'événement peut être modifié */
|
||||
public boolean permetModification() {
|
||||
return switch (this) {
|
||||
case PLANIFIE, CONFIRME, REPORTE -> true;
|
||||
case EN_COURS, TERMINE, ANNULE -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement peut être annulé */
|
||||
public boolean permetAnnulation() {
|
||||
return switch (this) {
|
||||
case PLANIFIE, CONFIRME, EN_COURS, REPORTE -> true;
|
||||
case TERMINE, ANNULE -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement est en cours d'exécution */
|
||||
public boolean isEnCours() {
|
||||
return this == EN_COURS;
|
||||
}
|
||||
|
||||
/** Vérifie si l'événement est terminé avec succès */
|
||||
public boolean isSucces() {
|
||||
return this == TERMINE;
|
||||
}
|
||||
|
||||
/** Retourne les statuts finaux */
|
||||
public static java.util.List<StatutEvenement> getStatutsFinaux() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutEvenement::isEstFinal)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les statuts d'échec */
|
||||
public static java.util.List<StatutEvenement> getStatutsEchec() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutEvenement::isEstEchec)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Vérifie si la transition vers un autre statut est valide */
|
||||
public boolean peutTransitionnerVers(StatutEvenement nouveauStatut) {
|
||||
if (this == nouveauStatut) return false;
|
||||
if (estFinal && nouveauStatut != REPORTE) return false;
|
||||
|
||||
return switch (this) {
|
||||
case PLANIFIE ->
|
||||
nouveauStatut == CONFIRME || nouveauStatut == ANNULE || nouveauStatut == REPORTE;
|
||||
case CONFIRME ->
|
||||
nouveauStatut == EN_COURS || nouveauStatut == ANNULE || nouveauStatut == REPORTE;
|
||||
case EN_COURS -> nouveauStatut == TERMINE || nouveauStatut == ANNULE;
|
||||
case REPORTE -> nouveauStatut == PLANIFIE || nouveauStatut == ANNULE;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne le niveau de priorité pour l'affichage */
|
||||
public int getNiveauPriorite() {
|
||||
return switch (this) {
|
||||
case EN_COURS -> 1;
|
||||
case CONFIRME -> 2;
|
||||
case PLANIFIE -> 3;
|
||||
case REPORTE -> 4;
|
||||
case TERMINE -> 5;
|
||||
case ANNULE -> 6;
|
||||
};
|
||||
}
|
||||
|
||||
// === MÉTHODES STATIQUES ===
|
||||
|
||||
/** Retourne les statuts actifs (non finaux) */
|
||||
public static StatutEvenement[] getStatutsActifs() {
|
||||
return new StatutEvenement[] {PLANIFIE, CONFIRME, EN_COURS, REPORTE};
|
||||
}
|
||||
|
||||
/** Trouve un statut par son code */
|
||||
public static StatutEvenement fromCode(String code) {
|
||||
if (code == null || code.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (StatutEvenement statut : values()) {
|
||||
if (statut.code.equals(code)) {
|
||||
return statut;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Trouve un statut par son libellé */
|
||||
public static StatutEvenement fromLibelle(String libelle) {
|
||||
if (libelle == null || libelle.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (StatutEvenement statut : values()) {
|
||||
if (statut.libelle.equals(libelle)) {
|
||||
return statut;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Retourne les transitions possibles depuis ce statut */
|
||||
public StatutEvenement[] getTransitionsPossibles() {
|
||||
return switch (this) {
|
||||
case PLANIFIE -> new StatutEvenement[] {CONFIRME, ANNULE, REPORTE};
|
||||
case CONFIRME -> new StatutEvenement[] {EN_COURS, ANNULE, REPORTE};
|
||||
case EN_COURS -> new StatutEvenement[] {TERMINE, ANNULE};
|
||||
case REPORTE -> new StatutEvenement[] {PLANIFIE, CONFIRME, ANNULE};
|
||||
case TERMINE, ANNULE -> new StatutEvenement[] {}; // Aucune transition possible
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.lions.unionflow.server.api.enums.evenement;
|
||||
|
||||
/**
|
||||
* Énumération des types d'événements métier dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum TypeEvenementMetier {
|
||||
ASSEMBLEE_GENERALE("Assemblée Générale"),
|
||||
FORMATION("Formation"),
|
||||
ACTIVITE_SOCIALE("Activité Sociale"),
|
||||
ACTION_CARITATIVE("Action Caritative"),
|
||||
REUNION_BUREAU("Réunion de Bureau"),
|
||||
CONFERENCE("Conférence"),
|
||||
ATELIER("Atelier"),
|
||||
CEREMONIE("Cérémonie"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeEvenementMetier(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.lions.unionflow.server.api.enums.finance;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de cotisations dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutCotisation {
|
||||
EN_ATTENTE("En attente"),
|
||||
PAYEE("Payée"),
|
||||
PARTIELLEMENT_PAYEE("Partiellement payée"),
|
||||
EN_RETARD("En retard"),
|
||||
ANNULEE("Annulée"),
|
||||
REMBOURSEE("Remboursée");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutCotisation(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dev.lions.unionflow.server.api.enums.membre;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de membres dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutMembre {
|
||||
ACTIF("Actif"),
|
||||
INACTIF("Inactif"),
|
||||
SUSPENDU("Suspendu"),
|
||||
RADIE("Radié");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutMembre(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
package dev.lions.unionflow.server.api.enums.notification;
|
||||
|
||||
/**
|
||||
* Énumération des canaux de notification pour Android et iOS
|
||||
*
|
||||
* <p>Cette énumération définit les différents canaux de notification utilisés pour organiser et
|
||||
* prioriser les notifications push dans UnionFlow.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum CanalNotification {
|
||||
|
||||
// === CANAUX PAR PRIORITÉ ===
|
||||
URGENT_CHANNEL(
|
||||
"urgent",
|
||||
"Notifications urgentes",
|
||||
"Alertes critiques nécessitant une action immédiate",
|
||||
5,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
"urgent",
|
||||
"#F44336"),
|
||||
|
||||
ERROR_CHANNEL(
|
||||
"error",
|
||||
"Erreurs système",
|
||||
"Notifications d'erreurs et de problèmes techniques",
|
||||
4,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"error",
|
||||
"#F44336"),
|
||||
|
||||
WARNING_CHANNEL(
|
||||
"warning",
|
||||
"Avertissements",
|
||||
"Notifications d'avertissement et d'attention",
|
||||
4,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"warning",
|
||||
"#FF9800"),
|
||||
|
||||
IMPORTANT_CHANNEL(
|
||||
"important",
|
||||
"Notifications importantes",
|
||||
"Informations importantes à ne pas manquer",
|
||||
4,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"important",
|
||||
"#FF5722"),
|
||||
|
||||
REMINDER_CHANNEL(
|
||||
"reminder",
|
||||
"Rappels",
|
||||
"Rappels d'événements, cotisations et échéances",
|
||||
3,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"reminder",
|
||||
"#2196F3"),
|
||||
|
||||
SUCCESS_CHANNEL(
|
||||
"success",
|
||||
"Confirmations",
|
||||
"Notifications de succès et confirmations",
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"success",
|
||||
"#4CAF50"),
|
||||
|
||||
CELEBRATION_CHANNEL(
|
||||
"celebration",
|
||||
"Célébrations",
|
||||
"Anniversaires, félicitations et événements joyeux",
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"celebration",
|
||||
"#FF9800"),
|
||||
|
||||
DEFAULT_CHANNEL(
|
||||
"default",
|
||||
"Notifications générales",
|
||||
"Notifications d'information générale",
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"info",
|
||||
"#2196F3"),
|
||||
|
||||
// === CANAUX PAR CATÉGORIE ===
|
||||
EVENTS_CHANNEL(
|
||||
"events",
|
||||
"Événements",
|
||||
"Notifications liées aux événements et activités",
|
||||
3,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"event",
|
||||
"#2196F3"),
|
||||
|
||||
PAYMENTS_CHANNEL(
|
||||
"payments",
|
||||
"Paiements",
|
||||
"Notifications de cotisations et paiements",
|
||||
4,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"payment",
|
||||
"#4CAF50"),
|
||||
|
||||
SOLIDARITY_CHANNEL(
|
||||
"solidarity",
|
||||
"Solidarité",
|
||||
"Notifications d'aide et de solidarité",
|
||||
3,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"help",
|
||||
"#E91E63"),
|
||||
|
||||
MEMBERS_CHANNEL(
|
||||
"members",
|
||||
"Membres",
|
||||
"Notifications concernant les membres",
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"people",
|
||||
"#2196F3"),
|
||||
|
||||
ORGANIZATION_CHANNEL(
|
||||
"organization",
|
||||
"Organisation",
|
||||
"Annonces et informations organisationnelles",
|
||||
3,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"business",
|
||||
"#2196F3"),
|
||||
|
||||
SYSTEM_CHANNEL(
|
||||
"system",
|
||||
"Système",
|
||||
"Notifications système et maintenance",
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"settings",
|
||||
"#607D8B"),
|
||||
|
||||
MESSAGES_CHANNEL(
|
||||
"messages",
|
||||
"Messages",
|
||||
"Messages privés et communications",
|
||||
3,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"message",
|
||||
"#2196F3"),
|
||||
|
||||
LOCATION_CHANNEL(
|
||||
"location",
|
||||
"Géolocalisation",
|
||||
"Notifications basées sur la localisation",
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"location_on",
|
||||
"#4CAF50");
|
||||
|
||||
private final String id;
|
||||
private final String nom;
|
||||
private final String description;
|
||||
private final int importance;
|
||||
private final boolean sonActive;
|
||||
private final boolean vibrationActive;
|
||||
private final boolean lumiereLED;
|
||||
private final String typeDefaut;
|
||||
private final String couleur;
|
||||
|
||||
/**
|
||||
* Constructeur de l'énumération CanalNotification
|
||||
*
|
||||
* @param id L'identifiant unique du canal
|
||||
* @param nom Le nom affiché du canal
|
||||
* @param description La description du canal
|
||||
* @param importance Le niveau d'importance (1=Min, 2=Low, 3=Default, 4=High, 5=Max)
|
||||
* @param sonActive true si le son est activé par défaut
|
||||
* @param vibrationActive true si la vibration est activée par défaut
|
||||
* @param lumiereLED true si la lumière LED est activée par défaut
|
||||
* @param typeDefaut Le type de notification par défaut pour ce canal
|
||||
* @param couleur La couleur hexadécimale du canal
|
||||
*/
|
||||
CanalNotification(
|
||||
String id,
|
||||
String nom,
|
||||
String description,
|
||||
int importance,
|
||||
boolean sonActive,
|
||||
boolean vibrationActive,
|
||||
boolean lumiereLED,
|
||||
String typeDefaut,
|
||||
String couleur) {
|
||||
this.id = id;
|
||||
this.nom = nom;
|
||||
this.description = description;
|
||||
this.importance = importance;
|
||||
this.sonActive = sonActive;
|
||||
this.vibrationActive = vibrationActive;
|
||||
this.lumiereLED = lumiereLED;
|
||||
this.typeDefaut = typeDefaut;
|
||||
this.couleur = couleur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'identifiant du canal
|
||||
*
|
||||
* @return L'ID unique du canal
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nom du canal
|
||||
*
|
||||
* @return Le nom affiché du canal
|
||||
*/
|
||||
public String getNom() {
|
||||
return nom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la description du canal
|
||||
*
|
||||
* @return La description détaillée du canal
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le niveau d'importance
|
||||
*
|
||||
* @return Le niveau d'importance (1-5)
|
||||
*/
|
||||
public int getImportance() {
|
||||
return importance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le son est activé par défaut
|
||||
*
|
||||
* @return true si le son est activé
|
||||
*/
|
||||
public boolean isSonActive() {
|
||||
return sonActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la vibration est activée par défaut
|
||||
*
|
||||
* @return true si la vibration est activée
|
||||
*/
|
||||
public boolean isVibrationActive() {
|
||||
return vibrationActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la lumière LED est activée par défaut
|
||||
*
|
||||
* @return true si la lumière LED est activée
|
||||
*/
|
||||
public boolean isLumiereLED() {
|
||||
return lumiereLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le type de notification par défaut
|
||||
*
|
||||
* @return Le type par défaut pour ce canal
|
||||
*/
|
||||
public String getTypeDefaut() {
|
||||
return typeDefaut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la couleur du canal
|
||||
*
|
||||
* @return Le code couleur hexadécimal
|
||||
*/
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le canal est critique
|
||||
*
|
||||
* @return true si le canal a une importance élevée (4-5)
|
||||
*/
|
||||
public boolean isCritique() {
|
||||
return importance >= 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le canal est silencieux par défaut
|
||||
*
|
||||
* @return true si le canal n'émet ni son ni vibration
|
||||
*/
|
||||
public boolean isSilencieux() {
|
||||
return !sonActive && !vibrationActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le niveau d'importance Android
|
||||
*
|
||||
* @return Le niveau d'importance pour Android (IMPORTANCE_MIN à IMPORTANCE_MAX)
|
||||
*/
|
||||
public String getImportanceAndroid() {
|
||||
return switch (importance) {
|
||||
case 1 -> "IMPORTANCE_MIN";
|
||||
case 2 -> "IMPORTANCE_LOW";
|
||||
case 3 -> "IMPORTANCE_DEFAULT";
|
||||
case 4 -> "IMPORTANCE_HIGH";
|
||||
case 5 -> "IMPORTANCE_MAX";
|
||||
default -> "IMPORTANCE_DEFAULT";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la priorité iOS
|
||||
*
|
||||
* @return La priorité pour iOS (low ou high)
|
||||
*/
|
||||
public String getPrioriteIOS() {
|
||||
return importance >= 4 ? "high" : "low";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le son par défaut pour le canal
|
||||
*
|
||||
* @return Le nom du fichier son ou "default"
|
||||
*/
|
||||
public String getSonDefaut() {
|
||||
return switch (this) {
|
||||
case URGENT_CHANNEL -> "urgent_sound.mp3";
|
||||
case ERROR_CHANNEL -> "error_sound.mp3";
|
||||
case WARNING_CHANNEL -> "warning_sound.mp3";
|
||||
case IMPORTANT_CHANNEL -> "important_sound.mp3";
|
||||
case REMINDER_CHANNEL -> "reminder_sound.mp3";
|
||||
case SUCCESS_CHANNEL -> "success_sound.mp3";
|
||||
case CELEBRATION_CHANNEL -> "celebration_sound.mp3";
|
||||
default -> "default";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le pattern de vibration
|
||||
*
|
||||
* @return Le pattern de vibration en millisecondes
|
||||
*/
|
||||
public long[] getPatternVibration() {
|
||||
return switch (this) {
|
||||
case URGENT_CHANNEL -> new long[] {0, 500, 200, 500, 200, 500}; // Triple vibration
|
||||
case ERROR_CHANNEL -> new long[] {0, 1000, 500, 1000}; // Double vibration longue
|
||||
case WARNING_CHANNEL -> new long[] {0, 300, 200, 300}; // Double vibration courte
|
||||
case IMPORTANT_CHANNEL -> new long[] {0, 500, 100, 200}; // Vibration distinctive
|
||||
case REMINDER_CHANNEL -> new long[] {0, 200, 100, 200}; // Vibration douce
|
||||
default -> new long[] {0, 250}; // Vibration simple
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le canal peut être désactivé par l'utilisateur
|
||||
*
|
||||
* @return true si l'utilisateur peut désactiver ce canal
|
||||
*/
|
||||
public boolean peutEtreDesactive() {
|
||||
return this != URGENT_CHANNEL && this != ERROR_CHANNEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la durée de vie par défaut des notifications de ce canal
|
||||
*
|
||||
* @return La durée de vie en millisecondes
|
||||
*/
|
||||
public long getDureeVieMs() {
|
||||
return switch (this) {
|
||||
case URGENT_CHANNEL -> 3600000L; // 1 heure
|
||||
case ERROR_CHANNEL -> 86400000L; // 24 heures
|
||||
case WARNING_CHANNEL -> 172800000L; // 48 heures
|
||||
case IMPORTANT_CHANNEL -> 259200000L; // 72 heures
|
||||
case REMINDER_CHANNEL -> 86400000L; // 24 heures
|
||||
case SUCCESS_CHANNEL -> 172800000L; // 48 heures
|
||||
case CELEBRATION_CHANNEL -> 259200000L; // 72 heures
|
||||
default -> 604800000L; // 1 semaine
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un canal par son ID
|
||||
*
|
||||
* @param id L'identifiant du canal
|
||||
* @return Le canal correspondant ou DEFAULT_CHANNEL si non trouvé
|
||||
*/
|
||||
public static CanalNotification parId(String id) {
|
||||
for (CanalNotification canal : values()) {
|
||||
if (canal.getId().equals(id)) {
|
||||
return canal;
|
||||
}
|
||||
}
|
||||
return DEFAULT_CHANNEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne tous les canaux critiques
|
||||
*
|
||||
* @return Un tableau des canaux critiques
|
||||
*/
|
||||
public static CanalNotification[] getCanauxCritiques() {
|
||||
return new CanalNotification[] {
|
||||
URGENT_CHANNEL, ERROR_CHANNEL, WARNING_CHANNEL, IMPORTANT_CHANNEL
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne tous les canaux par catégorie
|
||||
*
|
||||
* @return Un tableau des canaux catégoriels
|
||||
*/
|
||||
public static CanalNotification[] getCanauxCategories() {
|
||||
return new CanalNotification[] {
|
||||
EVENTS_CHANNEL,
|
||||
PAYMENTS_CHANNEL,
|
||||
SOLIDARITY_CHANNEL,
|
||||
MEMBERS_CHANNEL,
|
||||
ORGANIZATION_CHANNEL,
|
||||
SYSTEM_CHANNEL,
|
||||
MESSAGES_CHANNEL,
|
||||
LOCATION_CHANNEL
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.notification;
|
||||
|
||||
/**
|
||||
* Énumération des priorités de notifications
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum PrioriteNotification {
|
||||
CRITIQUE("Critique"),
|
||||
HAUTE("Haute"),
|
||||
NORMALE("Normale"),
|
||||
BASSE("Basse");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
PrioriteNotification(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
package dev.lions.unionflow.server.api.enums.notification;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de notification dans UnionFlow
|
||||
*
|
||||
* <p>Cette énumération définit les différents états qu'une notification peut avoir tout au long de
|
||||
* son cycle de vie, de la création à l'archivage.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum StatutNotification {
|
||||
|
||||
// === STATUTS DE CRÉATION ===
|
||||
BROUILLON(
|
||||
"Brouillon",
|
||||
"draft",
|
||||
"La notification est en cours de création",
|
||||
"edit",
|
||||
"#9E9E9E",
|
||||
false,
|
||||
false),
|
||||
|
||||
PROGRAMMEE(
|
||||
"Programmée",
|
||||
"scheduled",
|
||||
"La notification est programmée pour envoi ultérieur",
|
||||
"schedule",
|
||||
"#FF9800",
|
||||
false,
|
||||
false),
|
||||
|
||||
EN_ATTENTE(
|
||||
"En attente",
|
||||
"pending",
|
||||
"La notification est en attente d'envoi",
|
||||
"hourglass_empty",
|
||||
"#FF9800",
|
||||
false,
|
||||
false),
|
||||
|
||||
// === STATUTS D'ENVOI ===
|
||||
EN_COURS_ENVOI(
|
||||
"En cours d'envoi",
|
||||
"sending",
|
||||
"La notification est en cours d'envoi",
|
||||
"send",
|
||||
"#2196F3",
|
||||
false,
|
||||
false),
|
||||
|
||||
ENVOYEE(
|
||||
"Envoyée",
|
||||
"sent",
|
||||
"La notification a été envoyée avec succès",
|
||||
"check_circle",
|
||||
"#4CAF50",
|
||||
true,
|
||||
false),
|
||||
|
||||
ECHEC_ENVOI(
|
||||
"Échec d'envoi",
|
||||
"failed",
|
||||
"L'envoi de la notification a échoué",
|
||||
"error",
|
||||
"#F44336",
|
||||
true,
|
||||
true),
|
||||
|
||||
PARTIELLEMENT_ENVOYEE(
|
||||
"Partiellement envoyée",
|
||||
"partial",
|
||||
"La notification a été envoyée à certains destinataires seulement",
|
||||
"warning",
|
||||
"#FF9800",
|
||||
true,
|
||||
true),
|
||||
|
||||
// === STATUTS DE RÉCEPTION ===
|
||||
RECUE(
|
||||
"Reçue",
|
||||
"received",
|
||||
"La notification a été reçue par l'appareil",
|
||||
"download_done",
|
||||
"#4CAF50",
|
||||
true,
|
||||
false),
|
||||
|
||||
AFFICHEE(
|
||||
"Affichée",
|
||||
"displayed",
|
||||
"La notification a été affichée à l'utilisateur",
|
||||
"visibility",
|
||||
"#2196F3",
|
||||
true,
|
||||
false),
|
||||
|
||||
OUVERTE(
|
||||
"Ouverte",
|
||||
"opened",
|
||||
"L'utilisateur a ouvert la notification",
|
||||
"open_in_new",
|
||||
"#4CAF50",
|
||||
true,
|
||||
false),
|
||||
|
||||
IGNOREE(
|
||||
"Ignorée",
|
||||
"ignored",
|
||||
"La notification a été ignorée par l'utilisateur",
|
||||
"visibility_off",
|
||||
"#9E9E9E",
|
||||
true,
|
||||
false),
|
||||
|
||||
// === STATUTS D'INTERACTION ===
|
||||
LUE(
|
||||
"Lue",
|
||||
"read",
|
||||
"La notification a été lue par l'utilisateur",
|
||||
"mark_email_read",
|
||||
"#4CAF50",
|
||||
true,
|
||||
false),
|
||||
|
||||
NON_LUE(
|
||||
"Non lue",
|
||||
"unread",
|
||||
"La notification n'a pas encore été lue",
|
||||
"mark_email_unread",
|
||||
"#FF9800",
|
||||
true,
|
||||
false),
|
||||
|
||||
MARQUEE_IMPORTANTE(
|
||||
"Marquée importante",
|
||||
"starred",
|
||||
"L'utilisateur a marqué la notification comme importante",
|
||||
"star",
|
||||
"#FF9800",
|
||||
true,
|
||||
false),
|
||||
|
||||
ACTION_EXECUTEE(
|
||||
"Action exécutée",
|
||||
"action_done",
|
||||
"L'utilisateur a exécuté l'action demandée",
|
||||
"task_alt",
|
||||
"#4CAF50",
|
||||
true,
|
||||
false),
|
||||
|
||||
// === STATUTS DE GESTION ===
|
||||
SUPPRIMEE(
|
||||
"Supprimée",
|
||||
"deleted",
|
||||
"La notification a été supprimée par l'utilisateur",
|
||||
"delete",
|
||||
"#F44336",
|
||||
false,
|
||||
false),
|
||||
|
||||
ARCHIVEE(
|
||||
"Archivée", "archived", "La notification a été archivée", "archive", "#9E9E9E", false, false),
|
||||
|
||||
EXPIREE(
|
||||
"Expirée",
|
||||
"expired",
|
||||
"La notification a dépassé sa durée de vie",
|
||||
"schedule",
|
||||
"#9E9E9E",
|
||||
false,
|
||||
false),
|
||||
|
||||
ANNULEE(
|
||||
"Annulée",
|
||||
"cancelled",
|
||||
"L'envoi de la notification a été annulé",
|
||||
"cancel",
|
||||
"#F44336",
|
||||
false,
|
||||
true),
|
||||
|
||||
// === STATUTS D'ERREUR ===
|
||||
ERREUR_TECHNIQUE(
|
||||
"Erreur technique",
|
||||
"error",
|
||||
"Une erreur technique a empêché le traitement",
|
||||
"bug_report",
|
||||
"#F44336",
|
||||
false,
|
||||
true),
|
||||
|
||||
DESTINATAIRE_INVALIDE(
|
||||
"Destinataire invalide",
|
||||
"invalid_recipient",
|
||||
"Le destinataire n'est pas valide",
|
||||
"person_off",
|
||||
"#F44336",
|
||||
false,
|
||||
true),
|
||||
|
||||
TOKEN_INVALIDE(
|
||||
"Token invalide",
|
||||
"invalid_token",
|
||||
"Le token FCM du destinataire est invalide",
|
||||
"key_off",
|
||||
"#F44336",
|
||||
false,
|
||||
true),
|
||||
|
||||
QUOTA_DEPASSE(
|
||||
"Quota dépassé",
|
||||
"quota_exceeded",
|
||||
"Le quota d'envoi a été dépassé",
|
||||
"block",
|
||||
"#F44336",
|
||||
false,
|
||||
true);
|
||||
|
||||
private final String libelle;
|
||||
private final String code;
|
||||
private final String description;
|
||||
private final String icone;
|
||||
private final String couleur;
|
||||
private final boolean visibleUtilisateur;
|
||||
private final boolean necessiteAttention;
|
||||
|
||||
/**
|
||||
* Constructeur de l'énumération StatutNotification
|
||||
*
|
||||
* @param libelle Le libellé affiché à l'utilisateur
|
||||
* @param code Le code technique du statut
|
||||
* @param description La description détaillée du statut
|
||||
* @param icone L'icône Material Design
|
||||
* @param couleur La couleur hexadécimale
|
||||
* @param visibleUtilisateur true si visible à l'utilisateur final
|
||||
* @param necessiteAttention true si le statut nécessite une attention particulière
|
||||
*/
|
||||
StatutNotification(
|
||||
String libelle,
|
||||
String code,
|
||||
String description,
|
||||
String icone,
|
||||
String couleur,
|
||||
boolean visibleUtilisateur,
|
||||
boolean necessiteAttention) {
|
||||
this.libelle = libelle;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
this.icone = icone;
|
||||
this.couleur = couleur;
|
||||
this.visibleUtilisateur = visibleUtilisateur;
|
||||
this.necessiteAttention = necessiteAttention;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le libellé du statut
|
||||
*
|
||||
* @return Le libellé affiché à l'utilisateur
|
||||
*/
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le code technique du statut
|
||||
*
|
||||
* @return Le code technique
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la description du statut
|
||||
*
|
||||
* @return La description détaillée
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'icône du statut
|
||||
*
|
||||
* @return L'icône Material Design
|
||||
*/
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la couleur du statut
|
||||
*
|
||||
* @return Le code couleur hexadécimal
|
||||
*/
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut est visible à l'utilisateur final
|
||||
*
|
||||
* @return true si visible à l'utilisateur
|
||||
*/
|
||||
public boolean isVisibleUtilisateur() {
|
||||
return visibleUtilisateur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut nécessite une attention particulière
|
||||
*
|
||||
* @return true si le statut nécessite attention
|
||||
*/
|
||||
public boolean isNecessiteAttention() {
|
||||
return necessiteAttention;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut indique un succès
|
||||
*
|
||||
* @return true si le statut indique un succès
|
||||
*/
|
||||
public boolean isSucces() {
|
||||
return this == ENVOYEE
|
||||
|| this == RECUE
|
||||
|| this == AFFICHEE
|
||||
|| this == OUVERTE
|
||||
|| this == LUE
|
||||
|| this == ACTION_EXECUTEE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut indique une erreur
|
||||
*
|
||||
* @return true si le statut indique une erreur
|
||||
*/
|
||||
public boolean isErreur() {
|
||||
return this == ECHEC_ENVOI
|
||||
|| this == ERREUR_TECHNIQUE
|
||||
|| this == DESTINATAIRE_INVALIDE
|
||||
|| this == TOKEN_INVALIDE
|
||||
|| this == QUOTA_DEPASSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut indique un état en cours
|
||||
*
|
||||
* @return true si le statut indique un traitement en cours
|
||||
*/
|
||||
public boolean isEnCours() {
|
||||
return this == PROGRAMMEE || this == EN_ATTENTE || this == EN_COURS_ENVOI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut indique un état final
|
||||
*
|
||||
* @return true si le statut est final (pas de transition possible)
|
||||
*/
|
||||
public boolean isFinal() {
|
||||
return this == SUPPRIMEE
|
||||
|| this == ARCHIVEE
|
||||
|| this == EXPIREE
|
||||
|| this == ANNULEE
|
||||
|| isErreur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut permet la modification
|
||||
*
|
||||
* @return true si la notification peut encore être modifiée
|
||||
*/
|
||||
public boolean permetModification() {
|
||||
return this == BROUILLON || this == PROGRAMMEE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le statut permet l'annulation
|
||||
*
|
||||
* @return true si la notification peut être annulée
|
||||
*/
|
||||
public boolean permetAnnulation() {
|
||||
return this == PROGRAMMEE || this == EN_ATTENTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la priorité d'affichage du statut
|
||||
*
|
||||
* @return La priorité (1=haute, 5=basse)
|
||||
*/
|
||||
public int getPrioriteAffichage() {
|
||||
if (isErreur()) return 1;
|
||||
if (necessiteAttention) return 2;
|
||||
if (isEnCours()) return 3;
|
||||
if (isSucces()) return 4;
|
||||
return 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les statuts suivants possibles
|
||||
*
|
||||
* @return Un tableau des statuts de transition possibles
|
||||
*/
|
||||
public StatutNotification[] getStatutsSuivantsPossibles() {
|
||||
return switch (this) {
|
||||
case BROUILLON -> new StatutNotification[] {PROGRAMMEE, EN_ATTENTE, ANNULEE};
|
||||
case PROGRAMMEE -> new StatutNotification[] {EN_ATTENTE, EN_COURS_ENVOI, ANNULEE};
|
||||
case EN_ATTENTE -> new StatutNotification[] {EN_COURS_ENVOI, ECHEC_ENVOI, ANNULEE};
|
||||
case EN_COURS_ENVOI -> new StatutNotification[] {ENVOYEE, PARTIELLEMENT_ENVOYEE, ECHEC_ENVOI};
|
||||
case ENVOYEE -> new StatutNotification[] {RECUE, ECHEC_ENVOI};
|
||||
case RECUE -> new StatutNotification[] {AFFICHEE, IGNOREE};
|
||||
case AFFICHEE -> new StatutNotification[] {OUVERTE, LUE, NON_LUE, IGNOREE};
|
||||
case OUVERTE -> new StatutNotification[] {LUE, ACTION_EXECUTEE, MARQUEE_IMPORTANTE};
|
||||
case NON_LUE -> new StatutNotification[] {LUE, OUVERTE, SUPPRIMEE, ARCHIVEE};
|
||||
case LUE ->
|
||||
new StatutNotification[] {ACTION_EXECUTEE, MARQUEE_IMPORTANTE, SUPPRIMEE, ARCHIVEE};
|
||||
default -> new StatutNotification[] {};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un statut par son code
|
||||
*
|
||||
* @param code Le code du statut
|
||||
* @return Le statut correspondant ou null si non trouvé
|
||||
*/
|
||||
public static StatutNotification parCode(String code) {
|
||||
for (StatutNotification statut : values()) {
|
||||
if (statut.getCode().equals(code)) {
|
||||
return statut;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne tous les statuts visibles à l'utilisateur
|
||||
*
|
||||
* @return Un tableau des statuts visibles
|
||||
*/
|
||||
public static StatutNotification[] getStatutsVisibles() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutNotification::isVisibleUtilisateur)
|
||||
.toArray(StatutNotification[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne tous les statuts d'erreur
|
||||
*
|
||||
* @return Un tableau des statuts d'erreur
|
||||
*/
|
||||
public static StatutNotification[] getStatutsErreur() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutNotification::isErreur)
|
||||
.toArray(StatutNotification[]::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dev.lions.unionflow.server.api.enums.notification;
|
||||
|
||||
/**
|
||||
* Énumération des types de notifications
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeNotification {
|
||||
EMAIL("Email"),
|
||||
SMS("SMS"),
|
||||
PUSH("Push Notification"),
|
||||
IN_APP("Notification In-App"),
|
||||
SYSTEME("Notification Système");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeNotification(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si ce type de notification est activé par défaut */
|
||||
public boolean isActiveeParDefaut() {
|
||||
// Par défaut, tous les types sont activés sauf SYSTEME
|
||||
return this != SYSTEME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.organisation;
|
||||
|
||||
/**
|
||||
* Énumération des statuts d'organisations dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutOrganisation {
|
||||
ACTIVE("Active"),
|
||||
INACTIVE("Inactive"),
|
||||
SUSPENDUE("Suspendue"),
|
||||
EN_CREATION("En Création"),
|
||||
DISSOUTE("Dissoute");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutOrganisation(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.lions.unionflow.server.api.enums.organisation;
|
||||
|
||||
/**
|
||||
* Énumération des types d'organisations supportés par UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum TypeOrganisation {
|
||||
LIONS_CLUB("Lions Club"),
|
||||
ASSOCIATION("Association"),
|
||||
FEDERATION("Fédération"),
|
||||
COOPERATIVE("Coopérative"),
|
||||
MUTUELLE("Mutuelle"),
|
||||
SYNDICAT("Syndicat"),
|
||||
FONDATION("Fondation"),
|
||||
ONG("ONG");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeOrganisation(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des méthodes de paiement dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum MethodePaiement {
|
||||
WAVE_MOBILE_MONEY("Wave Mobile Money"),
|
||||
ORANGE_MONEY("Orange Money"),
|
||||
MTN_MOBILE_MONEY("MTN Mobile Money"),
|
||||
MOOV_MONEY("Moov Money"),
|
||||
VIREMENT_BANCAIRE("Virement Bancaire"),
|
||||
CARTE_BANCAIRE("Carte Bancaire"),
|
||||
ESPECES("Espèces"),
|
||||
CHEQUE("Chèque"),
|
||||
AUTRE("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
MethodePaiement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si c'est une méthode de mobile money */
|
||||
public boolean isMobileMoney() {
|
||||
return this == WAVE_MOBILE_MONEY
|
||||
|| this == ORANGE_MONEY
|
||||
|| this == MTN_MOBILE_MONEY
|
||||
|| this == MOOV_MONEY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de paiement dans UnionFlow
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutPaiement {
|
||||
EN_ATTENTE("En Attente"),
|
||||
EN_COURS("En Cours"),
|
||||
VALIDE("Validé"),
|
||||
ECHOUE("Échoué"),
|
||||
ANNULE("Annulé"),
|
||||
REMBOURSE("Remboursé");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutPaiement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si le paiement est finalisé (ne peut plus changer) */
|
||||
public boolean isFinalise() {
|
||||
return this == VALIDE || this == ECHOUE || this == ANNULE || this == REMBOURSE;
|
||||
}
|
||||
|
||||
/** Vérifie si le paiement est en cours de traitement */
|
||||
public boolean isEnTraitement() {
|
||||
return this == EN_ATTENTE || this == EN_COURS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de sessions de paiement Wave Money
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutSession {
|
||||
PENDING("En attente"),
|
||||
COMPLETED("Complétée"),
|
||||
CANCELLED("Annulée"),
|
||||
EXPIRED("Expirée"),
|
||||
FAILED("Échouée");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutSession(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de traitement des webhooks Wave Money
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum StatutTraitement {
|
||||
RECU("Reçu"),
|
||||
EN_COURS("En cours de traitement"),
|
||||
TRAITE("Traité avec succès"),
|
||||
ECHEC("Échec de traitement"),
|
||||
IGNORE("Ignoré");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutTraitement(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package dev.lions.unionflow.server.api.enums.paiement;
|
||||
|
||||
/**
|
||||
* Énumération des types d'événements Wave Money pour les webhooks
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
public enum TypeEvenement {
|
||||
CHECKOUT_COMPLETE("checkout.complete"),
|
||||
CHECKOUT_CANCELLED("checkout.cancelled"),
|
||||
CHECKOUT_EXPIRED("checkout.expired"),
|
||||
PAYOUT_COMPLETE("payout.complete"),
|
||||
PAYOUT_FAILED("payout.failed"),
|
||||
BALANCE_UPDATED("balance.updated"),
|
||||
TRANSACTION_CREATED("transaction.created"),
|
||||
TRANSACTION_UPDATED("transaction.updated");
|
||||
|
||||
private final String codeWave;
|
||||
|
||||
TypeEvenement(String codeWave) {
|
||||
this.codeWave = codeWave;
|
||||
}
|
||||
|
||||
public String getCodeWave() {
|
||||
return codeWave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un type d'événement par son code Wave
|
||||
*
|
||||
* @param code Le code Wave
|
||||
* @return Le type d'événement correspondant ou null si non trouvé
|
||||
*/
|
||||
public static TypeEvenement fromCode(String code) {
|
||||
for (TypeEvenement type : values()) {
|
||||
if (type.codeWave.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package dev.lions.unionflow.server.api.enums.solidarite;
|
||||
|
||||
/**
|
||||
* Énumération des priorités d'aide dans le système de solidarité
|
||||
*
|
||||
* <p>Cette énumération définit les niveaux de priorité pour les demandes d'aide, permettant de
|
||||
* prioriser le traitement selon l'urgence de la situation.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum PrioriteAide {
|
||||
CRITIQUE(
|
||||
"Critique",
|
||||
"critical",
|
||||
1,
|
||||
"Situation critique nécessitant une intervention immédiate",
|
||||
"#F44336",
|
||||
"emergency",
|
||||
24,
|
||||
true,
|
||||
true),
|
||||
|
||||
URGENTE(
|
||||
"Urgente",
|
||||
"urgent",
|
||||
2,
|
||||
"Situation urgente nécessitant une réponse rapide",
|
||||
"#FF5722",
|
||||
"priority_high",
|
||||
72,
|
||||
true,
|
||||
false),
|
||||
|
||||
ELEVEE(
|
||||
"Élevée",
|
||||
"high",
|
||||
3,
|
||||
"Priorité élevée, traitement dans les meilleurs délais",
|
||||
"#FF9800",
|
||||
"keyboard_arrow_up",
|
||||
168,
|
||||
false,
|
||||
false),
|
||||
|
||||
NORMALE(
|
||||
"Normale",
|
||||
"normal",
|
||||
4,
|
||||
"Priorité normale, traitement selon les délais standards",
|
||||
"#2196F3",
|
||||
"remove",
|
||||
336,
|
||||
false,
|
||||
false),
|
||||
|
||||
FAIBLE(
|
||||
"Faible",
|
||||
"low",
|
||||
5,
|
||||
"Priorité faible, traitement quand les ressources le permettent",
|
||||
"#4CAF50",
|
||||
"keyboard_arrow_down",
|
||||
720,
|
||||
false,
|
||||
false);
|
||||
|
||||
private final String libelle;
|
||||
private final String code;
|
||||
private final int niveau;
|
||||
private final String description;
|
||||
private final String couleur;
|
||||
private final String icone;
|
||||
private final int delaiTraitementHeures;
|
||||
private final boolean notificationImmediate;
|
||||
private final boolean escaladeAutomatique;
|
||||
|
||||
PrioriteAide(
|
||||
String libelle,
|
||||
String code,
|
||||
int niveau,
|
||||
String description,
|
||||
String couleur,
|
||||
String icone,
|
||||
int delaiTraitementHeures,
|
||||
boolean notificationImmediate,
|
||||
boolean escaladeAutomatique) {
|
||||
this.libelle = libelle;
|
||||
this.code = code;
|
||||
this.niveau = niveau;
|
||||
this.description = description;
|
||||
this.couleur = couleur;
|
||||
this.icone = icone;
|
||||
this.delaiTraitementHeures = delaiTraitementHeures;
|
||||
this.notificationImmediate = notificationImmediate;
|
||||
this.escaladeAutomatique = escaladeAutomatique;
|
||||
}
|
||||
|
||||
// === GETTERS ===
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getNiveau() {
|
||||
return niveau;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public int getDelaiTraitementHeures() {
|
||||
return delaiTraitementHeures;
|
||||
}
|
||||
|
||||
public boolean isNotificationImmediate() {
|
||||
return notificationImmediate;
|
||||
}
|
||||
|
||||
public boolean isEscaladeAutomatique() {
|
||||
return escaladeAutomatique;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si la priorité est critique ou urgente */
|
||||
public boolean isUrgente() {
|
||||
return this == CRITIQUE || this == URGENTE;
|
||||
}
|
||||
|
||||
/** Vérifie si la priorité nécessite un traitement immédiat */
|
||||
public boolean necessiteTraitementImmediat() {
|
||||
return niveau <= 2;
|
||||
}
|
||||
|
||||
/** Retourne la date limite de traitement */
|
||||
public java.time.LocalDateTime getDateLimiteTraitement() {
|
||||
return java.time.LocalDateTime.now().plusHours(delaiTraitementHeures);
|
||||
}
|
||||
|
||||
/** Retourne la priorité suivante (escalade) */
|
||||
public PrioriteAide getPrioriteEscalade() {
|
||||
return switch (this) {
|
||||
case FAIBLE -> NORMALE;
|
||||
case NORMALE -> ELEVEE;
|
||||
case ELEVEE -> URGENTE;
|
||||
case URGENTE -> CRITIQUE;
|
||||
case CRITIQUE -> CRITIQUE; // Déjà au maximum
|
||||
};
|
||||
}
|
||||
|
||||
/** Détermine la priorité basée sur le type d'aide */
|
||||
public static PrioriteAide determinerPriorite(TypeAide typeAide) {
|
||||
if (typeAide.isUrgent()) {
|
||||
return switch (typeAide) {
|
||||
case AIDE_FINANCIERE_URGENTE, AIDE_FRAIS_MEDICAUX -> CRITIQUE;
|
||||
case HEBERGEMENT_URGENCE, AIDE_ALIMENTAIRE -> URGENTE;
|
||||
default -> ELEVEE;
|
||||
};
|
||||
}
|
||||
|
||||
if (typeAide.getPriorite().equals("important")) {
|
||||
return ELEVEE;
|
||||
}
|
||||
|
||||
return NORMALE;
|
||||
}
|
||||
|
||||
/** Retourne les priorités urgentes */
|
||||
public static java.util.List<PrioriteAide> getPrioritesUrgentes() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(PrioriteAide::isUrgente)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les priorités par niveau croissant */
|
||||
public static java.util.List<PrioriteAide> getParNiveauCroissant() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.sorted(java.util.Comparator.comparingInt(PrioriteAide::getNiveau))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les priorités par niveau décroissant */
|
||||
public static java.util.List<PrioriteAide> getParNiveauDecroissant() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.sorted(java.util.Comparator.comparingInt(PrioriteAide::getNiveau).reversed())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Trouve la priorité par code */
|
||||
public static PrioriteAide parCode(String code) {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(p -> p.getCode().equals(code))
|
||||
.findFirst()
|
||||
.orElse(NORMALE);
|
||||
}
|
||||
|
||||
/** Calcule le score de priorité (plus bas = plus prioritaire) */
|
||||
public double getScorePriorite() {
|
||||
double score = niveau;
|
||||
|
||||
// Bonus pour notification immédiate
|
||||
if (notificationImmediate) score -= 0.5;
|
||||
|
||||
// Bonus pour escalade automatique
|
||||
if (escaladeAutomatique) score -= 0.3;
|
||||
|
||||
// Malus pour délai long
|
||||
if (delaiTraitementHeures > 168) score += 0.2;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/** Vérifie si le délai de traitement est dépassé */
|
||||
public boolean isDelaiDepasse(java.time.LocalDateTime dateCreation) {
|
||||
return isDelaiDepasse(dateCreation, java.time.LocalDateTime.now());
|
||||
}
|
||||
|
||||
/** Vérifie si le délai de traitement est dépassé (version avec date de référence) */
|
||||
public boolean isDelaiDepasse(java.time.LocalDateTime dateCreation, java.time.LocalDateTime maintenant) {
|
||||
java.time.LocalDateTime dateLimite = dateCreation.plusHours(delaiTraitementHeures);
|
||||
// Utilise isAfter strictement : le délai est dépassé seulement si on est APRÈS la limite
|
||||
// Si on est exactement à la limite, le délai n'est pas encore dépassé
|
||||
return maintenant.isAfter(dateLimite);
|
||||
}
|
||||
|
||||
/** Calcule le pourcentage de temps écoulé */
|
||||
public double getPourcentageTempsEcoule(java.time.LocalDateTime dateCreation) {
|
||||
java.time.LocalDateTime maintenant = java.time.LocalDateTime.now();
|
||||
java.time.LocalDateTime dateLimite = dateCreation.plusHours(delaiTraitementHeures);
|
||||
|
||||
long dureeTotal = java.time.Duration.between(dateCreation, dateLimite).toMinutes();
|
||||
long dureeEcoulee = java.time.Duration.between(dateCreation, maintenant).toMinutes();
|
||||
|
||||
if (dureeTotal <= 0) return 100.0;
|
||||
|
||||
return Math.min(100.0, (dureeEcoulee * 100.0) / dureeTotal);
|
||||
}
|
||||
|
||||
/** Retourne le message d'alerte selon le temps écoulé */
|
||||
public String getMessageAlerte(java.time.LocalDateTime dateCreation) {
|
||||
double pourcentage = getPourcentageTempsEcoule(dateCreation);
|
||||
|
||||
if (pourcentage >= 100) {
|
||||
return "Délai de traitement dépassé !";
|
||||
} else if (pourcentage >= 80) {
|
||||
return "Délai de traitement bientôt dépassé";
|
||||
} else if (pourcentage >= 60) {
|
||||
return "Plus de la moitié du délai écoulé";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package dev.lions.unionflow.server.api.enums.solidarite;
|
||||
|
||||
/**
|
||||
* Énumération des statuts d'aide dans le système de solidarité
|
||||
*
|
||||
* <p>Cette énumération définit les différents statuts qu'une demande d'aide peut avoir tout au long
|
||||
* de son cycle de vie.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum StatutAide {
|
||||
|
||||
// === STATUTS INITIAUX ===
|
||||
BROUILLON(
|
||||
"Brouillon",
|
||||
"draft",
|
||||
"La demande est en cours de rédaction",
|
||||
"#9E9E9E",
|
||||
"edit",
|
||||
false,
|
||||
false),
|
||||
SOUMISE(
|
||||
"Soumise",
|
||||
"submitted",
|
||||
"La demande a été soumise et attend validation",
|
||||
"#FF9800",
|
||||
"send",
|
||||
false,
|
||||
false),
|
||||
|
||||
// === STATUTS D'ÉVALUATION ===
|
||||
EN_ATTENTE(
|
||||
"En attente",
|
||||
"pending",
|
||||
"La demande est en attente d'évaluation",
|
||||
"#2196F3",
|
||||
"hourglass_empty",
|
||||
false,
|
||||
false),
|
||||
EN_COURS_EVALUATION(
|
||||
"En cours d'évaluation",
|
||||
"under_review",
|
||||
"La demande est en cours d'évaluation",
|
||||
"#FF9800",
|
||||
"rate_review",
|
||||
false,
|
||||
false),
|
||||
INFORMATIONS_REQUISES(
|
||||
"Informations requises",
|
||||
"info_required",
|
||||
"Des informations complémentaires sont requises",
|
||||
"#FF5722",
|
||||
"info",
|
||||
false,
|
||||
false),
|
||||
|
||||
// === STATUTS DE DÉCISION ===
|
||||
APPROUVEE(
|
||||
"Approuvée",
|
||||
"approved",
|
||||
"La demande a été approuvée",
|
||||
"#4CAF50",
|
||||
"check_circle",
|
||||
true,
|
||||
false),
|
||||
APPROUVEE_PARTIELLEMENT(
|
||||
"Approuvée partiellement",
|
||||
"partially_approved",
|
||||
"La demande a été approuvée partiellement",
|
||||
"#8BC34A",
|
||||
"check_circle_outline",
|
||||
true,
|
||||
false),
|
||||
REJETEE("Rejetée", "rejected", "La demande a été rejetée", "#F44336", "cancel", true, true),
|
||||
|
||||
// === STATUTS DE TRAITEMENT ===
|
||||
EN_COURS_TRAITEMENT(
|
||||
"En cours de traitement",
|
||||
"processing",
|
||||
"La demande approuvée est en cours de traitement",
|
||||
"#9C27B0",
|
||||
"settings",
|
||||
false,
|
||||
false),
|
||||
EN_COURS_VERSEMENT(
|
||||
"En cours de versement",
|
||||
"payment_processing",
|
||||
"Le versement est en cours",
|
||||
"#3F51B5",
|
||||
"payment",
|
||||
false,
|
||||
false),
|
||||
|
||||
// === STATUTS FINAUX ===
|
||||
VERSEE("Versée", "paid", "L'aide a été versée avec succès", "#4CAF50", "paid", true, false),
|
||||
LIVREE(
|
||||
"Livrée",
|
||||
"delivered",
|
||||
"L'aide matérielle a été livrée",
|
||||
"#4CAF50",
|
||||
"local_shipping",
|
||||
true,
|
||||
false),
|
||||
TERMINEE(
|
||||
"Terminée",
|
||||
"completed",
|
||||
"L'aide a été fournie avec succès",
|
||||
"#4CAF50",
|
||||
"done_all",
|
||||
true,
|
||||
false),
|
||||
|
||||
// === STATUTS D'EXCEPTION ===
|
||||
ANNULEE("Annulée", "cancelled", "La demande a été annulée", "#9E9E9E", "cancel", true, true),
|
||||
SUSPENDUE(
|
||||
"Suspendue",
|
||||
"suspended",
|
||||
"La demande a été suspendue temporairement",
|
||||
"#FF5722",
|
||||
"pause_circle",
|
||||
false,
|
||||
false),
|
||||
EXPIREE("Expirée", "expired", "La demande a expiré", "#795548", "schedule", true, true),
|
||||
|
||||
// === STATUTS DE SUIVI ===
|
||||
EN_SUIVI(
|
||||
"En suivi",
|
||||
"follow_up",
|
||||
"L'aide fait l'objet d'un suivi",
|
||||
"#607D8B",
|
||||
"track_changes",
|
||||
false,
|
||||
false),
|
||||
CLOTUREE("Clôturée", "closed", "Le dossier d'aide est clôturé", "#9E9E9E", "folder", true, false);
|
||||
|
||||
private final String libelle;
|
||||
private final String code;
|
||||
private final String description;
|
||||
private final String couleur;
|
||||
private final String icone;
|
||||
private final boolean estFinal;
|
||||
private final boolean estEchec;
|
||||
|
||||
StatutAide(
|
||||
String libelle,
|
||||
String code,
|
||||
String description,
|
||||
String couleur,
|
||||
String icone,
|
||||
boolean estFinal,
|
||||
boolean estEchec) {
|
||||
this.libelle = libelle;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
this.couleur = couleur;
|
||||
this.icone = icone;
|
||||
this.estFinal = estFinal;
|
||||
this.estEchec = estEchec;
|
||||
}
|
||||
|
||||
// === GETTERS ===
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public boolean isEstFinal() {
|
||||
return estFinal;
|
||||
}
|
||||
|
||||
public boolean isEstEchec() {
|
||||
return estEchec;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si le statut indique un succès */
|
||||
public boolean isSucces() {
|
||||
return this == VERSEE || this == LIVREE || this == TERMINEE;
|
||||
}
|
||||
|
||||
/** Vérifie si le statut est en cours de traitement */
|
||||
public boolean isEnCours() {
|
||||
return this == EN_COURS_EVALUATION || this == EN_COURS_TRAITEMENT || this == EN_COURS_VERSEMENT;
|
||||
}
|
||||
|
||||
/** Vérifie si le statut permet la modification */
|
||||
public boolean permetModification() {
|
||||
return this == BROUILLON || this == INFORMATIONS_REQUISES;
|
||||
}
|
||||
|
||||
/** Vérifie si le statut permet l'annulation */
|
||||
public boolean permetAnnulation() {
|
||||
return !estFinal && this != ANNULEE;
|
||||
}
|
||||
|
||||
/** Retourne les statuts finaux */
|
||||
public static java.util.List<StatutAide> getStatutsFinaux() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutAide::isEstFinal)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les statuts d'échec */
|
||||
public static java.util.List<StatutAide> getStatutsEchec() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutAide::isEstEchec)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les statuts de succès */
|
||||
public static java.util.List<StatutAide> getStatutsSucces() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutAide::isSucces)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les statuts en cours */
|
||||
public static java.util.List<StatutAide> getStatutsEnCours() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(StatutAide::isEnCours)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Vérifie si la transition vers un autre statut est valide */
|
||||
public boolean peutTransitionnerVers(StatutAide nouveauStatut) {
|
||||
// Règles de transition simplifiées
|
||||
if (this == nouveauStatut) return false;
|
||||
|
||||
// Les statuts finaux ne peuvent transitionner que vers EN_SUIVI
|
||||
// Exception : APPROUVEE et APPROUVEE_PARTIELLEMENT peuvent transitionner vers EN_COURS_TRAITEMENT ou SUSPENDUE
|
||||
// car ce sont des statuts de décision qui doivent permettre le démarrage du traitement
|
||||
if (estFinal
|
||||
&& this != APPROUVEE
|
||||
&& this != APPROUVEE_PARTIELLEMENT
|
||||
&& nouveauStatut != EN_SUIVI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return switch (this) {
|
||||
case BROUILLON -> nouveauStatut == SOUMISE || nouveauStatut == ANNULEE;
|
||||
case SOUMISE -> nouveauStatut == EN_ATTENTE || nouveauStatut == ANNULEE;
|
||||
case EN_ATTENTE -> nouveauStatut == EN_COURS_EVALUATION || nouveauStatut == ANNULEE;
|
||||
case EN_COURS_EVALUATION ->
|
||||
nouveauStatut == APPROUVEE
|
||||
|| nouveauStatut == APPROUVEE_PARTIELLEMENT
|
||||
|| nouveauStatut == REJETEE
|
||||
|| nouveauStatut == INFORMATIONS_REQUISES
|
||||
|| nouveauStatut == SUSPENDUE;
|
||||
case INFORMATIONS_REQUISES ->
|
||||
nouveauStatut == EN_COURS_EVALUATION || nouveauStatut == ANNULEE;
|
||||
case APPROUVEE, APPROUVEE_PARTIELLEMENT ->
|
||||
nouveauStatut == EN_COURS_TRAITEMENT || nouveauStatut == SUSPENDUE;
|
||||
case EN_COURS_TRAITEMENT ->
|
||||
nouveauStatut == EN_COURS_VERSEMENT
|
||||
|| nouveauStatut == LIVREE
|
||||
|| nouveauStatut == TERMINEE
|
||||
|| nouveauStatut == SUSPENDUE;
|
||||
case EN_COURS_VERSEMENT -> nouveauStatut == VERSEE || nouveauStatut == SUSPENDUE;
|
||||
case SUSPENDUE -> nouveauStatut == EN_COURS_EVALUATION || nouveauStatut == ANNULEE;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne le niveau de priorité pour l'affichage */
|
||||
public int getNiveauPriorite() {
|
||||
return switch (this) {
|
||||
case INFORMATIONS_REQUISES -> 1;
|
||||
case EN_COURS_EVALUATION, EN_COURS_TRAITEMENT, EN_COURS_VERSEMENT -> 2;
|
||||
case APPROUVEE, APPROUVEE_PARTIELLEMENT -> 3;
|
||||
case EN_ATTENTE, SOUMISE -> 4;
|
||||
case SUSPENDUE -> 5;
|
||||
case BROUILLON -> 6;
|
||||
case EN_SUIVI -> 7;
|
||||
default -> 8; // Statuts finaux
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
package dev.lions.unionflow.server.api.enums.solidarite;
|
||||
|
||||
/**
|
||||
* Énumération des types d'aide disponibles dans le système de solidarité
|
||||
*
|
||||
* <p>Cette énumération définit les différents types d'aide que les membres peuvent demander ou
|
||||
* proposer dans le cadre du système de solidarité.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public enum TypeAide {
|
||||
|
||||
// === AIDE FINANCIÈRE ===
|
||||
AIDE_FINANCIERE_URGENTE(
|
||||
"Aide financière urgente",
|
||||
"financiere",
|
||||
"urgent",
|
||||
"Aide financière pour situation d'urgence",
|
||||
"emergency_fund",
|
||||
"#F44336",
|
||||
true,
|
||||
true,
|
||||
5000.0,
|
||||
50000.0,
|
||||
7),
|
||||
|
||||
PRET_SANS_INTERET(
|
||||
"Prêt sans intérêt",
|
||||
"financiere",
|
||||
"important",
|
||||
"Prêt sans intérêt entre membres",
|
||||
"account_balance",
|
||||
"#FF9800",
|
||||
true,
|
||||
true,
|
||||
10000.0,
|
||||
100000.0,
|
||||
30),
|
||||
|
||||
AIDE_COTISATION(
|
||||
"Aide pour cotisation",
|
||||
"financiere",
|
||||
"normal",
|
||||
"Aide pour payer les cotisations",
|
||||
"payment",
|
||||
"#2196F3",
|
||||
true,
|
||||
false,
|
||||
1000.0,
|
||||
10000.0,
|
||||
14),
|
||||
|
||||
AIDE_FRAIS_MEDICAUX(
|
||||
"Aide frais médicaux",
|
||||
"financiere",
|
||||
"urgent",
|
||||
"Aide pour frais médicaux et hospitaliers",
|
||||
"medical_services",
|
||||
"#E91E63",
|
||||
true,
|
||||
true,
|
||||
5000.0,
|
||||
200000.0,
|
||||
7),
|
||||
|
||||
AIDE_FRAIS_SCOLARITE(
|
||||
"Aide frais de scolarité",
|
||||
"financiere",
|
||||
"important",
|
||||
"Aide pour frais de scolarité des enfants",
|
||||
"school",
|
||||
"#9C27B0",
|
||||
true,
|
||||
true,
|
||||
10000.0,
|
||||
100000.0,
|
||||
21),
|
||||
|
||||
// === AIDE MATÉRIELLE ===
|
||||
DON_MATERIEL(
|
||||
"Don de matériel",
|
||||
"materielle",
|
||||
"normal",
|
||||
"Don d'objets, équipements ou matériel",
|
||||
"inventory",
|
||||
"#4CAF50",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
14),
|
||||
|
||||
PRET_MATERIEL(
|
||||
"Prêt de matériel",
|
||||
"materielle",
|
||||
"normal",
|
||||
"Prêt temporaire d'objets ou équipements",
|
||||
"build",
|
||||
"#607D8B",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
30),
|
||||
|
||||
AIDE_DEMENAGEMENT(
|
||||
"Aide déménagement",
|
||||
"materielle",
|
||||
"normal",
|
||||
"Aide pour déménagement (transport, main d'œuvre)",
|
||||
"local_shipping",
|
||||
"#795548",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
7),
|
||||
|
||||
AIDE_TRAVAUX(
|
||||
"Aide travaux",
|
||||
"materielle",
|
||||
"normal",
|
||||
"Aide pour travaux de rénovation ou construction",
|
||||
"construction",
|
||||
"#FF5722",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
21),
|
||||
|
||||
// === AIDE PROFESSIONNELLE ===
|
||||
AIDE_RECHERCHE_EMPLOI(
|
||||
"Aide recherche d'emploi",
|
||||
"professionnelle",
|
||||
"important",
|
||||
"Aide pour recherche d'emploi et CV",
|
||||
"work",
|
||||
"#3F51B5",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
30),
|
||||
|
||||
FORMATION_PROFESSIONNELLE(
|
||||
"Formation professionnelle",
|
||||
"professionnelle",
|
||||
"normal",
|
||||
"Formation et développement des compétences",
|
||||
"school",
|
||||
"#009688",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
60),
|
||||
|
||||
CONSEIL_JURIDIQUE(
|
||||
"Conseil juridique",
|
||||
"professionnelle",
|
||||
"important",
|
||||
"Conseil et assistance juridique",
|
||||
"gavel",
|
||||
"#8BC34A",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
14),
|
||||
|
||||
AIDE_CREATION_ENTREPRISE(
|
||||
"Aide création d'entreprise",
|
||||
"professionnelle",
|
||||
"normal",
|
||||
"Accompagnement création d'entreprise",
|
||||
"business",
|
||||
"#CDDC39",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
90),
|
||||
|
||||
// === AIDE SOCIALE ===
|
||||
GARDE_ENFANTS(
|
||||
"Garde d'enfants",
|
||||
"sociale",
|
||||
"normal",
|
||||
"Garde d'enfants ponctuelle ou régulière",
|
||||
"child_care",
|
||||
"#FFC107",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
7),
|
||||
|
||||
AIDE_PERSONNES_AGEES(
|
||||
"Aide personnes âgées",
|
||||
"sociale",
|
||||
"important",
|
||||
"Aide et accompagnement personnes âgées",
|
||||
"elderly",
|
||||
"#FF9800",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
30),
|
||||
|
||||
TRANSPORT(
|
||||
"Transport",
|
||||
"sociale",
|
||||
"normal",
|
||||
"Aide au transport (covoiturage, accompagnement)",
|
||||
"directions_car",
|
||||
"#2196F3",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
7),
|
||||
|
||||
AIDE_ADMINISTRATIVE(
|
||||
"Aide administrative",
|
||||
"sociale",
|
||||
"normal",
|
||||
"Aide pour démarches administratives",
|
||||
"description",
|
||||
"#9E9E9E",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
14),
|
||||
|
||||
// === AIDE D'URGENCE ===
|
||||
HEBERGEMENT_URGENCE(
|
||||
"Hébergement d'urgence",
|
||||
"urgence",
|
||||
"urgent",
|
||||
"Hébergement temporaire d'urgence",
|
||||
"home",
|
||||
"#F44336",
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
7),
|
||||
|
||||
AIDE_ALIMENTAIRE(
|
||||
"Aide alimentaire",
|
||||
"urgence",
|
||||
"urgent",
|
||||
"Aide alimentaire d'urgence",
|
||||
"restaurant",
|
||||
"#FF5722",
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
3),
|
||||
|
||||
AIDE_VESTIMENTAIRE(
|
||||
"Aide vestimentaire",
|
||||
"urgence",
|
||||
"normal",
|
||||
"Don de vêtements et accessoires",
|
||||
"checkroom",
|
||||
"#795548",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
14),
|
||||
|
||||
// === AIDE SPÉCIALISÉE ===
|
||||
SOUTIEN_PSYCHOLOGIQUE(
|
||||
"Soutien psychologique",
|
||||
"specialisee",
|
||||
"important",
|
||||
"Soutien et écoute psychologique",
|
||||
"psychology",
|
||||
"#E91E63",
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
30),
|
||||
|
||||
AIDE_NUMERIQUE(
|
||||
"Aide numérique",
|
||||
"specialisee",
|
||||
"normal",
|
||||
"Aide pour utilisation outils numériques",
|
||||
"computer",
|
||||
"#607D8B",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
14),
|
||||
|
||||
TRADUCTION(
|
||||
"Traduction",
|
||||
"specialisee",
|
||||
"normal",
|
||||
"Services de traduction et interprétariat",
|
||||
"translate",
|
||||
"#9C27B0",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
7),
|
||||
|
||||
AUTRE(
|
||||
"Autre",
|
||||
"autre",
|
||||
"normal",
|
||||
"Autre type d'aide non catégorisé",
|
||||
"help",
|
||||
"#9E9E9E",
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
14);
|
||||
|
||||
private final String libelle;
|
||||
private final String categorie;
|
||||
private final String priorite;
|
||||
private final String description;
|
||||
private final String icone;
|
||||
private final String couleur;
|
||||
private final boolean necessiteMontant;
|
||||
private final boolean necessiteValidation;
|
||||
private final Double montantMin;
|
||||
private final Double montantMax;
|
||||
private final int delaiReponseJours;
|
||||
|
||||
TypeAide(
|
||||
String libelle,
|
||||
String categorie,
|
||||
String priorite,
|
||||
String description,
|
||||
String icone,
|
||||
String couleur,
|
||||
boolean necessiteMontant,
|
||||
boolean necessiteValidation,
|
||||
Double montantMin,
|
||||
Double montantMax,
|
||||
int delaiReponseJours) {
|
||||
this.libelle = libelle;
|
||||
this.categorie = categorie;
|
||||
this.priorite = priorite;
|
||||
this.description = description;
|
||||
this.icone = icone;
|
||||
this.couleur = couleur;
|
||||
this.necessiteMontant = necessiteMontant;
|
||||
this.necessiteValidation = necessiteValidation;
|
||||
this.montantMin = montantMin;
|
||||
this.montantMax = montantMax;
|
||||
this.delaiReponseJours = delaiReponseJours;
|
||||
}
|
||||
|
||||
// === GETTERS ===
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
public String getCategorie() {
|
||||
return categorie;
|
||||
}
|
||||
|
||||
public String getPriorite() {
|
||||
return priorite;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getIcone() {
|
||||
return icone;
|
||||
}
|
||||
|
||||
public String getCouleur() {
|
||||
return couleur;
|
||||
}
|
||||
|
||||
public boolean isNecessiteMontant() {
|
||||
return necessiteMontant;
|
||||
}
|
||||
|
||||
public boolean isNecessiteValidation() {
|
||||
return necessiteValidation;
|
||||
}
|
||||
|
||||
public Double getMontantMin() {
|
||||
return montantMin;
|
||||
}
|
||||
|
||||
public Double getMontantMax() {
|
||||
return montantMax;
|
||||
}
|
||||
|
||||
public int getDelaiReponseJours() {
|
||||
return delaiReponseJours;
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/** Vérifie si le type d'aide est urgent */
|
||||
public boolean isUrgent() {
|
||||
return "urgent".equals(priorite);
|
||||
}
|
||||
|
||||
/** Vérifie si le type d'aide est financier */
|
||||
public boolean isFinancier() {
|
||||
return "financiere".equals(categorie);
|
||||
}
|
||||
|
||||
/** Vérifie si le type d'aide est matériel */
|
||||
public boolean isMateriel() {
|
||||
return "materielle".equals(categorie);
|
||||
}
|
||||
|
||||
/** Vérifie si le montant est dans la fourchette autorisée */
|
||||
public boolean isMontantValide(Double montant) {
|
||||
if (!necessiteMontant || montant == null) return true;
|
||||
if (montantMin != null && montant < montantMin) return false;
|
||||
if (montantMax != null && montant > montantMax) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Retourne le niveau de priorité numérique */
|
||||
public int getNiveauPriorite() {
|
||||
return switch (priorite) {
|
||||
case "urgent" -> 1;
|
||||
case "important" -> 2;
|
||||
case "normal" -> 3;
|
||||
default -> 3;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne la date limite de réponse */
|
||||
public java.time.LocalDateTime getDateLimiteReponse() {
|
||||
return java.time.LocalDateTime.now().plusDays(delaiReponseJours);
|
||||
}
|
||||
|
||||
/** Retourne les types d'aide par catégorie */
|
||||
public static java.util.List<TypeAide> getParCategorie(String categorie) {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(type -> type.getCategorie().equals(categorie))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les types d'aide urgents */
|
||||
public static java.util.List<TypeAide> getUrgents() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(TypeAide::isUrgent)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les types d'aide financiers */
|
||||
public static java.util.List<TypeAide> getFinanciers() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.filter(TypeAide::isFinancier)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
/** Retourne les catégories disponibles */
|
||||
public static java.util.Set<String> getCategories() {
|
||||
return java.util.Arrays.stream(values())
|
||||
.map(TypeAide::getCategorie)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
}
|
||||
|
||||
/** Retourne le libellé de la catégorie */
|
||||
public String getLibelleCategorie() {
|
||||
return switch (categorie) {
|
||||
case "financiere" -> "Aide financière";
|
||||
case "materielle" -> "Aide matérielle";
|
||||
case "professionnelle" -> "Aide professionnelle";
|
||||
case "sociale" -> "Aide sociale";
|
||||
case "urgence" -> "Aide d'urgence";
|
||||
case "specialisee" -> "Aide spécialisée";
|
||||
case "autre" -> "Autre";
|
||||
default -> categorie;
|
||||
};
|
||||
}
|
||||
|
||||
/** Retourne l'unité du montant si applicable */
|
||||
public String getUniteMontant() {
|
||||
return necessiteMontant ? "FCFA" : null;
|
||||
}
|
||||
|
||||
/** Retourne le message de validation du montant */
|
||||
public String getMessageValidationMontant(Double montant) {
|
||||
if (!necessiteMontant) return null;
|
||||
if (montant == null) return "Le montant est obligatoire";
|
||||
if (montantMin != null && montant < montantMin) {
|
||||
return String.format("Le montant minimum est de %.0f FCFA", montantMin);
|
||||
}
|
||||
if (montantMax != null && montant > montantMax) {
|
||||
return String.format("Le montant maximum est de %.0f FCFA", montantMax);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de compte Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutCompteWave {
|
||||
NON_VERIFIE("Non Vérifié"),
|
||||
VERIFIE("Vérifié"),
|
||||
SUSPENDU("Suspendu"),
|
||||
BLOQUE("Bloqué");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutCompteWave(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de transaction Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutTransactionWave {
|
||||
INITIALISE("Initialisé"),
|
||||
EN_ATTENTE("En Attente"),
|
||||
EN_COURS("En Cours"),
|
||||
REUSSIE("Réussie"),
|
||||
ECHOUE("Échoué"),
|
||||
ANNULEE("Annulée"),
|
||||
EXPIRED("Expiré");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutTransactionWave(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
|
||||
/** Vérifie si la transaction est finalisée */
|
||||
public boolean isFinalise() {
|
||||
return this == REUSSIE || this == ECHOUE || this == ANNULEE || this == EXPIRED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des statuts de traitement de webhook Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum StatutWebhook {
|
||||
EN_ATTENTE("En Attente"),
|
||||
EN_TRAITEMENT("En Traitement"),
|
||||
TRAITE("Traité"),
|
||||
ECHOUE("Échoué"),
|
||||
IGNORE("Ignoré");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
StatutWebhook(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des types d'événements webhook Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeEvenementWebhook {
|
||||
TRANSACTION_CREATED("Transaction Créée"),
|
||||
TRANSACTION_COMPLETED("Transaction Complétée"),
|
||||
TRANSACTION_FAILED("Transaction Échouée"),
|
||||
TRANSACTION_CANCELLED("Transaction Annulée"),
|
||||
ACCOUNT_VERIFIED("Compte Vérifié"),
|
||||
ACCOUNT_SUSPENDED("Compte Suspendu"),
|
||||
OTHER("Autre");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeEvenementWebhook(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.lions.unionflow.server.api.enums.wave;
|
||||
|
||||
/**
|
||||
* Énumération des types de transaction Wave
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
public enum TypeTransactionWave {
|
||||
DEPOT("Dépôt"),
|
||||
RETRAIT("Retrait"),
|
||||
TRANSFERT("Transfert"),
|
||||
PAIEMENT("Paiement"),
|
||||
REMBOURSEMENT("Remboursement");
|
||||
|
||||
private final String libelle;
|
||||
|
||||
TypeTransactionWave(String libelle) {
|
||||
this.libelle = libelle;
|
||||
}
|
||||
|
||||
public String getLibelle() {
|
||||
return libelle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package dev.lions.unionflow.server.api.service.dashboard;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.DashboardDataDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.DashboardStatsDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.RecentActivityDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.UpcomingEventDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface de service pour la gestion des données du dashboard
|
||||
*
|
||||
* <p>Cette interface définit le contrat pour récupérer les données du dashboard,
|
||||
* incluant les statistiques, activités récentes et événements à venir.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
public interface DashboardService {
|
||||
|
||||
/**
|
||||
* Récupère toutes les données du dashboard pour une organisation et un utilisateur
|
||||
*
|
||||
* @param organizationId L'identifiant de l'organisation
|
||||
* @param userId L'identifiant de l'utilisateur
|
||||
* @return Les données complètes du dashboard
|
||||
*/
|
||||
DashboardDataDTO getDashboardData(String organizationId, String userId);
|
||||
|
||||
/**
|
||||
* Récupère uniquement les statistiques du dashboard
|
||||
*
|
||||
* @param organizationId L'identifiant de l'organisation
|
||||
* @param userId L'identifiant de l'utilisateur
|
||||
* @return Les statistiques du dashboard
|
||||
*/
|
||||
DashboardStatsDTO getDashboardStats(String organizationId, String userId);
|
||||
|
||||
/**
|
||||
* Récupère les activités récentes
|
||||
*
|
||||
* @param organizationId L'identifiant de l'organisation
|
||||
* @param userId L'identifiant de l'utilisateur
|
||||
* @param limit Le nombre maximum d'activités à retourner
|
||||
* @return La liste des activités récentes
|
||||
*/
|
||||
List<RecentActivityDTO> getRecentActivities(String organizationId, String userId, int limit);
|
||||
|
||||
/**
|
||||
* Récupère les événements à venir
|
||||
*
|
||||
* @param organizationId L'identifiant de l'organisation
|
||||
* @param userId L'identifiant de l'utilisateur
|
||||
* @param limit Le nombre maximum d'événements à retourner
|
||||
* @return La liste des événements à venir
|
||||
*/
|
||||
List<UpcomingEventDTO> getUpcomingEvents(String organizationId, String userId, int limit);
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package dev.lions.unionflow.server.api.validation;
|
||||
|
||||
/**
|
||||
* Constantes pour la validation des DTOs
|
||||
*
|
||||
* <p>Cette classe centralise toutes les contraintes de validation pour assurer la cohérence entre
|
||||
* les différents DTOs du système.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
public final class ValidationConstants {
|
||||
|
||||
private ValidationConstants() {
|
||||
// Classe utilitaire - constructeur privé
|
||||
}
|
||||
|
||||
// === CONTRAINTES DE TAILLE POUR LES TEXTES ===
|
||||
|
||||
/** Titre court (événements, aides, etc.) */
|
||||
public static final int TITRE_MIN_LENGTH = 5;
|
||||
|
||||
public static final int TITRE_MAX_LENGTH = 100;
|
||||
public static final String TITRE_SIZE_MESSAGE =
|
||||
"Le titre doit contenir entre "
|
||||
+ TITRE_MIN_LENGTH
|
||||
+ " et "
|
||||
+ TITRE_MAX_LENGTH
|
||||
+ " caractères";
|
||||
|
||||
/** Nom d'organisation */
|
||||
public static final int NOM_ORGANISATION_MIN_LENGTH = 2;
|
||||
|
||||
public static final int NOM_ORGANISATION_MAX_LENGTH = 200;
|
||||
public static final String NOM_ORGANISATION_SIZE_MESSAGE =
|
||||
"Le nom doit contenir entre "
|
||||
+ NOM_ORGANISATION_MIN_LENGTH
|
||||
+ " et "
|
||||
+ NOM_ORGANISATION_MAX_LENGTH
|
||||
+ " caractères";
|
||||
|
||||
/** Description standard */
|
||||
public static final int DESCRIPTION_MIN_LENGTH = 20;
|
||||
|
||||
public static final int DESCRIPTION_MAX_LENGTH = 2000;
|
||||
public static final String DESCRIPTION_SIZE_MESSAGE =
|
||||
"La description doit contenir entre "
|
||||
+ DESCRIPTION_MIN_LENGTH
|
||||
+ " et "
|
||||
+ DESCRIPTION_MAX_LENGTH
|
||||
+ " caractères";
|
||||
|
||||
/** Description courte (événements) */
|
||||
public static final int DESCRIPTION_COURTE_MAX_LENGTH = 1000;
|
||||
|
||||
public static final String DESCRIPTION_COURTE_SIZE_MESSAGE =
|
||||
"La description ne peut pas dépasser " + DESCRIPTION_COURTE_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Justification */
|
||||
public static final int JUSTIFICATION_MAX_LENGTH = 1000;
|
||||
|
||||
public static final String JUSTIFICATION_SIZE_MESSAGE =
|
||||
"La justification ne peut pas dépasser " + JUSTIFICATION_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Commentaires */
|
||||
public static final int COMMENTAIRES_MAX_LENGTH = 1000;
|
||||
|
||||
public static final String COMMENTAIRES_SIZE_MESSAGE =
|
||||
"Les commentaires ne peuvent pas dépasser " + COMMENTAIRES_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Raison de rejet */
|
||||
public static final int RAISON_REJET_MAX_LENGTH = 500;
|
||||
|
||||
public static final String RAISON_REJET_SIZE_MESSAGE =
|
||||
"La raison du rejet ne peut pas dépasser " + RAISON_REJET_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Adresse */
|
||||
public static final int ADRESSE_MAX_LENGTH = 200;
|
||||
|
||||
public static final String ADRESSE_SIZE_MESSAGE =
|
||||
"L'adresse ne peut pas dépasser " + ADRESSE_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Ville, région, quartier */
|
||||
public static final int LOCALISATION_MAX_LENGTH = 50;
|
||||
|
||||
public static final String VILLE_SIZE_MESSAGE =
|
||||
"La ville ne peut pas dépasser " + LOCALISATION_MAX_LENGTH + " caractères";
|
||||
public static final String REGION_SIZE_MESSAGE =
|
||||
"La région ne peut pas dépasser " + LOCALISATION_MAX_LENGTH + " caractères";
|
||||
public static final String QUARTIER_SIZE_MESSAGE =
|
||||
"Le quartier ne peut pas dépasser " + LOCALISATION_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Rôle */
|
||||
public static final int ROLE_MAX_LENGTH = 50;
|
||||
|
||||
public static final String ROLE_SIZE_MESSAGE =
|
||||
"Le rôle ne peut pas dépasser " + ROLE_MAX_LENGTH + " caractères";
|
||||
|
||||
/** URL */
|
||||
public static final int URL_MAX_LENGTH = 255;
|
||||
|
||||
public static final String URL_SIZE_MESSAGE =
|
||||
"L'URL ne peut pas dépasser " + URL_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Email */
|
||||
public static final int EMAIL_MAX_LENGTH = 100;
|
||||
|
||||
public static final String EMAIL_SIZE_MESSAGE =
|
||||
"L'email ne peut pas dépasser " + EMAIL_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Nom et prénom */
|
||||
public static final int NOM_PRENOM_MIN_LENGTH = 2;
|
||||
|
||||
public static final int NOM_PRENOM_MAX_LENGTH = 50;
|
||||
public static final String NOM_SIZE_MESSAGE =
|
||||
"Le nom doit contenir entre "
|
||||
+ NOM_PRENOM_MIN_LENGTH
|
||||
+ " et "
|
||||
+ NOM_PRENOM_MAX_LENGTH
|
||||
+ " caractères";
|
||||
public static final String PRENOM_SIZE_MESSAGE =
|
||||
"Le prénom doit contenir entre "
|
||||
+ NOM_PRENOM_MIN_LENGTH
|
||||
+ " et "
|
||||
+ NOM_PRENOM_MAX_LENGTH
|
||||
+ " caractères";
|
||||
|
||||
// === PATTERNS DE VALIDATION ===
|
||||
|
||||
/** Numéro de téléphone international */
|
||||
public static final String TELEPHONE_PATTERN = "^\\+?[0-9]{8,15}$";
|
||||
|
||||
public static final String TELEPHONE_MESSAGE =
|
||||
"Le numéro de téléphone doit contenir entre 8 et 15 chiffres, avec un indicatif optionnel"
|
||||
+ " (+)";
|
||||
|
||||
/** Code devise ISO */
|
||||
public static final String DEVISE_PATTERN = "^[A-Z]{3}$";
|
||||
|
||||
public static final String DEVISE_MESSAGE =
|
||||
"La devise doit être un code ISO à 3 lettres majuscules";
|
||||
|
||||
/** Numéro de référence aide */
|
||||
public static final String REFERENCE_AIDE_PATTERN = "^DA-\\d{4}-\\d{6}$";
|
||||
|
||||
public static final String REFERENCE_AIDE_MESSAGE =
|
||||
"Le numéro de référence doit suivre le format DA-YYYY-NNNNNN";
|
||||
|
||||
/** Numéro de membre */
|
||||
public static final String NUMERO_MEMBRE_PATTERN = "^UF-\\d{4}-[A-Z0-9]{8}$";
|
||||
|
||||
public static final String NUMERO_MEMBRE_MESSAGE =
|
||||
"Format de numéro de membre invalide (UF-YYYY-XXXXXXXX)";
|
||||
|
||||
/** Couleur hexadécimale */
|
||||
public static final String COULEUR_HEX_PATTERN = "^#[0-9A-Fa-f]{6}$";
|
||||
|
||||
public static final String COULEUR_HEX_MESSAGE = "Format de couleur invalide (format: #RRGGBB)";
|
||||
|
||||
// === CONTRAINTES NUMÉRIQUES ===
|
||||
|
||||
/** Montant minimum */
|
||||
public static final String MONTANT_MIN_VALUE = "0.0";
|
||||
|
||||
public static final String MONTANT_POSITIF_MESSAGE = "Le montant doit être positif";
|
||||
|
||||
/** Contraintes pour les montants */
|
||||
public static final int MONTANT_INTEGER_DIGITS = 10;
|
||||
|
||||
public static final int MONTANT_FRACTION_DIGITS = 2;
|
||||
public static final String MONTANT_DIGITS_MESSAGE =
|
||||
"Le montant doit avoir au maximum "
|
||||
+ MONTANT_INTEGER_DIGITS
|
||||
+ " chiffres entiers et "
|
||||
+ MONTANT_FRACTION_DIGITS
|
||||
+ " décimales";
|
||||
|
||||
// === MESSAGES D'ERREUR STANDARD ===
|
||||
|
||||
public static final String OBLIGATOIRE_MESSAGE = " est obligatoire";
|
||||
public static final String EMAIL_FORMAT_MESSAGE = "L'adresse email n'est pas valide";
|
||||
public static final String DATE_PASSEE_MESSAGE = "La date doit être dans le passé";
|
||||
public static final String DATE_FUTURE_MESSAGE = "La date doit être dans le futur";
|
||||
|
||||
// === CONTRAINTES SPÉCIFIQUES ===
|
||||
|
||||
/** Documents joints */
|
||||
public static final int DOCUMENTS_JOINTS_MAX_LENGTH = 1000;
|
||||
|
||||
public static final String DOCUMENTS_JOINTS_SIZE_MESSAGE =
|
||||
"La liste des documents ne peut pas dépasser " + DOCUMENTS_JOINTS_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Mode de versement */
|
||||
public static final int MODE_VERSEMENT_MAX_LENGTH = 50;
|
||||
|
||||
public static final String MODE_VERSEMENT_SIZE_MESSAGE =
|
||||
"Le mode de versement ne peut pas dépasser " + MODE_VERSEMENT_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Numéro de transaction */
|
||||
public static final int NUMERO_TRANSACTION_MAX_LENGTH = 100;
|
||||
|
||||
public static final String NUMERO_TRANSACTION_SIZE_MESSAGE =
|
||||
"Le numéro de transaction ne peut pas dépasser "
|
||||
+ NUMERO_TRANSACTION_MAX_LENGTH
|
||||
+ " caractères";
|
||||
|
||||
/** Numéro d'enregistrement */
|
||||
public static final int NUMERO_ENREGISTREMENT_MAX_LENGTH = 100;
|
||||
|
||||
public static final String NUMERO_ENREGISTREMENT_SIZE_MESSAGE =
|
||||
"Le numéro d'enregistrement ne peut pas dépasser "
|
||||
+ NUMERO_ENREGISTREMENT_MAX_LENGTH
|
||||
+ " caractères";
|
||||
|
||||
/** Nom court d'organisation */
|
||||
public static final int NOM_COURT_MAX_LENGTH = 50;
|
||||
|
||||
public static final String NOM_COURT_SIZE_MESSAGE =
|
||||
"Le nom court ne peut pas dépasser " + NOM_COURT_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Instructions et matériel */
|
||||
public static final int INSTRUCTIONS_MAX_LENGTH = 500;
|
||||
|
||||
public static final String INSTRUCTIONS_SIZE_MESSAGE =
|
||||
"Les instructions ne peuvent pas dépasser " + INSTRUCTIONS_MAX_LENGTH + " caractères";
|
||||
|
||||
/** Conditions météo */
|
||||
public static final int CONDITIONS_METEO_MAX_LENGTH = 100;
|
||||
|
||||
public static final String CONDITIONS_METEO_SIZE_MESSAGE =
|
||||
"Les conditions météo ne peuvent pas dépasser " + CONDITIONS_METEO_MAX_LENGTH + " caractères";
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package dev.lions.unionflow.server.api;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.evenement.EvenementDTO;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.DemandeAideDTO;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.PropositionAideDTO;
|
||||
|
||||
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 java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test de compilation pour vérifier que tous les DTOs compilent correctement
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 2.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@DisplayName("Tests de Compilation")
|
||||
class CompilationTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test compilation EvenementDTO")
|
||||
void testCompilationEvenementDTO() {
|
||||
EvenementDTO evenement = new EvenementDTO();
|
||||
evenement.setTitre("Test Formation");
|
||||
evenement.setStatut(StatutEvenement.PLANIFIE);
|
||||
evenement.setPriorite(PrioriteEvenement.NORMALE);
|
||||
evenement.setTypeEvenement(TypeEvenementMetier.FORMATION);
|
||||
evenement.setDateDebut(LocalDate.now().plusDays(30));
|
||||
|
||||
// Test des méthodes métier
|
||||
assertThat(evenement.estEnCours()).isFalse();
|
||||
assertThat(evenement.getStatutLibelle()).isEqualTo("Planifié");
|
||||
assertThat(evenement.getTypeEvenementLibelle()).isEqualTo("Formation");
|
||||
|
||||
// Test des setters
|
||||
evenement.setStatut(StatutEvenement.CONFIRME);
|
||||
assertThat(evenement.getStatut()).isEqualTo(StatutEvenement.CONFIRME);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test compilation DemandeAideDTO")
|
||||
void testCompilationDemandeAideDTO() {
|
||||
DemandeAideDTO demande = new DemandeAideDTO();
|
||||
demande.setTitre("Test Demande");
|
||||
demande.setMontantDemande(new BigDecimal("50000"));
|
||||
demande.setDevise("XOF");
|
||||
|
||||
// Test des méthodes métier
|
||||
assertThat(demande.getId()).isNotNull(); // BaseDTO génère automatiquement un UUID
|
||||
assertThat(demande.getVersion()).isEqualTo(0L);
|
||||
|
||||
// Test de la méthode marquerCommeModifie
|
||||
demande.marquerCommeModifie("testUser");
|
||||
assertThat(demande.getModifiePar()).isEqualTo("testUser");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test compilation PropositionAideDTO")
|
||||
void testCompilationPropositionAideDTO() {
|
||||
PropositionAideDTO proposition = new PropositionAideDTO();
|
||||
proposition.setTitre("Test Proposition");
|
||||
proposition.setMontantMaximum(new BigDecimal("100000"));
|
||||
|
||||
// Vérifier que le type est correct
|
||||
assertThat(proposition.getMontantMaximum()).isInstanceOf(BigDecimal.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("Test compilation ValidationConstants")
|
||||
void testCompilationValidationConstants() {
|
||||
// Test que les constantes sont accessibles
|
||||
assertThat(ValidationConstants.TITRE_MIN_LENGTH).isEqualTo(5);
|
||||
assertThat(ValidationConstants.TITRE_MAX_LENGTH).isEqualTo(100);
|
||||
assertThat(ValidationConstants.TELEPHONE_PATTERN).isNotNull();
|
||||
assertThat(ValidationConstants.DEVISE_PATTERN).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test compilation énumérations")
|
||||
void testCompilationEnumerations() {
|
||||
// Test StatutEvenement
|
||||
StatutEvenement statut = StatutEvenement.PLANIFIE;
|
||||
assertThat(statut.getLibelle()).isEqualTo("Planifié");
|
||||
assertThat(statut.permetModification()).isTrue();
|
||||
|
||||
// Test PrioriteEvenement
|
||||
PrioriteEvenement priorite = PrioriteEvenement.HAUTE;
|
||||
assertThat(priorite.getLibelle()).isEqualTo("Haute");
|
||||
assertThat(priorite.isUrgente()).isTrue(); // Amélioration TDD : HAUTE est maintenant urgente
|
||||
|
||||
// Test TypeEvenementMetier
|
||||
TypeEvenementMetier type = TypeEvenementMetier.FORMATION;
|
||||
assertThat(type.getLibelle()).isEqualTo("Formation");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test intégration complète")
|
||||
void testIntegrationComplete() {
|
||||
// Créer un événement complet
|
||||
EvenementDTO evenement =
|
||||
new EvenementDTO(
|
||||
"Formation Leadership",
|
||||
TypeEvenementMetier.FORMATION,
|
||||
LocalDate.now().plusDays(30),
|
||||
"Centre de Formation");
|
||||
evenement.setStatut(StatutEvenement.PLANIFIE);
|
||||
evenement.setPriorite(PrioriteEvenement.HAUTE);
|
||||
evenement.setCapaciteMax(50);
|
||||
evenement.setParticipantsInscrits(0);
|
||||
evenement.setBudget(new BigDecimal("500000"));
|
||||
evenement.setCodeDevise("XOF");
|
||||
evenement.setAssociationId(UUID.randomUUID());
|
||||
|
||||
// Vérifier que tout fonctionne
|
||||
assertThat(evenement.estEnCours()).isFalse();
|
||||
assertThat(evenement.estComplet()).isFalse();
|
||||
assertThat(evenement.sontInscriptionsOuvertes()).isTrue();
|
||||
|
||||
// Créer une demande d'aide complète
|
||||
DemandeAideDTO demande = new DemandeAideDTO();
|
||||
demande.setTitre("Aide Médicale Urgente");
|
||||
demande.setDescription("Besoin d'aide pour frais médicaux");
|
||||
demande.setMontantDemande(new BigDecimal("250000"));
|
||||
demande.setDevise("XOF");
|
||||
demande.setMembreDemandeurId(UUID.randomUUID());
|
||||
demande.setAssociationId(UUID.randomUUID());
|
||||
|
||||
// Vérifier que tout fonctionne
|
||||
assertThat(demande.getId()).isNotNull();
|
||||
assertThat(demande.getVersion()).isEqualTo(0L);
|
||||
assertThat(demande.getMontantDemande()).isEqualTo(new BigDecimal("250000"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package dev.lions.unionflow.server.api;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.analytics.AnalyticsDataDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.DashboardDataDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.DashboardStatsDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.RecentActivityDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.UpcomingEventDTO;
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreDTO;
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria;
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO;
|
||||
import dev.lions.unionflow.server.api.dto.notification.NotificationDTO;
|
||||
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Factory pour créer des objets de test réutilisables.
|
||||
* Principe: Write Once, Use Everywhere (WOU) et DRY.
|
||||
*/
|
||||
public final class TestDataFactory {
|
||||
|
||||
private TestDataFactory() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
// ===== MEMBRE DTO =====
|
||||
|
||||
public static MembreDTO createMembreDTO() {
|
||||
return createMembreDTO("UF-2025-000001", "Dupont", "Jean", "jean@example.com");
|
||||
}
|
||||
|
||||
public static MembreDTO createMembreDTO(String numero, String nom, String prenom, String email) {
|
||||
MembreDTO membre = new MembreDTO(numero, nom, prenom, email);
|
||||
membre.setAssociationId(UUID.randomUUID());
|
||||
membre.setStatut(StatutMembre.ACTIF);
|
||||
return membre;
|
||||
}
|
||||
|
||||
public static MembreDTO createMembreDTOWithAge(int age) {
|
||||
MembreDTO membre = createMembreDTO();
|
||||
membre.setDateNaissance(LocalDate.now().minusYears(age));
|
||||
return membre;
|
||||
}
|
||||
|
||||
// ===== MEMBRE SEARCH CRITERIA =====
|
||||
|
||||
public static MembreSearchCriteria createMembreSearchCriteria() {
|
||||
return new MembreSearchCriteria();
|
||||
}
|
||||
|
||||
public static MembreSearchCriteria createMembreSearchCriteria(String query) {
|
||||
MembreSearchCriteria criteria = new MembreSearchCriteria();
|
||||
criteria.setQuery(query);
|
||||
return criteria;
|
||||
}
|
||||
|
||||
// ===== MEMBRE SEARCH RESULT =====
|
||||
|
||||
public static MembreSearchResultDTO createMembreSearchResultDTO() {
|
||||
MembreSearchResultDTO result = new MembreSearchResultDTO();
|
||||
result.setMembres(List.of());
|
||||
result.setTotalElements(0L);
|
||||
result.setTotalPages(0);
|
||||
result.setCurrentPage(0);
|
||||
result.setPageSize(20);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MembreSearchResultDTO createMembreSearchResultDTO(
|
||||
List<MembreDTO> membres, long totalElements, int totalPages, int currentPage) {
|
||||
MembreSearchResultDTO result = new MembreSearchResultDTO();
|
||||
result.setMembres(membres);
|
||||
result.setTotalElements(totalElements);
|
||||
result.setTotalPages(totalPages);
|
||||
result.setCurrentPage(currentPage);
|
||||
result.setPageSize(20);
|
||||
result.setNumberOfElements(membres != null ? membres.size() : 0);
|
||||
result.calculatePaginationFlags();
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===== DASHBOARD DTOs =====
|
||||
|
||||
public static DashboardStatsDTO createDashboardStatsDTO() {
|
||||
return DashboardStatsDTO.builder()
|
||||
.totalMembers(100)
|
||||
.activeMembers(80)
|
||||
.totalEvents(50)
|
||||
.upcomingEvents(10)
|
||||
.totalContributions(200)
|
||||
.totalContributionAmount(50000.0)
|
||||
.pendingRequests(5)
|
||||
.completedProjects(15)
|
||||
.monthlyGrowth(5.5)
|
||||
.engagementRate(0.75)
|
||||
.lastUpdated(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static RecentActivityDTO createRecentActivityDTO() {
|
||||
return createRecentActivityDTO("member", LocalDateTime.now().minusHours(1));
|
||||
}
|
||||
|
||||
public static RecentActivityDTO createRecentActivityDTO(String type, LocalDateTime timestamp) {
|
||||
return RecentActivityDTO.builder()
|
||||
.id("act-" + UUID.randomUUID().toString().substring(0, 8))
|
||||
.type(type)
|
||||
.title("Test Activity")
|
||||
.description("Test Description")
|
||||
.userName("Test User")
|
||||
.timestamp(timestamp)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static UpcomingEventDTO createUpcomingEventDTO() {
|
||||
return createUpcomingEventDTO(LocalDateTime.now().plusDays(1));
|
||||
}
|
||||
|
||||
public static UpcomingEventDTO createUpcomingEventDTO(LocalDateTime startDate) {
|
||||
return UpcomingEventDTO.builder()
|
||||
.id("event-" + UUID.randomUUID().toString().substring(0, 8))
|
||||
.title("Test Event")
|
||||
.description("Test Description")
|
||||
.startDate(startDate)
|
||||
.endDate(startDate != null ? startDate.plusHours(2) : null)
|
||||
.location("Test Location")
|
||||
.maxParticipants(100)
|
||||
.currentParticipants(50)
|
||||
.status("open")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static DashboardDataDTO createDashboardDataDTO() {
|
||||
return DashboardDataDTO.builder()
|
||||
.stats(createDashboardStatsDTO())
|
||||
.recentActivities(List.of(createRecentActivityDTO()))
|
||||
.upcomingEvents(List.of(createUpcomingEventDTO()))
|
||||
.userPreferences(Map.of("theme", "royal_teal", "language", "fr"))
|
||||
.organizationId("org-123")
|
||||
.userId("user-456")
|
||||
.build();
|
||||
}
|
||||
|
||||
// ===== HELPERS POUR DATES =====
|
||||
|
||||
public static LocalDateTime now() {
|
||||
return LocalDateTime.now();
|
||||
}
|
||||
|
||||
public static LocalDateTime hoursAgo(int hours) {
|
||||
return now().minusHours(hours);
|
||||
}
|
||||
|
||||
public static LocalDateTime daysAgo(int days) {
|
||||
return now().minusDays(days);
|
||||
}
|
||||
|
||||
public static LocalDateTime daysFromNow(int days) {
|
||||
return now().plusDays(days);
|
||||
}
|
||||
|
||||
public static LocalDate date(int year, int month, int day) {
|
||||
return LocalDate.of(year, month, day);
|
||||
}
|
||||
|
||||
// ===== NOTIFICATION DTO =====
|
||||
|
||||
public static NotificationDTO createNotificationDTO() {
|
||||
NotificationDTO notification = new NotificationDTO();
|
||||
notification.setId(UUID.randomUUID());
|
||||
notification.setDateCreation(now());
|
||||
return notification;
|
||||
}
|
||||
|
||||
// ===== ANALYTICS DATA DTO =====
|
||||
|
||||
public static AnalyticsDataDTO createAnalyticsDataDTO() {
|
||||
return AnalyticsDataDTO.builder()
|
||||
.dateCalcul(now())
|
||||
.dateDebut(now().minusDays(30))
|
||||
.dateFin(now())
|
||||
.build();
|
||||
}
|
||||
|
||||
// ===== SOLIDARITE DTOs =====
|
||||
|
||||
public static dev.lions.unionflow.server.api.dto.solidarite.DemandeAideDTO createDemandeAideDTO() {
|
||||
dev.lions.unionflow.server.api.dto.solidarite.DemandeAideDTO dto =
|
||||
new dev.lions.unionflow.server.api.dto.solidarite.DemandeAideDTO();
|
||||
dto.setNumeroReference("DA-2025-000001");
|
||||
dto.setTypeAide(dev.lions.unionflow.server.api.enums.solidarite.TypeAide.AIDE_FINANCIERE_URGENTE);
|
||||
dto.setTitre("Aide pour frais médicaux");
|
||||
dto.setDescription("Demande d'aide pour couvrir les frais d'hospitalisation");
|
||||
dto.setMembreDemandeurId(UUID.randomUUID());
|
||||
dto.setNomDemandeur("Jean Dupont");
|
||||
dto.setAssociationId(UUID.randomUUID());
|
||||
dto.setStatut(dev.lions.unionflow.server.api.enums.solidarite.StatutAide.EN_ATTENTE);
|
||||
dto.setPriorite(dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide.NORMALE);
|
||||
return dto;
|
||||
}
|
||||
|
||||
public static dev.lions.unionflow.server.api.dto.solidarite.PropositionAideDTO
|
||||
createPropositionAideDTO() {
|
||||
return dev.lions.unionflow.server.api.dto.solidarite.PropositionAideDTO.builder()
|
||||
.id("prop-" + UUID.randomUUID().toString())
|
||||
.numeroReference("PA-2025-000001")
|
||||
.typeAide(dev.lions.unionflow.server.api.enums.solidarite.TypeAide.AIDE_FINANCIERE_URGENTE)
|
||||
.titre("Proposition d'aide financière")
|
||||
.description("Je propose une aide financière pour les membres en difficulté")
|
||||
.proposantId("membre-" + UUID.randomUUID().toString())
|
||||
.proposantNom("Marie Martin")
|
||||
.organisationId("org-" + UUID.randomUUID().toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static dev.lions.unionflow.server.api.dto.solidarite.EvaluationAideDTO
|
||||
createEvaluationAideDTO() {
|
||||
return dev.lions.unionflow.server.api.dto.solidarite.EvaluationAideDTO.builder()
|
||||
.id("eval-" + UUID.randomUUID().toString())
|
||||
.demandeAideId("demande-" + UUID.randomUUID().toString())
|
||||
.evaluateurId("eval-" + UUID.randomUUID().toString())
|
||||
.evaluateurNom("Évaluateur Test")
|
||||
.roleEvaluateur("beneficiaire")
|
||||
.noteGlobale(4.5)
|
||||
.commentairePrincipal("Très satisfait de l'aide reçue")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static dev.lions.unionflow.server.api.dto.solidarite.CommentaireAideDTO
|
||||
createCommentaireAideDTO() {
|
||||
return dev.lions.unionflow.server.api.dto.solidarite.CommentaireAideDTO.builder()
|
||||
.id("comment-" + UUID.randomUUID().toString())
|
||||
.auteurId("auteur-" + UUID.randomUUID().toString())
|
||||
.auteurNom("Auteur Test")
|
||||
.contenu("Ceci est un commentaire de test")
|
||||
.dateCreation(now())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
package dev.lions.unionflow.server.api.dto.abonnement;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests unitaires complets pour AbonnementDTO.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@DisplayName("Tests AbonnementDTO")
|
||||
class AbonnementDTOBasicTest {
|
||||
|
||||
private AbonnementDTO abonnement;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
abonnement = new AbonnementDTO();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
assertThat(abonnement).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de Construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut - Initialisation correcte")
|
||||
void testConstructeurParDefaut() {
|
||||
AbonnementDTO newAbonnement = new AbonnementDTO();
|
||||
|
||||
assertThat(newAbonnement.getId()).isNotNull();
|
||||
assertThat(newAbonnement.getDateCreation()).isNotNull();
|
||||
assertThat(newAbonnement.isActif()).isTrue();
|
||||
assertThat(newAbonnement.getVersion()).isEqualTo(0L);
|
||||
assertThat(newAbonnement.getStatut()).isEqualTo("EN_ATTENTE_PAIEMENT");
|
||||
assertThat(newAbonnement.getDevise()).isEqualTo("XOF");
|
||||
assertThat(newAbonnement.getRenouvellementAutomatique()).isTrue();
|
||||
assertThat(newAbonnement.getPeriodeEssaiUtilisee()).isFalse();
|
||||
assertThat(newAbonnement.getSupportTechnique()).isTrue();
|
||||
assertThat(newAbonnement.getFonctionnalitesAvancees()).isFalse();
|
||||
assertThat(newAbonnement.getApiAccess()).isFalse();
|
||||
assertThat(newAbonnement.getRapportsPersonnalises()).isFalse();
|
||||
assertThat(newAbonnement.getIntegrationsTierces()).isFalse();
|
||||
assertThat(newAbonnement.getConnexionsCeMois()).isEqualTo(0);
|
||||
assertThat(newAbonnement.getAlertesActivees()).isTrue();
|
||||
assertThat(newAbonnement.getNotificationsEmail()).isTrue();
|
||||
assertThat(newAbonnement.getNotificationsSMS()).isFalse();
|
||||
assertThat(newAbonnement.getNumeroReference()).isNotNull();
|
||||
assertThat(newAbonnement.getNumeroReference()).matches("^ABO-\\d{4}-\\d{8}$");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur avec paramètres - Initialisation correcte")
|
||||
void testConstructeurAvecParametres() {
|
||||
UUID organisationId = UUID.randomUUID();
|
||||
String nomOrganisation = "Lions Club Dakar";
|
||||
String typeFormule = "PREMIUM";
|
||||
|
||||
AbonnementDTO newAbonnement = new AbonnementDTO(organisationId, nomOrganisation, typeFormule);
|
||||
|
||||
assertThat(newAbonnement.getOrganisationId()).isEqualTo(organisationId);
|
||||
assertThat(newAbonnement.getNomOrganisation()).isEqualTo(nomOrganisation);
|
||||
assertThat(newAbonnement.getTypeFormule()).isEqualTo(typeFormule);
|
||||
assertThat(newAbonnement.getStatut()).isEqualTo("EN_ATTENTE_PAIEMENT");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Getters/Setters")
|
||||
class GettersSettersTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters - Partie 1")
|
||||
void testTousLesGettersSettersPart1() {
|
||||
// Données de test
|
||||
String numeroReference = "ABO-2025-12345678";
|
||||
UUID organisationId = UUID.randomUUID();
|
||||
String nomOrganisation = "Lions Club Test";
|
||||
UUID formulaireId = UUID.randomUUID();
|
||||
String codeFormule = "PREM001";
|
||||
String nomFormule = "Formule Premium";
|
||||
String typeFormule = "PREMIUM";
|
||||
String statut = "ACTIF";
|
||||
String typeAbonnement = "ANNUEL";
|
||||
LocalDate dateDebut = LocalDate.now();
|
||||
LocalDate dateFin = LocalDate.now().plusYears(1);
|
||||
LocalDate dateProchainePeriode = LocalDate.now().plusMonths(1);
|
||||
BigDecimal montant = new BigDecimal("500000.00");
|
||||
String devise = "XOF";
|
||||
BigDecimal remise = new BigDecimal("10.00");
|
||||
BigDecimal montantFinal = new BigDecimal("450000.00");
|
||||
|
||||
// Test des setters
|
||||
abonnement.setNumeroReference(numeroReference);
|
||||
abonnement.setOrganisationId(organisationId);
|
||||
abonnement.setNomOrganisation(nomOrganisation);
|
||||
abonnement.setFormulaireId(formulaireId);
|
||||
abonnement.setCodeFormule(codeFormule);
|
||||
abonnement.setNomFormule(nomFormule);
|
||||
abonnement.setTypeFormule(typeFormule);
|
||||
abonnement.setStatut(statut);
|
||||
abonnement.setTypeAbonnement(typeAbonnement);
|
||||
abonnement.setDateDebut(dateDebut);
|
||||
abonnement.setDateFin(dateFin);
|
||||
abonnement.setDateProchainePeriode(dateProchainePeriode);
|
||||
abonnement.setMontant(montant);
|
||||
abonnement.setDevise(devise);
|
||||
abonnement.setRemise(remise);
|
||||
abonnement.setMontantFinal(montantFinal);
|
||||
|
||||
// Test des getters
|
||||
assertThat(abonnement.getNumeroReference()).isEqualTo(numeroReference);
|
||||
assertThat(abonnement.getOrganisationId()).isEqualTo(organisationId);
|
||||
assertThat(abonnement.getNomOrganisation()).isEqualTo(nomOrganisation);
|
||||
assertThat(abonnement.getFormulaireId()).isEqualTo(formulaireId);
|
||||
assertThat(abonnement.getCodeFormule()).isEqualTo(codeFormule);
|
||||
assertThat(abonnement.getNomFormule()).isEqualTo(nomFormule);
|
||||
assertThat(abonnement.getTypeFormule()).isEqualTo(typeFormule);
|
||||
assertThat(abonnement.getStatut()).isEqualTo(statut);
|
||||
assertThat(abonnement.getTypeAbonnement()).isEqualTo(typeAbonnement);
|
||||
assertThat(abonnement.getDateDebut()).isEqualTo(dateDebut);
|
||||
assertThat(abonnement.getDateFin()).isEqualTo(dateFin);
|
||||
assertThat(abonnement.getDateProchainePeriode()).isEqualTo(dateProchainePeriode);
|
||||
assertThat(abonnement.getMontant()).isEqualTo(montant);
|
||||
assertThat(abonnement.getDevise()).isEqualTo(devise);
|
||||
assertThat(abonnement.getRemise()).isEqualTo(remise);
|
||||
assertThat(abonnement.getMontantFinal()).isEqualTo(montantFinal);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters - Partie 2")
|
||||
void testTousLesGettersSettersPart2() {
|
||||
// Données de test
|
||||
Boolean renouvellementAutomatique = false;
|
||||
Boolean periodeEssaiUtilisee = true;
|
||||
LocalDate dateFinEssai = LocalDate.now().plusDays(30);
|
||||
Integer maxMembres = 100;
|
||||
Integer nombreMembresActuels = 75;
|
||||
BigDecimal espaceStockageGB = new BigDecimal("50.0");
|
||||
BigDecimal espaceStockageUtilise = new BigDecimal("25.5");
|
||||
Boolean supportTechnique = true;
|
||||
String niveauSupport = "PREMIUM";
|
||||
Boolean fonctionnalitesAvancees = true;
|
||||
Boolean apiAccess = true;
|
||||
Boolean rapportsPersonnalises = true;
|
||||
Boolean integrationsTierces = true;
|
||||
LocalDateTime dateDerniereUtilisation = LocalDateTime.now();
|
||||
Integer connexionsCeMois = 150;
|
||||
|
||||
// Test des setters
|
||||
abonnement.setRenouvellementAutomatique(renouvellementAutomatique);
|
||||
abonnement.setPeriodeEssaiUtilisee(periodeEssaiUtilisee);
|
||||
abonnement.setDateFinEssai(dateFinEssai);
|
||||
abonnement.setMaxMembres(maxMembres);
|
||||
abonnement.setNombreMembresActuels(nombreMembresActuels);
|
||||
abonnement.setEspaceStockageGB(espaceStockageGB);
|
||||
abonnement.setEspaceStockageUtilise(espaceStockageUtilise);
|
||||
abonnement.setSupportTechnique(supportTechnique);
|
||||
abonnement.setNiveauSupport(niveauSupport);
|
||||
abonnement.setFonctionnalitesAvancees(fonctionnalitesAvancees);
|
||||
abonnement.setApiAccess(apiAccess);
|
||||
abonnement.setRapportsPersonnalises(rapportsPersonnalises);
|
||||
abonnement.setIntegrationsTierces(integrationsTierces);
|
||||
abonnement.setDateDerniereUtilisation(dateDerniereUtilisation);
|
||||
abonnement.setConnexionsCeMois(connexionsCeMois);
|
||||
|
||||
// Test des getters
|
||||
assertThat(abonnement.getRenouvellementAutomatique()).isEqualTo(renouvellementAutomatique);
|
||||
assertThat(abonnement.getPeriodeEssaiUtilisee()).isEqualTo(periodeEssaiUtilisee);
|
||||
assertThat(abonnement.getDateFinEssai()).isEqualTo(dateFinEssai);
|
||||
assertThat(abonnement.getMaxMembres()).isEqualTo(maxMembres);
|
||||
assertThat(abonnement.getNombreMembresActuels()).isEqualTo(nombreMembresActuels);
|
||||
assertThat(abonnement.getEspaceStockageGB()).isEqualTo(espaceStockageGB);
|
||||
assertThat(abonnement.getEspaceStockageUtilise()).isEqualTo(espaceStockageUtilise);
|
||||
assertThat(abonnement.getSupportTechnique()).isEqualTo(supportTechnique);
|
||||
assertThat(abonnement.getNiveauSupport()).isEqualTo(niveauSupport);
|
||||
assertThat(abonnement.getFonctionnalitesAvancees()).isEqualTo(fonctionnalitesAvancees);
|
||||
assertThat(abonnement.getApiAccess()).isEqualTo(apiAccess);
|
||||
assertThat(abonnement.getRapportsPersonnalises()).isEqualTo(rapportsPersonnalises);
|
||||
assertThat(abonnement.getIntegrationsTierces()).isEqualTo(integrationsTierces);
|
||||
assertThat(abonnement.getDateDerniereUtilisation()).isEqualTo(dateDerniereUtilisation);
|
||||
assertThat(abonnement.getConnexionsCeMois()).isEqualTo(connexionsCeMois);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters - Partie 3")
|
||||
void testTousLesGettersSettersPart3() {
|
||||
// Données de test
|
||||
UUID responsableId = UUID.randomUUID();
|
||||
String nomResponsable = "Jean Dupont";
|
||||
String emailResponsable = "jean.dupont@example.com";
|
||||
String telephoneResponsable = "+221701234567";
|
||||
String modePaiementPrefere = "WAVE_MONEY";
|
||||
String numeroPaiementMobile = "+221701234567";
|
||||
String historiquePaiements = "{\"paiements\": []}";
|
||||
String notes = "Notes sur l'abonnement";
|
||||
Boolean alertesActivees = false;
|
||||
Boolean notificationsEmail = false;
|
||||
Boolean notificationsSMS = true;
|
||||
LocalDateTime dateSuspension = LocalDateTime.now();
|
||||
String raisonSuspension = "Non-paiement";
|
||||
LocalDateTime dateAnnulation = LocalDateTime.now();
|
||||
String raisonAnnulation = "Demande client";
|
||||
|
||||
// Test des setters
|
||||
abonnement.setResponsableId(responsableId);
|
||||
abonnement.setNomResponsable(nomResponsable);
|
||||
abonnement.setEmailResponsable(emailResponsable);
|
||||
abonnement.setTelephoneResponsable(telephoneResponsable);
|
||||
abonnement.setModePaiementPrefere(modePaiementPrefere);
|
||||
abonnement.setNumeroPaiementMobile(numeroPaiementMobile);
|
||||
abonnement.setHistoriquePaiements(historiquePaiements);
|
||||
abonnement.setNotes(notes);
|
||||
abonnement.setAlertesActivees(alertesActivees);
|
||||
abonnement.setNotificationsEmail(notificationsEmail);
|
||||
abonnement.setNotificationsSMS(notificationsSMS);
|
||||
abonnement.setDateSuspension(dateSuspension);
|
||||
abonnement.setRaisonSuspension(raisonSuspension);
|
||||
abonnement.setDateAnnulation(dateAnnulation);
|
||||
abonnement.setRaisonAnnulation(raisonAnnulation);
|
||||
|
||||
// Test des getters
|
||||
assertThat(abonnement.getResponsableId()).isEqualTo(responsableId);
|
||||
assertThat(abonnement.getNomResponsable()).isEqualTo(nomResponsable);
|
||||
assertThat(abonnement.getEmailResponsable()).isEqualTo(emailResponsable);
|
||||
assertThat(abonnement.getTelephoneResponsable()).isEqualTo(telephoneResponsable);
|
||||
assertThat(abonnement.getModePaiementPrefere()).isEqualTo(modePaiementPrefere);
|
||||
assertThat(abonnement.getNumeroPaiementMobile()).isEqualTo(numeroPaiementMobile);
|
||||
assertThat(abonnement.getHistoriquePaiements()).isEqualTo(historiquePaiements);
|
||||
assertThat(abonnement.getNotes()).isEqualTo(notes);
|
||||
assertThat(abonnement.getAlertesActivees()).isEqualTo(alertesActivees);
|
||||
assertThat(abonnement.getNotificationsEmail()).isEqualTo(notificationsEmail);
|
||||
assertThat(abonnement.getNotificationsSMS()).isEqualTo(notificationsSMS);
|
||||
assertThat(abonnement.getDateSuspension()).isEqualTo(dateSuspension);
|
||||
assertThat(abonnement.getRaisonSuspension()).isEqualTo(raisonSuspension);
|
||||
assertThat(abonnement.getDateAnnulation()).isEqualTo(dateAnnulation);
|
||||
assertThat(abonnement.getRaisonAnnulation()).isEqualTo(raisonAnnulation);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test toString")
|
||||
void testToString() {
|
||||
abonnement.setNumeroReference("ABO-2025-12345678");
|
||||
abonnement.setNomOrganisation("Lions Club Test");
|
||||
abonnement.setStatut("ACTIF");
|
||||
|
||||
String result = abonnement.toString();
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).contains("AbonnementDTO");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
package dev.lions.unionflow.server.api.dto.analytics;
|
||||
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.createAnalyticsDataDTO;
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.now;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
|
||||
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
@DisplayName("Tests pour AnalyticsDataDTO")
|
||||
class AnalyticsDataDTOTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO();
|
||||
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
LocalDateTime dateCalcul = now();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.valeur(new BigDecimal("100.50"))
|
||||
.valeurPrecedente(new BigDecimal("90.25"))
|
||||
.pourcentageEvolution(new BigDecimal("11.36"))
|
||||
.dateDebut(now().minusDays(30))
|
||||
.dateFin(now())
|
||||
.dateCalcul(dateCalcul)
|
||||
.organisationId(orgId)
|
||||
.nomOrganisation("Test Org")
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getValeur()).isEqualTo(new BigDecimal("100.50"));
|
||||
assertThat(dto.getDateCalcul()).isEqualTo(dateCalcul);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur avec paramètres essentiels")
|
||||
void testConstructeurAvecParametres() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO(
|
||||
TypeMetrique.NOMBRE_MEMBRES_ACTIFS,
|
||||
PeriodeAnalyse.CE_MOIS,
|
||||
new BigDecimal("100.0"));
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getValeur()).isEqualTo(new BigDecimal("100.0"));
|
||||
assertThat(dto.getDateCalcul()).isNotNull();
|
||||
assertThat(dto.getTempsReel()).isFalse();
|
||||
assertThat(dto.getNecessiteMiseAJour()).isFalse();
|
||||
assertThat(dto.getNiveauPriorite()).isEqualTo(3);
|
||||
assertThat(dto.getIndicateurFiabilite()).isEqualTo(new BigDecimal("95.0"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getLibelleAffichage")
|
||||
class GetLibelleAffichageTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - avec libellé personnalisé")
|
||||
void testGetLibelleAffichagePersonnalise() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.libellePersonnalise("Membres Actifs")
|
||||
.build();
|
||||
|
||||
assertThat(dto.getLibelleAffichage()).isEqualTo("Membres Actifs");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - sans libellé personnalisé")
|
||||
void testGetLibelleAffichageParDefaut() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getLibelleAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - libellé personnalisé vide")
|
||||
void testGetLibelleAffichageVide() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.libellePersonnalise(" ")
|
||||
.build();
|
||||
|
||||
assertThat(dto.getLibelleAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - typeMetrique null")
|
||||
void testGetLibelleAffichageTypeMetriqueNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(null)
|
||||
.libellePersonnalise("Libellé personnalisé")
|
||||
.build();
|
||||
|
||||
// Si typeMetrique est null, getLibelle() lancerait NPE, mais testons le comportement
|
||||
// En fait, si libellePersonnalise n'est pas null/vide, il devrait être retourné
|
||||
assertThat(dto.getLibelleAffichage()).isEqualTo("Libellé personnalisé");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - typeMetrique null et libellé personnalisé null")
|
||||
void testGetLibelleAffichageTypeMetriqueNullEtLibelleNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(null)
|
||||
.libellePersonnalise(null)
|
||||
.build();
|
||||
|
||||
// Si typeMetrique est null et libellePersonnalise est null, cela devrait lancer NPE
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> dto.getLibelleAffichage())
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - typeMetrique null et libellePersonnalise vide")
|
||||
void testGetLibelleAffichageTypeMetriqueNullEtLibellePersonnaliseVide() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(null)
|
||||
.libellePersonnalise(" ")
|
||||
.build();
|
||||
// Si libellePersonnalise est vide (trim), on essaie d'appeler typeMetrique.getLibelle()
|
||||
// ce qui devrait lancer NPE
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> dto.getLibelleAffichage())
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - libellePersonnalise null et typeMetrique non null")
|
||||
void testGetLibelleAffichageLibelleNullEtTypeMetriqueNonNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.libellePersonnalise(null)
|
||||
.build();
|
||||
|
||||
// Si libellePersonnalise est null, on retourne typeMetrique.getLibelle()
|
||||
assertThat(dto.getLibelleAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleAffichage - libellePersonnalise vide et typeMetrique non null")
|
||||
void testGetLibelleAffichageLibelleVideEtTypeMetriqueNonNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.libellePersonnalise("")
|
||||
.build();
|
||||
|
||||
// Si libellePersonnalise est vide, on retourne typeMetrique.getLibelle()
|
||||
assertThat(dto.getLibelleAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests méthodes utilitaires TypeMetrique")
|
||||
class MethodesTypeMetriqueTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getUnite - retourne l'unité du type métrique")
|
||||
void testGetUnite() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getUnite()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getUnite());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getUnite - typeMetrique null")
|
||||
void testGetUniteTypeMetriqueNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(null)
|
||||
.build();
|
||||
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> dto.getUnite())
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIcone - retourne l'icône du type métrique")
|
||||
void testGetIcone() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getIcone()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getIcone());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIcone - typeMetrique null")
|
||||
void testGetIconeTypeMetriqueNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(null)
|
||||
.build();
|
||||
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> dto.getIcone())
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleur - retourne la couleur du type métrique")
|
||||
void testGetCouleur() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getCouleur()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getCouleur());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleur - typeMetrique null")
|
||||
void testGetCouleurTypeMetriqueNull() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(null)
|
||||
.build();
|
||||
|
||||
org.assertj.core.api.Assertions.assertThatThrownBy(() -> dto.getCouleur())
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests hasEvolutionPositive")
|
||||
class HasEvolutionPositiveTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("hasEvolutionPositive - évolution positive")
|
||||
void testHasEvolutionPositive() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(new BigDecimal("10.5"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.hasEvolutionPositive()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasEvolutionPositive - évolution négative")
|
||||
void testHasEvolutionPositiveNegative() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(new BigDecimal("-5.0"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.hasEvolutionPositive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasEvolutionPositive - null")
|
||||
void testHasEvolutionPositiveNull() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO();
|
||||
|
||||
assertThat(dto.hasEvolutionPositive()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests hasEvolutionNegative")
|
||||
class HasEvolutionNegativeTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("hasEvolutionNegative - évolution négative")
|
||||
void testHasEvolutionNegative() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(new BigDecimal("-10.5"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.hasEvolutionNegative()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasEvolutionNegative - évolution positive")
|
||||
void testHasEvolutionNegativePositive() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(new BigDecimal("5.0"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.hasEvolutionNegative()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests isStable")
|
||||
class IsStableTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("isStable - évolution nulle")
|
||||
void testIsStable() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(BigDecimal.ZERO)
|
||||
.build();
|
||||
|
||||
assertThat(dto.isStable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isStable - évolution non nulle")
|
||||
void testIsStableNonNulle() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(new BigDecimal("5.0"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.isStable()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getTendance")
|
||||
class GetTendanceTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"10.5, hausse",
|
||||
"-5.0, baisse",
|
||||
"0.0, stable"
|
||||
})
|
||||
@DisplayName("getTendance - toutes les tendances")
|
||||
void testGetTendance(String evolution, String expected) {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.pourcentageEvolution(new BigDecimal(evolution))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTendance()).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests isDonneesFiables")
|
||||
class IsDonneesFiablesTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"80.0, true",
|
||||
"95.0, true",
|
||||
"79.9, false",
|
||||
"50.0, false"
|
||||
})
|
||||
@DisplayName("isDonneesFiables - seuil 80%")
|
||||
void testIsDonneesFiables(String fiabilite, Boolean expected) {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.indicateurFiabilite(new BigDecimal(fiabilite))
|
||||
.build();
|
||||
|
||||
assertThat(dto.isDonneesFiables()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isDonneesFiables - null")
|
||||
void testIsDonneesFiablesNull() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO();
|
||||
|
||||
assertThat(dto.isDonneesFiables()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests isCritique")
|
||||
class IsCritiqueTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"4, true",
|
||||
"5, true",
|
||||
"3, false",
|
||||
"1, false"
|
||||
})
|
||||
@DisplayName("isCritique - priorité >= 4")
|
||||
void testIsCritique(Integer priorite, Boolean expected) {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.niveauPriorite(priorite)
|
||||
.build();
|
||||
|
||||
assertThat(dto.isCritique()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isCritique - null")
|
||||
void testIsCritiqueNull() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO();
|
||||
|
||||
assertThat(dto.isCritique()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Builder complet - tous les champs")
|
||||
class BuilderCompletTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - tous les champs")
|
||||
void testBuilderTousChamps() {
|
||||
UUID orgId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Map<String, Object> metadonnees = new HashMap<>();
|
||||
metadonnees.put("key", "value");
|
||||
List<String> tags = Arrays.asList("tag1", "tag2");
|
||||
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.valeur(new BigDecimal("100.50"))
|
||||
.valeurPrecedente(new BigDecimal("90.25"))
|
||||
.pourcentageEvolution(new BigDecimal("11.36"))
|
||||
.dateDebut(now.minusDays(30))
|
||||
.dateFin(now)
|
||||
.dateCalcul(now)
|
||||
.organisationId(orgId)
|
||||
.nomOrganisation("Test Org")
|
||||
.utilisateurId(userId)
|
||||
.nomUtilisateur("Test User")
|
||||
.libellePersonnalise("Libellé personnalisé")
|
||||
.description("Description test")
|
||||
.donneesDetaillees("{\"data\": \"test\"}")
|
||||
.configurationGraphique("{\"config\": \"test\"}")
|
||||
.metadonnees(metadonnees)
|
||||
.indicateurFiabilite(new BigDecimal("95.5"))
|
||||
.nombreElementsAnalyses(1000)
|
||||
.tempsCalculMs(150L)
|
||||
.tempsReel(true)
|
||||
.necessiteMiseAJour(true)
|
||||
.niveauPriorite(4)
|
||||
.tags(tags)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getValeur()).isEqualTo(new BigDecimal("100.50"));
|
||||
assertThat(dto.getValeurPrecedente()).isEqualTo(new BigDecimal("90.25"));
|
||||
assertThat(dto.getPourcentageEvolution()).isEqualTo(new BigDecimal("11.36"));
|
||||
assertThat(dto.getDateDebut()).isEqualTo(now.minusDays(30));
|
||||
assertThat(dto.getDateFin()).isEqualTo(now);
|
||||
assertThat(dto.getDateCalcul()).isEqualTo(now);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Test Org");
|
||||
assertThat(dto.getUtilisateurId()).isEqualTo(userId);
|
||||
assertThat(dto.getNomUtilisateur()).isEqualTo("Test User");
|
||||
assertThat(dto.getLibellePersonnalise()).isEqualTo("Libellé personnalisé");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description test");
|
||||
assertThat(dto.getDonneesDetaillees()).isEqualTo("{\"data\": \"test\"}");
|
||||
assertThat(dto.getConfigurationGraphique()).isEqualTo("{\"config\": \"test\"}");
|
||||
assertThat(dto.getMetadonnees()).isEqualTo(metadonnees);
|
||||
assertThat(dto.getIndicateurFiabilite()).isEqualTo(new BigDecimal("95.5"));
|
||||
assertThat(dto.getNombreElementsAnalyses()).isEqualTo(1000);
|
||||
assertThat(dto.getTempsCalculMs()).isEqualTo(150L);
|
||||
assertThat(dto.getTempsReel()).isTrue();
|
||||
assertThat(dto.getNecessiteMiseAJour()).isTrue();
|
||||
assertThat(dto.getNiveauPriorite()).isEqualTo(4);
|
||||
assertThat(dto.getTags()).isEqualTo(tags);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - valeurs par défaut")
|
||||
void testBuilderValeursParDefaut() {
|
||||
AnalyticsDataDTO dto = AnalyticsDataDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.valeur(new BigDecimal("100.0"))
|
||||
.dateDebut(LocalDateTime.now().minusDays(30))
|
||||
.dateFin(LocalDateTime.now())
|
||||
.dateCalcul(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTempsReel()).isFalse();
|
||||
assertThat(dto.getNecessiteMiseAJour()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getters/setters complets")
|
||||
class GettersSettersCompletsTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters")
|
||||
void testTousLesGettersSetters() {
|
||||
AnalyticsDataDTO dto = new AnalyticsDataDTO();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Map<String, Object> metadonnees = new HashMap<>();
|
||||
metadonnees.put("key", "value");
|
||||
List<String> tags = Arrays.asList("tag1", "tag2");
|
||||
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
dto.setPeriodeAnalyse(PeriodeAnalyse.CE_MOIS);
|
||||
dto.setValeur(new BigDecimal("100.50"));
|
||||
dto.setValeurPrecedente(new BigDecimal("90.25"));
|
||||
dto.setPourcentageEvolution(new BigDecimal("11.36"));
|
||||
dto.setDateDebut(now.minusDays(30));
|
||||
dto.setDateFin(now);
|
||||
dto.setDateCalcul(now);
|
||||
dto.setOrganisationId(orgId);
|
||||
dto.setNomOrganisation("Test Org");
|
||||
dto.setUtilisateurId(userId);
|
||||
dto.setNomUtilisateur("Test User");
|
||||
dto.setLibellePersonnalise("Libellé personnalisé");
|
||||
dto.setDescription("Description test");
|
||||
dto.setDonneesDetaillees("{\"data\": \"test\"}");
|
||||
dto.setConfigurationGraphique("{\"config\": \"test\"}");
|
||||
dto.setMetadonnees(metadonnees);
|
||||
dto.setIndicateurFiabilite(new BigDecimal("95.5"));
|
||||
dto.setNombreElementsAnalyses(1000);
|
||||
dto.setTempsCalculMs(150L);
|
||||
dto.setTempsReel(true);
|
||||
dto.setNecessiteMiseAJour(true);
|
||||
dto.setNiveauPriorite(4);
|
||||
dto.setTags(tags);
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getValeur()).isEqualTo(new BigDecimal("100.50"));
|
||||
assertThat(dto.getValeurPrecedente()).isEqualTo(new BigDecimal("90.25"));
|
||||
assertThat(dto.getPourcentageEvolution()).isEqualTo(new BigDecimal("11.36"));
|
||||
assertThat(dto.getDateDebut()).isEqualTo(now.minusDays(30));
|
||||
assertThat(dto.getDateFin()).isEqualTo(now);
|
||||
assertThat(dto.getDateCalcul()).isEqualTo(now);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Test Org");
|
||||
assertThat(dto.getUtilisateurId()).isEqualTo(userId);
|
||||
assertThat(dto.getNomUtilisateur()).isEqualTo("Test User");
|
||||
assertThat(dto.getLibellePersonnalise()).isEqualTo("Libellé personnalisé");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description test");
|
||||
assertThat(dto.getDonneesDetaillees()).isEqualTo("{\"data\": \"test\"}");
|
||||
assertThat(dto.getConfigurationGraphique()).isEqualTo("{\"config\": \"test\"}");
|
||||
assertThat(dto.getMetadonnees()).isEqualTo(metadonnees);
|
||||
assertThat(dto.getIndicateurFiabilite()).isEqualTo(new BigDecimal("95.5"));
|
||||
assertThat(dto.getNombreElementsAnalyses()).isEqualTo(1000);
|
||||
assertThat(dto.getTempsCalculMs()).isEqualTo(150L);
|
||||
assertThat(dto.getTempsReel()).isTrue();
|
||||
assertThat(dto.getNecessiteMiseAJour()).isTrue();
|
||||
assertThat(dto.getNiveauPriorite()).isEqualTo(4);
|
||||
assertThat(dto.getTags()).isEqualTo(tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,634 @@
|
||||
package dev.lions.unionflow.server.api.dto.analytics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
|
||||
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
@DisplayName("Tests pour DashboardWidgetDTO")
|
||||
class DashboardWidgetDTOTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
|
||||
assertThat(dto).isNotNull();
|
||||
assertThat(dto.getOrdreAffichage()).isEqualTo(0);
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getRedimensionnable()).isTrue();
|
||||
assertThat(dto.getDeplacable()).isTrue();
|
||||
assertThat(dto.getSupprimable()).isTrue();
|
||||
assertThat(dto.getMiseAJourAutomatique()).isTrue();
|
||||
assertThat(dto.getFrequenceMiseAJourSecondes()).isEqualTo(300);
|
||||
assertThat(dto.getAlerteActive()).isFalse();
|
||||
assertThat(dto.getNombreVues()).isEqualTo(0L);
|
||||
assertThat(dto.getNombreInteractions()).isEqualTo(0L);
|
||||
assertThat(dto.getTauxErreur()).isEqualTo(0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
UUID userId = UUID.randomUUID();
|
||||
DashboardWidgetDTO dto = DashboardWidgetDTO.builder()
|
||||
.titre("Widget Test")
|
||||
.typeWidget("kpi")
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.utilisateurProprietaireId(userId)
|
||||
.positionX(0)
|
||||
.positionY(0)
|
||||
.largeur(4)
|
||||
.hauteur(2)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTitre()).isEqualTo("Widget Test");
|
||||
assertThat(dto.getTypeWidget()).isEqualTo("kpi");
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getUtilisateurProprietaireId()).isEqualTo(userId);
|
||||
assertThat(dto.getPositionX()).isEqualTo(0);
|
||||
assertThat(dto.getPositionY()).isEqualTo(0);
|
||||
assertThat(dto.getLargeur()).isEqualTo(4);
|
||||
assertThat(dto.getHauteur()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests méthodes utilitaires")
|
||||
class MethodesUtilitairesTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleMetrique - avec typeMetrique")
|
||||
void testGetLibelleMetriqueAvecType() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getLibelleMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleMetrique - sans typeMetrique")
|
||||
void testGetLibelleMetriqueSansType() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setTypeMetrique(null);
|
||||
assertThat(dto.getLibelleMetrique()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getUnite - avec typeMetrique")
|
||||
void testGetUniteAvecType() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getUnite()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getUnite());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getUnite - sans typeMetrique")
|
||||
void testGetUniteSansType() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setTypeMetrique(null);
|
||||
assertThat(dto.getUnite()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIconeAffichage - avec icône personnalisée")
|
||||
void testGetIconeAffichagePersonnalisee() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setIcone("custom_icon");
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getIconeAffichage()).isEqualTo("custom_icon");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIconeAffichage - avec typeMetrique")
|
||||
void testGetIconeAffichageAvecType() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setIcone(null);
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getIconeAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getIcone());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIconeAffichage - sans icône ni type")
|
||||
void testGetIconeAffichageParDefaut() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setIcone(null);
|
||||
dto.setTypeMetrique(null);
|
||||
assertThat(dto.getIconeAffichage()).isEqualTo("dashboard");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIconeAffichage - icône vide (trim)")
|
||||
void testGetIconeAffichageIconeVide() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setIcone(" ");
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getIconeAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getIcone());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAffichage - avec couleur personnalisée")
|
||||
void testGetCouleurAffichagePersonnalisee() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setCouleurPrincipale("#FF0000");
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getCouleurAffichage()).isEqualTo("#FF0000");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAffichage - avec typeMetrique")
|
||||
void testGetCouleurAffichageAvecType() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setCouleurPrincipale(null);
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getCouleurAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getCouleur());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAffichage - sans couleur ni type")
|
||||
void testGetCouleurAffichageParDefaut() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setCouleurPrincipale(null);
|
||||
dto.setTypeMetrique(null);
|
||||
assertThat(dto.getCouleurAffichage()).isEqualTo("#757575");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleurAffichage - couleur vide (trim)")
|
||||
void testGetCouleurAffichageCouleurVide() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setCouleurPrincipale(" ");
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getCouleurAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getCouleur());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteMiseAJour - mise à jour automatique et prochaine mise à jour passée")
|
||||
void testNecessiteMiseAJourTrue() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setMiseAJourAutomatique(true);
|
||||
dto.setProchaineMiseAJour(LocalDateTime.now().minusMinutes(1));
|
||||
assertThat(dto.necessiteMiseAJour()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteMiseAJour - mise à jour automatique désactivée")
|
||||
void testNecessiteMiseAJourFalseDesactivee() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setMiseAJourAutomatique(false);
|
||||
dto.setProchaineMiseAJour(LocalDateTime.now().minusMinutes(1));
|
||||
assertThat(dto.necessiteMiseAJour()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteMiseAJour - prochaine mise à jour future")
|
||||
void testNecessiteMiseAJourFalseFuture() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setMiseAJourAutomatique(true);
|
||||
dto.setProchaineMiseAJour(LocalDateTime.now().plusMinutes(1));
|
||||
assertThat(dto.necessiteMiseAJour()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteMiseAJour - prochaine mise à jour null")
|
||||
void testNecessiteMiseAJourFalseNull() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setMiseAJourAutomatique(true);
|
||||
dto.setProchaineMiseAJour(null);
|
||||
assertThat(dto.necessiteMiseAJour()).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"chart, true",
|
||||
"table, true",
|
||||
"gauge, true",
|
||||
"kpi, false",
|
||||
"text, false",
|
||||
"progress, false"
|
||||
})
|
||||
@DisplayName("isInteractif - tous les types")
|
||||
void testIsInteractif(String typeWidget, boolean expected) {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setTypeWidget(typeWidget);
|
||||
assertThat(dto.isInteractif()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTempsReel - fréquence <= 60 secondes")
|
||||
void testIsTempsReelTrue() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setFrequenceMiseAJourSecondes(30);
|
||||
assertThat(dto.isTempsReel()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTempsReel - fréquence > 60 secondes")
|
||||
void testIsTempsReelFalse() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setFrequenceMiseAJourSecondes(300);
|
||||
assertThat(dto.isTempsReel()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTempsReel - fréquence null")
|
||||
void testIsTempsReelNull() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setFrequenceMiseAJourSecondes(null);
|
||||
assertThat(dto.isTempsReel()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getTailleWidget - calcul correct")
|
||||
void testGetTailleWidget() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setLargeur(4);
|
||||
dto.setHauteur(3);
|
||||
assertThat(dto.getTailleWidget()).isEqualTo(12);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isWidgetGrand - taille > 6")
|
||||
void testIsWidgetGrandTrue() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setLargeur(4);
|
||||
dto.setHauteur(2);
|
||||
assertThat(dto.isWidgetGrand()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isWidgetGrand - taille <= 6")
|
||||
void testIsWidgetGrandFalse() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setLargeur(2);
|
||||
dto.setHauteur(2);
|
||||
assertThat(dto.isWidgetGrand()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasErreursRecentes - erreur < 24h")
|
||||
void testHasErreursRecentesTrue() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(12));
|
||||
assertThat(dto.hasErreursRecentes()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasErreursRecentes - erreur > 24h")
|
||||
void testHasErreursRecentesFalse() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(25));
|
||||
assertThat(dto.hasErreursRecentes()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasErreursRecentes - date null")
|
||||
void testHasErreursRecentesNull() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(null);
|
||||
assertThat(dto.hasErreursRecentes()).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"true, true, 5.0, erreur",
|
||||
"false, false, 5.0, inactif",
|
||||
"false, true, 15.0, maintenance",
|
||||
"false, true, 5.0, actif"
|
||||
})
|
||||
@DisplayName("getStatutWidget - tous les cas")
|
||||
void testGetStatutWidget(boolean hasErreursRecentes, boolean visible, double tauxErreur,
|
||||
String expected) {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
if (hasErreursRecentes) {
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(12));
|
||||
} else {
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(25)); // Plus de 24h pour être sûr
|
||||
}
|
||||
dto.setVisible(visible);
|
||||
dto.setTauxErreur(tauxErreur);
|
||||
assertThat(dto.getStatutWidget()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getStatutWidget - tauxErreur exactement 10.0")
|
||||
void testGetStatutWidgetTauxErreurExactement10() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(25)); // Pas d'erreurs récentes
|
||||
dto.setVisible(true);
|
||||
dto.setTauxErreur(10.0);
|
||||
// tauxErreur > 10.0 est false, donc devrait retourner "actif"
|
||||
assertThat(dto.getStatutWidget()).isEqualTo("actif");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getStatutWidget - tauxErreur null (via setter)")
|
||||
void testGetStatutWidgetTauxErreurNull() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(25)); // Pas d'erreurs récentes
|
||||
dto.setVisible(true);
|
||||
dto.setTauxErreur(null);
|
||||
// Si tauxErreur est null, la condition tauxErreur > 10.0 est fausse, donc devrait retourner "actif"
|
||||
assertThat(dto.getStatutWidget()).isEqualTo("actif");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getStatutWidget - tauxErreur exactement 10.0 (limite)")
|
||||
void testGetStatutWidgetTauxErreurExactement10Limite() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(25)); // Pas d'erreurs récentes
|
||||
dto.setVisible(true);
|
||||
dto.setTauxErreur(10.0);
|
||||
// tauxErreur > 10.0 est false (10.0 n'est pas > 10.0), donc devrait retourner "actif"
|
||||
assertThat(dto.getStatutWidget()).isEqualTo("actif");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getStatutWidget - tauxErreur 10.1 (juste au-dessus)")
|
||||
void testGetStatutWidgetTauxErreurJusteAuDessus() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(25)); // Pas d'erreurs récentes
|
||||
dto.setVisible(true);
|
||||
dto.setTauxErreur(10.1);
|
||||
// tauxErreur > 10.0 est true, donc devrait retourner "maintenance"
|
||||
assertThat(dto.getStatutWidget()).isEqualTo("maintenance");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getters/setters")
|
||||
class GettersSettersTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters")
|
||||
void testTousLesGettersSetters() {
|
||||
DashboardWidgetDTO dto = new DashboardWidgetDTO();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
filtres.put("key", "value");
|
||||
Map<String, Object> alertes = new HashMap<>();
|
||||
alertes.put("alert", "config");
|
||||
Map<String, Object> metadonnees = new HashMap<>();
|
||||
metadonnees.put("meta", "data");
|
||||
|
||||
dto.setTitre("Titre Widget");
|
||||
dto.setDescription("Description");
|
||||
dto.setTypeWidget("kpi");
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
dto.setPeriodeAnalyse(PeriodeAnalyse.CE_MOIS);
|
||||
dto.setOrganisationId(orgId);
|
||||
dto.setNomOrganisation("Org Test");
|
||||
dto.setUtilisateurProprietaireId(userId);
|
||||
dto.setNomUtilisateurProprietaire("User Test");
|
||||
dto.setPositionX(1);
|
||||
dto.setPositionY(2);
|
||||
dto.setLargeur(4);
|
||||
dto.setHauteur(3);
|
||||
dto.setOrdreAffichage(1);
|
||||
dto.setConfigurationVisuelle("config");
|
||||
dto.setCouleurPrincipale("#FF0000");
|
||||
dto.setCouleurSecondaire("#00FF00");
|
||||
dto.setIcone("icon");
|
||||
dto.setVisible(false);
|
||||
dto.setRedimensionnable(false);
|
||||
dto.setDeplacable(false);
|
||||
dto.setSupprimable(false);
|
||||
dto.setMiseAJourAutomatique(false);
|
||||
dto.setFrequenceMiseAJourSecondes(60);
|
||||
dto.setDateDerniereMiseAJour(LocalDateTime.now());
|
||||
dto.setProchaineMiseAJour(LocalDateTime.now().plusHours(1));
|
||||
dto.setDonneesWidget("data");
|
||||
dto.setConfigurationFiltres(filtres);
|
||||
dto.setConfigurationAlertes(alertes);
|
||||
dto.setSeuilAlerteBas(10.0);
|
||||
dto.setSeuilAlerteHaut(90.0);
|
||||
dto.setAlerteActive(true);
|
||||
dto.setMessageAlerte("Alerte");
|
||||
dto.setTypeAlerte("warning");
|
||||
dto.setPermissions("read");
|
||||
dto.setRolesAutorises("ADMIN");
|
||||
dto.setTemplatePersonnalise("template");
|
||||
dto.setCssPersonnalise("css");
|
||||
dto.setJavascriptPersonnalise("js");
|
||||
dto.setMetadonnees(metadonnees);
|
||||
dto.setNombreVues(100L);
|
||||
dto.setNombreInteractions(50L);
|
||||
dto.setTempsMoyenSecondes(30);
|
||||
dto.setTauxErreur(5.0);
|
||||
dto.setDateDerniereErreur(LocalDateTime.now().minusHours(1));
|
||||
dto.setMessageDerniereErreur("Erreur");
|
||||
|
||||
assertThat(dto.getTitre()).isEqualTo("Titre Widget");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description");
|
||||
assertThat(dto.getTypeWidget()).isEqualTo("kpi");
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Org Test");
|
||||
assertThat(dto.getUtilisateurProprietaireId()).isEqualTo(userId);
|
||||
assertThat(dto.getNomUtilisateurProprietaire()).isEqualTo("User Test");
|
||||
assertThat(dto.getPositionX()).isEqualTo(1);
|
||||
assertThat(dto.getPositionY()).isEqualTo(2);
|
||||
assertThat(dto.getLargeur()).isEqualTo(4);
|
||||
assertThat(dto.getHauteur()).isEqualTo(3);
|
||||
assertThat(dto.getOrdreAffichage()).isEqualTo(1);
|
||||
assertThat(dto.getConfigurationVisuelle()).isEqualTo("config");
|
||||
assertThat(dto.getCouleurPrincipale()).isEqualTo("#FF0000");
|
||||
assertThat(dto.getCouleurSecondaire()).isEqualTo("#00FF00");
|
||||
assertThat(dto.getIcone()).isEqualTo("icon");
|
||||
assertThat(dto.getVisible()).isFalse();
|
||||
assertThat(dto.getRedimensionnable()).isFalse();
|
||||
assertThat(dto.getDeplacable()).isFalse();
|
||||
assertThat(dto.getSupprimable()).isFalse();
|
||||
assertThat(dto.getMiseAJourAutomatique()).isFalse();
|
||||
assertThat(dto.getFrequenceMiseAJourSecondes()).isEqualTo(60);
|
||||
assertThat(dto.getDateDerniereMiseAJour()).isNotNull();
|
||||
assertThat(dto.getProchaineMiseAJour()).isNotNull();
|
||||
assertThat(dto.getDonneesWidget()).isEqualTo("data");
|
||||
assertThat(dto.getConfigurationFiltres()).isEqualTo(filtres);
|
||||
assertThat(dto.getConfigurationAlertes()).isEqualTo(alertes);
|
||||
assertThat(dto.getSeuilAlerteBas()).isEqualTo(10.0);
|
||||
assertThat(dto.getSeuilAlerteHaut()).isEqualTo(90.0);
|
||||
assertThat(dto.getAlerteActive()).isTrue();
|
||||
assertThat(dto.getMessageAlerte()).isEqualTo("Alerte");
|
||||
assertThat(dto.getTypeAlerte()).isEqualTo("warning");
|
||||
assertThat(dto.getPermissions()).isEqualTo("read");
|
||||
assertThat(dto.getRolesAutorises()).isEqualTo("ADMIN");
|
||||
assertThat(dto.getTemplatePersonnalise()).isEqualTo("template");
|
||||
assertThat(dto.getCssPersonnalise()).isEqualTo("css");
|
||||
assertThat(dto.getJavascriptPersonnalise()).isEqualTo("js");
|
||||
assertThat(dto.getMetadonnees()).isEqualTo(metadonnees);
|
||||
assertThat(dto.getNombreVues()).isEqualTo(100L);
|
||||
assertThat(dto.getNombreInteractions()).isEqualTo(50L);
|
||||
assertThat(dto.getTempsMoyenSecondes()).isEqualTo(30);
|
||||
assertThat(dto.getTauxErreur()).isEqualTo(5.0);
|
||||
assertThat(dto.getDateDerniereErreur()).isNotNull();
|
||||
assertThat(dto.getMessageDerniereErreur()).isEqualTo("Erreur");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Builder complet - tous les champs")
|
||||
class BuilderCompletTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - tous les champs")
|
||||
void testBuilderTousChamps() {
|
||||
UUID orgId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
filtres.put("key", "value");
|
||||
Map<String, Object> alertes = new HashMap<>();
|
||||
alertes.put("alert", "config");
|
||||
Map<String, Object> metadonnees = new HashMap<>();
|
||||
metadonnees.put("meta", "data");
|
||||
|
||||
DashboardWidgetDTO dto = DashboardWidgetDTO.builder()
|
||||
.titre("Widget Test Complet")
|
||||
.description("Description complète")
|
||||
.typeWidget("kpi")
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.organisationId(orgId)
|
||||
.nomOrganisation("Org Test")
|
||||
.utilisateurProprietaireId(userId)
|
||||
.nomUtilisateurProprietaire("User Test")
|
||||
.positionX(1)
|
||||
.positionY(2)
|
||||
.largeur(4)
|
||||
.hauteur(3)
|
||||
.ordreAffichage(1)
|
||||
.configurationVisuelle("config visuelle")
|
||||
.couleurPrincipale("#FF0000")
|
||||
.couleurSecondaire("#00FF00")
|
||||
.icone("icon")
|
||||
.visible(true)
|
||||
.redimensionnable(true)
|
||||
.deplacable(true)
|
||||
.supprimable(true)
|
||||
.miseAJourAutomatique(true)
|
||||
.frequenceMiseAJourSecondes(60)
|
||||
.dateDerniereMiseAJour(now)
|
||||
.prochaineMiseAJour(now.plusHours(1))
|
||||
.donneesWidget("donnees")
|
||||
.configurationFiltres(filtres)
|
||||
.configurationAlertes(alertes)
|
||||
.seuilAlerteBas(10.0)
|
||||
.seuilAlerteHaut(90.0)
|
||||
.alerteActive(true)
|
||||
.messageAlerte("Alerte")
|
||||
.typeAlerte("warning")
|
||||
.permissions("read")
|
||||
.rolesAutorises("ADMIN")
|
||||
.templatePersonnalise("template")
|
||||
.cssPersonnalise("css")
|
||||
.javascriptPersonnalise("js")
|
||||
.metadonnees(metadonnees)
|
||||
.nombreVues(100L)
|
||||
.nombreInteractions(50L)
|
||||
.tempsMoyenSecondes(30)
|
||||
.tauxErreur(5.0)
|
||||
.dateDerniereErreur(now.minusHours(1))
|
||||
.messageDerniereErreur("Erreur")
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTitre()).isEqualTo("Widget Test Complet");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description complète");
|
||||
assertThat(dto.getTypeWidget()).isEqualTo("kpi");
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Org Test");
|
||||
assertThat(dto.getUtilisateurProprietaireId()).isEqualTo(userId);
|
||||
assertThat(dto.getNomUtilisateurProprietaire()).isEqualTo("User Test");
|
||||
assertThat(dto.getPositionX()).isEqualTo(1);
|
||||
assertThat(dto.getPositionY()).isEqualTo(2);
|
||||
assertThat(dto.getLargeur()).isEqualTo(4);
|
||||
assertThat(dto.getHauteur()).isEqualTo(3);
|
||||
assertThat(dto.getOrdreAffichage()).isEqualTo(1);
|
||||
assertThat(dto.getConfigurationVisuelle()).isEqualTo("config visuelle");
|
||||
assertThat(dto.getCouleurPrincipale()).isEqualTo("#FF0000");
|
||||
assertThat(dto.getCouleurSecondaire()).isEqualTo("#00FF00");
|
||||
assertThat(dto.getIcone()).isEqualTo("icon");
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getRedimensionnable()).isTrue();
|
||||
assertThat(dto.getDeplacable()).isTrue();
|
||||
assertThat(dto.getSupprimable()).isTrue();
|
||||
assertThat(dto.getMiseAJourAutomatique()).isTrue();
|
||||
assertThat(dto.getFrequenceMiseAJourSecondes()).isEqualTo(60);
|
||||
assertThat(dto.getDateDerniereMiseAJour()).isEqualTo(now);
|
||||
assertThat(dto.getProchaineMiseAJour()).isEqualTo(now.plusHours(1));
|
||||
assertThat(dto.getDonneesWidget()).isEqualTo("donnees");
|
||||
assertThat(dto.getConfigurationFiltres()).isEqualTo(filtres);
|
||||
assertThat(dto.getConfigurationAlertes()).isEqualTo(alertes);
|
||||
assertThat(dto.getSeuilAlerteBas()).isEqualTo(10.0);
|
||||
assertThat(dto.getSeuilAlerteHaut()).isEqualTo(90.0);
|
||||
assertThat(dto.getAlerteActive()).isTrue();
|
||||
assertThat(dto.getMessageAlerte()).isEqualTo("Alerte");
|
||||
assertThat(dto.getTypeAlerte()).isEqualTo("warning");
|
||||
assertThat(dto.getPermissions()).isEqualTo("read");
|
||||
assertThat(dto.getRolesAutorises()).isEqualTo("ADMIN");
|
||||
assertThat(dto.getTemplatePersonnalise()).isEqualTo("template");
|
||||
assertThat(dto.getCssPersonnalise()).isEqualTo("css");
|
||||
assertThat(dto.getJavascriptPersonnalise()).isEqualTo("js");
|
||||
assertThat(dto.getMetadonnees()).isEqualTo(metadonnees);
|
||||
assertThat(dto.getNombreVues()).isEqualTo(100L);
|
||||
assertThat(dto.getNombreInteractions()).isEqualTo(50L);
|
||||
assertThat(dto.getTempsMoyenSecondes()).isEqualTo(30);
|
||||
assertThat(dto.getTauxErreur()).isEqualTo(5.0);
|
||||
assertThat(dto.getDateDerniereErreur()).isEqualTo(now.minusHours(1));
|
||||
assertThat(dto.getMessageDerniereErreur()).isEqualTo("Erreur");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - valeurs par défaut")
|
||||
void testBuilderValeursParDefaut() {
|
||||
UUID userId = UUID.randomUUID();
|
||||
DashboardWidgetDTO dto = DashboardWidgetDTO.builder()
|
||||
.titre("Widget")
|
||||
.typeWidget("kpi")
|
||||
.utilisateurProprietaireId(userId)
|
||||
.positionX(0)
|
||||
.positionY(0)
|
||||
.largeur(4)
|
||||
.hauteur(2)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getOrdreAffichage()).isEqualTo(0);
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getRedimensionnable()).isTrue();
|
||||
assertThat(dto.getDeplacable()).isTrue();
|
||||
assertThat(dto.getSupprimable()).isTrue();
|
||||
assertThat(dto.getMiseAJourAutomatique()).isTrue();
|
||||
assertThat(dto.getFrequenceMiseAJourSecondes()).isEqualTo(300);
|
||||
assertThat(dto.getAlerteActive()).isFalse();
|
||||
assertThat(dto.getNombreVues()).isEqualTo(0L);
|
||||
assertThat(dto.getNombreInteractions()).isEqualTo(0L);
|
||||
assertThat(dto.getTauxErreur()).isEqualTo(0.0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
package dev.lions.unionflow.server.api.dto.analytics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.analytics.KPITrendDTO.PointDonneeDTO;
|
||||
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
|
||||
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
@DisplayName("Tests pour KPITrendDTO")
|
||||
class KPITrendDTOTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
assertThat(dto.getAlerteActive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
LocalDateTime debut = LocalDateTime.of(2025, 1, 1, 0, 0);
|
||||
LocalDateTime fin = LocalDateTime.of(2025, 1, 31, 23, 59);
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder()
|
||||
.date(debut)
|
||||
.valeur(new BigDecimal("100.0"))
|
||||
.build());
|
||||
|
||||
KPITrendDTO dto = KPITrendDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.dateDebut(debut)
|
||||
.dateFin(fin)
|
||||
.pointsDonnees(points)
|
||||
.valeurActuelle(new BigDecimal("150.0"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getDateDebut()).isEqualTo(debut);
|
||||
assertThat(dto.getDateFin()).isEqualTo(fin);
|
||||
assertThat(dto.getPointsDonnees()).isEqualTo(points);
|
||||
assertThat(dto.getValeurActuelle()).isEqualTo(new BigDecimal("150.0"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests méthodes utilitaires")
|
||||
class MethodesUtilitairesTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getLibelleMetrique")
|
||||
void testGetLibelleMetrique() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getLibelleMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getUnite")
|
||||
void testGetUnite() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getUnite()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getUnite());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIcone")
|
||||
void testGetIcone() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getIcone()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getIcone());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getCouleur")
|
||||
void testGetCouleur() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getCouleur()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getCouleur());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendancePositive - tendance positive")
|
||||
void testIsTendancePositiveTrue() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(new BigDecimal("5.5"));
|
||||
assertThat(dto.isTendancePositive()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendancePositive - tendance négative")
|
||||
void testIsTendancePositiveFalse() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(new BigDecimal("-5.5"));
|
||||
assertThat(dto.isTendancePositive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendancePositive - tendance null")
|
||||
void testIsTendancePositiveNull() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(null);
|
||||
assertThat(dto.isTendancePositive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendanceNegative - tendance négative")
|
||||
void testIsTendanceNegativeTrue() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(new BigDecimal("-5.5"));
|
||||
assertThat(dto.isTendanceNegative()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendanceNegative - tendance positive")
|
||||
void testIsTendanceNegativeFalse() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(new BigDecimal("5.5"));
|
||||
assertThat(dto.isTendanceNegative()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendanceNegative - tendance null")
|
||||
void testIsTendanceNegativeNull() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(null);
|
||||
assertThat(dto.isTendanceNegative()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendanceStable - tendance zéro")
|
||||
void testIsTendanceStableTrue() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(BigDecimal.ZERO);
|
||||
assertThat(dto.isTendanceStable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendanceStable - tendance non zéro")
|
||||
void testIsTendanceStableFalse() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(new BigDecimal("5.5"));
|
||||
assertThat(dto.isTendanceStable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isTendanceStable - tendance null")
|
||||
void testIsTendanceStableNull() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setTendanceGenerale(null);
|
||||
assertThat(dto.isTendanceStable()).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0.05, faible",
|
||||
"0.1, faible",
|
||||
"0.2, moyenne",
|
||||
"0.3, moyenne",
|
||||
"0.4, élevée",
|
||||
"null, inconnue"
|
||||
})
|
||||
@DisplayName("getVolatilite - tous les cas")
|
||||
void testGetVolatilite(String cvStr, String expected) {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
if ("null".equals(cvStr)) {
|
||||
dto.setCoefficientVariation(null);
|
||||
} else {
|
||||
dto.setCoefficientVariation(new BigDecimal(cvStr));
|
||||
}
|
||||
assertThat(dto.getVolatilite()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isPredictionFiable - R² > 0.7")
|
||||
void testIsPredictionFiableTrue() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setCoefficientCorrelation(new BigDecimal("0.8"));
|
||||
assertThat(dto.isPredictionFiable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isPredictionFiable - R² = 0.7")
|
||||
void testIsPredictionFiableTrueLimite() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setCoefficientCorrelation(new BigDecimal("0.7"));
|
||||
assertThat(dto.isPredictionFiable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isPredictionFiable - R² < 0.7")
|
||||
void testIsPredictionFiableFalse() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setCoefficientCorrelation(new BigDecimal("0.5"));
|
||||
assertThat(dto.isPredictionFiable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isPredictionFiable - R² null")
|
||||
void testIsPredictionFiableNull() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setCoefficientCorrelation(null);
|
||||
assertThat(dto.isPredictionFiable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNombrePointsDonnees - avec points")
|
||||
void testGetNombrePointsDonneesAvecPoints() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder().date(LocalDateTime.now()).valeur(new BigDecimal("100")).build(),
|
||||
PointDonneeDTO.builder().date(LocalDateTime.now()).valeur(new BigDecimal("200")).build());
|
||||
dto.setPointsDonnees(points);
|
||||
assertThat(dto.getNombrePointsDonnees()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNombrePointsDonnees - sans points")
|
||||
void testGetNombrePointsDonneesSansPoints() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setPointsDonnees(null);
|
||||
assertThat(dto.getNombrePointsDonnees()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAnomalies - avec anomalies")
|
||||
void testHasAnomaliesTrue() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder()
|
||||
.date(LocalDateTime.now())
|
||||
.valeur(new BigDecimal("100"))
|
||||
.anomalie(true)
|
||||
.build(),
|
||||
PointDonneeDTO.builder()
|
||||
.date(LocalDateTime.now())
|
||||
.valeur(new BigDecimal("200"))
|
||||
.anomalie(false)
|
||||
.build());
|
||||
dto.setPointsDonnees(points);
|
||||
assertThat(dto.hasAnomalies()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAnomalies - sans anomalies")
|
||||
void testHasAnomaliesFalse() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder()
|
||||
.date(LocalDateTime.now())
|
||||
.valeur(new BigDecimal("100"))
|
||||
.anomalie(false)
|
||||
.build());
|
||||
dto.setPointsDonnees(points);
|
||||
assertThat(dto.hasAnomalies()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAnomalies - points null")
|
||||
void testHasAnomaliesNull() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
dto.setPointsDonnees(null);
|
||||
assertThat(dto.hasAnomalies()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests classe interne PointDonneeDTO")
|
||||
class PointDonneeDTOTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
PointDonneeDTO point = new PointDonneeDTO();
|
||||
assertThat(point).isNotNull();
|
||||
assertThat(point.getAnomalie()).isFalse();
|
||||
assertThat(point.getPrediction()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
LocalDateTime date = LocalDateTime.of(2025, 1, 15, 10, 0);
|
||||
PointDonneeDTO point = PointDonneeDTO.builder()
|
||||
.date(date)
|
||||
.valeur(new BigDecimal("150.0"))
|
||||
.libelle("Point 1")
|
||||
.anomalie(true)
|
||||
.prediction(false)
|
||||
.metadonnees("meta")
|
||||
.build();
|
||||
|
||||
assertThat(point.getDate()).isEqualTo(date);
|
||||
assertThat(point.getValeur()).isEqualTo(new BigDecimal("150.0"));
|
||||
assertThat(point.getLibelle()).isEqualTo("Point 1");
|
||||
assertThat(point.getAnomalie()).isTrue();
|
||||
assertThat(point.getPrediction()).isFalse();
|
||||
assertThat(point.getMetadonnees()).isEqualTo("meta");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Getters/setters")
|
||||
void testGettersSetters() {
|
||||
PointDonneeDTO point = new PointDonneeDTO();
|
||||
LocalDateTime date = LocalDateTime.of(2025, 1, 15, 10, 0);
|
||||
|
||||
point.setDate(date);
|
||||
point.setValeur(new BigDecimal("200.0"));
|
||||
point.setLibelle("Test Point");
|
||||
point.setAnomalie(true);
|
||||
point.setPrediction(true);
|
||||
point.setMetadonnees("test meta");
|
||||
|
||||
assertThat(point.getDate()).isEqualTo(date);
|
||||
assertThat(point.getValeur()).isEqualTo(new BigDecimal("200.0"));
|
||||
assertThat(point.getLibelle()).isEqualTo("Test Point");
|
||||
assertThat(point.getAnomalie()).isTrue();
|
||||
assertThat(point.getPrediction()).isTrue();
|
||||
assertThat(point.getMetadonnees()).isEqualTo("test meta");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getters/setters complets")
|
||||
class GettersSettersCompletsTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters")
|
||||
void testTousLesGettersSetters() {
|
||||
KPITrendDTO dto = new KPITrendDTO();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
LocalDateTime debut = LocalDateTime.of(2025, 1, 1, 0, 0);
|
||||
LocalDateTime fin = LocalDateTime.of(2025, 1, 31, 23, 59);
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder()
|
||||
.date(debut)
|
||||
.valeur(new BigDecimal("100.0"))
|
||||
.build());
|
||||
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
dto.setPeriodeAnalyse(PeriodeAnalyse.CE_MOIS);
|
||||
dto.setOrganisationId(orgId);
|
||||
dto.setNomOrganisation("Org Test");
|
||||
dto.setDateDebut(debut);
|
||||
dto.setDateFin(fin);
|
||||
dto.setPointsDonnees(points);
|
||||
dto.setValeurActuelle(new BigDecimal("150.0"));
|
||||
dto.setValeurMinimale(new BigDecimal("50.0"));
|
||||
dto.setValeurMaximale(new BigDecimal("200.0"));
|
||||
dto.setValeurMoyenne(new BigDecimal("125.0"));
|
||||
dto.setEcartType(new BigDecimal("25.0"));
|
||||
dto.setCoefficientVariation(new BigDecimal("0.2"));
|
||||
dto.setTendanceGenerale(new BigDecimal("5.5"));
|
||||
dto.setCoefficientCorrelation(new BigDecimal("0.85"));
|
||||
dto.setPourcentageEvolutionGlobale(new BigDecimal("50.0"));
|
||||
dto.setPredictionProchainePeriode(new BigDecimal("160.0"));
|
||||
dto.setMargeErreurPrediction(new BigDecimal("5.0"));
|
||||
dto.setSeuilAlerteBas(new BigDecimal("80.0"));
|
||||
dto.setSeuilAlerteHaut(new BigDecimal("180.0"));
|
||||
dto.setAlerteActive(true);
|
||||
dto.setTypeAlerte("haut");
|
||||
dto.setMessageAlerte("Alerte haute");
|
||||
dto.setConfigurationGraphique("config");
|
||||
dto.setIntervalleRegroupement("jour");
|
||||
dto.setFormatDate("yyyy-MM-dd");
|
||||
dto.setDateDerniereMiseAJour(LocalDateTime.now());
|
||||
dto.setFrequenceMiseAJourMinutes(60);
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Org Test");
|
||||
assertThat(dto.getDateDebut()).isEqualTo(debut);
|
||||
assertThat(dto.getDateFin()).isEqualTo(fin);
|
||||
assertThat(dto.getPointsDonnees()).isEqualTo(points);
|
||||
assertThat(dto.getValeurActuelle()).isEqualTo(new BigDecimal("150.0"));
|
||||
assertThat(dto.getValeurMinimale()).isEqualTo(new BigDecimal("50.0"));
|
||||
assertThat(dto.getValeurMaximale()).isEqualTo(new BigDecimal("200.0"));
|
||||
assertThat(dto.getValeurMoyenne()).isEqualTo(new BigDecimal("125.0"));
|
||||
assertThat(dto.getEcartType()).isEqualTo(new BigDecimal("25.0"));
|
||||
assertThat(dto.getCoefficientVariation()).isEqualTo(new BigDecimal("0.2"));
|
||||
assertThat(dto.getTendanceGenerale()).isEqualTo(new BigDecimal("5.5"));
|
||||
assertThat(dto.getCoefficientCorrelation()).isEqualTo(new BigDecimal("0.85"));
|
||||
assertThat(dto.getPourcentageEvolutionGlobale()).isEqualTo(new BigDecimal("50.0"));
|
||||
assertThat(dto.getPredictionProchainePeriode()).isEqualTo(new BigDecimal("160.0"));
|
||||
assertThat(dto.getMargeErreurPrediction()).isEqualTo(new BigDecimal("5.0"));
|
||||
assertThat(dto.getSeuilAlerteBas()).isEqualTo(new BigDecimal("80.0"));
|
||||
assertThat(dto.getSeuilAlerteHaut()).isEqualTo(new BigDecimal("180.0"));
|
||||
assertThat(dto.getAlerteActive()).isTrue();
|
||||
assertThat(dto.getTypeAlerte()).isEqualTo("haut");
|
||||
assertThat(dto.getMessageAlerte()).isEqualTo("Alerte haute");
|
||||
assertThat(dto.getConfigurationGraphique()).isEqualTo("config");
|
||||
assertThat(dto.getIntervalleRegroupement()).isEqualTo("jour");
|
||||
assertThat(dto.getFormatDate()).isEqualTo("yyyy-MM-dd");
|
||||
assertThat(dto.getDateDerniereMiseAJour()).isNotNull();
|
||||
assertThat(dto.getFrequenceMiseAJourMinutes()).isEqualTo(60);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Builder complet - tous les champs")
|
||||
class BuilderCompletTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - tous les champs")
|
||||
void testBuilderTousChamps() {
|
||||
UUID orgId = UUID.randomUUID();
|
||||
LocalDateTime debut = LocalDateTime.of(2025, 1, 1, 0, 0);
|
||||
LocalDateTime fin = LocalDateTime.of(2025, 1, 31, 23, 59);
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder()
|
||||
.date(debut)
|
||||
.valeur(new BigDecimal("100.0"))
|
||||
.build());
|
||||
|
||||
KPITrendDTO dto = KPITrendDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.organisationId(orgId)
|
||||
.nomOrganisation("Org Test")
|
||||
.dateDebut(debut)
|
||||
.dateFin(fin)
|
||||
.pointsDonnees(points)
|
||||
.valeurActuelle(new BigDecimal("150.0"))
|
||||
.valeurMinimale(new BigDecimal("50.0"))
|
||||
.valeurMaximale(new BigDecimal("200.0"))
|
||||
.valeurMoyenne(new BigDecimal("125.0"))
|
||||
.ecartType(new BigDecimal("25.0"))
|
||||
.coefficientVariation(new BigDecimal("0.2"))
|
||||
.tendanceGenerale(new BigDecimal("5.5"))
|
||||
.coefficientCorrelation(new BigDecimal("0.85"))
|
||||
.pourcentageEvolutionGlobale(new BigDecimal("50.0"))
|
||||
.predictionProchainePeriode(new BigDecimal("160.0"))
|
||||
.margeErreurPrediction(new BigDecimal("5.0"))
|
||||
.seuilAlerteBas(new BigDecimal("80.0"))
|
||||
.seuilAlerteHaut(new BigDecimal("180.0"))
|
||||
.alerteActive(true)
|
||||
.typeAlerte("haut")
|
||||
.messageAlerte("Alerte haute")
|
||||
.configurationGraphique("config")
|
||||
.intervalleRegroupement("jour")
|
||||
.formatDate("yyyy-MM-dd")
|
||||
.dateDerniereMiseAJour(LocalDateTime.now())
|
||||
.frequenceMiseAJourMinutes(60)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Org Test");
|
||||
assertThat(dto.getDateDebut()).isEqualTo(debut);
|
||||
assertThat(dto.getDateFin()).isEqualTo(fin);
|
||||
assertThat(dto.getPointsDonnees()).isEqualTo(points);
|
||||
assertThat(dto.getValeurActuelle()).isEqualTo(new BigDecimal("150.0"));
|
||||
assertThat(dto.getValeurMinimale()).isEqualTo(new BigDecimal("50.0"));
|
||||
assertThat(dto.getValeurMaximale()).isEqualTo(new BigDecimal("200.0"));
|
||||
assertThat(dto.getValeurMoyenne()).isEqualTo(new BigDecimal("125.0"));
|
||||
assertThat(dto.getEcartType()).isEqualTo(new BigDecimal("25.0"));
|
||||
assertThat(dto.getCoefficientVariation()).isEqualTo(new BigDecimal("0.2"));
|
||||
assertThat(dto.getTendanceGenerale()).isEqualTo(new BigDecimal("5.5"));
|
||||
assertThat(dto.getCoefficientCorrelation()).isEqualTo(new BigDecimal("0.85"));
|
||||
assertThat(dto.getPourcentageEvolutionGlobale()).isEqualTo(new BigDecimal("50.0"));
|
||||
assertThat(dto.getPredictionProchainePeriode()).isEqualTo(new BigDecimal("160.0"));
|
||||
assertThat(dto.getMargeErreurPrediction()).isEqualTo(new BigDecimal("5.0"));
|
||||
assertThat(dto.getSeuilAlerteBas()).isEqualTo(new BigDecimal("80.0"));
|
||||
assertThat(dto.getSeuilAlerteHaut()).isEqualTo(new BigDecimal("180.0"));
|
||||
assertThat(dto.getAlerteActive()).isTrue();
|
||||
assertThat(dto.getTypeAlerte()).isEqualTo("haut");
|
||||
assertThat(dto.getMessageAlerte()).isEqualTo("Alerte haute");
|
||||
assertThat(dto.getConfigurationGraphique()).isEqualTo("config");
|
||||
assertThat(dto.getIntervalleRegroupement()).isEqualTo("jour");
|
||||
assertThat(dto.getFormatDate()).isEqualTo("yyyy-MM-dd");
|
||||
assertThat(dto.getDateDerniereMiseAJour()).isNotNull();
|
||||
assertThat(dto.getFrequenceMiseAJourMinutes()).isEqualTo(60);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - valeurs par défaut")
|
||||
void testBuilderValeursParDefaut() {
|
||||
LocalDateTime debut = LocalDateTime.of(2025, 1, 1, 0, 0);
|
||||
LocalDateTime fin = LocalDateTime.of(2025, 1, 31, 23, 59);
|
||||
List<PointDonneeDTO> points = Arrays.asList(
|
||||
PointDonneeDTO.builder()
|
||||
.date(debut)
|
||||
.valeur(new BigDecimal("100.0"))
|
||||
.build());
|
||||
|
||||
KPITrendDTO dto = KPITrendDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.dateDebut(debut)
|
||||
.dateFin(fin)
|
||||
.pointsDonnees(points)
|
||||
.valeurActuelle(new BigDecimal("150.0"))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getAlerteActive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("PointDonneeDTO.Builder - tous les champs")
|
||||
void testPointDonneeDTOBuilderTousChamps() {
|
||||
LocalDateTime date = LocalDateTime.of(2025, 1, 15, 10, 0);
|
||||
PointDonneeDTO point = PointDonneeDTO.builder()
|
||||
.date(date)
|
||||
.valeur(new BigDecimal("150.0"))
|
||||
.libelle("Point 1")
|
||||
.anomalie(true)
|
||||
.prediction(false)
|
||||
.metadonnees("meta")
|
||||
.build();
|
||||
|
||||
assertThat(point.getDate()).isEqualTo(date);
|
||||
assertThat(point.getValeur()).isEqualTo(new BigDecimal("150.0"));
|
||||
assertThat(point.getLibelle()).isEqualTo("Point 1");
|
||||
assertThat(point.getAnomalie()).isTrue();
|
||||
assertThat(point.getPrediction()).isFalse();
|
||||
assertThat(point.getMetadonnees()).isEqualTo("meta");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("PointDonneeDTO.Builder - valeurs par défaut")
|
||||
void testPointDonneeDTOBuilderValeursParDefaut() {
|
||||
LocalDateTime date = LocalDateTime.of(2025, 1, 15, 10, 0);
|
||||
PointDonneeDTO point = PointDonneeDTO.builder()
|
||||
.date(date)
|
||||
.valeur(new BigDecimal("100.0"))
|
||||
.build();
|
||||
|
||||
assertThat(point.getAnomalie()).isFalse();
|
||||
assertThat(point.getPrediction()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,683 @@
|
||||
package dev.lions.unionflow.server.api.dto.analytics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.analytics.ReportConfigDTO.MetriqueConfigDTO;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.ReportConfigDTO.SectionRapportDTO;
|
||||
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 java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
@DisplayName("Tests pour ReportConfigDTO")
|
||||
class ReportConfigDTOTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
assertThat(dto.getRapportPublic()).isFalse();
|
||||
assertThat(dto.getRapportAutomatique()).isFalse();
|
||||
assertThat(dto.getNiveauConfidentialite()).isEqualTo(1);
|
||||
assertThat(dto.getNombreGenerations()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
UUID userId = UUID.randomUUID();
|
||||
List<MetriqueConfigDTO> metriques = Arrays.asList(
|
||||
MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.position(1)
|
||||
.build());
|
||||
|
||||
ReportConfigDTO dto = ReportConfigDTO.builder()
|
||||
.nom("Rapport Test")
|
||||
.typeRapport("executif")
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.utilisateurCreateurId(userId)
|
||||
.metriques(metriques)
|
||||
.formatExport(FormatExport.PDF)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getNom()).isEqualTo("Rapport Test");
|
||||
assertThat(dto.getTypeRapport()).isEqualTo("executif");
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getUtilisateurCreateurId()).isEqualTo(userId);
|
||||
assertThat(dto.getMetriques()).isEqualTo(metriques);
|
||||
assertThat(dto.getFormatExport()).isEqualTo(FormatExport.PDF);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests méthodes utilitaires")
|
||||
class MethodesUtilitairesTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getNombreMetriques - avec métriques")
|
||||
void testGetNombreMetriquesAvecMetriques() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
List<MetriqueConfigDTO> metriques = Arrays.asList(
|
||||
MetriqueConfigDTO.builder().typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS).build(),
|
||||
MetriqueConfigDTO.builder().typeMetrique(TypeMetrique.NOMBRE_ORGANISATIONS_ACTIVES).build());
|
||||
dto.setMetriques(metriques);
|
||||
assertThat(dto.getNombreMetriques()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNombreMetriques - sans métriques")
|
||||
void testGetNombreMetriquesSansMetriques() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setMetriques(null);
|
||||
assertThat(dto.getNombreMetriques()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNombreSections - avec sections")
|
||||
void testGetNombreSectionsAvecSections() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
List<SectionRapportDTO> sections = Arrays.asList(
|
||||
SectionRapportDTO.builder().nom("Section 1").typeSection("resume").position(1).build(),
|
||||
SectionRapportDTO.builder().nom("Section 2").typeSection("metriques").position(2).build());
|
||||
dto.setSections(sections);
|
||||
assertThat(dto.getNombreSections()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNombreSections - sans sections")
|
||||
void testGetNombreSectionsSansSections() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setSections(null);
|
||||
assertThat(dto.getNombreSections()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isPeriodePersonnalisee - période personnalisée")
|
||||
void testIsPeriodePersonnaliseeTrue() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setPeriodeAnalyse(PeriodeAnalyse.PERIODE_PERSONNALISEE);
|
||||
assertThat(dto.isPeriodePersonnalisee()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isPeriodePersonnalisee - période non personnalisée")
|
||||
void testIsPeriodePersonnaliseeFalse() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setPeriodeAnalyse(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.isPeriodePersonnalisee()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isConfidentiel - niveau >= 4")
|
||||
void testIsConfidentielTrue() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setNiveauConfidentialite(4);
|
||||
assertThat(dto.isConfidentiel()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isConfidentiel - niveau < 4")
|
||||
void testIsConfidentielFalse() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setNiveauConfidentialite(3);
|
||||
assertThat(dto.isConfidentiel()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isConfidentiel - niveau null")
|
||||
void testIsConfidentielNull() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setNiveauConfidentialite(null);
|
||||
assertThat(dto.isConfidentiel()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteGeneration - rapport automatique et prochaine génération passée")
|
||||
void testNecessiteGenerationTrue() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setRapportAutomatique(true);
|
||||
dto.setProchaineGeneration(LocalDateTime.now().minusHours(1));
|
||||
assertThat(dto.necessiteGeneration()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteGeneration - rapport non automatique")
|
||||
void testNecessiteGenerationFalseNonAutomatique() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setRapportAutomatique(false);
|
||||
dto.setProchaineGeneration(LocalDateTime.now().minusHours(1));
|
||||
assertThat(dto.necessiteGeneration()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteGeneration - prochaine génération future")
|
||||
void testNecessiteGenerationFalseFuture() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setRapportAutomatique(true);
|
||||
dto.setProchaineGeneration(LocalDateTime.now().plusHours(1));
|
||||
assertThat(dto.necessiteGeneration()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("necessiteGeneration - prochaine génération null")
|
||||
void testNecessiteGenerationFalseNull() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
dto.setRapportAutomatique(true);
|
||||
dto.setProchaineGeneration(null);
|
||||
assertThat(dto.necessiteGeneration()).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"1, Toutes les heures",
|
||||
"24, Quotidienne",
|
||||
"168, Hebdomadaire",
|
||||
"720, Mensuelle",
|
||||
"48, Toutes les 48 heures",
|
||||
"null, Manuelle"
|
||||
})
|
||||
@DisplayName("getFrequenceTexte - tous les cas")
|
||||
void testGetFrequenceTexte(String heuresStr, String expected) {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
if ("null".equals(heuresStr)) {
|
||||
dto.setFrequenceGenerationHeures(null);
|
||||
} else {
|
||||
dto.setFrequenceGenerationHeures(Integer.parseInt(heuresStr));
|
||||
}
|
||||
assertThat(dto.getFrequenceTexte()).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests classe interne MetriqueConfigDTO")
|
||||
class MetriqueConfigDTOTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
MetriqueConfigDTO dto = new MetriqueConfigDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
assertThat(dto.getTailleAffichage()).isEqualTo(2);
|
||||
assertThat(dto.getInclureGraphique()).isTrue();
|
||||
assertThat(dto.getTypeGraphique()).isEqualTo("line");
|
||||
assertThat(dto.getInclureTendance()).isTrue();
|
||||
assertThat(dto.getInclureComparaison()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
Map<String, Object> seuils = new HashMap<>();
|
||||
seuils.put("bas", 10.0);
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
filtres.put("org", "org-1");
|
||||
|
||||
MetriqueConfigDTO dto = MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.libellePersonnalise("Membres Actifs")
|
||||
.position(1)
|
||||
.tailleAffichage(3)
|
||||
.couleurPersonnalisee("#FF0000")
|
||||
.inclureGraphique(false)
|
||||
.typeGraphique("bar")
|
||||
.inclureTendance(false)
|
||||
.inclureComparaison(false)
|
||||
.seuilsAlerte(seuils)
|
||||
.filtresSpecifiques(filtres)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getLibellePersonnalise()).isEqualTo("Membres Actifs");
|
||||
assertThat(dto.getPosition()).isEqualTo(1);
|
||||
assertThat(dto.getTailleAffichage()).isEqualTo(3);
|
||||
assertThat(dto.getCouleurPersonnalisee()).isEqualTo("#FF0000");
|
||||
assertThat(dto.getInclureGraphique()).isFalse();
|
||||
assertThat(dto.getTypeGraphique()).isEqualTo("bar");
|
||||
assertThat(dto.getInclureTendance()).isFalse();
|
||||
assertThat(dto.getInclureComparaison()).isFalse();
|
||||
assertThat(dto.getSeuilsAlerte()).isEqualTo(seuils);
|
||||
assertThat(dto.getFiltresSpecifiques()).isEqualTo(filtres);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Getters/setters")
|
||||
void testGettersSetters() {
|
||||
MetriqueConfigDTO dto = new MetriqueConfigDTO();
|
||||
Map<String, Object> seuils = new HashMap<>();
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
|
||||
dto.setTypeMetrique(TypeMetrique.NOMBRE_ORGANISATIONS_ACTIVES);
|
||||
dto.setLibellePersonnalise("Organisations");
|
||||
dto.setPosition(2);
|
||||
dto.setTailleAffichage(1);
|
||||
dto.setCouleurPersonnalisee("#00FF00");
|
||||
dto.setInclureGraphique(true);
|
||||
dto.setTypeGraphique("pie");
|
||||
dto.setInclureTendance(true);
|
||||
dto.setInclureComparaison(true);
|
||||
dto.setSeuilsAlerte(seuils);
|
||||
dto.setFiltresSpecifiques(filtres);
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_ORGANISATIONS_ACTIVES);
|
||||
assertThat(dto.getLibellePersonnalise()).isEqualTo("Organisations");
|
||||
assertThat(dto.getPosition()).isEqualTo(2);
|
||||
assertThat(dto.getTailleAffichage()).isEqualTo(1);
|
||||
assertThat(dto.getCouleurPersonnalisee()).isEqualTo("#00FF00");
|
||||
assertThat(dto.getInclureGraphique()).isTrue();
|
||||
assertThat(dto.getTypeGraphique()).isEqualTo("pie");
|
||||
assertThat(dto.getInclureTendance()).isTrue();
|
||||
assertThat(dto.getInclureComparaison()).isTrue();
|
||||
assertThat(dto.getSeuilsAlerte()).isEqualTo(seuils);
|
||||
assertThat(dto.getFiltresSpecifiques()).isEqualTo(filtres);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests classe interne SectionRapportDTO")
|
||||
class SectionRapportDTOTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
SectionRapportDTO dto = new SectionRapportDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getPliable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
List<TypeMetrique> metriques = Arrays.asList(
|
||||
TypeMetrique.NOMBRE_MEMBRES_ACTIFS,
|
||||
TypeMetrique.NOMBRE_ORGANISATIONS_ACTIVES);
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("key", "value");
|
||||
|
||||
SectionRapportDTO dto = SectionRapportDTO.builder()
|
||||
.nom("Section Résumé")
|
||||
.description("Description de la section")
|
||||
.position(1)
|
||||
.typeSection("resume")
|
||||
.metriquesIncluses(metriques)
|
||||
.configurationSection(config)
|
||||
.visible(true)
|
||||
.pliable(true)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getNom()).isEqualTo("Section Résumé");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description de la section");
|
||||
assertThat(dto.getPosition()).isEqualTo(1);
|
||||
assertThat(dto.getTypeSection()).isEqualTo("resume");
|
||||
assertThat(dto.getMetriquesIncluses()).isEqualTo(metriques);
|
||||
assertThat(dto.getConfigurationSection()).isEqualTo(config);
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getPliable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Getters/setters")
|
||||
void testGettersSetters() {
|
||||
SectionRapportDTO dto = new SectionRapportDTO();
|
||||
List<TypeMetrique> metriques = Arrays.asList(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
|
||||
dto.setNom("Section Test");
|
||||
dto.setDescription("Description test");
|
||||
dto.setPosition(2);
|
||||
dto.setTypeSection("metriques");
|
||||
dto.setMetriquesIncluses(metriques);
|
||||
dto.setConfigurationSection(config);
|
||||
dto.setVisible(false);
|
||||
dto.setPliable(true);
|
||||
|
||||
assertThat(dto.getNom()).isEqualTo("Section Test");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description test");
|
||||
assertThat(dto.getPosition()).isEqualTo(2);
|
||||
assertThat(dto.getTypeSection()).isEqualTo("metriques");
|
||||
assertThat(dto.getMetriquesIncluses()).isEqualTo(metriques);
|
||||
assertThat(dto.getConfigurationSection()).isEqualTo(config);
|
||||
assertThat(dto.getVisible()).isFalse();
|
||||
assertThat(dto.getPliable()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getters/setters complets")
|
||||
class GettersSettersCompletsTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters")
|
||||
void testTousLesGettersSetters() {
|
||||
ReportConfigDTO dto = new ReportConfigDTO();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
List<MetriqueConfigDTO> metriques = Arrays.asList(
|
||||
MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.position(1)
|
||||
.build());
|
||||
List<SectionRapportDTO> sections = Arrays.asList(
|
||||
SectionRapportDTO.builder()
|
||||
.nom("Section 1")
|
||||
.typeSection("resume")
|
||||
.position(1)
|
||||
.build());
|
||||
List<FormatExport> formats = Arrays.asList(FormatExport.PDF, FormatExport.EXCEL);
|
||||
Map<String, String> couleurs = new HashMap<>();
|
||||
couleurs.put("primary", "#FF0000");
|
||||
List<String> destinataires = Arrays.asList("email1@test.com", "email2@test.com");
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
filtres.put("key", "value");
|
||||
List<String> tags = Arrays.asList("tag1", "tag2");
|
||||
|
||||
dto.setNom("Rapport Complet");
|
||||
dto.setDescription("Description complète");
|
||||
dto.setTypeRapport("analytique");
|
||||
dto.setPeriodeAnalyse(PeriodeAnalyse.CE_MOIS);
|
||||
dto.setDateDebutPersonnalisee(LocalDateTime.of(2025, 1, 1, 0, 0));
|
||||
dto.setDateFinPersonnalisee(LocalDateTime.of(2025, 1, 31, 23, 59));
|
||||
dto.setOrganisationId(orgId);
|
||||
dto.setNomOrganisation("Org Test");
|
||||
dto.setUtilisateurCreateurId(userId);
|
||||
dto.setNomUtilisateurCreateur("User Test");
|
||||
dto.setMetriques(metriques);
|
||||
dto.setSections(sections);
|
||||
dto.setFormatExport(FormatExport.PDF);
|
||||
dto.setFormatsExportAutorises(formats);
|
||||
dto.setModeleRapport("modele1");
|
||||
dto.setConfigurationMiseEnPage("config");
|
||||
dto.setLogoPersonnalise("logo.png");
|
||||
dto.setCouleursPersonnalisees(couleurs);
|
||||
dto.setRapportPublic(true);
|
||||
dto.setRapportAutomatique(true);
|
||||
dto.setFrequenceGenerationHeures(24);
|
||||
dto.setProchaineGeneration(LocalDateTime.now().plusHours(24));
|
||||
dto.setDestinatairesEmail(destinataires);
|
||||
dto.setObjetEmail("Rapport automatique");
|
||||
dto.setCorpsEmail("Corps du rapport");
|
||||
dto.setParametresFiltrage(filtres);
|
||||
dto.setTags(tags);
|
||||
dto.setNiveauConfidentialite(3);
|
||||
dto.setDateDerniereGeneration(LocalDateTime.now().minusHours(1));
|
||||
dto.setNombreGenerations(10);
|
||||
dto.setTailleMoyenneKB(500L);
|
||||
dto.setTempsMoyenGenerationSecondes(30);
|
||||
|
||||
assertThat(dto.getNom()).isEqualTo("Rapport Complet");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description complète");
|
||||
assertThat(dto.getTypeRapport()).isEqualTo("analytique");
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getDateDebutPersonnalisee()).isNotNull();
|
||||
assertThat(dto.getDateFinPersonnalisee()).isNotNull();
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Org Test");
|
||||
assertThat(dto.getUtilisateurCreateurId()).isEqualTo(userId);
|
||||
assertThat(dto.getNomUtilisateurCreateur()).isEqualTo("User Test");
|
||||
assertThat(dto.getMetriques()).isEqualTo(metriques);
|
||||
assertThat(dto.getSections()).isEqualTo(sections);
|
||||
assertThat(dto.getFormatExport()).isEqualTo(FormatExport.PDF);
|
||||
assertThat(dto.getFormatsExportAutorises()).isEqualTo(formats);
|
||||
assertThat(dto.getModeleRapport()).isEqualTo("modele1");
|
||||
assertThat(dto.getConfigurationMiseEnPage()).isEqualTo("config");
|
||||
assertThat(dto.getLogoPersonnalise()).isEqualTo("logo.png");
|
||||
assertThat(dto.getCouleursPersonnalisees()).isEqualTo(couleurs);
|
||||
assertThat(dto.getRapportPublic()).isTrue();
|
||||
assertThat(dto.getRapportAutomatique()).isTrue();
|
||||
assertThat(dto.getFrequenceGenerationHeures()).isEqualTo(24);
|
||||
assertThat(dto.getProchaineGeneration()).isNotNull();
|
||||
assertThat(dto.getDestinatairesEmail()).isEqualTo(destinataires);
|
||||
assertThat(dto.getObjetEmail()).isEqualTo("Rapport automatique");
|
||||
assertThat(dto.getCorpsEmail()).isEqualTo("Corps du rapport");
|
||||
assertThat(dto.getParametresFiltrage()).isEqualTo(filtres);
|
||||
assertThat(dto.getTags()).isEqualTo(tags);
|
||||
assertThat(dto.getNiveauConfidentialite()).isEqualTo(3);
|
||||
assertThat(dto.getDateDerniereGeneration()).isNotNull();
|
||||
assertThat(dto.getNombreGenerations()).isEqualTo(10);
|
||||
assertThat(dto.getTailleMoyenneKB()).isEqualTo(500L);
|
||||
assertThat(dto.getTempsMoyenGenerationSecondes()).isEqualTo(30);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Builder complet - tous les champs")
|
||||
class BuilderCompletTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - tous les champs")
|
||||
void testBuilderTousChamps() {
|
||||
UUID orgId = UUID.randomUUID();
|
||||
UUID userId = UUID.randomUUID();
|
||||
List<MetriqueConfigDTO> metriques = Arrays.asList(
|
||||
MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.position(1)
|
||||
.build());
|
||||
List<SectionRapportDTO> sections = Arrays.asList(
|
||||
SectionRapportDTO.builder()
|
||||
.nom("Section 1")
|
||||
.typeSection("resume")
|
||||
.position(1)
|
||||
.build());
|
||||
List<FormatExport> formats = Arrays.asList(FormatExport.PDF, FormatExport.EXCEL);
|
||||
Map<String, String> couleurs = new HashMap<>();
|
||||
couleurs.put("primary", "#FF0000");
|
||||
List<String> destinataires = Arrays.asList("email1@test.com", "email2@test.com");
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
filtres.put("key", "value");
|
||||
List<String> tags = Arrays.asList("tag1", "tag2");
|
||||
|
||||
ReportConfigDTO dto = ReportConfigDTO.builder()
|
||||
.nom("Rapport Complet")
|
||||
.description("Description complète")
|
||||
.typeRapport("analytique")
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.dateDebutPersonnalisee(LocalDateTime.of(2025, 1, 1, 0, 0))
|
||||
.dateFinPersonnalisee(LocalDateTime.of(2025, 1, 31, 23, 59))
|
||||
.organisationId(orgId)
|
||||
.nomOrganisation("Org Test")
|
||||
.utilisateurCreateurId(userId)
|
||||
.nomUtilisateurCreateur("User Test")
|
||||
.metriques(metriques)
|
||||
.sections(sections)
|
||||
.formatExport(FormatExport.PDF)
|
||||
.formatsExportAutorises(formats)
|
||||
.modeleRapport("modele1")
|
||||
.configurationMiseEnPage("config")
|
||||
.logoPersonnalise("logo.png")
|
||||
.couleursPersonnalisees(couleurs)
|
||||
.rapportPublic(true)
|
||||
.rapportAutomatique(true)
|
||||
.frequenceGenerationHeures(24)
|
||||
.prochaineGeneration(LocalDateTime.now().plusHours(24))
|
||||
.destinatairesEmail(destinataires)
|
||||
.objetEmail("Rapport automatique")
|
||||
.corpsEmail("Corps du rapport")
|
||||
.parametresFiltrage(filtres)
|
||||
.tags(tags)
|
||||
.niveauConfidentialite(3)
|
||||
.dateDerniereGeneration(LocalDateTime.now().minusHours(1))
|
||||
.nombreGenerations(10)
|
||||
.tailleMoyenneKB(500L)
|
||||
.tempsMoyenGenerationSecondes(30)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getNom()).isEqualTo("Rapport Complet");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description complète");
|
||||
assertThat(dto.getTypeRapport()).isEqualTo("analytique");
|
||||
assertThat(dto.getPeriodeAnalyse()).isEqualTo(PeriodeAnalyse.CE_MOIS);
|
||||
assertThat(dto.getDateDebutPersonnalisee()).isNotNull();
|
||||
assertThat(dto.getDateFinPersonnalisee()).isNotNull();
|
||||
assertThat(dto.getOrganisationId()).isEqualTo(orgId);
|
||||
assertThat(dto.getNomOrganisation()).isEqualTo("Org Test");
|
||||
assertThat(dto.getUtilisateurCreateurId()).isEqualTo(userId);
|
||||
assertThat(dto.getNomUtilisateurCreateur()).isEqualTo("User Test");
|
||||
assertThat(dto.getMetriques()).isEqualTo(metriques);
|
||||
assertThat(dto.getSections()).isEqualTo(sections);
|
||||
assertThat(dto.getFormatExport()).isEqualTo(FormatExport.PDF);
|
||||
assertThat(dto.getFormatsExportAutorises()).isEqualTo(formats);
|
||||
assertThat(dto.getModeleRapport()).isEqualTo("modele1");
|
||||
assertThat(dto.getConfigurationMiseEnPage()).isEqualTo("config");
|
||||
assertThat(dto.getLogoPersonnalise()).isEqualTo("logo.png");
|
||||
assertThat(dto.getCouleursPersonnalisees()).isEqualTo(couleurs);
|
||||
assertThat(dto.getRapportPublic()).isTrue();
|
||||
assertThat(dto.getRapportAutomatique()).isTrue();
|
||||
assertThat(dto.getFrequenceGenerationHeures()).isEqualTo(24);
|
||||
assertThat(dto.getProchaineGeneration()).isNotNull();
|
||||
assertThat(dto.getDestinatairesEmail()).isEqualTo(destinataires);
|
||||
assertThat(dto.getObjetEmail()).isEqualTo("Rapport automatique");
|
||||
assertThat(dto.getCorpsEmail()).isEqualTo("Corps du rapport");
|
||||
assertThat(dto.getParametresFiltrage()).isEqualTo(filtres);
|
||||
assertThat(dto.getTags()).isEqualTo(tags);
|
||||
assertThat(dto.getNiveauConfidentialite()).isEqualTo(3);
|
||||
assertThat(dto.getDateDerniereGeneration()).isNotNull();
|
||||
assertThat(dto.getNombreGenerations()).isEqualTo(10);
|
||||
assertThat(dto.getTailleMoyenneKB()).isEqualTo(500L);
|
||||
assertThat(dto.getTempsMoyenGenerationSecondes()).isEqualTo(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder - valeurs par défaut")
|
||||
void testBuilderValeursParDefaut() {
|
||||
UUID userId = UUID.randomUUID();
|
||||
List<MetriqueConfigDTO> metriques = Arrays.asList(
|
||||
MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.position(1)
|
||||
.build());
|
||||
|
||||
ReportConfigDTO dto = ReportConfigDTO.builder()
|
||||
.nom("Rapport")
|
||||
.typeRapport("executif")
|
||||
.periodeAnalyse(PeriodeAnalyse.CE_MOIS)
|
||||
.utilisateurCreateurId(userId)
|
||||
.metriques(metriques)
|
||||
.formatExport(FormatExport.PDF)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getRapportPublic()).isFalse();
|
||||
assertThat(dto.getRapportAutomatique()).isFalse();
|
||||
assertThat(dto.getNiveauConfidentialite()).isEqualTo(1);
|
||||
assertThat(dto.getNombreGenerations()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MetriqueConfigDTO.Builder - tous les champs")
|
||||
void testMetriqueConfigDTOBuilderTousChamps() {
|
||||
Map<String, Object> seuils = new HashMap<>();
|
||||
seuils.put("bas", 10.0);
|
||||
Map<String, Object> filtres = new HashMap<>();
|
||||
filtres.put("org", "org-1");
|
||||
|
||||
MetriqueConfigDTO dto = MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.libellePersonnalise("Membres Actifs")
|
||||
.position(1)
|
||||
.tailleAffichage(3)
|
||||
.couleurPersonnalisee("#FF0000")
|
||||
.inclureGraphique(false)
|
||||
.typeGraphique("bar")
|
||||
.inclureTendance(false)
|
||||
.inclureComparaison(false)
|
||||
.seuilsAlerte(seuils)
|
||||
.filtresSpecifiques(filtres)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTypeMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
|
||||
assertThat(dto.getLibellePersonnalise()).isEqualTo("Membres Actifs");
|
||||
assertThat(dto.getPosition()).isEqualTo(1);
|
||||
assertThat(dto.getTailleAffichage()).isEqualTo(3);
|
||||
assertThat(dto.getCouleurPersonnalisee()).isEqualTo("#FF0000");
|
||||
assertThat(dto.getInclureGraphique()).isFalse();
|
||||
assertThat(dto.getTypeGraphique()).isEqualTo("bar");
|
||||
assertThat(dto.getInclureTendance()).isFalse();
|
||||
assertThat(dto.getInclureComparaison()).isFalse();
|
||||
assertThat(dto.getSeuilsAlerte()).isEqualTo(seuils);
|
||||
assertThat(dto.getFiltresSpecifiques()).isEqualTo(filtres);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MetriqueConfigDTO.Builder - valeurs par défaut")
|
||||
void testMetriqueConfigDTOBuilderValeursParDefaut() {
|
||||
MetriqueConfigDTO dto = MetriqueConfigDTO.builder()
|
||||
.typeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS)
|
||||
.position(1)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTailleAffichage()).isEqualTo(2);
|
||||
assertThat(dto.getInclureGraphique()).isTrue();
|
||||
assertThat(dto.getTypeGraphique()).isEqualTo("line");
|
||||
assertThat(dto.getInclureTendance()).isTrue();
|
||||
assertThat(dto.getInclureComparaison()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("SectionRapportDTO.Builder - tous les champs")
|
||||
void testSectionRapportDTOBuilderTousChamps() {
|
||||
List<TypeMetrique> metriques = Arrays.asList(
|
||||
TypeMetrique.NOMBRE_MEMBRES_ACTIFS,
|
||||
TypeMetrique.NOMBRE_ORGANISATIONS_ACTIVES);
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("key", "value");
|
||||
|
||||
SectionRapportDTO dto = SectionRapportDTO.builder()
|
||||
.nom("Section Résumé")
|
||||
.description("Description de la section")
|
||||
.position(1)
|
||||
.typeSection("resume")
|
||||
.metriquesIncluses(metriques)
|
||||
.configurationSection(config)
|
||||
.visible(true)
|
||||
.pliable(true)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getNom()).isEqualTo("Section Résumé");
|
||||
assertThat(dto.getDescription()).isEqualTo("Description de la section");
|
||||
assertThat(dto.getPosition()).isEqualTo(1);
|
||||
assertThat(dto.getTypeSection()).isEqualTo("resume");
|
||||
assertThat(dto.getMetriquesIncluses()).isEqualTo(metriques);
|
||||
assertThat(dto.getConfigurationSection()).isEqualTo(config);
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getPliable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("SectionRapportDTO.Builder - valeurs par défaut")
|
||||
void testSectionRapportDTOBuilderValeursParDefaut() {
|
||||
SectionRapportDTO dto = SectionRapportDTO.builder()
|
||||
.nom("Section")
|
||||
.typeSection("resume")
|
||||
.position(1)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getVisible()).isTrue();
|
||||
assertThat(dto.getPliable()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
package dev.lions.unionflow.server.api.dto.base;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour BaseDTO.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-10
|
||||
*/
|
||||
@DisplayName("Tests BaseDTO")
|
||||
class BaseDTOTest {
|
||||
|
||||
private TestableBaseDTO baseDto;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
baseDto = new TestableBaseDTO();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
assertThat(baseDto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de Construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut - Initialisation correcte")
|
||||
void testConstructeurParDefaut() {
|
||||
TestableBaseDTO newDto = new TestableBaseDTO();
|
||||
|
||||
assertThat(newDto.getId()).isNotNull();
|
||||
assertThat(newDto.getDateCreation()).isNotNull();
|
||||
assertThat(newDto.isActif()).isTrue();
|
||||
assertThat(newDto.getVersion()).isEqualTo(0L);
|
||||
assertThat(newDto.getDateModification()).isNull();
|
||||
assertThat(newDto.getCreePar()).isNull();
|
||||
assertThat(newDto.getModifiePar()).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Getters/Setters")
|
||||
class GettersSettersTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test tous les getters/setters")
|
||||
void testGettersSetters() {
|
||||
UUID id = UUID.randomUUID();
|
||||
LocalDateTime dateCreation = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime dateModification = LocalDateTime.now();
|
||||
String creePar = "user1";
|
||||
String modifiePar = "user2";
|
||||
Long version = 5L;
|
||||
|
||||
baseDto.setId(id);
|
||||
baseDto.setDateCreation(dateCreation);
|
||||
baseDto.setDateModification(dateModification);
|
||||
baseDto.setCreePar(creePar);
|
||||
baseDto.setModifiePar(modifiePar);
|
||||
baseDto.setVersion(version);
|
||||
baseDto.setActif(false);
|
||||
|
||||
assertThat(baseDto.getId()).isEqualTo(id);
|
||||
assertThat(baseDto.getDateCreation()).isEqualTo(dateCreation);
|
||||
assertThat(baseDto.getDateModification()).isEqualTo(dateModification);
|
||||
assertThat(baseDto.getCreePar()).isEqualTo(creePar);
|
||||
assertThat(baseDto.getModifiePar()).isEqualTo(modifiePar);
|
||||
assertThat(baseDto.getVersion()).isEqualTo(version);
|
||||
assertThat(baseDto.isActif()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests Méthodes Métier")
|
||||
class MethodesMetierTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test marquerCommeModifie")
|
||||
void testMarquerCommeModifie() {
|
||||
String utilisateur = "testUser";
|
||||
LocalDateTime avant = LocalDateTime.now().minusSeconds(1);
|
||||
|
||||
baseDto.marquerCommeModifie(utilisateur);
|
||||
|
||||
assertThat(baseDto.getModifiePar()).isEqualTo(utilisateur);
|
||||
assertThat(baseDto.getDateModification()).isAfter(avant);
|
||||
assertThat(baseDto.getVersion()).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test marquerCommeModifie - Incrémente version")
|
||||
void testMarquerCommeModifieIncrementeVersion() {
|
||||
baseDto.setVersion(3L);
|
||||
|
||||
baseDto.marquerCommeModifie("user");
|
||||
|
||||
assertThat(baseDto.getVersion()).isEqualTo(4L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test desactiver")
|
||||
void testDesactiver() {
|
||||
baseDto.setActif(true);
|
||||
|
||||
baseDto.desactiver("user");
|
||||
|
||||
assertThat(baseDto.isActif()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test reactiver")
|
||||
void testReactiver() {
|
||||
baseDto.setActif(false);
|
||||
|
||||
baseDto.reactiver("user");
|
||||
|
||||
assertThat(baseDto.isActif()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test isNouveau")
|
||||
void testIsNouveau() {
|
||||
// Nouveau DTO (ID null)
|
||||
baseDto.setId(null);
|
||||
assertThat(baseDto.isNouveau()).isTrue();
|
||||
|
||||
// DTO existant (ID non null)
|
||||
baseDto.setId(UUID.randomUUID());
|
||||
assertThat(baseDto.isNouveau()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test isActif")
|
||||
void testIsActif() {
|
||||
// Test actif
|
||||
baseDto.setActif(true);
|
||||
assertThat(baseDto.isActif()).isTrue();
|
||||
|
||||
// Test inactif
|
||||
baseDto.setActif(false);
|
||||
assertThat(baseDto.isActif()).isFalse();
|
||||
|
||||
// Test avec null
|
||||
baseDto.setActif(null);
|
||||
assertThat(baseDto.isActif()).isFalse();
|
||||
|
||||
// Test avec Boolean.TRUE explicite
|
||||
baseDto.setActif(Boolean.TRUE);
|
||||
assertThat(baseDto.isActif()).isTrue();
|
||||
|
||||
// Test avec Boolean.FALSE explicite
|
||||
baseDto.setActif(Boolean.FALSE);
|
||||
assertThat(baseDto.isActif()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test marquerCommeNouveau")
|
||||
void testMarquerCommeNouveau() {
|
||||
String utilisateur = "testUser";
|
||||
LocalDateTime avant = LocalDateTime.now().minusSeconds(1);
|
||||
|
||||
baseDto.marquerCommeNouveau(utilisateur);
|
||||
|
||||
assertThat(baseDto.getCreePar()).isEqualTo(utilisateur);
|
||||
assertThat(baseDto.getModifiePar()).isEqualTo(utilisateur);
|
||||
assertThat(baseDto.getDateCreation()).isAfter(avant);
|
||||
assertThat(baseDto.getDateModification()).isAfter(avant);
|
||||
assertThat(baseDto.getVersion()).isEqualTo(0L);
|
||||
assertThat(baseDto.isActif()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test marquerCommeModifie avec version null")
|
||||
void testMarquerCommeModifieAvecVersionNull() {
|
||||
// Given
|
||||
baseDto.setVersion(null);
|
||||
String utilisateur = "testUser";
|
||||
LocalDateTime avant = LocalDateTime.now().minusSeconds(1);
|
||||
|
||||
// When
|
||||
baseDto.marquerCommeModifie(utilisateur);
|
||||
|
||||
// Then
|
||||
assertThat(baseDto.getModifiePar()).isEqualTo(utilisateur);
|
||||
assertThat(baseDto.getDateModification()).isAfter(avant);
|
||||
assertThat(baseDto.getVersion()).isNull(); // Version reste null car elle était null
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test accès et modification du champ actif")
|
||||
void testAccesChampActif() {
|
||||
// Given & When - Vérifier l'initialisation par défaut
|
||||
assertThat(baseDto.getActif()).isTrue();
|
||||
assertThat(baseDto.isActif()).isTrue(); // Test de la méthode isActif() pour Boolean
|
||||
|
||||
// Test modification du champ actif
|
||||
baseDto.setActif(false);
|
||||
assertThat(baseDto.getActif()).isFalse();
|
||||
assertThat(baseDto.isActif()).isFalse();
|
||||
|
||||
baseDto.setActif(null);
|
||||
assertThat(baseDto.getActif()).isNull();
|
||||
assertThat(baseDto.isActif()).isFalse(); // isActif() retourne false pour null avec Lombok
|
||||
|
||||
baseDto.setActif(true);
|
||||
assertThat(baseDto.getActif()).isTrue();
|
||||
assertThat(baseDto.isActif()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests equals et hashCode")
|
||||
class EqualsHashCodeTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test equals - Même ID")
|
||||
void testEqualsMemeId() {
|
||||
UUID id = UUID.randomUUID();
|
||||
baseDto.setId(id);
|
||||
|
||||
TestableBaseDTO autre = new TestableBaseDTO();
|
||||
autre.setId(id);
|
||||
|
||||
assertThat(baseDto).isEqualTo(autre);
|
||||
assertThat(baseDto.hashCode()).isEqualTo(autre.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test equals - IDs différents")
|
||||
void testEqualsIdsDifferents() {
|
||||
baseDto.setId(UUID.randomUUID());
|
||||
|
||||
TestableBaseDTO autre = new TestableBaseDTO();
|
||||
autre.setId(UUID.randomUUID());
|
||||
|
||||
assertThat(baseDto).isNotEqualTo(autre);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test equals - ID null")
|
||||
void testEqualsIdNull() {
|
||||
baseDto.setId(null);
|
||||
|
||||
TestableBaseDTO autre = new TestableBaseDTO();
|
||||
autre.setId(null);
|
||||
|
||||
assertThat(baseDto).isNotEqualTo(autre);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test equals - Objet null")
|
||||
void testEqualsObjetNull() {
|
||||
assertThat(baseDto).isNotEqualTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test equals - Classe différente")
|
||||
void testEqualsClasseDifferente() {
|
||||
assertThat(baseDto).isNotEqualTo("string");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test equals - Même objet")
|
||||
void testEqualsMemeObjet() {
|
||||
assertThat(baseDto).isEqualTo(baseDto);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test hashCode - ID null")
|
||||
void testHashCodeIdNull() {
|
||||
baseDto.setId(null);
|
||||
assertThat(baseDto.hashCode()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test hashCode - ID non null")
|
||||
void testHashCodeIdNonNull() {
|
||||
UUID id = UUID.randomUUID();
|
||||
baseDto.setId(id);
|
||||
assertThat(baseDto.hashCode()).isEqualTo(id.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests toString")
|
||||
class ToStringTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test toString")
|
||||
void testToString() {
|
||||
UUID id = UUID.randomUUID();
|
||||
baseDto.setId(id);
|
||||
baseDto.setVersion(2L);
|
||||
baseDto.setActif(true);
|
||||
|
||||
String result = baseDto.toString();
|
||||
assertThat(result).contains("TestableBaseDTO");
|
||||
assertThat(result).contains("id=" + id.toString());
|
||||
assertThat(result).contains("version=2");
|
||||
assertThat(result).contains("actif=true");
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe de test concrète pour tester BaseDTO. */
|
||||
private static class TestableBaseDTO extends BaseDTO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestableBaseDTO{" + super.toString() + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package dev.lions.unionflow.server.api.dto.dashboard;
|
||||
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.createDashboardDataDTO;
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.createRecentActivityDTO;
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.createUpcomingEventDTO;
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.daysFromNow;
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.hoursAgo;
|
||||
import static dev.lions.unionflow.server.api.TestDataFactory.now;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@DisplayName("Tests pour DashboardDataDTO")
|
||||
class DashboardDataDTOTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
DashboardStatsDTO stats = DashboardStatsDTO.builder().build();
|
||||
List<RecentActivityDTO> activities = List.of();
|
||||
List<UpcomingEventDTO> events = List.of();
|
||||
Map<String, Object> preferences = new HashMap<>();
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.stats(stats)
|
||||
.recentActivities(activities)
|
||||
.upcomingEvents(events)
|
||||
.userPreferences(preferences)
|
||||
.organizationId("org-123")
|
||||
.userId("user-456")
|
||||
.build();
|
||||
|
||||
assertThat(dto.getStats()).isEqualTo(stats);
|
||||
assertThat(dto.getRecentActivities()).isEqualTo(activities);
|
||||
assertThat(dto.getUpcomingEvents()).isEqualTo(events);
|
||||
assertThat(dto.getUserPreferences()).isEqualTo(preferences);
|
||||
assertThat(dto.getOrganizationId()).isEqualTo("org-123");
|
||||
assertThat(dto.getUserId()).isEqualTo("user-456");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getTodayEventsCount")
|
||||
class GetTodayEventsCountTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getTodayEventsCount - avec événements d'aujourd'hui")
|
||||
void testGetTodayEventsCountAvecEvenementsAujourdhui() {
|
||||
UpcomingEventDTO event1 = createUpcomingEventDTO(now());
|
||||
UpcomingEventDTO event2 = createUpcomingEventDTO(daysFromNow(1));
|
||||
UpcomingEventDTO event3 = createUpcomingEventDTO(now());
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.upcomingEvents(List.of(event1, event2, event3))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTodayEventsCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getTodayEventsCount - sans événements d'aujourd'hui")
|
||||
void testGetTodayEventsCountSansEvenementsAujourdhui() {
|
||||
UpcomingEventDTO event = createUpcomingEventDTO(daysFromNow(1));
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.upcomingEvents(List.of(event))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTodayEventsCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getTodayEventsCount - upcomingEvents null")
|
||||
void testGetTodayEventsCountNull() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getTodayEventsCount()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getTomorrowEventsCount")
|
||||
class GetTomorrowEventsCountTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getTomorrowEventsCount - avec événements de demain")
|
||||
void testGetTomorrowEventsCountAvecEvenementsDemain() {
|
||||
UpcomingEventDTO event1 = createUpcomingEventDTO(daysFromNow(1));
|
||||
UpcomingEventDTO event2 = createUpcomingEventDTO(daysFromNow(2));
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.upcomingEvents(List.of(event1, event2))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTomorrowEventsCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getTomorrowEventsCount - upcomingEvents null")
|
||||
void testGetTomorrowEventsCountNull() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getTomorrowEventsCount()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getRecentActivitiesCount")
|
||||
class GetRecentActivitiesCountTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getRecentActivitiesCount - avec activités récentes")
|
||||
void testGetRecentActivitiesCountAvecActivitesRecent() {
|
||||
RecentActivityDTO activity1 = createRecentActivityDTO("member", hoursAgo(12));
|
||||
RecentActivityDTO activity2 = createRecentActivityDTO("event", hoursAgo(25));
|
||||
RecentActivityDTO activity3 = createRecentActivityDTO("contribution", hoursAgo(5));
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.recentActivities(List.of(activity1, activity2, activity3))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getRecentActivitiesCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getRecentActivitiesCount - recentActivities null")
|
||||
void testGetRecentActivitiesCountNull() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getRecentActivitiesCount()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getTodayActivitiesCount")
|
||||
class GetTodayActivitiesCountTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getTodayActivitiesCount - avec activités d'aujourd'hui")
|
||||
void testGetTodayActivitiesCountAvecActivitesAujourdhui() {
|
||||
RecentActivityDTO activity1 = createRecentActivityDTO("member", now());
|
||||
RecentActivityDTO activity2 = createRecentActivityDTO("event", hoursAgo(25));
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.recentActivities(List.of(activity1, activity2))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTodayActivitiesCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getTodayActivitiesCount - recentActivities null")
|
||||
void testGetTodayActivitiesCountNull() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getTodayActivitiesCount()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getHasUpcomingEvents")
|
||||
class GetHasUpcomingEventsTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasUpcomingEvents - avec événements")
|
||||
void testGetHasUpcomingEventsAvecEvenements() {
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.upcomingEvents(List.of(createUpcomingEventDTO()))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasUpcomingEvents()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasUpcomingEvents - liste vide")
|
||||
void testGetHasUpcomingEventsListeVide() {
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.upcomingEvents(List.of())
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasUpcomingEvents()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasUpcomingEvents - null")
|
||||
void testGetHasUpcomingEventsNull() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getHasUpcomingEvents()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getHasRecentActivities")
|
||||
class GetHasRecentActivitiesTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasRecentActivities - avec activités")
|
||||
void testGetHasRecentActivitiesAvecActivites() {
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.recentActivities(List.of(createRecentActivityDTO()))
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasRecentActivities()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasRecentActivities - liste vide")
|
||||
void testGetHasRecentActivitiesListeVide() {
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.recentActivities(List.of())
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasRecentActivities()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasRecentActivities - null")
|
||||
void testGetHasRecentActivitiesNull() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getHasRecentActivities()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests des préférences utilisateur")
|
||||
class UserPreferencesTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getThemePreference - avec préférence")
|
||||
void testGetThemePreferenceAvecPreference() {
|
||||
Map<String, Object> preferences = new HashMap<>();
|
||||
preferences.put("theme", "dark");
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.userPreferences(preferences)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getThemePreference()).isEqualTo("dark");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getThemePreference - sans préférence")
|
||||
void testGetThemePreferenceSansPreference() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getThemePreference()).isEqualTo("royal_teal");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLanguagePreference - avec préférence")
|
||||
void testGetLanguagePreferenceAvecPreference() {
|
||||
Map<String, Object> preferences = new HashMap<>();
|
||||
preferences.put("language", "en");
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.userPreferences(preferences)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getLanguagePreference()).isEqualTo("en");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getLanguagePreference - sans préférence")
|
||||
void testGetLanguagePreferenceSansPreference() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getLanguagePreference()).isEqualTo("fr");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNotificationsEnabled - avec préférence")
|
||||
void testGetNotificationsEnabledAvecPreference() {
|
||||
Map<String, Object> preferences = new HashMap<>();
|
||||
preferences.put("notifications", false);
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.userPreferences(preferences)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getNotificationsEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getNotificationsEnabled - sans préférence")
|
||||
void testGetNotificationsEnabledSansPreference() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getNotificationsEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getAutoRefreshEnabled - avec préférence")
|
||||
void testGetAutoRefreshEnabledAvecPreference() {
|
||||
Map<String, Object> preferences = new HashMap<>();
|
||||
preferences.put("autoRefresh", false);
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.userPreferences(preferences)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getAutoRefreshEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getAutoRefreshEnabled - sans préférence")
|
||||
void testGetAutoRefreshEnabledSansPreference() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getAutoRefreshEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getRefreshInterval - avec préférence")
|
||||
void testGetRefreshIntervalAvecPreference() {
|
||||
Map<String, Object> preferences = new HashMap<>();
|
||||
preferences.put("refreshInterval", 600);
|
||||
|
||||
DashboardDataDTO dto = DashboardDataDTO.builder()
|
||||
.userPreferences(preferences)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getRefreshInterval()).isEqualTo(600);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getRefreshInterval - sans préférence")
|
||||
void testGetRefreshIntervalSansPreference() {
|
||||
DashboardDataDTO dto = new DashboardDataDTO();
|
||||
|
||||
assertThat(dto.getRefreshInterval()).isEqualTo(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
package dev.lions.unionflow.server.api.dto.dashboard;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@DisplayName("Tests pour DashboardStatsDTO")
|
||||
class DashboardStatsDTOTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test de base - classe peut être instanciée")
|
||||
void testClasseInstanciable() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests de construction")
|
||||
class ConstructionTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructeur par défaut")
|
||||
void testConstructeurParDefaut() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
|
||||
assertThat(dto).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Builder pattern")
|
||||
void testBuilder() {
|
||||
LocalDateTime lastUpdated = LocalDateTime.now();
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalMembers(100)
|
||||
.activeMembers(80)
|
||||
.totalEvents(50)
|
||||
.upcomingEvents(10)
|
||||
.totalContributions(200)
|
||||
.totalContributionAmount(50000.0)
|
||||
.pendingRequests(5)
|
||||
.completedProjects(15)
|
||||
.monthlyGrowth(5.5)
|
||||
.engagementRate(0.75)
|
||||
.lastUpdated(lastUpdated)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getTotalMembers()).isEqualTo(100);
|
||||
assertThat(dto.getActiveMembers()).isEqualTo(80);
|
||||
assertThat(dto.getTotalEvents()).isEqualTo(50);
|
||||
assertThat(dto.getUpcomingEvents()).isEqualTo(10);
|
||||
assertThat(dto.getTotalContributions()).isEqualTo(200);
|
||||
assertThat(dto.getTotalContributionAmount()).isEqualTo(50000.0);
|
||||
assertThat(dto.getPendingRequests()).isEqualTo(5);
|
||||
assertThat(dto.getCompletedProjects()).isEqualTo(15);
|
||||
assertThat(dto.getMonthlyGrowth()).isEqualTo(5.5);
|
||||
assertThat(dto.getEngagementRate()).isEqualTo(0.75);
|
||||
assertThat(dto.getLastUpdated()).isEqualTo(lastUpdated);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getFormattedContributionAmount")
|
||||
class GetFormattedContributionAmountTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getFormattedContributionAmount - montant >= 1M")
|
||||
void testGetFormattedContributionAmountMillions() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalContributionAmount(1_500_000.0)
|
||||
.build();
|
||||
|
||||
// Le format utilise la locale du système, peut être "1,5M" ou "1.5M"
|
||||
assertThat(dto.getFormattedContributionAmount()).matches("1[.,]5M");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getFormattedContributionAmount - montant >= 1K")
|
||||
void testGetFormattedContributionAmountMilliers() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalContributionAmount(5_500.0)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getFormattedContributionAmount()).isEqualTo("6K");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getFormattedContributionAmount - montant < 1K")
|
||||
void testGetFormattedContributionAmountPetit() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalContributionAmount(500.0)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getFormattedContributionAmount()).isEqualTo("500");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getFormattedContributionAmount - null")
|
||||
void testGetFormattedContributionAmountNull() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
|
||||
assertThat(dto.getFormattedContributionAmount()).isEqualTo("0");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getHasGrowth")
|
||||
class GetHasGrowthTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasGrowth - croissance positive")
|
||||
void testGetHasGrowthPositif() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.monthlyGrowth(5.5)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasGrowth()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasGrowth - croissance nulle")
|
||||
void testGetHasGrowthNul() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.monthlyGrowth(0.0)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasGrowth()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasGrowth - croissance négative")
|
||||
void testGetHasGrowthNegatif() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.monthlyGrowth(-2.5)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getHasGrowth()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getHasGrowth - null")
|
||||
void testGetHasGrowthNull() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
|
||||
assertThat(dto.getHasGrowth()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getIsHighEngagement")
|
||||
class GetIsHighEngagementTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getIsHighEngagement - engagement élevé")
|
||||
void testGetIsHighEngagementEleve() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.engagementRate(0.75)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getIsHighEngagement()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIsHighEngagement - engagement à la limite")
|
||||
void testGetIsHighEngagementLimite() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.engagementRate(0.7)
|
||||
.build();
|
||||
|
||||
// 0.7 n'est pas > 0.7, donc false
|
||||
assertThat(dto.getIsHighEngagement()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIsHighEngagement - engagement faible")
|
||||
void testGetIsHighEngagementFaible() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.engagementRate(0.5)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getIsHighEngagement()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getIsHighEngagement - null")
|
||||
void testGetIsHighEngagementNull() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
|
||||
assertThat(dto.getIsHighEngagement()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getInactiveMembers")
|
||||
class GetInactiveMembersTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getInactiveMembers - calcul correct")
|
||||
void testGetInactiveMembers() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalMembers(100)
|
||||
.activeMembers(80)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getInactiveMembers()).isEqualTo(20.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getInactiveMembers - tous actifs")
|
||||
void testGetInactiveMembersTousActifs() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalMembers(100)
|
||||
.activeMembers(100)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getInactiveMembers()).isEqualTo(0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getInactiveMembers - null")
|
||||
void testGetInactiveMembersNull() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
|
||||
assertThat(dto.getInactiveMembers()).isEqualTo(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getActiveMemberPercentage")
|
||||
class GetActiveMemberPercentageTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getActiveMemberPercentage - calcul correct")
|
||||
void testGetActiveMemberPercentage() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalMembers(100)
|
||||
.activeMembers(80)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getActiveMemberPercentage()).isEqualTo(80.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getActiveMemberPercentage - tous actifs")
|
||||
void testGetActiveMemberPercentageTousActifs() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalMembers(100)
|
||||
.activeMembers(100)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getActiveMemberPercentage()).isEqualTo(100.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getActiveMemberPercentage - totalMembers null")
|
||||
void testGetActiveMemberPercentageTotalNull() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.activeMembers(80)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getActiveMemberPercentage()).isEqualTo(0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getActiveMemberPercentage - totalMembers = 0")
|
||||
void testGetActiveMemberPercentageTotalZero() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.totalMembers(0)
|
||||
.activeMembers(0)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getActiveMemberPercentage()).isEqualTo(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Tests getEngagementPercentage")
|
||||
class GetEngagementPercentageTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("getEngagementPercentage - calcul correct")
|
||||
void testGetEngagementPercentage() {
|
||||
DashboardStatsDTO dto = DashboardStatsDTO.builder()
|
||||
.engagementRate(0.75)
|
||||
.build();
|
||||
|
||||
assertThat(dto.getEngagementPercentage()).isEqualTo(75.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getEngagementPercentage - null")
|
||||
void testGetEngagementPercentageNull() {
|
||||
DashboardStatsDTO dto = new DashboardStatsDTO();
|
||||
|
||||
assertThat(dto.getEngagementPercentage()).isEqualTo(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user