Features: 1) Add slug field to BrandFilter with filtering and ordering support; 2) Implement send_promocode_created_email task using a new email template; 3) Create signal to notify users upon promocode creation.

Fixes: None;

Extra: Refactor imports in `signals.py` and `emailing.py`; Add formatting and responsive styles for email template.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-09-09 16:27:04 +03:00
parent 2f4cceaa6a
commit 2f98354f35
4 changed files with 175 additions and 3 deletions

View file

@ -473,12 +473,14 @@ class CategoryFilter(FilterSet):
class BrandFilter(FilterSet): class BrandFilter(FilterSet):
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact") uuid = UUIDFilter(field_name="uuid", lookup_expr="exact")
name = CharFilter(method="filter_name", label=_("Name")) name = CharFilter(method="filter_name", label=_("Name"))
slug = CharFilter(field_name="slug", lookup_expr="exact", label=_("Slug"))
categories = CaseInsensitiveListFilter(field_name="categories__uuid", lookup_expr="exact", label=_("Categories")) categories = CaseInsensitiveListFilter(field_name="categories__uuid", lookup_expr="exact", label=_("Categories"))
order_by = OrderingFilter( order_by = OrderingFilter(
fields=( fields=(
("priority", "priority"), ("priority", "priority"),
("uuid", "uuid"), ("uuid", "uuid"),
("slug", "slug"),
("name", "name"), ("name", "name"),
("?", "random"), ("?", "random"),
) )
@ -486,7 +488,7 @@ class BrandFilter(FilterSet):
class Meta: class Meta:
model = Brand model = Brand
fields = ["uuid", "name"] fields = ["uuid", "name", "slug", "priority"]
def filter_name(self, queryset, _name, value): def filter_name(self, queryset, _name, value):
search_results = process_query(query=value, request=self.request)["brands"] search_results = process_query(query=value, request=self.request)["brands"]

View file

@ -18,7 +18,7 @@ from core.utils import (
generate_human_readable_id, generate_human_readable_id,
resolve_translations_for_elasticsearch, resolve_translations_for_elasticsearch,
) )
from core.utils.emailing import send_order_created_email, send_order_finished_email from core.utils.emailing import send_order_created_email, send_order_finished_email, send_promocode_created_email
from evibes.utils.misc import create_object from evibes.utils.misc import create_object
from vibes_auth.models import User from vibes_auth.models import User
@ -155,3 +155,9 @@ def update_category_name_lang(instance, created, **_kwargs):
pass pass
resolve_translations_for_elasticsearch(instance, "name") resolve_translations_for_elasticsearch(instance, "name")
resolve_translations_for_elasticsearch(instance, "description") resolve_translations_for_elasticsearch(instance, "description")
@receiver(post_save, sender=PromoCode)
def send_promocode_creation_email(instance, created, **_kwargs):
if created:
send_promocode_created_email.delay(instance.uuid)

View file

@ -0,0 +1,122 @@
{% load tz static i18n filters conditions %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% blocktrans %}promocode granted{% endblocktrans %}</title>
<style>
* {
box-sizing: border-box;
}
body, table, td, a {
text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
}
body {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
}
img {
max-width: 100%;
height: auto;
}
.email-container {
max-width: 600px;
margin: 0 auto;
}
.content-cell {
border: 3px solid #000000;
padding: 20px;
font-family: Arial, sans-serif;
}
.header {
background-color: #000000;
padding: 20px;
text-align: center;
}
.header img {
width: 120px;
height: auto;
}
.footer {
background-color: #000000;
padding: 10px;
text-align: center;
font-size: 12px;
color: #888;
}
.order-table {
width: 100%;
margin-top: 20px;
border-collapse: collapse;
}
.order-table th, .order-table td {
border: 1px solid #ddd;
padding: 8px;
}
.order-table th {
background-color: #f4f4f4;
}
@media only screen and (max-width: 600px) {
.email-container {
width: 100% !important;
}
}
</style>
<link rel="icon" href="{% static 'favicon.png' %}" sizes="192x192">
</head>
<body>
<table>
<tr>
<td>
<table class="email-container">
<tr>
<td class="header">
<img src="{% static 'logo.png' %}"
alt="{% blocktrans %}logo{% endblocktrans %}" width="120">
</td>
</tr>
<tr>
<td class="content-cell">
<h2>{% blocktrans %}promocode granted{% endblocktrans %}</h2>
<p>{% blocktrans %}hello {{ user_first_name }},{% endblocktrans %}</p>
<p>{% blocktrans %}Thank you for staying with us! We have granted you with a promocode
for {% endblocktrans %}{% if promocode.discount_type == "amount" %}
{{ promocode.discount_amount }}{{ currency }}{% else %}{{ promocode.discount_percent }}
%{% endif %}</p>
<p>{% blocktrans %}if you have any questions, feel free to contact our support at
{{ contact_email }}.{% endblocktrans %}</p>
<p>{% blocktrans %}best regards,<br>the {{ project_name }} team{% endblocktrans %}</p>
</td>
</tr>
<tr>
<td class="footer">
&copy; {% now "Y" %} {{ project_name }}.
{% blocktrans %}all rights reserved{% endblocktrans %}.
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View file

@ -3,13 +3,14 @@ from datetime import datetime
from celery.app import shared_task from celery.app import shared_task
from celery.utils.log import get_task_logger from celery.utils.log import get_task_logger
from constance import config from constance import config
from django.conf import settings
from django.core import mail from django.core import mail
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import activate from django.utils.translation import activate
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.models import Order, OrderProduct from core.models import Order, OrderProduct, PromoCode
from core.utils.constance import set_email_settings from core.utils.constance import set_email_settings
logger = get_task_logger(__name__) logger = get_task_logger(__name__)
@ -165,3 +166,44 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
send_thank_you_email(shipped_ops) send_thank_you_email(shipped_ops)
return True, str(order.uuid) return True, str(order.uuid)
@shared_task(queue="default")
def send_promocode_created_email(promocode_pk: str) -> tuple[bool, str]:
try:
promocode = PromoCode.objects.get(pk=promocode_pk)
except PromoCode.DoesNotExist:
return False, f"Promocode not found with the given pk: {promocode_pk}"
if not promocode.user:
return True, "The promocode has no user"
activate(promocode.user.language)
set_email_settings()
connection = mail.get_connection()
email = EmailMessage(
_(f"{config.PROJECT_NAME} | promocode granted"),
render_to_string(
template_name="promocode_granted_email.html",
context={
"promocode": promocode,
"user_first_name": "" or promocode.user.first_name,
"project_name": config.PROJECT_NAME,
"contact_email": config.EMAIL_FROM,
"today": datetime.today(),
"currency": settings.CURRENCY_CODE,
},
),
to=[promocode.user.email],
from_email=f"{config.PROJECT_NAME} <{config.EMAIL_FROM}>",
connection=connection,
)
email.content_subtype = "html"
result = email.send()
logger.debug(
"Promocode %s: Tried to send email to %s, resulted with %s", promocode.pk, promocode.user.email, result
)
return True, str(promocode.uuid)