diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index c86fb6f178..d97da75b73 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -311,6 +311,7 @@ class POReceive(generics.CreateAPIView): item['quantity'], self.request.user, status=item['status'], + barcode=item.get('barcode', ''), ) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index e2753f702c..a069cf126f 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -411,6 +411,11 @@ class PurchaseOrder(Order): """ notes = kwargs.get('notes', '') + barcode = kwargs.get('barcode', '') + + # Prevent null values for barcode + if barcode is None: + barcode = '' if not self.status == PurchaseOrderStatus.PLACED: raise ValidationError({"status": _("Lines can only be received against an order marked as 'Placed'")}) @@ -434,6 +439,7 @@ class PurchaseOrder(Order): purchase_order=self, status=status, purchase_price=line.purchase_price, + uid=barcode ) stock.save(add_note=False) diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index f3ec114305..da2d23cd0d 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -233,6 +233,8 @@ class POLineItemReceiveSerializer(serializers.Serializer): barcode = serializers.CharField( label=_('Barcode Hash'), help_text=_('Unique identifier field'), + default='', + required=False, ) def validate_barcode(self, barcode): @@ -247,6 +249,8 @@ class POLineItemReceiveSerializer(serializers.Serializer): if stock.models.StockItem.objects.filter(uid=barcode).exists(): raise ValidationError(_('Barcode is already in use')) + return barcode + class Meta: fields = [ 'barcode', @@ -288,7 +292,7 @@ class POReceiveSerializer(serializers.Serializer): unique_barcodes = set() for item in items: - barcode = item.get('barcode', None) + barcode = item.get('barcode', '') if barcode: if barcode in unique_barcodes: diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py index 5fab3d8bd9..8476a9c668 100644 --- a/InvenTree/order/test_api.py +++ b/InvenTree/order/test_api.py @@ -332,6 +332,61 @@ class PurchaseOrderReceiveTest(OrderTest): # No new stock items have been created self.assertEqual(self.n, StockItem.objects.count()) + def test_invalid_barcodes(self): + """ + Tests for checking in items with invalid barcodes: + + - Cannot check in "duplicate" barcodes + - Barcodes cannot match UID field for existing StockItem + """ + + # Set stock item barcode + item = StockItem.objects.get(pk=1) + item.uid = 'MY-BARCODE-HASH' + item.save() + + response = self.post( + self.url, + { + 'items': [ + { + 'line_item': 1, + 'quantity': 50, + 'barcode': 'MY-BARCODE-HASH', + } + ], + 'location': 1, + }, + expected_code=400 + ) + + self.assertIn('Barcode is already in use', str(response.data)) + + response = self.post( + self.url, + { + 'items': [ + { + 'line_item': 1, + 'quantity': 5, + 'barcode': 'MY-BARCODE-HASH-1', + }, + { + 'line_item': 1, + 'quantity': 5, + 'barcode': 'MY-BARCODE-HASH-1' + }, + ], + 'location': 1, + }, + expected_code=400 + ) + + self.assertIn('barcode values must be unique', str(response.data)) + + # No new stock items have been created + self.assertEqual(self.n, StockItem.objects.count()) + def test_valid(self): """ Test receipt of valid data @@ -354,11 +409,13 @@ class PurchaseOrderReceiveTest(OrderTest): { '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 @@ -386,6 +443,10 @@ class PurchaseOrderReceiveTest(OrderTest): self.assertEqual(stock_1.last().location.pk, 1) self.assertEqual(stock_2.last().location.pk, 2) + # Barcodes should have been assigned to the stock items + self.assertTrue(StockItem.objects.filter(uid='MY-UNIQUE-BARCODE-123').exists()) + self.assertTrue(StockItem.objects.filter(uid='MY-UNIQUE-BARCODE-456').exists()) + class SalesOrderTest(OrderTest): """