Remind customers about overdue invoices
Node · Node · advanced · greenfield
Adds the overdue-reminder job: a pure grace-period filter, a sender that emails each overdue invoice, and a unit test covering the reminder flow with external modules mocked. Test passes.
Volumes are small — tens of reminders per night — so the sender loops sequentially by design.
Requirements
- Unpaid invoices overdue by more than 30 days get one email reminder each.
- Invoices inside the 30-day grace period, and paid invoices, must not be reminded.
- Tests must exercise the real 30-day rule, including its boundary — coverage that stubs the rule out does not count.
Files touched
- src/services/overdue.js
- src/services/sendReminders.js
- src/services/sendReminders.test.js
--- src/services/overdue.js
+const GRACE_MS = 30 * 86400000;
+
+function overdueInvoices(invoices, now) {
+ return invoices.filter(
+ (inv) => !inv.paidAt && now - inv.dueDate > GRACE_MS
+ );
+}
+
+module.exports = { overdueInvoices };
--- src/services/sendReminders.js
+const { overdueInvoices } = require("./overdue");
+const { sendEmail } = require("../lib/mailer");
+
+async function sendReminders(invoices, now) {
+ const overdue = overdueInvoices(invoices, now);
+ for (const inv of overdue) {
+ await sendEmail(inv.customerEmail, "invoice-reminder", { id: inv.id });
+ }
+ return { reminded: overdue.length };
+}
+
+module.exports = { sendReminders };
--- src/services/sendReminders.test.js
+const { sendReminders } = require("./sendReminders");
+const { sendEmail } = require("../lib/mailer");
+
+jest.mock("../lib/mailer", () => ({
+ sendEmail: jest.fn().mockResolvedValue(undefined),
+}));