From 939dddd1d88d57cfb60be7b1ecd7957d9ec79493 Mon Sep 17 00:00:00 2001 From: Manus Date: Fri, 9 Oct 2026 08:44:02 +0000 Subject: [PATCH] fix: require BAP before original PDF exports --- client/src/pages/Invoices.tsx | 9 ++++-- client/src/pages/InvoicesBAP.tsx | 10 ++++--- server/bapExportGuard.test.ts | 40 +++++++++++++++++++++++++ server/routers.ts | 27 +++++++++++++++++ shared/bapExportGuard.ts | 26 ++++++++++++++++ todo.md | 51 ++++++++++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 server/bapExportGuard.test.ts create mode 100644 shared/bapExportGuard.ts diff --git a/client/src/pages/Invoices.tsx b/client/src/pages/Invoices.tsx index db06cda..a919bad 100644 --- a/client/src/pages/Invoices.tsx +++ b/client/src/pages/Invoices.tsx @@ -40,6 +40,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp import { toast } from "sonner"; import { useLocation } from "wouter"; import { matchesInvoicePeriod } from "@shared/invoicePeriod"; +import { requiresBapBeforeOriginalPdfExport } from "@shared/bapExportGuard"; type SortField = "invoiceDate" | "createdAt"; type SortDir = "asc" | "desc"; @@ -358,10 +359,14 @@ export default function Invoices({ scope = "all" }: { scope?: InvoiceScope }) { const isEligibleForExport = (invoice: any) => { return (invoice.qualityScore || 0) === 100; }; + + const isEligibleForPdfExport = (invoice: any) => { + return isEligibleForExport(invoice) && !requiresBapBeforeOriginalPdfExport(invoice); + }; const hasOnlyEligibleSelected = selectedIds.length > 0 && selectedIds.every(id => { const invoice = invoices?.find(inv => inv.id === id); - return invoice && isEligibleForExport(invoice); + return invoice && isEligibleForPdfExport(invoice); }); const sortedInvoices = [...(filteredInvoices || [])].sort((a, b) => { @@ -403,7 +408,7 @@ export default function Invoices({ scope = "all" }: { scope?: InvoiceScope }) { onClick={handleExport} disabled={selectedIds.length === 0 || !hasOnlyEligibleSelected || exportMutation.isPending} className="bg-blue-600 hover:bg-blue-700" - title={!hasOnlyEligibleSelected && selectedIds.length > 0 ? "Seules les factures avec un score de 100% peuvent être exportées" : ""} + title={!hasOnlyEligibleSelected && selectedIds.length > 0 ? "Les factures hors abonnement doivent être validées BAP avant l'export PDF" : ""} > PDF ({selectedIds.length}) diff --git a/client/src/pages/InvoicesBAP.tsx b/client/src/pages/InvoicesBAP.tsx index 2a61f5e..06c2bb1 100644 --- a/client/src/pages/InvoicesBAP.tsx +++ b/client/src/pages/InvoicesBAP.tsx @@ -33,6 +33,7 @@ import { } from "@/components/ui/table"; import { trpc } from "@/lib/trpc"; import { BAP_MIN_QUALITY_SCORE, meetsBapQualityThreshold } from "@shared/bapEligibility"; +import { wasExportedBeforeBap } from "@shared/bapExportGuard"; import { Search, FileText, Download, FileSpreadsheet, Trash2, Edit, Trash, CheckCircle, CheckCircle2, ShieldCheck, RefreshCw, FolderDown, ChevronDown, ChevronRight, ChevronsUpDown, CalendarDays, ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -410,7 +411,6 @@ export default function InvoicesBAP() { const isEligibleForBAPValidation = (invoice: any) => { return ( meetsBapQualityThreshold(invoice.qualityScore) && - invoice.exportStatus !== "exported" && invoice.isSubscription === 0 && invoice.serviceConcerne && invoice.typeAchat && @@ -547,7 +547,10 @@ export default function InvoicesBAP() { exportExcelMutation.mutate({ invoiceIds: selectedIds }); }; - const getExportStatusBadge = (status: string) => { + const getExportStatusBadge = (status: string, invoice?: any) => { + if (invoice && wasExportedBeforeBap(invoice)) { + return Exporté avant BAP; + } switch (status) { case "exported": return Exporté; @@ -586,7 +589,6 @@ export default function InvoicesBAP() { const getBAPValidationTooltip = (invoice: any): string => { const reasons: string[] = []; if (!meetsBapQualityThreshold(invoice.qualityScore)) reasons.push(`Score < ${BAP_MIN_QUALITY_SCORE}%`); - if (invoice.exportStatus === "exported") reasons.push("Déjà exportée"); if (invoice.isSubscription !== 0) reasons.push("Marquée comme abonnement"); if (!invoice.serviceConcerne) reasons.push("Service manquant"); if (!invoice.typeAchat) reasons.push("Type d'achat manquant"); @@ -1042,7 +1044,7 @@ export default function InvoicesBAP() { )} - {getExportStatusBadge(invoice.exportStatus || "not_exported")} + {getExportStatusBadge(invoice.exportStatus || "not_exported", invoice)}