You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/apps/hq/src/reminder-templates.ts

73 lines
3.3 KiB
TypeScript

import { formatINR } from '@sims/domain'
/** Templated reminder emails — one place so WhatsApp can reuse them later (spec §7). */
export type ReminderRuleKind =
| 'invoice_overdue' | 'renewal_due' | 'amc_expiring'
| 'follow_up' | 'recurring_generated' | 'email_bounced'
export interface ReminderContext {
clientName: string; companyName: string
docNo?: string; amountPaise?: number; dueDate?: string
daysOverdue?: number; coverage?: string; period?: string
}
export interface ReminderMail { subject: string; bodyText: string }
const signOff = (ctx: ReminderContext): string => `Warm regards,\n${ctx.companyName}`
export function reminderEmail(kind: ReminderRuleKind, ctx: ReminderContext): ReminderMail {
switch (kind) {
case 'invoice_overdue': {
const amt = ctx.amountPaise !== undefined ? formatINR(ctx.amountPaise) : ''
return {
subject: `Payment reminder — Invoice ${ctx.docNo} (${ctx.companyName})`,
bodyText:
`Dear ${ctx.clientName},\n\n`
+ `This is a gentle reminder that Invoice ${ctx.docNo}${amt !== '' ? ` for ${amt}` : ''} is outstanding`
+ (ctx.daysOverdue !== undefined ? ` and is now ${ctx.daysOverdue} day(s) past due` : '')
+ `. We would be grateful if you could arrange payment at your earliest convenience.\n\n`
+ `If payment has already been made, kindly ignore this message.\n\n`
+ signOff(ctx),
}
}
case 'recurring_generated': {
const amt = ctx.amountPaise !== undefined ? formatINR(ctx.amountPaise) : ''
const forPeriod = ctx.period !== undefined ? ` for ${ctx.period}` : ''
return {
subject: `Invoice ${ctx.docNo}${forPeriod} (${ctx.companyName})`,
bodyText:
`Dear ${ctx.clientName},\n\n`
+ `Please find attached Invoice ${ctx.docNo}${forPeriod}${amt !== '' ? `, amounting to ${amt}` : ''}.\n\n`
+ `Kindly arrange payment as per the agreed terms. Thank you for your continued association.\n\n`
+ signOff(ctx),
}
}
case 'renewal_due':
return {
subject: `Subscription renewal reminder (${ctx.companyName})`,
bodyText:
`Dear ${ctx.clientName},\n\n`
+ `Your subscription is due for renewal${ctx.dueDate !== undefined ? ` on ${ctx.dueDate}` : ' shortly'}. `
+ `Please let us know if you would like us to raise the renewal invoice.\n\n`
+ `We value your continued association and look forward to serving you.\n\n`
+ signOff(ctx),
}
case 'amc_expiring':
return {
subject: `AMC renewal reminder${ctx.coverage !== undefined && ctx.coverage !== '' ? `${ctx.coverage}` : ''} (${ctx.companyName})`,
bodyText:
`Dear ${ctx.clientName},\n\n`
+ `Your Annual Maintenance Contract${ctx.coverage !== undefined && ctx.coverage !== '' ? ` (${ctx.coverage})` : ''} `
+ `is due to expire${ctx.dueDate !== undefined ? ` on ${ctx.dueDate}` : ' soon'}. `
+ `To ensure uninterrupted support, please confirm the renewal at your convenience.\n\n`
+ signOff(ctx),
}
case 'follow_up':
case 'email_bounced':
// Internal dashboard items — never emailed to the client. A neutral value
// exists so callers can't crash; repos-reminders refuses to send these kinds.
return { subject: '(internal reminder)', bodyText: '' }
}
}