fix(workflows): backfill existing invoices + anchor grace to due date when dunning is enabled (#750)
Enabling the invoice-dunning built-in suppressed the legacy reminder ladder
but only created runs for invoices sent AFTER enabling — already-sent unpaid
invoices got dunned by neither. Now:
- Turning dunning ON enrolls every open sent/overdue unpaid invoice via
emitWorkflowEvent('invoice.sent') (engine.backfillDunningRuns(), wired into
the workflow enable toggle). Idempotent via the per-(flow,entity) dedup.
- The grace wait is anchored to the invoice's due date: computeWakeAt now
treats { untilVar, delayDays } as "var + offset" (was var-only OR now+offset),
and the built-in's waitGrace becomes { untilVar: 'dueDate', delayDays:
firstDays } (seed v6 -> v7). An already-overdue invoice duns on its real
timeline instead of restarting a fresh grace clock.
Note: the due-date-anchored graph applies to freshly seeded built-ins; an
already-admin-enabled dunning workflow still enrolls via backfill but keeps its
current grace timing until re-seeded.
This commit is contained in:
@@ -254,6 +254,17 @@ router.patch('/:id/enabled', requirePermission('workflows.manage'), async (req,
|
||||
// enabled state on the next SEED_VERSION bump (review nit #1).
|
||||
if (await hasColumnCached('workflows', 'admin_toggled_at')) patch.admin_toggled_at = db.fn.now();
|
||||
await db('workflows').where({ id }).update(patch);
|
||||
// Turning dunning ON enrolls existing open/unpaid invoices (anchored to
|
||||
// their due date) so it starts chasing current debtors, not only invoices
|
||||
// sent after enabling (#750). Best-effort — never fail the toggle over it.
|
||||
if (enabled && wf.builtin_key === 'invoice_dunning') {
|
||||
try {
|
||||
const n = await require('../services/workflows').backfillDunningRuns();
|
||||
require('../utils/logger').info('[workflow] dunning enabled — enrolled existing invoices', { enrolled: n });
|
||||
} catch (e) {
|
||||
require('../utils/logger').warn('[workflow] dunning backfill failed', { error: e.message });
|
||||
}
|
||||
}
|
||||
res.json({ id, enabled });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user