diff --git a/server/routers.ts b/server/routers.ts
index 00a4c3e..d1b7532 100644
--- a/server/routers.ts
+++ b/server/routers.ts
@@ -525,6 +525,20 @@ export const appRouter = router({
exportToPdf: protectedProcedure
.input(z.object({ invoiceIds: z.array(z.number()) }))
.mutation(async ({ input, ctx }) => {
+ const fs = await import('fs/promises');
+ const path = await import('path');
+
+ // Get export folder from settings
+ const settings = await getImportSettingsByUser(ctx.user.id);
+ const exportFolder = settings?.exportFolder;
+
+ if (!exportFolder) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Le dossier d'export n'est pas configuré. Veuillez le définir dans les paramètres de réception."
+ });
+ }
+
// Validate that all invoices have quality score of 100
const invoices = await Promise.all(
input.invoiceIds.map(id => getInvoiceById(id))
@@ -537,27 +551,72 @@ export const appRouter = router({
if (invalidInvoices.length > 0) {
throw new TRPCError({
code: "BAD_REQUEST",
- message: "Toutes les factures doivent avoir un score de qualit\u00e9 de 100% pour \u00eatre export\u00e9es"
+ message: "Toutes les factures doivent avoir un score de qualité de 100% pour être exportées"
});
}
- // Update export status
+ // Create export folder if it doesn't exist
+ try {
+ await fs.mkdir(exportFolder, { recursive: true });
+ } catch (error) {
+ console.error('Error creating export folder:', error);
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: `Impossible de créer le dossier d'export: ${exportFolder}`
+ });
+ }
+
+ // Copy PDFs to export folder
+ const copiedFiles: string[] = [];
+ const errors: string[] = [];
+ const STORAGE_BASE_PATH = process.env.STORAGE_BASE_PATH || path.join(process.cwd(), "storage");
+
for (const invoice of invoices) {
- if (invoice) {
- await updateInvoice(invoice.id, {
- exportStatus: "exported",
- exportedAt: new Date(),
- exportMode: "manual",
- });
+ if (invoice && invoice.fileKey) {
+ try {
+ // Build source path from fileKey
+ const sourcePath = path.join(STORAGE_BASE_PATH, invoice.fileKey);
+
+ // Extract filename from fileKey
+ const filename = path.basename(invoice.fileKey);
+ const destPath = path.join(exportFolder, filename);
+
+ // Copy file
+ await fs.copyFile(sourcePath, destPath);
+ copiedFiles.push(destPath);
+
+ // Update export status
+ await updateInvoice(invoice.id, {
+ exportStatus: "exported",
+ exportedAt: new Date(),
+ exportMode: "manual",
+ });
+ } catch (error: any) {
+ console.error(`Error copying file for invoice ${invoice.id}:`, error);
+ errors.push(`${invoice.supplierName || 'Inconnu'} (${invoice.invoiceNumber || 'N/A'}): ${error.message}`);
+
+ // Update export status to error
+ await updateInvoice(invoice.id, {
+ exportStatus: "export_error",
+ exportMode: "manual",
+ });
+ }
}
}
- // Return the file URLs for PDF generation on client side
+ if (errors.length > 0) {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message: `Erreurs lors de l'export:\n${errors.join('\n')}`
+ });
+ }
+
return {
success: true,
+ exportFolder,
+ copiedCount: copiedFiles.length,
invoices: invoices.filter(Boolean).map(inv => ({
id: inv!.id,
- fileUrl: inv!.fileUrl,
supplierName: inv!.supplierName,
invoiceNumber: inv!.invoiceNumber,
invoiceDate: inv!.invoiceDate,
diff --git a/todo.md b/todo.md
index ee69dee..2f3ca74 100644
--- a/todo.md
+++ b/todo.md
@@ -486,4 +486,15 @@
- [x] Ajouter bouton "Exporter" qui s'active si au moins une facture est cochée
- [x] Utiliser la route tRPC existante pour exporter les factures sélectionnées
- [x] Implémenter la génération de fichiers d'export (PDF)
-- [ ] Tester la sélection et l'export des factures BAP
+- [x] Tester la sélection et l'export des factures BAP
+
+## Améliorations export factures BAP
+- [x] Ajouter icône verte ✓ dans la colonne Score pour les factures exportables
+- [x] Modifier la fonction getQualityBadge pour afficher l'indicateur d'exportabilité
+- [x] Modifier la route tRPC exportToPdf pour copier les PDFs vers exportFolder
+- [x] Ajouter la logique de copie de fichiers avec fs.copyFile
+- [x] Récupérer le paramètre exportFolder depuis importSettings
+- [x] Créer le dossier exportFolder s'il n'existe pas
+- [x] Gérer les erreurs de copie de fichiers
+- [x] Afficher une notification de succès avec le chemin du dossier d'export
+- [ ] Tester l'export vers le dossier configuré