mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Stock location detail
This commit is contained in:
parent
2ec7b74f6d
commit
ca34e97ab8
@ -886,6 +886,7 @@ class LocationSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
||||
'pathstring',
|
||||
'path',
|
||||
'items',
|
||||
'sublocations',
|
||||
'owner',
|
||||
'icon',
|
||||
'custom_icon',
|
||||
@ -911,13 +912,18 @@ class LocationSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
|
||||
def annotate_queryset(queryset):
|
||||
"""Annotate extra information to the queryset."""
|
||||
# Annotate the number of stock items which exist in this category (including subcategories)
|
||||
queryset = queryset.annotate(items=stock.filters.annotate_location_items())
|
||||
queryset = queryset.annotate(
|
||||
items=stock.filters.annotate_location_items(),
|
||||
sublocations=stock.filters.annotate_sub_locations(),
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
|
||||
items = serializers.IntegerField(read_only=True)
|
||||
items = serializers.IntegerField(read_only=True, label=_('Stock Items'))
|
||||
|
||||
sublocations = serializers.IntegerField(read_only=True, label=_('Sublocations'))
|
||||
|
||||
level = serializers.IntegerField(read_only=True)
|
||||
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
IconRulerMeasure,
|
||||
IconShoppingCart,
|
||||
IconShoppingCartHeart,
|
||||
IconSitemap,
|
||||
IconStack2,
|
||||
IconStatusChange,
|
||||
IconTag,
|
||||
@ -138,7 +139,8 @@ const icons: { [key: string]: (props: TablerIconsProps) => React.JSX.Element } =
|
||||
reference: IconHash,
|
||||
website: IconWorld,
|
||||
email: IconMail,
|
||||
phone: IconPhone
|
||||
phone: IconPhone,
|
||||
sitemap: IconSitemap
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { LoadingOverlay, Stack, Text } from '@mantine/core';
|
||||
import { IconPackages, IconSitemap } from '@tabler/icons-react';
|
||||
import { LoadingOverlay, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { IconInfoCircle, IconPackages, IconSitemap } from '@tabler/icons-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { PageDetail } from '../../components/nav/PageDetail';
|
||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { useInstance } from '../../hooks/UseInstance';
|
||||
import { DetailsField, DetailsTable } from '../../tables/Details';
|
||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||
import { StockLocationTable } from '../../tables/stock/StockLocationTable';
|
||||
|
||||
@ -35,8 +38,90 @@ export default function Stock() {
|
||||
}
|
||||
});
|
||||
|
||||
const detailsPanel = useMemo(() => {
|
||||
if (id && instanceQuery.isFetching) {
|
||||
return <Skeleton />;
|
||||
}
|
||||
|
||||
let left: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: t`Name`,
|
||||
copy: true
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'pathstring',
|
||||
label: t`Path`,
|
||||
icon: 'sitemap',
|
||||
copy: true,
|
||||
hidden: !id
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
label: t`Description`,
|
||||
copy: true
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'parent',
|
||||
model_field: 'name',
|
||||
icon: 'location',
|
||||
label: t`Parent Location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !location?.parent
|
||||
}
|
||||
];
|
||||
|
||||
let right: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'items',
|
||||
icon: 'stock',
|
||||
label: t`Stock Items`
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'sublocations',
|
||||
icon: 'location',
|
||||
label: t`Sublocations`,
|
||||
hidden: !location?.sublocations
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'structural',
|
||||
label: t`Structural`,
|
||||
icon: 'sitemap'
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
name: 'external',
|
||||
label: t`External`
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
{id && location ? (
|
||||
<DetailsTable item={location} fields={left} />
|
||||
) : (
|
||||
<Text>{t`Top level stock location`}</Text>
|
||||
)}
|
||||
{id && location && <DetailsTable item={location} fields={right} />}
|
||||
</ItemDetailsGrid>
|
||||
);
|
||||
}, [location, instanceQuery]);
|
||||
|
||||
const locationPanels: PanelType[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
name: 'details',
|
||||
label: t`Location Details`,
|
||||
icon: <IconInfoCircle />,
|
||||
content: detailsPanel
|
||||
},
|
||||
{
|
||||
name: 'stock-items',
|
||||
label: t`Stock Items`,
|
||||
|
@ -276,13 +276,13 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
||||
* If user is defined, a badge is rendered in addition to main value
|
||||
*/
|
||||
function TableStringValue(props: FieldProps) {
|
||||
let value = props.field_value;
|
||||
let value = props?.field_value ?? {};
|
||||
|
||||
if (props.field_data.value_formatter) {
|
||||
if (props.field_data?.value_formatter) {
|
||||
value = props.field_data.value_formatter();
|
||||
}
|
||||
|
||||
if (props.field_data.badge) {
|
||||
if (props.field_data?.badge) {
|
||||
return <NameBadge pk={value} type={props.field_data.badge} />;
|
||||
}
|
||||
|
||||
@ -290,12 +290,12 @@ function TableStringValue(props: FieldProps) {
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Suspense fallback={<Skeleton width={200} height={20} radius="xl" />}>
|
||||
<span>
|
||||
{value ? value : props.field_data.unit && '0'}{' '}
|
||||
{value ? value : props.field_data?.unit && '0'}{' '}
|
||||
{props.field_data.unit == true && props.unit}
|
||||
</span>
|
||||
</Suspense>
|
||||
{props.field_data.user && (
|
||||
<NameBadge pk={props.field_data.user} type="user" />
|
||||
<NameBadge pk={props.field_data?.user} type="user" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user