diff --git a/InvenTree/stock/admin.py b/InvenTree/stock/admin.py index 5f1b134966..abd3db20cb 100644 --- a/InvenTree/stock/admin.py +++ b/InvenTree/stock/admin.py @@ -117,6 +117,8 @@ class StockItemResource(ModelResource): exclude = [ # Exclude MPTT internal model fields 'lft', 'rght', 'tree_id', 'level', + # Exclude internal fields + 'serial_int', ] diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index e287441382..2ffc2e8d69 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -876,6 +876,7 @@ class StockList(generics.ListCreateAPIView): ordering_field_aliases = { 'SKU': 'supplier_part__SKU', + 'stock': ['quantity', 'serial_int', 'serial'], } ordering_fields = [ @@ -887,6 +888,7 @@ class StockList(generics.ListCreateAPIView): 'stocktake_date', 'expiry_date', 'quantity', + 'stock', 'status', 'SKU', ] diff --git a/InvenTree/stock/migrations/0068_stockitem_serial_int.py b/InvenTree/stock/migrations/0068_stockitem_serial_int.py new file mode 100644 index 0000000000..874978dc61 --- /dev/null +++ b/InvenTree/stock/migrations/0068_stockitem_serial_int.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2021-11-09 23:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0067_alter_stockitem_part'), + ] + + operations = [ + migrations.AddField( + model_name='stockitem', + name='serial_int', + field=models.IntegerField(default=0), + ), + ] diff --git a/InvenTree/stock/migrations/0069_auto_20211109_2347.py b/InvenTree/stock/migrations/0069_auto_20211109_2347.py new file mode 100644 index 0000000000..f4cdde7794 --- /dev/null +++ b/InvenTree/stock/migrations/0069_auto_20211109_2347.py @@ -0,0 +1,54 @@ +# Generated by Django 3.2.5 on 2021-11-09 23:47 + +import re + +from django.db import migrations + + +def update_serials(apps, schema_editor): + """ + Rebuild the integer serial number field for existing StockItem objects + """ + + StockItem = apps.get_model('stock', 'stockitem') + + for item in StockItem.objects.all(): + + if item.serial is None: + # Skip items without existing serial numbers + continue + + serial = 0 + + result = re.match(r"^(\d+)", str(item.serial)) + + if result and len(result.groups()) == 1: + try: + serial = int(result.groups()[0]) + except: + serial = 0 + + + item.serial_int = serial + item.save() + + +def nupdate_serials(apps, schema_editor): + """ + Provided only for reverse migration compatibility + """ + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0068_stockitem_serial_int'), + ] + + operations = [ + migrations.RunPython( + update_serials, + reverse_code=nupdate_serials, + ) + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 320807e0c1..8e07074a76 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -7,6 +7,7 @@ Stock database model definitions from __future__ import unicode_literals import os +import re from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError, FieldError @@ -223,6 +224,32 @@ class StockItem(MPTTModel): self.scheduled_for_deletion = True self.save() + def update_serial_number(self): + """ + Update the 'serial_int' field, to be an integer representation of the serial number. + This is used for efficient numerical sorting + """ + + serial = getattr(self, 'serial', '') + + # Default value if we cannot convert to an integer + serial_int = 0 + + if serial is not None: + + serial = str(serial) + + # Look at the start of the string - can it be "integerized"? + result = re.match(r'^(\d+)', serial) + + if result and len(result.groups()) == 1: + try: + serial_int = int(result.groups()[0]) + except: + serial_int = 0 + + self.serial_int = serial_int + def save(self, *args, **kwargs): """ Save this StockItem to the database. Performs a number of checks: @@ -234,6 +261,8 @@ class StockItem(MPTTModel): self.validate_unique() self.clean() + self.update_serial_number() + user = kwargs.pop('user', None) # If 'add_note = False' specified, then no tracking note will be added for item creation @@ -504,6 +533,8 @@ class StockItem(MPTTModel): help_text=_('Serial number for this item') ) + serial_int = models.IntegerField(default=0) + link = InvenTreeURLField( verbose_name=_('External Link'), max_length=125, blank=True, diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index ec785969cd..ba4238e6f7 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -1128,7 +1128,9 @@ function loadStockTable(table, options) { col = { field: 'quantity', + sortName: 'stock', title: '{% trans "Stock" %}', + sortable: true, formatter: function(value, row) { var val = parseFloat(value);