/**
* Add German translations for client-facing email templates
*
* Translates authentication, ticketing, and billing email templates to German
* for client portal users.
*/
exports.up = async function(knex) {
console.log('Adding German 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 German templates
await knex('system_email_templates').insert([
// Authentication templates
// NOTE: email-verification template is managed in base migration (20251027080000)
{
name: 'password-reset',
language_code: 'de',
subject: 'Passwort-Zurücksetzungsanfrage',
notification_subtype_id: getSubtypeId('password-reset'),
html_content: `
Passwort-Zurücksetzungsanfrage
Hallo {{userName}},
Wir haben eine Anfrage erhalten, das Passwort für Ihr Konto zurückzusetzen, das mit {{email}} verknüpft ist.
🔐 Kontosicherheitsüberprüfung
Angefordert: Vor einem Moment
Konto-E-Mail: {{email}}
Gültig für: {{expirationTime}}
Um ein neues Passwort für Ihr Konto zu erstellen, klicken Sie auf die Schaltfläche unten:
Oder kopieren Sie diesen Link in Ihren Browser:
{{resetLink}}
⚠️ Wichtige Sicherheitsinformationen
- Dieser Zurücksetzungslink läuft in {{expirationTime}} ab
- Aus Sicherheitsgründen kann dieser Link nur einmal verwendet werden
- Wenn Sie diese Zurücksetzung nicht angefordert haben, ignorieren Sie diese E-Mail
- Ihr Passwort wird nicht geändert, bis Sie ein neues erstellen
Was kommt als Nächstes?
- Klicken Sie auf die Zurücksetzungsschaltfläche oben oder verwenden Sie den bereitgestellten Link
- Erstellen Sie ein starkes, einzigartiges Passwort für Ihr Konto
- Sie werden nach dem Zurücksetzen automatisch angemeldet
- Alle bestehenden Sitzungen werden aus Sicherheitsgründen beendet
- Erwägen Sie die Aktivierung der Zwei-Faktor-Authentifizierung für zusätzlichen Schutz
Benötigen Sie Hilfe?
Wenn Sie Probleme beim Zurücksetzen Ihres Passworts haben, steht Ihnen unser Support-Team zur Verfügung.
Support kontaktieren: {{supportEmail}}
`,
text_content: `Passwort-Zurücksetzungsanfrage
Hallo {{userName}},
Wir haben eine Anfrage erhalten, das Passwort für Ihr Konto zurückzusetzen, das mit {{email}} verknüpft ist.
KONTOSICHERHEITSÜBERPRÜFUNG
- Angefordert: Vor einem Moment
- Konto-E-Mail: {{email}}
- Gültig für: {{expirationTime}}
Um ein neues Passwort zu erstellen, besuchen Sie den folgenden Link:
{{resetLink}}
WICHTIGE SICHERHEITSINFORMATIONEN:
- Dieser Link läuft in {{expirationTime}} ab
- Kann nur einmal verwendet werden
- Wenn Sie dies nicht angefordert haben, ignorieren Sie diese E-Mail
- Ihr Passwort wird nicht geändert, bis Sie ein neues erstellen
WAS KOMMT ALS NÄCHSTES:
1. Verwenden Sie den oben bereitgestellten Link
2. Erstellen Sie ein starkes, einzigartiges Passwort
3. Sie werden automatisch angemeldet
4. Alle bestehenden Sitzungen werden beendet
5. Erwägen Sie die Aktivierung der Zwei-Faktor-Authentifizierung
Benötigen Sie Hilfe?
Support kontaktieren: {{supportEmail}}
---
Dies ist eine automatische Sicherheits-E-Mail, die an {{email}} gesendet wurde.
© {{currentYear}} {{clientName}}. Alle Rechte vorbehalten.`
},
// NOTE: portal-invitation template is managed in migration 20251029100000
{
name: 'tenant-recovery',
language_code: 'de',
subject: '{{platformName}} - Ihre Anmeldelinks',
notification_subtype_id: getSubtypeId('tenant-recovery'),
html_content: `
{{platformName}}
Hallo,
Sie haben Zugang zu Ihrem Kundenportal{{#if isMultiple}} angefordert{{else}} angefordert{{/if}}.
{{#if isMultiple}}Wir haben {{tenantCount}} Organisationen gefunden, die mit Ihrer E-Mail-Adresse verknüpft sind.{{else}}Hier ist Ihr Anmeldelink:{{/if}}
Sicherheitshinweis: Wenn Sie diese Anmeldelinks nicht angefordert haben, können Sie diese E-Mail sicher ignorieren. Ihr Konto bleibt sicher.
Bei Fragen oder für Unterstützung wenden Sie sich bitte an das Support-Team Ihrer Organisation.
© {{currentYear}} {{platformName}}. Alle Rechte vorbehalten.
Dies ist eine automatisierte Nachricht. Bitte antworten Sie nicht auf diese E-Mail.
`,
text_content: `{{platformName}} - Ihre Anmeldelinks
Hallo,
Sie haben Zugang zu Ihrem Kundenportal{{#if isMultiple}} angefordert{{else}} angefordert{{/if}}.
{{#if isMultiple}}Wir haben {{tenantCount}} Organisationen gefunden, die mit Ihrer E-Mail-Adresse verknüpft sind.{{else}}Hier ist Ihr Anmeldelink:{{/if}}
Ihre Anmeldelinks:
{{tenantLinksText}}
Sicherheitshinweis: Wenn Sie diese Anmeldelinks nicht angefordert haben, können Sie diese E-Mail sicher ignorieren.
Bei Fragen oder für Unterstützung wenden Sie sich bitte an das Support-Team Ihrer Organisation.
---
© {{currentYear}} {{platformName}}. Alle Rechte vorbehalten.
Dies ist eine automatisierte Nachricht. Bitte antworten Sie nicht auf diese E-Mail.`
},
{
name: 'no-account-found',
language_code: 'de',
subject: '{{platformName}} - Zugriffsanfrage',
notification_subtype_id: getSubtypeId('no-account-found'),
html_content: `
{{platformName}}
Hallo,
Wir haben eine Anfrage für den Zugriff auf das Kundenportal mit dieser E-Mail-Adresse erhalten.
Wenn Sie ein Konto bei uns haben, sollten Sie eine separate E-Mail mit Ihren Anmeldelinks erhalten haben.
Wenn Sie keine Anmelde-E-Mail erhalten haben, könnte dies bedeuten:
- Diese E-Mail-Adresse ist mit keinem Kundenportal-Konto verknüpft
- Ihr Konto könnte inaktiv sein
- Die E-Mail könnte in Ihrem Spam-Ordner gefiltert worden sein
Benötigen Sie Hilfe?
Wenn Sie glauben, dass Sie Zugang zu einem Kundenportal haben sollten, wenden Sie sich bitte an das Support-Team Ihres Dienstleisters.
Sicherheitshinweis: Wenn Sie keinen Zugriff angefordert haben, können Sie diese E-Mail sicher ignorieren.
© {{currentYear}} {{platformName}}. Alle Rechte vorbehalten.
Dies ist eine automatisierte Nachricht. Bitte antworten Sie nicht auf diese E-Mail.
`,
text_content: `{{platformName}} - Zugriffsanfrage
Hallo,
Wir haben eine Anfrage für den Zugriff auf das Kundenportal mit dieser E-Mail-Adresse erhalten.
Wenn Sie ein Konto bei uns haben, sollten Sie eine separate E-Mail mit Ihren Anmeldelinks erhalten haben.
Wenn Sie keine Anmelde-E-Mail erhalten haben, könnte dies bedeuten:
- Diese E-Mail-Adresse ist mit keinem Kundenportal-Konto verknüpft
- Ihr Konto könnte inaktiv sein
- Die E-Mail könnte in Ihrem Spam-Ordner gefiltert worden sein
Benötigen Sie Hilfe?
Wenn Sie glauben, dass Sie Zugang zu einem Kundenportal haben sollten, wenden Sie sich bitte an das Support-Team Ihres Dienstleisters.
Sicherheitshinweis: Wenn Sie keinen Zugriff angefordert haben, können Sie diese E-Mail sicher ignorieren.
---
© {{currentYear}} {{platformName}}. Alle Rechte vorbehalten.
Dies ist eine automatisierte Nachricht. Bitte antworten Sie nicht auf diese E-Mail.`
},
// Ticketing templates
{
name: 'ticket-assigned',
language_code: 'de',
subject: 'Ticket Zugewiesen • {{ticket.title}} ({{ticket.priority}})',
notification_subtype_id: getSubtypeId('Ticket Assigned'),
html_content: `
|
Ticket Zugewiesen
{{ticket.title}}
{{ticket.metaLine}}
|
|
Dieses Ticket wurde Ihnen für {{ticket.clientName}} zugewiesen. Überprüfen Sie die Details unten und ergreifen Sie Maßnahmen.
| Priorität |
{{ticket.priority}}
|
| Status |
{{ticket.status}} |
| Zugewiesen von |
{{ticket.assignedBy}} |
| Zugewiesen an |
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
|
| Anforderer |
{{ticket.requesterName}}
{{ticket.requesterContact}}
|
| Board |
{{ticket.board}} |
| Kategorie |
{{ticket.categoryDetails}} |
| Standort |
{{ticket.locationSummary}} |
Beschreibung
{{ticket.description}}
Ticket Anzeigen
|
| Powered by Alga PSA • Teams auf Kurs halten |
|
`,
text_content: `
Ticket Zugewiesen an Sie
{{ticket.metaLine}}
Zugewiesen von: {{ticket.assignedBy}}
Priorität: {{ticket.priority}}
Status: {{ticket.status}}
Zugewiesen an: {{ticket.assignedDetails}}
Anforderer: {{ticket.requesterDetails}}
Board: {{ticket.board}}
Kategorie: {{ticket.categoryDetails}}
Standort: {{ticket.locationSummary}}
Beschreibung:
{{ticket.description}}
Ticket anzeigen: {{ticket.url}}
`
},
{
name: 'ticket-created',
language_code: 'de',
subject: 'Neues Ticket • {{ticket.title}} ({{ticket.priority}})',
notification_subtype_id: getSubtypeId('Ticket Created'),
html_content: `
|
Neues Ticket Erstellt
{{ticket.title}}
{{ticket.metaLine}}
|
|
Ein neues Ticket wurde für {{ticket.clientName}} registriert. Überprüfen Sie die Zusammenfassung unten und folgen Sie dem Link, um Maßnahmen zu ergreifen.
| Priorität |
{{ticket.priority}}
|
| Status |
{{ticket.status}} |
| Erstellt |
{{ticket.createdAt}} · {{ticket.createdBy}} |
| Zugewiesen an |
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
|
| Anforderer |
{{ticket.requesterName}}
{{ticket.requesterContact}}
|
| Board |
{{ticket.board}} |
| Kategorie |
{{ticket.categoryDetails}} |
| Standort |
{{ticket.locationSummary}} |
Beschreibung
{{ticket.description}}
Ticket Anzeigen
|
| Powered by Alga PSA • Teams auf Kurs halten |
|
`,
text_content: `
Neues Ticket Erstellt für {{ticket.clientName}}
{{ticket.metaLine}}
Erstellt: {{ticket.createdAt}} · {{ticket.createdBy}}
Priorität: {{ticket.priority}}
Status: {{ticket.status}}
Zugewiesen an: {{ticket.assignedDetails}}
Anforderer: {{ticket.requesterDetails}}
Board: {{ticket.board}}
Kategorie: {{ticket.categoryDetails}}
Standort: {{ticket.locationSummary}}
Beschreibung:
{{ticket.description}}
Ticket anzeigen: {{ticket.url}}
`
},
{
name: 'ticket-updated',
language_code: 'de',
subject: 'Ticket Aktualisiert • {{ticket.title}} ({{ticket.priority}})',
notification_subtype_id: getSubtypeId('Ticket Updated'),
html_content: `
|
Ticket Aktualisiert
{{ticket.title}}
{{ticket.metaLine}}
|
|
Ein Ticket wurde für {{ticket.clientName}} aktualisiert. Überprüfen Sie die Änderungen unten.
| Priorität |
{{ticket.priority}}
|
| Status |
{{ticket.status}} |
| Aktualisiert von |
{{ticket.updatedBy}} |
| Zugewiesen an |
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
|
| Anforderer |
{{ticket.requesterName}}
{{ticket.requesterContact}}
|
| Board |
{{ticket.board}} |
| Kategorie |
{{ticket.categoryDetails}} |
| Standort |
{{ticket.locationSummary}} |
Änderungen
{{ticket.changes}}
Ticket Anzeigen
|
| Powered by Alga PSA • Teams auf Kurs halten |
|
`,
text_content: `
Ticket Aktualisiert
{{ticket.metaLine}}
Aktualisiert von: {{ticket.updatedBy}}
Priorität: {{ticket.priority}}
Status: {{ticket.status}}
Zugewiesen an: {{ticket.assignedDetails}}
Anforderer: {{ticket.requesterDetails}}
Board: {{ticket.board}}
Kategorie: {{ticket.categoryDetails}}
Standort: {{ticket.locationSummary}}
Änderungen:
{{ticket.changes}}
Ticket anzeigen: {{ticket.url}}
`
},
{
name: 'ticket-closed',
language_code: 'de',
subject: 'Ticket Geschlossen • {{ticket.title}}',
notification_subtype_id: getSubtypeId('Ticket Closed'),
html_content: `
|
Ticket Geschlossen
{{ticket.title}}
{{ticket.metaLine}}
|
|
Ein Ticket wurde für {{ticket.clientName}} gelöst und geschlossen. Überprüfen Sie die Lösungsdetails unten.
| Status |
Geschlossen
|
| Geschlossen von |
{{ticket.closedBy}} |
| Zugewiesen an |
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
|
| Anforderer |
{{ticket.requesterName}}
{{ticket.requesterContact}}
|
| Board |
{{ticket.board}} |
| Kategorie |
{{ticket.categoryDetails}} |
| Standort |
{{ticket.locationSummary}} |
Lösung
{{ticket.resolution}}
Ticket Anzeigen
|
| Powered by Alga PSA • Teams auf Kurs halten |
|
`,
text_content: `
Ticket Geschlossen
{{ticket.metaLine}}
Geschlossen von: {{ticket.closedBy}}
Status: Geschlossen
Zugewiesen an: {{ticket.assignedDetails}}
Anforderer: {{ticket.requesterDetails}}
Board: {{ticket.board}}
Kategorie: {{ticket.categoryDetails}}
Standort: {{ticket.locationSummary}}
Lösung:
{{ticket.resolution}}
Ticket anzeigen: {{ticket.url}}
`
},
{
name: 'ticket-comment-added',
language_code: 'de',
subject: 'Neuer Kommentar • {{ticket.title}}',
notification_subtype_id: getSubtypeId('Ticket Comment Added'),
html_content: `
|
Neuer Kommentar Hinzugefügt
{{ticket.title}}
{{ticket.metaLine}}
|
|
Ein neuer Kommentar wurde zu einem Ticket für {{ticket.clientName}} hinzugefügt.
| Priorität |
{{ticket.priority}}
|
| Status |
{{ticket.status}} |
| Kommentar von |
{{comment.author}} |
| Zugewiesen an |
{{ticket.assignedToName}}
{{ticket.assignedToEmail}}
|
| Anforderer |
{{ticket.requesterName}}
{{ticket.requesterContact}}
|
| Board |
{{ticket.board}} |
| Kategorie |
{{ticket.categoryDetails}} |
| Standort |
{{ticket.locationSummary}} |
💬 Kommentar
{{comment.content}}
Ticket Anzeigen
|
| Powered by Alga PSA • Teams auf Kurs halten |
|
`,
text_content: `
Neuer Kommentar Hinzugefügt
{{ticket.metaLine}}
Kommentar von: {{comment.author}}
Priorität: {{ticket.priority}}
Status: {{ticket.status}}
Zugewiesen an: {{ticket.assignedDetails}}
Anforderer: {{ticket.requesterDetails}}
Board: {{ticket.board}}
Kategorie: {{ticket.categoryDetails}}
Standort: {{ticket.locationSummary}}
Kommentar:
{{comment.content}}
Ticket anzeigen: {{ticket.url}}
`
},
// Billing templates
{
name: 'invoice-generated',
language_code: 'de',
subject: 'Neue Rechnung #{{invoice.number}}',
notification_subtype_id: getSubtypeId('Invoice Generated'),
html_content: `
Rechnung {{invoice.number}}
Eine neue Rechnung wurde zur Überprüfung erstellt:
Rechnungsnummer: {{invoice.number}}
Betrag: {{invoice.amount}}
Fälligkeitsdatum: {{invoice.dueDate}}
Kunde: {{invoice.clientName}}
Rechnung anzeigen
`,
text_content: `
Rechnung {{invoice.number}}
Eine neue Rechnung wurde zur Überprüfung erstellt:
Rechnungsnummer: {{invoice.number}}
Betrag: {{invoice.amount}}
Fälligkeitsdatum: {{invoice.dueDate}}
Kunde: {{invoice.clientName}}
Rechnung anzeigen: {{invoice.url}}
`
},
{
name: 'payment-received',
language_code: 'de',
subject: 'Zahlung erhalten: Rechnung #{{invoice.number}}',
notification_subtype_id: getSubtypeId('Payment Received'),
html_content: `
Zahlung erhalten
Die Zahlung für Rechnung #{{invoice.number}} wurde erhalten:
Rechnungsnummer: {{invoice.number}}
Gezahlter Betrag: {{invoice.amountPaid}}
Zahlungsdatum: {{invoice.paymentDate}}
Zahlungsmethode: {{invoice.paymentMethod}}
Rechnung anzeigen
`,
text_content: `
Zahlung erhalten
Die Zahlung für Rechnung #{{invoice.number}} wurde erhalten:
Rechnungsnummer: {{invoice.number}}
Gezahlter Betrag: {{invoice.amountPaid}}
Zahlungsdatum: {{invoice.paymentDate}}
Zahlungsmethode: {{invoice.paymentMethod}}
Rechnung anzeigen: {{invoice.url}}
`
},
{
name: 'payment-overdue',
language_code: 'de',
subject: 'Zahlung überfällig: Rechnung #{{invoice.number}}',
notification_subtype_id: getSubtypeId('Payment Overdue'),
html_content: `
Zahlung überfällig
Die Zahlung für Rechnung #{{invoice.number}} ist überfällig:
Rechnungsnummer: {{invoice.number}}
Fälliger Betrag: {{invoice.amountDue}}
Fälligkeitsdatum: {{invoice.dueDate}}
Tage überfällig: {{invoice.daysOverdue}}
Rechnung anzeigen
`,
text_content: `
Zahlung überfällig
Die Zahlung für Rechnung #{{invoice.number}} ist überfällig:
Rechnungsnummer: {{invoice.number}}
Fälliger Betrag: {{invoice.amountDue}}
Fälligkeitsdatum: {{invoice.dueDate}}
Tage überfällig: {{invoice.daysOverdue}}
Rechnung anzeigen: {{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('✓ German email templates added (auth + notifications)');
};
exports.down = async function(knex) {
// Remove German email templates
// NOTE: email-verification and portal-invitation are NOT removed as they're managed by other migrations
await knex('system_email_templates')
.where({ language_code: 'de' })
.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('German email templates removed');
};