Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#10887](https://github.com/inventree/InvenTree/pull/10887) adds the ability to auto-allocate tracked items against specific build outputs. Currently, this will only allocate items where the serial number of the tracked item matches the serial number of the build output, but in future this may be extended to allow for more flexible allocation rules.
- [#11372](https://github.com/inventree/InvenTree/pull/11372) adds backup metadata setter and restore metadata validator functions to ensure common footguns are harder to trigger when using the backup and restore functionality.
- [#11374](https://github.com/inventree/InvenTree/pull/11374) adds `updated_at` field on purchase, sales and return orders.
- [#11459](https://github.com/inventree/InvenTree/pull/11459) adds parameter support for the StockItem model

### Changed

Expand Down
12 changes: 6 additions & 6 deletions docs/docs/concepts/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ Parameter templates are created and edited via the [admin interface](../settings
To create a template:

- Navigate to the "Settings" page
- Click on the "Part Parameters" tab
- Click on the "Parameters" tab
- Click on the "New Parameter" button
- Fill out the `Create Part Parameter Template` form: `Name` (required) and `Units` (optional) fields
- Fill out the `Create Parameter Template` form: `Name` (required) and `Units` (optional) fields
- Click on the "Submit" button.

An existing template can be edited by clicking on the "Edit" button associated with that template:
Expand All @@ -53,9 +53,9 @@ An existing template can be edited by clicking on the "Edit" button associated w

After [creating a template](#create-template) or using the existing templates, you can add parameters to any part.

To add a parameter, navigate to a specific part detail page, click on the "Parameters" tab then click on the "New Parameters" button, the `Create Part Parameter` form will be displayed:
To add a parameter, navigate to a specific part detail page, click on the "Parameters" tab then click on the "New Parameters" button, the `Create Parameter` form will be displayed:

{{ image("part/create_part_parameter.png", "Create Part Parameter Form") }}
{{ image("part/create_part_parameter.png", "Create Parameter Form") }}

Select the parameter `Template` you would like to use for this parameter, fill-out the `Data` field (value of this specific parameter) and click the "Submit" button.

Expand Down Expand Up @@ -132,7 +132,7 @@ The in-built conversion functionality means that parameter values can be input i

### Incompatible Units

If a part parameter is created with a value which is incompatible with the units specified for the template, it will be rejected:
If a parameter is created with a value which is incompatible with the units specified for the template, it will be rejected:

{{ image("part/part_invalid_units.png", "Invalid Parameter Units") }}

Expand All @@ -151,4 +151,4 @@ Selection Lists can be used to add a large number of predefined values to a para
It is possible that plugins lock selection lists to ensure a known state.


Administration of lists can be done through the Part Parameter section in the [Admin Center](../settings/admin.md#admin-center) or via the API.
Administration of lists can be done through the Parameters section in the [Admin Center](../settings/admin.md#admin-center) or via the API.
4 changes: 2 additions & 2 deletions docs/docs/concepts/units.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Support for real-world "physical" units of measure is implemented using the [pin

- Ensures consistent use of real units for your inventory management
- Convert between compatible units of measure from suppliers
- Enforce use of compatible units when creating part parameters
- Enforce use of compatible units when creating parameters
- Enable custom units as required

### Unit Conversion
Expand Down Expand Up @@ -61,7 +61,7 @@ The [supplier part](../part/index.md/#supplier-parts) model uses real-world unit

### Parameter

The [parameter template](../concepts/parameters.md#parameter-templates) model can specify units of measure, and part parameters can be specified against these templates with compatible units
The [parameter template](../concepts/parameters.md#parameter-templates) model can specify units of measure, and parameters can be specified against these templates with compatible units

## Custom Units

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/concepts/user_interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ This will display the data in a calendar format:

## Parametric Views

Some [table views](#table-views) can be switched to a parametric view, which provides a visual representation of data based on specific parameters or attributes. The parametric view allows users to easily see and interact with data that is organized by certain characteristics, such as categories, types, or other relevant attributes.
Some [table views](#table-views) can be switched to a parametric view, which provides a visual representation of data based on specific [parameters attributes](./parameters.md). The parametric view allows users to easily see and interact with data that is organized by certain characteristics, such as categories, types, or other relevant attributes.

To switch to the "parametric view" (for a table which supports it), click on the "parametric view" button located above and to the right of the table view:

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/part/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ New parts can be created manually by selecting the *Create Part* option from the
{{ image("part/part_create_form.png", "New part form") }}


Fill out the required part parameters and then press *Submit* to create the new part. If there are any form errors, you must fix these before the form can be successfully submitted.
Fill out the required part attributes and then press *Submit* to create the new part. If there are any form errors, you must fix these before the form can be successfully submitted.

Once the form is completed, the browser window is redirected to the new part detail page.

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/part/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Parts can be locked to prevent them from being modified. This is useful for part

- Locked parts cannot be deleted
- BOM items cannot be created, edited, or deleted when they are part of a locked assembly
- Part parameters linked to a locked part cannot be created, edited or deleted
- Parameters linked to a locked part cannot be created, edited or deleted

## Active Parts

Expand Down
5 changes: 4 additions & 1 deletion src/backend/InvenTree/InvenTree/api_version.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""InvenTree API version information."""

# InvenTree API version
INVENTREE_API_VERSION = 460
INVENTREE_API_VERSION = 461
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""

INVENTREE_API_TEXT = """

v461 -> 2026-03-04 : https://github.com/inventree/InvenTree/pull/11459
- Adds "parameter" support for StockItem API endpoints

v460 -> 2026-02-25 : https://github.com/inventree/InvenTree/pull/11374
- Adds "updated_at" field to PurchaseOrder, SalesOrder and ReturnOrder API endpoints
- Adds "updated_before" and "updated_after" date filters to all three order list endpoints
Expand Down
1 change: 1 addition & 0 deletions src/backend/InvenTree/stock/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ class StockItemReportContext(report.mixins.BaseReportContext):

class StockItem(
InvenTree.models.PluginValidationMixin,
InvenTree.models.InvenTreeParameterMixin,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just applying the default business logic for parameters and parameter template <> stock item is not really intuitive.

As far as I can see this just enabled parameters but no mechanisms to handle the splitting / merging of stock items (as regularly happens) and what should happen to parameters in that case. That leaves holes compared to the default usage flow for stock items.

Was this considered, and should this limitation be documented or addressed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll chime in since I started the discussion thread. FWIW, I didn't consider stock splitting/merging. I avoid using merging at all in my workflow due to it destroying history in the merge. Since merging is already destructive, I'd vote that the "winning" stock item retains its original parameters as with the rest of history and document that. That's just my $0.02.

Splitting is an interesting, and I'd say new, case. For my use cases I'd expect split items to inherit their parents' parameters. Now that raises the question is it a one-time copy-on-split, or do child stock items dynamically inherit their parents parameters? I honestly don't have a good opinion on this and it feels like this has nuance depending on use case. Either way, I'd expect stock items to inherit parent parameters via some method.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought more about this, and my vote is to do a "copy-on-split" of params into the split child stock item. new/changed params on the parent after that time don't reflect in the child. I think that is both most clear to document and implement. Again, just my $0.02.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I may, as the second requester: merging stock can be blocked altogether if there are custom parameters present that have different values, same as there are options to block for mismatched suppliers and states.

InvenTree.models.InvenTreeAttachmentMixin,
InvenTree.models.InvenTreeBarcodeMixin,
InvenTree.models.InvenTreeNotesMixin,
Expand Down
3 changes: 3 additions & 0 deletions src/backend/InvenTree/stock/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ class Meta:
'stale',
'tracking_items',
'tags',
'parameters',
# Detail fields (FK relationships)
'supplier_part_detail',
'part_detail',
Expand Down Expand Up @@ -657,6 +658,8 @@ def annotate_queryset(queryset):

tags = common.filters.enable_tags_filter()

parameters = common.filters.enable_parameters_filter()


class SerializeStockItemSerializer(serializers.Serializer):
"""A DRF serializer for "serializing" a StockItem.
Expand Down
44 changes: 31 additions & 13 deletions src/frontend/src/pages/stock/LocationDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { useInstance } from '../../hooks/UseInstance';
import { useStockAdjustActions } from '../../hooks/UseStockAdjustActions';
import { useUserState } from '../../states/UserState';
import { PartListTable } from '../../tables/part/PartTable';
import ParametricStockItemTable from '../../tables/stock/ParametricStockItemTable';
import { StockItemTable } from '../../tables/stock/StockItemTable';
import StockLocationParametricTable from '../../tables/stock/StockLocationParametricTable';
import { StockLocationTable } from '../../tables/stock/StockLocationTable';
Expand Down Expand Up @@ -171,6 +172,7 @@ export default function Stock() {
}, [location, instanceQuery]);

const [sublocationView, setSublocationView] = useState<string>('table');
const [stockView, setStockView] = useState<string>('table');

const locationPanels: PanelType[] = useMemo(() => {
return [
Expand Down Expand Up @@ -206,20 +208,36 @@ export default function Stock() {
}
]
}),
{
name: 'stock-items',
SegmentedControlPanel({
name: 'stockitems',
label: t`Stock Items`,
icon: <IconPackages />,
content: (
<StockItemTable
tableName='location-stock'
allowAdd
params={{
location: id
}}
/>
)
},
hidden: !user.hasViewPermission(ModelType.stockitem),
selection: stockView,
onChange: setStockView,
options: [
{
value: 'table',
label: t`Table View`,
icon: <IconTable />,
content: (
<StockItemTable
tableName='location-stock'
allowAdd
params={{
location: id
}}
/>
)
},
{
value: 'parametric',
label: t`Parametric View`,
icon: <IconListDetails />,
content: <ParametricStockItemTable locationId={id} />
}
]
}),
{
name: 'default_parts',
label: t`Default Parts`,
Expand All @@ -241,7 +259,7 @@ export default function Stock() {
hidden: !location.pk
})
];
}, [sublocationView, location, id]);
}, [sublocationView, stockView, location, id]);

const editLocation = useEditApiFormModal({
url: ApiEndpoints.stock_location_list,
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/pages/stock/StockDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import AttachmentPanel from '../../components/panels/AttachmentPanel';
import NotesPanel from '../../components/panels/NotesPanel';
import type { PanelType } from '../../components/panels/Panel';
import { PanelGroup } from '../../components/panels/PanelGroup';
import ParametersPanel from '../../components/panels/ParametersPanel';
import LocateItemButton from '../../components/plugins/LocateItemButton';
import { StatusRenderer } from '../../components/render/StatusRenderer';
import OrderPartsWizard from '../../components/wizards/OrderPartsWizard';
Expand Down Expand Up @@ -617,6 +618,11 @@ export default function StockDetail() {
<Skeleton />
)
},
ParametersPanel({
model_type: ModelType.stockitem,
model_id: stockitem.pk,
hidden: !stockitem.pk
}),
AttachmentPanel({
model_type: ModelType.stockitem,
model_id: stockitem.pk
Expand Down
59 changes: 59 additions & 0 deletions src/frontend/src/tables/stock/ParametricStockItemTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ApiEndpoints, ModelType } from '@lib/index';
import type { TableFilter } from '@lib/types/Filters';
import type { TableColumn } from '@lib/types/Tables';
import { t } from '@lingui/core/macro';
import { useMemo } from 'react';
import {
DescriptionColumn,
IPNColumn,
PartColumn,
StockColumn
} from '../ColumnRenderers';
import ParametricDataTable from '../general/ParametricDataTable';

export default function ParametricStockItemTable({
locationId
}: Readonly<{
locationId?: any;
}>) {
const customFilters: TableFilter[] = useMemo(() => {
// Custom filters for the parametric stock item table
return [];
}, []);

const customColumns: TableColumn[] = useMemo(() => {
return [
PartColumn({
part: 'part_detail',
switchable: false
}),
IPNColumn({}),
DescriptionColumn({
accessor: 'part_detail.description'
}),
StockColumn({
accessor: '',
title: t`Stock`,
sortable: true,
ordering: 'stock'
})
];
}, []);

return (
<ParametricDataTable
modelType={ModelType.stockitem}
relatedModel={'location'}
relatedModelId={locationId}
endpoint={ApiEndpoints.stock_item_list}
customColumns={customColumns}
customFilters={customFilters}
queryParams={{
location: locationId,
cascade: true,
location_detail: true,
part_detail: true
}}
/>
);
}
16 changes: 15 additions & 1 deletion src/frontend/tests/pages/pui_stock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
loadTab,
navigate,
openFilterDrawer,
setTableChoiceFilter
setTableChoiceFilter,
showParametricView,
showTableView
} from '../helpers.js';
import { doCachedLogin } from '../login.js';

Expand Down Expand Up @@ -36,6 +38,10 @@ test('Stock - Basic Tests', async ({ browser }) => {
await loadTab(page, 'Test Results');
await page.getByText('395c6d5586e5fb656901d047be27e1f7').waitFor();
await loadTab(page, 'Installed Items');

await loadTab(page, 'Parameters');
await loadTab(page, 'Attachments');
await loadTab(page, 'Notes');
});

test('Stock - Location Tree', async ({ browser }) => {
Expand Down Expand Up @@ -479,7 +485,15 @@ test('Stock - Location', async ({ browser }) => {

await loadTab(page, 'Default Parts');
await loadTab(page, 'Stock Items');

await showParametricView(page);
await showTableView(page);

await loadTab(page, 'Sublocations');

await showParametricView(page);
await showTableView(page);

await loadTab(page, 'Location Details');

await page.getByLabel('action-menu-barcode-actions').click();
Expand Down
Loading