mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-13 02:25:38 +00:00
Fixes for aggregation issues
- Ensure that "distinct=True" is set! - ARRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
This commit is contained in:
@ -10,6 +10,12 @@ from .models import PartCategory
|
||||
from .models import BomItem
|
||||
from .models import PartParameter, PartParameterTemplate
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db.models import Q, F, Sum, Count
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
from InvenTree.status_codes import StockStatus, OrderStatus, BuildStatus
|
||||
from InvenTree.serializers import InvenTreeModelSerializer
|
||||
|
||||
|
||||
@ -78,24 +84,77 @@ class PartSerializer(InvenTreeModelSerializer):
|
||||
Used when displaying all details of a single component.
|
||||
"""
|
||||
|
||||
allocated_stock = serializers.FloatField(source='allocation_count', read_only=True)
|
||||
bom_items = serializers.IntegerField(source='bom_count', read_only=True)
|
||||
building = serializers.FloatField(source='quantity_being_built', read_only=False)
|
||||
category_name = serializers.CharField(source='category_path', read_only=True)
|
||||
image = serializers.CharField(source='get_image_url', read_only=True)
|
||||
on_order = serializers.FloatField(read_only=True)
|
||||
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
used_in = serializers.IntegerField(source='used_in_count', read_only=True)
|
||||
@staticmethod
|
||||
def prefetch_queryset(queryset):
|
||||
return queryset.prefetch_related(
|
||||
'category',
|
||||
'stock_items',
|
||||
'bom_items',
|
||||
'builds',
|
||||
'supplier_parts',
|
||||
'supplier_parts__purchase_order_line_items',
|
||||
'supplier_parts__purcahes_order_line_items__order'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def setup_eager_loading(queryset):
|
||||
queryset = queryset.prefetch_related('category')
|
||||
queryset = queryset.prefetch_related('stock_items')
|
||||
queryset = queryset.prefetch_related('bom_items')
|
||||
queryset = queryset.prefetch_related('builds')
|
||||
def annotate_queryset(queryset):
|
||||
"""
|
||||
Add some extra annotations to the queryset,
|
||||
performing database queries as efficiently as possible,
|
||||
to reduce database trips.
|
||||
"""
|
||||
|
||||
# Filter to limit stock items to "available"
|
||||
stock_filter = Q(stock_items__status__in=StockStatus.AVAILABLE_CODES)
|
||||
|
||||
# Filter to limit orders to "open"
|
||||
order_filter = Q(supplier_parts__purchase_order_line_items__order__status__in=OrderStatus.OPEN)
|
||||
|
||||
# Filter to limit builds to "active"
|
||||
build_filter = Q(builds__status__in=BuildStatus.ACTIVE_CODES)
|
||||
|
||||
# Annotate the number total stock count
|
||||
queryset = queryset.annotate(
|
||||
in_stock=Coalesce(Sum('stock_items__quantity', filter=stock_filter, distinct=True), Decimal(0))
|
||||
)
|
||||
|
||||
# Annotate the number of parts "on order"
|
||||
# Total "on order" parts = "Quantity" - "Received" for each active purchase order
|
||||
queryset = queryset.annotate(
|
||||
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))
|
||||
)
|
||||
|
||||
# Annotate number of parts being build
|
||||
queryset = queryset.annotate(
|
||||
building=Coalesce(
|
||||
Sum('builds__quantity', filter=build_filter, distinct=True), Decimal(0)
|
||||
)
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
in_stock = serializers.FloatField(read_only=True)
|
||||
ordering = serializers.FloatField(read_only=True)
|
||||
building = serializers.FloatField(read_only=True)
|
||||
|
||||
#allocated_stock = serializers.FloatField(source='allocation_count', read_only=True)
|
||||
#bom_items = serializers.IntegerField(source='bom_count', read_only=True)
|
||||
#building = serializers.FloatField(source='quantity_being_built', read_only=False)
|
||||
#category_name = serializers.CharField(source='category_path', read_only=True)
|
||||
image = serializers.CharField(source='get_image_url', read_only=True)
|
||||
#on_order = serializers.FloatField(read_only=True)
|
||||
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
#used_in = serializers.IntegerField(source='used_in_count', read_only=True)
|
||||
|
||||
# TODO - Include a 'category_detail' field which serializers the category object
|
||||
|
||||
class Meta:
|
||||
@ -103,31 +162,34 @@ class PartSerializer(InvenTreeModelSerializer):
|
||||
partial = True
|
||||
fields = [
|
||||
'active',
|
||||
'allocated_stock',
|
||||
#'allocated_stock',
|
||||
'assembly',
|
||||
'bom_items',
|
||||
'building',
|
||||
#'bom_items',
|
||||
#'building',
|
||||
'category',
|
||||
'category_name',
|
||||
#'category_name',
|
||||
'component',
|
||||
'description',
|
||||
'full_name',
|
||||
'image',
|
||||
'in_stock',
|
||||
'ordering',
|
||||
'building',
|
||||
'IPN',
|
||||
'is_template',
|
||||
'keywords',
|
||||
'link',
|
||||
'name',
|
||||
'notes',
|
||||
'on_order',
|
||||
#'on_order',
|
||||
'pk',
|
||||
'purchaseable',
|
||||
'salable',
|
||||
'thumbnail',
|
||||
'trackable',
|
||||
'total_stock',
|
||||
#'total_stock',
|
||||
'units',
|
||||
'used_in',
|
||||
#'used_in',
|
||||
'url', # Link to the part detail page
|
||||
'variant_of',
|
||||
'virtual',
|
||||
|
Reference in New Issue
Block a user