diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/admin/AuditLogDTO.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/admin/AuditLogDTO.java new file mode 100644 index 0000000..41a00ba --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/admin/AuditLogDTO.java @@ -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; +} + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/finance/AdhesionDTO.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/finance/AdhesionDTO.java new file mode 100644 index 0000000..3692d11 --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/finance/AdhesionDTO.java @@ -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()); + } +} + + + diff --git a/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/organisation/TypeOrganisationDTO.java b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/organisation/TypeOrganisationDTO.java new file mode 100644 index 0000000..1f4ecc6 --- /dev/null +++ b/unionflow-server-api/src/main/java/dev/lions/unionflow/server/api/dto/organisation/TypeOrganisationDTO.java @@ -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. + * + *
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; +} + +