fix(crm): self-heal missing CRM email templates at boot + recover queue

The CRM template seeders (crmEmailTemplates / contractEmailTemplates /
eventReminderTemplates) were idempotent and ready, but only
contractEmailTemplates was actually called (lazily, by contractService
sends). crmEmailTemplates had no caller anywhere — every install that
didn't pre-exist its templates failed every quote_sent / invoice_sent /
storno_issued / invoice_reminder_* send with "Email template '<key>'
not found". The queue processor retries 3 times then leaves the row
in status='pending', retry_count=3, silently dead with no admin
surface (see project_crm_backlog for the eventual System Health page).

Fix: wire all three seeders into server.js startServer() right before
startEmailQueueProcessor. The new _emailTemplateBoot.js orchestrates
all three and then, for any template_key it just inserted, resets
retry_count on stuck email_queue rows of that email_type so the
queue processor's next tick picks them back up. Recovery is targeted:
unrelated retry-exhausted rows (e.g. SMTP-timeout failures) are not
touched.

Integration test boots a fresh CRM DB, pre-seeds a stuck quote_sent
row plus an unrelated stuck row, runs the boot helper, and asserts:
templates landed, stuck quote_sent row was reset, unrelated row was
left alone.

Already-deployed installs heal automatically on the next backend
restart after this lands.
This commit is contained in:
Luca
2026-05-27 15:18:29 +02:00
parent 09c5110d2b
commit 83933baeec
3 changed files with 205 additions and 0 deletions
+10
View File
@@ -777,6 +777,16 @@ async function startServer() {
// Initialize email transporter and start queue processor
await initializeTransporter();
// Seed CRM / contract / event-reminder email templates and recover
// any queue rows that exhausted retries because their template
// didn't exist yet. Runs once per boot via module-level caches in
// each seeder. See _emailTemplateBoot.js for the full rationale.
try {
const { seedEmailTemplatesAndRecoverQueue } = require('./src/services/_emailTemplateBoot');
await seedEmailTemplatesAndRecoverQueue(db, logger);
} catch (err) {
logger.warn('Email template self-heal failed at boot:', err.message);
}
startEmailQueueProcessor();
// Start webhook delivery worker (#327)