From 3afc8a34f730123fa532e03a21fface9eef09f12 Mon Sep 17 00:00:00 2001 From: Mohamad Liyaghi Date: Thu, 8 Aug 2024 13:34:25 +0330 Subject: [PATCH 1/3] feat(vendor-orders): list view --- .../tests/test_views/test_vendor/__init__.py | 0 .../tests/test_views/test_vendor/test_list.py | 21 +++++++++++ apps/orders/urls.py | 5 ++- apps/orders/views/vendor.py | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 apps/orders/tests/test_views/test_vendor/__init__.py create mode 100644 apps/orders/tests/test_views/test_vendor/test_list.py create mode 100644 apps/orders/views/vendor.py diff --git a/apps/orders/tests/test_views/test_vendor/__init__.py b/apps/orders/tests/test_views/test_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/orders/tests/test_views/test_vendor/test_list.py b/apps/orders/tests/test_views/test_vendor/test_list.py new file mode 100644 index 0000000..5b13287 --- /dev/null +++ b/apps/orders/tests/test_views/test_vendor/test_list.py @@ -0,0 +1,21 @@ +import pytest +from django.urls import reverse +from rest_framework import status + + +@pytest.mark.django_db +class TestOrderListView: + @pytest.fixture(autouse=True) + def setup(self, api_client, user): + self.url = reverse("orders:vendor-list") + self.client = api_client + self.user = user + + def test_get_unauthorized_fails(self): + response = self.client.get(self.url) + assert response.status_code == status.HTTP_403_FORBIDDEN + + def test_get_by_user_succeeds(self): + self.client.force_authenticate(self.user) + response = self.client.get(self.url) + assert response.status_code == status.HTTP_200_OK diff --git a/apps/orders/urls.py b/apps/orders/urls.py index 73df67e..c7bfcc1 100644 --- a/apps/orders/urls.py +++ b/apps/orders/urls.py @@ -1,9 +1,12 @@ from django.urls import path, include from orders.views.customer import OrderListCreateView, OrderPayView, OrderCancelView +from orders.views.vendor import VendorOrderListView app_name = "orders" -VENDOR_URLS = [] +VENDOR_URLS = [ + path("", VendorOrderListView.as_view(), name="vendor-list"), +] CUSTOMER_URLS = [ path("", OrderListCreateView.as_view(), name="customer-list-create"), path("/pay/", OrderPayView.as_view(), name="customer-pay"), diff --git a/apps/orders/views/vendor.py b/apps/orders/views/vendor.py new file mode 100644 index 0000000..39c3123 --- /dev/null +++ b/apps/orders/views/vendor.py @@ -0,0 +1,36 @@ +from rest_framework.generics import ListAPIView +from rest_framework.permissions import IsAuthenticated +from drf_spectacular.utils import extend_schema_view, OpenApiResponse, extend_schema +from restaurants.models import Restaurant +from orders.models import Order +from orders.enums import OrderStatus +from orders.serializers import OrderSerializer + + +@extend_schema_view( + get=extend_schema( + summary="List Of Customer Orders", + description="List of all orders placed by the customer", + responses={ + 200: OrderSerializer(many=True), + 403: OpenApiResponse(description="Unauthorized"), + }, + tags=["Vendor Orders"], + ), +) +class VendorOrderListView(ListAPIView): + permission_classes = (IsAuthenticated,) + serializer_class = OrderSerializer + + def get_queryset(self): + restaurant = Restaurant.objects.filter(owner=self.request.user, is_soft_deleted=False) + + if not restaurant.exists(): + return Order.objects.none() + + return ( + Order.objects.select_related("restaurant", "user") + .prefetch_related("items", "items__product") + .filter(restaurant__in=restaurant, status=OrderStatus.PENDING_PAYMENT) + .order_by("-created_at") + ) From f174a896140e90482307a190d9523db637588af1 Mon Sep 17 00:00:00 2001 From: Mohamad Liyaghi Date: Thu, 8 Aug 2024 13:38:22 +0330 Subject: [PATCH 2/3] feat(vendor-orders): ship view --- .../tests/test_views/test_vendor/test_ship.py | 41 +++++++++++++++++++ apps/orders/urls.py | 7 +++- apps/orders/views/vendor.py | 35 ++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 apps/orders/tests/test_views/test_vendor/test_ship.py diff --git a/apps/orders/tests/test_views/test_vendor/test_ship.py b/apps/orders/tests/test_views/test_vendor/test_ship.py new file mode 100644 index 0000000..50f17b2 --- /dev/null +++ b/apps/orders/tests/test_views/test_vendor/test_ship.py @@ -0,0 +1,41 @@ +import pytest +from django.urls import reverse +from rest_framework import status +from orders.enums import OrderStatus + + +@pytest.mark.django_db +class TestOrderShipView: + @pytest.fixture(autouse=True) + def setup_method(self, user, api_client, processing_order): + self.url = reverse("orders:vendor-shipped", kwargs={"uuid": processing_order.uuid}) + self.client = api_client + self.user = processing_order.restaurant.owner + self.order = processing_order + self.data = {} + + def test_ship_unauthorized_fails(self): + response = self.client.post(self.url, self.data) + assert response.status_code == status.HTTP_403_FORBIDDEN + + def test_ship_by_another_user_fails(self, another_user): + self.client.force_authenticate(another_user) + response = self.client.post(self.url, self.data) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_ship_for_paid_order_fails(self, shipped_order): + self.client.force_authenticate(self.user) + response = self.client.post( + reverse("orders:vendor-shipped", kwargs={"uuid": shipped_order.uuid}), + self.data, + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_update_with_valid_data_succeeds(self): + self.client.force_authenticate(self.user) + response = self.client.post(self.url, self.data) + assert response.status_code == status.HTTP_200_OK + self.order.refresh_from_db() + assert self.order.status == OrderStatus.SHIPPED + self.order.status = OrderStatus.PROCESSING + self.order.save() diff --git a/apps/orders/urls.py b/apps/orders/urls.py index c7bfcc1..93afb4f 100644 --- a/apps/orders/urls.py +++ b/apps/orders/urls.py @@ -1,11 +1,16 @@ from django.urls import path, include from orders.views.customer import OrderListCreateView, OrderPayView, OrderCancelView -from orders.views.vendor import VendorOrderListView +from orders.views.vendor import VendorOrderListView, VendorOrderSetShippedView app_name = "orders" VENDOR_URLS = [ path("", VendorOrderListView.as_view(), name="vendor-list"), + path( + "/shipped/", + VendorOrderSetShippedView.as_view(), + name="vendor-shipped", + ), ] CUSTOMER_URLS = [ path("", OrderListCreateView.as_view(), name="customer-list-create"), diff --git a/apps/orders/views/vendor.py b/apps/orders/views/vendor.py index 39c3123..3ac23c2 100644 --- a/apps/orders/views/vendor.py +++ b/apps/orders/views/vendor.py @@ -1,5 +1,9 @@ +from django.shortcuts import get_object_or_404 from rest_framework.generics import ListAPIView from rest_framework.permissions import IsAuthenticated +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status from drf_spectacular.utils import extend_schema_view, OpenApiResponse, extend_schema from restaurants.models import Restaurant from orders.models import Order @@ -34,3 +38,34 @@ def get_queryset(self): .filter(restaurant__in=restaurant, status=OrderStatus.PENDING_PAYMENT) .order_by("-created_at") ) + + +@extend_schema_view( + post=extend_schema( + summary="Pay for an order", + description="Pay for an order", + responses={ + 200: OrderSerializer(), + 400: OpenApiResponse(description="Bad Request"), + 403: OpenApiResponse(description="Unauthorized"), + }, + tags=["Customer Orders"], + ), +) +class VendorOrderSetShippedView(APIView): + permission_classes = (IsAuthenticated,) + + def get_object(self): + return get_object_or_404( + Order, + uuid=self.kwargs["uuid"], + restaurant__owner=self.request.user, + status=OrderStatus.PROCESSING, + ) + + def post(self, request, *args, **kwargs): + order = self.get_object() + + order.status = OrderStatus.SHIPPED + order.save() + return Response({"message": "Order has been shipped"}, status=status.HTTP_200_OK) From 40ce0ea9d3e206d666d4d787a85867e5752e8d12 Mon Sep 17 00:00:00 2001 From: Mohamad Liyaghi Date: Thu, 8 Aug 2024 13:42:43 +0330 Subject: [PATCH 3/3] feat(vendor-orders): set delivered view --- .../test_customer/test_set_delievered.py | 44 +++++++++++++++++++ apps/orders/urls.py | 12 ++++- apps/orders/views/customer.py | 32 ++++++++++++++ apps/orders/views/vendor.py | 5 ++- 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 apps/orders/tests/test_views/test_customer/test_set_delievered.py diff --git a/apps/orders/tests/test_views/test_customer/test_set_delievered.py b/apps/orders/tests/test_views/test_customer/test_set_delievered.py new file mode 100644 index 0000000..244fadd --- /dev/null +++ b/apps/orders/tests/test_views/test_customer/test_set_delievered.py @@ -0,0 +1,44 @@ +import pytest +from django.urls import reverse +from rest_framework import status +from orders.enums import OrderStatus + + +@pytest.mark.django_db +class TestOrderDeliverView: + @pytest.fixture(autouse=True) + def setup_method(self, user, api_client, shipped_order): + self.url = reverse("orders:customer-delivered", kwargs={"uuid": shipped_order.uuid}) + self.client = api_client + self.user = user + self.order = shipped_order + self.data = {} + + def test_set_delivered_unauthorized_fails(self): + response = self.client.post(self.url, self.data) + assert response.status_code == status.HTTP_403_FORBIDDEN + + def test_set_delivered_by_another_user_fails(self, another_user): + self.client.force_authenticate(another_user) + response = self.client.post(self.url, self.data) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_set_delivered_for_paid_order_fails(self, processing_order): + self.client.force_authenticate(self.user) + response = self.client.post( + reverse( + "orders:customer-delivered", + kwargs={"uuid": processing_order.uuid}, + ), + self.data, + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_update_with_valid_data_succeeds(self): + self.client.force_authenticate(self.user) + response = self.client.post(self.url, self.data) + assert response.status_code == status.HTTP_200_OK + self.order.refresh_from_db() + assert self.order.status == OrderStatus.DELIVERED + self.order.status = OrderStatus.SHIPPED + self.order.save() diff --git a/apps/orders/urls.py b/apps/orders/urls.py index 93afb4f..720cf54 100644 --- a/apps/orders/urls.py +++ b/apps/orders/urls.py @@ -1,5 +1,10 @@ from django.urls import path, include -from orders.views.customer import OrderListCreateView, OrderPayView, OrderCancelView +from orders.views.customer import ( + OrderListCreateView, + OrderPayView, + OrderCancelView, + OrderDeliveredView, +) from orders.views.vendor import VendorOrderListView, VendorOrderSetShippedView app_name = "orders" @@ -16,6 +21,11 @@ path("", OrderListCreateView.as_view(), name="customer-list-create"), path("/pay/", OrderPayView.as_view(), name="customer-pay"), path("/cancel/", OrderCancelView.as_view(), name="customer-cancel"), + path( + "/delivered/", + OrderDeliveredView.as_view(), + name="customer-delivered", + ), ] urlpatterns = [ diff --git a/apps/orders/views/customer.py b/apps/orders/views/customer.py index ffcd945..0fbc278 100644 --- a/apps/orders/views/customer.py +++ b/apps/orders/views/customer.py @@ -84,6 +84,37 @@ def post(self, request, *args, **kwargs): return Response({"error": "Insufficient balance"}, status=status.HTTP_400_BAD_REQUEST) +@extend_schema_view( + post=extend_schema( + summary="Set As Delivered", + description="Set an order as delivered", + responses={ + 200: OrderSerializer(), + 400: OpenApiResponse(description="Bad Request"), + 403: OpenApiResponse(description="Unauthorized"), + 404: OpenApiResponse(description="Not Found"), + }, + tags=["Customer Orders"], + ), +) +class OrderDeliveredView(APIView): + permission_classes = (IsAuthenticated,) + + def get_object(self): + return get_object_or_404( + Order, + uuid=self.kwargs["uuid"], + user=self.request.user, + status=OrderStatus.SHIPPED, + ) + + def post(self, request, *args, **kwargs): + order = self.get_object() + order.status = OrderStatus.DELIVERED + order.save() + return Response({"message": "Order is being processed"}, status=status.HTTP_200_OK) + + @extend_schema_view( delete=extend_schema( summary="Cancel an order", @@ -92,6 +123,7 @@ def post(self, request, *args, **kwargs): 204: OpenApiResponse(description="Cancelled"), 400: OpenApiResponse(description="Bad Request"), 403: OpenApiResponse(description="Unauthorized"), + 404: OpenApiResponse(description="Not Found"), }, tags=["Customer Orders"], ), diff --git a/apps/orders/views/vendor.py b/apps/orders/views/vendor.py index 3ac23c2..7dcce73 100644 --- a/apps/orders/views/vendor.py +++ b/apps/orders/views/vendor.py @@ -42,12 +42,13 @@ def get_queryset(self): @extend_schema_view( post=extend_schema( - summary="Pay for an order", - description="Pay for an order", + summary="Set Order As Shipped", + description="Set the order as shipped", responses={ 200: OrderSerializer(), 400: OpenApiResponse(description="Bad Request"), 403: OpenApiResponse(description="Unauthorized"), + 404: OpenApiResponse(description="Not Found"), }, tags=["Customer Orders"], ),