maling system is now enabled for orders

pull/2/head
mtyton 2023-06-22 23:44:22 +02:00
rodzic 1326b13948
commit e800d44e23
10 zmienionych plików z 202 dodań i 62 usunięć

Wyświetl plik

@ -199,4 +199,4 @@ EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', '')
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", '')
EMAIL_PORT = os.environ.get('EMAIL_PORT', 587)
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'mtyton@tepewu.pl')
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'artel-sklep@tepewu.pl')

Wyświetl plik

@ -1,3 +1,75 @@
from django.contrib import admin
from django.forms import fields
# Register your models here.
from wagtail.contrib.modeladmin.options import (
ModelAdmin,
ModelAdminGroup,
modeladmin_register
)
from mailings import models
class MailTemplateAdmin(ModelAdmin):
model = models.MailTemplate
menu_label = "Mail templates"
menu_icon = 'mail'
menu_order = 100
add_to_settings_menu = False
exclude_from_explorer = False
list_display = (
"template_name",
)
search_fields = (
"template_name",
)
list_filter = (
"template_name",
)
form_fields = (
"template_name",
"template",
)
class OutgoingMailAdmin(ModelAdmin):
model = models.OutgoingEmail
menu_label = "Outgoing mails"
menu_icon = 'mail'
menu_order = 100
add_to_settings_menu = False
exclude_from_explorer = False
list_display = (
"subject",
"to",
"sent",
)
search_fields = (
"subject",
"to",
)
list_filter = (
"subject",
"sender",
"recipient",
"template__template_name",
"sent",
)
readonly_fields = (
"subject",
"sender",
"recipient",
"sent"
)
class MailingGroup(ModelAdminGroup):
menu_label = "Mailings"
menu_icon = 'mail'
menu_order = 200
items = (
MailTemplateAdmin,
OutgoingMailAdmin
)
modeladmin_register(MailingGroup)

Wyświetl plik

@ -1,4 +1,4 @@
# Generated by Django 4.1.9 on 2023-06-21 18:32
# Generated by Django 4.1.9 on 2023-06-22 14:09
from django.db import migrations, models
import django.db.models.deletion
@ -16,13 +16,13 @@ class Migration(migrations.Migration):
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("template_name", models.CharField(max_length=255, unique=True)),
("template", models.FileField(upload_to="mail_templates")),
("subject", models.CharField(max_length=255)),
],
),
migrations.CreateModel(
name="OutgoingEmail",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("subject", models.CharField(max_length=255)),
("sender", models.EmailField(max_length=254)),
("recipient", models.EmailField(max_length=254)),
("sent", models.BooleanField(default=False)),

Wyświetl plik

@ -22,15 +22,16 @@ def send_mail(
to: list[str],
attachments: list[Attachment],
subject: str,
body: str,
content: str,
sender_email: str = settings.DEFAULT_FROM_EMAIL
):
message = EmailMessage(
subject=subject,
body=body,
body=content,
from_email=sender_email,
to=to
)
message.content_subtype = 'html'
for attachment in attachments:
message.attach(attachment.name, attachment.content, attachment.contenttype)
return bool(message.send())
@ -42,7 +43,6 @@ class MailTemplate(models.Model):
template = models.FileField(
upload_to="mail_templates"
)
subject = models.CharField(max_length=255)
def delete(self, *args, **kwargs):
# delete file
@ -66,17 +66,20 @@ class MailTemplate(models.Model):
class OutgoingEmailManager(models.Manager):
def send(
self, template_name: str,
self, template_name: str, subject: str,
recipient: str, context: dict | Context,
sender:str, attachments: list[Attachment] = None
):
template = MailTemplate.objects.get(template_name=template_name)
outgoing_email = self.create(template=template, recipient=recipient)
outgoing_email = self.create(
template=template, recipient=recipient, subject=subject,
sender=sender
)
attachments = attachments or []
# send email
sent = send_mail(
to=[recipient], sender_email=sender,
subject=template.subject, content=template.load_and_process_template(context),
subject=subject, content=template.load_and_process_template(context),
attachments=attachments
)
outgoing_email.sent = sent
@ -85,6 +88,7 @@ class OutgoingEmailManager(models.Manager):
class OutgoingEmail(models.Model):
subject = models.CharField(max_length=255)
template = models.ForeignKey(MailTemplate, on_delete=models.CASCADE)
sender = models.EmailField()
recipient = models.EmailField()

Wyświetl plik

@ -0,0 +1,10 @@
from factory.django import DjangoModelFactory
from factory import Faker
class MailTemplateFactory(DjangoModelFactory):
class Meta:
model = "mailings.MailTemplate"
template_name = Faker("name")
template = Faker("file_name", extension="html")

Wyświetl plik

@ -1,5 +1,6 @@
from django.test import TestCase
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core import mail
from mailings.models import (
MailTemplate,
@ -15,8 +16,7 @@ class TestMailTemplate(TestCase):
template_name="test_template",
template=SimpleUploadedFile(
"test_template.html", b"<html>{{test_var}}</html>"
),
subject="Test subject",
)
)
def test_load_and_process_template_success(self):
@ -43,22 +43,24 @@ class TestOutgoingEmail(TestCase):
template_name="test_template",
template=SimpleUploadedFile(
"test_template.html", b"<html>{{test_var}}</html>"
),
subject="Test subject",
)
)
def test_send_success(self):
mail = OutgoingEmail.objects.send(
email = OutgoingEmail.objects.send(
template_name="test_template",
recipient="test@stardust.io", context={},
sender="sklep-test@stardust.io"
sender="sklep-test@stardust.io",
subject="Test subject"
)
self.assertEqual(mail.sent, True)
# TODO outbox
self.assertEqual(email.sent, True)
self.assertEqual(mail.outbox[0].subject, "Test subject")
def test_send_missing_template_failure(self):
with self.assertRaises(MailTemplate.DoesNotExist):
OutgoingEmail.objects.send(
template_name="missing_template",
recipient="", sender="", context={}\
recipient="", sender="", context={},
subject="Test subject"
)
self.assertEqual(len(mail.outbox), 0)

Wyświetl plik

@ -0,0 +1,16 @@
# Generated by Django 4.1.9 on 2023-06-22 16:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("store", "0005_order_order_number"),
]
operations = [
migrations.RemoveField(
model_name="orderdocument",
name="sent",
),
]

