mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Child item table (#6334)
* Add child stock item table * Fix stock item splitting bug - StockItem tree was not being rebuilt correctly - Add unit tests * Annotate StockItem serializer with "child_items" count * Show or hide "child items" panel * Account for case where tree_id is zero
This commit is contained in:
parent
65ecb975c6
commit
0f2675c139
@ -655,6 +655,16 @@ class StockFilter(rest_filters.FilterSet):
|
|||||||
return queryset.filter(installed_items__gt=0)
|
return queryset.filter(installed_items__gt=0)
|
||||||
return queryset.filter(installed_items=0)
|
return queryset.filter(installed_items=0)
|
||||||
|
|
||||||
|
has_child_items = rest_filters.BooleanFilter(
|
||||||
|
label='Has child items', method='filter_has_child_items'
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_has_child_items(self, queryset, name, value):
|
||||||
|
"""Filter stock items by "belongs_to" field being empty."""
|
||||||
|
if str2bool(value):
|
||||||
|
return queryset.filter(child_items__gt=0)
|
||||||
|
return queryset.filter(child_items=0)
|
||||||
|
|
||||||
sent_to_customer = rest_filters.BooleanFilter(
|
sent_to_customer = rest_filters.BooleanFilter(
|
||||||
label='Sent to customer', method='filter_sent_to_customer'
|
label='Sent to customer', method='filter_sent_to_customer'
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
from django.db.models import F, Func, IntegerField, OuterRef, Q, Subquery
|
from django.db.models import F, Func, IntegerField, OuterRef, Q, Subquery
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
|
||||||
|
from sql_util.utils import SubqueryCount
|
||||||
|
|
||||||
import stock.models
|
import stock.models
|
||||||
|
|
||||||
|
|
||||||
@ -33,3 +35,23 @@ def annotate_location_items(filter: Q = None):
|
|||||||
0,
|
0,
|
||||||
output_field=IntegerField(),
|
output_field=IntegerField(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def annotate_child_items():
|
||||||
|
"""Construct a queryset annotation which returns the number of children below a certain StockItem node in a StockItem tree."""
|
||||||
|
child_stock_query = stock.models.StockItem.objects.filter(
|
||||||
|
tree_id=OuterRef('tree_id'),
|
||||||
|
lft__gt=OuterRef('lft'),
|
||||||
|
rght__lt=OuterRef('rght'),
|
||||||
|
level__gte=OuterRef('level'),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Coalesce(
|
||||||
|
Subquery(
|
||||||
|
child_stock_query.annotate(
|
||||||
|
count=Func(F('pk'), function='COUNT', output_field=IntegerField())
|
||||||
|
).values('count')
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
output_field=IntegerField(),
|
||||||
|
)
|
||||||
|
@ -170,6 +170,7 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
|||||||
'allocated',
|
'allocated',
|
||||||
'expired',
|
'expired',
|
||||||
'installed_items',
|
'installed_items',
|
||||||
|
'child_items',
|
||||||
'stale',
|
'stale',
|
||||||
'tracking_items',
|
'tracking_items',
|
||||||
'tags',
|
'tags',
|
||||||
@ -288,6 +289,9 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
|||||||
# Annotate with the total number of "installed items"
|
# Annotate with the total number of "installed items"
|
||||||
queryset = queryset.annotate(installed_items=SubqueryCount('installed_parts'))
|
queryset = queryset.annotate(installed_items=SubqueryCount('installed_parts'))
|
||||||
|
|
||||||
|
# Annotate with the total number of "child items" (split stock items)
|
||||||
|
queryset = queryset.annotate(child_items=stock.filters.annotate_child_items())
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||||
@ -315,6 +319,7 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
|||||||
allocated = serializers.FloatField(required=False)
|
allocated = serializers.FloatField(required=False)
|
||||||
expired = serializers.BooleanField(required=False, read_only=True)
|
expired = serializers.BooleanField(required=False, read_only=True)
|
||||||
installed_items = serializers.IntegerField(read_only=True, required=False)
|
installed_items = serializers.IntegerField(read_only=True, required=False)
|
||||||
|
child_items = serializers.IntegerField(read_only=True, required=False)
|
||||||
stale = serializers.BooleanField(required=False, read_only=True)
|
stale = serializers.BooleanField(required=False, read_only=True)
|
||||||
tracking_items = serializers.IntegerField(read_only=True, required=False)
|
tracking_items = serializers.IntegerField(read_only=True, required=False)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Alert, LoadingOverlay, Stack, Text } from '@mantine/core';
|
import { Alert, LoadingOverlay, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconBookmark,
|
IconBookmark,
|
||||||
IconBoxPadding,
|
IconBoxPadding,
|
||||||
@ -34,6 +34,7 @@ import { PageDetail } from '../../components/nav/PageDetail';
|
|||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { ApiPaths } from '../../enums/ApiEndpoints';
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { useEditStockItem } from '../../forms/StockForms';
|
import { useEditStockItem } from '../../forms/StockForms';
|
||||||
@ -94,14 +95,18 @@ export default function StockDetail() {
|
|||||||
name: 'installed_items',
|
name: 'installed_items',
|
||||||
label: t`Installed Items`,
|
label: t`Installed Items`,
|
||||||
icon: <IconBoxPadding />,
|
icon: <IconBoxPadding />,
|
||||||
content: <PlaceholderPanel />,
|
|
||||||
hidden: !stockitem?.part_detail?.assembly
|
hidden: !stockitem?.part_detail?.assembly
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'child_items',
|
name: 'child_items',
|
||||||
label: t`Child Items`,
|
label: t`Child Items`,
|
||||||
icon: <IconSitemap />,
|
icon: <IconSitemap />,
|
||||||
content: <PlaceholderPanel />
|
hidden: (stockitem?.child_items ?? 0) == 0,
|
||||||
|
content: stockitem?.pk ? (
|
||||||
|
<StockItemTable params={{ ancestor: stockitem.pk }} />
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'attachments',
|
name: 'attachments',
|
||||||
|
Loading…
Reference in New Issue
Block a user