From f38bf6e20a32bc3aab4a3885aed5db0464dd3b7c Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Tue, 7 Sep 2021 23:34:14 +1000
Subject: [PATCH] Adds unit testing for barcode field

---
 InvenTree/order/api.py         |  1 +
 InvenTree/order/models.py      |  6 ++++
 InvenTree/order/serializers.py |  6 +++-
 InvenTree/order/test_api.py    | 61 ++++++++++++++++++++++++++++++++++
 4 files changed, 73 insertions(+), 1 deletion(-)

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):
     """