From 74d09c3d81a2f1fa29b734a53877983bc4ed6b9c Mon Sep 17 00:00:00 2001 From: RyanAquino Date: Fri, 24 Dec 2021 21:12:53 +0800 Subject: [PATCH] ZDL-95: Integrate sending of email after order --- .gitignore | 1 - README.md | 5 + orders/models.py | 25 ++ orders/tests/test_orders_api.py | 3 + orders/views.py | 55 ++- requirements.txt | 8 +- static/images/zadala-logo.png | Bin 0 -> 13417 bytes templates/invoice_email_template.html | 560 ++++++++++++++++++++++++++ zadalaAPI/settings.py | 19 +- zadala_config.py | 6 + 10 files changed, 660 insertions(+), 22 deletions(-) create mode 100644 static/images/zadala-logo.png create mode 100644 templates/invoice_email_template.html diff --git a/.gitignore b/.gitignore index 374dc3f..5baf72a 100644 --- a/.gitignore +++ b/.gitignore @@ -145,4 +145,3 @@ dmypy.json .pytype/ # End of https://www.toptal.com/developers/gitignore/api/django -static \ No newline at end of file diff --git a/README.md b/README.md index 5113b40..ceb564b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,11 @@ python manage.py createsuperuser python manage.py runserver ``` +#### Access on browser +``` +http://localhost:8000/api-docs +``` + ### Setup with Docker (Alternative) ``` docker-compose up -d diff --git a/orders/models.py b/orders/models.py index a3c185d..c11a71b 100644 --- a/orders/models.py +++ b/orders/models.py @@ -1,7 +1,15 @@ +from typing import Dict + +from django.conf import settings +from django.core.mail import send_mail from django.db import models from django.db.models.signals import post_delete from django.dispatch import receiver +from django.template.loader import render_to_string from django.utils import timezone +from django.utils.html import strip_tags +from pydantic import EmailStr +from validate_email import validate_email from authentication.models import User from products.models import Product @@ -43,6 +51,23 @@ def get_cart_items(self): total = sum([item.quantity for item in items]) return total + @staticmethod + def send_email_notification( + customer_email: EmailStr, template: str, subject: str, context_data: Dict + ): + if validate_email(email_address=customer_email, check_smtp=False): + html_message = render_to_string(template, context_data) + plain_message = strip_tags(html_message) + + send_mail( + subject, + plain_message, + settings.EMAIL_HOST_USER, + [customer_email, settings.EMAIL_HOST_USER], + fail_silently=False, + html_message=html_message, + ) + class OrderItem(models.Model): product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True) diff --git a/orders/tests/test_orders_api.py b/orders/tests/test_orders_api.py index 529e1fd..8b82b9f 100644 --- a/orders/tests/test_orders_api.py +++ b/orders/tests/test_orders_api.py @@ -1,4 +1,5 @@ from datetime import datetime +from unittest.mock import patch import pytest @@ -149,6 +150,8 @@ def test_update_cart(logged_in_client): @pytest.mark.django_db +@patch("django.core.mail.send_mail", lambda **kwargs: kwargs) +@patch("validate_email.validate_email", lambda z, x: x) def test_process_order(logged_in_client, logged_in_user): """ Test process order diff --git a/orders/views.py b/orders/views.py index d86ee41..d397064 100644 --- a/orders/views.py +++ b/orders/views.py @@ -1,4 +1,5 @@ -import datetime +from datetime import datetime +from uuid import uuid4 from rest_condition import Or from rest_framework import mixins, status, viewsets @@ -105,7 +106,8 @@ def update_cart(self, request): ) def process_order(self, request): request_data = self.get_serializer(data=request.data) - transaction_id = datetime.datetime.now().timestamp() + transaction_id = str(uuid4()) + transaction_timestamp = datetime.now().strftime("%b %d, %Y %H:%M") customer = request.user self.check_object_permissions(self.request, customer) @@ -116,18 +118,37 @@ def process_order(self, request): order.complete = True order.save() - if order.shipping: - shipping = ShippingAddress.objects.create( - customer=customer, - order=order, - address=request_data.validated_data["address"], - city=request_data.validated_data["city"], - state=request_data.validated_data["state"], - zipcode=request_data.validated_data["zipcode"], - ) - shipping.save() - - return Response( - ShippingAddressSerializer(shipping).data, status=status.HTTP_201_CREATED - ) - return Response("Order not found", status=status.HTTP_400_BAD_REQUEST) + if not order.shipping: + return Response("Order not found", status=status.HTTP_400_BAD_REQUEST) + + shipping = ShippingAddress.objects.create( + customer=customer, + order=order, + address=request_data.validated_data["address"], + city=request_data.validated_data["city"], + state=request_data.validated_data["state"], + zipcode=request_data.validated_data["zipcode"], + ) + shipping.save() + + Order.send_email_notification( + customer.email, + "invoice_email_template.html", + f"Order Being Processed: {order.transaction_id}", + { + "user_first_name": customer.first_name, + "user_last_name": customer.last_name, + "shipping_address": shipping.address, + "shipping_city": shipping.city, + "shipping_state": shipping.state, + "shipping_zipcode": shipping.zipcode, + "invoice_code": transaction_id, + "order_items": order.order_items, + "order": order, + "date_ordered": transaction_timestamp, + }, + ) + + return Response( + ShippingAddressSerializer(shipping).data, status=status.HTTP_201_CREATED + ) diff --git a/requirements.txt b/requirements.txt index 1e4cf70..707d0fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ attrs==19.3.0 black==20.8b1 certifi==2020.6.20 chardet==3.0.4 +charset-normalizer==2.0.9 click==7.1.2 colorama==0.4.4 coreapi==2.3.3 @@ -14,12 +15,14 @@ Django==3.0.8 django-cors-headers==3.7.0 djangorestframework==3.11.0 djangorestframework-simplejwt==4.4.0 +dnspython==2.1.0 drf-yasg==1.17.1 factory-boy==3.2.0 Faker==8.8.0 +filelock==3.4.0 flake8==3.8.3 gunicorn==20.0.4 -idna==2.10 +idna==3.0 importlib-metadata==1.7.0 inflection==0.5.1 iniconfig==1.0.1 @@ -37,6 +40,7 @@ Pillow==7.2.0 pluggy==0.13.1 psycopg2-binary==2.8.6 py==1.9.0 +py3-validate-email==1.0.5 pycodestyle==2.6.0 pydantic==1.6.1 pyflakes==2.2.0 @@ -48,7 +52,7 @@ pytest-django==3.9.0 python-dateutil==2.8.1 pytz==2020.1 regex==2020.7.14 -requests==2.24.0 +requests==2.26.0 rest-condition==1.0.3 ruamel.yaml==0.16.12 ruamel.yaml.clib==0.2.2 diff --git a/static/images/zadala-logo.png b/static/images/zadala-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e66a466a9d2e207a82bd3426fb472431051d5c98 GIT binary patch literal 13417 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 literal 0 HcmV?d00001 diff --git a/templates/invoice_email_template.html b/templates/invoice_email_template.html new file mode 100644 index 0000000..faee9f5 --- /dev/null +++ b/templates/invoice_email_template.html @@ -0,0 +1,560 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ + + + + +
+ Logo image + +
+ +
+ +
+
+
+ + +
+
+
+ + + + + + + +
+ +
+

