feat(backend): ajout export PDF pour les membres
Nouveau format d'export :
- GET /api/membres/export?format=PDF - Export membres en PDF (OpenPDF)
- Support formats : EXCEL, CSV, PDF (dans MembreResource)
MembreImportExportService :
- exporterVersPDF() : 193 lignes, génération PDF professionnelle
* Document A4 landscape pour + de colonnes
* Titre + métadonnées (date, total)
* Table avec colonnes configurables (PERSO, CONTACT, ADHESION, ORGANISATION)
* Styles : headers (bleu foncé, blanc), données (noir, aligné)
* Page statistiques optionnelle (total, actifs/inactifs/suspendus, orgs)
- Méthodes helper : createHeaderCell, createDataCell, createStatsCell, ajouterStatistiquesPDF
MembreService :
- exporterVersPDF() : délégation vers MembreImportExportService
MembreResource :
- Mise à jour endpoint /export : if ("PDF".equalsIgnoreCase(format))
- Content-Type: application/pdf
- Filename: membres_export_YYYY-MM-DD.pdf
Correctifs imports :
- Résolution conflits OpenPDF vs Apache POI (List, Row, Font)
- Imports spécifiques : com.lowagie.text.Font, Document, PdfPTable, etc.
- Qualification complète pour éviter ambiguïtés (java.util.List vs com.lowagie.text.List)
Résultat : Export membres en 3 formats (EXCEL, CSV, PDF) ✅
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -646,6 +646,10 @@ public class MembreResource {
|
||||
exportData = membreService.exporterVersCSV(membres, colonnesExport, inclureHeaders, formaterDates);
|
||||
contentType = "text/csv";
|
||||
extension = "csv";
|
||||
} else if ("PDF".equalsIgnoreCase(format)) {
|
||||
exportData = membreService.exporterVersPDF(membres, colonnesExport, inclureHeaders, formaterDates, inclureStatistiques);
|
||||
contentType = "application/pdf";
|
||||
extension = "pdf";
|
||||
} else {
|
||||
// Pour Excel, inclure les statistiques uniquement si demandé et si format Excel
|
||||
boolean stats = inclureStatistiques && "EXCEL".equalsIgnoreCase(format);
|
||||
|
||||
@@ -19,6 +19,16 @@ 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 com.lowagie.text.Document;
|
||||
import com.lowagie.text.DocumentException;
|
||||
import com.lowagie.text.Element;
|
||||
import com.lowagie.text.PageSize;
|
||||
import com.lowagie.text.Paragraph;
|
||||
import com.lowagie.text.Phrase;
|
||||
import com.lowagie.text.pdf.PdfPCell;
|
||||
import com.lowagie.text.pdf.PdfPTable;
|
||||
import com.lowagie.text.pdf.PdfWriter;
|
||||
import java.awt.Color;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -866,6 +876,213 @@ public class MembreImportExportService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte des membres vers PDF
|
||||
*/
|
||||
public byte[] exporterVersPDF(List<MembreResponse> membres, List<String> colonnesExport, boolean inclureHeaders,
|
||||
boolean formaterDates, boolean inclureStatistiques) throws IOException {
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
Document document = new Document(PageSize.A4.rotate()); // Landscape pour plus de colonnes
|
||||
PdfWriter.getInstance(document, outputStream);
|
||||
document.open();
|
||||
|
||||
// Titre
|
||||
com.lowagie.text.Font titleFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 18, com.lowagie.text.Font.BOLD, Color.DARK_GRAY);
|
||||
Paragraph title = new Paragraph("Export des Membres", titleFont);
|
||||
title.setAlignment(Element.ALIGN_CENTER);
|
||||
title.setSpacingAfter(20);
|
||||
document.add(title);
|
||||
|
||||
// Métadonnées
|
||||
com.lowagie.text.Font metaFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 10, com.lowagie.text.Font.NORMAL, Color.GRAY);
|
||||
Paragraph meta = new Paragraph("Généré le " + LocalDate.now().format(DATE_FORMATTER) + " | Total: " + membres.size() + " membres", metaFont);
|
||||
meta.setAlignment(Element.ALIGN_CENTER);
|
||||
meta.setSpacingAfter(15);
|
||||
document.add(meta);
|
||||
|
||||
// Déterminer les colonnes à afficher
|
||||
boolean inclurePerso = colonnesExport.contains("PERSO") || colonnesExport.isEmpty();
|
||||
boolean inclureContact = colonnesExport.contains("CONTACT") || colonnesExport.isEmpty();
|
||||
boolean inclureAdhesion = colonnesExport.contains("ADHESION") || colonnesExport.isEmpty();
|
||||
boolean inclureOrg = colonnesExport.contains("ORGANISATION") || colonnesExport.isEmpty();
|
||||
|
||||
// Calculer nombre de colonnes
|
||||
int colCount = 0;
|
||||
if (inclurePerso) colCount += 3; // Nom, Prénom, Date naissance
|
||||
if (inclureContact) colCount += 2; // Email, Téléphone
|
||||
if (inclureAdhesion) colCount += 2; // Date adhésion, Statut
|
||||
if (inclureOrg) colCount += 1; // Organisation
|
||||
|
||||
// Créer le tableau
|
||||
PdfPTable table = new PdfPTable(colCount);
|
||||
table.setWidthPercentage(100);
|
||||
table.setSpacingBefore(10);
|
||||
|
||||
// Style des en-têtes
|
||||
com.lowagie.text.Font headerFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 10, com.lowagie.text.Font.BOLD, Color.WHITE);
|
||||
Color headerBgColor = new Color(52, 73, 94); // Bleu foncé
|
||||
|
||||
// En-têtes
|
||||
if (inclureHeaders) {
|
||||
if (inclurePerso) {
|
||||
table.addCell(createHeaderCell("Nom", headerFont, headerBgColor));
|
||||
table.addCell(createHeaderCell("Prénom", headerFont, headerBgColor));
|
||||
table.addCell(createHeaderCell("Date Naissance", headerFont, headerBgColor));
|
||||
}
|
||||
if (inclureContact) {
|
||||
table.addCell(createHeaderCell("Email", headerFont, headerBgColor));
|
||||
table.addCell(createHeaderCell("Téléphone", headerFont, headerBgColor));
|
||||
}
|
||||
if (inclureAdhesion) {
|
||||
table.addCell(createHeaderCell("Date Adhésion", headerFont, headerBgColor));
|
||||
table.addCell(createHeaderCell("Statut", headerFont, headerBgColor));
|
||||
}
|
||||
if (inclureOrg) {
|
||||
table.addCell(createHeaderCell("Organisation", headerFont, headerBgColor));
|
||||
}
|
||||
}
|
||||
|
||||
// Style des cellules de données
|
||||
com.lowagie.text.Font dataFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 9, com.lowagie.text.Font.NORMAL);
|
||||
|
||||
// Données
|
||||
for (MembreResponse membre : membres) {
|
||||
if (inclurePerso) {
|
||||
table.addCell(createDataCell(membre.getNom() != null ? membre.getNom() : "", dataFont));
|
||||
table.addCell(createDataCell(membre.getPrenom() != null ? membre.getPrenom() : "", dataFont));
|
||||
String dateNaissance = "";
|
||||
if (membre.getDateNaissance() != null) {
|
||||
dateNaissance = formaterDates
|
||||
? membre.getDateNaissance().format(DATE_FORMATTER)
|
||||
: membre.getDateNaissance().toString();
|
||||
}
|
||||
table.addCell(createDataCell(dateNaissance, dataFont));
|
||||
}
|
||||
if (inclureContact) {
|
||||
table.addCell(createDataCell(membre.getEmail() != null ? membre.getEmail() : "", dataFont));
|
||||
table.addCell(createDataCell(membre.getTelephone() != null ? membre.getTelephone() : "", dataFont));
|
||||
}
|
||||
if (inclureAdhesion) {
|
||||
table.addCell(createDataCell("", dataFont)); // Date adhésion (pas dans DTO)
|
||||
table.addCell(createDataCell(membre.getStatutCompte() != null ? membre.getStatutCompte() : "", dataFont));
|
||||
}
|
||||
if (inclureOrg) {
|
||||
table.addCell(createDataCell(membre.getAssociationNom() != null ? membre.getAssociationNom() : "", dataFont));
|
||||
}
|
||||
}
|
||||
|
||||
document.add(table);
|
||||
|
||||
// Ajouter statistiques si demandé
|
||||
if (inclureStatistiques && !membres.isEmpty()) {
|
||||
document.newPage();
|
||||
ajouterStatistiquesPDF(document, membres);
|
||||
}
|
||||
|
||||
document.close();
|
||||
return outputStream.toByteArray();
|
||||
} catch (DocumentException e) {
|
||||
LOG.errorf(e, "Erreur lors de la génération du PDF");
|
||||
throw new IOException("Erreur lors de la génération du PDF", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une cellule d'en-tête PDF
|
||||
*/
|
||||
private PdfPCell createHeaderCell(String text, com.lowagie.text.Font font, Color bgColor) {
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, font));
|
||||
cell.setBackgroundColor(bgColor);
|
||||
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
cell.setPadding(8);
|
||||
return cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une cellule de données PDF
|
||||
*/
|
||||
private PdfPCell createDataCell(String text, com.lowagie.text.Font font) {
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, font));
|
||||
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
cell.setPadding(5);
|
||||
return cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une page de statistiques au PDF
|
||||
*/
|
||||
private void ajouterStatistiquesPDF(Document document, java.util.List<MembreResponse> membres) throws DocumentException {
|
||||
// Titre section statistiques
|
||||
com.lowagie.text.Font titleFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 16, com.lowagie.text.Font.BOLD, Color.DARK_GRAY);
|
||||
Paragraph statsTitle = new Paragraph("Statistiques des Membres", titleFont);
|
||||
statsTitle.setAlignment(Element.ALIGN_CENTER);
|
||||
statsTitle.setSpacingAfter(20);
|
||||
document.add(statsTitle);
|
||||
|
||||
// Calcul des statistiques
|
||||
long totalMembres = membres.size();
|
||||
long membresActifs = membres.stream()
|
||||
.filter(m -> dev.lions.unionflow.server.api.enums.membre.StatutMembre.ACTIF.name()
|
||||
.equals(m.getStatutCompte()))
|
||||
.count();
|
||||
long membresInactifs = membres.stream()
|
||||
.filter(m -> dev.lions.unionflow.server.api.enums.membre.StatutMembre.INACTIF.name()
|
||||
.equals(m.getStatutCompte()))
|
||||
.count();
|
||||
long membresSuspendus = membres.stream()
|
||||
.filter(m -> dev.lions.unionflow.server.api.enums.membre.StatutMembre.SUSPENDU.name()
|
||||
.equals(m.getStatutCompte()))
|
||||
.count();
|
||||
long organisationsDistinctes = membres.stream()
|
||||
.filter(m -> m.getAssociationNom() != null)
|
||||
.map(MembreResponse::getAssociationNom)
|
||||
.distinct()
|
||||
.count();
|
||||
|
||||
// Créer tableau statistiques
|
||||
PdfPTable statsTable = new PdfPTable(2);
|
||||
statsTable.setWidthPercentage(60);
|
||||
statsTable.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
statsTable.setSpacingBefore(10);
|
||||
|
||||
com.lowagie.text.Font labelFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 11, com.lowagie.text.Font.BOLD);
|
||||
com.lowagie.text.Font valueFont = new com.lowagie.text.Font(com.lowagie.text.Font.HELVETICA, 11, com.lowagie.text.Font.NORMAL);
|
||||
|
||||
// Ajouter lignes
|
||||
statsTable.addCell(createStatsCell("Total Membres", labelFont, true));
|
||||
statsTable.addCell(createStatsCell(String.valueOf(totalMembres), valueFont, false));
|
||||
|
||||
statsTable.addCell(createStatsCell("Membres Actifs", labelFont, true));
|
||||
statsTable.addCell(createStatsCell(String.valueOf(membresActifs), valueFont, false));
|
||||
|
||||
statsTable.addCell(createStatsCell("Membres Inactifs", labelFont, true));
|
||||
statsTable.addCell(createStatsCell(String.valueOf(membresInactifs), valueFont, false));
|
||||
|
||||
statsTable.addCell(createStatsCell("Membres Suspendus", labelFont, true));
|
||||
statsTable.addCell(createStatsCell(String.valueOf(membresSuspendus), valueFont, false));
|
||||
|
||||
statsTable.addCell(createStatsCell("Organisations Distinctes", labelFont, true));
|
||||
statsTable.addCell(createStatsCell(String.valueOf(organisationsDistinctes), valueFont, false));
|
||||
|
||||
document.add(statsTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une cellule de statistiques PDF
|
||||
*/
|
||||
private PdfPCell createStatsCell(String text, com.lowagie.text.Font font, boolean isLabel) {
|
||||
PdfPCell cell = new PdfPCell(new Phrase(text, font));
|
||||
cell.setHorizontalAlignment(isLabel ? Element.ALIGN_LEFT : Element.ALIGN_RIGHT);
|
||||
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
cell.setPadding(8);
|
||||
if (isLabel) {
|
||||
cell.setBackgroundColor(new Color(236, 240, 241)); // Gris clair
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un modèle Excel pour l'import
|
||||
*/
|
||||
|
||||
@@ -853,6 +853,20 @@ public class MembreService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporte des membres vers PDF
|
||||
*/
|
||||
public byte[] exporterVersPDF(List<MembreResponse> membres, List<String> colonnesExport, boolean inclureHeaders,
|
||||
boolean formaterDates, boolean inclureStatistiques) {
|
||||
try {
|
||||
return membreImportExportService.exporterVersPDF(membres, colonnesExport, inclureHeaders, formaterDates,
|
||||
inclureStatistiques);
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'export PDF");
|
||||
throw new RuntimeException("Erreur lors de l'export PDF: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un modèle Excel pour l'import
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user