Files
orion/docs/implementation/email-templates-architecture.md
Samir Boulahtit c52af2a155 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>
2026-01-03 18:29:26 +01:00

8.0 KiB

Email System Enhancements - Implementation Plan

Overview

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)

Phase 1: Database & Model Foundation

1.1 Update EmailTemplate Model

File: models/database/email.py

Add columns:

  • is_platform_only: bool = False - Cannot be overridden by vendors
  • required_variables: JSON - List of variables that must be provided
is_platform_only = Column(Boolean, default=False, nullable=False)
required_variables = Column(JSON, default=list)

1.2 Create VendorEmailTemplate Model

File: models/database/vendor_email_template.py

class VendorEmailTemplate(Base):
    __tablename__ = "vendor_email_templates"

    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

    # Unique constraint: (vendor_id, template_code, language)

1.3 Migration

File: alembic/versions/xxx_add_vendor_email_templates.py

  • Add columns to email_templates table
  • Create vendor_email_templates table
  • Add indexes for lookup performance

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

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

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