diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 1296ba8978..99232519dc 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -152,8 +152,8 @@ def extract_int(reference, clip=0x7fffffff): if clip is not None: if ref_int > clip: ref_int = clip - elif ref_int < clip: - ref_int = clip + elif ref_int < -clip: + ref_int = -clip return ref_int diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 2abcecdc5f..69d2bbaf26 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -269,11 +269,14 @@ class StockItem(MPTTModel): serial_int = 0 if serial is not None: + + serial = str(serial).strip() + serial_int = extract_int(str(serial)) self.serial_int = serial_int - def get_next_serial_number(self, include_variants=True, reverse=False): + def get_next_serialized_item(self, include_variants=True, reverse=False): """ Get the "next" serial number for the part this stock item references. diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index 98a8f5e288..ba0e6554d6 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -380,6 +380,78 @@ class StockTest(TestCase): item.save() self.assertTrue(item.serialized) + def test_big_serials(self): + """ + Unit tests for "large" serial numbers which exceed integer encoding + """ + + p = Part.objects.create( + name='trackable part', + description='trackable part', + trackable=True, + ) + + item = StockItem.objects.create( + part=p, + quantity=1, + ) + + for sn in [12345, '12345', ' 12345 ']: + item.serial = sn + item.save() + + self.assertEqual(item.serial_int, 12345) + + item.serial = "-123" + item.save() + + # Negative number should map to zero + self.assertEqual(item.serial_int, 0) + + # Non-numeric values should encode to zero + for sn in ['apple', 'banana', 'carrot']: + item.serial = sn + item.save() + + self.assertEqual(item.serial_int, 0) + + # Next, test for incremenet / decrement functionality + item.serial = 100 + item.save() + + item_next = StockItem.objects.create( + part=p, + serial=150, + quantity=1 + ) + + self.assertEqual(item.get_next_serialized_item(), item_next) + + item_prev = StockItem.objects.create( + part=p, + serial=' 57', + quantity=1, + ) + + self.assertEqual(item.get_next_serialized_item(reverse=True), item_prev) + + # Create a number of serialized stock items around the current item + for i in range(75, 125): + try: + StockItem.objects.create( + part=p, + serial=i, + quantity=1, + ) + except: + pass + + item_next = item.get_next_serialized_item() + item_prev = item.get_next_serialized_item(reverse=True) + + self.assertEqual(item_next.serial_int, 101) + self.assertEqual(item_prev.serial_int, 99) + def test_serialize_stock_invalid(self): """ Test manual serialization of parts. diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 71dc592080..079f9c2dc9 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -109,8 +109,8 @@ class StockItemDetail(InvenTreeRoleMixin, DetailView): data = super().get_context_data(**kwargs) if self.object.serialized: - data['previous'] = self.object.get_next_serial_number(reverse=True) - data['next'] = self.object.get_next_serial_number() + data['previous'] = self.object.get_next_serialized_item(reverse=True) + data['next'] = self.object.get_next_serialized_item() data['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') data['item_owner'] = self.object.get_item_owner()