From c7980a347eab283978ce6017212d948456887144 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 4 May 2022 15:06:02 +1000 Subject: [PATCH] Adds ability to add custom serializer context information to the OPTIONS endpoint - Will be useful for constructing prettier forms with information about the endpoint --- InvenTree/InvenTree/metadata.py | 21 +++++++++++++++ InvenTree/order/api.py | 47 ++++++++++++++++++++++----------- InvenTree/order/models.py | 1 + InvenTree/order/serializers.py | 29 ++++++++++++++++++++ 4 files changed, 83 insertions(+), 15 deletions(-) diff --git a/InvenTree/InvenTree/metadata.py b/InvenTree/InvenTree/metadata.py index e0f8a23322..792714e439 100644 --- a/InvenTree/InvenTree/metadata.py +++ b/InvenTree/InvenTree/metadata.py @@ -37,6 +37,25 @@ class InvenTreeMetadata(SimpleMetadata): metadata = super().determine_metadata(request, view) + """ + Custom context information to pass through to the OPTIONS endpoint, + if the "context=True" is supplied to the OPTIONS requst + + Serializer class can supply either: + + - get_context_data() (method) + - CONTEXT_DATA (dict) + """ + + context = {} + + if hasattr(self.serializer, 'get_context_data'): + context = self.serializer.get_context_data() + elif hasattr(self.erializer, 'CONTEXT_DATA'): + context = self.serializer.CONTEXT_DATA + + metadata['context'] = context + user = request.user if user is None: @@ -99,6 +118,8 @@ class InvenTreeMetadata(SimpleMetadata): to any fields whose Meta.model specifies a default value """ + self.serializer = serializer + serializer_info = super().get_serializer_info(serializer) model_class = None diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index a54c370121..12ceb600fc 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -286,7 +286,37 @@ class PurchaseOrderDetail(generics.RetrieveUpdateDestroyAPIView): return queryset -class PurchaseOrderReceive(generics.CreateAPIView): +class PurchaseOrderContextMixin: + + def get_serializer_context(self): + """ Add the PurchaseOrder object to the serializer context """ + + context = super().get_serializer_context() + + # Pass the purchase order through to the serializer for validation + try: + context['order'] = models.PurchaseOrder.objects.get(pk=self.kwargs.get('pk', None)) + except: + pass + + context['request'] = self.request + + return context + + +class PurchaseOrderCancel(PurchaseOrderContextMixin, generics.CreateAPIView): + """ + API endpoint to 'cancel' a purchase order. + + The purchase order must be in a state which can be cancelled + """ + + queryset = models.PurchaseOrderLineItem.objects.all() + + serializer_class = serializers.PurchaseOrderCancelSerializer + + +class PurchaseOrderReceive(PurchaseOrderContextMixin, generics.CreateAPIView): """ API endpoint to receive stock items against a purchase order. @@ -303,20 +333,6 @@ class PurchaseOrderReceive(generics.CreateAPIView): serializer_class = serializers.PurchaseOrderReceiveSerializer - def get_serializer_context(self): - - context = super().get_serializer_context() - - # Pass the purchase order through to the serializer for validation - try: - context['order'] = models.PurchaseOrder.objects.get(pk=self.kwargs.get('pk', None)) - except: - pass - - context['request'] = self.request - - return context - class PurchaseOrderLineItemFilter(rest_filters.FilterSet): """ @@ -1107,6 +1123,7 @@ order_api_urls = [ # Individual purchase order detail URLs re_path(r'^(?P\d+)/', include([ re_path(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'), + re_path(r'^cancel/', PurchaseOrderCancel.as_view(), name='api-po-cancel'), re_path(r'.*$', PurchaseOrderDetail.as_view(), name='api-po-detail'), ])), diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 2b722ddecd..af1b159afa 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -380,6 +380,7 @@ class PurchaseOrder(Order): PurchaseOrderStatus.PENDING ] + @transaction.atomic def cancel_order(self): """ Marks the PurchaseOrder as CANCELLED. """ diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 7d26ce741d..a151da77b9 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -179,6 +179,35 @@ class PurchaseOrderSerializer(AbstractOrderSerializer, ReferenceIndexingSerializ ] +class PurchaseOrderCancelSerializer(serializers.Serializer): + """ + Serializer for cancelling a PurchaseOrder + """ + + class Meta: + fields = [], + + def get_context_data(self): + """ + Return custom context information about the order + """ + + self.order = self.context['order'] + + return { + 'can_cancel': self.order.can_cancel(), + } + + def save(self): + + order = self.context['order'] + + if not order.can_cancel(): + raise ValidationError(_("Order cannot be cancelled")) + + order.cancel_order() + + class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer): @staticmethod