feat: add email system with multi-provider support
Implements a comprehensive email system with: - Multi-provider support (SMTP, SendGrid, Mailgun, Amazon SES) - Database-stored templates with i18n (EN, FR, DE, LB) - Jinja2 template rendering with variable interpolation - Email logging for debugging and compliance - Debug mode for development (logs instead of sending) - Welcome email integration in signup flow New files: - models/database/email.py: EmailTemplate and EmailLog models - app/services/email_service.py: Provider abstraction and service - scripts/seed_email_templates.py: Template seeding script - tests/unit/services/test_email_service.py: 28 unit tests - docs/features/email-system.md: Complete documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
418
scripts/seed_email_templates.py
Normal file
418
scripts/seed_email_templates.py
Normal file
@@ -0,0 +1,418 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Seed default email templates.
|
||||
|
||||
Run: python scripts/seed_email_templates.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add project root to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from app.core.database import get_db
|
||||
from models.database.email import EmailCategory, EmailTemplate
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# EMAIL TEMPLATES
|
||||
# =============================================================================
|
||||
|
||||
TEMPLATES = [
|
||||
# -------------------------------------------------------------------------
|
||||
# SIGNUP WELCOME
|
||||
# -------------------------------------------------------------------------
|
||||
{
|
||||
"code": "signup_welcome",
|
||||
"language": "en",
|
||||
"name": "Signup Welcome",
|
||||
"description": "Sent to new vendors after successful signup",
|
||||
"category": EmailCategory.AUTH.value,
|
||||
"variables": json.dumps([
|
||||
"first_name", "company_name", "email", "vendor_code",
|
||||
"login_url", "trial_days", "tier_name"
|
||||
]),
|
||||
"subject": "Welcome to Wizamart, {{ first_name }}!",
|
||||
"body_html": """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<div style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); padding: 30px; border-radius: 10px 10px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 28px;">Welcome to Wizamart!</h1>
|
||||
</div>
|
||||
|
||||
<div style="background: #f9fafb; padding: 30px; border-radius: 0 0 10px 10px;">
|
||||
<p style="font-size: 16px;">Hi {{ first_name }},</p>
|
||||
|
||||
<p>Thank you for signing up for Wizamart! Your account for <strong>{{ company_name }}</strong> is now active.</p>
|
||||
|
||||
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; border-left: 4px solid #6366f1;">
|
||||
<h3 style="margin-top: 0; color: #6366f1;">Your Account Details</h3>
|
||||
<p style="margin: 5px 0;"><strong>Vendor Code:</strong> {{ vendor_code }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Plan:</strong> {{ tier_name }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Trial Period:</strong> {{ trial_days }} days free</p>
|
||||
</div>
|
||||
|
||||
<p>You can start managing your orders, inventory, and invoices right away:</p>
|
||||
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ login_url }}" style="background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block;">
|
||||
Go to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="color: #374151;">Getting Started</h3>
|
||||
<ol style="color: #4b5563;">
|
||||
<li>Complete your company profile</li>
|
||||
<li>Connect your Letzshop API credentials</li>
|
||||
<li>Import your products</li>
|
||||
<li>Start syncing orders!</li>
|
||||
</ol>
|
||||
|
||||
<p style="color: #6b7280; font-size: 14px; margin-top: 30px;">
|
||||
If you have any questions, just reply to this email or visit our help center.
|
||||
</p>
|
||||
|
||||
<p>Best regards,<br><strong>The Wizamart Team</strong></p>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; padding: 20px; color: #9ca3af; font-size: 12px;">
|
||||
<p>© 2024 Wizamart. Built for Luxembourg e-commerce.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>""",
|
||||
"body_text": """Welcome to Wizamart!
|
||||
|
||||
Hi {{ first_name }},
|
||||
|
||||
Thank you for signing up for Wizamart! Your account for {{ company_name }} is now active.
|
||||
|
||||
Your Account Details:
|
||||
- Vendor Code: {{ vendor_code }}
|
||||
- Plan: {{ tier_name }}
|
||||
- Trial Period: {{ trial_days }} days free
|
||||
|
||||
You can start managing your orders, inventory, and invoices right away.
|
||||
|
||||
Go to Dashboard: {{ login_url }}
|
||||
|
||||
Getting Started:
|
||||
1. Complete your company profile
|
||||
2. Connect your Letzshop API credentials
|
||||
3. Import your products
|
||||
4. Start syncing orders!
|
||||
|
||||
If you have any questions, just reply to this email.
|
||||
|
||||
Best regards,
|
||||
The Wizamart Team
|
||||
""",
|
||||
},
|
||||
{
|
||||
"code": "signup_welcome",
|
||||
"language": "fr",
|
||||
"name": "Bienvenue après inscription",
|
||||
"description": "Envoyé aux nouveaux vendeurs après inscription",
|
||||
"category": EmailCategory.AUTH.value,
|
||||
"variables": json.dumps([
|
||||
"first_name", "company_name", "email", "vendor_code",
|
||||
"login_url", "trial_days", "tier_name"
|
||||
]),
|
||||
"subject": "Bienvenue sur Wizamart, {{ first_name }} !",
|
||||
"body_html": """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<div style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); padding: 30px; border-radius: 10px 10px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 28px;">Bienvenue sur Wizamart !</h1>
|
||||
</div>
|
||||
|
||||
<div style="background: #f9fafb; padding: 30px; border-radius: 0 0 10px 10px;">
|
||||
<p style="font-size: 16px;">Bonjour {{ first_name }},</p>
|
||||
|
||||
<p>Merci de vous être inscrit sur Wizamart ! Votre compte pour <strong>{{ company_name }}</strong> est maintenant actif.</p>
|
||||
|
||||
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; border-left: 4px solid #6366f1;">
|
||||
<h3 style="margin-top: 0; color: #6366f1;">Détails de votre compte</h3>
|
||||
<p style="margin: 5px 0;"><strong>Code vendeur :</strong> {{ vendor_code }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Forfait :</strong> {{ tier_name }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Période d'essai :</strong> {{ trial_days }} jours gratuits</p>
|
||||
</div>
|
||||
|
||||
<p>Vous pouvez commencer à gérer vos commandes, stocks et factures dès maintenant :</p>
|
||||
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ login_url }}" style="background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block;">
|
||||
Accéder au tableau de bord
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="color: #374151;">Pour commencer</h3>
|
||||
<ol style="color: #4b5563;">
|
||||
<li>Complétez votre profil d'entreprise</li>
|
||||
<li>Connectez vos identifiants API Letzshop</li>
|
||||
<li>Importez vos produits</li>
|
||||
<li>Commencez à synchroniser vos commandes !</li>
|
||||
</ol>
|
||||
|
||||
<p style="color: #6b7280; font-size: 14px; margin-top: 30px;">
|
||||
Si vous avez des questions, répondez simplement à cet email.
|
||||
</p>
|
||||
|
||||
<p>Cordialement,<br><strong>L'équipe Wizamart</strong></p>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; padding: 20px; color: #9ca3af; font-size: 12px;">
|
||||
<p>© 2024 Wizamart. Conçu pour le e-commerce luxembourgeois.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>""",
|
||||
"body_text": """Bienvenue sur Wizamart !
|
||||
|
||||
Bonjour {{ first_name }},
|
||||
|
||||
Merci de vous être inscrit sur Wizamart ! Votre compte pour {{ company_name }} est maintenant actif.
|
||||
|
||||
Détails de votre compte :
|
||||
- Code vendeur : {{ vendor_code }}
|
||||
- Forfait : {{ tier_name }}
|
||||
- Période d'essai : {{ trial_days }} jours gratuits
|
||||
|
||||
Accéder au tableau de bord : {{ login_url }}
|
||||
|
||||
Pour commencer :
|
||||
1. Complétez votre profil d'entreprise
|
||||
2. Connectez vos identifiants API Letzshop
|
||||
3. Importez vos produits
|
||||
4. Commencez à synchroniser vos commandes !
|
||||
|
||||
Cordialement,
|
||||
L'équipe Wizamart
|
||||
""",
|
||||
},
|
||||
{
|
||||
"code": "signup_welcome",
|
||||
"language": "de",
|
||||
"name": "Willkommen nach Anmeldung",
|
||||
"description": "An neue Verkäufer nach erfolgreicher Anmeldung gesendet",
|
||||
"category": EmailCategory.AUTH.value,
|
||||
"variables": json.dumps([
|
||||
"first_name", "company_name", "email", "vendor_code",
|
||||
"login_url", "trial_days", "tier_name"
|
||||
]),
|
||||
"subject": "Willkommen bei Wizamart, {{ first_name }}!",
|
||||
"body_html": """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<div style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); padding: 30px; border-radius: 10px 10px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 28px;">Willkommen bei Wizamart!</h1>
|
||||
</div>
|
||||
|
||||
<div style="background: #f9fafb; padding: 30px; border-radius: 0 0 10px 10px;">
|
||||
<p style="font-size: 16px;">Hallo {{ first_name }},</p>
|
||||
|
||||
<p>Vielen Dank für Ihre Anmeldung bei Wizamart! Ihr Konto für <strong>{{ company_name }}</strong> ist jetzt aktiv.</p>
|
||||
|
||||
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; border-left: 4px solid #6366f1;">
|
||||
<h3 style="margin-top: 0; color: #6366f1;">Ihre Kontodaten</h3>
|
||||
<p style="margin: 5px 0;"><strong>Verkäufercode:</strong> {{ vendor_code }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Tarif:</strong> {{ tier_name }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Testzeitraum:</strong> {{ trial_days }} Tage kostenlos</p>
|
||||
</div>
|
||||
|
||||
<p>Sie können sofort mit der Verwaltung Ihrer Bestellungen, Bestände und Rechnungen beginnen:</p>
|
||||
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ login_url }}" style="background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block;">
|
||||
Zum Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="color: #374151;">Erste Schritte</h3>
|
||||
<ol style="color: #4b5563;">
|
||||
<li>Vervollständigen Sie Ihr Firmenprofil</li>
|
||||
<li>Verbinden Sie Ihre Letzshop API-Zugangsdaten</li>
|
||||
<li>Importieren Sie Ihre Produkte</li>
|
||||
<li>Starten Sie die Bestellungssynchronisierung!</li>
|
||||
</ol>
|
||||
|
||||
<p style="color: #6b7280; font-size: 14px; margin-top: 30px;">
|
||||
Bei Fragen antworten Sie einfach auf diese E-Mail.
|
||||
</p>
|
||||
|
||||
<p>Mit freundlichen Grüßen,<br><strong>Das Wizamart-Team</strong></p>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; padding: 20px; color: #9ca3af; font-size: 12px;">
|
||||
<p>© 2024 Wizamart. Entwickelt für den luxemburgischen E-Commerce.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>""",
|
||||
"body_text": """Willkommen bei Wizamart!
|
||||
|
||||
Hallo {{ first_name }},
|
||||
|
||||
Vielen Dank für Ihre Anmeldung bei Wizamart! Ihr Konto für {{ company_name }} ist jetzt aktiv.
|
||||
|
||||
Ihre Kontodaten:
|
||||
- Verkäufercode: {{ vendor_code }}
|
||||
- Tarif: {{ tier_name }}
|
||||
- Testzeitraum: {{ trial_days }} Tage kostenlos
|
||||
|
||||
Zum Dashboard: {{ login_url }}
|
||||
|
||||
Erste Schritte:
|
||||
1. Vervollständigen Sie Ihr Firmenprofil
|
||||
2. Verbinden Sie Ihre Letzshop API-Zugangsdaten
|
||||
3. Importieren Sie Ihre Produkte
|
||||
4. Starten Sie die Bestellungssynchronisierung!
|
||||
|
||||
Mit freundlichen Grüßen,
|
||||
Das Wizamart-Team
|
||||
""",
|
||||
},
|
||||
{
|
||||
"code": "signup_welcome",
|
||||
"language": "lb",
|
||||
"name": "Wëllkomm no der Umeldung",
|
||||
"description": "Un nei Verkeefer no erfollegräicher Umeldung geschéckt",
|
||||
"category": EmailCategory.AUTH.value,
|
||||
"variables": json.dumps([
|
||||
"first_name", "company_name", "email", "vendor_code",
|
||||
"login_url", "trial_days", "tier_name"
|
||||
]),
|
||||
"subject": "Wëllkomm op Wizamart, {{ first_name }}!",
|
||||
"body_html": """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<div style="background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); padding: 30px; border-radius: 10px 10px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 28px;">Wëllkomm op Wizamart!</h1>
|
||||
</div>
|
||||
|
||||
<div style="background: #f9fafb; padding: 30px; border-radius: 0 0 10px 10px;">
|
||||
<p style="font-size: 16px;">Moien {{ first_name }},</p>
|
||||
|
||||
<p>Merci fir d'Umeldung op Wizamart! Äre Kont fir <strong>{{ company_name }}</strong> ass elo aktiv.</p>
|
||||
|
||||
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; border-left: 4px solid #6366f1;">
|
||||
<h3 style="margin-top: 0; color: #6366f1;">Är Kontdetailer</h3>
|
||||
<p style="margin: 5px 0;"><strong>Verkeefer Code:</strong> {{ vendor_code }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Plang:</strong> {{ tier_name }}</p>
|
||||
<p style="margin: 5px 0;"><strong>Testperiod:</strong> {{ trial_days }} Deeg gratis</p>
|
||||
</div>
|
||||
|
||||
<p>Dir kënnt direkt ufänken Är Bestellungen, Lager a Rechnungen ze verwalten:</p>
|
||||
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ login_url }}" style="background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block;">
|
||||
Zum Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 style="color: #374151;">Fir unzefänken</h3>
|
||||
<ol style="color: #4b5563;">
|
||||
<li>Fëllt Äre Firmeprofil aus</li>
|
||||
<li>Verbindt Är Letzshop API Zougangsdaten</li>
|
||||
<li>Importéiert Är Produkter</li>
|
||||
<li>Fänkt un Bestellungen ze synchroniséieren!</li>
|
||||
</ol>
|
||||
|
||||
<p style="color: #6b7280; font-size: 14px; margin-top: 30px;">
|
||||
Wann Dir Froen hutt, äntwert einfach op dës E-Mail.
|
||||
</p>
|
||||
|
||||
<p>Mat beschte Gréiss,<br><strong>D'Wizamart Team</strong></p>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; padding: 20px; color: #9ca3af; font-size: 12px;">
|
||||
<p>© 2024 Wizamart. Gemaach fir de lëtzebuergeschen E-Commerce.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>""",
|
||||
"body_text": """Wëllkomm op Wizamart!
|
||||
|
||||
Moien {{ first_name }},
|
||||
|
||||
Merci fir d'Umeldung op Wizamart! Äre Kont fir {{ company_name }} ass elo aktiv.
|
||||
|
||||
Är Kontdetailer:
|
||||
- Verkeefer Code: {{ vendor_code }}
|
||||
- Plang: {{ tier_name }}
|
||||
- Testperiod: {{ trial_days }} Deeg gratis
|
||||
|
||||
Zum Dashboard: {{ login_url }}
|
||||
|
||||
Fir unzefänken:
|
||||
1. Fëllt Äre Firmeprofil aus
|
||||
2. Verbindt Är Letzshop API Zougangsdaten
|
||||
3. Importéiert Är Produkter
|
||||
4. Fänkt un Bestellungen ze synchroniséieren!
|
||||
|
||||
Mat beschte Gréiss,
|
||||
D'Wizamart Team
|
||||
""",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def seed_templates():
|
||||
"""Seed email templates into database."""
|
||||
db = next(get_db())
|
||||
|
||||
try:
|
||||
created = 0
|
||||
updated = 0
|
||||
|
||||
for template_data in TEMPLATES:
|
||||
# Check if template already exists
|
||||
existing = (
|
||||
db.query(EmailTemplate)
|
||||
.filter(
|
||||
EmailTemplate.code == template_data["code"],
|
||||
EmailTemplate.language == template_data["language"],
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
if existing:
|
||||
# Update existing template
|
||||
for key, value in template_data.items():
|
||||
setattr(existing, key, value)
|
||||
updated += 1
|
||||
print(f"Updated: {template_data['code']} ({template_data['language']})")
|
||||
else:
|
||||
# Create new template
|
||||
template = EmailTemplate(**template_data)
|
||||
db.add(template)
|
||||
created += 1
|
||||
print(f"Created: {template_data['code']} ({template_data['language']})")
|
||||
|
||||
db.commit()
|
||||
print(f"\nDone! Created: {created}, Updated: {updated}")
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"Error: {e}")
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed_templates()
|
||||
Reference in New Issue
Block a user