mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	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:
		| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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', | ||||
|     ) | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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')) | ||||
|   | ||||
| @@ -317,6 +317,8 @@ | ||||
|                 params: { | ||||
|                     {% if category %} | ||||
|                     parent: {{ category.pk }}, | ||||
|                     {% else %} | ||||
|                     top_level: true, | ||||
|                     {% endif %} | ||||
|                 }, | ||||
|                 allowTreeView: true, | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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')) | ||||
|   | ||||
| @@ -280,6 +280,8 @@ | ||||
|             params: { | ||||
|                 {% if location %} | ||||
|                 parent: {{ location.pk }}, | ||||
|                 {% else %} | ||||
|                 top_level: true, | ||||
|                 {% endif %} | ||||
|             }, | ||||
|             allowTreeView: true, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user