Files
TapTrack-Hub/.paul/phases/09-email-dispatcher/README.md
kevin-asprec 1febb3cfa9 feat(phase-9): email dispatcher — HTML templates, delivery log, test endpoint
Backend:
- app/models/email_log.py: EmailLog table (school_id, to, subject, type, status,
  error, sent_at) with EmailType + EmailStatus enums
- migrations/002_phase9_email_logs.py: Alembic migration for email_logs table
- app/templates/email/: 6 Jinja2 HTML templates — base layout, invoice,
  low_credit, license_expiry, overdue_warning, suspension
- app/services/email.py: enhanced send_email() — accepts template_name+context
  for HTML rendering, logs every attempt to email_logs, retries up to 3x on
  transient SMTP failure with exponential backoff
- app/routers/email.py: GET /api/email/logs (paginated, filterable by type/status/school),
  POST /api/email/test (send test email, super admin)
- tasks/billing.py: invoice + overdue warning + suspension emails now use HTML templates
- tasks/sms.py: low credit alert now uses HTML template
- tasks/license.py: expiry warning now uses HTML template
- app/main.py + migrations/env.py: wire in email_log model + email router

Frontend:
- EmailLogsPage.vue: table with to/subject/type badge/status badge/sent_at/error,
  type+status filters, pagination, Send Test Email modal
- router/index.ts: /email-logs route
- AppSidebar.vue: Email Logs nav item
- api.ts: getEmailLogs, sendTestEmail
2026-03-16 14:03:02 +08:00

2.0 KiB

Phase 09: Email Dispatcher

Status: Complete Completed: 2026-03-16 Depends on: Phase 8 (Billing Engine)

Goal

All automated emails send via HTML templates with consistent branding. Every send attempt is logged to email_logs. Super admin can view the delivery log and send a test email to verify SMTP config.

What was built

Backend

app/models/email_log.py (new)

  • EmailLog table: id, school_id (nullable FK), to_email, subject, email_type, status (sent/failed), error_message, sent_at, created_at

app/templates/email/ (new — 6 files)

  • base.html — shared branded layout (header + footer)
  • invoice.html — invoice notification with amount + due date
  • low_credit.html — low credit warning with balance + threshold
  • license_expiry.html — license expiry countdown
  • overdue_warning.html — overdue invoice warning
  • suspension.html — account suspended notice

app/services/email.py — enhanced

  • send_email() now accepts template_name + context for HTML rendering
  • Logs every attempt to email_logs via a sync session
  • Falls back to plain text if template not found
  • Retries up to 3 times on transient SMTP failure

app/routers/email.py (new)

  • GET /api/email-logs — paginated log with type/status/school filters (super admin)
  • POST /api/email/test — send test email to verify SMTP (super admin)

Updated tasks — all now pass template_name + context to send_email():

  • tasks/billing.py — invoice email, overdue warning, suspension email
  • tasks/sms.py — low credit alert
  • tasks/license.py — expiry warning

Frontend

EmailLogsPage.vue (new)

  • Table: to, subject, type badge, status badge, sent_at, error message (expandable)
  • Filter by type + status; pagination
  • "Send Test Email" button → modal with address input

Router/email-logs route added (super admin) AppSidebar — "Email Logs" nav item added api.tsgetEmailLogs(params), sendTestEmail(to)