From 42a01a011ec684136bbf97ca947903a424f048f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Feb 2026 20:49:43 +0000 Subject: [PATCH] add design for registration-confirmation-email Covers: notify_parent() on AdminNotifier, multipart HTML email with inline Swiss QR-bill (qrbill library), CHF 80 fixed payment data, German default language, best-effort error handling. https://claude.ai/code/session_01LjjK7RjKVnC8bETtccfgna --- .../registration-confirmation-email/design.md | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 openspec/changes/registration-confirmation-email/design.md diff --git a/openspec/changes/registration-confirmation-email/design.md b/openspec/changes/registration-confirmation-email/design.md new file mode 100644 index 0000000..881241a --- /dev/null +++ b/openspec/changes/registration-confirmation-email/design.md @@ -0,0 +1,97 @@ +## Context + +When a registration is completed, `AdminNotifier.notify_admin()` sends an email to the relevant playgroup leaders. The parent receives nothing — no acknowledgement, no summary, no payment details. + +The existing `AdminNotifier` in `src/notifications/notifier.py` handles both new-registration and update notifications to admins. It uses `MIMEMultipart("alternative")` and sends via the configured SMTP server. All the SMTP plumbing already works. + +This change adds a parent-facing confirmation email triggered at the same point as the admin notification. + +## Goals / Non-Goals + +**Goals:** +- Parent receives an HTML confirmation email immediately after registration is stored +- Email contains full registration summary (all 13 fields) +- Email contains payment instructions for the CHF 80 registration fee +- Email contains a Swiss QR-bill (QR code image) embedded inline so the parent can pay via banking app or print + +**Non-Goals:** +- Translating the payment section into English (Swiss QR-bill and bank instructions are always in German regardless of language) +- Sending a reminder if the parent hasn't paid (payment tracking is out of scope) +- Generating a full PDF invoice (QR code embedded in HTML email is sufficient) +- Sibling discount handling in the QR-bill amount (CHF 80 is always fixed for the registration fee) + +## Decisions + +### 1. Extend `AdminNotifier` vs. New Class + +**Decision**: Add `notify_parent()` to the existing `AdminNotifier` class (renamed conceptually; kept in the same file for now). + +**Rationale**: The SMTP plumbing (`_send`, `_smtp_host`, credentials) is already there. A `notify_parent` method reuses all of it. Splitting into a separate class would require duplicating constructor parameters and SMTP setup for no structural benefit at this stage. + +**Trade-off**: `AdminNotifier` becomes slightly misnamed. Acceptable — the class handles all outbound notification emails. Rename in a future refactor if needed. + +### 2. HTML Email Format + +**Decision**: Send `multipart/alternative` with both plain-text and HTML parts. The HTML part is the primary view; plain-text is fallback. + +**Rationale**: Matches the existing `_send` method's `MIMEMultipart("alternative")` pattern. HTML is needed to embed the QR-bill image inline. + +**QR image embedding**: Use `multipart/related` wrapping the HTML part, with the QR PNG attached as `Content-ID` (`cid:qrbill`). This is the standard approach for inline images that don't appear as attachments. + +**Structure:** +``` +multipart/mixed +└── multipart/alternative + ├── text/plain (fallback) + └── multipart/related + ├── text/html (references cid:qrbill) + └── image/png (Content-ID: qrbill, inline) +``` + +### 3. Swiss QR-Bill Generation + +**Decision**: Use the `qrbill` Python library to generate the QR code image. + +**Rationale**: `qrbill` implements the Swiss QR-bill standard (SIX Group spec) directly. It accepts IBAN, payee address, amount, and currency, and outputs an SVG or PNG. No external services required. + +**Fixed payment data** (hardcoded in the notifier, not in config — this is stable bank data): +- IBAN: `CH14 0900 0000 4930 8018 8` +- Payee: Familienverein Fällanden Spielgruppen, c/o Markus Graf, Huebwisstrase 5, 8117 Fällanden +- Amount: CHF 80.00 +- Currency: CHF +- Reference type: NON (no structured reference) + +**Output**: PNG bytes, embedded as inline image in HTML email. + +**Dependency**: Add `qrbill` to `pyproject.toml` dependencies. + +### 4. Language + +**Decision**: Confirmation email body in German by default. The payment block is always German. + +**Rationale**: The registration schema does not store the parent's detected language. German is the default for all admin-facing and parent-facing content. Adding language detection to the registration model is out of scope for this change. + +**Future improvement**: Store `language` in `metadata` during conversation and pass it to the notifier so the body can be in English when detected. + +### 5. Trigger Point + +**Decision**: Call `notify_parent()` immediately after `notify_admin()` at the same trigger site — wherever `notify_admin` is currently called (in `src/agent/core.py` or equivalent). + +**Rationale**: The parent notification is a direct consequence of the same event (registration completed). No separate trigger or queue needed. + +**Error isolation**: If the parent email fails, log the error but do not fail the registration or block the admin notification. Both notifications are best-effort. + +## Risks / Trade-offs + +**`qrbill` library maturity**: Actively maintained, used in production Swiss applications. Risk is low. If the library is unavailable, the QR code can be omitted and the plain IBAN text still enables payment. + +**Inline image rendering**: Some email clients block inline images by default (Outlook, some mobile clients). The plain-text fallback and the raw IBAN text in the HTML body ensure the payment info is always readable even if the QR image is blocked. + +**SMTP failure for parent email**: Parent notification is non-critical (the registration is already stored). Failure is logged as a warning, not an exception. + +**Language mismatch**: A parent who spoke English during chat will receive a German confirmation. Acceptable for MVP given the Swiss German context and the fact that the payment instructions must be in German regardless. + +## Open Questions + +- Should the confirmation email also include the monthly subscription fee (in addition to the CHF 80 registration fee), or only the registration fee QR-bill? The CHF 80 one-time fee is the immediate action required; the monthly fee is recurring and not yet payable. **Proposed answer: include both as informational text, but the QR-bill is for CHF 80 only.** +- Should the parent's email be CC'd on the admin notification, or kept as a separate send? **Proposed answer: separate send — keeps admin and parent content distinct.**