From 9e3f3b27dbb67a0dc664a2ab1a607f6a7a7faca0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 25 Nov 2025 09:03:45 +0000 Subject: [PATCH] Add parameter support for orders --- src/backend/InvenTree/order/api.py | 24 ++++++++++++++-------- src/backend/InvenTree/order/models.py | 3 ++- src/backend/InvenTree/order/serializers.py | 17 +++++++++++++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index ddf7dadb57..0609ab365a 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -28,7 +28,12 @@ import stock.models as stock_models import stock.serializers as stock_serializers from data_exporter.mixins import DataExportViewMixin from generic.states.api import StatusView -from InvenTree.api import BulkUpdateMixin, ListCreateDestroyAPIView, MetadataView +from InvenTree.api import ( + BulkUpdateMixin, + ListCreateDestroyAPIView, + MetadataView, + ParameterListMixin, +) from InvenTree.fields import InvenTreeOutputOption, OutputConfiguration from InvenTree.filters import ( SEARCH_ORDER_FILTER, @@ -378,6 +383,7 @@ class PurchaseOrderList( OrderCreateMixin, DataExportViewMixin, OutputOptionsMixin, + ParameterListMixin, ListCreateAPI, ): """API endpoint for accessing a list of PurchaseOrder objects. @@ -386,6 +392,7 @@ class PurchaseOrderList( - POST: Create a new PurchaseOrder object """ + parameter_model_class = models.PurchaseOrder filterset_class = PurchaseOrderFilter filter_backends = SEARCH_ORDER_FILTER_ALIAS output_options = PurchaseOrderOutputOptions @@ -847,6 +854,7 @@ class SalesOrderList( OrderCreateMixin, DataExportViewMixin, OutputOptionsMixin, + ParameterListMixin, ListCreateAPI, ): """API endpoint for accessing a list of SalesOrder objects. @@ -855,10 +863,9 @@ class SalesOrderList( - POST: Create a new SalesOrder """ + parameter_model_class = models.SalesOrder filterset_class = SalesOrderFilter - filter_backends = SEARCH_ORDER_FILTER_ALIAS - output_options = SalesOrderOutputOptions ordering_field_aliases = { @@ -1323,7 +1330,7 @@ class SalesOrderAllocationList( class SalesOrderAllocationDetail(SalesOrderAllocationMixin, RetrieveUpdateDestroyAPI): - """API endpoint for detali view of a SalesOrderAllocation object.""" + """API endpoint for detail view of a SalesOrderAllocation object.""" class SalesOrderShipmentFilter(FilterSet): @@ -1374,7 +1381,7 @@ class SalesOrderShipmentFilter(FilterSet): ) def filter_order_status(self, queryset, name, value): - """Filter by linked SalesOrderrder status.""" + """Filter by linked SalesOrder status.""" q1 = Q(order__status=value, order__status_custom_key__isnull=True) q2 = Q(order__status_custom_key=value) @@ -1412,7 +1419,7 @@ class SalesOrderShipmentList(SalesOrderShipmentMixin, ListCreateAPI): class SalesOrderShipmentDetail(SalesOrderShipmentMixin, RetrieveUpdateDestroyAPI): - """API detail endpooint for SalesOrderShipment model.""" + """API detail endpoint for SalesOrderShipment model.""" class SalesOrderShipmentComplete(CreateAPI): @@ -1525,12 +1532,13 @@ class ReturnOrderList( OrderCreateMixin, DataExportViewMixin, OutputOptionsMixin, + ParameterListMixin, ListCreateAPI, ): """API endpoint for accessing a list of ReturnOrder objects.""" + parameter_model_class = models.ReturnOrder filterset_class = ReturnOrderFilter - filter_backends = SEARCH_ORDER_FILTER_ALIAS output_options = ReturnOrderOutputOptions @@ -1964,7 +1972,7 @@ order_api_urls = [ ), ]), ), - # API endpoints for sales ordesr + # API endpoints for sales orders path( 'so/', include([ diff --git a/src/backend/InvenTree/order/models.py b/src/backend/InvenTree/order/models.py index 4efa189e71..bf699af334 100644 --- a/src/backend/InvenTree/order/models.py +++ b/src/backend/InvenTree/order/models.py @@ -268,6 +268,7 @@ class ReturnOrderReportContext(report.mixins.BaseReportContext): class Order( StatusCodeMixin, StateTransitionMixin, + InvenTree.models.InvenTreeParameterMixin, InvenTree.models.InvenTreeAttachmentMixin, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNotesMixin, @@ -988,7 +989,7 @@ class PurchaseOrder(TotalPriceMixin, Order): # Before we continue, validate that each line item is valid # We validate this here because it is far more efficient, - # after we have fetched *all* line itemes in a single DB query + # after we have fetched *all* line items in a single DB query for line_item in line_item_map.values(): if line_item.order != self: raise ValidationError({_('Line item does not match purchase order')}) diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index bc993581c1..caac022c91 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -22,6 +22,7 @@ from rest_framework.serializers import ValidationError from sql_util.utils import SubqueryCount, SubquerySum import build.serializers +import common.serializers import order.models import part.filters as part_filters import part.models as part_models @@ -177,6 +178,12 @@ class AbstractOrderSerializer( True, ) + parameters = enable_filter( + common.serializers.ParameterSerializer(many=True, read_only=True), + False, + filter_name='parameters', + ) + # Boolean field indicating if this order is overdue (Note: must be annotated) overdue = serializers.BooleanField(read_only=True, allow_null=True) @@ -240,6 +247,7 @@ class AbstractOrderSerializer( 'project_code_detail', 'project_code_label', 'responsible_detail', + 'parameters', *extra_fields, ] @@ -444,6 +452,9 @@ class PurchaseOrderSerializer( """ queryset = AbstractOrderSerializer.annotate_queryset(queryset) + # Annotate parametric data + queryset = order.models.PurchaseOrder.annotate_parameters(queryset) + queryset = queryset.annotate( completed_lines=SubqueryCount( 'lines', filter=Q(quantity__lte=F('received')) @@ -1087,6 +1098,9 @@ class SalesOrderSerializer( """ queryset = AbstractOrderSerializer.annotate_queryset(queryset) + # Annotate parametric data + queryset = order.models.SalesOrder.annotate_parameters(queryset) + queryset = queryset.annotate( completed_lines=SubqueryCount('lines', filter=Q(quantity__lte=F('shipped'))) ) @@ -1936,6 +1950,9 @@ class ReturnOrderSerializer( """Custom annotation for the serializer queryset.""" queryset = AbstractOrderSerializer.annotate_queryset(queryset) + # Annotate parametric data + queryset = order.models.ReturnOrder.annotate_parameters(queryset) + queryset = queryset.annotate( completed_lines=SubqueryCount( 'lines', filter=~Q(outcome=ReturnOrderLineStatus.PENDING.value)