from django.http import Http404
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from salesman.checkout.payment import PaymentError, payment_methods_pool
from salesman.conf import app_settings
from salesman.core.utils import get_salesman_model
from .serializers import (
OrderPaySerializer,
OrderRefundSerializer,
OrderStatusSerializer,
)
Order = get_salesman_model('Order')
[docs]class OrderViewSet(viewsets.ReadOnlyModelViewSet):
"""
Orders API endpoint.
"""
serializer_class = app_settings.SALESMAN_ORDER_SERIALIZER
lookup_field = 'ref'
[docs] def get_queryset(self):
queryset = self.optimize_queryset(Order.objects.all())
if self.request.user.is_authenticated:
if self.request.user.is_staff and self.action != 'list':
# Allow access for admin user to all orders except on `list`.
return queryset
return queryset.filter(user=self.request.user.id)
if 'token' in self.request.GET:
# Allow non-authenticated users access to order with token.
return queryset.filter(token=self.request.GET['token'])
return Order.objects.none()
[docs] def optimize_queryset(self, queryset):
"""
Extract fields for pre-fetching from order serializer and apply to queryset.
"""
serializer_class = self.get_serializer_class()
if hasattr(serializer_class, 'Meta'):
fields = getattr(serializer_class.Meta, 'select_related_fields', None)
if fields and (isinstance(fields, list) or isinstance(fields, tuple)):
queryset = queryset.select_related(*fields)
fields = getattr(serializer_class.Meta, 'prefetch_related_fields', None)
if fields and (isinstance(fields, list) or isinstance(fields, tuple)):
queryset = queryset.prefetch_related(*fields)
return queryset
[docs] def get_object(self):
if not hasattr(self, '_object'):
self._object = super().get_object()
return self._object
[docs] def get_serializer_class(self):
if self.action in ['list', 'all']:
return app_settings.SALESMAN_ORDER_SUMMARY_SERIALIZER
return super().get_serializer_class()
[docs] def get_serializer_context(self):
context = super().get_serializer_context()
if self.detail and self.lookup_field in self.kwargs:
context['order'] = self.get_object()
return context
[docs] @method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
[docs] @action(detail=False, methods=['get'])
def last(self, request):
"""
Show last customer order.
"""
order = self.get_queryset().order_by('date_created').last()
if not order:
raise Http404
serializer = self.get_serializer(order)
return Response(serializer.data)
[docs] @action(detail=False, methods=['get'], permission_classes=[IsAdminUser])
def all(self, request):
"""
Show all orders to the admin user.
"""
return self.list(request)
[docs] @action(
detail=True,
methods=['get', 'put'],
serializer_class=OrderStatusSerializer,
permission_classes=[IsAdminUser],
)
def status(self, request, ref):
"""
Change order status. Available only to admin user.
"""
order = self.get_object()
if request.method == 'GET':
serializer = self.get_serializer(order)
return Response(serializer.data)
serializer = self.get_serializer(order, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
[docs] @action(detail=True, methods=['get', 'post'], serializer_class=OrderPaySerializer)
def pay(self, request, ref):
"""
Pay for order.
"""
if request.method == 'GET':
instance = {'payment_methods': payment_methods_pool.get_payments('order')}
serializer = self.get_serializer(instance)
return Response(serializer.data)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
serializer.save()
headers = {'Location': serializer.data['url']}
return Response(serializer.data, headers=headers)
except PaymentError as e:
return Response({'detail': str(e)}, status=status.HTTP_402_PAYMENT_REQUIRED)
[docs] @action(
detail=True,
methods=['post'],
serializer_class=OrderRefundSerializer,
permission_classes=[IsAdminUser],
)
def refund(self, request, ref):
"""
Refund all order payments.
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
if serializer.data['failed']:
return Response(serializer.data, status=status.HTTP_206_PARTIAL_CONTENT)
return Response(serializer.data)