Added document generation, configured email sending

feature/product_models_refactor
mtyton 2023-06-08 01:09:51 +02:00
rodzic 55c20c7193
commit d2b3cf486c
14 zmienionych plików z 252 dodań i 47 usunięć

Wyświetl plik

@ -22,6 +22,7 @@ RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-r
libjpeg62-turbo-dev \
zlib1g-dev \
libwebp-dev \
wkhtmltopdf \
&& rm -rf /var/lib/apt/lists/*
# Install the application server.

Wyświetl plik

@ -19,6 +19,7 @@ RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-r
libjpeg62-turbo-dev \
zlib1g-dev \
libwebp-dev \
wkhtmltopdf \
&& rm -rf /var/lib/apt/lists/*
# Install the project requirements.

Wyświetl plik

@ -170,10 +170,18 @@ WAGTAILSEARCH_BACKENDS = {
# Base URL to use when referring to full URLs within the Wagtail admin backend -
# e.g. in notification emails. Don't include '/admin' or a trailing slash
WAGTAILADMIN_BASE_URL = "http://example.com"
WAGTAILADMIN_BASE_URL = "https://artel.tepewu.pl"
# STORE SETTINGS
PRODUCTS_PER_PAGE = 6
# CART settings
CART_SESSION_ID = 'cart'
# EMAIL settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST', 'smtp.gmail.com')
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)
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'mtyton@tepewu.pl')

Wyświetl plik

@ -24,6 +24,3 @@
<hr>
<a href={% url 'cart' %} alt="Koszyk" > Koszyk </a>
</div>

Wyświetl plik

@ -6,3 +6,5 @@ dj-database-url<=2.0.0
djangorestframework==3.14.0
phonenumbers==8.13.13
django-phonenumber-field==7.1.0
factory-boy==3.2.1
pdfkit==1.0.0

Wyświetl plik

@ -0,0 +1,41 @@
# Generated by Django 4.1.9 on 2023-06-04 09:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("store", "0004_customerdata_order_orderproduct"),
]
operations = [
migrations.CreateModel(
name="DocumentTemplate",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("name", models.CharField(max_length=255)),
("file", models.FileField(upload_to="documents")),
(
"doc_type",
models.CharField(choices=[("agreement", "Agreement"), ("receipt", "Receipt")], max_length=255),
),
],
),
migrations.CreateModel(
name="OrderDocument",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
(
"order",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="documents", to="store.order"
),
),
(
"template",
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="store.documenttemplate"),
),
],
),
]

Wyświetl plik

@ -1,3 +1,4 @@
import pdfkit
from django.db import models
from django.core.paginator import (
Paginator,
@ -5,6 +6,10 @@ from django.core.paginator import (
)
from django.conf import settings
from django.core.validators import MinValueValidator
from django.template import (
Template,
Context
)
from modelcluster.models import ClusterableModel
from modelcluster.fields import ParentalKey
@ -17,6 +22,10 @@ from wagtail import fields as wagtail_fields
from taggit.managers import TaggableManager
from phonenumber_field.modelfields import PhoneNumberField
from store.utils import (
send_mail
)
class ProductAuthor(models.Model):
name = models.CharField(max_length=255)
@ -194,8 +203,76 @@ class OrderProduct(models.Model):
objects = OrderProductManager()
class OrderManager(models.Manager):
def create_from_cart(self, cart, customer_data):
order = self.create(customer=customer_data)
OrderProduct.objects.create_from_cart(cart, order)
# create proper documents
agreement_template = DocumentTemplate.objects.filter(
doc_type=DocumentTypeChoices.AGREEMENT
).order_by("-created_at").first()
receipt_template = DocumentTemplate.objects.filter(
doc_type=DocumentTypeChoices.RECEIPT
).order_by("-created_at").first()
agreement = OrderDocument.objects.create(
order=order,
template=agreement_template
)
receipt = OrderDocument.objects.create(
order=order,
template=receipt_template
)
send_mail(agreement)
send_mail(receipt)
return order
class Order(models.Model):
customer = models.ForeignKey(CustomerData, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
sent = models.BooleanField(default=False)
objects = OrderManager()
@property
def order_number(self) -> str:
return f"{self.id:06}/{self.created_at.year}"
class DocumentTypeChoices(models.TextChoices):
AGREEMENT = "agreement"
RECEIPT = "receipt"
class DocumentTemplate(models.Model):
name = models.CharField(max_length=255)
file = models.FileField(upload_to="documents")
doc_type = models.CharField(max_length=255, choices=DocumentTypeChoices.choices)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
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 = {
"order": self.order,
"customer": self.order.customer,
"products": self.order.products.all(),
}
return Context(_context)
@property
def document(self):
with open(self.template.file.path, "rb") as f:
content = f.read()
template = Template(content)
context = self.get_document_context()
content = template.render(context)
return pdfkit.from_string(content, False)

Wyświetl plik

@ -1,36 +0,0 @@
from django.test import TestCase
from django.urls import reverse
from store.models import ProductAuthor, ProductCategory, ProductTemplate, Product
# TODO - this is fine for now, but we'll want to use factoryboy for this:
# https://factoryboy.readthedocs.io/en/stable/
# TODO - test have to rewritten - I'll do it tommorow
class CartTestCase(TestCase):
def setUp(self):
self.productid = 1
self.author = ProductAuthor.objects.create(name='Test Author')
self.category = ProductCategory.objects.create(name='Test Category')
self.template = ProductTemplate.objects.create(category=self.category,
author=self.author,
title='Test title',
code='Test code',
description='Test description'
)
self.product = Product.objects.create(template=self.template,
price=10.99)
self.cart_url = reverse('view_cart')
def test_add_to_cart(self):
response = self.client.post(reverse('add_to_cart',
args=[self.productid]))
self.assertEqual(response.status_code, 302)
def test_remove_from_cart(self):
response = self.client.post(reverse('remove_from_cart',
args=[self.productid]))
self.assertEqual(response.status_code, 302)
def test_view_cart(self):
response = self.client.get(self.cart_url)
self.assertEqual(response.status_code, 200)

Wyświetl plik

@ -0,0 +1,41 @@
from factory import (
Faker,
SubFactory
)
from factory.django import (
FileField,
DjangoModelFactory
)
class CustomerDataFactory(DjangoModelFactory):
class Meta:
model = 'store.CustomerData'
name = Faker('name')
surname = Faker('name')
email = Faker('email')
phone = Faker('phone_number')
street = Faker('street_address')
city = Faker('city')
zip_code = Faker('postcode')
country = Faker('country')
class OrderFactory(DjangoModelFactory):
class Meta:
model = 'store.Order'
customer = SubFactory(CustomerDataFactory)
created_at = Faker('date_time')
updated_at = Faker('date_time')
sent = Faker('boolean')
class DocumentTemplateFactory(DjangoModelFactory):
class Meta:
model = 'store.DocumentTemplate'
name = Faker('name')
file = FileField(filename="doc.odt")
doc_type = "AGREEMENT"

Wyświetl plik

@ -0,0 +1,41 @@
from django.test import TestCase
from django.urls import reverse
from store.tests import factories
from store import models as store_models
# TODO - this is fine for now, but we'll want to use factoryboy for this:
# https://factoryboy.readthedocs.io/en/stable/
# TODO - test have to rewritten - I'll do it tommorow
class OrderDocumentTestCase(TestCase):
def setUp(self):
super().setUp()
self.order = factories.OrderFactory()
self.document_template = factories.DocumentTemplateFactory(file__data="test")
def test_generate_document_success(self):
order_doc = store_models.OrderDocument.objects.create(
order=self.order,
template=self.document_template
)
document = order_doc.document
self.assertIsInstance(document, bytes)
def test_get_document_context_success(self):
order_doc = store_models.OrderDocument.objects.create(
order=self.order,
template=self.document_template
)
context = order_doc.get_document_context()
self.assertIsInstance(context, store_models.Context)
self.assertEqual(context["order"].id, self.order.id)
self.assertEqual(context["customer"].id, self.order.customer.id)
self.assertEqual(context["products"].count(), 0)
def test_send_order_document_mail_success(self):
...
def test_send_order_document_mail_failure_wrong_email(self):
...

Wyświetl plik

@ -12,4 +12,5 @@ urlpatterns = [
path('cart/', store_views.CartView.as_view(), name='cart'),
path("order/", store_views.OrderView.as_view(), name="order"),
path("order/confirm/", store_views.OrderConfirmView.as_view(), name="order-confirm"),
path("send-mail/", store_views.SendMailView.as_view(), name="send-mail"),
] + router.urls

Wyświetl plik

@ -0,0 +1,18 @@
from django.core.mail import EmailMessage
from django.conf import settings
# TODO - add celery task for sending not sent earlier
def send_mail(order_doc):
order = order_doc.order
message = EmailMessage(
subject=f"Zamówienie {order.order_number}",
body="Dokumenty dla Twojego zamówienia",
from_email=settings.DEFAULT_FROM_EMAIL,
to=[order.customer.email]
)
message.attach(f"{order.order_number}.pdf", order_doc.document, "application/pdf")
sent = bool(message.send())
order_doc.sent = sent
order_doc.save()
return sent

Wyświetl plik

@ -20,7 +20,9 @@ from store.forms import CustomerDataForm
from store.models import (
CustomerData,
Order,
OrderProduct
OrderProduct,
OrderDocument,
DocumentTemplate
)
@ -133,12 +135,23 @@ class OrderConfirmView(View):
def post(self, request):
customer_data = CustomerData.objects.get(id=self.request.session["customer_data_id"])
cart = SessionCart(self.request)
order = Order.objects.create(
customer=customer_data,
order = Order.objects.create_from_cart(
cart, customer_data
)
OrderProduct.objects.create_from_cart(order, cart)
cart.clear()
self.request.session.pop("customer_data_id")
# TODO - to be tested
# TODO - messages
return HttpResponseRedirect(reverse("cart"))
return HttpResponseRedirect(reverse("cart"))
class SendMailView(View):
def get(self, request):
from django.core import mail
from django.http import HttpResponse
from django.conf import settings
r = mail.send_mail(
subject=f"Test",
message="Dokumenty dla Twojego zamówienia",
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=["mateusz.tyton99@gmail.com"]
)
return HttpResponse(f"Mail sent: {r}")