#!/usr/bin/env python3 """ Seed loyalty email templates. Idempotent: safe to run repeatedly — upserts by (code, language). Run: python scripts/seed/seed_email_templates_loyalty.py """ import contextlib import json import sys from pathlib import Path # Add project root to path sys.path.insert(0, str(Path(__file__).parent.parent.parent)) for _mod in [ "app.modules.billing.models", "app.modules.inventory.models", "app.modules.cart.models", "app.modules.messaging.models", "app.modules.loyalty.models", "app.modules.catalog.models", "app.modules.customers.models", "app.modules.orders.models", "app.modules.marketplace.models", "app.modules.cms.models", ]: with contextlib.suppress(ImportError): __import__(_mod) from app.core.database import get_db from app.modules.messaging.models import EmailCategory, EmailTemplate # ============================================================================= # SHARED HTML STRUCTURE # ============================================================================= _HEAD = """ """ def _wrap(gradient_from, gradient_to, title, body_html): """Wrap email body in the standard template chrome.""" return f"""{_HEAD}

{title}

{body_html}

Powered by RewardFlow

""" # ============================================================================= # LOYALTY TEMPLATES # ============================================================================= # ── Enrollment ────────────────────────────────────────────────────────────── _ENROLLMENT_BODY_EN = """

Hi {{ customer_name }},

Welcome to {{ program_name }}! Your loyalty card is ready.

Card Number: {{ card_number }}

Store: {{ store_name }}

Start earning rewards on every visit!

Best regards,
{{ store_name }}

""" _ENROLLMENT_BODY_FR = """

Bonjour {{ customer_name }},

Bienvenue dans {{ program_name }} ! Votre carte de fidélité est prête.

Numéro de carte : {{ card_number }}

Point de vente : {{ store_name }}

Commencez à gagner des récompenses à chaque visite !

Cordialement,
{{ store_name }}

""" _ENROLLMENT_BODY_DE = """

Hallo {{ customer_name }},

Willkommen bei {{ program_name }}! Ihre Treuekarte ist bereit.

Kartennummer: {{ card_number }}

Filiale: {{ store_name }}

Sammeln Sie ab sofort Prämien bei jedem Besuch!

Mit freundlichen Grüßen,
{{ store_name }}

""" _ENROLLMENT_BODY_LB = """

Moien {{ customer_name }},

Wëllkomm bei {{ program_name }}! Är Treiekaart ass prett.

Kaartenummer: {{ card_number }}

Geschäft: {{ store_name }}

Fänkt un Belounungen ze sammelen bei all Besuch!

Léif Gréiss,
{{ store_name }}

""" # ── Welcome Bonus ─────────────────────────────────────────────────────────── _WELCOME_BONUS_BODY_EN = """

Hi {{ customer_name }},

Great news! You've received {{ points }} bonus points as a welcome gift from {{ program_name }}.

+{{ points }}

bonus points

These points are already in your balance and ready to use toward rewards.

Best regards,
{{ store_name }}

""" _WELCOME_BONUS_BODY_FR = """

Bonjour {{ customer_name }},

Bonne nouvelle ! Vous avez reçu {{ points }} points bonus en cadeau de bienvenue de {{ program_name }}.

+{{ points }}

points bonus

Ces points sont déjà sur votre solde et utilisables pour des récompenses.

Cordialement,
{{ store_name }}

""" _WELCOME_BONUS_BODY_DE = """

Hallo {{ customer_name }},

Tolle Neuigkeiten! Sie haben {{ points }} Bonuspunkte als Willkommensgeschenk von {{ program_name }} erhalten.

+{{ points }}

Bonuspunkte

Diese Punkte sind bereits auf Ihrem Konto und können für Prämien eingesetzt werden.

Mit freundlichen Grüßen,
{{ store_name }}

""" _WELCOME_BONUS_BODY_LB = """

Moien {{ customer_name }},

Gutt Noriichten! Dir hutt {{ points }} Bonuspunkten als Wëllkommsgeschenk vu {{ program_name }} kritt.

+{{ points }}

Bonuspunkten

Dës Punkten sinn schonn op Ärem Konto a kënne fir Beloununge benotzt ginn.

Léif Gréiss,
{{ store_name }}

""" # ── Points Expiring ───────────────────────────────────────────────────────── _POINTS_EXPIRING_BODY_EN = """

