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 0000000..e66a466 Binary files /dev/null and b/static/images/zadala-logo.png differ 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 @@ + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% for order_item in order_items %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
|
+