2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-07 06:00:57 +00:00

[UI] Location filter (#9939)

* Filter incomplete outputs by location

* Filter build allocated stock by location

* Filter sales order allocations by location

* Bump API version

* Fix API version

* Fix annotations
This commit is contained in:
Oliver
2025-07-04 10:24:12 +10:00
committed by GitHub
parent a954555eb7
commit e7b27c9e2f
8 changed files with 54 additions and 4 deletions

View File

@ -1,12 +1,16 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 361
INVENTREE_API_VERSION = 362
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v362 -> 2025-07-02 : https://github.com/inventree/InvenTree/pull/9939
- Allow filtering of BuildItem API by "location" of StockItem
- Allow filtering of SalesOrderAllocation API by "location" of StockItem
v361 -> 2025-07-03 : https://github.com/inventree/InvenTree/pull/9944
- Enable SalesOrderAllocation list to be filtered by part IPN value
- Enable SalesOrderAllocation list to be ordered by part MPN value

View File

@ -16,6 +16,7 @@ from rest_framework.response import Response
import build.serializers
import common.models
import part.models as part_models
import stock.models as stock_models
import stock.serializers
from build.models import Build, BuildItem, BuildLine
from build.status_codes import BuildStatus, BuildStatusGroups
@ -830,6 +831,18 @@ class BuildItemFilter(rest_filters.FilterSet):
return queryset.exclude(install_into=None)
return queryset.filter(install_into=None)
location = rest_filters.ModelChoiceFilter(
queryset=stock_models.StockLocation.objects.all(),
label=_('Location'),
method='filter_location',
)
@extend_schema_field(serializers.IntegerField(help_text=_('Location')))
def filter_location(self, queryset, name, location):
"""Filter the queryset based on the specified location."""
locations = location.get_descendants(include_self=True)
return queryset.filter(stock_item__location__in=locations)
class BuildItemList(DataExportViewMixin, BulkDeleteMixin, ListCreateAPI):
"""API endpoint for accessing a list of BuildItem objects.

View File

@ -23,6 +23,7 @@ import build.models
import common.models
import common.settings
import company.models
import stock.models as stock_models
from data_exporter.mixins import DataExportViewMixin
from generic.states.api import StatusView
from InvenTree.api import BulkUpdateMixin, ListCreateDestroyAPIView, MetadataView
@ -1178,6 +1179,20 @@ class SalesOrderAllocationFilter(rest_filters.FilterSet):
return queryset.exclude(shipment=None)
return queryset.filter(shipment=None)
location = rest_filters.ModelChoiceFilter(
queryset=stock_models.StockLocation.objects.all(),
label=_('Location'),
method='filter_location',
)
@extend_schema_field(
rest_framework.serializers.IntegerField(help_text=_('Location'))
)
def filter_location(self, queryset, name, location):
"""Filter by the location of the allocated StockItem."""
locations = location.get_descendants(include_self=True)
return queryset.filter(item__location__in=locations)
class SalesOrderAllocationMixin:
"""Mixin class for SalesOrderAllocation endpoints."""

View File

@ -350,3 +350,14 @@ export function PartCategoryFilter(): TableFilter {
modelRenderer: (instance: any) => instance.name
};
}
export function StockLocationFilter(): TableFilter {
return {
name: 'location',
label: t`Location`,
description: t`Filter by stock location`,
apiUrl: apiUrl(ApiEndpoints.stock_location_list),
model: ModelType.stocklocation,
modelRenderer: (instance: any) => instance.name
};
}

View File

@ -21,6 +21,7 @@ import {
ReferenceColumn,
StatusColumn
} from '../ColumnRenderers';
import { StockLocationFilter } from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable';
import { type RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
@ -69,6 +70,8 @@ export default function BuildAllocatedStockTable({
});
}
filters.push(StockLocationFilter());
return filters;
}, [partId]);

View File

@ -57,7 +57,8 @@ import {
SerialFilter,
SerialGTEFilter,
SerialLTEFilter,
StatusFilterOptions
StatusFilterOptions,
StockLocationFilter
} from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable';
import { type RowAction, RowEditAction, RowViewAction } from '../RowActions';
@ -363,6 +364,7 @@ export default function BuildOutputTable({
description: t`Filter by stock status`,
choiceFunction: StatusFilterOptions(ModelType.stockitem)
},
StockLocationFilter(),
HasBatchCodeFilter(),
BatchFilter(),
IsSerializedFilter(),

View File

@ -27,6 +27,7 @@ import {
ReferenceColumn,
StatusColumn
} from '../ColumnRenderers';
import { StockLocationFilter } from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable';
import { type RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
@ -84,7 +85,8 @@ export default function SalesOrderAllocationTable({
name: 'assigned_to_shipment',
label: t`Assigned to Shipment`,
description: t`Show allocations assigned to a shipment`
}
},
StockLocationFilter()
];
if (!!partId) {

View File

@ -486,7 +486,7 @@ export function StockItemTable({
[showLocation, showPricing]
);
const tableFilters = useMemo(
const tableFilters: TableFilter[] = useMemo(
() =>
stockItemTableFilters({
enableExpiry: stockExpiryEnabled