diff --git a/lemarche/conversations/admin.py b/lemarche/conversations/admin.py index cab8b4f50..f9a88e2cc 100644 --- a/lemarche/conversations/admin.py +++ b/lemarche/conversations/admin.py @@ -3,7 +3,7 @@ from django.urls import reverse from django.utils.html import format_html -from lemarche.conversations.models import Conversation, TemplateTransactional, TemplateTransactionalSendLog +from lemarche.conversations.models import Conversation, EmailGroup, TemplateTransactional, TemplateTransactionalSendLog from lemarche.utils.admin.admin_site import admin_site from lemarche.utils.fields import pretty_print_readonly_jsonfield, pretty_print_readonly_jsonfield_to_table from lemarche.www.conversations.tasks import send_first_email_from_conversation @@ -153,7 +153,7 @@ class TemplateTransactionalAdmin(admin.ModelAdmin): readonly_fields = ["code", "template_transactional_send_log_count_with_link", "created_at", "updated_at"] fieldsets = ( - (None, {"fields": ("name", "code", "description")}), + (None, {"fields": ("name", "code", "description", "group")}), ("Paramètres d'envoi", {"fields": ("mailjet_id", "brevo_id", "source", "is_active")}), ("Stats", {"fields": ("template_transactional_send_log_count_with_link",)}), ("Dates", {"fields": ("created_at", "updated_at")}), @@ -234,3 +234,10 @@ def extra_data_display(self, instance: TemplateTransactionalSendLog = None): return "-" extra_data_display.short_description = TemplateTransactionalSendLog._meta.get_field("extra_data").verbose_name + + +@admin.register(EmailGroup, site=admin_site) +class EmailGroupAdmin(admin.ModelAdmin): + list_display = ["id", "relevant_user_kind", "display_name", "description", "can_be_unsubscribed"] + search_fields = ["id", "display_name"] + readonly_fields = [] diff --git a/lemarche/conversations/migrations/0017_emailgroup_disabledemail_templatetransactional_group_and_more.py b/lemarche/conversations/migrations/0017_emailgroup_disabledemail_templatetransactional_group_and_more.py new file mode 100644 index 000000000..c9c949452 --- /dev/null +++ b/lemarche/conversations/migrations/0017_emailgroup_disabledemail_templatetransactional_group_and_more.py @@ -0,0 +1,126 @@ +# Generated by Django 4.2.15 on 2024-12-11 17:02 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +def create_email_groups(apps, schema_editor): + # Get the model + EmailGroup = apps.get_model("conversations", "EmailGroup") + + # Create email groups + email_groups = [ + { + "id": 1, + "display_name": "Structure(s) intéressée(s)", + "description": "En désactivant cette option, vous ne serez plus averti par email lorsque des fournisseurs s'intéressent à votre besoin, ce qui pourrait vous faire perdre des opportunités de collaboration rapide et efficace.", + "relevant_user_kind": "BUYER", + "can_be_unsubscribed": True, + }, + { + "id": 2, + "display_name": "Communication marketing", + "description": "En désactivant cette option, vous ne recevrez plus par email nos newsletters, enquêtes, invitations à des webinaires et Open Labs, ce qui pourrait vous priver d'informations utiles et de moments d'échange exclusifs.", + "relevant_user_kind": "BUYER", + "can_be_unsubscribed": True, + }, + { + "id": 3, + "display_name": "Opportunités commerciales", + "description": "En désactivant cette option, vous ne recevrez plus par email les demandes de devis et les appels d'offres spécialement adaptés à votre activité, ce qui pourrait vous faire manquer des opportunités importantes pour votre entreprise.", + "relevant_user_kind": "SIAE", + "can_be_unsubscribed": True, + }, + { + "id": 4, + "display_name": "Demandes de mise en relation", + "description": "En désactivant cette option, vous ne recevrez plus par email les demandes de mise en relation de clients intéressés par votre structure, ce qui pourrait vous faire perdre des opportunités précieuses de collaboration et de développement.", + "relevant_user_kind": "SIAE", + "can_be_unsubscribed": True, + }, + { + "id": 5, + "display_name": "Communication marketing", + "description": "En désactivant cette option, vous ne recevrez plus par email nos newsletters, enquêtes, invitations aux webinaires et Open Labs, ce qui pourrait vous faire passer à côté d’informations clés, de ressources utiles et d’événements exclusifs.", + "relevant_user_kind": "SIAE", + "can_be_unsubscribed": True, + }, + ] + + for group in email_groups: + EmailGroup.objects.create(**group) + + +def delete_email_groups(apps, schema_editor): + # Get the model + EmailGroup = apps.get_model("conversations", "EmailGroup") + # Delete all email groups + EmailGroup.objects.all().delete() + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("conversations", "0016_templatetransactionalsendlog"), + ] + + operations = [ + migrations.CreateModel( + name="EmailGroup", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("display_name", models.CharField(blank=True, max_length=255, verbose_name="Nom")), + ("description", models.TextField(blank=True, verbose_name="Description")), + ( + "relevant_user_kind", + models.CharField( + choices=[ + ("SIAE", "Structure"), + ("BUYER", "Acheteur"), + ("PARTNER", "Partenaire"), + ("INDIVIDUAL", "Particulier"), + ], + default="BUYER", + max_length=20, + verbose_name="Type d'utilisateur", + ), + ), + ( + "can_be_unsubscribed", + models.BooleanField(default=False, verbose_name="L'utilisateur peut s'y désincrire"), + ), + ], + ), + migrations.CreateModel( + name="DisabledEmail", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("disabled_at", models.DateTimeField(auto_now_add=True)), + ( + "group", + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="conversations.emailgroup"), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="disabled_emails", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.AddField( + model_name="templatetransactional", + name="group", + field=models.ForeignKey( + null=True, on_delete=django.db.models.deletion.CASCADE, to="conversations.emailgroup" + ), + ), + migrations.AddConstraint( + model_name="disabledemail", + constraint=models.UniqueConstraint(models.F("user"), models.F("group"), name="unique_group_per_user"), + ), + migrations.RunPython(create_email_groups, delete_email_groups), + ] diff --git a/lemarche/conversations/models.py b/lemarche/conversations/models.py index e274393cb..bdd3ca5d3 100644 --- a/lemarche/conversations/models.py +++ b/lemarche/conversations/models.py @@ -13,6 +13,7 @@ from shortuuid import uuid from lemarche.conversations import constants as conversation_constants +from lemarche.users import constants as user_constants from lemarche.utils.apis import api_brevo, api_mailjet from lemarche.utils.data import add_validation_error @@ -200,6 +201,24 @@ def set_validated(self): self.save() +class EmailGroup(models.Model): + display_name = models.CharField(verbose_name="Nom", max_length=255, blank=True) + description = models.TextField(verbose_name="Description", blank=True) + relevant_user_kind = models.CharField( + verbose_name="Type d'utilisateur", + max_length=20, + choices=user_constants.KIND_CHOICES, + default=user_constants.KIND_BUYER, + ) + can_be_unsubscribed = models.BooleanField(verbose_name="L'utilisateur peut s'y désincrire", default=False) + + def __str__(self): + return self.display_name + + def disabled_for_user(self, user): + return DisabledEmail.objects.filter(user=user, group=self).exists() + + class TemplateTransactionalQuerySet(models.QuerySet): def with_stats(self): return self.annotate( @@ -213,6 +232,7 @@ class TemplateTransactional(models.Model): verbose_name="Nom technique", max_length=255, unique=True, db_index=True, blank=True, null=True ) description = models.TextField(verbose_name="Description", blank=True) + group = models.ForeignKey("EmailGroup", on_delete=models.CASCADE, null=True) # email_subject = models.CharField( # verbose_name="E-mail : objet", @@ -363,3 +383,14 @@ class TemplateTransactionalSendLog(models.Model): class Meta: verbose_name = "Template transactionnel: logs d'envois" verbose_name_plural = "Templates transactionnels: logs d'envois" + + +class DisabledEmail(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="disabled_emails") + group = models.ForeignKey("EmailGroup", on_delete=models.CASCADE) + disabled_at = models.DateTimeField(auto_now_add=True) + + class Meta: + constraints = [ + models.UniqueConstraint("user", "group", name="unique_group_per_user"), + ] diff --git a/lemarche/templates/dashboard/disabled_email_edit.html b/lemarche/templates/dashboard/disabled_email_edit.html new file mode 100644 index 000000000..b71a2594e --- /dev/null +++ b/lemarche/templates/dashboard/disabled_email_edit.html @@ -0,0 +1,62 @@ +{% extends "layouts/base.html" %} +{% load static widget_tweaks dsfr_tags process_dict theme_inclusion %} +{% block page_title %} + Notifications{{ block.super }} +{% endblock page_title %} +{% block breadcrumb %} + {% process_dict root_dir=HOME_PAGE_PATH current="Notifications" as breadcrumb_data %} + {% dsfr_breadcrumb breadcrumb_data %} +{% endblock breadcrumb %} +{% block content %} +