Checkpoint: La route /api/download-bap accepte maintenant un param filename et le met dans Content-Disposition (RFC 5987). Les fonctions downloadBapPdf dans InvoicesBAP.tsx et BapHistory.tsx passent le filename formaté dans l'URL.

This commit is contained in:
Manus
2026-04-12 17:42:15 -04:00
parent d1dda66b9a
commit ba3392b1e4
3 changed files with 13 additions and 6 deletions

View File

@@ -30,15 +30,16 @@ function downloadBapPdf(
validatedAt?: Date | string | null validatedAt?: Date | string | null
) { ) {
const fallback = pdfUrl.split('/').pop() || 'BAP.pdf'; const fallback = pdfUrl.split('/').pop() || 'BAP.pdf';
const downloadUrl = `/api/download-bap?pdfPath=${encodeURIComponent(pdfUrl)}`;
// Construire le nom de fichier : date - fournisseur - numero.pdf // Construire le nom de fichier : date - fournisseur - numero.pdf
const datePart = validatedAt const datePart = validatedAt
? new Date(validatedAt).toLocaleDateString('fr-CA') // format AAAA-MM-JJ ? new Date(validatedAt).toLocaleDateString('fr-CA') // format AAAA-MM-JJ
: ''; : '';
const supplierPart = (supplierName || '').replace(/[^a-zA-Z0-9à-ÿ \-]/g, '').trim(); const supplierPart = (supplierName || '').replace(/[^a-zA-Z0-9\u00e0-\u00ff \-]/g, '').trim();
const numberPart = (invoiceNumber || '').replace(/[^a-zA-Z0-9\-]/g, '').trim(); const numberPart = (invoiceNumber || '').replace(/[^a-zA-Z0-9\-]/g, '').trim();
const parts = [datePart, supplierPart, numberPart].filter(Boolean); const parts = [datePart, supplierPart, numberPart].filter(Boolean);
const filename = parts.length > 0 ? `${parts.join(' - ')}.pdf` : fallback; const filename = parts.length > 0 ? `${parts.join(' - ')}.pdf` : fallback;
// Passer le filename au serveur pour qu'il soit dans Content-Disposition
const downloadUrl = `/api/download-bap?pdfPath=${encodeURIComponent(pdfUrl)}&filename=${encodeURIComponent(filename)}`;
const a = document.createElement('a'); const a = document.createElement('a');
a.href = downloadUrl; a.href = downloadUrl;
a.download = filename; a.download = filename;

View File

@@ -43,14 +43,15 @@ function downloadBapPdf(
validatedAt?: Date | string | null validatedAt?: Date | string | null
) { ) {
const fallback = pdfUrl.split('/').pop() || 'BAP.pdf'; const fallback = pdfUrl.split('/').pop() || 'BAP.pdf';
const downloadUrl = `/api/download-bap?pdfPath=${encodeURIComponent(pdfUrl)}`;
const datePart = validatedAt const datePart = validatedAt
? new Date(validatedAt).toLocaleDateString('fr-CA') // AAAA-MM-JJ ? new Date(validatedAt).toLocaleDateString('fr-CA') // AAAA-MM-JJ
: new Date().toLocaleDateString('fr-CA'); : new Date().toLocaleDateString('fr-CA');
const supplierPart = (supplierName || '').replace(/[^a-zA-Z0-9à-ÿ \-]/g, '').trim(); const supplierPart = (supplierName || '').replace(/[^a-zA-Z0-9\u00e0-\u00ff \-]/g, '').trim();
const numberPart = (invoiceNumber || '').replace(/[^a-zA-Z0-9\-]/g, '').trim(); const numberPart = (invoiceNumber || '').replace(/[^a-zA-Z0-9\-]/g, '').trim();
const parts = [datePart, supplierPart, numberPart].filter(Boolean); const parts = [datePart, supplierPart, numberPart].filter(Boolean);
const filename = parts.length > 0 ? `${parts.join(' - ')}.pdf` : fallback; const filename = parts.length > 0 ? `${parts.join(' - ')}.pdf` : fallback;
// Passer le filename au serveur pour qu'il soit dans Content-Disposition
const downloadUrl = `/api/download-bap?pdfPath=${encodeURIComponent(pdfUrl)}&filename=${encodeURIComponent(filename)}`;
const a = document.createElement('a'); const a = document.createElement('a');
a.href = downloadUrl; a.href = downloadUrl;
a.download = filename; a.download = filename;

View File

@@ -61,8 +61,13 @@ async function startServer() {
res.status(404).json({ error: "Fichier introuvable" }); res.status(404).json({ error: "Fichier introuvable" });
return; return;
} }
const filename = path.basename(storagePath); // Utiliser le nom de fichier fourni par le client (format Date - Fournisseur - N°Facture.pdf)
res.setHeader("Content-Disposition", `attachment; filename="${encodeURIComponent(filename)}"`); const customFilename = req.query.filename as string | undefined;
const fallbackFilename = path.basename(storagePath);
const finalFilename = customFilename ? customFilename : fallbackFilename;
// RFC 5987 : utiliser filename* pour les caractères non-ASCII
const encodedFilename = encodeURIComponent(finalFilename);
res.setHeader("Content-Disposition", `attachment; filename="${encodedFilename}"; filename*=UTF-8''${encodedFilename}`);
res.setHeader("Content-Type", "application/pdf"); res.setHeader("Content-Type", "application/pdf");
res.sendFile(storagePath); res.sendFile(storagePath);
}); });