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' | 'quote_followup' export interface ReminderContext { clientName: string; companyName: string docNo?: string; amountPaise?: number; dueDate?: string daysOverdue?: number; coverage?: string; period?: string /** quote_followup: client-facing reference — the docNo, or 'dated YYYY-MM-DD' when unissued (never "null"). */ ref?: string /** quote_followup: the live public share link (resolved read-only; minted only in the send path). */ shareUrl?: string /** quote_followup: dated message text from resolveSchedule; falls back to the code constants below. */ subjectTemplate?: string bodyTemplate?: string } /** Code-constant fallback text for quote_followup (spec §7) — reminder_schedule rows * override these with dated subject/body; they live here so repos-reminders can * reuse them without an import cycle. */ export const QUOTE_FOLLOWUP_DEFAULT_SUBJECT = 'Following up on our quotation {ref} ({companyName})' export const QUOTE_FOLLOWUP_DEFAULT_BODY = 'Dear {clientName}, we wanted to check whether you had a chance to review our quotation {ref}. ' + 'You can view it any time here: {shareUrl}. We\'d be glad to answer any questions. ' + 'Warm regards, {companyName}.' 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) : '' // D18: with a due date the mail says the honest thing ("was due on X, N days // overdue"); legacy invoices without one keep the past-due-since-issue wording. const lateness = ctx.dueDate !== undefined ? ` was due on ${ctx.dueDate}` + (ctx.daysOverdue !== undefined ? ` and is now ${ctx.daysOverdue} day(s) overdue` : '') : ` is outstanding` + (ctx.daysOverdue !== undefined ? ` and is now ${ctx.daysOverdue} day(s) past due` : '') 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}` : ''}${lateness}` + `. 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 'quote_followup': { // Dated subject/body come from reminder_schedule via the context builder; // placeholders are filled here so a schedule row can rewrite the copy freely. const fill = (t: string): string => t .replaceAll('{ref}', ctx.ref ?? '') .replaceAll('{clientName}', ctx.clientName) .replaceAll('{companyName}', ctx.companyName) .replaceAll('{shareUrl}', ctx.shareUrl ?? '') return { subject: fill(ctx.subjectTemplate ?? QUOTE_FOLLOWUP_DEFAULT_SUBJECT), bodyText: fill(ctx.bodyTemplate ?? QUOTE_FOLLOWUP_DEFAULT_BODY), } } 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: '' } } }