From 250b240337733362cb9a74b248ac78b6e9347bf7 Mon Sep 17 00:00:00 2001 From: Luca <102960244+Luca-Timo@users.noreply.github.com> Date: Thu, 25 Jun 2026 19:22:04 +0200 Subject: [PATCH] fix(crm): pre-event reminder passes raw event_date (fixes "Invalid Date" in the email) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit composePayload pre-formatted event_date to DD.MM.YYYY, but emailProcessor runs date variables through formatDate(value, language) — new Date("25.06.2026") can't parse → the email rendered "Invalid Date". Pass the raw event_date and let the processor localise it, matching the expiry mailer's contract. Pre-existing in the migration-143 composePayload (dormant while the legacy pass was gated off); surfaced once the pre_event_email flow ran. --- .../__tests__/integration/workflowEngine.test.js | 10 ++++++++++ backend/src/services/eventReminderService.js | 14 +++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/__tests__/integration/workflowEngine.test.js b/backend/__tests__/integration/workflowEngine.test.js index 5981efc5..d247609e 100644 --- a/backend/__tests__/integration/workflowEngine.test.js +++ b/backend/__tests__/integration/workflowEngine.test.js @@ -415,6 +415,16 @@ describe('workflow engine', () => { expect(await _internal.resolveTemplateKey('zzznotype', 'promo_')).toBe('promo_default'); }); + test('pre-event payload passes the RAW event_date (processor formats it — no "Invalid Date")', async () => { + const { _internal } = require('../../src/services/eventReminderService'); + const p = _internal.composePayload({ + event: { id: 1, event_name: 'X', event_date: '2026-06-25', customer_name: 'A' }, + recipientEmail: 'a@x.test', daysBefore: 2, businessName: 'Biz', + }); + expect(p.event_date).toBe('2026-06-25'); // raw, not pre-formatted DD.MM.YYYY + expect(p.event_date).not.toMatch(/invalid/i); + }); + test('webhook action enqueues a delivery for a configured subscription (full pipeline)', async () => { const webhook = engine.registry.getAction('webhook'); expect(typeof webhook).toBe('function'); // registered — no longer a silent no-op diff --git a/backend/src/services/eventReminderService.js b/backend/src/services/eventReminderService.js index c22c3f62..a9972407 100644 --- a/backend/src/services/eventReminderService.js +++ b/backend/src/services/eventReminderService.js @@ -103,18 +103,14 @@ function composePayload({ event, recipientEmail, daysBefore, businessName }) { || event.host_name || recipientEmail || ''; - // Event date formatted DD.MM.YYYY here for simplicity; the rendered - // email may further re-locale via the template engine when locale- - // aware formatters are introduced. - const ed = event.event_date instanceof Date ? event.event_date : new Date(event.event_date); - const day = String(ed.getUTCDate()).padStart(2, '0'); - const month = String(ed.getUTCMonth() + 1).padStart(2, '0'); - const year = ed.getUTCFullYear(); - const eventDateFormatted = `${day}.${month}.${year}`; + // Pass the RAW event_date — emailProcessor.processTemplate runs it through + // formatDate(value, recipientLanguage). Pre-formatting it (e.g. DD.MM.YYYY) + // makes the processor's new Date(...) reparse fail → "Invalid Date". Same + // contract the expiry mailer uses. return { customer_name: customerName, event_name: event.event_name || `Event #${event.id}`, - event_date: eventDateFormatted, + event_date: event.event_date || '', event_type: event.event_type || '', days_before: daysBefore, business_name: businessName || '',