/** * Add French translations for client-facing email templates * * Translates authentication, ticketing, and billing email templates to French * for client portal users. */ exports.up = async function(knex) { console.log('Adding French email templates...'); // Get notification subtypes const subtypes = await knex('notification_subtypes') .select('id', 'name') .whereIn('name', [ 'email-verification', 'password-reset', 'portal-invitation', 'tenant-recovery', 'no-account-found', 'Ticket Assigned', 'Ticket Created', 'Ticket Updated', 'Ticket Closed', 'Ticket Comment Added', 'Invoice Generated', 'Payment Received', 'Payment Overdue' ]); const getSubtypeId = (name) => { const subtype = subtypes.find(s => s.name === name); if (!subtype) { throw new Error(`Notification subtype '${name}' not found`); } return subtype.id; }; // Insert French templates await knex('system_email_templates').insert([ // Authentication templates // NOTE: email-verification template is managed in migration 20251029100000 { name: 'password-reset', language_code: 'fr', subject: 'Demande de Réinitialisation du Mot de Passe', notification_subtype_id: getSubtypeId('password-reset'), html_content: ` Demande de Réinitialisation du Mot de Passe

Demande de Réinitialisation du Mot de Passe

Récupération sécurisée du mot de passe de votre compte

Bonjour {{userName}},

Nous avons reçu une demande de réinitialisation du mot de passe pour votre compte associé à {{email}}.

🔐 Vérification de Sécurité du Compte

Demandé : À l'instant

E-mail du compte : {{email}}

Valable pendant : {{expirationTime}}

Pour créer un nouveau mot de passe pour votre compte, cliquez sur le bouton ci-dessous :

Réinitialiser Votre Mot de Passe

Ou copiez et collez ce lien dans votre navigateur :

⚠️ Informations de Sécurité Importantes

Et Ensuite ?

  1. Cliquez sur le bouton de réinitialisation ci-dessus ou utilisez le lien fourni
  2. Créez un mot de passe fort et unique pour votre compte
  3. Vous serez automatiquement connecté après la réinitialisation
  4. Toutes les sessions existantes seront fermées pour des raisons de sécurité
  5. Envisagez d'activer l'authentification à deux facteurs pour une protection accrue

Besoin d'Aide ?

Si vous rencontrez des difficultés pour réinitialiser votre mot de passe, notre équipe d'assistance est là pour vous aider.

Contacter l'Assistance : {{supportEmail}}

`, text_content: `Demande de Réinitialisation du Mot de Passe Bonjour {{userName}}, Nous avons reçu une demande de réinitialisation du mot de passe pour votre compte associé à {{email}}. VÉRIFICATION DE SÉCURITÉ DU COMPTE - Demandé : À l'instant - E-mail du compte : {{email}} - Valable pendant : {{expirationTime}} Pour créer un nouveau mot de passe, visitez le lien suivant : {{resetLink}} INFORMATIONS DE SÉCURITÉ IMPORTANTES : - Ce lien expirera dans {{expirationTime}} - Ne peut être utilisé qu'une seule fois - Si vous n'avez pas demandé cela, ignorez cet e-mail - Votre mot de passe ne changera pas tant que vous n'en créerez pas un nouveau ET ENSUITE : 1. Utilisez le lien fourni ci-dessus 2. Créez un mot de passe fort et unique 3. Vous serez automatiquement connecté 4. Toutes les sessions existantes seront fermées 5. Envisagez d'activer l'authentification à deux facteurs Besoin d'aide ? Contacter l'Assistance : {{supportEmail}} --- Ceci est un e-mail de sécurité automatisé envoyé à {{email}}. © {{currentYear}} {{clientName}}. Tous droits réservés.` }, // NOTE: portal-invitation template is managed in migration 20251029100000 { name: 'tenant-recovery', language_code: 'fr', subject: '{{platformName}} - Vos liens de connexion', notification_subtype_id: getSubtypeId('tenant-recovery'), html_content: `

{{platformName}}

Bonjour,

Vous avez demandé l'accès à votre portail{{#if isMultiple}}s{{/if}} client{{#if isMultiple}}s{{/if}}. {{#if isMultiple}}Nous avons trouvé {{tenantCount}} organisations associées à votre adresse e-mail.{{else}}Voici votre lien de connexion :{{/if}}

{{tenantLinksHtml}}

Note de sécurité : Si vous n'avez pas demandé ces liens de connexion, vous pouvez ignorer cet e-mail en toute sécurité. Votre compte reste sécurisé.

Si vous avez des questions ou besoin d'assistance, veuillez contacter l'équipe d'assistance de votre organisation.

© {{currentYear}} {{platformName}}. Tous droits réservés.

Ceci est un message automatisé. Veuillez ne pas répondre à cet e-mail.

`, text_content: `{{platformName}} - Vos liens de connexion Bonjour, Vous avez demandé l'accès à votre portail{{#if isMultiple}}s{{/if}} client{{#if isMultiple}}s{{/if}}. {{#if isMultiple}}Nous avons trouvé {{tenantCount}} organisations associées à votre adresse e-mail.{{else}}Voici votre lien de connexion :{{/if}} Vos liens de connexion : {{tenantLinksText}} Note de sécurité : Si vous n'avez pas demandé ces liens de connexion, vous pouvez ignorer cet e-mail en toute sécurité. Si vous avez des questions ou besoin d'assistance, veuillez contacter l'équipe d'assistance de votre organisation. --- © {{currentYear}} {{platformName}}. Tous droits réservés. Ceci est un message automatisé. Veuillez ne pas répondre à cet e-mail.` }, { name: 'no-account-found', language_code: 'fr', subject: '{{platformName}} - Demande d\'accès', notification_subtype_id: getSubtypeId('no-account-found'), html_content: `

{{platformName}}

Bonjour,

Nous avons reçu une demande d'accès au portail client utilisant cette adresse e-mail.

Si vous avez un compte chez nous, vous devriez avoir reçu un e-mail séparé avec vos liens de connexion.

Si vous n'avez pas reçu d'e-mail de connexion, cela peut signifier :

Besoin d'aide ?

Si vous pensez que vous devriez avoir accès à un portail client, veuillez contacter l'équipe d'assistance de votre fournisseur de services pour obtenir de l'aide.

Note de sécurité : Si vous n'avez pas demandé d'accès, vous pouvez ignorer cet e-mail en toute sécurité.

© {{currentYear}} {{platformName}}. Tous droits réservés.

Ceci est un message automatisé. Veuillez ne pas répondre à cet e-mail.

`, text_content: `{{platformName}} - Demande d'accès Bonjour, Nous avons reçu une demande d'accès au portail client utilisant cette adresse e-mail. Si vous avez un compte chez nous, vous devriez avoir reçu un e-mail séparé avec vos liens de connexion. Si vous n'avez pas reçu d'e-mail de connexion, cela peut signifier : - Cette adresse e-mail n'est associée à aucun compte de portail client - Votre compte peut être inactif - L'e-mail peut avoir été filtré vers votre dossier spam Besoin d'aide ? Si vous pensez que vous devriez avoir accès à un portail client, veuillez contacter l'équipe d'assistance de votre fournisseur de services pour obtenir de l'aide. Note de sécurité : Si vous n'avez pas demandé d'accès, vous pouvez ignorer cet e-mail en toute sécurité. --- © {{currentYear}} {{platformName}}. Tous droits réservés. Ceci est un message automatisé. Veuillez ne pas répondre à cet e-mail.` }, // Ticketing templates { name: 'ticket-assigned', language_code: 'fr', subject: 'Ticket Assigné • {{ticket.title}} ({{ticket.priority}})', notification_subtype_id: getSubtypeId('Ticket Assigned'), html_content: `
Ticket Assigné
{{ticket.title}}
{{ticket.metaLine}}

