From 6d448d84753f2ac065c6240dc0ce140332fc6910 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 3 Oct 2021 00:21:06 +1000 Subject: [PATCH 1/2] Handle internal django errors when receiving purchase order items via the API (cherry picked from commit 9355c68024acf47ee4f200b6b9689ca37959dc3b) --- InvenTree/order/api.py | 20 ++++++++++++++------ InvenTree/order/models.py | 16 ++++++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index d97da75b73..ab6c4d7c0b 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -8,11 +8,13 @@ from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ from django.conf.urls import url, include from django.db import transaction +from django.core.exceptions import ValidationError as DjangoValidationError from django_filters import rest_framework as rest_filters from rest_framework import generics from rest_framework import filters, status from rest_framework.response import Response +from rest_framework import serializers from rest_framework.serializers import ValidationError @@ -243,11 +245,12 @@ class POReceive(generics.CreateAPIView): pk = self.kwargs.get('pk', None) - if pk is None: - return None - else: - order = PurchaseOrder.objects.get(pk=self.kwargs['pk']) - return order + try: + order = PurchaseOrder.objects.get(pk=pk) + except (PurchaseOrder.DoesNotExist, ValueError): + raise ValidationError(_("Matching purchase order does not exist")) + + return order def create(self, request, *args, **kwargs): @@ -259,9 +262,14 @@ class POReceive(generics.CreateAPIView): serializer.is_valid(raise_exception=True) # Receive the line items - self.receive_items(serializer) + try: + self.receive_items(serializer) + except DjangoValidationError as exc: + # Re-throw a django error as a DRF error + raise ValidationError(detail=serializers.as_serializer_error(exc)) headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) @transaction.atomic diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index a069cf126f..061286367c 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -418,16 +418,24 @@ class PurchaseOrder(Order): barcode = '' if not self.status == PurchaseOrderStatus.PLACED: - raise ValidationError({"status": _("Lines can only be received against an order marked as 'Placed'")}) + raise ValidationError({ + "status": _("Lines can only be received against an order marked as 'Placed'") + }) try: if not (quantity % 1 == 0): - raise ValidationError({"quantity": _("Quantity must be an integer")}) + raise ValidationError({ + "quantity": _("Quantity must be an integer") + }) if quantity < 0: - raise ValidationError({"quantity": _("Quantity must be a positive number")}) + raise ValidationError({ + "quantity": _("Quantity must be a positive number") + }) quantity = int(quantity) except (ValueError, TypeError): - raise ValidationError({"quantity": _("Invalid quantity provided")}) + raise ValidationError({ + "quantity": _("Invalid quantity provided") + }) # Create a new stock item if line.part and quantity > 0: From 8c78d3b8ea6ae4beba99629d79761bc9e68726c6 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 3 Oct 2021 01:03:40 +1000 Subject: [PATCH 2/2] Add unit test --- InvenTree/order/models.py | 6 ++--- InvenTree/order/test_api.py | 52 +++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 061286367c..495ea2d333 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -418,9 +418,9 @@ class PurchaseOrder(Order): barcode = '' if not self.status == PurchaseOrderStatus.PLACED: - raise ValidationError({ - "status": _("Lines can only be received against an order marked as 'Placed'") - }) + raise ValidationError( + "Lines can only be received against an order marked as 'PLACED'" + ) try: if not (quantity % 1 == 0): diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py index 8476a9c668..765c58cc3d 100644 --- a/InvenTree/order/test_api.py +++ b/InvenTree/order/test_api.py @@ -401,25 +401,45 @@ class PurchaseOrderReceiveTest(OrderTest): self.assertEqual(line_1.received, 0) self.assertEqual(line_2.received, 50) + valid_data = { + 'items': [ + { + 'line_item': 1, + 'quantity': 50, + 'barcode': 'MY-UNIQUE-BARCODE-123', + }, + { + 'line_item': 2, + 'quantity': 200, + 'location': 2, # Explicit location + 'barcode': 'MY-UNIQUE-BARCODE-456', + } + ], + 'location': 1, # Default location + } + + # Before posting "valid" data, we will mark the purchase order as "pending" + # In this case we do expect an error! + order = PurchaseOrder.objects.get(pk=1) + order.status = PurchaseOrderStatus.PENDING + order.save() + + response = self.post( + self.url, + valid_data, + expected_code=400 + ) + + self.assertIn('can only be received against', str(response.data)) + + # Now, set the PO back to "PLACED" so the items can be received + order.status = PurchaseOrderStatus.PLACED + order.save() + # Receive two separate line items against this order self.post( self.url, - { - 'items': [ - { - 'line_item': 1, - 'quantity': 50, - 'barcode': 'MY-UNIQUE-BARCODE-123', - }, - { - 'line_item': 2, - 'quantity': 200, - 'location': 2, # Explicit location - 'barcode': 'MY-UNIQUE-BARCODE-456', - } - ], - 'location': 1, # Default location - }, + valid_data, expected_code=201, )