Hi {{ customer_name }},

This is a friendly reminder that {{ points }} points in your {{ program_name }} account will expire in {{ days_remaining }} days (on {{ expiration_date }}).

⏳ {{ points }} points expiring on {{ expiration_date }}

Visit us before then to use your points toward a reward!

Best regards,
{{ store_name }}

""" _POINTS_EXPIRING_BODY_FR = """

Bonjour {{ customer_name }},

Un petit rappel : {{ points }} points de votre compte {{ program_name }} expireront dans {{ days_remaining }} jours (le {{ expiration_date }}).

⏳ {{ points }} points expirent le {{ expiration_date }}

Rendez-nous visite avant cette date pour utiliser vos points !

Cordialement,
{{ store_name }}

""" _POINTS_EXPIRING_BODY_DE = """

Hallo {{ customer_name }},

Eine freundliche Erinnerung: {{ points }} Punkte auf Ihrem {{ program_name }}-Konto verfallen in {{ days_remaining }} Tagen (am {{ expiration_date }}).

⏳ {{ points }} Punkte verfallen am {{ expiration_date }}

Besuchen Sie uns vorher, um Ihre Punkte einzulösen!

Mit freundlichen Grüßen,
{{ store_name }}

""" _POINTS_EXPIRING_BODY_LB = """

Moien {{ customer_name }},

Eng kleng Erënnerung: {{ points }} Punkten op Ärem {{ program_name }}-Konto verfalen an {{ days_remaining }} Deeg (den {{ expiration_date }}).

⏳ {{ points }} Punkten verfalen den {{ expiration_date }}

Besicht eis virun deem Datum fir Är Punkten anzeléisen!

Léif Gréiss,
{{ store_name }}

""" # ── Points Expired ────────────────────────────────────────────────────────── _POINTS_EXPIRED_BODY_EN = """

Hi {{ customer_name }},

Unfortunately, {{ expired_points }} points in your {{ program_name }} account have expired.

{{ expired_points }} points expired

Don't worry — you can keep earning points on your next visit!

Best regards,
{{ store_name }}

""" _POINTS_EXPIRED_BODY_FR = """

Bonjour {{ customer_name }},

Malheureusement, {{ expired_points }} points de votre compte {{ program_name }} ont expiré.

{{ expired_points }} points expirés

Pas d'inquiétude — vous pouvez continuer à gagner des points lors de votre prochaine visite !

Cordialement,
{{ store_name }}

""" _POINTS_EXPIRED_BODY_DE = """

Hallo {{ customer_name }},

Leider sind {{ expired_points }} Punkte auf Ihrem {{ program_name }}-Konto verfallen.

{{ expired_points }} Punkte verfallen

Keine Sorge — Sie können bei Ihrem nächsten Besuch weiter Punkte sammeln!

Mit freundlichen Grüßen,
{{ store_name }}

""" _POINTS_EXPIRED_BODY_LB = """

Moien {{ customer_name }},

Leider sinn {{ expired_points }} Punkten op Ärem {{ program_name }}-Konto ofgelaf.

{{ expired_points }} Punkten ofgelaf

Keng Suergen — Dir kënnt bei Ärem nächste Besuch weider Punkten sammelen!

Léif Gréiss,
{{ store_name }}

""" # ── Reward Ready ──────────────────────────────────────────────────────────── _REWARD_READY_BODY_EN = """

Hi {{ customer_name }},

Congratulations! You've earned a reward at {{ program_name }}! 🎉

🎁 {{ reward_name }}

Visit {{ store_name }} to redeem your reward. Just show your loyalty card!

Best regards,
{{ store_name }}

""" _REWARD_READY_BODY_FR = """

Bonjour {{ customer_name }},

Félicitations ! Vous avez gagné une récompense chez {{ program_name }} ! 🎉

🎁 {{ reward_name }}

Rendez-vous chez {{ store_name }} pour récupérer votre récompense. Montrez simplement votre carte de fidélité !

Cordialement,
{{ store_name }}

""" _REWARD_READY_BODY_DE = """

Hallo {{ customer_name }},

Herzlichen Glückwunsch! Sie haben eine Prämie bei {{ program_name }} verdient! 🎉

🎁 {{ reward_name }}

Besuchen Sie {{ store_name }} um Ihre Prämie einzulösen. Zeigen Sie einfach Ihre Treuekarte!

