diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index ec19418475..5cd04449c7 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,36 +1,39 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 303 +INVENTREE_API_VERSION = 304 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ -v303 - 2025-01-20 - https://github.com/inventree/InvenTree/pull/8915 +v304 - 2025-01-22 : https://github.com/inventree/InvenTree/pull/8940 + - Adds "category" filter to build list API + +v303 - 2025-01-20 : https://github.com/inventree/InvenTree/pull/8915 - Adds "start_date" field to Build model and API endpoints - Adds additional API filtering and sorting options for Build list -v302 - 2025-01-18 - https://github.com/inventree/InvenTree/pull/8905 +v302 - 2025-01-18 : https://github.com/inventree/InvenTree/pull/8905 - Fix schema definition on the /label/print endpoint -v301 - 2025-01-14 - https://github.com/inventree/InvenTree/pull/8894 +v301 - 2025-01-14 : https://github.com/inventree/InvenTree/pull/8894 - Remove ui preferences from the API -v300 - 2025-01-13 - https://github.com/inventree/InvenTree/pull/8886 +v300 - 2025-01-13 : https://github.com/inventree/InvenTree/pull/8886 - Allow null value for 'expiry_date' field introduced in #8867 -v299 - 2025-01-10 - https://github.com/inventree/InvenTree/pull/8867 +v299 - 2025-01-10 : https://github.com/inventree/InvenTree/pull/8867 - Adds 'expiry_date' field to the PurchaseOrderReceive API endpoint - Adds 'default_expiry` field to the PartBriefSerializer, affecting API endpoints which use it -v298 - 2025-01-07 - https://github.com/inventree/InvenTree/pull/8848 +v298 - 2025-01-07 : https://github.com/inventree/InvenTree/pull/8848 - Adds 'created_by' field to PurchaseOrder API endpoints - Adds 'created_by' field to SalesOrder API endpoints - Adds 'created_by' field to ReturnOrder API endpoints -v297 - 2024-12-29 - https://github.com/inventree/InvenTree/pull/8438 +v297 - 2024-12-29 : https://github.com/inventree/InvenTree/pull/8438 - Adjustments to the CustomUserState API endpoints and serializers v296 - 2024-12-25 : https://github.com/inventree/InvenTree/pull/8732 diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index 455548effb..4f8274b5ea 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -13,7 +13,7 @@ from rest_framework.exceptions import ValidationError import build.admin import build.serializers import common.models -import part.models +import part.models as part_models from build.models import Build, BuildItem, BuildLine from build.status_codes import BuildStatus, BuildStatusGroups from generic.states.api import StatusView @@ -77,7 +77,10 @@ class BuildFilter(rest_filters.FilterSet): return queryset part = rest_filters.ModelChoiceFilter( - queryset=part.models.Part.objects.all(), field_name='part', method='filter_part' + queryset=part_models.Part.objects.all(), + field_name='part', + method='filter_part', + label=_('Part'), ) def filter_part(self, queryset, name, part): @@ -94,6 +97,17 @@ class BuildFilter(rest_filters.FilterSet): else: return queryset.filter(part=part) + category = rest_filters.ModelChoiceFilter( + queryset=part_models.PartCategory.objects.all(), + method='filter_category', + label=_('Category'), + ) + + def filter_category(self, queryset, name, category): + """Filter by part category (including sub-categories).""" + categories = category.get_descendants(include_self=True) + return queryset.filter(part__category__in=categories) + ancestor = rest_filters.ModelChoiceFilter( queryset=Build.objects.all(), label=_('Ancestor Build'), @@ -417,7 +431,7 @@ class BuildLineFilter(rest_filters.FilterSet): ) part = rest_filters.ModelChoiceFilter( - queryset=part.models.Part.objects.all(), + queryset=part_models.Part.objects.all(), label=_('Part'), field_name='bom_item__sub_part', ) @@ -729,7 +743,7 @@ class BuildItemFilter(rest_filters.FilterSet): return queryset part = rest_filters.ModelChoiceFilter( - queryset=part.models.Part.objects.all(), + queryset=part_models.Part.objects.all(), label=_('Part'), method='filter_part', field_name='stock_item__part', diff --git a/src/frontend/src/tables/FilterSelectDrawer.tsx b/src/frontend/src/tables/FilterSelectDrawer.tsx index cdb82344cc..c209d30d04 100644 --- a/src/frontend/src/tables/FilterSelectDrawer.tsx +++ b/src/frontend/src/tables/FilterSelectDrawer.tsx @@ -179,10 +179,17 @@ function FilterAddGroup({ // Determine the "type" of filter (default = boolean) const filterType: TableFilterType = useMemo(() => { - return ( - availableFilters?.find((flt) => flt.name === selectedFilter)?.type ?? - 'boolean' - ); + const filter = availableFilters?.find((flt) => flt.name === selectedFilter); + + if (filter?.type) { + return filter.type; + } else if (filter?.choices) { + // If choices are provided, it is a choice filter + return 'choice'; + } else { + // Default fallback + return 'boolean'; + } }, [selectedFilter]); const setSelectedValue = useCallback( diff --git a/src/frontend/src/tables/build/BuildOrderTable.tsx b/src/frontend/src/tables/build/BuildOrderTable.tsx index faa96952ff..9aae96df0a 100644 --- a/src/frontend/src/tables/build/BuildOrderTable.tsx +++ b/src/frontend/src/tables/build/BuildOrderTable.tsx @@ -8,7 +8,9 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { useBuildOrderFields } from '../../forms/BuildForms'; +import { shortenString } from '../../functions/tables'; import { + useFilters, useOwnerFilters, useProjectCodeFilters, useUserFilters @@ -131,6 +133,17 @@ export function BuildOrderTable({ const ownerFilters = useOwnerFilters(); const userFilters = useUserFilters(); + const categoryFilters = useFilters({ + url: apiUrl(ApiEndpoints.category_list), + transform: (item) => ({ + value: item.pk, + label: shortenString({ + str: item.pathstring, + len: 50 + }) + }) + }); + const tableFilters: TableFilter[] = useMemo(() => { const filters: TableFilter[] = [ OutstandingFilter(), @@ -177,7 +190,13 @@ export function BuildOrderTable({ description: t`Filter by user who issued this order`, choices: userFilters.choices }, - ResponsibleFilter({ choices: ownerFilters.choices }) + ResponsibleFilter({ choices: ownerFilters.choices }), + { + name: 'category', + label: t`Category`, + description: t`Filter by part category`, + choices: categoryFilters.choices + } ]; // If we are filtering on a specific part, we can include the "include variants" filter @@ -193,6 +212,7 @@ export function BuildOrderTable({ return filters; }, [ partId, + categoryFilters.choices, projectCodeFilters.choices, ownerFilters.choices, userFilters.choices