diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 80e45523c9..abc4181895 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -377,6 +377,7 @@ class PartList(generics.ListCreateAPIView): def get_queryset(self, *args, **kwargs): queryset = super().get_queryset(*args, **kwargs) + queryset = part_serializers.PartSerializer.prefetch_queryset(queryset) queryset = part_serializers.PartSerializer.annotate_queryset(queryset) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index c1ecfb5cce..09b5b21d91 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -15,7 +15,9 @@ from .models import PartTestTemplate from decimal import Decimal -from django.db.models import Q, Sum +from sql_util.utils import SubquerySum + +from django.db.models import Q from django.db.models.functions import Coalesce from InvenTree.status_codes import StockStatus, PurchaseOrderStatus, BuildStatus @@ -190,28 +192,48 @@ class PartSerializer(InvenTreeModelSerializer): """ # Filter to limit stock items to "available" - stock_filter = Q(stock_items__status__in=StockStatus.AVAILABLE_CODES) + stock_filter = Q( + status__in=StockStatus.AVAILABLE_CODES, + sales_order=None, + build_order=None, + belongs_to=None, + customer=None, + ) - # Filter to limit orders to "open" - order_filter = Q(supplier_parts__purchase_order_line_items__order__status__in=PurchaseOrderStatus.OPEN) + # Annotate with the total 'in stock' quantity + queryset = queryset.annotate( + in_stock=Coalesce( + SubquerySum('stock_items__quantity', filter=stock_filter), + Decimal(0) + ), + ) # Filter to limit builds to "active" - build_filter = Q(builds__status__in=BuildStatus.ACTIVE_CODES) + build_filter = Q( + status__in=BuildStatus.ACTIVE_CODES + ) - # Annotate the number total stock count + # Annotate with the total 'building' quantity queryset = queryset.annotate( - in_stock=Coalesce(Sum('stock_items__quantity', filter=stock_filter, distinct=True), Decimal(0)), - ordering=Coalesce(Sum( - 'supplier_parts__purchase_order_line_items__quantity', - filter=order_filter, - distinct=True - ), Decimal(0)) - Coalesce(Sum( - 'supplier_parts__purchase_order_line_items__received', - filter=order_filter, - distinct=True - ), Decimal(0)), building=Coalesce( - Sum('builds__quantity', filter=build_filter, distinct=True), Decimal(0) + SubquerySum('builds__quantity', filter=build_filter), + Decimal(0), + ) + ) + + # Filter to limit orders to "open" + order_filter = Q( + order__status__in=PurchaseOrderStatus.OPEN + ) + + # Annotate with the total 'on order' quantity + queryset = queryset.annotate( + ordering=Coalesce( + SubquerySum('supplier_parts__purchase_order_line_items__quantity', filter=order_filter), + Decimal(0), + ) - Coalesce( + SubquerySum('supplier_parts__purchase_order_line_items__received', filter=order_filter), + Decimal(0), ) ) diff --git a/InvenTree/templates/js/part.html b/InvenTree/templates/js/part.html index 2fdcfd57f7..03c98e09a4 100644 --- a/InvenTree/templates/js/part.html +++ b/InvenTree/templates/js/part.html @@ -122,6 +122,9 @@ function loadPartVariantTable(table, partId, options) { { field: 'in_stock', title: '{% trans "Stock" %}', + formatter: function(value, row) { + return renderLink(value, `/part/${row.pk}/stock/`); + } } ]; diff --git a/requirements.txt b/requirements.txt index 38b5a37b6d..8faa7f58a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ django-dbbackup==3.3.0 # Database backup / restore functionality django-cors-headers==3.2.0 # CORS headers extension for DRF django_filter==2.2.0 # Extended filtering options django-mptt==0.11.0 # Modified Preorder Tree Traversal +django-sql-utils==0.5.0 # Advanced query annotation / aggregation django-markdownx==3.0.1 # Markdown form fields django-markdownify==0.8.0 # Markdown rendering coreapi==2.3.0 # API documentation