feat(workflows): migrate the dunning ladder onto the engine (cutover)
Makes the built-in dunning flow a faithful replacement for the hardcoded reminder ladder instead of a disabled representation: - queue_payment_check action delegates to invoiceService.queuePaymentCheckEmail, so the proven confirm + reminder_level + Mahngebühr state machine (recordPaymentCheckAction) stays the single source of truth — the workflow only decides WHEN the payment-check email (the gate) fires. - runScheduledTasks now SKIPS the hardcoded reminder batches when workflows is on AND the invoice_dunning built-in is enabled, so the two never double-send. - The built-in graph is re-authored to the delegation model (wait→due, grace, loop: check-paid → payment-check → wait-gap), dropping the redundant gate + generic reminder emails. A SEED_VERSION re-seeds the disabled, never-activated built-in on boot but never touches an enabled/edited one. Tests: delegation graph shape, re-seed-when-stale, enabled-protection (9 engine + 8 route = 17 passing).
This commit is contained in:
@@ -3392,7 +3392,18 @@ async function runScheduledTasks() {
|
||||
// Throttled to one email per 24h per invoice via
|
||||
// invoices.last_payment_check_at.
|
||||
const remindersEnabled = await getAppSetting('crm_invoices_reminders_enabled');
|
||||
if (remindersEnabled !== false) {
|
||||
// Mutual exclusion with the workflow engine: when the `workflows` flag is on
|
||||
// AND the invoice_dunning built-in is enabled, the engine fires the same
|
||||
// payment-check emails — running this hardcoded ladder too would double-send.
|
||||
let engineDrivesDunning = false;
|
||||
try {
|
||||
const { isFeatureEnabled } = require('../middleware/requireFeatureFlag');
|
||||
if (await isFeatureEnabled('workflows')) {
|
||||
const dunning = await db('workflows').where({ builtin_key: 'invoice_dunning', enabled: true }).first();
|
||||
engineDrivesDunning = !!dunning;
|
||||
}
|
||||
} catch (_) { /* workflows tables absent / flag system down → ladder stays on */ }
|
||||
if (remindersEnabled !== false && !engineDrivesDunning) {
|
||||
const firstDays = ensureInt(await getAppSetting('crm_invoices_reminder_first_days')) || 14;
|
||||
const secondDays = ensureInt(await getAppSetting('crm_invoices_reminder_second_days')) || 30;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user