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,27 @@ 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_serializer (
202+ data = request .data ,
203+ context = {
204+ "serializer_type" : ActionSerializerType .REQUEST ,
205+ },
206+ )
183207 if not serializer .is_valid ():
184208 return Response (serializer .errors , status = 400 )
185209
186210 product = serializer .validated_data .get ("product" )
187211 course = serializer .validated_data .get ("course" )
188- billing_address = serializer .initial_data .get ("billing_address" )
212+ billing_address = serializer .validated_data .get ("billing_address" )
213+ credit_card_id = serializer .validated_data .get ("credit_card_id" )
189214
190215 # Populate organization field if it is not set and there is only one
191216 # on the product
@@ -210,7 +235,13 @@ def create(self, request, *args, **kwargs):
210235
211236 # - Validate data then create an order
212237 try :
213- self .perform_create (serializer )
238+ order_validated_data = {** serializer .validated_data }
239+ if billing_address :
240+ order_validated_data .pop ("billing_address" )
241+ if credit_card_id :
242+ order_validated_data .pop ("credit_card_id" )
243+ # FIXME this pop stuff should be done in OrderCreateSerializer.save
244+ order = self .perform_create (order_validated_data )
214245 except (DRFValidationError , IntegrityError ):
215246 return Response (
216247 (
@@ -222,10 +253,7 @@ def create(self, request, *args, **kwargs):
222253
223254 # Once order has been created, if product is not free, create a payment
224255 if product .price .amount > 0 :
225- order = serializer .instance
226256 payment_backend = get_payment_backend ()
227- credit_card_id = serializer .initial_data .get ("credit_card_id" )
228-
229257 # if payment in one click
230258 if credit_card_id :
231259 try :
@@ -245,14 +273,28 @@ def create(self, request, *args, **kwargs):
245273 request = request , order = order , billing_address = billing_address
246274 )
247275
248- # Return the fresh new order with payment_info
249- return Response (
250- {** serializer .data , "payment_info" : payment_info }, status = 201
276+ response_serializer = self .get_serializer (
277+ instance = order ,
278+ context = {
279+ "serializer_type" : ActionSerializerType .RESPONSE ,
280+ "payment_info" : payment_info ,
281+ },
251282 )
283+ return Response (response_serializer .data , status = 201 )
252284
253285 # Else return the fresh new order
254- return Response (serializer .data , status = 201 )
286+ response_serializer = self .get_serializer (
287+ instance = order ,
288+ context = {
289+ "serializer_type" : ActionSerializerType .RESPONSE ,
290+ },
291+ )
292+ return Response (response_serializer .data , status = 201 )
255293
294+ @swagger_auto_schema (
295+ request_body = serializers .OrderAbortBodySerializer ,
296+ responses = {204 : serializers .EmptyResponseSerializer },
297+ )
256298 @action (detail = True , methods = ["POST" ])
257299 def abort (self , request , pk = None ): # pylint: disable=no-self-use, invalid-name
258300 """Abort a pending order and the related payment if there is one."""
@@ -277,6 +319,17 @@ def abort(self, request, pk=None): # pylint: disable=no-self-use, invalid-name
277319
278320 return Response (status = 204 )
279321
322+ @swagger_auto_schema (
323+ query_serializer = serializers .OrderInvoiceQuerySerializer ,
324+ responses = {
325+ 200 : openapi .Response (
326+ "File Attachment" , schema = openapi .Schema (type = openapi .TYPE_FILE )
327+ ),
328+ 400 : serializers .ErrorResponseSerializer ,
329+ 404 : serializers .ErrorResponseSerializer ,
330+ },
331+ produces = "application/pdf" ,
332+ )
280333 @action (detail = True , methods = ["GET" ])
281334 def invoice (self , request , pk = None ): # pylint: disable=no-self-use, invalid-name
282335 """
@@ -391,6 +444,16 @@ def get_queryset(self):
391444 user = User .update_or_create_from_request_user (request_user = self .request .user )
392445 return models .Certificate .objects .filter (order__owner = user )
393446
447+ @swagger_auto_schema (
448+ responses = {
449+ 200 : openapi .Response (
450+ "File Attachment" , schema = openapi .Schema (type = openapi .TYPE_FILE )
451+ ),
452+ 404 : serializers .ErrorResponseSerializer ,
453+ 422 : serializers .ErrorResponseSerializer ,
454+ },
455+ produces = "application/pdf" ,
456+ )
394457 @action (detail = True , methods = ["GET" ])
395458 def download (self , request , pk = None ): # pylint: disable=no-self-use, invalid-name
396459 """
0 commit comments