Ce ticket vous a été assigné pour {{ticket.clientName}}. Consultez les détails ci-dessous et prenez les mesures appropriées.

Ticket #{{ticket.id}}
Priorité {{ticket.priority}}
Statut {{ticket.status}}
Assigné par {{ticket.assignedBy}}
Assigné à
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
Demandeur
{{ticket.requesterName}}
{{ticket.requesterContact}}
Tableau {{ticket.board}}
Catégorie {{ticket.categoryDetails}}
Emplacement {{ticket.locationSummary}}
Description
{{ticket.description}}
Voir le Ticket
Powered by Alga PSA • Gardons les équipes alignées
`, text_content: ` Ticket Assigné à Vous {{ticket.metaLine}} Assigné par: {{ticket.assignedBy}} Priorité: {{ticket.priority}} Statut: {{ticket.status}} Assigné à: {{ticket.assignedDetails}} Demandeur: {{ticket.requesterDetails}} Tableau: {{ticket.board}} Catégorie: {{ticket.categoryDetails}} Emplacement: {{ticket.locationSummary}} Description: {{ticket.description}} Voir le ticket: {{ticket.url}} ` }, { name: 'ticket-created', language_code: 'fr', subject: 'Nouveau Ticket • {{ticket.title}} ({{ticket.priority}})', notification_subtype_id: getSubtypeId('Ticket Created'), html_content: `
Nouveau Ticket Créé
{{ticket.title}}
{{ticket.metaLine}}

