From 3ce8d71fe32776b374a9dad2adbbee21834b5709 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 14 Jul 2026 02:40:33 +0000 Subject: [PATCH] Fix total_price annotation for ExtraLineItem --- src/backend/InvenTree/order/api.py | 17 ++++++++++++++--- src/backend/InvenTree/order/serializers.py | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index ab7c65d07e..9ee3645c79 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -80,6 +80,7 @@ class GeneralExtraLineList(SerializerContextMixin, DataExportViewMixin): queryset = super().get_queryset(*args, **kwargs) queryset = queryset.prefetch_related('order') + queryset = self.serializer_class.annotate_queryset(queryset) return queryset @@ -96,6 +97,16 @@ 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.""" @@ -778,7 +789,7 @@ class PurchaseOrderExtraLineList( serializer_class = serializers.PurchaseOrderExtraLineSerializer -class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI): +class PurchaseOrderExtraLineDetail(GeneralExtraLineDetail, RetrieveUpdateDestroyAPI): """API endpoint for detail view of a PurchaseOrderExtraLine object.""" queryset = models.PurchaseOrderExtraLine.objects.all() @@ -1113,7 +1124,7 @@ class SalesOrderExtraLineList(GeneralExtraLineList, OutputOptionsMixin, ListCrea serializer_class = serializers.SalesOrderExtraLineSerializer -class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI): +class SalesOrderExtraLineDetail(GeneralExtraLineDetail, RetrieveUpdateDestroyAPI): """API endpoint for detail view of a SalesOrderExtraLine object.""" queryset = models.SalesOrderExtraLine.objects.all() @@ -1826,7 +1837,7 @@ class ReturnOrderExtraLineList(GeneralExtraLineList, OutputOptionsMixin, ListCre serializer_class = serializers.ReturnOrderExtraLineSerializer -class ReturnOrderExtraLineDetail(RetrieveUpdateDestroyAPI): +class ReturnOrderExtraLineDetail(GeneralExtraLineDetail, 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 100abbf820..a4cc336236 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -334,6 +334,7 @@ class AbstractExtraLineSerializer( 'quantity', 'reference', 'target_date', + 'total_price', # Filterable detail fields 'order_detail', 'project_code_label', @@ -341,6 +342,19 @@ 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) @@ -349,6 +363,8 @@ class AbstractExtraLineSerializer( price_currency = InvenTreeCurrencySerializer() + total_price = serializers.FloatField(read_only=True) + project_code_label = common.filters.enable_project_label_filter() project_code_detail = common.filters.enable_project_code_filter() @@ -369,6 +385,7 @@ class AbstractExtraLineMeta: 'order_detail', 'price', 'price_currency', + 'total_price', 'link', ]