From ba3392b1e47642d7a3ffa1522a36f153d2159915 Mon Sep 17 00:00:00 2001 From: Manus Date: Sun, 12 Apr 2026 17:42:15 -0400 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20La=20route=20/api/download-bap=20?= =?UTF-8?q?accepte=20maintenant=20un=20param=20filename=20et=20le=20met=20?= =?UTF-8?q?dans=20Content-Disposition=20(RFC=205987).=20Les=20fonctions=20?= =?UTF-8?q?downloadBapPdf=20dans=20InvoicesBAP.tsx=20et=20BapHistory.tsx?= =?UTF-8?q?=20passent=20le=20filename=20format=C3=A9=20dans=20l'URL.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/BapHistory.tsx | 5 +++-- client/src/pages/InvoicesBAP.tsx | 5 +++-- server/_core/index.ts | 9 +++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/client/src/pages/BapHistory.tsx b/client/src/pages/BapHistory.tsx index 5e9b8e6..f638861 100644 --- a/client/src/pages/BapHistory.tsx +++ b/client/src/pages/BapHistory.tsx @@ -30,15 +30,16 @@ function downloadBapPdf( validatedAt?: Date | string | null ) { const fallback = pdfUrl.split('/').pop() || 'BAP.pdf'; - const downloadUrl = `/api/download-bap?pdfPath=${encodeURIComponent(pdfUrl)}`; // Construire le nom de fichier : date - fournisseur - numero.pdf const datePart = validatedAt ? 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 parts = [datePart, supplierPart, numberPart].filter(Boolean); 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'); a.href = downloadUrl; a.download = filename; diff --git a/client/src/pages/InvoicesBAP.tsx b/client/src/pages/InvoicesBAP.tsx index d11e6d9..c626009 100644 --- a/client/src/pages/InvoicesBAP.tsx +++ b/client/src/pages/InvoicesBAP.tsx @@ -43,14 +43,15 @@ function downloadBapPdf( validatedAt?: Date | string | null ) { const fallback = pdfUrl.split('/').pop() || 'BAP.pdf'; - const downloadUrl = `/api/download-bap?pdfPath=${encodeURIComponent(pdfUrl)}`; const datePart = validatedAt ? new Date(validatedAt).toLocaleDateString('fr-CA') // AAAA-MM-JJ : 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 parts = [datePart, supplierPart, numberPart].filter(Boolean); 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'); a.href = downloadUrl; a.download = filename; diff --git a/server/_core/index.ts b/server/_core/index.ts index 45b6ed9..2d83347 100644 --- a/server/_core/index.ts +++ b/server/_core/index.ts @@ -61,8 +61,13 @@ async function startServer() { res.status(404).json({ error: "Fichier introuvable" }); return; } - const filename = path.basename(storagePath); - res.setHeader("Content-Disposition", `attachment; filename="${encodeURIComponent(filename)}"`); + // Utiliser le nom de fichier fourni par le client (format Date - Fournisseur - N°Facture.pdf) + 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.sendFile(storagePath); });