feat: implement email template system with vendor overrides

Add comprehensive email template management for both admin and vendors:

Admin Features:
- Email templates management page at /admin/email-templates
- Edit platform templates with language support (en, fr, de, lb)
- Preview templates with sample variables
- Send test emails
- View email logs per template

Vendor Features:
- Email templates customization page at /vendor/{code}/email-templates
- Override platform templates with vendor-specific versions
- Preview and test customized templates
- Revert to platform defaults

Technical Changes:
- Migration for vendor_email_templates table
- VendorEmailTemplate model with override management
- Enhanced EmailService with language resolution chain
  (customer preferred -> vendor preferred -> platform default)
- Branding resolution (Wizamart default, removed for whitelabel)
- Platform-only template protection (billing templates)
- Admin and vendor API endpoints with full CRUD
- Updated seed script with billing and team templates

Files: 22 changed, ~3,900 lines added

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-03 18:29:26 +01:00
parent 2e1a2fc9fc
commit c52af2a155
22 changed files with 3882 additions and 119 deletions

View File

@@ -1,107 +1,289 @@
# Email Templates Architecture (Future)
# Email System Enhancements - Implementation Plan
## Overview
Email templates follow a similar pattern to the CMS content pages system, with platform defaults that vendors can override.
Enhance the email system to support:
- Platform-level templates with vendor overrides
- Wizamart branding by default (removed for Enterprise whitelabel tier)
- Platform-only templates that cannot be overridden
- Admin UI for editing platform templates
- 4-language support (en, fr, de, lb)
- Smart language resolution (customer → vendor → platform default)
## Architecture
---
### Template Hierarchy
## Phase 1: Database & Model Foundation
1. **Platform Default Templates** - Admin-managed base templates
- Order confirmation
- Shipping notification
- Password reset
- Welcome email
- Invoice email
- etc.
### 1.1 Update EmailTemplate Model
2. **Vendor Overrides** - Optional customization
- Vendors can override platform templates
- Cannot create new template types (unlike CMS pages)
- Must maintain required placeholders
**File:** `models/database/email.py`
### Multi-language Support
Add columns:
- `is_platform_only: bool = False` - Cannot be overridden by vendors
- `required_variables: JSON` - List of variables that must be provided
- Each template exists in all supported languages (FR, EN, DE, LB)
- Vendor overrides are per-language
- Falls back to platform default if no vendor override
### Key Differences from CMS Pages
| Feature | CMS Pages | Email Templates |
|---------|-----------|-----------------|
| Create new | Vendors can create | Vendors cannot create |
| Template types | Unlimited | Fixed set by platform |
| Tier limits | Number of pages per tier | No limits (all templates available) |
| Override | Full content | Full content |
## Database Design (Proposed)
```sql
-- Platform templates (admin-managed)
CREATE TABLE email_templates (
id SERIAL PRIMARY KEY,
template_key VARCHAR(100) NOT NULL, -- e.g., 'order_confirmation'
language VARCHAR(5) NOT NULL, -- e.g., 'fr', 'en', 'de'
subject TEXT NOT NULL,
html_body TEXT NOT NULL,
text_body TEXT,
placeholders JSONB, -- Required placeholders
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(template_key, language)
);
-- Vendor overrides
CREATE TABLE vendor_email_templates (
id SERIAL PRIMARY KEY,
vendor_id INTEGER REFERENCES vendors(id),
template_key VARCHAR(100) NOT NULL,
language VARCHAR(5) NOT NULL,
subject TEXT NOT NULL,
html_body TEXT NOT NULL,
text_body TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(vendor_id, template_key, language)
);
```python
is_platform_only = Column(Boolean, default=False, nullable=False)
required_variables = Column(JSON, default=list)
```
## Rendering Flow
### 1.2 Create VendorEmailTemplate Model
1. Email service receives request (e.g., send order confirmation)
2. Check for vendor override by `(vendor_id, template_key, language)`
3. If no override, use platform default by `(template_key, language)`
4. Render template with context variables
5. Send via email service
**File:** `models/database/vendor_email_template.py`
## API Endpoints (Proposed)
```python
class VendorEmailTemplate(Base):
__tablename__ = "vendor_email_templates"
### Admin
- `GET /admin/email-templates` - List all platform templates
- `GET /admin/email-templates/{key}` - Get template by key
- `PUT /admin/email-templates/{key}` - Update platform template
- `POST /admin/email-templates/{key}/preview` - Preview template
id: int
vendor_id: int (FK vendors.id)
template_code: str # References EmailTemplate.code
language: str # en, fr, de, lb
subject: str
body_html: str
body_text: str
is_active: bool = True
created_at: datetime
updated_at: datetime
### Vendor
- `GET /vendor/email-templates` - List available templates with override status
- `GET /vendor/email-templates/{key}` - Get template (override or platform)
- `PUT /vendor/email-templates/{key}` - Create/update override
- `DELETE /vendor/email-templates/{key}` - Remove override (revert to platform)
- `POST /vendor/email-templates/{key}/preview` - Preview with vendor context
# Unique constraint: (vendor_id, template_code, language)
```
## UI Considerations
### 1.3 Migration
- Admin: Template editor with WYSIWYG or code view
- Vendor: Simple override interface showing platform default as reference
- Placeholder validation on save
- Preview with sample data
**File:** `alembic/versions/xxx_add_vendor_email_templates.py`
## Notes
- Add columns to email_templates table
- Create vendor_email_templates table
- Add indexes for lookup performance
- Email templates feature is NOT part of the current settings page
- Contact support messaging in settings directs vendors to admin
- Full implementation to follow CMS pages pattern
---
## Phase 2: Email Service Enhancement
### 2.1 Language Resolution
**Priority order for customer-facing emails:**
1. `customer.preferred_language` (if customer exists)
2. `vendor.storefront_language`
3. Platform default (`en`)
**Priority order for vendor-facing emails:**
1. `vendor.preferred_language`
2. Platform default (`en`)
### 2.2 Template Resolution
**File:** `app/services/email_service.py`
```python
def resolve_template(
self,
template_code: str,
language: str,
vendor_id: int | None = None
) -> tuple[str, str, str]: # subject, body_html, body_text
"""
Resolve template with vendor override support.
1. If vendor_id provided and template not platform-only:
- Look for VendorEmailTemplate override
- Fall back to platform EmailTemplate
2. If no vendor or platform-only:
- Use platform EmailTemplate
3. Language fallback: requested → en
"""
```
### 2.3 Branding Resolution
```python
def get_branding(self, vendor_id: int | None) -> dict:
"""
Get branding variables for email.
Returns:
- platform_name: "Wizamart" or vendor.name (if whitelabel)
- platform_logo: Wizamart logo or vendor logo (if whitelabel)
- support_email: platform or vendor support email
- vendor_name: Always vendor.name
- vendor_logo: Always vendor logo
"""
if vendor_id:
vendor = self._get_vendor(vendor_id)
if has_feature(vendor, "white_label"):
return {
"platform_name": vendor.name,
"platform_logo": vendor.logo_url,
...
}
return {
"platform_name": "Wizamart",
"platform_logo": PLATFORM_LOGO_URL,
...
}
```
---
## Phase 3: Admin API & UI
### 3.1 Admin API Endpoints
**File:** `app/api/v1/admin/email_templates.py`
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/email-templates` | List all platform templates |
| GET | `/email-templates/{code}` | Get template (all languages) |
| PUT | `/email-templates/{code}/{language}` | Update template |
| POST | `/email-templates/{code}/preview` | Preview with sample data |
| POST | `/email-templates/{code}/test` | Send test email |
### 3.2 Admin UI
**File:** `app/templates/admin/email-templates.html`
Features:
- List view with template codes, categories, language badges
- Edit view with:
- Language tabs (en, fr, de, lb)
- Subject field
- Rich text editor for body_html
- Plain text preview for body_text
- Variable reference panel
- Preview pane
- Test send button
- Platform-only badge/indicator
- Required variables display
---
## Phase 4: Vendor API & UI
### 4.1 Vendor API Endpoints
**File:** `app/api/v1/vendor/email_templates.py`
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/email-templates` | List overridable templates |
| GET | `/email-templates/{code}` | Get vendor override or default |
| PUT | `/email-templates/{code}/{language}` | Create/update override |
| DELETE | `/email-templates/{code}/{language}` | Reset to platform default |
| POST | `/email-templates/{code}/preview` | Preview with vendor branding |
| POST | `/email-templates/{code}/test` | Send test email |
### 4.2 Vendor UI
**File:** `app/templates/vendor/email-templates.html`
Features:
- List view with templates, showing "Customized" vs "Using Default"
- Edit view similar to admin but:
- Can only edit non-platform-only templates
- "Reset to Default" button
- Shows platform default as reference
- Tier gating: Only accessible for Business+ tiers
---
## Phase 5: Missing Templates
### Platform-Only Templates (Billing/Subscription)
| Code | Category | Description |
|------|----------|-------------|
| `tier_upgrade` | BILLING | Vendor upgraded subscription |
| `tier_downgrade` | BILLING | Vendor downgraded subscription |
| `payment_failed` | BILLING | Subscription payment failed |
| `subscription_cancelled` | BILLING | Subscription cancelled |
| `trial_ending` | BILLING | Trial period ending soon |
| `usage_limit_warning` | BILLING | Approaching usage limits |
### Overridable Templates
| Code | Category | Description |
|------|----------|-------------|
| `team_invite` | AUTH | Team member invitation |
| `order_shipped` | ORDERS | Order has shipped |
| `shipping_update` | ORDERS | Shipping status update |
| `low_stock_alert` | SYSTEM | Low inventory warning |
---
## Language Support Matrix
| Language | Code | Platform Default |
|----------|------|------------------|
| English | `en` | Yes (fallback) |
| French | `fr` | No |
| German | `de` | No |
| Luxembourgish | `lb` | No |
**Fallback Logic:**
```
requested_language → en (if template not found)
```
---
## Files to Create/Modify
### Phase 1 (Database)
| File | Action |
|------|--------|
| `alembic/versions/xxx_add_vendor_email_templates.py` | Create |
| `models/database/email.py` | Modify |
| `models/database/vendor_email_template.py` | Create |
| `models/database/__init__.py` | Modify |
| `models/schema/email.py` | Create |
### Phase 2 (Service)
| File | Action |
|------|--------|
| `app/services/email_service.py` | Modify |
### Phase 3 (Admin)
| File | Action |
|------|--------|
| `app/api/v1/admin/email_templates.py` | Create |
| `app/api/v1/admin/__init__.py` | Modify |
| `app/templates/admin/email-templates.html` | Create |
| `app/routes/admin_pages.py` | Modify |
| `static/admin/js/email-templates.js` | Create |
### Phase 4 (Vendor)
| File | Action |
|------|--------|
| `app/api/v1/vendor/email_templates.py` | Create |
| `app/api/v1/vendor/__init__.py` | Modify |
| `app/templates/vendor/email-templates.html` | Create |
| `app/routes/vendor_pages.py` | Modify |
| `static/vendor/js/email-templates.js` | Create |
### Phase 5 (Templates)
| File | Action |
|------|--------|
| `scripts/seed_email_templates.py` | Modify |
---
## Execution Order
1. **Phase 1**: Database migration and models (~30 min)
2. **Phase 2**: Email service enhancement (~45 min)
3. **Phase 3**: Admin API and UI (~1-2 hours)
4. **Phase 4**: Vendor API and UI (~1-2 hours)
5. **Phase 5**: Add missing templates (~30 min)
6. **Testing**: Integration tests for all phases
---
## Security Considerations
1. **XSS Prevention**: Sanitize HTML in templates before storage
2. **Access Control**: Vendors can only edit their own templates
3. **Platform-only Protection**: API enforces is_platform_only flag
4. **Template Validation**: Ensure required variables are present in template
5. **Rate Limiting**: Test email sending limited to prevent abuse