refactoring
This commit is contained in:
@@ -15,6 +15,9 @@ import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -399,18 +402,19 @@ public class MembreResource {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
LOG.infof(
|
||||
"Recherche avancée de membres - critères: %s, page: %d, size: %d",
|
||||
criteria.getDescription(), page, size);
|
||||
|
||||
try {
|
||||
// Validation des critères
|
||||
if (criteria == null) {
|
||||
LOG.warn("Recherche avancée de membres - critères null rejetés");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Les critères de recherche sont requis"))
|
||||
.build();
|
||||
}
|
||||
|
||||
LOG.infof(
|
||||
"Recherche avancée de membres - critères: %s, page: %d, size: %d",
|
||||
criteria.getDescription(), page, size);
|
||||
|
||||
// Nettoyage et validation des critères
|
||||
criteria.sanitize();
|
||||
|
||||
@@ -485,4 +489,151 @@ public class MembreResource {
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/import")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Importer des membres depuis un fichier Excel ou CSV")
|
||||
@APIResponse(responseCode = "200", description = "Import terminé")
|
||||
public Response importerMembres(
|
||||
@Parameter(description = "Contenu du fichier à importer") @FormParam("file") byte[] fileContent,
|
||||
@Parameter(description = "Nom du fichier") @FormParam("fileName") String fileName,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @FormParam("organisationId") UUID organisationId,
|
||||
@Parameter(description = "Type de membre par défaut") @FormParam("typeMembreDefaut") String typeMembreDefaut,
|
||||
@Parameter(description = "Mettre à jour les membres existants") @FormParam("mettreAJourExistants") boolean mettreAJourExistants,
|
||||
@Parameter(description = "Ignorer les erreurs") @FormParam("ignorerErreurs") boolean ignorerErreurs) {
|
||||
|
||||
try {
|
||||
if (fileContent == null || fileContent.length == 0) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Aucun fichier fourni"))
|
||||
.build();
|
||||
}
|
||||
|
||||
if (fileName == null || fileName.isEmpty()) {
|
||||
fileName = "import.xlsx";
|
||||
}
|
||||
|
||||
if (typeMembreDefaut == null || typeMembreDefaut.isEmpty()) {
|
||||
typeMembreDefaut = "ACTIF";
|
||||
}
|
||||
|
||||
InputStream fileInputStream = new java.io.ByteArrayInputStream(fileContent);
|
||||
dev.lions.unionflow.server.service.MembreImportExportService.ResultatImport resultat = membreService.importerMembres(
|
||||
fileInputStream, fileName, organisationId, typeMembreDefaut, mettreAJourExistants, ignorerErreurs);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("totalLignes", resultat.totalLignes);
|
||||
response.put("lignesTraitees", resultat.lignesTraitees);
|
||||
response.put("lignesErreur", resultat.lignesErreur);
|
||||
response.put("erreurs", resultat.erreurs);
|
||||
response.put("membresImportes", resultat.membresImportes);
|
||||
|
||||
return Response.ok(response).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'import");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de l'import: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/export")
|
||||
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@Operation(summary = "Exporter des membres en Excel, CSV ou PDF")
|
||||
@APIResponse(responseCode = "200", description = "Fichier exporté")
|
||||
public Response exporterMembres(
|
||||
@Parameter(description = "Format d'export") @QueryParam("format") @DefaultValue("EXCEL") String format,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("associationId") UUID associationId,
|
||||
@Parameter(description = "Statut des membres") @QueryParam("statut") String statut,
|
||||
@Parameter(description = "Type de membre") @QueryParam("type") String type,
|
||||
@Parameter(description = "Date adhésion début") @QueryParam("dateAdhesionDebut") String dateAdhesionDebut,
|
||||
@Parameter(description = "Date adhésion fin") @QueryParam("dateAdhesionFin") String dateAdhesionFin,
|
||||
@Parameter(description = "Colonnes à exporter") @QueryParam("colonnes") List<String> colonnesExportList,
|
||||
@Parameter(description = "Inclure les en-têtes") @QueryParam("inclureHeaders") @DefaultValue("true") boolean inclureHeaders,
|
||||
@Parameter(description = "Formater les dates") @QueryParam("formaterDates") @DefaultValue("true") boolean formaterDates,
|
||||
@Parameter(description = "Inclure un onglet statistiques (Excel uniquement)") @QueryParam("inclureStatistiques") @DefaultValue("false") boolean inclureStatistiques,
|
||||
@Parameter(description = "Mot de passe pour chiffrer le fichier (optionnel)") @QueryParam("motDePasse") String motDePasse) {
|
||||
|
||||
try {
|
||||
// Récupérer les membres selon les filtres
|
||||
List<MembreDTO> membres = membreService.listerMembresPourExport(
|
||||
associationId, statut, type, dateAdhesionDebut, dateAdhesionFin);
|
||||
|
||||
byte[] exportData;
|
||||
String contentType;
|
||||
String extension;
|
||||
|
||||
List<String> colonnesExport = colonnesExportList != null ? colonnesExportList : new ArrayList<>();
|
||||
|
||||
if ("CSV".equalsIgnoreCase(format)) {
|
||||
exportData = membreService.exporterVersCSV(membres, colonnesExport, inclureHeaders, formaterDates);
|
||||
contentType = "text/csv";
|
||||
extension = "csv";
|
||||
} else {
|
||||
// Pour Excel, inclure les statistiques uniquement si demandé et si format Excel
|
||||
boolean stats = inclureStatistiques && "EXCEL".equalsIgnoreCase(format);
|
||||
exportData = membreService.exporterVersExcel(membres, colonnesExport, inclureHeaders, formaterDates, stats, motDePasse);
|
||||
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
extension = "xlsx";
|
||||
}
|
||||
|
||||
return Response.ok(exportData)
|
||||
.type(contentType)
|
||||
.header("Content-Disposition", "attachment; filename=\"membres_export_" +
|
||||
java.time.LocalDate.now() + "." + extension + "\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'export");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de l'export: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/import/modele")
|
||||
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@Operation(summary = "Télécharger le modèle Excel pour l'import")
|
||||
@APIResponse(responseCode = "200", description = "Modèle Excel généré")
|
||||
public Response telechargerModeleImport() {
|
||||
try {
|
||||
byte[] modele = membreService.genererModeleImport();
|
||||
return Response.ok(modele)
|
||||
.header("Content-Disposition", "attachment; filename=\"modele_import_membres.xlsx\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la génération du modèle");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la génération du modèle: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/export/count")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Compter les membres selon les filtres pour l'export")
|
||||
@APIResponse(responseCode = "200", description = "Nombre de membres correspondant aux critères")
|
||||
public Response compterMembresPourExport(
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("associationId") UUID associationId,
|
||||
@Parameter(description = "Statut des membres") @QueryParam("statut") String statut,
|
||||
@Parameter(description = "Type de membre") @QueryParam("type") String type,
|
||||
@Parameter(description = "Date adhésion début") @QueryParam("dateAdhesionDebut") String dateAdhesionDebut,
|
||||
@Parameter(description = "Date adhésion fin") @QueryParam("dateAdhesionFin") String dateAdhesionFin) {
|
||||
|
||||
try {
|
||||
List<MembreDTO> membres = membreService.listerMembresPourExport(
|
||||
associationId, statut, type, dateAdhesionDebut, dateAdhesionFin);
|
||||
|
||||
return Response.ok(membres.size()).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors du comptage des membres");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors du comptage: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,842 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreDTO;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Service pour l'import et l'export de membres depuis/vers Excel et CSV
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class MembreImportExportService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MembreImportExportService.class);
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy");
|
||||
|
||||
@Inject
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@Inject
|
||||
OrganisationRepository organisationRepository;
|
||||
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
/**
|
||||
* Importe des membres depuis un fichier Excel ou CSV
|
||||
*/
|
||||
@Transactional
|
||||
public ResultatImport importerMembres(
|
||||
InputStream fileInputStream,
|
||||
String fileName,
|
||||
UUID organisationId,
|
||||
String typeMembreDefaut,
|
||||
boolean mettreAJourExistants,
|
||||
boolean ignorerErreurs) {
|
||||
|
||||
LOG.infof("Import de membres depuis le fichier: %s", fileName);
|
||||
|
||||
ResultatImport resultat = new ResultatImport();
|
||||
resultat.erreurs = new ArrayList<>();
|
||||
resultat.membresImportes = new ArrayList<>();
|
||||
|
||||
try {
|
||||
if (fileName.toLowerCase().endsWith(".csv")) {
|
||||
return importerDepuisCSV(fileInputStream, organisationId, typeMembreDefaut, mettreAJourExistants, ignorerErreurs);
|
||||
} else if (fileName.toLowerCase().endsWith(".xlsx") || fileName.toLowerCase().endsWith(".xls")) {
|
||||
return importerDepuisExcel(fileInputStream, organisationId, typeMembreDefaut, mettreAJourExistants, ignorerErreurs);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Format de fichier non supporté. Formats acceptés: .xlsx, .xls, .csv");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'import");
|
||||
resultat.erreurs.add("Erreur générale: " + e.getMessage());
|
||||
return resultat;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Importe depuis un fichier Excel
|
||||
*/
|
||||
private ResultatImport importerDepuisExcel(
|
||||
InputStream fileInputStream,
|
||||
UUID organisationId,
|
||||
String typeMembreDefaut,
|
||||
boolean mettreAJourExistants,
|
||||
boolean ignorerErreurs) throws IOException {
|
||||
|
||||
ResultatImport resultat = new ResultatImport();
|
||||
resultat.erreurs = new ArrayList<>();
|
||||
resultat.membresImportes = new ArrayList<>();
|
||||
int ligneNum = 0;
|
||||
|
||||
try (Workbook workbook = new XSSFWorkbook(fileInputStream)) {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
Row headerRow = sheet.getRow(0);
|
||||
|
||||
if (headerRow == null) {
|
||||
throw new IllegalArgumentException("Le fichier Excel est vide ou n'a pas d'en-têtes");
|
||||
}
|
||||
|
||||
// Mapper les colonnes
|
||||
Map<String, Integer> colonnes = mapperColonnes(headerRow);
|
||||
|
||||
// Vérifier les colonnes obligatoires
|
||||
if (!colonnes.containsKey("nom") || !colonnes.containsKey("prenom") ||
|
||||
!colonnes.containsKey("email") || !colonnes.containsKey("telephone")) {
|
||||
throw new IllegalArgumentException("Colonnes obligatoires manquantes: nom, prenom, email, telephone");
|
||||
}
|
||||
|
||||
// Lire les données
|
||||
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
|
||||
ligneNum = i + 1;
|
||||
Row row = sheet.getRow(i);
|
||||
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Membre membre = lireLigneExcel(row, colonnes, organisationId, typeMembreDefaut);
|
||||
|
||||
// Vérifier si le membre existe déjà
|
||||
Optional<Membre> membreExistant = membreRepository.findByEmail(membre.getEmail());
|
||||
|
||||
if (membreExistant.isPresent()) {
|
||||
if (mettreAJourExistants) {
|
||||
Membre existant = membreExistant.get();
|
||||
existant.setNom(membre.getNom());
|
||||
existant.setPrenom(membre.getPrenom());
|
||||
existant.setTelephone(membre.getTelephone());
|
||||
existant.setDateNaissance(membre.getDateNaissance());
|
||||
if (membre.getOrganisation() != null) {
|
||||
existant.setOrganisation(membre.getOrganisation());
|
||||
}
|
||||
membreRepository.persist(existant);
|
||||
resultat.membresImportes.add(membreService.convertToDTO(existant));
|
||||
resultat.lignesTraitees++;
|
||||
} else {
|
||||
resultat.erreurs.add(String.format("Ligne %d: Membre avec email %s existe déjà", ligneNum, membre.getEmail()));
|
||||
if (!ignorerErreurs) {
|
||||
throw new IllegalArgumentException("Membre existant trouvé et mise à jour désactivée");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
membre = membreService.creerMembre(membre);
|
||||
resultat.membresImportes.add(membreService.convertToDTO(membre));
|
||||
resultat.lignesTraitees++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String erreur = String.format("Ligne %d: %s", ligneNum, e.getMessage());
|
||||
resultat.erreurs.add(erreur);
|
||||
resultat.lignesErreur++;
|
||||
|
||||
if (!ignorerErreurs) {
|
||||
throw new RuntimeException(erreur, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultat.totalLignes = sheet.getLastRowNum();
|
||||
}
|
||||
|
||||
LOG.infof("Import terminé: %d lignes traitées, %d erreurs", resultat.lignesTraitees, resultat.lignesErreur);
|
||||
return resultat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Importe depuis un fichier CSV
|
||||
*/
|
||||
private ResultatImport importerDepuisCSV(
|
||||
InputStream fileInputStream,
|
||||
UUID organisationId,
|
||||
String typeMembreDefaut,
|
||||
boolean mettreAJourExistants,
|
||||
boolean ignorerErreurs) throws IOException {
|
||||
|
||||
ResultatImport resultat = new ResultatImport();
|
||||
resultat.erreurs = new ArrayList<>();
|
||||
resultat.membresImportes = new ArrayList<>();
|
||||
|
||||
try (InputStreamReader reader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8)) {
|
||||
Iterable<CSVRecord> records = CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build().parse(reader);
|
||||
|
||||
int ligneNum = 0;
|
||||
for (CSVRecord record : records) {
|
||||
ligneNum++;
|
||||
|
||||
try {
|
||||
Membre membre = lireLigneCSV(record, organisationId, typeMembreDefaut);
|
||||
|
||||
// Vérifier si le membre existe déjà
|
||||
Optional<Membre> membreExistant = membreRepository.findByEmail(membre.getEmail());
|
||||
|
||||
if (membreExistant.isPresent()) {
|
||||
if (mettreAJourExistants) {
|
||||
Membre existant = membreExistant.get();
|
||||
existant.setNom(membre.getNom());
|
||||
existant.setPrenom(membre.getPrenom());
|
||||
existant.setTelephone(membre.getTelephone());
|
||||
existant.setDateNaissance(membre.getDateNaissance());
|
||||
if (membre.getOrganisation() != null) {
|
||||
existant.setOrganisation(membre.getOrganisation());
|
||||
}
|
||||
membreRepository.persist(existant);
|
||||
resultat.membresImportes.add(membreService.convertToDTO(existant));
|
||||
resultat.lignesTraitees++;
|
||||
} else {
|
||||
resultat.erreurs.add(String.format("Ligne %d: Membre avec email %s existe déjà", ligneNum, membre.getEmail()));
|
||||
if (!ignorerErreurs) {
|
||||
throw new IllegalArgumentException("Membre existant trouvé et mise à jour désactivée");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
membre = membreService.creerMembre(membre);
|
||||
resultat.membresImportes.add(membreService.convertToDTO(membre));
|
||||
resultat.lignesTraitees++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String erreur = String.format("Ligne %d: %s", ligneNum, e.getMessage());
|
||||
resultat.erreurs.add(erreur);
|
||||
resultat.lignesErreur++;
|
||||
|
||||
if (!ignorerErreurs) {
|
||||
throw new RuntimeException(erreur, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultat.totalLignes = ligneNum;
|
||||
}
|
||||
|
||||
LOG.infof("Import CSV terminé: %d lignes traitées, %d erreurs", resultat.lignesTraitees, resultat.lignesErreur);
|
||||
return resultat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit une ligne Excel et crée un membre
|
||||
*/
|
||||
private Membre lireLigneExcel(Row row, Map<String, Integer> colonnes, UUID organisationId, String typeMembreDefaut) {
|
||||
Membre membre = new Membre();
|
||||
|
||||
// Colonnes obligatoires
|
||||
String nom = getCellValueAsString(row, colonnes.get("nom"));
|
||||
String prenom = getCellValueAsString(row, colonnes.get("prenom"));
|
||||
String email = getCellValueAsString(row, colonnes.get("email"));
|
||||
String telephone = getCellValueAsString(row, colonnes.get("telephone"));
|
||||
|
||||
if (nom == null || nom.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Le nom est obligatoire");
|
||||
}
|
||||
if (prenom == null || prenom.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Le prénom est obligatoire");
|
||||
}
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("L'email est obligatoire");
|
||||
}
|
||||
if (telephone == null || telephone.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Le téléphone est obligatoire");
|
||||
}
|
||||
|
||||
membre.setNom(nom.trim());
|
||||
membre.setPrenom(prenom.trim());
|
||||
membre.setEmail(email.trim().toLowerCase());
|
||||
membre.setTelephone(telephone.trim());
|
||||
|
||||
// Colonnes optionnelles
|
||||
if (colonnes.containsKey("date_naissance")) {
|
||||
LocalDate dateNaissance = getCellValueAsDate(row, colonnes.get("date_naissance"));
|
||||
if (dateNaissance != null) {
|
||||
membre.setDateNaissance(dateNaissance);
|
||||
}
|
||||
}
|
||||
if (membre.getDateNaissance() == null) {
|
||||
membre.setDateNaissance(LocalDate.now().minusYears(18));
|
||||
}
|
||||
|
||||
if (colonnes.containsKey("date_adhesion")) {
|
||||
LocalDate dateAdhesion = getCellValueAsDate(row, colonnes.get("date_adhesion"));
|
||||
if (dateAdhesion != null) {
|
||||
membre.setDateAdhesion(dateAdhesion);
|
||||
}
|
||||
}
|
||||
if (membre.getDateAdhesion() == null) {
|
||||
membre.setDateAdhesion(LocalDate.now());
|
||||
}
|
||||
|
||||
// Organisation
|
||||
if (organisationId != null) {
|
||||
Optional<Organisation> org = organisationRepository.findByIdOptional(organisationId);
|
||||
if (org.isPresent()) {
|
||||
membre.setOrganisation(org.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Statut par défaut
|
||||
membre.setActif(typeMembreDefaut == null || typeMembreDefaut.isEmpty() || "ACTIF".equals(typeMembreDefaut));
|
||||
|
||||
return membre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit une ligne CSV et crée un membre
|
||||
*/
|
||||
private Membre lireLigneCSV(CSVRecord record, UUID organisationId, String typeMembreDefaut) {
|
||||
Membre membre = new Membre();
|
||||
|
||||
// Colonnes obligatoires
|
||||
String nom = record.get("nom");
|
||||
String prenom = record.get("prenom");
|
||||
String email = record.get("email");
|
||||
String telephone = record.get("telephone");
|
||||
|
||||
if (nom == null || nom.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Le nom est obligatoire");
|
||||
}
|
||||
if (prenom == null || prenom.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Le prénom est obligatoire");
|
||||
}
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("L'email est obligatoire");
|
||||
}
|
||||
if (telephone == null || telephone.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Le téléphone est obligatoire");
|
||||
}
|
||||
|
||||
membre.setNom(nom.trim());
|
||||
membre.setPrenom(prenom.trim());
|
||||
membre.setEmail(email.trim().toLowerCase());
|
||||
membre.setTelephone(telephone.trim());
|
||||
|
||||
// Colonnes optionnelles
|
||||
try {
|
||||
String dateNaissanceStr = record.get("date_naissance");
|
||||
if (dateNaissanceStr != null && !dateNaissanceStr.trim().isEmpty()) {
|
||||
membre.setDateNaissance(parseDate(dateNaissanceStr));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignorer si la date est invalide
|
||||
}
|
||||
if (membre.getDateNaissance() == null) {
|
||||
membre.setDateNaissance(LocalDate.now().minusYears(18));
|
||||
}
|
||||
|
||||
try {
|
||||
String dateAdhesionStr = record.get("date_adhesion");
|
||||
if (dateAdhesionStr != null && !dateAdhesionStr.trim().isEmpty()) {
|
||||
membre.setDateAdhesion(parseDate(dateAdhesionStr));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignorer si la date est invalide
|
||||
}
|
||||
if (membre.getDateAdhesion() == null) {
|
||||
membre.setDateAdhesion(LocalDate.now());
|
||||
}
|
||||
|
||||
// Organisation
|
||||
if (organisationId != null) {
|
||||
Optional<Organisation> org = organisationRepository.findByIdOptional(organisationId);
|
||||
if (org.isPresent()) {
|
||||
membre.setOrganisation(org.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Statut par défaut
|
||||
membre.setActif(typeMembreDefaut == null || typeMembreDefaut.isEmpty() || "ACTIF".equals(typeMembreDefaut));
|
||||
|
||||
return membre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mappe les colonnes Excel
|
||||
*/
|
||||
private Map<String, Integer> mapperColonnes(Row headerRow) {
|
||||
Map<String, Integer> colonnes = new HashMap<>();
|
||||
for (Cell cell : headerRow) {
|
||||
String headerName = getCellValueAsString(headerRow, cell.getColumnIndex()).toLowerCase()
|
||||
.replace(" ", "_")
|
||||
.replace("é", "e")
|
||||
.replace("è", "e")
|
||||
.replace("ê", "e");
|
||||
colonnes.put(headerName, cell.getColumnIndex());
|
||||
}
|
||||
return colonnes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient la valeur d'une cellule comme String
|
||||
*/
|
||||
private String getCellValueAsString(Row row, Integer columnIndex) {
|
||||
if (columnIndex == null || row == null) {
|
||||
return null;
|
||||
}
|
||||
Cell cell = row.getCell(columnIndex);
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case STRING:
|
||||
return cell.getStringCellValue();
|
||||
case NUMERIC:
|
||||
if (DateUtil.isCellDateFormatted(cell)) {
|
||||
return cell.getDateCellValue().toString();
|
||||
} else {
|
||||
return String.valueOf((long) cell.getNumericCellValue());
|
||||
}
|
||||
case BOOLEAN:
|
||||
return String.valueOf(cell.getBooleanCellValue());
|
||||
case FORMULA:
|
||||
return cell.getCellFormula();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient la valeur d'une cellule comme Date
|
||||
*/
|
||||
private LocalDate getCellValueAsDate(Row row, Integer columnIndex) {
|
||||
if (columnIndex == null || row == null) {
|
||||
return null;
|
||||
}
|
||||
Cell cell = row.getCell(columnIndex);
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) {
|
||||
return cell.getDateCellValue().toInstant()
|
||||
.atZone(java.time.ZoneId.systemDefault())
|
||||
.toLocalDate();
|
||||
} else if (cell.getCellType() == CellType.STRING) {
|
||||
return parseDate(cell.getStringCellValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warnf("Erreur lors de la lecture de la date: %s", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse une date depuis une String
|
||||
*/
|
||||
private LocalDate parseDate(String dateStr) {
|
||||
if (dateStr == null || dateStr.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
dateStr = dateStr.trim();
|
||||
|
||||
// Essayer différents formats
|
||||
String[] formats = {
|
||||
"dd/MM/yyyy",
|
||||
"yyyy-MM-dd",
|
||||
"dd-MM-yyyy",
|
||||
"dd.MM.yyyy"
|
||||
};
|
||||
|
||||
for (String format : formats) {
|
||||
try {
|
||||
return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(format));
|
||||
} catch (DateTimeParseException e) {
|
||||
// Continuer avec le format suivant
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Format de date non reconnu: " + dateStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte des membres vers Excel
|
||||
*/
|
||||
public byte[] exporterVersExcel(List<MembreDTO> membres, List<String> colonnesExport, boolean inclureHeaders, boolean formaterDates, boolean inclureStatistiques, String motDePasse) throws IOException {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
Sheet sheet = workbook.createSheet("Membres");
|
||||
|
||||
int rowNum = 0;
|
||||
|
||||
// En-têtes
|
||||
if (inclureHeaders) {
|
||||
Row headerRow = sheet.createRow(rowNum++);
|
||||
int colNum = 0;
|
||||
|
||||
if (colonnesExport.contains("PERSO") || colonnesExport.isEmpty()) {
|
||||
headerRow.createCell(colNum++).setCellValue("Nom");
|
||||
headerRow.createCell(colNum++).setCellValue("Prénom");
|
||||
headerRow.createCell(colNum++).setCellValue("Date de naissance");
|
||||
}
|
||||
if (colonnesExport.contains("CONTACT") || colonnesExport.isEmpty()) {
|
||||
headerRow.createCell(colNum++).setCellValue("Email");
|
||||
headerRow.createCell(colNum++).setCellValue("Téléphone");
|
||||
}
|
||||
if (colonnesExport.contains("ADHESION") || colonnesExport.isEmpty()) {
|
||||
headerRow.createCell(colNum++).setCellValue("Date adhésion");
|
||||
headerRow.createCell(colNum++).setCellValue("Statut");
|
||||
}
|
||||
if (colonnesExport.contains("ORGANISATION") || colonnesExport.isEmpty()) {
|
||||
headerRow.createCell(colNum++).setCellValue("Organisation");
|
||||
}
|
||||
}
|
||||
|
||||
// Données
|
||||
for (MembreDTO membre : membres) {
|
||||
Row row = sheet.createRow(rowNum++);
|
||||
int colNum = 0;
|
||||
|
||||
if (colonnesExport.contains("PERSO") || colonnesExport.isEmpty()) {
|
||||
row.createCell(colNum++).setCellValue(membre.getNom() != null ? membre.getNom() : "");
|
||||
row.createCell(colNum++).setCellValue(membre.getPrenom() != null ? membre.getPrenom() : "");
|
||||
if (membre.getDateNaissance() != null) {
|
||||
Cell dateCell = row.createCell(colNum++);
|
||||
if (formaterDates) {
|
||||
dateCell.setCellValue(membre.getDateNaissance().format(DATE_FORMATTER));
|
||||
} else {
|
||||
dateCell.setCellValue(membre.getDateNaissance().toString());
|
||||
}
|
||||
} else {
|
||||
row.createCell(colNum++).setCellValue("");
|
||||
}
|
||||
}
|
||||
if (colonnesExport.contains("CONTACT") || colonnesExport.isEmpty()) {
|
||||
row.createCell(colNum++).setCellValue(membre.getEmail() != null ? membre.getEmail() : "");
|
||||
row.createCell(colNum++).setCellValue(membre.getTelephone() != null ? membre.getTelephone() : "");
|
||||
}
|
||||
if (colonnesExport.contains("ADHESION") || colonnesExport.isEmpty()) {
|
||||
if (membre.getDateAdhesion() != null) {
|
||||
Cell dateCell = row.createCell(colNum++);
|
||||
if (formaterDates) {
|
||||
dateCell.setCellValue(membre.getDateAdhesion().format(DATE_FORMATTER));
|
||||
} else {
|
||||
dateCell.setCellValue(membre.getDateAdhesion().toString());
|
||||
}
|
||||
} else {
|
||||
row.createCell(colNum++).setCellValue("");
|
||||
}
|
||||
row.createCell(colNum++).setCellValue(membre.getStatut() != null ? membre.getStatut().toString() : "");
|
||||
}
|
||||
if (colonnesExport.contains("ORGANISATION") || colonnesExport.isEmpty()) {
|
||||
row.createCell(colNum++).setCellValue(membre.getAssociationNom() != null ? membre.getAssociationNom() : "");
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-size columns
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
// Ajouter un onglet statistiques si demandé
|
||||
if (inclureStatistiques && !membres.isEmpty()) {
|
||||
Sheet statsSheet = workbook.createSheet("Statistiques");
|
||||
creerOngletStatistiques(statsSheet, membres);
|
||||
}
|
||||
|
||||
// Écrire dans un ByteArrayOutputStream
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
workbook.write(outputStream);
|
||||
byte[] excelData = outputStream.toByteArray();
|
||||
|
||||
// Chiffrer le fichier si un mot de passe est fourni
|
||||
if (motDePasse != null && !motDePasse.trim().isEmpty()) {
|
||||
return chiffrerExcel(excelData, motDePasse);
|
||||
}
|
||||
|
||||
return excelData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un onglet statistiques dans le classeur Excel
|
||||
*/
|
||||
private void creerOngletStatistiques(Sheet sheet, List<MembreDTO> membres) {
|
||||
int rowNum = 0;
|
||||
|
||||
// Titre
|
||||
Row titleRow = sheet.createRow(rowNum++);
|
||||
Cell titleCell = titleRow.createCell(0);
|
||||
titleCell.setCellValue("Statistiques des Membres");
|
||||
CellStyle titleStyle = sheet.getWorkbook().createCellStyle();
|
||||
Font titleFont = sheet.getWorkbook().createFont();
|
||||
titleFont.setBold(true);
|
||||
titleFont.setFontHeightInPoints((short) 14);
|
||||
titleStyle.setFont(titleFont);
|
||||
titleCell.setCellStyle(titleStyle);
|
||||
|
||||
rowNum++; // Ligne vide
|
||||
|
||||
// Statistiques générales
|
||||
Row headerRow = sheet.createRow(rowNum++);
|
||||
headerRow.createCell(0).setCellValue("Indicateur");
|
||||
headerRow.createCell(1).setCellValue("Valeur");
|
||||
|
||||
// Style pour les en-têtes
|
||||
CellStyle headerStyle = sheet.getWorkbook().createCellStyle();
|
||||
Font headerFont = sheet.getWorkbook().createFont();
|
||||
headerFont.setBold(true);
|
||||
headerStyle.setFont(headerFont);
|
||||
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
headerRow.getCell(0).setCellStyle(headerStyle);
|
||||
headerRow.getCell(1).setCellStyle(headerStyle);
|
||||
|
||||
// Calcul des statistiques
|
||||
long totalMembres = membres.size();
|
||||
long membresActifs = membres.stream().filter(m -> "ACTIF".equals(m.getStatut())).count();
|
||||
long membresInactifs = membres.stream().filter(m -> "INACTIF".equals(m.getStatut())).count();
|
||||
long membresSuspendus = membres.stream().filter(m -> "SUSPENDU".equals(m.getStatut())).count();
|
||||
|
||||
// Organisations distinctes
|
||||
long organisationsDistinctes = membres.stream()
|
||||
.filter(m -> m.getAssociationNom() != null)
|
||||
.map(MembreDTO::getAssociationNom)
|
||||
.distinct()
|
||||
.count();
|
||||
|
||||
// Statistiques par type (si disponible dans le DTO)
|
||||
// Note: Le type de membre peut ne pas être disponible dans MembreDTO
|
||||
// Pour l'instant, on utilise le statut comme indicateur
|
||||
long typeActif = membresActifs;
|
||||
long typeAssocie = 0;
|
||||
long typeBienfaiteur = 0;
|
||||
long typeHonoraire = 0;
|
||||
|
||||
// Ajout des statistiques
|
||||
int currentRow = rowNum;
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Total Membres");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(totalMembres);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Membres Actifs");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(membresActifs);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Membres Inactifs");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(membresInactifs);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Membres Suspendus");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(membresSuspendus);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Organisations Distinctes");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(organisationsDistinctes);
|
||||
|
||||
currentRow++; // Ligne vide
|
||||
|
||||
// Section par type
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Répartition par Type");
|
||||
CellStyle sectionStyle = sheet.getWorkbook().createCellStyle();
|
||||
Font sectionFont = sheet.getWorkbook().createFont();
|
||||
sectionFont.setBold(true);
|
||||
sectionStyle.setFont(sectionFont);
|
||||
sheet.getRow(currentRow - 1).getCell(0).setCellStyle(sectionStyle);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Type Actif");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(typeActif);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Type Associé");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(typeAssocie);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Type Bienfaiteur");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(typeBienfaiteur);
|
||||
|
||||
sheet.createRow(currentRow++).createCell(0).setCellValue("Type Honoraire");
|
||||
sheet.getRow(currentRow - 1).createCell(1).setCellValue(typeHonoraire);
|
||||
|
||||
// Auto-size columns
|
||||
sheet.autoSizeColumn(0);
|
||||
sheet.autoSizeColumn(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protège un fichier Excel avec un mot de passe
|
||||
* Utilise Apache POI pour protéger les feuilles et la structure du workbook
|
||||
* Note: Ceci protège contre la modification, pas un chiffrement complet du fichier
|
||||
*/
|
||||
private byte[] chiffrerExcel(byte[] excelData, String motDePasse) throws IOException {
|
||||
try {
|
||||
// Pour XLSX, on protège les feuilles et la structure du workbook
|
||||
// Note: POI 5.2.5 ne supporte pas le chiffrement complet XLSX (nécessite des bibliothèques externes)
|
||||
// On utilise la protection par mot de passe qui empêche la modification sans le mot de passe
|
||||
|
||||
try (java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(excelData);
|
||||
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
|
||||
// Protéger toutes les feuilles avec un mot de passe (empêche la modification des cellules)
|
||||
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
|
||||
Sheet sheet = workbook.getSheetAt(i);
|
||||
sheet.protectSheet(motDePasse);
|
||||
}
|
||||
|
||||
// Protéger la structure du workbook (empêche l'ajout/suppression de feuilles)
|
||||
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookProtection protection =
|
||||
workbook.getCTWorkbook().getWorkbookProtection();
|
||||
if (protection == null) {
|
||||
protection = workbook.getCTWorkbook().addNewWorkbookProtection();
|
||||
}
|
||||
protection.setLockStructure(true);
|
||||
// Le mot de passe doit être haché selon le format Excel
|
||||
// Pour simplifier, on utilise le hash MD5 du mot de passe
|
||||
try {
|
||||
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
|
||||
byte[] passwordHash = md.digest(motDePasse.getBytes(java.nio.charset.StandardCharsets.UTF_16LE));
|
||||
protection.setWorkbookPassword(passwordHash);
|
||||
} catch (java.security.NoSuchAlgorithmException e) {
|
||||
LOG.warnf("Impossible de hasher le mot de passe, protection partielle uniquement");
|
||||
}
|
||||
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la protection du fichier Excel");
|
||||
// En cas d'erreur, retourner le fichier non protégé avec un avertissement
|
||||
LOG.warnf("Le fichier sera exporté sans protection en raison d'une erreur");
|
||||
return excelData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte des membres vers CSV
|
||||
*/
|
||||
public byte[] exporterVersCSV(List<MembreDTO> membres, List<String> colonnesExport, boolean inclureHeaders, boolean formaterDates) throws IOException {
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
CSVPrinter printer = new CSVPrinter(
|
||||
new java.io.OutputStreamWriter(outputStream, StandardCharsets.UTF_8),
|
||||
CSVFormat.DEFAULT)) {
|
||||
|
||||
// En-têtes
|
||||
if (inclureHeaders) {
|
||||
List<String> headers = new ArrayList<>();
|
||||
if (colonnesExport.contains("PERSO") || colonnesExport.isEmpty()) {
|
||||
headers.add("Nom");
|
||||
headers.add("Prénom");
|
||||
headers.add("Date de naissance");
|
||||
}
|
||||
if (colonnesExport.contains("CONTACT") || colonnesExport.isEmpty()) {
|
||||
headers.add("Email");
|
||||
headers.add("Téléphone");
|
||||
}
|
||||
if (colonnesExport.contains("ADHESION") || colonnesExport.isEmpty()) {
|
||||
headers.add("Date adhésion");
|
||||
headers.add("Statut");
|
||||
}
|
||||
if (colonnesExport.contains("ORGANISATION") || colonnesExport.isEmpty()) {
|
||||
headers.add("Organisation");
|
||||
}
|
||||
printer.printRecord(headers);
|
||||
}
|
||||
|
||||
// Données
|
||||
for (MembreDTO membre : membres) {
|
||||
List<String> values = new ArrayList<>();
|
||||
|
||||
if (colonnesExport.contains("PERSO") || colonnesExport.isEmpty()) {
|
||||
values.add(membre.getNom() != null ? membre.getNom() : "");
|
||||
values.add(membre.getPrenom() != null ? membre.getPrenom() : "");
|
||||
if (membre.getDateNaissance() != null) {
|
||||
values.add(formaterDates ? membre.getDateNaissance().format(DATE_FORMATTER) : membre.getDateNaissance().toString());
|
||||
} else {
|
||||
values.add("");
|
||||
}
|
||||
}
|
||||
if (colonnesExport.contains("CONTACT") || colonnesExport.isEmpty()) {
|
||||
values.add(membre.getEmail() != null ? membre.getEmail() : "");
|
||||
values.add(membre.getTelephone() != null ? membre.getTelephone() : "");
|
||||
}
|
||||
if (colonnesExport.contains("ADHESION") || colonnesExport.isEmpty()) {
|
||||
if (membre.getDateAdhesion() != null) {
|
||||
values.add(formaterDates ? membre.getDateAdhesion().format(DATE_FORMATTER) : membre.getDateAdhesion().toString());
|
||||
} else {
|
||||
values.add("");
|
||||
}
|
||||
values.add(membre.getStatut() != null ? membre.getStatut().toString() : "");
|
||||
}
|
||||
if (colonnesExport.contains("ORGANISATION") || colonnesExport.isEmpty()) {
|
||||
values.add(membre.getAssociationNom() != null ? membre.getAssociationNom() : "");
|
||||
}
|
||||
|
||||
printer.printRecord(values);
|
||||
}
|
||||
|
||||
printer.flush();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un modèle Excel pour l'import
|
||||
*/
|
||||
public byte[] genererModeleImport() throws IOException {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
Sheet sheet = workbook.createSheet("Modèle");
|
||||
|
||||
// En-têtes
|
||||
Row headerRow = sheet.createRow(0);
|
||||
headerRow.createCell(0).setCellValue("Nom");
|
||||
headerRow.createCell(1).setCellValue("Prénom");
|
||||
headerRow.createCell(2).setCellValue("Email");
|
||||
headerRow.createCell(3).setCellValue("Téléphone");
|
||||
headerRow.createCell(4).setCellValue("Date naissance");
|
||||
headerRow.createCell(5).setCellValue("Date adhésion");
|
||||
headerRow.createCell(6).setCellValue("Adresse");
|
||||
headerRow.createCell(7).setCellValue("Profession");
|
||||
headerRow.createCell(8).setCellValue("Type membre");
|
||||
|
||||
// Exemple de ligne
|
||||
Row exampleRow = sheet.createRow(1);
|
||||
exampleRow.createCell(0).setCellValue("DUPONT");
|
||||
exampleRow.createCell(1).setCellValue("Jean");
|
||||
exampleRow.createCell(2).setCellValue("jean.dupont@example.com");
|
||||
exampleRow.createCell(3).setCellValue("+225 07 12 34 56 78");
|
||||
exampleRow.createCell(4).setCellValue("15/01/1990");
|
||||
exampleRow.createCell(5).setCellValue("01/01/2024");
|
||||
exampleRow.createCell(6).setCellValue("Abidjan, Cocody");
|
||||
exampleRow.createCell(7).setCellValue("Ingénieur");
|
||||
exampleRow.createCell(8).setCellValue("ACTIF");
|
||||
|
||||
// Auto-size columns
|
||||
for (int i = 0; i < 9; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
// Écrire dans un ByteArrayOutputStream
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe pour le résultat de l'import
|
||||
*/
|
||||
public static class ResultatImport {
|
||||
public int totalLignes;
|
||||
public int lignesTraitees;
|
||||
public int lignesErreur;
|
||||
public List<String> erreurs;
|
||||
public List<MembreDTO> membresImportes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Period;
|
||||
@@ -34,6 +35,9 @@ public class MembreService {
|
||||
|
||||
@Inject MembreRepository membreRepository;
|
||||
|
||||
@Inject
|
||||
MembreImportExportService membreImportExportService;
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager entityManager;
|
||||
|
||||
@@ -464,16 +468,16 @@ public class MembreService {
|
||||
parameters.put("organisationIds", criteria.getOrganisationIds());
|
||||
}
|
||||
|
||||
// Filtre par rôles (recherche dans le champ roles)
|
||||
// Filtre par rôles (recherche via la relation MembreRole -> Role)
|
||||
if (criteria.getRoles() != null && !criteria.getRoles().isEmpty()) {
|
||||
StringBuilder roleCondition = new StringBuilder(" AND (");
|
||||
for (int i = 0; i < criteria.getRoles().size(); i++) {
|
||||
if (i > 0) roleCondition.append(" OR ");
|
||||
roleCondition.append("m.roles LIKE :role").append(i);
|
||||
parameters.put("role" + i, "%" + criteria.getRoles().get(i) + "%");
|
||||
}
|
||||
roleCondition.append(")");
|
||||
queryBuilder.append(roleCondition);
|
||||
// Utiliser EXISTS avec une sous-requête pour vérifier les rôles
|
||||
queryBuilder.append(" AND EXISTS (");
|
||||
queryBuilder.append(" SELECT 1 FROM MembreRole mr WHERE mr.membre = m");
|
||||
queryBuilder.append(" AND mr.actif = true");
|
||||
queryBuilder.append(" AND mr.role.code IN :roleCodes");
|
||||
queryBuilder.append(")");
|
||||
// Convertir les noms de rôles en codes (supposant que criteria.getRoles() contient des codes)
|
||||
parameters.put("roleCodes", criteria.getRoles());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,4 +637,101 @@ public class MembreService {
|
||||
|
||||
return csv.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Importe des membres depuis un fichier Excel ou CSV
|
||||
*/
|
||||
public MembreImportExportService.ResultatImport importerMembres(
|
||||
InputStream fileInputStream,
|
||||
String fileName,
|
||||
UUID organisationId,
|
||||
String typeMembreDefaut,
|
||||
boolean mettreAJourExistants,
|
||||
boolean ignorerErreurs) {
|
||||
return membreImportExportService.importerMembres(
|
||||
fileInputStream, fileName, organisationId, typeMembreDefaut, mettreAJourExistants, ignorerErreurs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte des membres vers Excel
|
||||
*/
|
||||
public byte[] exporterVersExcel(List<MembreDTO> membres, List<String> colonnesExport, boolean inclureHeaders, boolean formaterDates, boolean inclureStatistiques, String motDePasse) {
|
||||
try {
|
||||
return membreImportExportService.exporterVersExcel(membres, colonnesExport, inclureHeaders, formaterDates, inclureStatistiques, motDePasse);
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'export Excel");
|
||||
throw new RuntimeException("Erreur lors de l'export Excel: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte des membres vers CSV
|
||||
*/
|
||||
public byte[] exporterVersCSV(List<MembreDTO> membres, List<String> colonnesExport, boolean inclureHeaders, boolean formaterDates) {
|
||||
try {
|
||||
return membreImportExportService.exporterVersCSV(membres, colonnesExport, inclureHeaders, formaterDates);
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'export CSV");
|
||||
throw new RuntimeException("Erreur lors de l'export CSV: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un modèle Excel pour l'import
|
||||
*/
|
||||
public byte[] genererModeleImport() {
|
||||
try {
|
||||
return membreImportExportService.genererModeleImport();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la génération du modèle");
|
||||
throw new RuntimeException("Erreur lors de la génération du modèle: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les membres pour l'export selon les filtres
|
||||
*/
|
||||
public List<MembreDTO> listerMembresPourExport(
|
||||
UUID associationId,
|
||||
String statut,
|
||||
String type,
|
||||
String dateAdhesionDebut,
|
||||
String dateAdhesionFin) {
|
||||
|
||||
List<Membre> membres;
|
||||
|
||||
if (associationId != null) {
|
||||
TypedQuery<Membre> query = entityManager.createQuery(
|
||||
"SELECT m FROM Membre m WHERE m.organisation.id = :associationId", Membre.class);
|
||||
query.setParameter("associationId", associationId);
|
||||
membres = query.getResultList();
|
||||
} else {
|
||||
membres = membreRepository.listAll();
|
||||
}
|
||||
|
||||
// Filtrer par statut
|
||||
if (statut != null && !statut.isEmpty()) {
|
||||
boolean actif = "ACTIF".equals(statut);
|
||||
membres = membres.stream()
|
||||
.filter(m -> m.getActif() == actif)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// Filtrer par dates d'adhésion
|
||||
if (dateAdhesionDebut != null && !dateAdhesionDebut.isEmpty()) {
|
||||
LocalDate dateDebut = LocalDate.parse(dateAdhesionDebut);
|
||||
membres = membres.stream()
|
||||
.filter(m -> m.getDateAdhesion() != null && !m.getDateAdhesion().isBefore(dateDebut))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (dateAdhesionFin != null && !dateAdhesionFin.isEmpty()) {
|
||||
LocalDate dateFin = LocalDate.parse(dateAdhesionFin);
|
||||
membres = membres.stream()
|
||||
.filter(m -> m.getDateAdhesion() != null && !m.getDateAdhesion().isAfter(dateFin))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return convertToDTOList(membres);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,77 @@
|
||||
# Configuration UnionFlow Server - Profil Production
|
||||
# Ce fichier est chargé automatiquement quand le profil 'prod' est actif
|
||||
# Configuration UnionFlow Server - PRODUCTION
|
||||
# Ce fichier est utilisé avec le profil Quarkus "prod"
|
||||
|
||||
# Configuration Hibernate pour production
|
||||
quarkus.hibernate-orm.database.generation=validate
|
||||
# Configuration HTTP
|
||||
quarkus.http.port=8085
|
||||
quarkus.http.host=0.0.0.0
|
||||
|
||||
# Configuration CORS - Production (strict)
|
||||
quarkus.http.cors=true
|
||||
quarkus.http.cors.origins=${CORS_ORIGINS:https://unionflow.lions.dev,https://security.lions.dev}
|
||||
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
|
||||
quarkus.http.cors.headers=Content-Type,Authorization
|
||||
quarkus.http.cors.allow-credentials=true
|
||||
|
||||
# Configuration Base de données PostgreSQL - Production
|
||||
quarkus.datasource.db-kind=postgresql
|
||||
quarkus.datasource.username=${DB_USERNAME:unionflow}
|
||||
quarkus.datasource.password=${DB_PASSWORD}
|
||||
quarkus.datasource.jdbc.url=${DB_URL:jdbc:postgresql://localhost:5432/unionflow}
|
||||
quarkus.datasource.jdbc.min-size=5
|
||||
quarkus.datasource.jdbc.max-size=20
|
||||
|
||||
# Configuration Hibernate - Production (IMPORTANT: update, pas drop-and-create)
|
||||
quarkus.hibernate-orm.database.generation=update
|
||||
quarkus.hibernate-orm.log.sql=false
|
||||
quarkus.hibernate-orm.jdbc.timezone=UTC
|
||||
quarkus.hibernate-orm.packages=dev.lions.unionflow.server.entity
|
||||
quarkus.hibernate-orm.metrics.enabled=false
|
||||
|
||||
# Configuration logging pour production
|
||||
quarkus.log.console.level=WARN
|
||||
quarkus.log.category."dev.lions.unionflow".level=INFO
|
||||
quarkus.log.category.root.level=WARN
|
||||
# Configuration Flyway - Production (ACTIVÉ)
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
quarkus.flyway.baseline-on-migrate=true
|
||||
quarkus.flyway.baseline-version=1.0.0
|
||||
|
||||
# Configuration Keycloak pour production
|
||||
quarkus.oidc.auth-server-url=${KEYCLOAK_SERVER_URL:http://localhost:8180/realms/unionflow}
|
||||
quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID:unionflow-server}
|
||||
# Configuration Keycloak OIDC - Production
|
||||
quarkus.oidc.auth-server-url=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/unionflow}
|
||||
quarkus.oidc.client-id=unionflow-server
|
||||
quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
quarkus.oidc.tls.verification=required
|
||||
quarkus.oidc.application-type=service
|
||||
|
||||
# Configuration Keycloak Policy Enforcer
|
||||
quarkus.keycloak.policy-enforcer.enable=false
|
||||
quarkus.keycloak.policy-enforcer.lazy-load-paths=true
|
||||
quarkus.keycloak.policy-enforcer.enforcement-mode=PERMISSIVE
|
||||
|
||||
# Chemins publics (non protégés)
|
||||
quarkus.http.auth.permission.public.paths=/health,/q/*,/favicon.ico
|
||||
quarkus.http.auth.permission.public.policy=permit
|
||||
|
||||
# Configuration OpenAPI - Production (Swagger désactivé ou protégé)
|
||||
quarkus.smallrye-openapi.info-title=UnionFlow Server API
|
||||
quarkus.smallrye-openapi.info-version=1.0.0
|
||||
quarkus.smallrye-openapi.info-description=API REST pour la gestion d'union avec authentification Keycloak
|
||||
quarkus.smallrye-openapi.servers=https://api.lions.dev/unionflow
|
||||
|
||||
# Configuration Swagger UI - Production (DÉSACTIVÉ pour sécurité)
|
||||
quarkus.swagger-ui.always-include=false
|
||||
|
||||
# Configuration santé
|
||||
quarkus.smallrye-health.root-path=/health
|
||||
|
||||
# Configuration logging - Production
|
||||
quarkus.log.console.enable=true
|
||||
quarkus.log.console.level=INFO
|
||||
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{2.}] (%t) %s%e%n
|
||||
quarkus.log.category."dev.lions.unionflow".level=INFO
|
||||
quarkus.log.category."org.hibernate".level=WARN
|
||||
quarkus.log.category."io.quarkus".level=INFO
|
||||
quarkus.log.category."org.jboss.resteasy".level=WARN
|
||||
|
||||
# Configuration Wave Money - Production
|
||||
wave.api.key=${WAVE_API_KEY:}
|
||||
wave.api.secret=${WAVE_API_SECRET:}
|
||||
wave.api.base.url=${WAVE_API_BASE_URL:https://api.wave.com/v1}
|
||||
wave.environment=${WAVE_ENVIRONMENT:production}
|
||||
wave.webhook.secret=${WAVE_WEBHOOK_SECRET:}
|
||||
|
||||
@@ -9,9 +9,12 @@ quarkus.datasource.jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
|
||||
|
||||
# Configuration Hibernate pour tests
|
||||
quarkus.hibernate-orm.database.generation=drop-and-create
|
||||
# Désactiver l'exécution de import.sql pendant les tests
|
||||
quarkus.hibernate-orm.sql-load-script=
|
||||
|
||||
# Configuration Flyway pour tests (désactivé)
|
||||
quarkus.flyway.migrate-at-start=false
|
||||
quarkus.flyway.enabled=false
|
||||
|
||||
# Configuration Keycloak pour tests (désactivé)
|
||||
quarkus.oidc.tenant-enabled=false
|
||||
|
||||
@@ -122,90 +122,9 @@ BEGIN
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Insertion de données de test pour le développement
|
||||
INSERT INTO organisations (
|
||||
nom, nom_court, type_organisation, statut, description,
|
||||
email, telephone, adresse, ville, region, pays,
|
||||
objectifs, activites_principales, nombre_membres,
|
||||
organisation_publique, accepte_nouveaux_membres,
|
||||
cree_par
|
||||
) VALUES
|
||||
(
|
||||
'Lions Club Abidjan Plateau',
|
||||
'LC Plateau',
|
||||
'LIONS_CLUB',
|
||||
'ACTIVE',
|
||||
'Lions Club du district 403 A1, zone Plateau d''Abidjan',
|
||||
'plateau@lionsclub-ci.org',
|
||||
'+225 27 20 21 22 23',
|
||||
'Immeuble SCIAM, Boulevard de la République',
|
||||
'Abidjan',
|
||||
'Lagunes',
|
||||
'Côte d''Ivoire',
|
||||
'Servir la communauté par des actions humanitaires et sociales',
|
||||
'Actions de santé, éducation, environnement et aide aux démunis',
|
||||
45,
|
||||
true,
|
||||
true,
|
||||
'system'
|
||||
),
|
||||
(
|
||||
'Lions Club Abidjan Cocody',
|
||||
'LC Cocody',
|
||||
'LIONS_CLUB',
|
||||
'ACTIVE',
|
||||
'Lions Club du district 403 A1, zone Cocody',
|
||||
'cocody@lionsclub-ci.org',
|
||||
'+225 27 22 44 55 66',
|
||||
'Riviera Golf, Cocody',
|
||||
'Abidjan',
|
||||
'Lagunes',
|
||||
'Côte d''Ivoire',
|
||||
'Servir la communauté par des actions humanitaires et sociales',
|
||||
'Actions de santé, éducation, environnement et aide aux démunis',
|
||||
38,
|
||||
true,
|
||||
true,
|
||||
'system'
|
||||
),
|
||||
(
|
||||
'Association des Femmes Entrepreneures CI',
|
||||
'AFECI',
|
||||
'ASSOCIATION',
|
||||
'ACTIVE',
|
||||
'Association pour la promotion de l''entrepreneuriat féminin en Côte d''Ivoire',
|
||||
'contact@afeci.org',
|
||||
'+225 05 06 07 08 09',
|
||||
'Marcory Zone 4C',
|
||||
'Abidjan',
|
||||
'Lagunes',
|
||||
'Côte d''Ivoire',
|
||||
'Promouvoir l''entrepreneuriat féminin et l''autonomisation des femmes',
|
||||
'Formation, accompagnement, financement de projets féminins',
|
||||
120,
|
||||
true,
|
||||
true,
|
||||
'system'
|
||||
),
|
||||
(
|
||||
'Coopérative Agricole du Nord',
|
||||
'COOP-NORD',
|
||||
'COOPERATIVE',
|
||||
'ACTIVE',
|
||||
'Coopérative des producteurs agricoles du Nord de la Côte d''Ivoire',
|
||||
'info@coop-nord.ci',
|
||||
'+225 09 10 11 12 13',
|
||||
'Korhogo Centre',
|
||||
'Korhogo',
|
||||
'Savanes',
|
||||
'Côte d''Ivoire',
|
||||
'Améliorer les conditions de vie des producteurs agricoles',
|
||||
'Production, transformation et commercialisation de produits agricoles',
|
||||
250,
|
||||
true,
|
||||
true,
|
||||
'system'
|
||||
);
|
||||
-- IMPORTANT: Aucune donnée fictive n'est insérée dans ce script de migration.
|
||||
-- Les données doivent être insérées manuellement via l'interface d'administration
|
||||
-- ou via des scripts de migration séparés si nécessaire pour la production.
|
||||
|
||||
-- Mise à jour des statistiques de la base de données
|
||||
ANALYZE organisations;
|
||||
|
||||
@@ -1,128 +1,10 @@
|
||||
-- Script d'insertion de données initiales pour UnionFlow (avec UUID)
|
||||
-- Script d'insertion de données initiales pour UnionFlow
|
||||
-- Ce fichier est exécuté automatiquement par Hibernate au démarrage
|
||||
-- Utilisé uniquement en mode développement (quarkus.hibernate-orm.database.generation=drop-and-create)
|
||||
-- NOTE: Les IDs sont maintenant des UUID générés automatiquement
|
||||
|
||||
-- Insertion d'organisations initiales avec UUIDs générés
|
||||
INSERT INTO organisations (id, nom, nom_court, type_organisation, statut, description,
|
||||
email, telephone, adresse, ville, region, pays,
|
||||
objectifs, activites_principales, nombre_membres,
|
||||
organisation_publique, accepte_nouveaux_membres, cree_par,
|
||||
actif, date_creation, niveau_hierarchique, nombre_administrateurs, cotisation_obligatoire) VALUES
|
||||
(
|
||||
gen_random_uuid(),
|
||||
'Lions Club Abidjan Plateau',
|
||||
'LC Plateau',
|
||||
'LIONS_CLUB',
|
||||
'ACTIVE',
|
||||
'Lions Club du district 403 A1, zone Plateau d''Abidjan',
|
||||
'plateau@lionsclub-ci.org',
|
||||
'+225 27 20 21 22 23',
|
||||
'Immeuble SCIAM, Boulevard de la République',
|
||||
'Abidjan',
|
||||
'Lagunes',
|
||||
'Côte d''Ivoire',
|
||||
'Servir la communauté par des actions humanitaires et sociales',
|
||||
'Actions de santé, éducation, environnement et aide aux démunis',
|
||||
45,
|
||||
true,
|
||||
true,
|
||||
'system',
|
||||
true,
|
||||
CURRENT_TIMESTAMP,
|
||||
0,
|
||||
0,
|
||||
false
|
||||
),
|
||||
(
|
||||
gen_random_uuid(),
|
||||
'Lions Club Abidjan Cocody',
|
||||
'LC Cocody',
|
||||
'LIONS_CLUB',
|
||||
'ACTIVE',
|
||||
'Lions Club du district 403 A1, zone Cocody',
|
||||
'cocody@lionsclub-ci.org',
|
||||
'+225 27 22 44 55 66',
|
||||
'Riviera Golf, Cocody',
|
||||
'Abidjan',
|
||||
'Lagunes',
|
||||
'Côte d''Ivoire',
|
||||
'Servir la communauté par des actions humanitaires et sociales',
|
||||
'Actions de santé, éducation, environnement et aide aux démunis',
|
||||
38,
|
||||
true,
|
||||
true,
|
||||
'system',
|
||||
true,
|
||||
CURRENT_TIMESTAMP,
|
||||
0,
|
||||
0,
|
||||
false
|
||||
),
|
||||
(
|
||||
gen_random_uuid(),
|
||||
'Association des Femmes Entrepreneures CI',
|
||||
'AFECI',
|
||||
'ASSOCIATION',
|
||||
'ACTIVE',
|
||||
'Association pour la promotion de l''entrepreneuriat féminin en Côte d''Ivoire',
|
||||
'contact@afeci.org',
|
||||
'+225 05 06 07 08 09',
|
||||
'Marcory Zone 4C',
|
||||
'Abidjan',
|
||||
'Lagunes',
|
||||
'Côte d''Ivoire',
|
||||
'Promouvoir l''entrepreneuriat féminin et l''autonomisation des femmes',
|
||||
'Formation, accompagnement, financement de projets féminins',
|
||||
120,
|
||||
true,
|
||||
true,
|
||||
'system',
|
||||
true,
|
||||
CURRENT_TIMESTAMP,
|
||||
0,
|
||||
0,
|
||||
false
|
||||
),
|
||||
(
|
||||
gen_random_uuid(),
|
||||
'Coopérative Agricole du Nord',
|
||||
'COOP-NORD',
|
||||
'COOPERATIVE',
|
||||
'ACTIVE',
|
||||
'Coopérative des producteurs agricoles du Nord de la Côte d''Ivoire',
|
||||
'info@coop-nord.ci',
|
||||
'+225 09 10 11 12 13',
|
||||
'Korhogo Centre',
|
||||
'Korhogo',
|
||||
'Savanes',
|
||||
'Côte d''Ivoire',
|
||||
'Améliorer les conditions de vie des producteurs agricoles',
|
||||
'Production, transformation et commercialisation de produits agricoles',
|
||||
250,
|
||||
true,
|
||||
true,
|
||||
'system',
|
||||
true,
|
||||
CURRENT_TIMESTAMP,
|
||||
0,
|
||||
0,
|
||||
false
|
||||
);
|
||||
|
||||
-- Insertion de membres initiaux (avec UUIDs générés et références aux organisations)
|
||||
-- On utilise des sous-requêtes pour récupérer les IDs des organisations par leur nom
|
||||
INSERT INTO membres (id, numero_membre, nom, prenom, email, telephone, date_naissance, date_adhesion, actif, date_creation, organisation_id) VALUES
|
||||
(gen_random_uuid(), 'MBR001', 'Kouassi', 'Jean-Baptiste', 'jb.kouassi@email.ci', '+225071234567', '1985-03-15', '2023-01-15', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Lions Club Abidjan Plateau' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR002', 'Traoré', 'Aminata', 'aminata.traore@email.ci', '+225059876543', '1990-07-22', '2023-02-10', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Lions Club Abidjan Plateau' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR003', 'Bamba', 'Seydou', 'seydou.bamba@email.ci', '+225012345678', '1988-11-08', '2023-03-05', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Lions Club Abidjan Cocody' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR004', 'Ouattara', 'Fatoumata', 'fatoumata.ouattara@email.ci', '+225078765432', '1992-05-18', '2023-04-12', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Lions Club Abidjan Cocody' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR005', 'Koné', 'Ibrahim', 'ibrahim.kone@email.ci', '+225051122334', '1987-09-30', '2023-05-20', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Association des Femmes Entrepreneures CI' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR006', 'Diabaté', 'Mariam', 'mariam.diabate@email.ci', '+225015566778', '1991-12-03', '2023-06-08', false, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Association des Femmes Entrepreneures CI' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR007', 'Sangaré', 'Moussa', 'moussa.sangare@email.ci', '+225079988776', '1989-04-25', '2023-07-15', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Coopérative Agricole du Nord' LIMIT 1)),
|
||||
(gen_random_uuid(), 'MBR008', 'Coulibaly', 'Awa', 'awa.coulibaly@email.ci', '+225054433221', '1993-08-14', '2023-08-22', true, '2024-01-01 10:00:00', (SELECT id FROM organisations WHERE nom = 'Coopérative Agricole du Nord' LIMIT 1));
|
||||
|
||||
-- Note: Les insertions de cotisations et événements avec des références aux membres
|
||||
-- nécessitent de récupérer les UUIDs réels des membres insérés ci-dessus.
|
||||
-- Pour le développement, ces données peuvent être insérées via l'application ou
|
||||
-- via des scripts de test plus complexes qui récupèrent les UUIDs générés.
|
||||
--
|
||||
-- IMPORTANT: Ce fichier ne doit PAS contenir de données fictives pour la production.
|
||||
-- Les données doivent être insérées manuellement via l'interface d'administration
|
||||
-- ou via des scripts de migration Flyway si nécessaire.
|
||||
--
|
||||
-- Ce fichier est laissé vide intentionnellement pour éviter l'insertion automatique
|
||||
-- de données fictives lors du démarrage du serveur.
|
||||
|
||||
@@ -45,6 +45,7 @@ class MembreServiceAdvancedSearchTest {
|
||||
.nom("Organisation Test")
|
||||
.typeOrganisation("ASSOCIATION")
|
||||
.statut("ACTIF")
|
||||
.email("test@organisation.com")
|
||||
.build();
|
||||
testOrganisation.setDateCreation(LocalDateTime.now());
|
||||
testOrganisation.setActif(true);
|
||||
|
||||
Reference in New Issue
Block a user