mirror of
https://github.com/inventree/InvenTree.git
synced 2026-07-28 01:17:21 +00:00
On order filter (#12476)
* Add "on order" filter for Part model - Added API filter - Added table filter - Added table column * Bump API version * Add table sorting * Adjust API text
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 528
|
||||
INVENTREE_API_VERSION = 529
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v529 -> 2026-07-26 : https://github.com/inventree/InvenTree/pull/12471
|
||||
- Adds "on_order" filter to the Part API
|
||||
- Enable sorting by "ordering" quantity on the Part API endpoint
|
||||
|
||||
v528 -> 2026-07-26 : https://github.com/inventree/InvenTree/pull/12469
|
||||
- Additional ordering options for SalesOrderAllocation API endpoint
|
||||
|
||||
|
||||
@@ -784,6 +784,15 @@ class PartFilter(FilterSet):
|
||||
return queryset.filter(Q(unallocated_stock__gt=0))
|
||||
return queryset.filter(Q(unallocated_stock__lte=0))
|
||||
|
||||
# on_order filter
|
||||
on_order = rest_filters.BooleanFilter(label='On order', method='filter_on_order')
|
||||
|
||||
def filter_on_order(self, queryset, name, value):
|
||||
"""Filter by whether the Part has any stock on order."""
|
||||
if str2bool(value):
|
||||
return queryset.filter(Q(ordering__gt=0))
|
||||
return queryset.filter(Q(ordering__lte=0))
|
||||
|
||||
convert_from = rest_filters.ModelChoiceFilter(
|
||||
label='Can convert from',
|
||||
queryset=Part.objects.all(),
|
||||
@@ -1096,6 +1105,7 @@ class PartList(
|
||||
'name',
|
||||
'creation_date',
|
||||
'IPN',
|
||||
'ordering',
|
||||
'in_stock',
|
||||
'total_in_stock',
|
||||
'unallocated_stock',
|
||||
|
||||
@@ -2782,6 +2782,25 @@ class PartAPIAggregationTest(InvenTreeAPITestCase):
|
||||
# The annotated quantity must also match the part.on_order quantity
|
||||
self.assertEqual(on_order, p.on_order)
|
||||
|
||||
# Test the 'on_order' filter
|
||||
response = self.get(
|
||||
reverse('api-part-list'),
|
||||
{'category': paint.pk, 'on_order': True},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
for item in response.data:
|
||||
self.assertGreater(item['ordering'], 0)
|
||||
|
||||
response = self.get(
|
||||
reverse('api-part-list'),
|
||||
{'category': paint.pk, 'on_order': False},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
for item in response.data:
|
||||
self.assertLessEqual(item['ordering'], 0)
|
||||
|
||||
def test_building(self):
|
||||
"""Test the 'building' quantity annotations."""
|
||||
# Create a new "buildable" part
|
||||
|
||||
@@ -29,7 +29,7 @@ export function renderPartStockCell(record: any): ReactNode {
|
||||
|
||||
if (min_stock > stock) {
|
||||
extra.push(
|
||||
<Text key='min-stock' c='orange'>
|
||||
<Text key='min-stock' c='orange' size='sm'>
|
||||
{`${t`Minimum stock`}: ${formatDecimal(min_stock)}`}
|
||||
</Text>
|
||||
);
|
||||
@@ -39,7 +39,7 @@ export function renderPartStockCell(record: any): ReactNode {
|
||||
|
||||
if (max_stock > 0 && stock > max_stock) {
|
||||
extra.push(
|
||||
<Text key='max-stock' c='teal'>
|
||||
<Text key='max-stock' c='teal' size='sm'>
|
||||
{`${t`Maximum stock`}: ${formatDecimal(max_stock)}`}
|
||||
</Text>
|
||||
);
|
||||
@@ -47,19 +47,25 @@ export function renderPartStockCell(record: any): ReactNode {
|
||||
|
||||
if (record.ordering > 0) {
|
||||
extra.push(
|
||||
<Text key='on-order'>{`${t`On Order`}: ${formatDecimal(record.ordering)}`}</Text>
|
||||
<Text
|
||||
key='on-order'
|
||||
size='sm'
|
||||
>{`${t`On Order`}: ${formatDecimal(record.ordering)}`}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.building) {
|
||||
extra.push(
|
||||
<Text key='building'>{`${t`Building`}: ${formatDecimal(record.building)}`}</Text>
|
||||
<Text
|
||||
key='building'
|
||||
size='sm'
|
||||
>{`${t`Building`}: ${formatDecimal(record.building)}`}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.allocated_to_build_orders > 0) {
|
||||
extra.push(
|
||||
<Text key='bo-allocations'>
|
||||
<Text key='bo-allocations' size='sm'>
|
||||
{`${t`Build Order Allocations`}: ${formatDecimal(record.allocated_to_build_orders)}`}
|
||||
</Text>
|
||||
);
|
||||
@@ -67,7 +73,7 @@ export function renderPartStockCell(record: any): ReactNode {
|
||||
|
||||
if (record.allocated_to_sales_orders > 0) {
|
||||
extra.push(
|
||||
<Text key='so-allocations'>
|
||||
<Text key='so-allocations' size='sm'>
|
||||
{`${t`Sales Order Allocations`}: ${formatDecimal(record.allocated_to_sales_orders)}`}
|
||||
</Text>
|
||||
);
|
||||
@@ -75,7 +81,7 @@ export function renderPartStockCell(record: any): ReactNode {
|
||||
|
||||
if (available != stock) {
|
||||
extra.push(
|
||||
<Text key='available'>
|
||||
<Text key='available' size='sm'>
|
||||
{t`Available`}: {formatDecimal(available)}
|
||||
</Text>
|
||||
);
|
||||
@@ -83,7 +89,7 @@ export function renderPartStockCell(record: any): ReactNode {
|
||||
|
||||
if (record.external_stock > 0) {
|
||||
extra.push(
|
||||
<Text key='external'>
|
||||
<Text key='external' size='sm'>
|
||||
{t`External stock`}: {formatDecimal(record.external_stock)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ import { ActionDropdown } from '../../components/items/ActionDropdown';
|
||||
import {
|
||||
BooleanColumn,
|
||||
CategoryColumn,
|
||||
DecimalColumn,
|
||||
DefaultLocationColumn,
|
||||
DescriptionColumn,
|
||||
IPNColumn,
|
||||
@@ -85,6 +86,13 @@ function partTableColumns(): TableColumn[] {
|
||||
filter: ['has_stock', 'low_stock', 'high_stock'],
|
||||
render: renderPartStockCell
|
||||
},
|
||||
DecimalColumn({
|
||||
accessor: 'ordering',
|
||||
title: t`On Order`,
|
||||
filter: 'on_order',
|
||||
sortable: true,
|
||||
defaultVisible: false
|
||||
}),
|
||||
{
|
||||
accessor: 'price_range',
|
||||
title: t`Price Range`,
|
||||
|
||||
@@ -144,6 +144,12 @@ export function PartTableFilters(): TableFilter[] {
|
||||
description: t`Filter by parts which have available stock`,
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'on_order',
|
||||
label: t`On Order`,
|
||||
description: t`Filter by parts which are on order`,
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'starred',
|
||||
label: t`Subscribed`,
|
||||
|
||||
Reference in New Issue
Block a user