import { NextRequest, NextResponse } from "next/server"; import { createSbServiceRole } from "@/lib/supabaseServer"; import { sendUniversalEmailV2, EmailDataV2 } from "@/lib/emailTemplateService"; export const dynamic = "force-dynamic"; /** * Webhook appelé quand TOUTES les parties ont signé l'avenant (employeur + salarié) * Envoie les emails de confirmation finaux aux deux parties * * Flux : * 1. Employeur signe → Lambda postDocuSealAvenantSalarie → /api/webhooks/docuseal-amendment * 2. Salarié signe → DocuSeal webhook → Cette route * 3. Envoi emails de confirmation à l'employeur ET au salarié */ export async function POST(request: NextRequest) { console.log("🎉 [WEBHOOK AVENANT COMPLETED] Début du traitement"); try { const body = await request.json(); console.log("📦 [WEBHOOK AVENANT COMPLETED] Body reçu:", JSON.stringify(body, null, 2)); // Extraire les données du webhook DocuSeal const eventType = body.event_type; const submissionId = body.data?.submission?.id || body.data?.submission_id; const templateExternalId = body.data?.template?.external_id; const submitters = body.data?.submitters || body.data?.submission?.submitters || []; console.log("🔍 [WEBHOOK AVENANT COMPLETED] Event type:", eventType); console.log("🔍 [WEBHOOK AVENANT COMPLETED] Submission ID:", submissionId); console.log("🔍 [WEBHOOK AVENANT COMPLETED] Template external ID:", templateExternalId); console.log("🔍 [WEBHOOK AVENANT COMPLETED] Submitters dans payload:", JSON.stringify(submitters, null, 2)); // Vérifier que c'est bien un événement de complétion FINALE (form.completed) // "submission.completed" est envoyé à CHAQUE signature individuelle // "form.completed" est envoyé UNE SEULE FOIS quand TOUS ont signé if (eventType !== "form.completed") { console.log("ℹ️ [WEBHOOK AVENANT COMPLETED] Event type non géré (attendu: form.completed):", eventType); return NextResponse.json({ message: "Event type ignoré - attendu form.completed" }, { status: 200 }); } if (!submissionId) { console.error("❌ [WEBHOOK AVENANT COMPLETED] submission_id manquant"); return NextResponse.json( { error: "submission_id requis" }, { status: 400 } ); } // Récupérer les submitters via l'API DocuSeal car le payload ne les contient pas tous console.log("🔍 [WEBHOOK AVENANT COMPLETED] Récupération des submitters via API DocuSeal..."); let allSubmitters: any[] = []; try { const docusealResponse = await fetch( `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/api/docuseal/submissions/${submissionId}`, { method: 'GET', cache: 'no-store', } ); if (docusealResponse.ok) { const submission = await docusealResponse.json(); allSubmitters = submission.submitters || []; console.log("✅ [WEBHOOK AVENANT COMPLETED] Submitters récupérés:", allSubmitters.map((s: any) => ({ role: s.role, email: s.email, status: s.status }))); } else { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur récupération submitters"); } } catch (error) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur API DocuSeal:", error); } // IMPORTANT: Vérifier que TOUS les submitters ont signé (status = 'completed') const allSigned = allSubmitters.length > 0 && allSubmitters.every((s: any) => s.status === 'completed'); if (!allSigned) { console.log("⏸️ [WEBHOOK AVENANT COMPLETED] Tous les submitters n'ont pas encore signé"); console.log("📋 [WEBHOOK AVENANT COMPLETED] Statuts:", allSubmitters.map((s: any) => ({ role: s.role, email: s.email, status: s.status }))); return NextResponse.json({ message: "En attente de toutes les signatures", submitters: allSubmitters.map((s: any) => ({ role: s.role, status: s.status })) }, { status: 200 }); } console.log("✅ [WEBHOOK AVENANT COMPLETED] Toutes les signatures sont complètes !"); const supabase = createSbServiceRole(); // 1. Récupérer l'avenant depuis Supabase via le docuseal_submission_id console.log("🔍 [WEBHOOK AVENANT COMPLETED] Recherche de l'avenant..."); const { data: avenant, error: avenantError } = await supabase .from("avenants") .select(` *, cddu_contracts ( *, salaries ( prenom, nom, adresse_mail ), organizations ( id, name ) ) `) .eq("docuseal_submission_id", submissionId) .maybeSingle(); if (avenantError || !avenant) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Avenant non trouvé:", avenantError); return NextResponse.json( { error: "Avenant non trouvé", details: avenantError?.message }, { status: 404 } ); } console.log("✅ [WEBHOOK AVENANT COMPLETED] Avenant trouvé:", { id: avenant.id, numero: avenant.numero_avenant, contract_id: avenant.contract_id, }); // 2. Mettre à jour le statut de l'avenant console.log("🔄 [WEBHOOK AVENANT COMPLETED] Mise à jour du statut..."); const { error: updateError } = await supabase .from("avenants") .update({ signature_status: "signed", }) .eq("id", avenant.id); if (updateError) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur mise à jour avenant:", updateError); } else { console.log("✅ [WEBHOOK AVENANT COMPLETED] Statut avenant mis à jour: signed"); } // 2b. Mettre à jour le contrat pour marquer l'avenant comme signé if (avenant.contract_id) { console.log("🔄 [WEBHOOK AVENANT COMPLETED] Mise à jour du contrat..."); const { error: contractUpdateError } = await supabase .from("cddu_contracts") .update({ avenant_signe: true, avenant_signe_date: new Date().toISOString(), }) .eq("id", avenant.contract_id); if (contractUpdateError) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur mise à jour contrat:", contractUpdateError); } else { console.log("✅ [WEBHOOK AVENANT COMPLETED] Contrat mis à jour: avenant_signe = true"); } } // 3. Préparer les données pour les emails const contract = avenant.cddu_contracts; const salarie = contract?.salaries; const organization = contract?.organizations; if (!contract || !salarie || !organization) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Données manquantes:", { hasContract: !!contract, hasSalarie: !!salarie, hasOrganization: !!organization, }); return NextResponse.json( { error: "Données du contrat/salarié/organisation manquantes" }, { status: 400 } ); } // Formater la date const formatDate = (dateStr?: string | null) => { if (!dateStr) return "-"; try { const parts = dateStr.split("-"); if (parts.length === 3) { const [y, m, d] = parts; return `${d}/${m}/${y}`; } return dateStr; } catch { return dateStr; } }; const startDate = formatDate(contract.start_date); const employeeEmail = salarie.adresse_mail; // Récupérer les infos employeur depuis organization_details let employerCode = "Non spécifié"; let employerFirstName = "Employeur"; let employerEmail = "hello@odentas.fr"; if (organization.id) { const { data: orgDetails } = await supabase .from("organization_details") .select("code_employeur, prenom_signataire, email_signature") .eq("org_id", organization.id) .maybeSingle(); if (orgDetails) { if (orgDetails.code_employeur) { employerCode = orgDetails.code_employeur; } if (orgDetails.prenom_signataire) { employerFirstName = orgDetails.prenom_signataire; } if (orgDetails.email_signature) { employerEmail = orgDetails.email_signature; } } } // 4. Envoyer l'email à l'EMPLOYEUR console.log("📧 [WEBHOOK AVENANT COMPLETED] Envoi email employeur..."); const employerEmailData: EmailDataV2 = { firstName: employerFirstName, organizationName: organization.name || "Votre structure", employerCode: employerCode, handlerName: "Renaud BREVIERE-ABRAHAM", contractReference: contract.contract_number || "Non spécifié", startDate: startDate, profession: contract.profession || "Non spécifié", productionName: contract.analytique || contract.production_name || "Non spécifié", ctaUrl: "https://paie.odentas.fr/", ctaText: "Accès à l'Espace Paie", numeroAvenant: avenant.numero_avenant, employeeName: `${salarie.prenom} ${salarie.nom}`, matricule: contract.matricule || contract.employee_matricule || "Non spécifié", contractType: "CDDU (contrat intermittent)", }; try { const employerMessageId = await sendUniversalEmailV2({ type: "amendment-completed-employer", toEmail: employerEmail, data: employerEmailData, }); console.log("✅ [WEBHOOK AVENANT COMPLETED] Email employeur envoyé:", employerMessageId); } catch (emailError: any) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur email employeur:", emailError); } // 5. Envoyer l'email au SALARIÉ console.log("📧 [WEBHOOK AVENANT COMPLETED] Envoi email salarié..."); // Récupérer le slug DocuSeal du salarié depuis l'avenant (stocké lors de la signature employeur) const employeeDocusealSlug = avenant.employee_docuseal_slug || ""; console.log("🔑 [WEBHOOK AVENANT COMPLETED] Slug salarié:", employeeDocusealSlug); const employeeEmailData: EmailDataV2 = { firstName: salarie.prenom || "Salarié", organizationName: organization.name || "Votre employeur", matricule: contract.matricule || contract.employee_matricule || "Non spécifié", contractReference: contract.contract_number || "Non spécifié", startDate: startDate, profession: contract.profession || "Non spécifié", productionName: contract.analytique || contract.production_name || "Non spécifié", ctaUrl: employeeDocusealSlug ? `https://paie.odentas.fr/signature-salarie/?docuseal_id=${employeeDocusealSlug}` : `https://paie.odentas.fr/`, ctaText: "Télécharger l'avenant", numeroAvenant: avenant.numero_avenant, contractType: "CDDU (contrat intermittent)", }; try { const employeeMessageId = await sendUniversalEmailV2({ type: "amendment-completed-employee", toEmail: employeeEmail, data: employeeEmailData, }); console.log("✅ [WEBHOOK AVENANT COMPLETED] Email salarié envoyé:", employeeMessageId); } catch (emailError: any) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur email salarié:", emailError); } return NextResponse.json({ success: true, message: "Emails de confirmation envoyés", avenantId: avenant.id, numeroAvenant: avenant.numero_avenant, }); } catch (error: any) { console.error("❌ [WEBHOOK AVENANT COMPLETED] Erreur:", error); return NextResponse.json( { error: "Erreur lors du traitement", details: error.message, }, { status: 500 } ); } }