Hello {{ user_first_name }} {{ user_last_name }}!

+
+ +
+ +
+
+
+ + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ +
+

Shipping Address

+
+ +
+ + + + + + + +
+ +
+

{{ shipping_address }}, {{ shipping_city }}

+

{{ shipping_state }}, Philippines {{ shipping_zipcode }}

+
+ +
+ +
+
+
+ + +
+
+
+ + + + + + + +
+ +
+

Invoice Code

+
+ +
+ + + + + + + +
+ +
+

#{{ invoice_code }}

+
+ +
+ +
+
+
+ + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ +
+

Purchased Items

+
+ +
+ + + + + + + +
+ + + + + + + +
+   +
+ +
+ +
+
+
+ + +
+
+
+ + +{% for order_item in order_items %} +
+
+
+ + + +
+
+
+ + + + + + + +
+ +
+

{{ order_item.product.name }}

+

Quantity : {{ order_item.quantity }} x ₱{{ order_item.product.price }}

+
+ +
+ +
+
+
+ + +
+
+
+ + + + + + + +
+ +
+

₱{{ order_item.total }}

+
+ +
+ +
+
+
+ + +
+
+
+ + +{% endfor %} + +
+
+
+ + + +
+
+
+ + + + + + + +
+ + + + + + + +
+   +
+ +
+ +
+
+
+ + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ +
+

Grand Total

+
+ +
+ +
+
+
+ + +
+
+
+ + + + + + + +
+ +
+

₱{{ order.get_cart_total }}

+
+ +
+ +
+
+
+ + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ +
+

We received your order on {{ date_ordered }} and you’ll be paying for this via Cash on delivery. We wish you enjoy shopping with us and hope to see you again real soon!

+

 

+

zadala.herokuapp.com

+
+ +
+ +
+
+
+ + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ +
+
You received this email because you signed up for Zadala Inc.
+
 
+
+ +
+ +
+
+
+ + +
+
+
+ + + +
+ + + + + diff --git a/zadalaAPI/settings.py b/zadalaAPI/settings.py index b551436..d7d9ae2 100644 --- a/zadalaAPI/settings.py +++ b/zadalaAPI/settings.py @@ -12,7 +12,14 @@ import datetime import os -from zadala_config import ZADALA_SECRET_KEY, database +from zadala_config import ( + EMAIL_HOST_PASSWORD, + EMAIL_HOST_PORT, + EMAIL_HOST_PROVIDER, + EMAIL_HOST_USER, + ZADALA_SECRET_KEY, + database, +) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -68,11 +75,12 @@ STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" ROOT_URLCONF = "zadalaAPI.urls" +TEMPLATE_ROOT = os.path.join(BASE_DIR, "templates") TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], + "DIRS": [TEMPLATE_ROOT], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -181,3 +189,10 @@ STATIC_URL = "/static/" MEDIA_ROOT = os.path.join(BASE_DIR, "static/images") STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") + +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST = os.environ.get("EMAIL_HOST_PROVIDER", EMAIL_HOST_PROVIDER) +EMAIL_PORT = os.environ.get("EMAIL_HOST_PORT", EMAIL_HOST_PORT) +EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER", EMAIL_HOST_USER) +EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", EMAIL_HOST_PASSWORD) +EMAIL_USE_TLS = True diff --git a/zadala_config.py b/zadala_config.py index 6aca6b7..7b30fc6 100644 --- a/zadala_config.py +++ b/zadala_config.py @@ -8,3 +8,9 @@ } ZADALA_SECRET_KEY = "my-secret" + + +EMAIL_HOST_PROVIDER = "smtp.gmail.com" +EMAIL_HOST_PORT = 587 +EMAIL_HOST_USER = "gmail email" +EMAIL_HOST_PASSWORD = "gmail password"