Wyświetl plik

@ -31,6 +31,10 @@ from store.utils import (
notify_user_about_order,
notify_manufacturer_about_order
)
from mailings.models import (
OutgoingEmail,
Attachment
)
class PersonalData(models.Model):
@ -233,6 +237,44 @@ class OrderManager(models.Manager):
year = datetime.datetime.now().year
return f"{author.id}/{number_of_prev_orders:06}/{year}"
def _send_notifications(
self, order: models.Model, author: ProductAuthor,
customer_data: dict[str, Any], docs: list[models.Model]
):
# for user
attachments = [
Attachment(
content=doc.generate_document({"customer_data": customer_data}),
contenttype="application/pdf",
name=f"{doc.template.doc_type}_{order.order_number}.pdf"
) for doc in docs
]
mail_subject = f"Wygenerowano umowę numer {order.order_number} z dnia {order.created_at.strftime('%d.%m.%Y')}"
user_mail = OutgoingEmail.objects.send(
recipient=customer_data["email"],
subject=mail_subject,
context = {
"docs": docs,
"order_number": order.order_number,
"customer_email": customer_data["email"],
}, sender=settings.DEFAULT_FROM_EMAIL,
template_name="order_created_user",
attachments=attachments
)
# for author
author_mail = OutgoingEmail.objects.send(
recipient=author.email,
subject=mail_subject,
context = {
"docs": docs,
"order_number": order.order_number,
"manufacturer_email": author.email,
}, sender=settings.DEFAULT_FROM_EMAIL,
template_name="order_created_author",
attachments=attachments
)
return user_mail is not None and author_mail is not None
def create_from_cart(
self, cart_items: list[dict[str, str|dict]],
payment_method: models.Model| None,
@ -242,8 +284,9 @@ class OrderManager(models.Manager):
orders_pks = []
payment_method = payment_method or PaymentMethod.objects.first()
agreement_template = DocumentTemplate.objects.get(doc_type=DocumentTypeChoices.AGREEMENT)
receipt_template = DocumentTemplate.objects.get(doc_type=DocumentTypeChoices.RECEIPT)
doc_templates = DocumentTemplate.objects.filter(
doc_type__in=[DocumentTypeChoices.AGREEMENT, DocumentTypeChoices.RECEIPT]
)
for item in cart_items:
author = item["author"]
@ -255,39 +298,18 @@ class OrderManager(models.Manager):
)
OrderProduct.objects.create_from_cart(author_products, order)
orders_pks.append(order.pk)
agreement = OrderDocument.objects.create(
order=order,
template=agreement_template
)
receipt = OrderDocument.objects.create(
order=order,
template=receipt_template
)
extra_document_kwargs = {
"customer_data": customer_data
}
default_kwargs ={
"docs": [
agreement.generate_document(extra_document_kwargs),
receipt.generate_document(extra_document_kwargs)
],
"order_number": order.order_number
}
user_kwargs = {
"customer_email": customer_data["email"],
}
user_kwargs.update(default_kwargs)
user_notified = notify_user_about_order(**user_kwargs)
manufacturer_kwargs = {
"manufacturer_email": author.email,
}
manufacturer_kwargs.update(default_kwargs)
manufacturer_notified = notify_manufacturer_about_order(**manufacturer_kwargs)
sent = user_notified and manufacturer_notified
agreement.sent = sent
receipt.sent = sent
agreement.save()
receipt.save()
docs = []
for template in doc_templates:
doc = OrderDocument.objects.create(
order=order, template=template
)
docs.append(doc)
sent = self._send_notifications(order, author, customer_data, docs)
if not sent:
# TODO - store data temporarily
raise Exception("Error while sending emails")
return Order.objects.filter(pk__in=orders_pks)
@ -346,7 +368,6 @@ class DocumentTemplate(models.Model):
class OrderDocument(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="documents")
template = models.ForeignKey(DocumentTemplate, on_delete=models.CASCADE)
sent = models.BooleanField(default=False)
def get_document_context(self):
_context = {

Wyświetl plik

@ -1,9 +1,12 @@
from unittest.mock import patch
from django.test import TestCase
from django.urls import reverse
from django.core import mail
from store.tests import factories
from store import models as store_models
from mailings.tests.factories import MailTemplateFactory
# TODO - this is fine for now, but we'll want to use factoryboy for this:
@ -72,8 +75,11 @@ class OrderTestCase(TestCase):
self.payment_method = factories.PaymentMethodFactory()
factories.DocumentTemplateFactory()
factories.DocumentTemplateFactory(doc_type="receipt")
MailTemplateFactory(template_name="order_created_user")
MailTemplateFactory(template_name="order_created_author")
def test_create_from_cart_success_single_author(self):
@patch("mailings.models.MailTemplate.load_and_process_template", return_value="test")
def test_create_from_cart_success_single_author(self, mocked_load):
product = factories.ProductFactory(template__author=self.author, price=100)
cart_items = [{
"author": self.author,
@ -86,10 +92,13 @@ class OrderTestCase(TestCase):
)
self.assertEqual(orders.count(), 1)
self.assertEqual(len(mail.outbox), 2)
self.assertEqual(mail.outbox[0].subject, f"Zamówienie {orders[0].order_number}")
self.assertEqual(
mail.outbox[0].subject,
f"Wygenerowano umowę numer {orders[0].order_number} z dnia {orders[0].created_at.strftime('%d.%m.%Y')}"
)
def test_create_from_cart_success_multpile_authors(self):
@patch("mailings.models.MailTemplate.load_and_process_template", return_value="test")
def test_create_from_cart_success_multpile_authors(self, mocked_load):
product = factories.ProductFactory(template__author=self.second_author, price=100)
cart_items = [
{
@ -107,5 +116,11 @@ class OrderTestCase(TestCase):
)
self.assertEqual(orders.count(), 2)
self.assertEqual(len(mail.outbox), 4)
self.assertEqual(mail.outbox[0].subject, f"Zamówienie {orders[0].order_number}")
self.assertEqual(mail.outbox[2].subject, f"Zamówienie {orders[1].order_number}")
self.assertEqual(
mail.outbox[0].subject,
f"Wygenerowano umowę numer {orders[0].order_number} z dnia {orders[0].created_at.strftime('%d.%m.%Y')}"
)
self.assertEqual(
mail.outbox[2].subject,
f"Wygenerowano umowę numer {orders[1].order_number} z dnia {orders[1].created_at.strftime('%d.%m.%Y')}"
)