diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index 7ad506bc9e..9c608e32a5 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -84,6 +84,8 @@ class GeneralExtraLineList(SerializerContextMixin, DataExportViewMixin): ordering_fields = ['quantity', 'notes', 'reference', 'line'] + ordering_field_aliases = {'line': ['line_int', 'line']} + search_fields = ['quantity', 'notes', 'reference', 'description'] filterset_fields = ['order'] @@ -717,7 +719,7 @@ class PurchaseOrderLineItemList( 'order': 'order__reference', 'status': 'order__status', 'complete_date': 'order__complete_date', - 'line': ['line', 'part__SKU'], + 'line': ['line_int', 'line', 'part__SKU'], } ordering_fields = [ @@ -1077,7 +1079,7 @@ class SalesOrderLineItemList( 'part': 'part__name', 'IPN': 'part__IPN', 'order': 'order__reference', - 'line': ['line', 'part__name'], + 'line': ['line_int', 'line', 'part__name'], } search_fields = ['part__name', 'quantity', 'reference'] @@ -1728,7 +1730,7 @@ class ReturnOrderLineItemList( ] ordering_field_aliases = { - 'line': ['line', 'item__part__name'], + 'line': ['line_int', 'line', 'item__part__name'], 'part': 'item__part__name', 'IPN': 'item__part__IPN', 'stock': ['item__quantity', 'item__serial_int', 'item__serial'], diff --git a/src/backend/InvenTree/order/migrations/0117_purchaseorderextraline_line_int_and_more.py b/src/backend/InvenTree/order/migrations/0117_purchaseorderextraline_line_int_and_more.py new file mode 100644 index 0000000000..ae7271d82a --- /dev/null +++ b/src/backend/InvenTree/order/migrations/0117_purchaseorderextraline_line_int_and_more.py @@ -0,0 +1,76 @@ +# Generated by Django 5.2.13 on 2026-05-07 04:10 + +from django.db import migrations, models + + +def add_integer_line_number(apps, schema_editor): + """Add integer line numbers to existing line items.""" + + print("\nUpdating line items to add integer line numbers:") + + for model in [ + apps.get_model('order', 'PurchaseOrderLineItem'), + apps.get_model('order', 'ReturnOrderLineItem'), + apps.get_model('order', 'SalesOrderLineItem'), + apps.get_model('order', 'PurchaseOrderExtraLine'), + apps.get_model('order', 'ReturnOrderExtraLine'), + apps.get_model('order', 'SalesOrderExtraLine'), + ]: + items = [] + + for item in model.objects.exclude(line__isnull=True).exclude(line=''): + # Attempt to convert the existing line number to an integer + try: + item.line_int = int(item.line) + items.append(item) + except (ValueError, TypeError): + pass + + if len(items) > 0: + print(f"- Updating {len(items)} items for model {model._meta.model_name}") + model.objects.bulk_update(items, ['line_int'], batch_size=250) + + + +class Migration(migrations.Migration): + + dependencies = [ + ("order", "0116_purchaseorderextraline_line_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="purchaseorderextraline", + name="line_int", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="purchaseorderlineitem", + name="line_int", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="returnorderextraline", + name="line_int", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="returnorderlineitem", + name="line_int", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="salesorderextraline", + name="line_int", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="salesorderlineitem", + name="line_int", + field=models.IntegerField(default=0), + ), + migrations.RunPython( + code=add_integer_line_number, + reverse_code=migrations.RunPython.noop, + ), + ] diff --git a/src/backend/InvenTree/order/models.py b/src/backend/InvenTree/order/models.py index 2e36b6228c..589291b2e7 100644 --- a/src/backend/InvenTree/order/models.py +++ b/src/backend/InvenTree/order/models.py @@ -1755,6 +1755,8 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel): Attributes: quantity: Number of items + line: The line number for this item (optional) + line_int: An integer line number for this item (optional - used for natural sorting) reference: Reference text (e.g. customer reference) for this line item project_code: Project code associated with this line item (optional) note: Annotation for the item @@ -1778,6 +1780,15 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel): update_order = kwargs.pop('update_order', True) + # Update the integer representation of the line number (for natural sorting) + if self.line: + try: + self.line_int = int(self.line) + except (TypeError, ValueError): + self.line_int = 0 + else: + self.line_int = 0 + super().save(*args, **kwargs) if update_order and self.order: self.order.save() @@ -1819,6 +1830,8 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel): help_text=_('Line number for this item (optional)'), ) + line_int = models.IntegerField(default=0, blank=False, null=False) + reference = models.CharField( max_length=100, blank=True,