225 lines
No EOL
7.8 KiB
TypeScript
225 lines
No EOL
7.8 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
|
|
import { cookies } from 'next/headers';
|
|
import { sendUniversalEmailV2, EmailDataV2 } from '@/lib/emailTemplateService';
|
|
import { ENV } from '@/lib/cleanEnv';
|
|
// Envoi via le système universel (pas de configuration SES directe ici)
|
|
|
|
// POST /api/signatures-electroniques/relance
|
|
// Envoie un email de relance pour la signature d'un contrat salarié
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const { contractId } = await req.json();
|
|
|
|
if (!contractId) {
|
|
return NextResponse.json({ error: 'Contract ID manquant' }, { status: 400 });
|
|
}
|
|
|
|
// Vérification de l'authentification
|
|
const sb = createRouteHandlerClient({ cookies });
|
|
const { data: { user } } = await sb.auth.getUser();
|
|
if (!user) {
|
|
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
|
}
|
|
|
|
// Vérifier staff pour lire la cible via cookie active_org_id
|
|
let isStaff = false;
|
|
try {
|
|
const { data } = await sb.from('staff_users').select('is_staff').eq('user_id', user.id).maybeSingle();
|
|
isStaff = !!data?.is_staff;
|
|
} catch {}
|
|
|
|
let orgId: string | null = null;
|
|
try {
|
|
if (isStaff) {
|
|
const c = cookies();
|
|
orgId = c.get('active_org_id')?.value || null;
|
|
} else {
|
|
const { data, error } = await sb
|
|
.from('organization_members')
|
|
.select('org_id')
|
|
.eq('user_id', user.id)
|
|
.single();
|
|
if (error || !data?.org_id) {
|
|
return NextResponse.json({ error: 'Aucune organisation active' }, { status: 403 });
|
|
}
|
|
orgId = data.org_id;
|
|
}
|
|
} catch {}
|
|
|
|
// Récupération des données du contrat depuis Supabase (cddu_contracts)
|
|
let query = sb
|
|
.from('cddu_contracts')
|
|
.select(`
|
|
id,
|
|
reference,
|
|
contract_number,
|
|
employee_name,
|
|
employee_matricule,
|
|
production_name,
|
|
role,
|
|
start_date,
|
|
end_date,
|
|
docuseal_template_id,
|
|
docuseal_submission_id,
|
|
signature_link,
|
|
org_id
|
|
`)
|
|
.eq('id', contractId);
|
|
|
|
if (orgId) {
|
|
query = query.eq('org_id', orgId);
|
|
}
|
|
|
|
const { data: contract, error: contractError } = await query.single();
|
|
|
|
if (contractError || !contract) {
|
|
console.error('Erreur récupération contrat:', contractError);
|
|
return NextResponse.json({ error: 'Contrat non trouvé' }, { status: 404 });
|
|
}
|
|
|
|
// Vérifier que c'est bien un contrat en attente de signature salarié
|
|
// Email: récupéré depuis la table salaries (colonne adresse_mail) à partir du matricule
|
|
|
|
// Récupération du slug du salarié via DocuSeal API
|
|
let employeeSlug: string | null = null;
|
|
let docusealEmail: string | null = null;
|
|
if (contract.docuseal_submission_id) {
|
|
try {
|
|
// Appel direct à l'API DocuSeal (pas de route interne)
|
|
const docusealResponse = await fetch(`https://api.docuseal.eu/submissions/${contract.docuseal_submission_id}`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Auth-Token': ENV.DOCUSEAL_TOKEN,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
if (docusealResponse.ok) {
|
|
const docusealData = await docusealResponse.json();
|
|
const submitters = docusealData?.submitters || [];
|
|
const employeeSubmitter = submitters.find((s: any) => s.role === 'Salarié');
|
|
employeeSlug = employeeSubmitter?.slug || null;
|
|
// Fallback email via DocuSeal si introuvable en base salaries (voir plus bas)
|
|
if (employeeSubmitter?.email) {
|
|
docusealEmail = String(employeeSubmitter.email);
|
|
}
|
|
} else {
|
|
console.error('DocuSeal API error:', docusealResponse.status, await docusealResponse.text());
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur récupération DocuSeal:', error);
|
|
}
|
|
}
|
|
|
|
// Construction du lien de signature
|
|
// TOUJOURS utiliser le slug du salarié (pas celui de l'employeur stocké dans signature_link)
|
|
let signatureLink: string | null = null;
|
|
if (employeeSlug) {
|
|
const siteBase = process.env.NEXT_PUBLIC_SITE_URL || 'https://paie.odentas.fr';
|
|
signatureLink = `${siteBase}/signature-salarie?docuseal_id=${employeeSlug}`;
|
|
}
|
|
|
|
if (!signatureLink) {
|
|
return NextResponse.json({ error: 'Lien de signature indisponible' }, { status: 400 });
|
|
}
|
|
|
|
// Formatage des données pour l'email
|
|
const formattedDate = formatDate((contract as any).start_date);
|
|
// Récupérer le nom d'employeur depuis organizations.name
|
|
let employerName = 'Employeur';
|
|
try {
|
|
const targetOrgId = (contract as any).org_id || orgId;
|
|
if (targetOrgId) {
|
|
const { data: org } = await sb
|
|
.from('organizations')
|
|
.select('name')
|
|
.eq('id', targetOrgId)
|
|
.maybeSingle();
|
|
employerName = org?.name || (contract as any).structure || 'Employeur';
|
|
} else {
|
|
employerName = (contract as any).structure || 'Employeur';
|
|
}
|
|
} catch (e) {
|
|
employerName = (contract as any).structure || 'Employeur';
|
|
}
|
|
const prenom_salarie = contract.employee_name?.split(' ')[0] || 'Salarié';
|
|
|
|
// Récupérer l'email du salarié depuis salaries.adresse_mail
|
|
let toEmail: string | null = null;
|
|
if (contract.employee_matricule) {
|
|
try {
|
|
let salQ = sb
|
|
.from('salaries')
|
|
.select('adresse_mail')
|
|
.or(`code_salarie.eq.${contract.employee_matricule},num_salarie.eq.${contract.employee_matricule}`)
|
|
.limit(1);
|
|
if (orgId) salQ = salQ.eq('employer_id', orgId);
|
|
const { data: salData, error: salErr } = await salQ;
|
|
if (!salErr && salData && salData[0]?.adresse_mail) {
|
|
toEmail = salData[0].adresse_mail as string;
|
|
}
|
|
} catch (e) {
|
|
console.warn('Impossible de récupérer adresse_mail depuis salaries:', e);
|
|
}
|
|
}
|
|
// Fallback via DocuSeal si disponible
|
|
if (!toEmail && docusealEmail) {
|
|
toEmail = docusealEmail;
|
|
}
|
|
if (!toEmail) {
|
|
return NextResponse.json({ error: 'Email du salarié manquant' }, { status: 400 });
|
|
}
|
|
|
|
// Envoi de l'email via le template universel (variant salarié)
|
|
const emailData: EmailDataV2 = {
|
|
firstName: prenom_salarie,
|
|
organizationName: employerName,
|
|
matricule: contract.employee_matricule || (contract as any).matricule || '',
|
|
profession: contract.role || 'Contrat',
|
|
startDate: formattedDate,
|
|
productionName: (contract as any).production_name || '',
|
|
documentType: contract.role || 'Contrat',
|
|
contractReference: contract.reference || String(contract.id),
|
|
ctaUrl: signatureLink,
|
|
};
|
|
|
|
const messageId = await sendUniversalEmailV2({
|
|
type: 'signature-request-employee',
|
|
toEmail,
|
|
subject: `[Rappel] Signez votre contrat ${employerName}`,
|
|
data: emailData,
|
|
});
|
|
|
|
console.log('Email de relance envoyé:', {
|
|
contractId,
|
|
email: toEmail,
|
|
messageId
|
|
});
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: 'Email de relance envoyé avec succès',
|
|
messageId
|
|
});
|
|
|
|
} catch (error: any) {
|
|
console.error('Erreur envoi relance:', error);
|
|
return NextResponse.json({
|
|
error: 'Erreur lors de l\'envoi de la relance',
|
|
message: error.message
|
|
}, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// Fonction pour formater une date ISO au format DD/MM/AAAA
|
|
function formatDate(isoDate: string): string {
|
|
if (!isoDate) return '';
|
|
const date = new Date(isoDate);
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const year = date.getFullYear();
|
|
return `${day}/${month}/${year}`;
|
|
}
|
|
|
|
// Ancien HTML de relance supprimé au profit du template universel
|