From 986c8e6ce006b7f618db7c3f24bee111d468ea3b Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Mon, 8 Sep 2025 17:43:27 +0000 Subject: [PATCH] Send credit note details via webhook when status changes to received MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement webhook functionality to send credit note information to configured groups when the status is updated to 'Reçu'. The webhook payload includes credit note details, sender information, and group details. Default group ID is set to 1 for administrators if not specified. Also, adds a visual indicator for sent webhooks on the frontend. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/9fcf4b21-eb0c-4e53-a567-e2ce4a6ad869/liv4dfe --- client/src/pages/Avoirs.tsx | 13 +++++++---- server/routes.ts | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/client/src/pages/Avoirs.tsx b/client/src/pages/Avoirs.tsx index 13d601a..046dd2e 100644 --- a/client/src/pages/Avoirs.tsx +++ b/client/src/pages/Avoirs.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck } from "lucide-react"; +import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send } from "lucide-react"; import { useStore } from "@/components/Layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -159,7 +159,7 @@ export default function Avoirs() { // ✅ FIX: Invalider toutes les variations de queryKey avoirs queryClient.invalidateQueries({ predicate: (query) => { - return query.queryKey[0]?.toString().includes('/api/avoirs'); + return query.queryKey[0]?.toString().includes('/api/avoirs') || false; } }); setIsCreateDialogOpen(false); @@ -197,7 +197,7 @@ export default function Avoirs() { // ✅ FIX: Invalider toutes les variations de queryKey avoirs queryClient.invalidateQueries({ predicate: (query) => { - return query.queryKey[0]?.toString().includes('/api/avoirs'); + return query.queryKey[0]?.toString().includes('/api/avoirs') || false; } }); setIsEditDialogOpen(false); @@ -232,7 +232,7 @@ export default function Avoirs() { // ✅ FIX: Invalider toutes les variations de queryKey avoirs queryClient.invalidateQueries({ predicate: (query) => { - return query.queryKey[0]?.toString().includes('/api/avoirs'); + return query.queryKey[0]?.toString().includes('/api/avoirs') || false; } }); setIsDeleteDialogOpen(false); @@ -660,6 +660,11 @@ export default function Avoirs() { )} + {avoir.webhookSent && ( +
+ +
+ )} diff --git a/server/routes.ts b/server/routes.ts index 05f0cb0..320b74d 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -2508,6 +2508,51 @@ export async function registerRoutes(app: Express): Promise { const updatedAvoir = await storage.updateAvoir(id, validatedData); console.log('✅ Avoir updated:', id, 'by user:', user.id); + + // 🎯 WEBHOOK QUAND STATUT PASSE À "Reçu" + if (validatedData.status === 'Reçu' && existingAvoir.status !== 'Reçu') { + try { + // Utiliser groupe par défaut (1) pour admin si pas de groupe sélectionné + const groupId = updatedAvoir.groupId || (user.role === 'admin' ? 1 : updatedAvoir.groupId); + const group = await storage.getGroup(groupId); + + if (group && group.webhookUrl) { + const webhookData = { + type: "Avoir", + avoirId: updatedAvoir.id, + invoiceReference: updatedAvoir.invoiceReference, + amount: updatedAvoir.amount, + supplierName: "Fournisseur", // Sera enrichi avec relations + groupName: group.name, + comment: updatedAvoir.comment || "", + commercialProcessed: updatedAvoir.commercialProcessed, + status: "Reçu", + createdBy: user.firstName && user.lastName ? `${user.firstName} ${user.lastName}` : user.username, + processedAt: new Date().toISOString() + }; + + console.log('🌐 Envoi webhook avoir reçu:', { groupId, webhookUrl: group.webhookUrl }); + + const webhookResponse = await fetch(group.webhookUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(webhookData) + }); + + if (webhookResponse.ok) { + await storage.updateAvoirWebhookStatus(updatedAvoir.id, true); + console.log('✅ Webhook avoir reçu envoyé:', updatedAvoir.id); + } else { + console.error('❌ Échec envoi webhook avoir:', webhookResponse.status); + } + } + } catch (webhookError) { + console.error('❌ Erreur webhook avoir reçu:', webhookError); + } + } + res.json(updatedAvoir); } catch (error) { if (error instanceof z.ZodError) {