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({
|
const updateFieldMutation = trpc.invoices.update.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Facture mise à jour");
|
toast.success("Facture mise à jour");
|
||||||
@@ -556,7 +567,9 @@ export default function InvoicesBAP() {
|
|||||||
<h1 className="text-3xl font-bold">Factures BAP</h1>
|
<h1 className="text-3xl font-bold">Factures BAP</h1>
|
||||||
<p className="text-gray-500 mt-1">Factures non-abonnement (Abonnement = NON)</p>
|
<p className="text-gray-500 mt-1">Factures non-abonnement (Abonnement = NON)</p>
|
||||||
</div>
|
</div>
|
||||||
{/* Barre d'actions : boutons sur une ligne dédiée avec retour à la ligne automatique */}
|
{/* 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">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={handleExportExcel}
|
onClick={handleExportExcel}
|
||||||
@@ -567,17 +580,8 @@ export default function InvoicesBAP() {
|
|||||||
<FileSpreadsheet className="w-4 h-4 mr-2" />
|
<FileSpreadsheet className="w-4 h-4 mr-2" />
|
||||||
Excel ({selectedIds.length})
|
Excel ({selectedIds.length})
|
||||||
</Button>
|
</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
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// Construire la liste des PDFs validés parmi la sélection
|
|
||||||
const files = selectedIds
|
const files = selectedIds
|
||||||
.map(id => {
|
.map(id => {
|
||||||
const inv = invoices?.find(i => i.id === 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' : ''}`} />
|
<FolderDown className={`w-4 h-4 mr-2 ${isZipDownloading ? 'animate-bounce' : ''}`} />
|
||||||
{isZipDownloading ? 'ZIP...' : `ZIP (${selectedIds.length})`}
|
{isZipDownloading ? 'ZIP...' : `ZIP (${selectedIds.length})`}
|
||||||
</Button>
|
</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
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (confirm(`Voulez-vous vraiment supprimer ${selectedIds.length} facture(s) ?`)) {
|
if (confirm(`Voulez-vous vraiment supprimer ${selectedIds.length} facture(s) ?`)) {
|
||||||
@@ -635,15 +658,21 @@ export default function InvoicesBAP() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
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.")) {
|
const bapIds = selectedIds.filter(id => invoices?.find(inv => inv.id === id)?.bapValidated === 1);
|
||||||
validateBAPBulkMutation.mutate();
|
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}
|
disabled={selectedIds.length === 0 || devalidateBAPMutation.isPending}
|
||||||
className="bg-green-700 hover:bg-green-800 text-white"
|
variant="outline"
|
||||||
|
className="border-orange-500 text-orange-600 hover:bg-orange-50"
|
||||||
>
|
>
|
||||||
<ShieldCheck className="w-4 h-4 mr-2" />
|
<ShieldCheck className="w-4 h-4 mr-2 rotate-180" />
|
||||||
{validateBAPBulkMutation.isPending ? "Validation en cours..." : "Valider tout en BAP"}
|
{devalidateBAPMutation.isPending ? "Dévalidation..." : `Dévalider BAP (${selectedIds.length})`}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -662,10 +691,7 @@ export default function InvoicesBAP() {
|
|||||||
<RefreshCw className={`w-4 h-4 mr-2 ${reprocessMutation.isPending ? 'animate-spin' : ''}`} />
|
<RefreshCw className={`w-4 h-4 mr-2 ${reprocessMutation.isPending ? 'animate-spin' : ''}`} />
|
||||||
{reprocessMutation.isPending ? `Retraitement...` : `Relancer (${selectedIds.length})`}
|
{reprocessMutation.isPending ? `Retraitement...` : `Relancer (${selectedIds.length})`}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => setLocation("/upload")}>
|
</div>
|
||||||
<FileText className="w-4 h-4 mr-2" />
|
|
||||||
Importer
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<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 ──────────────────────────────
|
// ── Validation BAP en masse ──────────────────────────────
|
||||||
validateBAPBulk: protectedProcedure
|
validateBAPBulk: protectedProcedure
|
||||||
.mutation(async ({ ctx }) => {
|
.mutation(async ({ ctx }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user