2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-06-12 11:38:47 +00:00

Maximum stock level (#11914)

* Add "maximum_stock" field

* Add API filter

* Update API version

* Update CHANGELOG

* Frontend updates

* Add dashboard widget

* docs

* Add API tests
This commit is contained in:
Oliver
2026-05-10 20:55:05 +10:00
committed by GitHub
parent bb2a72a6fb
commit 35aa4d33d3
14 changed files with 181 additions and 7 deletions
@@ -70,6 +70,17 @@ function BuiltinQueryCountWidgets(): DashboardWidgetProps[] {
virtual: false
}
}),
QueryCountDashboardWidget({
title: t`High Stock`,
label: 'hgh-stk',
description: t`Show the number of parts which have excess stock`,
modelType: ModelType.part,
params: {
active: true,
high_stock: true,
virtual: false
}
}),
QueryCountDashboardWidget({
title: t`Required for Build Orders`,
label: 'bld-req',
@@ -35,6 +35,10 @@ export function RenderPart(
} else if (stock != null) {
badgeText = `${t`Stock`}: ${formatDecimal(stock)}`;
badgeColor = instance.minimum_stock > stock ? 'yellow' : 'green';
if (instance.maximum_stock > 0 && stock > instance.maximum_stock) {
badgeColor = 'teal';
}
}
const extra: ReactNode[] = [];
+1
View File
@@ -57,6 +57,7 @@ export function usePartFields({
},
default_expiry: {},
minimum_stock: {},
maximum_stock: {},
responsible: {
filters: {
is_active: true
+1
View File
@@ -120,6 +120,7 @@ const icons: InvenTreeIconType = {
unallocated_stock: IconPackage,
total_in_stock: IconPackages,
minimum_stock: IconFlag,
maximum_stock: IconFlag,
allocated_to_build_orders: IconTool,
allocated_to_sales_orders: IconTruck,
can_build: IconTools,
+19 -5
View File
@@ -454,6 +454,13 @@ export default function PartDetail() {
unit: part.units,
label: t`Minimum Stock`,
hidden: part.minimum_stock <= 0
},
{
type: 'number',
name: 'maximum_stock',
unit: part.units,
label: t`Maximum Stock`,
hidden: part.maximum_stock <= 0
}
];
@@ -875,14 +882,21 @@ export default function PartDetail() {
const shortfall = Math.max(required - partRequirements.total_stock, 0);
let stockColor = 'green';
if (partRequirements.total_stock <= part.minimum_stock) {
stockColor = 'orange';
} else if (
part.maximum_stock > 0 &&
partRequirements.total_stock > part.maximum_stock
) {
stockColor = 'teal';
}
return [
<DetailsBadge
label={`${t`In Stock`}: ${formatDecimal(partRequirements.total_stock)}`}
color={
partRequirements.total_stock >= part.minimum_stock
? 'green'
: 'orange'
}
color={stockColor}
visible={!part.virtual && partRequirements.total_stock > 0}
key='in_stock'
/>,
@@ -97,6 +97,7 @@ function partTableColumns(): TableColumn[] {
(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));
@@ -112,6 +113,14 @@ function partTableColumns(): TableColumn[] {
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>
@@ -273,6 +282,12 @@ function partTableFilters(): TableFilter[] {
description: t`Filter by parts which have low stock`,
type: 'boolean'
},
{
name: 'high_stock',
label: t`High Stock`,
description: t`Filter by parts which have high stock`,
type: 'boolean'
},
{
name: 'purchaseable',
label: t`Purchaseable`,