Top level cascade (#7514)

* Add "top_level" filter for PartCategory API endpoint

* Update CUI tables

* Update PUI table

* Similar updates for stock location table

* Fix "parent" field label

* Bump API version
This commit is contained in:
Oliver 2024-06-26 12:28:33 +10:00 committed by GitHub
parent 81ae9026b2
commit 937824dceb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 66 additions and 7 deletions

View File

@ -1,11 +1,15 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 208
INVENTREE_API_VERSION = 209
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v209 - 2024-06-26 : https://github.com/inventree/InvenTree/pull/7514
- Add "top_level" filter to PartCategory API endpoint
- Add "top_level" filter to StockLocation API endpoint
v208 - 2024-06-19 : https://github.com/inventree/InvenTree/pull/7479
- Adds documentation for the user roles API endpoint (no functional changes)

View File

@ -779,7 +779,7 @@ class InvenTreeTree(MetadataMixin, PluginValidationMixin, MPTTModel):
on_delete=models.DO_NOTHING,
blank=True,
null=True,
verbose_name=_('parent'),
verbose_name='parent',
related_name='children',
)

View File

@ -137,6 +137,21 @@ class CategoryFilter(rest_filters.FilterSet):
return queryset
top_level = rest_filters.BooleanFilter(
label=_('Top Level'),
method='filter_top_level',
help_text=_('Filter by top-level categories'),
)
def filter_top_level(self, queryset, name, value):
"""Filter by top-level categories."""
cascade = str2bool(self.data.get('cascade', False))
if value and not cascade:
return queryset.filter(parent=None)
return queryset
cascade = rest_filters.BooleanFilter(
label=_('Cascade'),
method='filter_cascade',
@ -148,10 +163,11 @@ class CategoryFilter(rest_filters.FilterSet):
Note: If the "parent" filter is provided, we offload the logic to that method.
"""
parent = self.data.get('parent', None)
parent = str2bool(self.data.get('parent', None))
top_level = str2bool(self.data.get('top_level', None))
# If the parent is *not* provided, update the results based on the "cascade" value
if not parent:
if not parent or top_level:
if not value:
# If "cascade" is False, only return top-level categories
queryset = queryset.filter(parent=None)

View File

@ -111,6 +111,14 @@ class CategorySerializer(InvenTree.serializers.InvenTreeModelSerializer):
return queryset
parent = serializers.PrimaryKeyRelatedField(
queryset=PartCategory.objects.all(),
required=False,
allow_null=True,
label=_('Parent Category'),
help_text=_('Parent part category'),
)
url = serializers.CharField(source='get_absolute_url', read_only=True)
part_count = serializers.IntegerField(read_only=True, label=_('Parts'))

View File

@ -317,6 +317,8 @@
params: {
{% if category %}
parent: {{ category.pk }},
{% else %}
top_level: true,
{% endif %}
},
allowTreeView: true,

View File

@ -326,6 +326,21 @@ class StockLocationFilter(rest_filters.FilterSet):
return queryset
top_level = rest_filters.BooleanFilter(
label=_('Top Level'),
method='filter_top_level',
help_text=_('Filter by top-level locations'),
)
def filter_top_level(self, queryset, name, value):
"""Filter by top-level locations."""
cascade = str2bool(self.data.get('cascade', False))
if value and not cascade:
return queryset.filter(parent=None)
return queryset
cascade = rest_filters.BooleanFilter(
label=_('Cascade'),
method='filter_cascade',
@ -338,9 +353,10 @@ class StockLocationFilter(rest_filters.FilterSet):
Note: If the "parent" filter is provided, we offload the logic to that method.
"""
parent = self.data.get('parent', None)
top_level = str2bool(self.data.get('top_level', None))
# If the parent is *not* provided, update the results based on the "cascade" value
if not parent:
if not parent or top_level:
if not value:
# If "cascade" is False, only return top-level location
queryset = queryset.filter(parent=None)

View File

@ -1077,6 +1077,15 @@ class LocationSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
return queryset
parent = serializers.PrimaryKeyRelatedField(
queryset=StockLocation.objects.all(),
many=False,
allow_null=True,
required=False,
label=_('Parent Location'),
help_text=_('Parent stock location'),
)
url = serializers.CharField(source='get_absolute_url', read_only=True)
items = serializers.IntegerField(read_only=True, label=_('Stock Items'))

View File

@ -280,6 +280,8 @@
params: {
{% if location %}
parent: {{ location.pk }},
{% else %}
top_level: true,
{% endif %}
},
allowTreeView: true,

View File

@ -130,7 +130,8 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
props={{
enableDownload: true,
params: {
parent: parentId
parent: parentId,
top_level: parentId === undefined ? true : undefined
},
tableFilters: tableFilters,
tableActions: tableActions,

View File

@ -145,7 +145,8 @@ export function StockLocationTable({ parentId }: { parentId?: any }) {
enableLabels: true,
enableReports: true,
params: {
parent: parentId
parent: parentId,
top_level: parentId === undefined ? true : undefined
},
tableFilters: tableFilters,
tableActions: tableActions,