Un nouveau ticket a été enregistré pour {{ticket.clientName}}. Consultez le résumé ci-dessous et suivez le lien pour agir.

Ticket #{{ticket.id}}
Priorité {{ticket.priority}}
Statut {{ticket.status}}
Créé {{ticket.createdAt}} · {{ticket.createdBy}}
Assigné à
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
Demandeur
{{ticket.requesterName}}
{{ticket.requesterContact}}
Tableau {{ticket.board}}
Catégorie {{ticket.categoryDetails}}
Emplacement {{ticket.locationSummary}}
Description
{{ticket.description}}
Voir le Ticket
Powered by Alga PSA • Maintenir les équipes alignées
`, text_content: ` Nouveau Ticket Créé pour {{ticket.clientName}} {{ticket.metaLine}} Créé : {{ticket.createdAt}} · {{ticket.createdBy}} Priorité : {{ticket.priority}} Statut : {{ticket.status}} Assigné à : {{ticket.assignedDetails}} Demandeur : {{ticket.requesterDetails}} Tableau : {{ticket.board}} Catégorie : {{ticket.categoryDetails}} Emplacement : {{ticket.locationSummary}} Description : {{ticket.description}} Voir le ticket : {{ticket.url}} ` }, { name: 'ticket-updated', language_code: 'fr', subject: 'Ticket Mis à Jour • {{ticket.title}} ({{ticket.priority}})', notification_subtype_id: getSubtypeId('Ticket Updated'), html_content: `
Ticket Mis à Jour
{{ticket.title}}
{{ticket.metaLine}}

Un ticket a été mis à jour pour {{ticket.clientName}}. Consultez les modifications ci-dessous.

Ticket #{{ticket.id}}
Priorité {{ticket.priority}}
Statut {{ticket.status}}
Mis à jour par {{ticket.updatedBy}}
Assigné à
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
Demandeur
{{ticket.requesterName}}
{{ticket.requesterContact}}
Tableau {{ticket.board}}
Catégorie {{ticket.categoryDetails}}
Emplacement {{ticket.locationSummary}}
Modifications
{{ticket.changes}}
Voir le Ticket
Powered by Alga PSA • Gardons les équipes alignées
`, text_content: ` Ticket Mis à Jour {{ticket.metaLine}} Mis à jour par: {{ticket.updatedBy}} Priorité: {{ticket.priority}} Statut: {{ticket.status}} Assigné à: {{ticket.assignedDetails}} Demandeur: {{ticket.requesterDetails}} Tableau: {{ticket.board}} Catégorie: {{ticket.categoryDetails}} Emplacement: {{ticket.locationSummary}} Modifications: {{ticket.changes}} Voir le ticket: {{ticket.url}} ` }, { name: 'ticket-closed', language_code: 'fr', subject: 'Ticket Fermé • {{ticket.title}}', notification_subtype_id: getSubtypeId('Ticket Closed'), html_content: `
Ticket Fermé
{{ticket.title}}
{{ticket.metaLine}}

Un ticket a été résolu et fermé pour {{ticket.clientName}}. Consultez les détails de la résolution ci-dessous.

Ticket #{{ticket.id}}
Statut Fermé
Fermé par {{ticket.closedBy}}
Assigné à
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
Demandeur
{{ticket.requesterName}}
{{ticket.requesterContact}}
Tableau {{ticket.board}}
Catégorie {{ticket.categoryDetails}}
Emplacement {{ticket.locationSummary}}
Résolution
{{ticket.resolution}}
Voir le Ticket
Powered by Alga PSA • Gardons les équipes alignées
`, text_content: ` Ticket Fermé {{ticket.metaLine}} Fermé par: {{ticket.closedBy}} Statut: Fermé Assigné à: {{ticket.assignedDetails}} Demandeur: {{ticket.requesterDetails}} Tableau: {{ticket.board}} Catégorie: {{ticket.categoryDetails}} Emplacement: {{ticket.locationSummary}} Résolution: {{ticket.resolution}} Voir le ticket: {{ticket.url}} ` }, { name: 'ticket-comment-added', language_code: 'fr', subject: 'Nouveau Commentaire • {{ticket.title}}', notification_subtype_id: getSubtypeId('Ticket Comment Added'), html_content: `
Nouveau Commentaire Ajouté
{{ticket.title}}
{{ticket.metaLine}}

