feat: bouton Dévalider BAP, barre actions 2 lignes, suppression colonne Utilisateur
This commit is contained in:
@@ -284,6 +284,17 @@ export default function InvoicesBAP() {
|
||||
},
|
||||
});
|
||||
|
||||
const devalidateBAPMutation = trpc.invoices.devalidateBAP.useMutation({
|
||||
onSuccess: (data) => {
|
||||
toast.success(`${data.processed} facture(s) dévalidée(s) BAP avec succès`);
|
||||
setSelectedIds([]);
|
||||
utils.invoices.list.invalidate();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message || "Erreur lors de la dévalidation BAP");
|
||||
},
|
||||
});
|
||||
|
||||
const updateFieldMutation = trpc.invoices.update.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success("Facture mise à jour");
|
||||
@@ -556,8 +567,10 @@ export default function InvoicesBAP() {
|
||||
<h1 className="text-3xl font-bold">Factures BAP</h1>
|
||||
<p className="text-gray-500 mt-1">Factures non-abonnement (Abonnement = NON)</p>
|
||||
</div>
|
||||
{/* Barre d'actions : boutons sur une ligne dédiée avec retour à la ligne automatique */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{/* Barre d'actions : 2 lignes */}
|
||||
<div className="space-y-2">
|
||||
{/* Ligne 1 : Excel, ZIP, Valider tout en BAP, Importer */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
onClick={handleExportExcel}
|
||||
disabled={selectedIds.length === 0 || exportExcelMutation.isPending}
|
||||
@@ -567,17 +580,8 @@ export default function InvoicesBAP() {
|
||||
<FileSpreadsheet className="w-4 h-4 mr-2" />
|
||||
Excel ({selectedIds.length})
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleExport}
|
||||
disabled={selectedIds.length === 0 || exportMutation.isPending}
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Exporter ({selectedIds.length})
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
// Construire la liste des PDFs validés parmi la sélection
|
||||
const files = selectedIds
|
||||
.map(id => {
|
||||
const inv = invoices?.find(i => i.id === id);
|
||||
@@ -612,6 +616,25 @@ export default function InvoicesBAP() {
|
||||
<FolderDown className={`w-4 h-4 mr-2 ${isZipDownloading ? 'animate-bounce' : ''}`} />
|
||||
{isZipDownloading ? 'ZIP...' : `ZIP (${selectedIds.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (confirm("Valider en BAP toutes les factures éligibles (score 100%, champs remplis, non abonnement) ? Les PDFs annotés seront générés automatiquement.")) {
|
||||
validateBAPBulkMutation.mutate();
|
||||
}
|
||||
}}
|
||||
disabled={validateBAPBulkMutation.isPending}
|
||||
className="bg-green-700 hover:bg-green-800 text-white"
|
||||
>
|
||||
<ShieldCheck className="w-4 h-4 mr-2" />
|
||||
{validateBAPBulkMutation.isPending ? "Validation en cours..." : "Valider tout en BAP"}
|
||||
</Button>
|
||||
<Button onClick={() => setLocation("/upload")}>
|
||||
<FileText className="w-4 h-4 mr-2" />
|
||||
Importer
|
||||
</Button>
|
||||
</div>
|
||||
{/* Ligne 2 : Supprimer, Dévalider BAP, Relancer */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
onClick={async () => {
|
||||
if (confirm(`Voulez-vous vraiment supprimer ${selectedIds.length} facture(s) ?`)) {
|
||||
@@ -635,15 +658,21 @@ export default function InvoicesBAP() {
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (confirm("Valider en BAP toutes les factures éligibles (score 100%, champs remplis, non abonnement) ? Les PDFs annotés seront générés automatiquement.")) {
|
||||
validateBAPBulkMutation.mutate();
|
||||
const bapIds = selectedIds.filter(id => invoices?.find(inv => inv.id === id)?.bapValidated === 1);
|
||||
if (bapIds.length === 0) {
|
||||
toast.error("Aucune facture BAP validée parmi la sélection");
|
||||
return;
|
||||
}
|
||||
if (confirm(`Dévalider ${bapIds.length} facture(s) BAP ? Elles repasser ont en statut à traiter.`)) {
|
||||
devalidateBAPMutation.mutate({ invoiceIds: bapIds });
|
||||
}
|
||||
}}
|
||||
disabled={validateBAPBulkMutation.isPending}
|
||||
className="bg-green-700 hover:bg-green-800 text-white"
|
||||
disabled={selectedIds.length === 0 || devalidateBAPMutation.isPending}
|
||||
variant="outline"
|
||||
className="border-orange-500 text-orange-600 hover:bg-orange-50"
|
||||
>
|
||||
<ShieldCheck className="w-4 h-4 mr-2" />
|
||||
{validateBAPBulkMutation.isPending ? "Validation en cours..." : "Valider tout en BAP"}
|
||||
<ShieldCheck className="w-4 h-4 mr-2 rotate-180" />
|
||||
{devalidateBAPMutation.isPending ? "Dévalidation..." : `Dévalider BAP (${selectedIds.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -662,10 +691,7 @@ export default function InvoicesBAP() {
|
||||
<RefreshCw className={`w-4 h-4 mr-2 ${reprocessMutation.isPending ? 'animate-spin' : ''}`} />
|
||||
{reprocessMutation.isPending ? `Retraitement...` : `Relancer (${selectedIds.length})`}
|
||||
</Button>
|
||||
<Button onClick={() => setLocation("/upload")}>
|
||||
<FileText className="w-4 h-4 mr-2" />
|
||||
Importer
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
|
||||
@@ -654,6 +654,24 @@ export const appRouter = router({
|
||||
};
|
||||
}),
|
||||
|
||||
// ── Dévalidation BAP ──────────────────────────────────────
|
||||
devalidateBAP: protectedProcedure
|
||||
.input(z.object({ invoiceIds: z.array(z.number()) }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
let processed = 0;
|
||||
for (const id of input.invoiceIds) {
|
||||
const invoice = await getInvoiceById(id);
|
||||
if (!invoice || invoice.userId !== ctx.user.id) continue;
|
||||
await updateInvoice(id, {
|
||||
bapValidated: 0,
|
||||
bapValidatedAt: null,
|
||||
exportStatus: "not_exported",
|
||||
});
|
||||
processed++;
|
||||
}
|
||||
return { success: true, processed };
|
||||
}),
|
||||
|
||||
// ── Validation BAP en masse ──────────────────────────────
|
||||
validateBAPBulk: protectedProcedure
|
||||
.mutation(async ({ ctx }) => {
|
||||
|
||||
Reference in New Issue
Block a user