2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-09-14 14:41:33 +00:00

[PUI] Part allocations (#8458)

* Add new backend filters for BuildLine API

* PUI: Better display of part allocations against build orders

* Add 'order_outstanding' filter to SalesOrderLineItem API

* Add new table showing outstanding SalesOrder allocations against a part

* Update playwright test

* Cleanup

* Bump API version

* Add more table columns

* Tweak UsedInTable

* Another table tweak

* Tweak playwright tests
This commit is contained in:
Oliver
2024-11-09 20:22:26 +11:00
committed by GitHub
parent ad39d3fd95
commit 255a5d083e
15 changed files with 457 additions and 81 deletions

View File

@@ -1,13 +1,17 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 278
INVENTREE_API_VERSION = 279
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v279 - 2024-11-09 : https://github.com/inventree/InvenTree/pull/8458
- Adds "order_outstanding" and "part" filters to the BuildLine API endpoint
- Adds "order_outstanding" filter to the SalesOrderLineItem API endpoint
v278 - 2024-11-07 : https://github.com/inventree/InvenTree/pull/8445
- Updates to the SalesOrder API endpoints
- Add "shipment count" information to the SalesOrder API endpoints

View File

@@ -357,6 +357,23 @@ class BuildLineFilter(rest_filters.FilterSet):
tracked = rest_filters.BooleanFilter(label=_('Tracked'), field_name='bom_item__sub_part__trackable')
testable = rest_filters.BooleanFilter(label=_('Testable'), field_name='bom_item__sub_part__testable')
part = rest_filters.ModelChoiceFilter(
queryset=part.models.Part.objects.all(),
label=_('Part'),
field_name='bom_item__sub_part',
)
order_outstanding = rest_filters.BooleanFilter(
label=_('Order Outstanding'),
method='filter_order_outstanding'
)
def filter_order_outstanding(self, queryset, name, value):
"""Filter by whether the associated BuildOrder is 'outstanding'."""
if str2bool(value):
return queryset.filter(build__status__in=BuildStatusGroups.ACTIVE_CODES)
return queryset.exclude(build__status__in=BuildStatusGroups.ACTIVE_CODES)
allocated = rest_filters.BooleanFilter(label=_('Allocated'), method='filter_allocated')
def filter_allocated(self, queryset, name, value):
@@ -383,12 +400,28 @@ class BuildLineFilter(rest_filters.FilterSet):
return queryset.exclude(flt)
class BuildLineEndpoint:
"""Mixin class for BuildLine API endpoints."""
queryset = BuildLine.objects.all()
serializer_class = build.serializers.BuildLineSerializer
def get_serializer(self, *args, **kwargs):
"""Return the serializer instance for this endpoint."""
kwargs['context'] = self.get_serializer_context()
try:
params = self.request.query_params
kwargs['part_detail'] = str2bool(params.get('part_detail', True))
kwargs['build_detail'] = str2bool(params.get('build_detail', False))
except AttributeError:
pass
return self.serializer_class(*args, **kwargs)
def get_source_build(self) -> Build:
"""Return the source Build object for the BuildLine queryset.

View File

@@ -1278,8 +1278,6 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'pk',
'build',
'bom_item',
'bom_item_detail',
'part_detail',
'quantity',
# Build detail fields
@@ -1315,6 +1313,11 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
# Extra fields only for data export
'part_description',
'part_category_name',
# Extra detail (related field) serializers
'bom_item_detail',
'part_detail',
'build_detail',
]
read_only_fields = [
@@ -1323,6 +1326,19 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'allocations',
]
def __init__(self, *args, **kwargs):
"""Determine which extra details fields should be included"""
part_detail = kwargs.pop('part_detail', True)
build_detail = kwargs.pop('build_detail', False)
super().__init__(*args, **kwargs)
if not part_detail:
self.fields.pop('part_detail', None)
if not build_detail:
self.fields.pop('build_detail', None)
# Build info fields
build_reference = serializers.CharField(source='build.reference', label=_('Build Reference'), read_only=True)
@@ -1362,6 +1378,7 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
)
part_detail = part_serializers.PartBriefSerializer(source='bom_item.sub_part', many=False, read_only=True, pricing=False)
build_detail = BuildSerializer(source='build', part_detail=False, many=False, read_only=True)
# Annotated (calculated) fields
@@ -1404,9 +1421,13 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
"""
queryset = queryset.select_related(
'build',
'build__part',
'build__part__pricing_data',
'bom_item',
'bom_item__part',
'bom_item__part__pricing_data',
'bom_item__sub_part',
'bom_item__sub_part__pricing_data'
)
# Pre-fetch related fields

View File

@@ -816,6 +816,17 @@ class SalesOrderLineItemFilter(LineItemFilter):
return queryset.exclude(order__status__in=SalesOrderStatusGroups.COMPLETE)
order_outstanding = rest_filters.BooleanFilter(
label=_('Order Outstanding'), method='filter_order_outstanding'
)
def filter_order_outstanding(self, queryset, name, value):
"""Filter by whether the order is 'outstanding' or not."""
if str2bool(value):
return queryset.filter(order__status__in=SalesOrderStatusGroups.OPEN)
return queryset.exclude(order__status__in=SalesOrderStatusGroups.OPEN)
class SalesOrderLineItemMixin:
"""Mixin class for SalesOrderLineItem endpoints."""