Un nouveau commentaire a été ajouté à un ticket pour {{ticket.clientName}}.

Ticket #{{ticket.id}}
Priorité {{ticket.priority}}
Statut {{ticket.status}}
Commentaire de {{comment.author}}
Assigné à
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
Demandeur
{{ticket.requesterName}}
{{ticket.requesterContact}}
Tableau {{ticket.board}}
Catégorie {{ticket.categoryDetails}}
Emplacement {{ticket.locationSummary}}
💬 Commentaire
{{comment.content}}
Voir le Ticket
Powered by Alga PSA • Gardons les équipes alignées
`, text_content: ` Nouveau Commentaire Ajouté {{ticket.metaLine}} Commentaire de: {{comment.author}} Priorité: {{ticket.priority}} Statut: {{ticket.status}} Assigné à: {{ticket.assignedDetails}} Demandeur: {{ticket.requesterDetails}} Tableau: {{ticket.board}} Catégorie: {{ticket.categoryDetails}} Emplacement: {{ticket.locationSummary}} Commentaire: {{comment.content}} Voir le ticket: {{ticket.url}} ` }, // Billing templates { name: 'invoice-generated', language_code: 'fr', subject: 'Nouvelle facture #{{invoice.number}}', notification_subtype_id: getSubtypeId('Invoice Generated'), html_content: `

Facture {{invoice.number}}

Une nouvelle facture a été générée pour votre examen :

Numéro de facture : {{invoice.number}}

Montant : {{invoice.amount}}

Date d'échéance : {{invoice.dueDate}}

Client : {{invoice.clientName}}

Voir la facture `, text_content: ` Facture {{invoice.number}} Une nouvelle facture a été générée pour votre examen : Numéro de facture : {{invoice.number}} Montant : {{invoice.amount}} Date d'échéance : {{invoice.dueDate}} Client : {{invoice.clientName}} Voir la facture : {{invoice.url}} ` }, { name: 'payment-received', language_code: 'fr', subject: 'Paiement reçu : Facture #{{invoice.number}}', notification_subtype_id: getSubtypeId('Payment Received'), html_content: `

Paiement reçu

Le paiement a été reçu pour la facture #{{invoice.number}} :

Numéro de facture : {{invoice.number}}

Montant payé : {{invoice.amountPaid}}

Date de paiement : {{invoice.paymentDate}}

Méthode de paiement : {{invoice.paymentMethod}}

Voir la facture `, text_content: ` Paiement reçu Le paiement a été reçu pour la facture #{{invoice.number}} : Numéro de facture : {{invoice.number}} Montant payé : {{invoice.amountPaid}} Date de paiement : {{invoice.paymentDate}} Méthode de paiement : {{invoice.paymentMethod}} Voir la facture : {{invoice.url}} ` }, { name: 'payment-overdue', language_code: 'fr', subject: 'Paiement en retard : Facture #{{invoice.number}}', notification_subtype_id: getSubtypeId('Payment Overdue'), html_content: `

Paiement en retard

Le paiement de la facture #{{invoice.number}} est en retard :

Numéro de facture : {{invoice.number}}

Montant dû : {{invoice.amountDue}}

Date d'échéance : {{invoice.dueDate}}

Jours de retard : {{invoice.daysOverdue}}

Voir la facture `, text_content: ` Paiement en retard Le paiement de la facture #{{invoice.number}} est en retard : Numéro de facture : {{invoice.number}} Montant dû : {{invoice.amountDue}} Date d'échéance : {{invoice.dueDate}} Jours de retard : {{invoice.daysOverdue}} Voir la facture : {{invoice.url}} ` } ]).onConflict(['name', 'language_code']).merge({ subject: knex.raw('excluded.subject'), html_content: knex.raw('excluded.html_content'), text_content: knex.raw('excluded.text_content'), notification_subtype_id: knex.raw('excluded.notification_subtype_id') }); console.log('✓ French email templates added (auth + notifications)'); }; exports.down = async function(knex) { // Remove French email templates // NOTE: email-verification and portal-invitation are NOT removed as they're managed by migration 20251029100000 await knex('system_email_templates') .where({ language_code: 'fr' }) .whereIn('name', [ 'password-reset', 'tenant-recovery', 'no-account-found', 'ticket-assigned', 'ticket-created', 'ticket-updated', 'ticket-closed', 'ticket-comment-added', 'invoice-generated', 'payment-received', 'payment-overdue' ]) .del(); console.log('French email templates removed'); };