From 6091f2ba337991ec9e2e60ede22d6e2a55b6de57 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 24 Aug 2021 00:29:38 +1000 Subject: [PATCH] Serializer improvements - Pass the "order" down to the nested serializers for validation --- InvenTree/order/api.py | 47 ++++++++++++++++++++++++++++++++++ InvenTree/order/serializers.py | 17 +++++++----- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index 40819a3bff..84c5c2ed3c 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -225,6 +225,15 @@ class POReceive(generics.CreateAPIView): serializer_class = POReceiveSerializer + def get_serializer_context(self): + + context = super().get_serializer_context() + + # Pass the purchase order through to the serializer for validation + context['order'] = self.get_order() + + return context + def get_order(self): """ Returns the PurchaseOrder associated with this API endpoint @@ -235,13 +244,51 @@ class POReceive(generics.CreateAPIView): return order def create(self, request, *args, **kwargs): + + # Which purchase order are we receiving against? + self.order = self.get_order() + # Validate the serialized data serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) + # Check that the received line items are indeed correct + self.validate(serializer.validated_data) + headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + def validate(self, data): + """ + Validate the deserialized data. + + At this point, much of the heavy lifting has been done for us by DRF serializers + """ + + location = data['location'] + + # Keep track of validated data "on the fly" + self.items = [] + + for item in data['items']: + + supplier_part = item['supplier_part'] + + # Location specified for this part + item_location = item['location'] + + if not item_location: + + # Both item_location and location are not specified + if not location: + raise ValidationError({ + 'location': _("Destination location must be specified"), + }) + + item['location'] = location + + quantity = item['quantity'] + class POLineItemList(generics.ListCreateAPIView): """ API endpoint for accessing a list of POLineItem objects diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index f090faf214..cc40c321dc 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -172,12 +172,19 @@ class POLineItemReceiveSerializer(serializers.Serializer): A serializer for receiving a single purchase order line item against a purchase order """ - supplier_part = serializers.PrimaryKeyRelatedField( - queryset=company.models.SupplierPart.objects.all(), + line_item = serializers.PrimaryKeyRelatedField( + queryset=PurchaseOrderLineItem.objects.all(), many=False, - label=_('Supplier Part'), + allow_null=False, + required=True, + label=_('Line Item'), ) + def validate_line_item(self, item): + + if item.order != self.context['order']: + raise ValidationError(_('Line item does not match purchase order')) + location = serializers.PrimaryKeyRelatedField( queryset=stock.models.StockLocation.objects.all(), many=False, @@ -205,9 +212,7 @@ class POReceiveSerializer(serializers.Serializer): Serializer for receiving items against a purchase order """ - items = POLineItemReceiveSerializer( - many=True, - ) + items = POLineItemReceiveSerializer(many=True) location = serializers.PrimaryKeyRelatedField( queryset=stock.models.StockLocation.objects.all(),