fix(projects): scope email rollup to CRM types + re-render unstored previews

- The customer address often doubles as the admin notification target, so
  matching emails purely by recipient swept in system alerts (backup_failed,
  restore_failed, …). The recipient match is now restricted to CRM document
  types (quote_/contract_/invoice_/storno_); event-scoped mails still match
  by event_id.
- getEmailPreview now falls back to renderQueuedEmail() — re-rendering from
  the current template + the row's stored email_data — for emails sent before
  rendered_html capture, flagged exact:false with an amber 're-rendered' note.
  Only a missing template / no variables falls through to 'nothing stored'.

Known limitation: a customer with multiple projects sees their event_id=null
CRM mails under each (email_queue has no project_id).
This commit is contained in:
Luca
2026-06-06 22:47:43 +02:00
parent c0b6d14d08
commit f02fba6332
6 changed files with 76 additions and 14 deletions
+17
View File
@@ -772,6 +772,22 @@ async function sendTemplateEmail(to, templateKey, variables) {
}
}
/**
* Render a queued email's HTML WITHOUT sending it. Used by the Project
* Overview cockpit to preview emails that predate the rendered_html column
* (so nothing was stored at send time). The result is rendered from the
* CURRENT template + the row's stored variables, so it's a faithful
* approximation rather than the exact bytes that were sent — callers flag
* it as a re-render. Returns null when the template no longer exists.
*/
async function renderQueuedEmail(templateKey, variables = {}, to = '') {
const template = await db('email_templates').where('template_key', templateKey).first();
if (!template) return null;
const language = await getRecipientLanguage(to, variables.eventId || null);
const { subject, htmlBody } = await processTemplate(template, variables, language);
return { subject, html: htmlBody };
}
// Process email queue.
//
// Options:
@@ -1074,6 +1090,7 @@ module.exports = {
initializeTransporter,
startEmailQueueProcessor,
sendTemplateEmail,
renderQueuedEmail,
processEmailQueue,
queueEmail,
stopEmailQueueProcessor,