From 4e100643f5c7e89a9c7a649ec1e6e7b3dc25d74e Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Fri, 1 Apr 2022 17:24:15 +1100
Subject: [PATCH] Add annotation for "allocated_to_sales_orders"

---
 InvenTree/part/serializers.py | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py
index c46950adca..dafb4a846e 100644
--- a/InvenTree/part/serializers.py
+++ b/InvenTree/part/serializers.py
@@ -24,7 +24,10 @@ from InvenTree.serializers import (DataFileUploadSerializer,
                                    InvenTreeAttachmentSerializer,
                                    InvenTreeMoneySerializer)
 
-from InvenTree.status_codes import BuildStatus, PurchaseOrderStatus
+from InvenTree.status_codes import (BuildStatus,
+                                    PurchaseOrderStatus,
+                                    SalesOrderStatus)
+
 from stock.models import StockItem
 
 from .models import (BomItem, BomItemSubstitute,
@@ -363,6 +366,26 @@ class PartSerializer(InvenTreeModelSerializer):
             ),
         )
 
+        """
+        Annotate with the number of stock items allocated to sales orders.
+        This annotation is modeled on Part.sales_order_allocations() method:
+
+        - Only look for "open" orders
+        - Stock items have not been "shipped"
+        """
+        so_allocation_filter = Q(
+            line__order__status__in=SalesOrderStatus.OPEN,  # LineItem points to an OPEN order
+            shipment=None,  # Allocated item has *not* been shipped out
+        )
+
+        queryset = queryset.annotate(
+            allocated_to_sales_orders=Coalesce(
+                SubquerySum('stock_items__sales_order_allocations__quantity', filter=so_allocation_filter),
+                Decimal(0),
+                output_field=models.DecimalField(),
+            )
+        )
+
         return queryset
 
     def get_starred(self, part):
@@ -376,9 +399,10 @@ class PartSerializer(InvenTreeModelSerializer):
     category_detail = CategorySerializer(source='category', many=False, read_only=True)
 
     # Calculated fields
+    allocated_to_sales_orders = serializers.FloatField(read_only=True)
+    building = serializers.FloatField(read_only=True)
     in_stock = serializers.FloatField(read_only=True)
     ordering = serializers.FloatField(read_only=True)
-    building = serializers.FloatField(read_only=True)
     stock_item_count = serializers.IntegerField(read_only=True)
     suppliers = serializers.IntegerField(read_only=True)
 
@@ -399,7 +423,7 @@ class PartSerializer(InvenTreeModelSerializer):
         partial = True
         fields = [
             'active',
-
+            'allocated_to_sales_orders',
             'assembly',
             'category',
             'category_detail',