- 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
618 lines
No EOL
15 KiB
TypeScript
618 lines
No EOL
15 KiB
TypeScript
// lib/emailMigrationHelpers.ts
|
||
import { sendUniversalEmailV2, EmailDataV2 } from "./emailTemplateService";
|
||
|
||
/**
|
||
* Migration helper pour les emails d'invitation
|
||
*/
|
||
export async function sendInvitationEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
email: string;
|
||
role: string;
|
||
actionLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
email: data.email,
|
||
role: data.role,
|
||
ctaUrl: data.actionLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'invitation',
|
||
toEmail,
|
||
subject: 'Votre accès à l\'Espace Paie Odentas',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de facture
|
||
*/
|
||
export async function sendInvoiceEmail(
|
||
toEmail: string,
|
||
ccEmail: string | null,
|
||
data: {
|
||
firstName: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
invoiceNumber: string;
|
||
amount: string;
|
||
customMessage: string;
|
||
ctaUrl?: string;
|
||
invoiceDate?: string;
|
||
sepaDate?: string;
|
||
paymentMethod?: string;
|
||
invoiceType?: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
invoiceNumber: data.invoiceNumber,
|
||
amount: data.amount,
|
||
customMessage: data.customMessage,
|
||
invoiceDate: data.invoiceDate,
|
||
sepaDate: data.sepaDate,
|
||
ctaUrl: data.ctaUrl,
|
||
paymentMethod: data.paymentMethod,
|
||
invoiceType: data.invoiceType
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'invoice',
|
||
toEmail,
|
||
ccEmail: ccEmail || undefined,
|
||
subject: `Votre facture n°${data.invoiceNumber} est disponible`,
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de signature électronique
|
||
*/
|
||
export async function sendSignatureRequestEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
employeeName?: string;
|
||
documentType?: string;
|
||
contractReference?: string;
|
||
status?: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
employeeName: data.employeeName,
|
||
documentType: data.documentType,
|
||
contractReference: data.contractReference,
|
||
status: data.status || 'En attente',
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'signature-request',
|
||
toEmail,
|
||
subject: 'Demande de signature électronique',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Demande de signature pour l'employeur (style universel)
|
||
*/
|
||
export async function sendEmployerSignatureRequestEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
employeeName?: string;
|
||
documentType?: string;
|
||
contractReference?: string;
|
||
status?: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
employeeName: data.employeeName,
|
||
documentType: data.documentType,
|
||
contractReference: data.contractReference,
|
||
status: data.status || 'En attente',
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'signature-request-employer',
|
||
toEmail,
|
||
subject: 'Demande de signature électronique',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Demande de signature pour le salarié (style universel)
|
||
*/
|
||
export async function sendEmployeeSignatureRequestEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
documentType?: string;
|
||
contractReference?: string;
|
||
status?: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
documentType: data.documentType,
|
||
contractReference: data.contractReference,
|
||
status: data.status || 'En attente',
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'signature-request-employee',
|
||
toEmail,
|
||
subject: 'Demande de signature électronique',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de rappel de signature
|
||
*/
|
||
export async function sendSignatureReminderEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
contractReference: string;
|
||
signatureLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
contractReference: data.contractReference,
|
||
ctaUrl: data.signatureLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: `[Rappel] Signez votre contrat ${data.organizationName}`,
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de réinitialisation de mot de passe
|
||
*/
|
||
export async function sendPasswordResetEmail(
|
||
toEmail: string,
|
||
data: {
|
||
resetLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
ctaUrl: data.resetLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: 'Réinitialisation de votre mot de passe',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails OTP
|
||
*/
|
||
export async function sendOTPEmail(
|
||
toEmail: string,
|
||
data: {
|
||
code: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
alertMessage: `Votre code de vérification : <strong style="font-size: 24px; color: #004aad;">${data.code}</strong>`
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: 'Votre code de vérification',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails d'accès accordé
|
||
*/
|
||
export async function sendAccessGrantedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
role: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
role: data.role
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'notification',
|
||
toEmail,
|
||
subject: 'Accès accordé',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails d'accès révoqué
|
||
*/
|
||
export async function sendAccessRevokedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
userEmail?: string;
|
||
revocationDate?: string;
|
||
revokedBy?: string;
|
||
reason?: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
userEmail: data.userEmail || toEmail,
|
||
revocationDate: data.revocationDate,
|
||
revokedBy: data.revokedBy,
|
||
reason: data.reason,
|
||
status: 'Révoqué',
|
||
ctaUrl: 'mailto:paie@odentas.fr'
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'access-revoked',
|
||
toEmail,
|
||
subject: 'Accès révoqué',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Activation de compte (via lien d'invitation Supabase)
|
||
*/
|
||
export async function sendAccountActivationEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
activationUrl: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
userEmail: toEmail,
|
||
status: "En attente d'activation",
|
||
platform: 'Odentas Paie',
|
||
actionRequired: 'Activation du compte',
|
||
ctaUrl: data.activationUrl,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'account-activation',
|
||
toEmail,
|
||
subject: `Activez votre espace Odentas Paie – ${data.organizationName}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Habilitation modifiée (rôle changé)
|
||
*/
|
||
export async function sendAccessUpdatedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
organizationName: string;
|
||
oldRole?: string;
|
||
newRole: string;
|
||
updatedBy?: string;
|
||
updateDate?: string;
|
||
ctaUrl?: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
userEmail: toEmail,
|
||
oldRole: data.oldRole,
|
||
newRole: data.newRole,
|
||
updatedBy: data.updatedBy,
|
||
updateDate: data.updateDate,
|
||
ctaUrl: data.ctaUrl || `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/vos-acces`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'access-updated',
|
||
toEmail,
|
||
subject: `Vos habilitations ont été modifiées – ${data.organizationName}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: mot de passe créé (premier mot de passe)
|
||
*/
|
||
export async function sendPasswordCreatedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
eventDate?: string;
|
||
platform?: string;
|
||
} = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: 'Créé',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Odentas Paie',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/securite`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'password-created',
|
||
toEmail,
|
||
subject: 'Mot de passe créé sur votre compte',
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: mot de passe modifié
|
||
*/
|
||
export async function sendPasswordChangedEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
eventDate?: string;
|
||
platform?: string;
|
||
} = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: 'Modifié',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Espace Paie Odentas',
|
||
ctaUrl: 'https://paie.odentas.fr',
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'password-changed',
|
||
toEmail,
|
||
subject: 'Votre mot de passe a été modifié',
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: 2FA activée
|
||
*/
|
||
export async function sendTwoFaEnabledEmail(
|
||
toEmail: string,
|
||
data: { firstName?: string; eventDate?: string; platform?: string } = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: '2FA désactivée',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Odentas Paie',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/compte/securite`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'twofa-disabled',
|
||
toEmail,
|
||
subject: '2FA désactivée sur votre compte',
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Migration helper pour les emails de demande de mandat SEPA
|
||
*/
|
||
export async function sendSepaMandateRequestEmail(
|
||
toEmail: string,
|
||
ccEmail: string | null,
|
||
data: {
|
||
firstName: string;
|
||
organizationName: string;
|
||
employerCode?: string;
|
||
mandateLink: string;
|
||
}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
organizationName: data.organizationName,
|
||
employerCode: data.employerCode,
|
||
handlerName: 'Renaud BREVIERE-ABRAHAM',
|
||
ctaUrl: data.mandateLink
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'sepa-mandate-request',
|
||
toEmail,
|
||
ccEmail: ccEmail || undefined,
|
||
subject: 'Signez votre mandat de prélèvement SEPA',
|
||
data: emailData
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Support: Notification interne de création de ticket
|
||
*/
|
||
export async function sendInternalTicketCreatedEmail(
|
||
data: {
|
||
ticketId: string;
|
||
ticketSubject: string;
|
||
ticketCategory: string;
|
||
ticketMessage: string;
|
||
userName: string;
|
||
userEmail: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
}
|
||
) {
|
||
// Convertir les sauts de ligne en <br> pour l'affichage HTML
|
||
const formattedMessage = data.ticketMessage.replace(/\n/g, '<br>');
|
||
|
||
const emailData: EmailDataV2 = {
|
||
ticketId: data.ticketId,
|
||
ticketSubject: data.ticketSubject,
|
||
ticketCategory: data.ticketCategory,
|
||
ticketMessage: formattedMessage,
|
||
userName: data.userName,
|
||
userEmail: data.userEmail,
|
||
organizationName: data.organizationName || 'Non définie',
|
||
employerCode: data.employerCode || 'Non défini',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/staff/tickets/${data.ticketId}`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'support-ticket-created',
|
||
toEmail: 'paie@odentas.fr',
|
||
subject: `[SUPPORT] Nouveau ticket : ${data.ticketSubject}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Support: Notification interne de réponse utilisateur
|
||
*/
|
||
export async function sendInternalTicketReplyEmail(
|
||
data: {
|
||
ticketId: string;
|
||
ticketSubject: string;
|
||
ticketStatus: string;
|
||
userMessage: string;
|
||
userName: string;
|
||
userEmail: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
}
|
||
) {
|
||
// Convertir les sauts de ligne en <br> pour l'affichage HTML
|
||
const formattedMessage = data.userMessage.replace(/\n/g, '<br>');
|
||
|
||
const emailData: EmailDataV2 = {
|
||
ticketId: data.ticketId,
|
||
ticketSubject: data.ticketSubject,
|
||
ticketStatus: data.ticketStatus,
|
||
userMessage: formattedMessage,
|
||
userName: data.userName,
|
||
userEmail: data.userEmail,
|
||
organizationName: data.organizationName || 'Non définie',
|
||
employerCode: data.employerCode || 'Non défini',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/staff/tickets/${data.ticketId}`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'support-ticket-reply',
|
||
toEmail: 'paie@odentas.fr',
|
||
subject: `[SUPPORT] Réponse au ticket : ${data.ticketSubject}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Support: Notification de réponse à un ticket
|
||
*/
|
||
export async function sendSupportReplyEmail(
|
||
toEmail: string,
|
||
data: {
|
||
firstName?: string;
|
||
ticketId: string;
|
||
ticketSubject: string;
|
||
staffName: string;
|
||
staffMessage: string;
|
||
organizationName?: string;
|
||
employerCode?: string;
|
||
}
|
||
) {
|
||
// Formater le nom du staff : première lettre en majuscule + [Staff Odentas]
|
||
const formattedStaffName = data.staffName.charAt(0).toUpperCase() + data.staffName.slice(1) + ' [Staff Odentas]';
|
||
|
||
// Convertir les sauts de ligne en <br> pour l'affichage HTML
|
||
const formattedMessage = data.staffMessage.replace(/\n/g, '<br>');
|
||
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
ticketId: data.ticketId,
|
||
ticketSubject: data.ticketSubject,
|
||
staffName: formattedStaffName,
|
||
staffMessage: formattedMessage,
|
||
organizationName: data.organizationName || 'Non définie',
|
||
employerCode: data.employerCode || 'Non défini',
|
||
handlerName: 'Renaud BREVIERE-ABRAHAM',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/support/${data.ticketId}`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'support-reply',
|
||
toEmail,
|
||
subject: `Nouvelle réponse à votre ticket: ${data.ticketSubject}`,
|
||
data: emailData,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Sécurité: 2FA désactivée
|
||
*/
|
||
export async function sendTwoFaDisabledEmail(
|
||
toEmail: string,
|
||
data: { firstName?: string; eventDate?: string; platform?: string } = {}
|
||
) {
|
||
const emailData: EmailDataV2 = {
|
||
firstName: data.firstName,
|
||
userEmail: toEmail,
|
||
status: '2FA désactivée',
|
||
eventDate: data.eventDate || new Date().toLocaleString('fr-FR'),
|
||
platform: data.platform || 'Odentas Paie',
|
||
ctaUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'https://paie.odentas.fr'}/compte/securite`,
|
||
};
|
||
|
||
await sendUniversalEmailV2({
|
||
type: 'twofa-disabled',
|
||
toEmail,
|
||
subject: '2FA désactivée sur votre compte',
|
||
data: emailData,
|
||
});
|
||
} |