2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-17 18:26:32 +00:00

BOM filter by category (#9989)

* Add "category" filter for BomItem API endpoint

* Filter BOM table by part category

* Tweak filter label

* Bump API version

* Schema annotation

* Fix playwright test
This commit is contained in:
Oliver
2025-07-09 21:46:07 +10:00
committed by GitHub
parent 6f08bdca46
commit 7ff2ca914a
5 changed files with 25 additions and 4 deletions

View File

@@ -1,12 +1,15 @@
"""InvenTree API version information.""" """InvenTree API version information."""
# InvenTree API version # InvenTree API version
INVENTREE_API_VERSION = 365 INVENTREE_API_VERSION = 366
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" """Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """ INVENTREE_API_TEXT = """
v366 -> 2025-07-09 : https://github.com/inventree/InvenTree/pull/9987
- Adds "category" filter to BomItem API endpoint
v365 -> 2025-07-09 : https://github.com/inventree/InvenTree/pull/9984 v365 -> 2025-07-09 : https://github.com/inventree/InvenTree/pull/9984
- Allow filtering of DataOutput API by "user" field - Allow filtering of DataOutput API by "user" field
- Allow individual deletion of DataOutput objects via the API - Allow individual deletion of DataOutput objects via the API

View File

@@ -1631,10 +1631,24 @@ class BomFilter(rest_filters.FilterSet):
queryset=Part.objects.all(), method='filter_part', label=_('Part') queryset=Part.objects.all(), method='filter_part', label=_('Part')
) )
@extend_schema_field(OpenApiTypes.INT)
def filter_part(self, queryset, name, part): def filter_part(self, queryset, name, part):
"""Filter the queryset based on the specified part.""" """Filter the queryset based on the specified part."""
return queryset.filter(part.get_bom_item_filter()) return queryset.filter(part.get_bom_item_filter())
category = rest_filters.ModelChoiceFilter(
queryset=PartCategory.objects.all(),
method='filter_category',
label=_('Category'),
)
@extend_schema_field(OpenApiTypes.INT)
def filter_category(self, queryset, name, category):
"""Filter the queryset based on the specified PartCategory."""
cats = category.get_descendants(include_self=True)
return queryset.filter(sub_part__category__in=cats)
uses = rest_filters.ModelChoiceFilter( uses = rest_filters.ModelChoiceFilter(
queryset=Part.objects.all(), method='filter_uses', label=_('Uses') queryset=Part.objects.all(), method='filter_uses', label=_('Uses')
) )

View File

@@ -343,7 +343,7 @@ export function IssuedByFilter(): TableFilter {
export function PartCategoryFilter(): TableFilter { export function PartCategoryFilter(): TableFilter {
return { return {
name: 'category', name: 'category',
label: t`Category`, label: t`Part Category`,
description: t`Filter by part category`, description: t`Filter by part category`,
apiUrl: apiUrl(ApiEndpoints.category_list), apiUrl: apiUrl(ApiEndpoints.category_list),
model: ModelType.partcategory, model: ModelType.partcategory,

View File

@@ -42,6 +42,7 @@ import {
NoteColumn, NoteColumn,
ReferenceColumn ReferenceColumn
} from '../ColumnRenderers'; } from '../ColumnRenderers';
import { PartCategoryFilter } from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable'; import { InvenTreeTable } from '../InvenTreeTable';
import { type RowAction, RowDeleteAction, RowEditAction } from '../RowActions'; import { type RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
import { TableHoverCard } from '../TableHoverCard'; import { TableHoverCard } from '../TableHoverCard';
@@ -369,7 +370,8 @@ export function BomTable({
name: 'has_pricing', name: 'has_pricing',
label: t`Has Pricing`, label: t`Has Pricing`,
description: t`Show items with pricing` description: t`Show items with pricing`
} },
PartCategoryFilter()
]; ];
}, [partId, params]); }, [partId, params]);

View File

@@ -109,7 +109,9 @@ test('Build Order - Calendar', async ({ browser }) => {
await page.getByLabel('calendar-select-filters').click(); await page.getByLabel('calendar-select-filters').click();
await page.getByRole('button', { name: 'Add Filter' }).click(); await page.getByRole('button', { name: 'Add Filter' }).click();
await page.getByPlaceholder('Select filter').fill('category'); await page.getByPlaceholder('Select filter').fill('category');
await page.getByRole('option', { name: 'Category', exact: true }).click(); await page
.getByRole('option', { name: 'Part Category', exact: true })
.click();
await page.getByLabel('related-field-filter-category').click(); await page.getByLabel('related-field-filter-category').click();
await page.getByText('Part category, level 1').waitFor(); await page.getByText('Part category, level 1').waitFor();