feat: add quick purchase email templates to admin panel

- Register 4 guest purchase template types in admin email templates:
  guest_subscription_delivered, guest_activation_required,
  guest_gift_received, guest_cabinet_credentials
- Add sample contexts with placeholders for preview/test
- Add DB override support to send_guest_notification for all 4 types
This commit is contained in:
Fringg
2026-03-07 04:13:41 +03:00
parent 9217352685
commit 6970340e62
2 changed files with 122 additions and 2 deletions
@@ -298,6 +298,70 @@ TEMPLATE_TYPES = [
},
'context_vars': ['username', 'reset_url', 'expire_hours'],
},
{
'type': 'guest_subscription_delivered',
'label': {
'ru': 'Быстрая покупка: подписка доставлена',
'en': 'Quick Purchase: Subscription Delivered',
'zh': '快捷购买:订阅已交付',
'ua': 'Швидка покупка: підписка доставлена',
},
'description': {
'ru': 'Письмо покупателю после успешной оплаты через лендинг',
'en': 'Email to buyer after successful landing page payment',
'zh': '通过落地页成功付款后发送给买家的邮件',
'ua': 'Лист покупцю після успішної оплати через лендінг',
},
'context_vars': ['tariff_name', 'period_days', 'cabinet_url'],
},
{
'type': 'guest_activation_required',
'label': {
'ru': 'Быстрая покупка: требуется активация',
'en': 'Quick Purchase: Activation Required',
'zh': '快捷购买:需要激活',
'ua': 'Швидка покупка: потрібна активація',
},
'description': {
'ru': 'Письмо когда у покупателя уже есть активная подписка',
'en': 'Email when buyer already has an active subscription',
'zh': '买家已有活跃订阅时发送的邮件',
'ua': 'Лист коли у покупця вже є активна підписка',
},
'context_vars': ['tariff_name', 'period_days', 'success_page_url', 'gift_message'],
},
{
'type': 'guest_gift_received',
'label': {
'ru': 'Быстрая покупка: подарок получен',
'en': 'Quick Purchase: Gift Received',
'zh': '快捷购买:收到礼物',
'ua': 'Швидка покупка: подарунок отримано',
},
'description': {
'ru': 'Письмо получателю подарочной подписки',
'en': 'Email to gift subscription recipient',
'zh': '发送给礼物订阅接收者的邮件',
'ua': 'Лист отримувачу подарункової підписки',
},
'context_vars': ['tariff_name', 'period_days', 'cabinet_url', 'gift_message', 'cabinet_email', 'cabinet_password'],
},
{
'type': 'guest_cabinet_credentials',
'label': {
'ru': 'Быстрая покупка: данные для входа',
'en': 'Quick Purchase: Login Credentials',
'zh': '快捷购买:登录凭据',
'ua': 'Швидка покупка: дані для входу',
},
'description': {
'ru': 'Письмо с логином и паролем для личного кабинета',
'en': 'Email with login credentials for the cabinet',
'zh': '包含个人中心登录信息的邮件',
'ua': 'Лист з логіном та паролем для особистого кабінету',
},
'context_vars': ['tariff_name', 'period_days', 'cabinet_url', 'cabinet_email', 'cabinet_password'],
},
]
SAMPLE_CONTEXTS: dict[str, dict[str, Any]] = {
@@ -335,6 +399,34 @@ SAMPLE_CONTEXTS: dict[str, dict[str, Any]] = {
'expire_hours': 24,
},
'password_reset': {'username': 'John', 'reset_url': 'https://example.com/reset?token=abc123', 'expire_hours': 1},
'guest_subscription_delivered': {
'tariff_name': 'Premium',
'period_days': 30,
'cabinet_url': 'https://example.com/cabinet',
'is_existing_user': False,
},
'guest_activation_required': {
'tariff_name': 'Premium',
'period_days': 30,
'success_page_url': 'https://example.com/cabinet/buy/success/abc123',
'is_gift': True,
'gift_message': 'Happy birthday!',
},
'guest_gift_received': {
'tariff_name': 'Premium',
'period_days': 30,
'cabinet_url': 'https://example.com/cabinet',
'gift_message': 'Happy birthday!',
'cabinet_email': 'recipient@example.com',
'cabinet_password': 'SecurePass123',
},
'guest_cabinet_credentials': {
'tariff_name': 'Premium',
'period_days': 30,
'cabinet_url': 'https://example.com/cabinet',
'cabinet_email': 'user@example.com',
'cabinet_password': 'SecurePass123',
},
}
AVAILABLE_LANGUAGES = ['ru', 'en', 'zh', 'ua', 'fa']
+30 -2
View File
@@ -469,7 +469,24 @@ async def send_guest_notification(
notification_type = NotificationType.GUEST_SUBSCRIPTION_DELIVERED
templates = EmailNotificationTemplates()
template = templates.get_template(notification_type, language, context)
# Check DB override first, then fall back to hardcoded template
template = None
try:
from app.cabinet.services.email_template_overrides import get_template_override
override = await get_template_override(notification_type.value, language)
if override:
template = {
'subject': override['subject'],
'body_html': templates._get_base_template(override['body_html'], language),
}
except Exception as e:
logger.debug('Failed to check template override', e=e)
if not template:
template = templates.get_template(notification_type, language, context)
if not template:
logger.warning('No email template found for guest notification', notification_type=notification_type.value)
return
@@ -497,7 +514,18 @@ async def send_guest_notification(
# Send separate credentials email for new/upgraded accounts (non-gift self-purchases)
if purchase.cabinet_password and not purchase.is_gift:
cred_template = templates.get_template(NotificationType.GUEST_CABINET_CREDENTIALS, language, context)
cred_template = None
try:
cred_override = await get_template_override(NotificationType.GUEST_CABINET_CREDENTIALS.value, language)
if cred_override:
cred_template = {
'subject': cred_override['subject'],
'body_html': templates._get_base_template(cred_override['body_html'], language),
}
except Exception:
pass
if not cred_template:
cred_template = templates.get_template(NotificationType.GUEST_CABINET_CREDENTIALS, language, context)
if cred_template:
cred_result = await asyncio.to_thread(
email_service.send_email,