Skip to content

Commit

Permalink
Adds new templates and email sending for user actions
Browse files Browse the repository at this point in the history
  • Loading branch information
alemangui committed Aug 14, 2024
1 parent 40118f1 commit 9767f29
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 47 deletions.
64 changes: 59 additions & 5 deletions api/tests/test_company.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from unittest import mock

from django.test.utils import override_settings

from rest_framework import status

from data.factories import (
Expand Down Expand Up @@ -242,10 +246,17 @@ def setUp(self):
self.recipient_email = "[email protected]"
self.payload = dict(recipient_email=self.recipient_email, roles=["DeclarantRole"])

def test_add_collaborator_without_account_but_invitation_already_sent_ko(self):
# L'invité n'existe pas en base, mais une invitation non traitée a déjà été envoyée.
@override_settings(ANYMAIL={"SENDINBLUE_API_KEY": "fake-api-key"})
@override_settings(CONTACT_EMAIL="[email protected]")
@override_settings(SECURE=True)
@override_settings(HOSTNAME="hostname")
@mock.patch("config.email.send_sib_template")
def test_add_collaborator_without_account_but_invitation_already_sent_ko(self, mocked_brevo):
self.login(self.adder)
CollaborationInvitationFactory(recipient_email=self.recipient_email, company=self.company)
mocked_brevo.reset_mock()

# L'invité n'existe pas en base, mais une invitation non traitée a déjà été envoyée.
response = self.post(self.url(pk=self.company.pk), self.payload)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand All @@ -255,21 +266,52 @@ def test_add_collaborator_without_account_but_invitation_already_sent_ko(self):
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_add_collaborator_without_account_ok(self):
mocked_brevo.assert_not_called()

@override_settings(ANYMAIL={"SENDINBLUE_API_KEY": "fake-api-key"})
@override_settings(CONTACT_EMAIL="[email protected]")
@override_settings(SECURE=True)
@override_settings(HOSTNAME="hostname")
@mock.patch("config.email.send_sib_template")
def test_add_collaborator_without_account_ok(self, mocked_brevo):
# L'invité n'existe pas en base, et aucune invitation n'a été envoyée
self.login(self.adder)
response = self.post(self.url(pk=self.company.pk), self.payload)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn("message", response.data)

def test_add_collaborator_already_a_collaborator_ko(self):
template_number = 16
mocked_brevo.assert_called_once_with(
template_number,
{
"COMPANY_NAME": self.company.social_name,
"SENDER_NAME": self.adder.get_full_name(),
"SIGNUP_LINK": f"https://hostname/inscription?email={self.recipient_email}",
},
self.recipient_email,
self.recipient_email,
)

@override_settings(ANYMAIL={"SENDINBLUE_API_KEY": "fake-api-key"})
@override_settings(CONTACT_EMAIL="[email protected]")
@override_settings(SECURE=True)
@override_settings(HOSTNAME="hostname")
@mock.patch("config.email.send_sib_template")
def test_add_collaborator_already_a_collaborator_ko(self, mocked_brevo):
# L'invité existe en base et fait déjà partie des collaborateurs de l'entreprise.
self.login(self.adder)
DeclarantRoleFactory(user=UserFactory(email=self.recipient_email), company=self.company)
response = self.post(self.url(pk=self.company.pk), self.payload)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_add_collaborator_with_account_ok(self):
mocked_brevo.assert_not_called()

@override_settings(ANYMAIL={"SENDINBLUE_API_KEY": "fake-api-key"})
@override_settings(CONTACT_EMAIL="[email protected]")
@override_settings(SECURE=True)
@override_settings(HOSTNAME="hostname")
@mock.patch("config.email.send_sib_template")
def test_add_collaborator_with_account_ok(self, mocked_brevo):
# L'invité existe en base mais n'est pas encore collaborateur de l'entreprise.
self.login(self.adder)
recipient = UserFactory()
Expand All @@ -280,6 +322,18 @@ def test_add_collaborator_with_account_ok(self):
self.assertIn("message", response.data)
self.assertIn(recipient, self.company.collaborators)

template_number = 18
mocked_brevo.assert_called_once_with(
template_number,
{
"SENDER_NAME": self.adder.get_full_name(),
"COMPANY_NAME": self.company.social_name,
"DASHBOARD_LINK": f"https://hostname/tableau-de-bord?company={self.company.id}",
},
recipient.email,
recipient.get_full_name(),
)

def test_add_collaborator_not_logged_ko(self):
response = self.post(self.url(pk=self.company.pk), self.payload)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
Expand Down
33 changes: 32 additions & 1 deletion api/tests/test_solicitation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.test.utils import override_settings

from rest_framework import status
from rest_framework.test import APITestCase

from data.factories import (
CollaborationInvitationFactory,
Expand All @@ -18,7 +19,8 @@
class TestListCollaborationInvitations(ProjectAPITestCase):
viewname = "list_collaboration_invitation"

def setUp(self):
@mock.patch("config.email.send_sib_template")
def setUp(self, _):
self.user = UserFactory()
self.company_1 = CompanyFactory()
self.company_2 = CompanyFactory()
Expand Down Expand Up @@ -55,6 +57,35 @@ def test_get_collaboration_invitations_unauthenticated(self):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)


class TestCollaborationInvitationEmail(APITestCase):
@override_settings(ANYMAIL={"SENDINBLUE_API_KEY": "fake-api-key"})
@override_settings(CONTACT_EMAIL="[email protected]")
@override_settings(SECURE=True)
@override_settings(HOSTNAME="hostname")
@mock.patch("config.email.send_sib_template")
def test_account_created_sends_email(self, mocked_brevo):
email = "[email protected]"
company = CompanyFactory()
sender = SupervisorRoleFactory(user=UserFactory(), company=company)

invitation = CollaborationInvitationFactory(recipient_email=email, company=company, sender=sender.user)
mocked_brevo.reset_mock()
recipient = UserFactory(email=email)
invitation.account_created(processor=recipient)

template_number = 17
mocked_brevo.assert_called_once_with(
template_number,
{
"COMPANY_NAME": company.social_name,
"NEW_COLLABORATOR": recipient.get_full_name(),
"MEMBERS_LINK": f"https://hostname/gestion-des-collaborateurs/{company.id}",
},
sender.user.email,
sender.user.get_full_name(),
)


class TestListCompanyAccessClaims(ProjectAPITestCase):
viewname = "list_company_access_claim"

Expand Down
43 changes: 25 additions & 18 deletions api/views/solicitation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.db import transaction
from django.shortcuts import get_object_or_404

Expand All @@ -11,13 +11,16 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from api.utils.urls import get_base_url
from config import email
from data.models import CollaborationInvitation, Company, CompanyAccessClaim

from ..exception_handling import ProjectAPIException
from ..permissions import IsSolicitationRecipient, IsSupervisor
from ..serializers import AddNewCollaboratorSerializer, CollaborationInvitationSerializer, CompanyAccessClaimSerializer

User = get_user_model()
logger = logging.getLogger(__name__)


class CollaborationInvitationListView(ListAPIView):
Expand Down Expand Up @@ -79,9 +82,7 @@ def post(self, request, pk: int, *args, **kwargs):
company=company, recipient_email=recipient_email, processed_at__isnull=True
).exists():
raise ProjectAPIException(
field_errors={
"recipient_email": "Une invitation a déjà été envoyée à cette adresse e-mail (peut-être par quelqu'un d'autre)."
}
field_errors={"recipient_email": "Une invitation a déjà été envoyée à cette adresse e-mail."}
)
# Cas B : l'invité n'existe pas en base, et aucune invitation n'a été envoyée
CollaborationInvitation.objects.create(
Expand All @@ -98,16 +99,22 @@ def post(self, request, pk: int, *args, **kwargs):
else:
for role_name in roles:
role_class = apps.get_model("data", role_name)
role_class.objects.get_or_create(
company=company, user=recipient
) # le get évite une potentielle erreur

send_mail(
subject=f"[Compl'Alim] {sender.name} vous a ajouté en tant que collaborateur.",
message=f"{sender.name} vous a ajouté en tant que collaborateur de l'entreprise {company.social_name}.",
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[recipient.email],
)
return Response(
{"message": f"{recipient.name} a été ajouté à vos collaborateurs. Un e-mail lui a été envoyé."}
)
role_class.objects.get_or_create(company=company, user=recipient)

try:
brevo_template_id = 18
email.send_sib_template(
brevo_template_id,
{
"SENDER_NAME": sender.get_full_name(),
"COMPANY_NAME": company.social_name,
"DASHBOARD_LINK": f"{get_base_url()}tableau-de-bord?company={company.id}",
},
recipient.email,
recipient.get_full_name(),
)
except Exception as e:
logger.error(f"Email not sent on AddNewCollaboratorView for recipient {recipient.id}")
logger.exception(e)

return Response({"message": f"{recipient.name} a été ajouté à vos collaborateurs."})
78 changes: 55 additions & 23 deletions data/models/solicitation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import logging
from urllib.parse import urljoin

from django.apps import apps
from django.conf import settings
Expand Down Expand Up @@ -109,16 +108,29 @@ def description(self):

def create_hook(self):
recipients = User.objects.filter(is_staff=True)
self.recipients.set(recipients)
send_mail(
subject="[Compl'Alim] Nouvelle demande de gestion d'une entreprise",
message=f"{self.description} {self.personal_message_for_mail}",
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=self.recipients.values_list("email", flat=True),
)
self.recipients.set(recipients) # TODO: Je ne pense pas que le self.recipients sert à quelque chose
brevo_template_id = 15
for recipient in recipients:
try:
email.send_sib_template(
brevo_template_id,
{
"REQUESTER_NAME": self.sender.get_full_name(),
"COMPANY_NAME": self.company.social_name,
"COMPANY_ID": self.company.id,
"ADMIN_LINK": f"{get_base_url()}admin/",
},
recipient.email,
recipient.get_full_name(),
)
except Exception as e:
logger.error(f"Email not sent on SupervisionClaim creation with id {self.id}")
logger.exception(e)

@processable_action
def accept(self, processor):
# TODO : cette action n'est jamais appelé de nulle part. Lors qu'elle le sera il faudra
# créer un template Brevo
self.company.supervisors.add(self.sender)
send_mail(
subject="[Compl'Alim] Votre demande de gestion a été acceptée",
Expand All @@ -129,6 +141,8 @@ def accept(self, processor):

@processable_action
def refuse(self, processor):
# TODO : cette action n'est jamais appelé de nulle part. Lors qu'elle le sera il faudra
# créer un template Brevo
send_mail(
subject="[Compl'Alim] Votre demande de gestion a été refusée",
message=f"L'équipe Compl'Alim a refusé que vous deveniez gestionnaire de {self.company.social_name}. N'hésitez pas à nous contacter pour en savoir plus.",
Expand Down Expand Up @@ -172,7 +186,7 @@ def create_hook(self):
{
"REQUESTER_NAME": self.sender.get_full_name(),
"COMPANY_NAME": self.company.social_name,
"REQUEST_LINK": f"{'https' if settings.SECURE else 'http'}://{settings.HOSTNAME}/gestion-des-collaborateurs/{self.company.id}",
"REQUEST_LINK": f"{get_base_url()}gestion-des-collaborateurs/{self.company.id}",
},
recipient.email,
recipient.get_full_name(),
Expand All @@ -195,7 +209,7 @@ def accept(self, processor):
{
"REQUESTER_NAME": self.sender.get_full_name(),
"COMPANY_NAME": self.company.social_name,
"DASHBOARD_LINK": f"{'https' if settings.SECURE else 'http'}://{settings.HOSTNAME}/tableau-de-bord?company={self.company.id}",
"DASHBOARD_LINK": f"{get_base_url()}tableau-de-bord?company={self.company.id}",
},
self.sender.email,
self.sender.get_full_name(),
Expand Down Expand Up @@ -246,23 +260,41 @@ def description(self):
return f"{self.sender.name} vous invite à créer un compte Compl'Alim et rejoindre l'entreprise {self.company.social_name}."

def create_hook(self):
create_account_page_url = urljoin(get_base_url(), "inscription") + f"?email={self.recipient_email}"
send_mail(
subject="[Compl'Alim] Invitation à créer votre compte",
message=f"{self.description} {self.personal_message_for_mail} Veuillez vous rendre sur <a href='{create_account_page_url}'>la plateforme Compl'Alim</a> pour créer votre compte.",
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[self.recipient_email],
)
brevo_template_id = 16
try:
email.send_sib_template(
brevo_template_id,
{
"COMPANY_NAME": self.company.social_name,
"SENDER_NAME": self.sender.name,
"SIGNUP_LINK": f"{get_base_url()}inscription?email={self.recipient_email}",
},
self.recipient_email,
self.recipient_email,
)
except Exception as e:
logger.error(f"Email not sent on CollaborationInvitation creation with id {self.id}")
logger.exception(e)

@processable_action
def account_created(self, processor):
"""Action déclenchable par l'inscription de l'utilisateur"""
for role_classname in self.roles:
role_class = apps.get_model("data", role_classname)
role_class.objects.get_or_create(company=self.company, user=processor)
send_mail(
subject=f"[Compl'Alim] {processor.name} vous a rejoint en tant que collaborateur.",
message=f"Suite à votre invitation, {processor.name} est devenu colloborateur de votre entreprise {self.company.social_name}.",
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[self.sender.email],
)

try:
brevo_template_id = 17
email.send_sib_template(
brevo_template_id,
{
"COMPANY_NAME": self.company.social_name,
"NEW_COLLABORATOR": processor.get_full_name(),
"MEMBERS_LINK": f"{get_base_url()}gestion-des-collaborateurs/{self.company.id}",
},
self.sender.email,
self.sender.get_full_name(),
)
except Exception as e:
logger.error(f"Email not sent on CollaborationInvitation account_created with id {self.id}")
logger.exception(e)

0 comments on commit 9767f29

Please sign in to comment.