66from django .http import HttpResponse
77from django .utils .translation import gettext_lazy as _
88
9+ from drf_yasg import openapi
10+ from drf_yasg .utils import swagger_auto_schema
911from rest_framework import mixins , pagination , permissions , viewsets
1012from rest_framework .decorators import action
1113from rest_framework .exceptions import ValidationError as DRFValidationError
1416
1517from joanie .core import models
1618from joanie .core .enums import ORDER_STATE_PENDING
19+ from joanie .core .viewsets import (
20+ RequestResponseSerializersViewSetMixin ,
21+ ActionSerializerType ,
22+ )
1723from joanie .payment import get_payment_backend
1824from joanie .payment .models import Invoice
1925
@@ -111,6 +117,12 @@ def get_serializer_context(self):
111117
112118 return context
113119
120+ @swagger_auto_schema (
121+ query_serializer = serializers .ProductRetrieveQuerySerializer ,
122+ )
123+ def retrieve (self , * args , ** kwargs ):
124+ return super ().retrieve (* args , ** kwargs )
125+
114126
115127# pylint: disable=too-many-ancestors
116128class EnrollmentViewSet (
@@ -141,6 +153,7 @@ def perform_create(self, serializer):
141153
142154# pylint: disable=too-many-ancestors
143155class OrderViewSet (
156+ RequestResponseSerializersViewSetMixin ,
144157 mixins .ListModelMixin ,
145158 mixins .RetrieveModelMixin ,
146159 mixins .CreateModelMixin ,
@@ -163,6 +176,12 @@ class OrderViewSet(
163176 pagination_class = Pagination
164177 permission_classes = [permissions .IsAuthenticated ]
165178 serializer_class = serializers .OrderSerializer
179+ action_serializers = {
180+ "create" : {
181+ "request" : serializers .OrderCreateSerializer ,
182+ "response" : serializers .OrderCreateResponseSerializer ,
183+ }
184+ }
166185 filterset_class = filters .OrderViewSetFilter
167186 ordering = ["-created_on" ]
168187
@@ -171,21 +190,22 @@ def get_queryset(self):
171190 user = User .update_or_create_from_request_user (request_user = self .request .user )
172191 return user .orders .all ().select_related ("owner" , "product" , "certificate" )
173192
174- def perform_create (self , serializer ):
193+ def perform_create (self , validated_data ):
175194 """Force the order's "owner" field to the logged-in user."""
176195 owner = User .update_or_create_from_request_user (request_user = self .request .user )
177- serializer . save ( owner = owner )
196+ return models . Order . objects . create ( ** validated_data , owner = owner )
178197
179198 @transaction .atomic
180199 def create (self , request , * args , ** kwargs ):
181200 """Try to create an order and a related payment if the payment is fee."""
182- serializer = self .get_serializer (data = request .data )
201+ serializer = self .get_request_serializer (data = request .data )
183202 if not serializer .is_valid ():
184203 return Response (serializer .errors , status = 400 )
185204
186205 product = serializer .validated_data .get ("product" )
187206 course = serializer .validated_data .get ("course" )
188- billing_address = serializer .initial_data .get ("billing_address" )
207+ billing_address = serializer .validated_data .get ("billing_address" )
208+ credit_card_id = serializer .validated_data .get ("credit_card_id" )
189209
190210 # Populate organization field if it is not set and there is only one
191211 # on the product
@@ -210,7 +230,13 @@ def create(self, request, *args, **kwargs):
210230
211231 # - Validate data then create an order
212232 try :
213- self .perform_create (serializer )
233+ order_validated_data = {** serializer .validated_data }
234+ if billing_address :
235+ order_validated_data .pop ("billing_address" )
236+ if credit_card_id :
237+ order_validated_data .pop ("credit_card_id" )
238+ # FIXME this pop stuff should be done in OrderCreateSerializer.save
239+ order = self .perform_create (order_validated_data )
214240 except (DRFValidationError , IntegrityError ):
215241 return Response (
216242 (
@@ -222,10 +248,7 @@ def create(self, request, *args, **kwargs):
222248
223249 # Once order has been created, if product is not free, create a payment
224250 if product .price .amount > 0 :
225- order = serializer .instance
226251 payment_backend = get_payment_backend ()
227- credit_card_id = serializer .initial_data .get ("credit_card_id" )
228-
229252 # if payment in one click
230253 if credit_card_id :
231254 try :
@@ -245,14 +268,22 @@ def create(self, request, *args, **kwargs):
245268 request = request , order = order , billing_address = billing_address
246269 )
247270
248- # Return the fresh new order with payment_info
249- return Response (
250- {** serializer .data , "payment_info" : payment_info }, status = 201
271+ response_serializer = self .get_response_serializer (
272+ instance = order ,
273+ context = {
274+ "payment_info" : payment_info ,
275+ },
251276 )
277+ return Response (response_serializer .data , status = 201 )
252278
253279 # Else return the fresh new order
254- return Response (serializer .data , status = 201 )
280+ response_serializer = self .get_response_serializer (instance = order )
281+ return Response (response_serializer .data , status = 201 )
255282
283+ @swagger_auto_schema (
284+ request_body = serializers .OrderAbortBodySerializer ,
285+ responses = {204 : serializers .EmptyResponseSerializer },
286+ )
256287 @action (detail = True , methods = ["POST" ])
257288 def abort (self , request , pk = None ): # pylint: disable=no-self-use, invalid-name
258289 """Abort a pending order and the related payment if there is one."""
@@ -277,6 +308,17 @@ def abort(self, request, pk=None): # pylint: disable=no-self-use, invalid-name
277308
278309 return Response (status = 204 )
279310
311+ @swagger_auto_schema (
312+ query_serializer = serializers .OrderInvoiceQuerySerializer ,
313+ responses = {
314+ 200 : openapi .Response (
315+ "File Attachment" , schema = openapi .Schema (type = openapi .TYPE_FILE )
316+ ),
317+ 400 : serializers .ErrorResponseSerializer ,
318+ 404 : serializers .ErrorResponseSerializer ,
319+ },
320+ produces = "application/pdf" ,
321+ )
280322 @action (detail = True , methods = ["GET" ])
281323 def invoice (self , request , pk = None ): # pylint: disable=no-self-use, invalid-name
282324 """
@@ -391,6 +433,16 @@ def get_queryset(self):
391433 user = User .update_or_create_from_request_user (request_user = self .request .user )
392434 return models .Certificate .objects .filter (order__owner = user )
393435
436+ @swagger_auto_schema (
437+ responses = {
438+ 200 : openapi .Response (
439+ "File Attachment" , schema = openapi .Schema (type = openapi .TYPE_FILE )
440+ ),
441+ 404 : serializers .ErrorResponseSerializer ,
442+ 422 : serializers .ErrorResponseSerializer ,
443+ },
444+ produces = "application/pdf" ,
445+ )
394446 @action (detail = True , methods = ["GET" ])
395447 def download (self , request , pk = None ): # pylint: disable=no-self-use, invalid-name
396448 """
0 commit comments