From 90c2245e5a39a720621f699de93c0184925e3f16 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 20 Jul 2026 03:26:45 +0000 Subject: [PATCH] Remove 'total_price' annotation - Can be achieved using in-memory python methods --- src/backend/InvenTree/order/api.py | 18 +++---------- src/backend/InvenTree/order/serializers.py | 31 +++++----------------- src/backend/InvenTree/order/test_api.py | 2 +- 3 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index 20ac304260..634a31198a 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -80,7 +80,6 @@ class GeneralExtraLineList(SerializerContextMixin, DataExportViewMixin): queryset = super().get_queryset(*args, **kwargs) queryset = queryset.prefetch_related('order') - queryset = self.serializer_class.annotate_queryset(queryset) return queryset @@ -97,16 +96,6 @@ class GeneralExtraLineList(SerializerContextMixin, DataExportViewMixin): filterset_fields = ['order'] -class GeneralExtraLineDetail: - """General template for ExtraLine detail API classes.""" - - def get_queryset(self, *args, **kwargs): - """Return the annotated queryset for this endpoint.""" - queryset = super().get_queryset(*args, **kwargs) - - return self.serializer_class.annotate_queryset(queryset) - - class OrderCreateMixin: """Mixin class which handles order creation via API.""" @@ -755,7 +744,6 @@ class PurchaseOrderLineItemList( 'reference', 'SKU', 'IPN', - 'total_price', 'target_date', 'order', 'status', @@ -789,7 +777,7 @@ class PurchaseOrderExtraLineList( serializer_class = serializers.PurchaseOrderExtraLineSerializer -class PurchaseOrderExtraLineDetail(GeneralExtraLineDetail, RetrieveUpdateDestroyAPI): +class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI): """API endpoint for detail view of a PurchaseOrderExtraLine object.""" queryset = models.PurchaseOrderExtraLine.objects.all() @@ -1129,7 +1117,7 @@ class SalesOrderExtraLineList( serializer_class = serializers.SalesOrderExtraLineSerializer -class SalesOrderExtraLineDetail(GeneralExtraLineDetail, RetrieveUpdateDestroyAPI): +class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI): """API endpoint for detail view of a SalesOrderExtraLine object.""" queryset = models.SalesOrderExtraLine.objects.all() @@ -1847,7 +1835,7 @@ class ReturnOrderExtraLineList( serializer_class = serializers.ReturnOrderExtraLineSerializer -class ReturnOrderExtraLineDetail(GeneralExtraLineDetail, RetrieveUpdateDestroyAPI): +class ReturnOrderExtraLineDetail(RetrieveUpdateDestroyAPI): """API endpoint for detail view of a ReturnOrderExtraLine object.""" queryset = models.ReturnOrderExtraLine.objects.all() diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index 94b277d9e2..e5d7f0ac0a 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -342,19 +342,6 @@ class AbstractExtraLineSerializer( *extra_fields, ] - @staticmethod - def annotate_queryset(queryset): - """Add annotations to the queryset. - - - total_price: price * quantity, adjusted for the line discount - """ - return queryset.annotate( - total_price=ExpressionWrapper( - F('price') * F('quantity') * (1 - F('discount') / Decimal(100)), - output_field=models.DecimalField(), - ) - ) - quantity = serializers.FloatField() discount = InvenTreeDecimalField(required=False) @@ -363,7 +350,9 @@ class AbstractExtraLineSerializer( price_currency = InvenTreeCurrencySerializer() - total_price = serializers.FloatField(read_only=True) + total_price = InvenTreeMoneySerializer( + source='total_line_price', allow_null=True, read_only=True + ) project_code_label = common.filters.enable_project_label_filter() @@ -610,7 +599,6 @@ class PurchaseOrderLineItemSerializer( def annotate_queryset(queryset): """Add some extra annotations to this queryset. - - "total_price" = purchase_price * quantity - "overdue" status (boolean field) """ queryset = queryset.prefetch_related( @@ -626,15 +614,6 @@ class PurchaseOrderLineItemSerializer( 'part__manufacturer_part__manufacturer', ) - queryset = queryset.annotate( - total_price=ExpressionWrapper( - F('purchase_price') - * F('quantity') - * (1 - F('discount') / Decimal(100)), - output_field=models.DecimalField(), - ) - ) - queryset = queryset.annotate( overdue=Case( When( @@ -675,7 +654,9 @@ class PurchaseOrderLineItemSerializer( overdue = serializers.BooleanField(read_only=True, allow_null=True) - total_price = serializers.FloatField(read_only=True) + total_price = InvenTreeMoneySerializer( + source='total_line_price', allow_null=True, read_only=True + ) part_detail = OptionalField( serializer_class=PartBriefSerializer, diff --git a/src/backend/InvenTree/order/test_api.py b/src/backend/InvenTree/order/test_api.py index 0204369d50..60803b182e 100644 --- a/src/backend/InvenTree/order/test_api.py +++ b/src/backend/InvenTree/order/test_api.py @@ -3583,7 +3583,7 @@ class ExtraLineTotalPriceTest(InvenTreeAPITestCase): """Unit tests for the 'total_price' field on ExtraLine API endpoints. Covers PurchaseOrderExtraLine, SalesOrderExtraLine and ReturnOrderExtraLine, - which all share the same 'total_price' annotation via AbstractExtraLineSerializer. + which all share the same 'total_price' field via AbstractExtraLineSerializer. """ fixtures = [