Mit freundlichen Grüßen,
{{ store_name }}

""" _REWARD_READY_BODY_LB = """

Moien {{ customer_name }},

Felicitatiounen! Dir hutt eng Belounung bei {{ program_name }} verdéngt! 🎉

🎁 {{ reward_name }}

Besicht {{ store_name }} fir Är Belounung ofzehuelen. Weist einfach Är Treiekaart!

Léif Gréiss,
{{ store_name }}

""" # ============================================================================= # BUILD TEMPLATE LIST # ============================================================================= def _make_templates(): """Build the full list of loyalty email templates.""" templates = [] _defs = [ { "code": "loyalty_enrollment", "name": { "en": "Loyalty Enrollment", "fr": "Inscription fidélité", "de": "Treue-Anmeldung", "lb": "Treie-Umeldung", }, "description": "Sent when a customer enrolls in a loyalty program", "category": EmailCategory.SYSTEM.value, "variables": ["customer_name", "program_name", "card_number", "store_name"], "subject": { "en": "Welcome to {{ program_name }}!", "fr": "Bienvenue chez {{ program_name }} !", "de": "Willkommen bei {{ program_name }}!", "lb": "Wëllkomm bei {{ program_name }}!", }, "body_html": { "en": _wrap("#6366f1", "#8b5cf6", "Welcome!", _ENROLLMENT_BODY_EN), "fr": _wrap("#6366f1", "#8b5cf6", "Bienvenue !", _ENROLLMENT_BODY_FR), "de": _wrap("#6366f1", "#8b5cf6", "Willkommen!", _ENROLLMENT_BODY_DE), "lb": _wrap("#6366f1", "#8b5cf6", "Wëllkomm!", _ENROLLMENT_BODY_LB), }, "body_text": { "en": "Hi {{ customer_name }},\n\nWelcome to {{ program_name }}! Your loyalty card number is {{ card_number }}.\n\nStart earning rewards on every visit!\n\nBest regards,\n{{ store_name }}", "fr": "Bonjour {{ customer_name }},\n\nBienvenue chez {{ program_name }} ! Votre numéro de carte est {{ card_number }}.\n\nCommencez à gagner des récompenses !\n\nCordialement,\n{{ store_name }}", "de": "Hallo {{ customer_name }},\n\nWillkommen bei {{ program_name }}! Ihre Kartennummer ist {{ card_number }}.\n\nSammeln Sie ab sofort Prämien!\n\nMit freundlichen Grüßen,\n{{ store_name }}", "lb": "Moien {{ customer_name }},\n\nWëllkomm bei {{ program_name }}! Är Kaartenummer ass {{ card_number }}.\n\nFänkt un Belounungen ze sammelen!\n\nLéif Gréiss,\n{{ store_name }}", }, }, { "code": "loyalty_welcome_bonus", "name": { "en": "Loyalty Welcome Bonus", "fr": "Bonus de bienvenue fidélité", "de": "Treue-Willkommensbonus", "lb": "Treie-Wëllkommsbonus", }, "description": "Sent when a customer receives welcome bonus points", "category": EmailCategory.SYSTEM.value, "variables": ["customer_name", "program_name", "points", "store_name"], "subject": { "en": "You earned {{ points }} bonus points!", "fr": "Vous avez gagné {{ points }} points bonus !", "de": "Sie haben {{ points }} Bonuspunkte erhalten!", "lb": "Dir hutt {{ points }} Bonuspunkten kritt!", }, "body_html": { "en": _wrap("#6366f1", "#8b5cf6", "Bonus Points!", _WELCOME_BONUS_BODY_EN), "fr": _wrap("#6366f1", "#8b5cf6", "Points Bonus !", _WELCOME_BONUS_BODY_FR), "de": _wrap("#6366f1", "#8b5cf6", "Bonuspunkte!", _WELCOME_BONUS_BODY_DE), "lb": _wrap("#6366f1", "#8b5cf6", "Bonuspunkten!", _WELCOME_BONUS_BODY_LB), }, "body_text": { "en": "Hi {{ customer_name }},\n\nYou've received {{ points }} bonus points as a welcome gift from {{ program_name }}.\n\nThese points are already in your balance.\n\nBest regards,\n{{ store_name }}", "fr": "Bonjour {{ customer_name }},\n\nVous avez reçu {{ points }} points bonus de {{ program_name }}.\n\nCes points sont déjà sur votre solde.\n\nCordialement,\n{{ store_name }}", "de": "Hallo {{ customer_name }},\n\nSie haben {{ points }} Bonuspunkte von {{ program_name }} erhalten.\n\nDiese Punkte sind bereits auf Ihrem Konto.\n\nMit freundlichen Grüßen,\n{{ store_name }}", "lb": "Moien {{ customer_name }},\n\nDir hutt {{ points }} Bonuspunkten vu {{ program_name }} kritt.\n\nDës Punkten sinn schonn op Ärem Konto.\n\nLéif Gréiss,\n{{ store_name }}", }, }, { "code": "loyalty_points_expiring", "name": { "en": "Points Expiring Warning", "fr": "Avertissement expiration points", "de": "Punkteverfall-Warnung", "lb": "Punkten-Oflaaf-Warnung", }, "description": "Sent 14 days before loyalty points expire", "category": EmailCategory.SYSTEM.value, "variables": ["customer_name", "program_name", "points", "days_remaining", "expiration_date", "store_name"], "subject": { "en": "Your {{ points }} points expire in {{ days_remaining }} days", "fr": "Vos {{ points }} points expirent dans {{ days_remaining }} jours", "de": "Ihre {{ points }} Punkte verfallen in {{ days_remaining }} Tagen", "lb": "Är {{ points }} Punkten verfalen an {{ days_remaining }} Deeg", }, "body_html": { "en": _wrap("#f59e0b", "#d97706", "Points Expiring Soon", _POINTS_EXPIRING_BODY_EN), "fr": _wrap("#f59e0b", "#d97706", "Points bientôt expirés", _POINTS_EXPIRING_BODY_FR), "de": _wrap("#f59e0b", "#d97706", "Punkte verfallen bald", _POINTS_EXPIRING_BODY_DE), "lb": _wrap("#f59e0b", "#d97706", "Punkten verfalen geschwënn", _POINTS_EXPIRING_BODY_LB), }, "body_text": { "en": "Hi {{ customer_name }},\n\n{{ points }} points in your {{ program_name }} account will expire in {{ days_remaining }} days (on {{ expiration_date }}).\n\nVisit us to use your points!\n\nBest regards,\n{{ store_name }}", "fr": "Bonjour {{ customer_name }},\n\n{{ points }} points de votre compte {{ program_name }} expireront dans {{ days_remaining }} jours (le {{ expiration_date }}).\n\nRendez-nous visite !\n\nCordialement,\n{{ store_name }}", "de": "Hallo {{ customer_name }},\n\n{{ points }} Punkte auf Ihrem {{ program_name }}-Konto verfallen in {{ days_remaining }} Tagen (am {{ expiration_date }}).\n\nBesuchen Sie uns!\n\nMit freundlichen Grüßen,\n{{ store_name }}", "lb": "Moien {{ customer_name }},\n\n{{ points }} Punkten op Ärem {{ program_name }}-Konto verfalen an {{ days_remaining }} Deeg (den {{ expiration_date }}).\n\nBesicht eis!\n\nLéif Gréiss,\n{{ store_name }}", }, }, { "code": "loyalty_points_expired", "name": { "en": "Points Expired", "fr": "Points expirés", "de": "Punkte verfallen", "lb": "Punkten ofgelaf", }, "description": "Sent when loyalty points have expired", "category": EmailCategory.SYSTEM.value, "variables": ["customer_name", "program_name", "expired_points", "store_name"], "subject": { "en": "{{ expired_points }} points have expired", "fr": "{{ expired_points }} points ont expiré", "de": "{{ expired_points }} Punkte sind verfallen", "lb": "{{ expired_points }} Punkten sinn ofgelaf", }, "body_html": { "en": _wrap("#ef4444", "#dc2626", "Points Expired", _POINTS_EXPIRED_BODY_EN), "fr": _wrap("#ef4444", "#dc2626", "Points expirés", _POINTS_EXPIRED_BODY_FR), "de": _wrap("#ef4444", "#dc2626", "Punkte verfallen", _POINTS_EXPIRED_BODY_DE), "lb": _wrap("#ef4444", "#dc2626", "Punkten ofgelaf", _POINTS_EXPIRED_BODY_LB), }, "body_text": { "en": "Hi {{ customer_name }},\n\n{{ expired_points }} points in your {{ program_name }} account have expired.\n\nKeep earning on your next visit!\n\nBest regards,\n{{ store_name }}", "fr": "Bonjour {{ customer_name }},\n\n{{ expired_points }} points de votre compte {{ program_name }} ont expiré.\n\nContinuez à gagner des points !\n\nCordialement,\n{{ store_name }}", "de": "Hallo {{ customer_name }},\n\n{{ expired_points }} Punkte auf Ihrem {{ program_name }}-Konto sind verfallen.\n\nSammeln Sie weiter!\n\nMit freundlichen Grüßen,\n{{ store_name }}", "lb": "Moien {{ customer_name }},\n\n{{ expired_points }} Punkten op Ärem {{ program_name }}-Konto sinn ofgelaf.\n\nSammelt weider!\n\nLéif Gréiss,\n{{ store_name }}", }, }, { "code": "loyalty_reward_ready", "name": { "en": "Reward Ready", "fr": "Récompense disponible", "de": "Prämie bereit", "lb": "Belounung prett", }, "description": "Sent when a customer earns enough stamps for a reward", "category": EmailCategory.MARKETING.value, "variables": ["customer_name", "program_name", "reward_name", "store_name"], "subject": { "en": "You've earned a reward at {{ program_name }}! 🎉", "fr": "Vous avez gagné une récompense chez {{ program_name }} ! 🎉", "de": "Sie haben eine Prämie bei {{ program_name }} verdient! 🎉", "lb": "Dir hutt eng Belounung bei {{ program_name }} verdéngt! 🎉", }, "body_html": { "en": _wrap("#10b981", "#059669", "Reward Earned! 🎉", _REWARD_READY_BODY_EN), "fr": _wrap("#10b981", "#059669", "Récompense gagnée ! 🎉", _REWARD_READY_BODY_FR), "de": _wrap("#10b981", "#059669", "Prämie verdient! 🎉", _REWARD_READY_BODY_DE), "lb": _wrap("#10b981", "#059669", "Belounung verdéngt! 🎉", _REWARD_READY_BODY_LB), }, "body_text": { "en": "Hi {{ customer_name }},\n\nCongratulations! You've earned a reward: {{ reward_name }}\n\nVisit {{ store_name }} to redeem it!\n\nBest regards,\n{{ store_name }}", "fr": "Bonjour {{ customer_name }},\n\nFélicitations ! Vous avez gagné : {{ reward_name }}\n\nRendez-vous chez {{ store_name }} !\n\nCordialement,\n{{ store_name }}", "de": "Hallo {{ customer_name }},\n\nHerzlichen Glückwunsch! Ihre Prämie: {{ reward_name }}\n\nBesuchen Sie {{ store_name }}!\n\nMit freundlichen Grüßen,\n{{ store_name }}", "lb": "Moien {{ customer_name }},\n\nFelicitatiounen! Är Belounung: {{ reward_name }}\n\nBesicht {{ store_name }}!\n\nLéif Gréiss,\n{{ store_name }}", }, }, ] for defn in _defs: for lang in ("en", "fr", "de", "lb"): templates.append({ "code": defn["code"], "language": lang, "name": defn["name"][lang], "description": defn["description"], "category": defn["category"], "variables": json.dumps(defn["variables"]), "required_variables": json.dumps(defn["variables"]), "subject": defn["subject"][lang], "body_html": defn["body_html"][lang], "body_text": defn["body_text"][lang], "is_platform_only": False, }) return templates TEMPLATES = _make_templates() # ============================================================================= # SEED FUNCTION # ============================================================================= def seed_templates(): """Seed loyalty email templates into database (idempotent).""" db = next(get_db()) try: created = 0 updated = 0 for template_data in TEMPLATES: existing = ( db.query(EmailTemplate) .filter( EmailTemplate.code == template_data["code"], EmailTemplate.language == template_data["language"], ) .first() ) if existing: for key, value in template_data.items(): setattr(existing, key, value) updated += 1 print(f" Updated: {template_data['code']} ({template_data['language']})") else: template = EmailTemplate(**template_data) db.add(template) created += 1 print(f" Created: {template_data['code']} ({template_data['language']})") db.commit() print(f"\nLoyalty templates — Created: {created}, Updated: {updated}") except Exception as e: db.rollback() print(f"Error: {e}") raise finally: db.close() if __name__ == "__main__": seed_templates()