Refactor the POReceive API endpoint

- Lessons learned from the build allocate
- Use serializer.save() directly
This commit is contained in:
Oliver 2021-10-05 11:20:43 +11:00
parent 7ecd4c70e7
commit 29588ff2c5
3 changed files with 42 additions and 72 deletions

View File

@ -172,7 +172,7 @@ class TestBuildAPI(APITestCase):
# Filter by 'part' status # Filter by 'part' status
response = self.client.get(url, {'part': 25}, format='json') response = self.client.get(url, {'part': 25}, format='json')
self.assertEqual(len(response.data), 2) self.assertEqual(len(response.data), 1)
# Filter by an invalid part # Filter by an invalid part
response = self.client.get(url, {'part': 99999}, format='json') response = self.client.get(url, {'part': 99999}, format='json')

View File

@ -252,76 +252,6 @@ class POReceive(generics.CreateAPIView):
return order 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)
# Receive the line items
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
def receive_items(self, serializer):
"""
Receive the items
At this point, much of the heavy lifting has been done for us by DRF serializers!
We have a list of "items", each a dict which contains:
- line_item: A PurchaseOrderLineItem matching this order
- location: A destination location
- quantity: A validated numerical quantity
- status: The status code for the received item
"""
data = serializer.validated_data
location = data['location']
items = data['items']
# Check if the location is not specified for any particular item
for item in items:
line = item['line_item']
if not item.get('location', None):
# If a global location is specified, use that
item['location'] = location
if not item['location']:
# The line item specifies a location?
item['location'] = line.get_destination()
if not item['location']:
raise ValidationError({
'location': _("Destination location must be specified"),
})
# Now we can actually receive the items
for item in items:
self.order.receive_line_item(
item['line_item'],
item['location'],
item['quantity'],
self.request.user,
status=item['status'],
barcode=item.get('barcode', ''),
)
class POLineItemList(generics.ListCreateAPIView): class POLineItemList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of POLineItem objects """ API endpoint for accessing a list of POLineItem objects

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db import models from django.db import models, transaction
from django.db.models import Case, When, Value from django.db.models import Case, When, Value
from django.db.models import BooleanField, ExpressionWrapper, F from django.db.models import BooleanField, ExpressionWrapper, F
@ -301,6 +301,46 @@ class POReceiveSerializer(serializers.Serializer):
return data return data
def save(self):
data = self.validated_data
order = self.context['order']
items = data['items']
location = data.get('location', None)
# Check if the location is not specified for any particular item
for item in items:
line = item['line_item']
if not item.get('location', None):
# If a global location is specified, use that
item['location'] = location
if not item['location']:
# The line item specifies a location?
item['location'] = line.get_destination()
if not item['location']:
raise ValidationError({
'location': _("Destination location must be specified"),
})
# Now we can actually receive the items into stock
with transaction.atomic():
for item in items:
order.receive_line_item(
item['line_item'],
item['location'],
item['quantity'],
self.request.user,
status=item['status'],
barcode=item.get('barcode', ''),
)
class Meta: class Meta:
fields = [ fields = [
'items', 'items',