diff --git a/backend/account/models.py b/backend/account/models.py
index c4fcc24..2d6fecc 100644
--- a/backend/account/models.py
+++ b/backend/account/models.py
@@ -63,6 +63,7 @@ class CustomUser(SoftDeleteModel, AbstractUser):
email_verification_token = models.CharField(max_length=128, null=True, blank=True, db_index=True)
email_verification_sent_at = models.DateTimeField(null=True, blank=True)
+ newsletter = models.BooleanField(default=True)
#misc
gdpr = models.BooleanField(default=False)
diff --git a/backend/account/views.py b/backend/account/views.py
index 6905591..16a0fef 100644
--- a/backend/account/views.py
+++ b/backend/account/views.py
@@ -405,4 +405,7 @@ class PasswordResetConfirmView(APIView):
user.set_password(serializer.validated_data['password'])
user.save()
return Response({"detail": "Heslo bylo úspěšně změněno."})
- return Response(serializer.errors, status=400)
\ No newline at end of file
+ return Response(serializer.errors, status=400)
+
+
+
diff --git a/backend/advertisement/tasks.py b/backend/advertisement/tasks.py
index d9f6874..a62019d 100644
--- a/backend/advertisement/tasks.py
+++ b/backend/advertisement/tasks.py
@@ -1,9 +1,13 @@
+from venv import create
from account.tasks import send_email_with_context
from configuration.models import SiteConfiguration
from celery import shared_task
from celery.schedules import crontab
+from commerce.models import Product
+import datetime
+
@shared_task
def send_contact_me_email_task(client_email, message_content):
context = {
@@ -18,13 +22,28 @@ def send_contact_me_email_task(client_email, message_content):
)
-def send_newly_added_items_to_store_email_task_last_week(item_id):
+@shared_task
+def send_newly_added_items_to_store_email_task_last_week():
+ last_week_date = datetime.datetime.now() - datetime.timedelta(days=7)
+
+ """
+ __lte -> Less than or equal
+ __gte -> Greater than or equal
+ __lt -> Less than
+ __gt -> Greater than
+ """
+
+ products_of_week = Product.objects.filter(
+ include_in_week_summary_email=True,
+ created_at__gte=last_week_date
+ )
send_email_with_context(
recipients=SiteConfiguration.get_solo().contact_email,
subject="Nový produkt přidán do obchodu",
- template_path="email/new_item_added.html",
+ template_path="email/advertisement/commerce/new_items_added_this_week.html",
context={
- "item": item,
+ "products_of_week": products_of_week,
}
- )
\ No newline at end of file
+ )
+
diff --git a/backend/advertisement/templates/email/advertisement/commerce/new_items_added_this_week.html b/backend/advertisement/templates/email/advertisement/commerce/new_items_added_this_week.html
new file mode 100644
index 0000000..98eea1a
--- /dev/null
+++ b/backend/advertisement/templates/email/advertisement/commerce/new_items_added_this_week.html
@@ -0,0 +1,83 @@
+
+
+
🆕 Nové produkty v obchodě
+Týdenní přehled nově přidaných produktů
+
+
+
📊 Celkem nových produktů: {{ products_of_week|length }}
+
Přehled produktů přidaných za posledních 7 dní
+
+
+{% if products_of_week %}
+ {% for product in products_of_week %}
+
+
{{ product.name }}
+
+ {% if product.price %}
+
+ {{ product.price|floatformat:0 }} {{ product.currency|default:"Kč" }}
+
+ {% endif %}
+
+ {% if product.short_description %}
+
+ {{ product.short_description|truncatewords:20 }}
+
+ {% endif %}
+
+
+ Přidáno: {{ product.created_at|date:"d.m.Y H:i" }}
+
+
+ {% endfor %}
+{% else %}
+
+
🤷♂️ Žádné nové produkty
+
Za posledních 7 dní nebyly přidány žádné nové produkty, které by měly být zahrnuty do týdenního přehledu.
+
+{% endif %}
\ No newline at end of file
diff --git a/backend/advertisement/urls.py b/backend/advertisement/urls.py
index ad96ccc..6160f80 100644
--- a/backend/advertisement/urls.py
+++ b/backend/advertisement/urls.py
@@ -1,7 +1,7 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
-from .views import ContactMePublicView, ContactMeAdminViewSet
+from .views import ContactMePublicView, ContactMeAdminViewSet, trigger_weekly_email
router = DefaultRouter()
router.register(r"contact-messages", ContactMeAdminViewSet, basename="contactme")
@@ -12,4 +12,5 @@ urlpatterns = [
# Admin endpoints
path("", include(router.urls)),
+ path("trigger-weekly-email/", trigger_weekly_email, name="trigger-weekly-email"),
]
diff --git a/backend/advertisement/views.py b/backend/advertisement/views.py
index 2de85f7..ec0c1b5 100644
--- a/backend/advertisement/views.py
+++ b/backend/advertisement/views.py
@@ -3,11 +3,12 @@ from rest_framework.response import Response
from rest_framework import status, viewsets
from rest_framework.permissions import AllowAny, IsAdminUser
from rest_framework.authentication import SessionAuthentication
+from rest_framework.decorators import api_view, permission_classes
from drf_spectacular.utils import extend_schema, extend_schema_view
from .models import ContactMe
from .serializer import ContactMeSerializer
-from .tasks import send_contact_me_email_task
+from .tasks import send_contact_me_email_task, send_newly_added_items_to_store_email_task_last_week
@extend_schema(tags=["advertisement", "public"])
@@ -54,3 +55,32 @@ class ContactMeAdminViewSet(viewsets.ModelViewSet):
serializer_class = ContactMeSerializer
permission_classes = [IsAdminUser]
+
+@extend_schema(
+ tags=["advertisement"],
+ summary="Manually trigger weekly new items email",
+ description="Triggers the weekly email task that sends a summary of newly added products from the last week. Only accessible by admin users.",
+ methods=["POST"]
+)
+@api_view(['POST'])
+@permission_classes([IsAdminUser])
+def trigger_weekly_email(request):
+ """
+ Manually trigger the weekly new items email task.
+ Only accessible by admin users.
+ """
+ try:
+ # Trigger the task asynchronously
+ task = send_newly_added_items_to_store_email_task_last_week.delay()
+
+ return Response({
+ 'success': True,
+ 'message': 'Weekly email task triggered successfully',
+ 'task_id': task.id
+ }, status=status.HTTP_200_OK)
+
+ except Exception as e:
+ return Response({
+ 'success': False,
+ 'message': f'Failed to trigger weekly email task: {str(e)}'
+ }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
diff --git a/backend/commerce/models.py b/backend/commerce/models.py
index 8aad58b..169c437 100644
--- a/backend/commerce/models.py
+++ b/backend/commerce/models.py
@@ -94,6 +94,8 @@ class Product(models.Model):
"Carrier", on_delete=models.SET_NULL, null=True, blank=True, related_name="default_for_products"
)
+ include_in_week_summary_email = models.BooleanField(default=False)
+
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
diff --git a/backend/templates/email/email_verification.html b/backend/templates/email/email_verification.html
new file mode 100644
index 0000000..475cd0d
--- /dev/null
+++ b/backend/templates/email/email_verification.html
@@ -0,0 +1,78 @@
+
+
+
+
✉️ Ověření e-mailové adresy
+
+
+ Vítejte {{ user.first_name|default:user.username }}!
+ Děkujeme za registraci. Pro dokončení vytvoření účtu je nutné
+ ověřit vaši e-mailovou adresu kliknutím na tlačítko níže.
+
+
+
{{ cta_label }}
+
+
+ 🎉 Těšíme se na vás!
+ Po ověření e-mailu budete moci využívat všechny funkce naší platformy
+ a začít nakupovat nebo prodávat.
+
+
+
+ ℹ️ Co dělat, když tlačítko nefunguje?
+ Zkopírujte a vložte následující odkaz do adresního řádku prohlížeče:
+
+ {{ action_url }}
+
+
+
+ Odkaz pro ověření je platný po omezenou dobu.
+ Pokud jste se neregistrovali na našich stránkách, ignorujte tento e-mail.
+
+
\ No newline at end of file
diff --git a/backend/templates/email/password_reset.html b/backend/templates/email/password_reset.html
new file mode 100644
index 0000000..c99236d
--- /dev/null
+++ b/backend/templates/email/password_reset.html
@@ -0,0 +1,75 @@
+
+
+
+
🔐 Obnova hesla
+
+
+ Dobrý den {{ user.first_name|default:user.username }},
+ Obdrželi jsme požadavek na obnovení hesla k vašemu účtu.
+ Klikněte na tlačítko níže pro vytvoření nového hesla.
+
+
+
{{ cta_label }}
+
+
+ ⚠️ Bezpečnostní upozornění:
+ Pokud jste o obnovu hesla nepožádali, ignorujte tento e-mail.
+ Váše heslo zůstane nezměněno.
+
+
+
+ Odkaz pro obnovu hesla je platný pouze po omezenou dobu.
+ Pokud odkaz nefunguje, zkopírujte a vložte následující adresu do prohlížeče:
+
+
+
+ {{ action_url }}
+
+
+
+ Tento odkaz je určen pouze pro vás a nelze ho sdílet s ostatními.
+
+
\ No newline at end of file
diff --git a/backend/vontor_cz/settings.py b/backend/vontor_cz/settings.py
index 233ffb2..54c266b 100644
--- a/backend/vontor_cz/settings.py
+++ b/backend/vontor_cz/settings.py
@@ -19,6 +19,7 @@ from django.db import OperationalError, connections
from datetime import timedelta
import json
+from celery.schedules import crontab
from dotenv import load_dotenv
load_dotenv() # Pouze načte proměnné lokálně, pokud nejsou dostupné
@@ -508,6 +509,14 @@ CELERY_TASK_SERIALIZER = os.getenv("CELERY_TASK_SERIALIZER", "json")
CELERY_RESULT_SERIALIZER = os.getenv("CELERY_RESULT_SERIALIZER", "json")
CELERY_TIMEZONE = os.getenv("CELERY_TIMEZONE", TIME_ZONE)
CELERY_BEAT_SCHEDULER = os.getenv("CELERY_BEAT_SCHEDULER")
+
+# Celery Beat Schedule for periodic tasks
+CELERY_BEAT_SCHEDULE = {
+ 'send-weekly-new-items-email': {
+ 'task': 'advertisement.tasks.send_newly_added_items_to_store_email_task_last_week',
+ 'schedule': crontab(hour=9, minute=0, day_of_week=1), # Every Monday at 9:00 AM
+ },
+}
#-------------------------------------END CELERY 📅------------------------------------