diff --git a/client/src/pages/Avoirs.tsx b/client/src/pages/Avoirs.tsx index 0758772..a6c376c 100644 --- a/client/src/pages/Avoirs.tsx +++ b/client/src/pages/Avoirs.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload, XCircle } from "lucide-react"; import { useStore } from "@/components/Layout"; @@ -362,42 +362,61 @@ export default function Avoirs() { }; // 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 handleValidateAvoir = async (avoirId: number) => { + try { + // Utiliser la route spécifique pour la vérification NocoDB + await apiRequest(`/api/avoirs/${avoirId}/nocodb-verification`, 'PUT', { + verified: true + }); + + // Marquer le cache comme réconcilié (permanent) + const avoir = avoirs.find(a => a.id === avoirId); + if (avoir?.invoiceReference) { + try { + await apiRequest('/api/cache/mark-reconciled', 'POST', { + invoiceReference: avoir.invoiceReference, + groupId: avoir.groupId + }); + } catch (cacheError) { + console.warn('Cache marking failed but verification succeeded:', cacheError); } + } + + // Recharger les avoirs pour voir les changements + queryClient.invalidateQueries({ queryKey: ['/api/avoirs'] }); + + toast({ + title: "Avoir validé", + description: "L'avoir a été marqué comme validé avec succès", + }); + } catch (error) { + toast({ + title: "Erreur de validation", + description: error instanceof Error ? error.message : "Erreur lors de la validation", + variant: "destructive", }); } }; - 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, - } + const handleDevalidateAvoir = async (avoirId: number) => { + try { + // Utiliser la route spécifique pour la vérification NocoDB + await apiRequest(`/api/avoirs/${avoirId}/nocodb-verification`, 'PUT', { + verified: false + }); + + // Recharger les avoirs pour voir les changements + queryClient.invalidateQueries({ queryKey: ['/api/avoirs'] }); + + toast({ + title: "Avoir dévalidé", + description: "L'avoir a été marqué comme non-validé avec succès", + }); + } catch (error) { + toast({ + title: "Erreur de dévalidation", + description: error instanceof Error ? error.message : "Erreur lors de la dévalidation", + variant: "destructive", }); } }; diff --git a/server/routes.ts b/server/routes.ts index cc5dfb2..f5c2199 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -2718,6 +2718,24 @@ export async function registerRoutes(app: Express): Promise { } await storage.updateAvoirNocodbVerification(id, verified); + + // Si validé, marquer le cache comme réconcilié (permanent) + if (verified) { + try { + const avoir = await storage.getAvoir(id); + if (avoir?.invoiceReference?.trim()) { + await invoiceVerificationService.updateCacheAsReconciled( + avoir.invoiceReference, + avoir.groupId + ); + console.log('✅ Cache marqué comme réconcilié pour avoir:', id); + } + } catch (cacheError) { + console.error('❌ Erreur marquage cache réconcilié:', cacheError); + // Ne pas faire échouer la validation si le cache échoue + } + } + console.log('✅ Avoir NocoDB verification updated:', id, 'verified:', verified, 'by user:', user.id); res.json({ message: "NocoDB verification status updated successfully" }); } catch (error) { @@ -2726,6 +2744,35 @@ export async function registerRoutes(app: Express): Promise { } }); + // Route pour marquer explicitement le cache comme réconcilié + app.post('/api/cache/mark-reconciled', isAuthenticated, async (req: any, res) => { + try { + const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id); + if (!user) { + return res.status(404).json({ message: "User not found" }); + } + + const { invoiceReference, groupId } = req.body; + + if (!invoiceReference || !groupId) { + return res.status(400).json({ message: "Invoice reference and group ID required" }); + } + + // Seuls admin et directeur peuvent marquer comme réconcilié + if (user.role !== 'admin' && user.role !== 'directeur') { + return res.status(403).json({ message: "Insufficient permissions" }); + } + + await invoiceVerificationService.updateCacheAsReconciled(invoiceReference, groupId); + console.log('✅ Cache marqué comme réconcilié:', { invoiceReference, groupId, user: user.id }); + + res.json({ message: "Cache marked as reconciled successfully" }); + } catch (error) { + console.error("Error marking cache as reconciled:", error); + res.status(500).json({ message: "Failed to mark cache as reconciled" }); + } + }); + // Statistics routes app.get('/api/stats/monthly', isAuthenticated, async (req: any, res) => { try {