mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Actually receive items
This commit is contained in:
		| @@ -5,7 +5,9 @@ JSON API for the Order app | ||||
| # -*- coding: utf-8 -*- | ||||
| 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_filters.rest_framework import DjangoFilterBackend | ||||
| from rest_framework import generics | ||||
| @@ -13,7 +15,6 @@ from rest_framework import filters, status | ||||
| from rest_framework.response import Response | ||||
| from rest_framework.serializers import ValidationError | ||||
|  | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
|  | ||||
| from InvenTree.helpers import str2bool | ||||
| from InvenTree.api import AttachmentMixin | ||||
| @@ -252,25 +253,34 @@ class POReceive(generics.CreateAPIView): | ||||
|         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) | ||||
|         # Receive the line items | ||||
|         self.receive_items(serializer) | ||||
|  | ||||
|         headers = self.get_success_headers(serializer.data) | ||||
|         return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) | ||||
|  | ||||
|     def validate(self, data): | ||||
|     @transaction.atomic | ||||
|     def receive_items(self, serializer): | ||||
|         """ | ||||
|         Validate the deserialized data. | ||||
|         Receive the items | ||||
|  | ||||
|         At this point, much of the heavy lifting has been done for us by DRF serializers | ||||
|         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'] | ||||
|  | ||||
|         # Keep track of validated data "on the fly" | ||||
|         self.items = [] | ||||
|         items = data['items'] | ||||
|  | ||||
|         for item in data['items']: | ||||
|         # Check if the location is not specified for any particular item | ||||
|         for item in items: | ||||
|  | ||||
|             supplier_part = item['supplier_part'] | ||||
|  | ||||
| @@ -287,7 +297,15 @@ class POReceive(generics.CreateAPIView): | ||||
|  | ||||
|                 item['location'] = location | ||||
|  | ||||
|             quantity = item['quantity'] | ||||
|         # 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'], | ||||
|             ) | ||||
|  | ||||
|  | ||||
| class POLineItemList(generics.ListCreateAPIView): | ||||
|   | ||||
| @@ -433,7 +433,7 @@ class PurchaseOrder(Order): | ||||
|                 quantity=quantity, | ||||
|                 purchase_order=self, | ||||
|                 status=status, | ||||
|                 purchase_price=purchase_price, | ||||
|                 purchase_price=line.purchase_price, | ||||
|             ) | ||||
|  | ||||
|             stock.save(add_note=False) | ||||
|   | ||||
| @@ -20,6 +20,8 @@ from InvenTree.serializers import InvenTreeAttachmentSerializer | ||||
| from InvenTree.serializers import InvenTreeMoneySerializer | ||||
| from InvenTree.serializers import InvenTreeAttachmentSerializerField | ||||
|  | ||||
| from InvenTree.status_codes import StockStatus | ||||
|  | ||||
| import company.models | ||||
| from company.serializers import CompanyBriefSerializer, SupplierPartSerializer | ||||
|  | ||||
| @@ -200,10 +202,17 @@ class POLineItemReceiveSerializer(serializers.Serializer): | ||||
|         required=True, | ||||
|     ) | ||||
|  | ||||
|     status = serializers.ChoiceField( | ||||
|         choices=StockStatus.options, | ||||
|         default=StockStatus.OK, | ||||
|         label=_('Status'), | ||||
|     ) | ||||
|  | ||||
|     class Meta: | ||||
|         fields = [ | ||||
|             'supplier_part', | ||||
|             'location', | ||||
|             'status', | ||||
|         ] | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| Tests for the Order API | ||||
| """ | ||||
|  | ||||
| from InvenTree.stock.models import StockItem | ||||
| from datetime import datetime, timedelta | ||||
|  | ||||
| from rest_framework import status | ||||
| @@ -213,6 +214,9 @@ class PurchaseOrderReceiveTest(OrderTest): | ||||
|  | ||||
|         self.url = reverse('api-po-receive', kwargs={'pk': 1}) | ||||
|  | ||||
|         # Number of stock items which exist at the start of each test | ||||
|         self.n = StockItem.objects.count() | ||||
|  | ||||
|     def test_empty(self): | ||||
|         """ | ||||
|         Test without any POST data | ||||
| @@ -223,6 +227,9 @@ class PurchaseOrderReceiveTest(OrderTest): | ||||
|         self.assertIn('This field is required', str(data['items'])) | ||||
|         self.assertIn('This field is required', str(data['location'])) | ||||
|  | ||||
|         # No new stock items have been created | ||||
|         self.assertEqual(self.n, StockItem.objects.count()) | ||||
|  | ||||
|     def test_no_items(self): | ||||
|         """ | ||||
|         Test with an empty list of items | ||||
| @@ -239,6 +246,9 @@ class PurchaseOrderReceiveTest(OrderTest): | ||||
|  | ||||
|         self.assertIn('Line items must be provided', str(data['items'])) | ||||
|  | ||||
|         # No new stock items have been created | ||||
|         self.assertEqual(self.n, StockItem.objects.count()) | ||||
|  | ||||
|     def test_invalid_items(self): | ||||
|         """ | ||||
|         Test than errors are returned as expected for invalid data  | ||||
| @@ -262,6 +272,34 @@ class PurchaseOrderReceiveTest(OrderTest): | ||||
|         self.assertIn('Invalid pk "12345"', str(items['line_item'])) | ||||
|         self.assertIn("object does not exist", str(items['location'])) | ||||
|  | ||||
|         # No new stock items have been created | ||||
|         self.assertEqual(self.n, StockItem.objects.count()) | ||||
|  | ||||
|     def test_invalid_status(self): | ||||
|         """ | ||||
|         Test with an invalid StockStatus value | ||||
|         """ | ||||
|  | ||||
|         data = self.post( | ||||
|             self.url, | ||||
|             { | ||||
|                 "items": [ | ||||
|                     { | ||||
|                         "line_item": 22, | ||||
|                         "location": 1, | ||||
|                         "status": 99999, | ||||
|                         "quantity": 5, | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             expected_code=400 | ||||
|         ).data | ||||
|  | ||||
|         self.assertIn('"99999" is not a valid choice.', str(data)) | ||||
|  | ||||
|         # No new stock items have been created | ||||
|         self.assertEqual(self.n, StockItem.objects.count()) | ||||
|  | ||||
|     def test_mismatched_items(self): | ||||
|         """ | ||||
|         Test for supplier parts which *do* exist but do not match the order supplier | ||||
| @@ -284,6 +322,9 @@ class PurchaseOrderReceiveTest(OrderTest): | ||||
|  | ||||
|         self.assertIn('Line item does not match purchase order', str(data)) | ||||
|  | ||||
|         # No new stock items have been created | ||||
|         self.assertEqual(self.n, StockItem.objects.count()) | ||||
|  | ||||
|  | ||||
| class SalesOrderTest(OrderTest): | ||||
|     """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user