espace-paie-odentas/app/api/webhooks/docuseal-contract/route.ts
odentas 897af4b23a feat: Ajout fonctionnalités virements, facturation, signatures et emails
- Ajout sous-header total net à payer sur page virements-salaires
- Migration transfer_done_at pour tracking précis des virements
- Nouvelle page saisie tableau pour création factures en masse
- APIs bulk pour mise à jour dates signature et jours technicien
- API demande mandat SEPA avec email template
- Webhook DocuSeal pour signature contrats (mode TEST)
- Composants modaux détails et vérification PDF fiches de paie
- Upload/suppression/remplacement PDFs dans PayslipsGrid
- Amélioration affichage colonnes et filtres grilles contrats/paies
- Template email mandat SEPA avec sous-texte CTA
- APIs bulk facturation (création, update statut/date paiement)
- API clients sans facture pour période donnée
- Corrections calculs dates et montants avec auto-remplissage
2025-11-02 23:26:19 +01:00

211 lines
7.2 KiB
TypeScript

import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
const DOCUSEAL_TOKEN = process.env.DOCUSEAL_TOKEN;
const DOCUSEAL_API_BASE_URL = process.env.DOCUSEAL_API_BASE || 'https://api.docuseal.eu';
/**
* Webhook DocuSeal pour la signature des contrats (CDDU et RG)
* Mode TEST : Lecture seule, pas d'impact sur la production
*
* Cette route remplace les Lambda Functions AWS :
* - lambdaRouterDocuseal
* - postDocuSealSalarie
* - postDocuSealFinalEmails
*/
export async function POST(request: Request) {
const TEST_MODE = true; // ⚠️ Mode TEST activé - Pas de modifications en BDD ni d'envoi d'emails
console.log('🔔 [DOCUSEAL WEBHOOK TEST] Réception webhook contrat');
try {
// 1. Parse le payload
const payload = await request.json();
console.log('📦 [TEST] Payload reçu:', JSON.stringify(payload, null, 2));
// 2. Vérifications de base
const eventType = payload.event_type || payload.event;
console.log('📋 [TEST] Event type:', eventType);
if (eventType !== 'form.completed') {
console.log('⏭️ [TEST] Event ignoré (pas form.completed)');
return NextResponse.json({
received: true,
ignored: true,
reason: 'Event type non géré',
test_mode: true
});
}
// 3. Extraire les données importantes
const data = payload.data;
const documents = data?.documents || [];
const role = data?.role;
const submissionId = data?.id; // ID de la submission DocuSeal
console.log('🔍 [TEST] Données extraites:', {
role,
submissionId,
documentsCount: documents.length,
documentNames: documents.map((d: any) => d.name)
});
// 4. Filtrer : ne traiter que les documents "contrat*"
const contratDocs = documents.filter((doc: any) =>
doc.name && doc.name.toLowerCase().startsWith('contrat')
);
if (contratDocs.length === 0) {
console.log('⏭️ [TEST] Aucun document "contrat" trouvé (probablement un avenant)');
return NextResponse.json({
received: true,
ignored: true,
reason: 'Pas de document contrat',
test_mode: true
});
}
console.log('✅ [TEST] Document contrat trouvé:', contratDocs[0].name);
// 5. Chercher le contrat dans la BDD via docuseal_submission_id
const supabase = createRouteHandlerClient({ cookies });
console.log('🔍 [TEST] Recherche du contrat avec submission_id:', submissionId);
const { data: contract, error: contractError } = await supabase
.from('cddu_contracts')
.select('*')
.eq('docuseal_submission_id', submissionId)
.single();
if (contractError || !contract) {
console.error('❌ [TEST] Contrat non trouvé:', contractError);
return NextResponse.json({
received: true,
error: 'Contract not found',
submissionId,
test_mode: true
}, { status: 404 });
}
console.log('✅ [TEST] Contrat trouvé:', {
id: contract.id,
contract_number: contract.contract_number,
employee_id: contract.employee_id,
signature_status: contract.signature_status,
contrat_signe_par_employeur: contract.contrat_signe_par_employeur,
contrat_signe: contract.contrat_signe,
contract_pdf_s3_key: contract.contract_pdf_s3_key
});
// 6. Router selon le rôle (Employeur ou Salarié)
if (role === 'Employeur') {
console.log('👔 [TEST] === SIGNATURE EMPLOYEUR ===');
// Récupérer le slug du salarié depuis DocuSeal API
console.log('🔍 [TEST] Récupération du employee_docuseal_slug depuis DocuSeal API');
const docusealResponse = await fetch(
`${DOCUSEAL_API_BASE_URL}/submissions/${submissionId}`,
{
headers: {
'X-Auth-Token': DOCUSEAL_TOKEN || '',
'Content-Type': 'application/json'
}
}
);
if (!docusealResponse.ok) {
console.error('❌ [TEST] Erreur DocuSeal API:', docusealResponse.status);
return NextResponse.json({
error: 'DocuSeal API error',
test_mode: true
}, { status: 500 });
}
const submissionData = await docusealResponse.json();
console.log('📦 [TEST] Données submission DocuSeal:', JSON.stringify(submissionData, null, 2));
// Trouver le submitter avec le rôle "Salarié"
const employeeSubmitter = submissionData.submitters?.find(
(s: any) => s.role === 'Salarié'
);
const employeeSlug = employeeSubmitter?.slug;
console.log('🔗 [TEST] Employee slug trouvé:', employeeSlug);
if (TEST_MODE) {
console.log('⚠️ [TEST MODE] On NE met PAS à jour la BDD');
console.log('📝 [TEST] Données qui seraient mises à jour:', {
signature_status: 'pending_employee',
contrat_signe_par_employeur: 'Oui',
employee_docuseal_slug: employeeSlug,
last_employer_notification_at: new Date().toISOString()
});
console.log('📧 [TEST] Email qui serait envoyé au salarié (NON ENVOYÉ EN MODE TEST)');
}
return NextResponse.json({
success: true,
test_mode: true,
role: 'Employeur',
contract_id: contract.id,
contract_number: contract.contract_number,
employee_slug: employeeSlug,
message: 'TEST MODE : Aucune modification effectuée'
});
} else if (role === 'Salarié') {
console.log('👤 [TEST] === SIGNATURE SALARIE ===');
// URL du PDF signé
const pdfUrl = contratDocs[0].url;
console.log('📄 [TEST] URL du PDF signé:', pdfUrl);
if (TEST_MODE) {
console.log('⚠️ [TEST MODE] On NE télécharge PAS le PDF');
console.log('⚠️ [TEST MODE] On N\'upload PAS sur S3');
console.log('⚠️ [TEST MODE] On NE met PAS à jour la BDD');
console.log('📝 [TEST] Données qui seraient mises à jour:', {
signature_status: 'signed',
contrat_signe: 'Oui',
date_signature: new Date().toISOString()
});
console.log('📧 [TEST] Emails de confirmation qui seraient envoyés (NON ENVOYÉS EN MODE TEST)');
console.log('🪣 [TEST] S3 upload qui serait fait:', {
bucket: 'odentas-docs',
key: contract.contract_pdf_s3_key
});
}
return NextResponse.json({
success: true,
test_mode: true,
role: 'Salarié',
contract_id: contract.id,
contract_number: contract.contract_number,
pdf_url: pdfUrl,
s3_key: contract.contract_pdf_s3_key,
message: 'TEST MODE : Aucune modification effectuée'
});
} else {
console.warn('⚠️ [TEST] Rôle inconnu:', role);
return NextResponse.json({
received: true,
error: 'Unknown role',
role,
test_mode: true
}, { status: 400 });
}
} catch (error) {
console.error('❌ [TEST] Erreur dans le webhook:', error);
return NextResponse.json({
error: 'Internal server error',
details: error instanceof Error ? error.message : 'Unknown error',
test_mode: true
}, { status: 500 });
}
}