diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 416138846116..adf6b0c03729 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,12 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 230 +INVENTREE_API_VERSION = 231 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v231 - 2024-08-03 : https://github.com/inventree/InvenTree/pull/7794 + - Optimize BuildItem and BuildLine serializers to improve API efficiency + v230 - 2024-05-05 : https://github.com/inventree/InvenTree/pull/7164 - Adds test statistics endpoint diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index 5aacea397d3f..167b447c2c6a 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -575,19 +575,23 @@ def get_serializer(self, *args, **kwargs): return self.serializer_class(*args, **kwargs) def get_queryset(self): - """Override the queryset method, to allow filtering by stock_item.part.""" + """Override the queryset method, to perform custom prefetch.""" queryset = super().get_queryset() queryset = queryset.select_related( 'build_line', 'build_line__build', 'build_line__bom_item', + 'build_line__bom_item__part', + 'build_line__bom_item__sub_part', 'install_into', 'stock_item', 'stock_item__location', 'stock_item__part', - 'stock_item__supplier_part', + 'stock_item__supplier_part__part', + 'stock_item__supplier_part__supplier', 'stock_item__supplier_part__manufacturer_part', + 'stock_item__supplier_part__manufacturer_part__manufacturer', ).prefetch_related( 'stock_item__location__tags', ) diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index 390cabab92d8..e961d281446b 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -23,7 +23,7 @@ from stock.generators import generate_batch_code from stock.models import StockItem, StockLocation -from stock.serializers import StockItemSerializerBrief, LocationSerializer +from stock.serializers import StockItemSerializerBrief, LocationBriefSerializer import common.models from common.serializers import ProjectCodeSerializer @@ -1064,6 +1064,8 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali # These fields are only used for data export export_only_fields = [ + 'bom_part_id', + 'bom_part_name', 'build_reference', 'sku', 'mpn', @@ -1071,9 +1073,11 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali 'part_id', 'part_name', 'part_ipn', + 'part_description', 'available_quantity', 'item_batch_code', 'item_serial', + 'item_packaging', ] class Meta: @@ -1097,6 +1101,8 @@ class Meta: # The following fields are only used for data export 'bom_reference', + 'bom_part_id', + 'bom_part_name', 'build_reference', 'location_name', 'mpn', @@ -1104,9 +1110,11 @@ class Meta: 'part_id', 'part_name', 'part_ipn', + 'part_description', 'available_quantity', 'item_batch_code', 'item_serial_number', + 'item_packaging', ] def __init__(self, *args, **kwargs): @@ -1136,11 +1144,17 @@ def __init__(self, *args, **kwargs): location_name = serializers.CharField(source='stock_item.location.name', label=_('Location Name'), read_only=True) build_reference = serializers.CharField(source='build.reference', label=_('Build Reference'), read_only=True) bom_reference = serializers.CharField(source='build_line.bom_item.reference', label=_('BOM Reference'), read_only=True) + item_packaging = serializers.CharField(source='stock_item.packaging', label=_('Packaging'), read_only=True) # Part detail fields part_id = serializers.PrimaryKeyRelatedField(source='stock_item.part', label=_('Part ID'), many=False, read_only=True) part_name = serializers.CharField(source='stock_item.part.name', label=_('Part Name'), read_only=True) part_ipn = serializers.CharField(source='stock_item.part.IPN', label=_('Part IPN'), read_only=True) + part_description = serializers.CharField(source='stock_item.part.description', label=_('Part Description'), read_only=True) + + # BOM Item Part ID (it may be different to the allocated part) + bom_part_id = serializers.PrimaryKeyRelatedField(source='build_line.bom_item.sub_part', label=_('BOM Part ID'), many=False, read_only=True) + bom_part_name = serializers.CharField(source='build_line.bom_item.sub_part.name', label=_('BOM Part Name'), read_only=True) item_batch_code = serializers.CharField(source='stock_item.batch', label=_('Batch Code'), read_only=True) item_serial_number = serializers.CharField(source='stock_item.serial', label=_('Serial Number'), read_only=True) @@ -1152,9 +1166,9 @@ def __init__(self, *args, **kwargs): part_detail = part_serializers.PartBriefSerializer(source='stock_item.part', many=False, read_only=True, pricing=False) stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True) location = serializers.PrimaryKeyRelatedField(source='stock_item.location', many=False, read_only=True) - location_detail = LocationSerializer(source='stock_item.location', read_only=True) + location_detail = LocationBriefSerializer(source='stock_item.location', read_only=True) build_detail = BuildSerializer(source='build_line.build', many=False, read_only=True) - supplier_part_detail = company.serializers.SupplierPartSerializer(source='stock_item.supplier_part', many=False, read_only=True) + supplier_part_detail = company.serializers.SupplierPartSerializer(source='stock_item.supplier_part', many=False, read_only=True, brief=True) quantity = InvenTreeDecimalField(label=_('Allocated Quantity')) available_quantity = InvenTreeDecimalField(source='stock_item.quantity', read_only=True, label=_('Available Quantity')) @@ -1243,7 +1257,7 @@ class Meta: # Foreign key fields bom_item_detail = part_serializers.BomItemSerializer(source='bom_item', many=False, read_only=True, pricing=False) - part_detail = part_serializers.PartSerializer(source='bom_item.sub_part', many=False, read_only=True, pricing=False) + part_detail = part_serializers.PartBriefSerializer(source='bom_item.sub_part', many=False, read_only=True, pricing=False) allocations = BuildItemSerializer(many=True, read_only=True) # Annotated (calculated) fields @@ -1289,16 +1303,20 @@ def annotate_queryset(queryset, build=None): """ queryset = queryset.select_related( - 'build', 'bom_item', + 'build', + 'bom_item', + 'bom_item__part', + 'bom_item__part__pricing_data', + 'bom_item__sub_part', + 'bom_item__sub_part__pricing_data', ) # Pre-fetch related fields queryset = queryset.prefetch_related( - 'bom_item__sub_part', + 'bom_item__sub_part__tags', 'bom_item__sub_part__stock_items', 'bom_item__sub_part__stock_items__allocations', 'bom_item__sub_part__stock_items__sales_order_allocations', - 'bom_item__sub_part__tags', 'bom_item__substitutes', 'bom_item__substitutes__part__stock_items', @@ -1310,6 +1328,11 @@ def annotate_queryset(queryset, build=None): 'allocations__stock_item__part', 'allocations__stock_item__location', 'allocations__stock_item__location__tags', + 'allocations__stock_item__supplier_part', + 'allocations__stock_item__supplier_part__part', + 'allocations__stock_item__supplier_part__supplier', + 'allocations__stock_item__supplier_part__manufacturer_part', + 'allocations__stock_item__supplier_part__manufacturer_part__manufacturer', ) # Annotate the "allocated" quantity diff --git a/src/backend/InvenTree/company/serializers.py b/src/backend/InvenTree/company/serializers.py index 428ea7b5b855..5123b3f1e880 100644 --- a/src/backend/InvenTree/company/serializers.py +++ b/src/backend/InvenTree/company/serializers.py @@ -381,9 +381,14 @@ def __init__(self, *args, **kwargs): self.fields.pop('manufacturer_detail', None) self.fields.pop('manufacturer_part_detail', None) - if prettify is not True: + if brief or prettify is not True: self.fields.pop('pretty_name', None) + if brief: + self.fields.pop('tags') + self.fields.pop('available') + self.fields.pop('availability_updated') + # Annotated field showing total in-stock quantity in_stock = serializers.FloatField(read_only=True, label=_('In Stock')) diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index d22e6151a64a..49be598d888e 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -1195,6 +1195,10 @@ def get_queryset(self, *args, **kwargs): queryset = part_serializers.PartSerializer.annotate_queryset(queryset) + # Annotate with parameter template data? + if str2bool(self.request.query_params.get('parameters', False)): + queryset = queryset.prefetch_related('parameters', 'parameters__template') + return queryset def get_serializer(self, *args, **kwargs): diff --git a/src/backend/InvenTree/stock/serializers.py b/src/backend/InvenTree/stock/serializers.py index e92c133eb72c..8f83b59751e1 100644 --- a/src/backend/InvenTree/stock/serializers.py +++ b/src/backend/InvenTree/stock/serializers.py @@ -506,7 +506,10 @@ def annotate_queryset(queryset): 'part__category', 'part__pricing_data', 'supplier_part', + 'supplier_part__part', + 'supplier_part__supplier', 'supplier_part__manufacturer_part', + 'supplier_part__manufacturer_part__manufacturer', 'supplier_part__tags', 'test_results', 'tags',