diff --git a/client/src/pages/Avoirs.tsx b/client/src/pages/Avoirs.tsx index 62fe7b9..c343bd0 100644 --- a/client/src/pages/Avoirs.tsx +++ b/client/src/pages/Avoirs.tsx @@ -98,6 +98,12 @@ const avoirSchema = z.object({ type AvoirFormData = z.infer; +// Type étendu pour les mises à jour incluant les champs de validation +type AvoirUpdateData = AvoirFormData & { + nocodbVerified?: boolean; + nocodbVerifiedAt?: Date | null; +}; + export default function Avoirs() { const [searchTerm, setSearchTerm] = useState(""); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); @@ -198,7 +204,7 @@ export default function Avoirs() { // Edit avoir mutation const editAvoirMutation = useMutation({ - mutationFn: async ({ id, data }: { id: number, data: AvoirFormData }) => { + mutationFn: async ({ id, data }: { id: number, data: AvoirUpdateData }) => { const response = await fetch(`/api/avoirs/${id}`, { method: 'PUT', headers: { @@ -336,6 +342,66 @@ export default function Avoirs() { setIsDeleteDialogOpen(true); }; + // Handle status change + const handleStatusChange = (avoirId: number, newStatus: string) => { + const avoir = avoirs.find(a => a.id === avoirId); + if (avoir) { + editAvoirMutation.mutate({ + id: avoirId, + data: { + supplierId: avoir.supplierId, + groupId: avoir.groupId, + invoiceReference: avoir.invoiceReference || "", + amount: avoir.amount, + comment: avoir.comment || "", + commercialProcessed: avoir.commercialProcessed, + status: newStatus as "En attente de demande" | "Demandé" | "Reçu", + } + }); + } + }; + + // Handle validation/devalidation + const handleValidateAvoir = (avoirId: number) => { + const avoir = avoirs.find(a => a.id === avoirId); + if (avoir) { + editAvoirMutation.mutate({ + id: avoirId, + data: { + supplierId: avoir.supplierId, + groupId: avoir.groupId, + invoiceReference: avoir.invoiceReference || "", + amount: avoir.amount, + comment: avoir.comment || "", + commercialProcessed: avoir.commercialProcessed, + status: avoir.status as "En attente de demande" | "Demandé" | "Reçu", + nocodbVerified: true, + nocodbVerifiedAt: new Date(), + } + }); + } + }; + + const handleDevalidateAvoir = (avoirId: number) => { + const avoir = avoirs.find(a => a.id === avoirId); + if (avoir) { + editAvoirMutation.mutate({ + id: avoirId, + data: { + supplierId: avoir.supplierId, + groupId: avoir.groupId, + invoiceReference: avoir.invoiceReference || "", + amount: avoir.amount, + comment: avoir.comment || "", + commercialProcessed: avoir.commercialProcessed, + status: avoir.status as "En attente de demande" | "Demandé" | "Reçu", + nocodbVerified: false, + nocodbVerifiedAt: null, + } + }); + } + }; + // 🔍 FONCTION DE VÉRIFICATION DE FACTURE (comme rapprochement) const verifyAvoirInvoiceMutation = useMutation({ mutationFn: async ({ avoirId, invoiceReference, forceRefresh }: { avoirId: number; invoiceReference?: string; forceRefresh?: boolean }) => { @@ -558,6 +624,13 @@ export default function Avoirs() { description: "Avoir traité avec succès via le webhook", }); + // Vérification automatique de la facture après envoi réussi + if (selectedAvoirForUpload.invoiceReference?.trim()) { + setTimeout(() => { + handleVerifyAvoirInvoice(selectedAvoirForUpload, true); + }, 1000); // Délai de 1 seconde pour laisser le temps aux données de se synchroniser + } + // Reset des données setSelectedAvoirForUpload(null); setSelectedFile(null); @@ -921,17 +994,24 @@ export default function Avoirs() { const canEditDelete = ['admin', 'directeur'].includes((user as any)?.role); return ( - + {avoir.supplier?.name || 'Fournisseur non défini'} -
- {getStatusIcon(avoir.status)} - - {avoir.status} - -
+ #{avoir.invoiceReference || 'Sans référence'} @@ -1010,6 +1090,33 @@ export default function Avoirs() { )} + + {/* Bouton Valider - apparaît si vérification réussie et pas encore validé */} + {avoirVerificationResults[avoir.id]?.exists && !avoir.nocodbVerified && ( + + )} + + {/* Bouton Dévalider - pour admins sur avoirs validés */} + {avoir.nocodbVerified && canEditDelete && ( + + )} + {canEditDelete && ( <>