mirror of
https://github.com/inventree/InvenTree.git
synced 2026-07-21 22:23:03 +00:00
@@ -0,0 +1,132 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import type { ReactNode } from 'react';
|
||||
import { formatDecimal } from '../../defaults/formatters';
|
||||
import { TableHoverCard } from './TableHoverCard';
|
||||
|
||||
export function renderPartStockCell(record: any): ReactNode {
|
||||
if (record.virtual) {
|
||||
return (
|
||||
<Text size='sm' c='dimmed' fs='italic'>
|
||||
{t`Virtual part`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const extra: ReactNode[] = [];
|
||||
|
||||
const stock = record?.total_in_stock ?? 0;
|
||||
const allocated =
|
||||
(record?.allocated_to_build_orders ?? 0) +
|
||||
(record?.allocated_to_sales_orders ?? 0);
|
||||
const available = Math.max(0, stock - allocated);
|
||||
const min_stock = record?.minimum_stock ?? 0;
|
||||
const max_stock = record?.maximum_stock ?? 0;
|
||||
|
||||
let text = String(formatDecimal(stock));
|
||||
|
||||
let color: string | undefined = undefined;
|
||||
|
||||
if (min_stock > stock) {
|
||||
extra.push(
|
||||
<Text key='min-stock' c='orange'>
|
||||
{`${t`Minimum stock`}: ${formatDecimal(min_stock)}`}
|
||||
</Text>
|
||||
);
|
||||
|
||||
color = 'orange';
|
||||
}
|
||||
|
||||
if (max_stock > 0 && stock > max_stock) {
|
||||
extra.push(
|
||||
<Text key='max-stock' c='teal'>
|
||||
{`${t`Maximum stock`}: ${formatDecimal(max_stock)}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.ordering > 0) {
|
||||
extra.push(
|
||||
<Text key='on-order'>{`${t`On Order`}: ${formatDecimal(record.ordering)}`}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.building) {
|
||||
extra.push(
|
||||
<Text key='building'>{`${t`Building`}: ${formatDecimal(record.building)}`}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.allocated_to_build_orders > 0) {
|
||||
extra.push(
|
||||
<Text key='bo-allocations'>
|
||||
{`${t`Build Order Allocations`}: ${formatDecimal(record.allocated_to_build_orders)}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.allocated_to_sales_orders > 0) {
|
||||
extra.push(
|
||||
<Text key='so-allocations'>
|
||||
{`${t`Sales Order Allocations`}: ${formatDecimal(record.allocated_to_sales_orders)}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (available != stock) {
|
||||
extra.push(
|
||||
<Text key='available'>
|
||||
{t`Available`}: {formatDecimal(available)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.external_stock > 0) {
|
||||
extra.push(
|
||||
<Text key='external'>
|
||||
{t`External stock`}: {formatDecimal(record.external_stock)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if ((record.variant_stock ?? 0) > 0) {
|
||||
extra.push(
|
||||
<Text key='variant-stock' size='sm'>
|
||||
{t`Includes variant stock`}: {formatDecimal(record.variant_stock)}
|
||||
</Text>
|
||||
);
|
||||
extra.push(
|
||||
<Text key='direct-stock' size='sm'>
|
||||
{t`Direct stock`}: {formatDecimal(record.in_stock ?? 0)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (stock <= 0) {
|
||||
color = 'red';
|
||||
text = t`No stock`;
|
||||
} else if (available <= 0) {
|
||||
color = 'orange';
|
||||
} else if (available < min_stock) {
|
||||
color = 'yellow';
|
||||
}
|
||||
|
||||
return (
|
||||
<TableHoverCard
|
||||
value={
|
||||
<Group gap='xs' justify='left' wrap='nowrap'>
|
||||
<Text c={color} size='sm'>
|
||||
{text}
|
||||
</Text>
|
||||
{record.units && (
|
||||
<Text size='xs' c={color}>
|
||||
[{record.units}]
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
}
|
||||
title={t`Stock Information`}
|
||||
extra={extra}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
DescriptionColumn,
|
||||
PartColumn
|
||||
} from '../../components/tables/ColumnRenderers';
|
||||
import { renderPartStockCell } from '../../components/tables/PartStockCell';
|
||||
import ParametricDataTable from '../general/ParametricDataTable';
|
||||
import { PartTableFilters } from './PartTableFilters';
|
||||
|
||||
@@ -33,7 +34,8 @@ export default function ParametricPartTable({
|
||||
},
|
||||
{
|
||||
accessor: 'total_in_stock',
|
||||
sortable: true
|
||||
sortable: true,
|
||||
render: renderPartStockCell
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
|
||||
@@ -12,14 +12,13 @@ import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||
import type { TableColumn } from '@lib/types/Tables';
|
||||
import type { InvenTreeTableProps } from '@lib/types/Tables';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import {
|
||||
IconFileUpload,
|
||||
IconPackageImport,
|
||||
IconPlus,
|
||||
IconShoppingCart
|
||||
} from '@tabler/icons-react';
|
||||
import { type ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { ActionDropdown } from '../../components/items/ActionDropdown';
|
||||
import {
|
||||
BooleanColumn,
|
||||
@@ -31,10 +30,10 @@ import {
|
||||
PartColumn
|
||||
} from '../../components/tables/ColumnRenderers';
|
||||
import { InvenTreeTable } from '../../components/tables/InvenTreeTable';
|
||||
import { TableHoverCard } from '../../components/tables/TableHoverCard';
|
||||
import { renderPartStockCell } from '../../components/tables/PartStockCell';
|
||||
import ImportPartWizard from '../../components/wizards/ImportPartWizard';
|
||||
import OrderPartsWizard from '../../components/wizards/OrderPartsWizard';
|
||||
import { formatDecimal, formatPriceRange } from '../../defaults/formatters';
|
||||
import { formatPriceRange } from '../../defaults/formatters';
|
||||
import { DuplicateField } from '../../forms/CommonFields';
|
||||
import { dataImporterSessionFields } from '../../forms/ImporterForms';
|
||||
import { usePartFields } from '../../forms/PartForms';
|
||||
@@ -84,119 +83,7 @@ function partTableColumns(): TableColumn[] {
|
||||
accessor: 'total_in_stock',
|
||||
sortable: true,
|
||||
filter: ['has_stock', 'low_stock', 'high_stock'],
|
||||
render: (record) => {
|
||||
if (record.virtual) {
|
||||
return (
|
||||
<Text size='sm' c='dimmed' fs='italic'>
|
||||
{t`Virtual part`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const extra: ReactNode[] = [];
|
||||
|
||||
const stock = record?.total_in_stock ?? 0;
|
||||
const allocated =
|
||||
(record?.allocated_to_build_orders ?? 0) +
|
||||
(record?.allocated_to_sales_orders ?? 0);
|
||||
const available = Math.max(0, stock - allocated);
|
||||
const min_stock = record?.minimum_stock ?? 0;
|
||||
const max_stock = record?.maximum_stock ?? 0;
|
||||
|
||||
let text = String(formatDecimal(stock));
|
||||
|
||||
let color: string | undefined = undefined;
|
||||
|
||||
if (min_stock > stock) {
|
||||
extra.push(
|
||||
<Text key='min-stock' c='orange'>
|
||||
{`${t`Minimum stock`}: ${formatDecimal(min_stock)}`}
|
||||
</Text>
|
||||
);
|
||||
|
||||
color = 'orange';
|
||||
}
|
||||
|
||||
if (max_stock > 0 && stock > max_stock) {
|
||||
extra.push(
|
||||
<Text key='max-stock' c='teal'>
|
||||
{`${t`Maximum stock`}: ${formatDecimal(max_stock)}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.ordering > 0) {
|
||||
extra.push(
|
||||
<Text key='on-order'>{`${t`On Order`}: ${formatDecimal(record.ordering)}`}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.building) {
|
||||
extra.push(
|
||||
<Text key='building'>{`${t`Building`}: ${formatDecimal(record.building)}`}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.allocated_to_build_orders > 0) {
|
||||
extra.push(
|
||||
<Text key='bo-allocations'>
|
||||
{`${t`Build Order Allocations`}: ${formatDecimal(record.allocated_to_build_orders)}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.allocated_to_sales_orders > 0) {
|
||||
extra.push(
|
||||
<Text key='so-allocations'>
|
||||
{`${t`Sales Order Allocations`}: ${formatDecimal(record.allocated_to_sales_orders)}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (available != stock) {
|
||||
extra.push(
|
||||
<Text key='available'>
|
||||
{t`Available`}: {formatDecimal(available)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.external_stock > 0) {
|
||||
extra.push(
|
||||
<Text key='external'>
|
||||
{t`External stock`}: {formatDecimal(record.external_stock)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (stock <= 0) {
|
||||
color = 'red';
|
||||
text = t`No stock`;
|
||||
} else if (available <= 0) {
|
||||
color = 'orange';
|
||||
} else if (available < min_stock) {
|
||||
color = 'yellow';
|
||||
}
|
||||
|
||||
return (
|
||||
<TableHoverCard
|
||||
value={
|
||||
<Group gap='xs' justify='left' wrap='nowrap'>
|
||||
<Text c={color} size='sm'>
|
||||
{text}
|
||||
</Text>
|
||||
{record.units && (
|
||||
<Text size='xs' c={color}>
|
||||
[{record.units}]
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
}
|
||||
title={t`Stock Information`}
|
||||
extra={extra}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render: renderPartStockCell
|
||||
},
|
||||
{
|
||||
accessor: 'price_range',
|
||||
|
||||
Reference in New Issue
Block a user