M17 Communications — outbound email + notifications
PHPMailer 6.12 + branded HTML templates + token engine + queue cron + delivery tracking. 9 firm-vetted templates wired to lifecycle events. Test mode for safe sandbox testing. Internal in-app notifications via NotificationService with quiet hours + per-user preferences.
Two channels
Branded HTML emails to clients + employees. 9 lifecycle-triggered templates. Queue + delivery tracking.
Real-time notifications between users. 16 event types × 3 channels (in-app · email · push). Per-user preferences + quiet hours.
SMTP configuration
Settings → Communication. SMTP credentials live in config/communication.local.php (gitignored, never in DB):
- Host · port · user · password · encryption (TLS/SSL)
- From-name + from-email
- Reply-to · BCC-archive (auto-bcc to internal address for compliance)
- Test-mode + test-recipient
- Queue max-per-run · retry delay
- Signature HTML
System fallback: if local.php is absent, reads from system_settings. Local file is preferred for security.
The 9 templates
| Code | Trigger |
|---|---|
| client.welcome | Lead converted to client |
| client.info_request | Manual |
| client.fieldwork_start | Job → in_progress |
| client.report_delivered | Audit report sent |
| quote.sent | Quote → sent |
| invoice.sent | Invoice → sent |
| invoice.payment_reminder | Invoice → overdue + dunning chain |
| payment.received | Payment recorded |
| compliance.expiry_reminder | Doc/visa expiring (M13/M16) |
Branded HTML layout
Every email follows the same 600px-wide table layout:
- Firm logo (loaded inline from companies.logo_path)
- Gradient header in firm primary/secondary colours
- Body content area
- Signature footer with firm address + phone + VAT TRN
- "TEST MODE" yellow banner when test_mode=1
- Plain-text auto-fallback
Queue + cron
Emails are queued to email_queue at trigger time. cron-m17-email-queue.php processes pending queue rows every 5 minutes:
- Fetches next batch (capped by
communication.queue_max_per_run) - Sends each via SMTP
- On success → moves to
email_logwith sent_at - On failure → increments retry_count, requeues with delay
- After max_attempts → flips to failed
Email log
Forensic per-send record at Settings → Communication → Email Log:
- recipient · cc · bcc · subject
- template_code · related entity
- queued_at · sent_at · delivered_at · opened_at
- result (success / bounced / failed)
- SMTP host · Message-ID · error message
500 most recent rows shown. CSV export available. Never deleted (forensic immutability).
Test mode
For sandbox testing without spamming clients:
communication.test_mode=1communication.test_recipient=qa@firm.com- All outbound mail redirects to test_recipient
- Yellow "TEST MODE" banner inserted at top of HTML
- Original-recipient stamped in email_log
Critical for staging. Never deploy to production with test_mode=1.
In-app notifications
NotificationService emits to user inbox + bell icon. 16 event types:
- job.created · job.assigned · job.status_changed · job.completed
- invoice.sent · invoice.overdue · payment.received
- quote.sent · quote.accepted
- review_point.raised · review_point.resolved
- leave.submitted · leave.decided
- document.expiring · document.uploaded · vault.download
Per-user preferences at My Profile → Notification preferences: 16-event grid × 3-channel (in-app / email / push) toggles, plus quiet hours + sound + toast settings.
EventDispatcher
Single funnel for outbound triggers. 4 events wired:
onInvoiceSent→ invoice.sent templateonQuoteSent→ quote.sent templateonPaymentReceived→ payment.received templateonJobCompleted→ conditional client.report_delivered
All hooks are post-commit + try/catch — SMTP failures never break the upstream state machine.
Settings → Communication → Send test email. Type your own email + click. Within 5 seconds you should see it arrive. Check Email Log — your test send appears with SMTP host + Message-ID + 200 OK.
SMTP credentials in DB are visible to super_admin via Settings → Communication. Use the local.php file pattern instead — credentials never persist in DB or git. The .example file shows the format.
Configure SPF + DKIM on the firm's sending domain. Without these, Gmail / Outlook may flag outbound emails as spam. Talk to your IT / hosting provider — 30 min to set up, dramatic deliverability improvement.