From c2ade848725a56dfe31bf1b6bc5264bd2638b0b3 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 15 Oct 2025 09:08:38 +1100 Subject: [PATCH 1/5] Plugin stock forms (#10584) * Expose stock adjustment forms to plugins * Update changelog * Expand type exports * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/frontend/CHANGELOG.md | 8 +++++++ src/frontend/lib/index.ts | 12 +++++++++- src/frontend/lib/types/Forms.tsx | 8 +++++++ src/frontend/lib/types/Plugins.tsx | 19 ++++++++++++++- src/frontend/package.json | 2 +- .../src/components/plugins/PluginContext.tsx | 24 ++++++++++++++++++- src/frontend/src/forms/StockForms.tsx | 11 ++------- .../src/hooks/UseStockAdjustActions.tsx | 4 ++-- src/frontend/src/pages/part/PartDetail.tsx | 7 ++---- .../src/pages/stock/LocationDetail.tsx | 6 ++--- src/frontend/src/pages/stock/StockDetail.tsx | 2 +- .../tables/build/BuildAllocatedStockTable.tsx | 2 +- .../src/tables/build/BuildOutputTable.tsx | 2 +- .../sales/SalesOrderAllocationTable.tsx | 2 +- .../src/tables/stock/StockItemTable.tsx | 6 ++--- 16 files changed, 84 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b47d981f1b..29411a85e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added much more detailed status information for machines to the API endpoint (including backend and frontend changes) in [#10381](https://github.com/inventree/InvenTree/pull/10381) - Added ability to partially complete and partially scrap build outputs in [#10499](https://github.com/inventree/InvenTree/pull/10499) - Added support for Redis ACL user-based authentication in [#10551](https://github.com/inventree/InvenTree/pull/10551) +- Expose stock adjustment forms to the UI plugin context in [#10584](https://github.com/inventree/InvenTree/pull/10584) ### Changed diff --git a/src/frontend/CHANGELOG.md b/src/frontend/CHANGELOG.md index ca6b880027..deec3c3251 100644 --- a/src/frontend/CHANGELOG.md +++ b/src/frontend/CHANGELOG.md @@ -2,6 +2,14 @@ This file contains historical changelog information for the InvenTree UI components library. +### 0.7.0 - October 2025 + +Exposes stock adjustment forms to plugins, allowing plugins to adjust stock adjustments using the common InvenTree UI form components. + +### 0.6.0 - September 2025 + +Updated underlying Mantine library versions. + ### 0.5.0 - August 2025 This release updates the base `react` major version from `18.3.1` to `19.1.1`. This change may introduce breaking changes for plugins that rely on the InvenTree UI components library (plugin developers should test their plugins against this new version). diff --git a/src/frontend/lib/index.ts b/src/frontend/lib/index.ts index 35eb3aff19..17e7e20f36 100644 --- a/src/frontend/lib/index.ts +++ b/src/frontend/lib/index.ts @@ -12,7 +12,12 @@ export { ModelType } from './enums/ModelType'; export type { ModelDict } from './enums/ModelInformation'; export { UserRoles, UserPermissions } from './enums/Roles'; -export type { InvenTreePluginContext } from './types/Plugins'; +export type { + InvenTreePluginContext, + InvenTreeFormsContext, + PluginVersion, + StockAdjustmentFormsContext +} from './types/Plugins'; export type { RowAction, RowViewProps } from './types/Tables'; export type { @@ -25,6 +30,11 @@ export type { BulkEditApiFormModalProps } from './types/Forms'; +export type { + UseModalProps, + UseModalReturn +} from './types/Modals'; + // Common utility functions export { apiUrl } from './functions/Api'; export { diff --git a/src/frontend/lib/types/Forms.tsx b/src/frontend/lib/types/Forms.tsx index cf9b6e43fd..3393ec361a 100644 --- a/src/frontend/lib/types/Forms.tsx +++ b/src/frontend/lib/types/Forms.tsx @@ -191,3 +191,11 @@ export interface ApiFormModalProps extends ApiFormProps { export interface BulkEditApiFormModalProps extends ApiFormModalProps { items: number[]; } + +export type StockOperationProps = { + items?: any[]; + pk?: number; + filters?: any; + model: ModelType.stockitem | 'location' | ModelType.part; + refresh: () => void; +}; diff --git a/src/frontend/lib/types/Plugins.tsx b/src/frontend/lib/types/Plugins.tsx index b93e99ef90..cc10083b6e 100644 --- a/src/frontend/lib/types/Plugins.tsx +++ b/src/frontend/lib/types/Plugins.tsx @@ -5,7 +5,11 @@ import type { AxiosInstance } from 'axios'; import type { NavigateFunction } from 'react-router-dom'; import type { ModelDict } from '../enums/ModelInformation'; import type { ModelType } from '../enums/ModelType'; -import type { ApiFormModalProps, BulkEditApiFormModalProps } from './Forms'; +import type { + ApiFormModalProps, + BulkEditApiFormModalProps, + StockOperationProps +} from './Forms'; import type { UseModalReturn } from './Modals'; import type { RenderInstanceProps } from './Rendering'; import type { SettingsStateProps } from './Settings'; @@ -24,11 +28,24 @@ export interface PluginVersion { mantine: string; } +export type StockAdjustmentFormsContext = { + addStock: (props: StockOperationProps) => UseModalReturn; + assignStock: (props: StockOperationProps) => UseModalReturn; + changeStatus: (props: StockOperationProps) => UseModalReturn; + countStock: (props: StockOperationProps) => UseModalReturn; + deleteStock: (props: StockOperationProps) => UseModalReturn; + mergeStock: (props: StockOperationProps) => UseModalReturn; + removeStock: (props: StockOperationProps) => UseModalReturn; + transferStock: (props: StockOperationProps) => UseModalReturn; + returnStock: (props: StockOperationProps) => UseModalReturn; +}; + export type InvenTreeFormsContext = { bulkEdit: (props: BulkEditApiFormModalProps) => UseModalReturn; create: (props: ApiFormModalProps) => UseModalReturn; delete: (props: ApiFormModalProps) => UseModalReturn; edit: (props: ApiFormModalProps) => UseModalReturn; + stockActions: StockAdjustmentFormsContext; }; /** diff --git a/src/frontend/package.json b/src/frontend/package.json index ecfd110472..b56f61cc99 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -1,7 +1,7 @@ { "name": "@inventreedb/ui", "description": "UI components for the InvenTree project", - "version": "0.6.0", + "version": "0.7.0", "private": false, "type": "module", "license": "MIT", diff --git a/src/frontend/src/components/plugins/PluginContext.tsx b/src/frontend/src/components/plugins/PluginContext.tsx index 0176c42b40..26c62dd5dc 100644 --- a/src/frontend/src/components/plugins/PluginContext.tsx +++ b/src/frontend/src/components/plugins/PluginContext.tsx @@ -18,6 +18,17 @@ import { type InvenTreePluginContext } from '@lib/types/Plugins'; import { i18n } from '@lingui/core'; +import { + useAddStockItem, + useAssignStockItem, + useChangeStockStatus, + useCountStockItem, + useDeleteStockItem, + useMergeStockItem, + useRemoveStockItem, + useReturnStockItem, + useTransferStockItem +} from '../../forms/StockForms'; import { useBulkEditApiFormModal, useCreateApiFormModal, @@ -60,7 +71,18 @@ export const useInvenTreeContext = () => { bulkEdit: useBulkEditApiFormModal, create: useCreateApiFormModal, delete: useDeleteApiFormModal, - edit: useEditApiFormModal + edit: useEditApiFormModal, + stockActions: { + addStock: useAddStockItem, + assignStock: useAssignStockItem, + changeStatus: useChangeStockStatus, + countStock: useCountStockItem, + deleteStock: useDeleteStockItem, + mergeStock: useMergeStockItem, + removeStock: useRemoveStockItem, + transferStock: useTransferStockItem, + returnStock: useReturnStockItem + } } }; }, [ diff --git a/src/frontend/src/forms/StockForms.tsx b/src/frontend/src/forms/StockForms.tsx index 9da55a945c..f3c407eba9 100644 --- a/src/frontend/src/forms/StockForms.tsx +++ b/src/frontend/src/forms/StockForms.tsx @@ -37,7 +37,8 @@ import type { ApiFormAdjustFilterType, ApiFormFieldChoice, ApiFormFieldSet, - ApiFormModalProps + ApiFormModalProps, + StockOperationProps } from '@lib/types/Forms'; import { TableFieldExtraRow, @@ -1172,14 +1173,6 @@ function useStockOperationModal({ }); } -export type StockOperationProps = { - items?: any[]; - pk?: number; - filters?: any; - model: ModelType.stockitem | 'location' | ModelType.part; - refresh: () => void; -}; - export function useAddStockItem(props: StockOperationProps) { return useStockOperationModal({ ...props, diff --git a/src/frontend/src/hooks/UseStockAdjustActions.tsx b/src/frontend/src/hooks/UseStockAdjustActions.tsx index d9e9ba927e..f0450bf0b3 100644 --- a/src/frontend/src/hooks/UseStockAdjustActions.tsx +++ b/src/frontend/src/hooks/UseStockAdjustActions.tsx @@ -1,11 +1,11 @@ import { UserRoles } from '@lib/index'; +import type { StockOperationProps } from '@lib/types/Forms'; import type { UseModalReturn } from '@lib/types/Modals'; import { t } from '@lingui/core/macro'; import { type ReactNode, useMemo } from 'react'; import type { ActionDropdownItem } from '../components/items/ActionDropdown'; import { ActionDropdown } from '../components/items/ActionDropdown'; import { - type StockOperationProps, useAddStockItem, useAssignStockItem, useChangeStockStatus, @@ -54,8 +54,8 @@ export function useStockAdjustActions( // The available modals for stock adjustment actions const addStock = useAddStockItem(props.formProps); const assignStock = useAssignStockItem(props.formProps); - const countStock = useCountStockItem(props.formProps); const changeStatus = useChangeStockStatus(props.formProps); + const countStock = useCountStockItem(props.formProps); const deleteStock = useDeleteStockItem(props.formProps); const mergeStock = useMergeStockItem(props.formProps); const removeStock = useRemoveStockItem(props.formProps); diff --git a/src/frontend/src/pages/part/PartDetail.tsx b/src/frontend/src/pages/part/PartDetail.tsx index 12a8a22ad6..a35d50482e 100644 --- a/src/frontend/src/pages/part/PartDetail.tsx +++ b/src/frontend/src/pages/part/PartDetail.tsx @@ -47,7 +47,7 @@ import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import { getDetailUrl } from '@lib/functions/Navigation'; import { ActionButton } from '@lib/index'; -import type { ApiFormFieldSet } from '@lib/types/Forms'; +import type { ApiFormFieldSet, StockOperationProps } from '@lib/types/Forms'; import AdminButton from '../../components/buttons/AdminButton'; import { PrintingActions } from '../../components/buttons/PrintingActions'; import StarredToggleButton from '../../components/buttons/StarredToggleButton'; @@ -80,10 +80,7 @@ import OrderPartsWizard from '../../components/wizards/OrderPartsWizard'; import { useApi } from '../../contexts/ApiContext'; import { formatDecimal, formatPriceRange } from '../../defaults/formatters'; import { usePartFields } from '../../forms/PartForms'; -import { - type StockOperationProps, - useFindSerialNumberForm -} from '../../forms/StockForms'; +import { useFindSerialNumberForm } from '../../forms/StockForms'; import { useApiFormModal, useCreateApiFormModal, diff --git a/src/frontend/src/pages/stock/LocationDetail.tsx b/src/frontend/src/pages/stock/LocationDetail.tsx index 0486bb5ec8..eb844ab928 100644 --- a/src/frontend/src/pages/stock/LocationDetail.tsx +++ b/src/frontend/src/pages/stock/LocationDetail.tsx @@ -3,6 +3,7 @@ import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import { getDetailUrl } from '@lib/functions/Navigation'; +import type { StockOperationProps } from '@lib/types/Forms'; import { t } from '@lingui/core/macro'; import { Group, Skeleton, Stack, Text } from '@mantine/core'; import { IconInfoCircle, IconPackages, IconSitemap } from '@tabler/icons-react'; @@ -30,10 +31,7 @@ import { PageDetail } from '../../components/nav/PageDetail'; import type { PanelType } from '../../components/panels/Panel'; import { PanelGroup } from '../../components/panels/PanelGroup'; import LocateItemButton from '../../components/plugins/LocateItemButton'; -import { - type StockOperationProps, - stockLocationFields -} from '../../forms/StockForms'; +import { stockLocationFields } from '../../forms/StockForms'; import { InvenTreeIcon } from '../../functions/icons'; import { useDeleteApiFormModal, diff --git a/src/frontend/src/pages/stock/StockDetail.tsx b/src/frontend/src/pages/stock/StockDetail.tsx index faba55f015..b6466b0b85 100644 --- a/src/frontend/src/pages/stock/StockDetail.tsx +++ b/src/frontend/src/pages/stock/StockDetail.tsx @@ -33,6 +33,7 @@ import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import { getDetailUrl } from '@lib/functions/Navigation'; +import type { StockOperationProps } from '@lib/types/Forms'; import { notifications } from '@mantine/notifications'; import { useBarcodeScanDialog } from '../../components/barcodes/BarcodeScanDialog'; import AdminButton from '../../components/buttons/AdminButton'; @@ -66,7 +67,6 @@ import OrderPartsWizard from '../../components/wizards/OrderPartsWizard'; import { useApi } from '../../contexts/ApiContext'; import { formatCurrency, formatDecimal } from '../../defaults/formatters'; import { - type StockOperationProps, useFindSerialNumberForm, useStockFields, useStockItemSerializeFields diff --git a/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx b/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx index 6f42b8c7f5..bdecc5d61b 100644 --- a/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx +++ b/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx @@ -12,10 +12,10 @@ import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import { ActionButton } from '@lib/index'; import type { TableFilter } from '@lib/types/Filters'; +import type { StockOperationProps } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import { IconCircleDashedCheck } from '@tabler/icons-react'; import { useConsumeBuildItemsForm } from '../../forms/BuildForms'; -import type { StockOperationProps } from '../../forms/StockForms'; import { useDeleteApiFormModal, useEditApiFormModal diff --git a/src/frontend/src/tables/build/BuildOutputTable.tsx b/src/frontend/src/tables/build/BuildOutputTable.tsx index 89a20e47e2..ac7322e6f5 100644 --- a/src/frontend/src/tables/build/BuildOutputTable.tsx +++ b/src/frontend/src/tables/build/BuildOutputTable.tsx @@ -33,6 +33,7 @@ import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import type { TableFilter } from '@lib/types/Filters'; +import type { StockOperationProps } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import { StylishText } from '../../components/items/StylishText'; import { useApi } from '../../contexts/ApiContext'; @@ -43,7 +44,6 @@ import { useScrapBuildOutputsForm } from '../../forms/BuildForms'; import { - type StockOperationProps, useStockFields, useStockItemSerializeFields } from '../../forms/StockForms'; diff --git a/src/frontend/src/tables/sales/SalesOrderAllocationTable.tsx b/src/frontend/src/tables/sales/SalesOrderAllocationTable.tsx index df247781dd..96ac375258 100644 --- a/src/frontend/src/tables/sales/SalesOrderAllocationTable.tsx +++ b/src/frontend/src/tables/sales/SalesOrderAllocationTable.tsx @@ -12,11 +12,11 @@ import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import type { TableFilter } from '@lib/types/Filters'; +import type { StockOperationProps } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import { IconTruckDelivery } from '@tabler/icons-react'; import { formatDate } from '../../defaults/formatters'; import { useSalesOrderAllocationFields } from '../../forms/SalesOrderForms'; -import type { StockOperationProps } from '../../forms/StockForms'; import { useBulkEditApiFormModal, useDeleteApiFormModal, diff --git a/src/frontend/src/tables/stock/StockItemTable.tsx b/src/frontend/src/tables/stock/StockItemTable.tsx index 4d8f98226a..0d4d868285 100644 --- a/src/frontend/src/tables/stock/StockItemTable.tsx +++ b/src/frontend/src/tables/stock/StockItemTable.tsx @@ -11,6 +11,7 @@ import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import { getDetailUrl } from '@lib/functions/Navigation'; import type { TableFilter } from '@lib/types/Filters'; +import type { StockOperationProps } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import OrderPartsWizard from '../../components/wizards/OrderPartsWizard'; import { @@ -18,10 +19,7 @@ import { formatDecimal, formatPriceRange } from '../../defaults/formatters'; -import { - type StockOperationProps, - useStockFields -} from '../../forms/StockForms'; +import { useStockFields } from '../../forms/StockForms'; import { InvenTreeIcon } from '../../functions/icons'; import { useCreateApiFormModal } from '../../hooks/UseForm'; import { useStockAdjustActions } from '../../hooks/UseStockAdjustActions'; From e040d99665a7a65bc272242fe4adce1170e0844f Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 16 Oct 2025 07:07:15 +1100 Subject: [PATCH 2/5] Order labels (#10588) * Add label actions for build orders * Support other order types --- src/frontend/src/pages/build/BuildDetail.tsx | 1 + src/frontend/src/pages/purchasing/PurchaseOrderDetail.tsx | 1 + src/frontend/src/pages/sales/ReturnOrderDetail.tsx | 1 + src/frontend/src/pages/sales/SalesOrderDetail.tsx | 1 + src/frontend/src/tables/build/BuildOrderTable.tsx | 1 + src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx | 3 ++- src/frontend/src/tables/sales/ReturnOrderTable.tsx | 3 ++- src/frontend/src/tables/sales/SalesOrderTable.tsx | 3 ++- 8 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/pages/build/BuildDetail.tsx b/src/frontend/src/pages/build/BuildDetail.tsx index 28337dde2b..d856376d95 100644 --- a/src/frontend/src/pages/build/BuildDetail.tsx +++ b/src/frontend/src/pages/build/BuildDetail.tsx @@ -679,6 +679,7 @@ export default function BuildDetail() { , , , , diff --git a/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx b/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx index 314227811d..81fe9db967 100644 --- a/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx +++ b/src/frontend/src/tables/purchasing/PurchaseOrderTable.tsx @@ -190,7 +190,8 @@ export function PurchaseOrderTable({ modelType: ModelType.purchaseorder, enableSelection: true, enableDownload: true, - enableReports: true + enableReports: true, + enableLabels: true }} /> diff --git a/src/frontend/src/tables/sales/ReturnOrderTable.tsx b/src/frontend/src/tables/sales/ReturnOrderTable.tsx index 33b6141dec..f01dedfbf9 100644 --- a/src/frontend/src/tables/sales/ReturnOrderTable.tsx +++ b/src/frontend/src/tables/sales/ReturnOrderTable.tsx @@ -189,7 +189,8 @@ export function ReturnOrderTable({ modelType: ModelType.returnorder, enableSelection: true, enableDownload: true, - enableReports: true + enableReports: true, + enableLabels: true }} /> diff --git a/src/frontend/src/tables/sales/SalesOrderTable.tsx b/src/frontend/src/tables/sales/SalesOrderTable.tsx index 770eca785f..01249aad03 100644 --- a/src/frontend/src/tables/sales/SalesOrderTable.tsx +++ b/src/frontend/src/tables/sales/SalesOrderTable.tsx @@ -201,7 +201,8 @@ export function SalesOrderTable({ modelType: ModelType.salesorder, enableSelection: true, enableDownload: true, - enableReports: true + enableReports: true, + enableLabels: true }} /> From 485aa6324c25f18c6ed1c237d5f6fb19cc8e57c4 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 16 Oct 2025 23:03:02 +1100 Subject: [PATCH 3/5] Dashboard item fix (#10596) * Fix for "subscribed categories" dashboard item * Tweak filter display * Tweak filter for "Subscribed Parts" --- .../src/components/dashboard/DashboardWidgetLibrary.tsx | 7 +++++-- src/frontend/src/tables/InvenTreeTableHeader.tsx | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx b/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx index 49f1c20e33..17a9cf739f 100644 --- a/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx +++ b/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx @@ -24,14 +24,17 @@ export function BuiltinQueryCountWidgets(): DashboardWidgetProps[] { title: t`Subscribed Parts`, description: t`Show the number of parts which you have subscribed to`, modelType: ModelType.part, - params: { starred: true } + params: { starred: true, active: true } }), QueryCountDashboardWidget({ label: 'sub-cat', title: t`Subscribed Categories`, description: t`Show the number of part categories which you have subscribed to`, modelType: ModelType.partcategory, - params: { starred: true } + params: { + starred: true, + top_level: 'none' + } }), QueryCountDashboardWidget({ label: 'invalid-bom', diff --git a/src/frontend/src/tables/InvenTreeTableHeader.tsx b/src/frontend/src/tables/InvenTreeTableHeader.tsx index d023dec887..dc55b6f8d9 100644 --- a/src/frontend/src/tables/InvenTreeTableHeader.tsx +++ b/src/frontend/src/tables/InvenTreeTableHeader.tsx @@ -250,7 +250,10 @@ export default function InvenTreeTableHeader({ Date: Fri, 17 Oct 2025 11:53:51 +1100 Subject: [PATCH 4/5] Allow adjument of build outputs (#10600) * Auto-select location * Allow stock adjustments for "in production" items * Tweak stock move check * Allow splitting of production stock * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/backend/InvenTree/stock/models.py | 8 ++++---- src/backend/InvenTree/stock/serializers.py | 2 +- src/frontend/src/forms/StockForms.tsx | 4 ++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29411a85e1..ce309cc1e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added ability to partially complete and partially scrap build outputs in [#10499](https://github.com/inventree/InvenTree/pull/10499) - Added support for Redis ACL user-based authentication in [#10551](https://github.com/inventree/InvenTree/pull/10551) - Expose stock adjustment forms to the UI plugin context in [#10584](https://github.com/inventree/InvenTree/pull/10584) +- Allow stock adjustments for "in production" items in [#10600](https://github.com/inventree/InvenTree/pull/10600) ### Changed diff --git a/src/backend/InvenTree/stock/models.py b/src/backend/InvenTree/stock/models.py index 7718dd0a00..d66895b30b 100644 --- a/src/backend/InvenTree/stock/models.py +++ b/src/backend/InvenTree/stock/models.py @@ -1743,7 +1743,6 @@ class StockItem( self.belongs_to is None, # Not installed inside another StockItem self.customer is None, # Not assigned to a customer self.consumed_by is None, # Not consumed by a build - not self.is_building, # Not part of an active build ]) @property @@ -2221,7 +2220,6 @@ class StockItem( - The new item will have a different StockItem ID, while this will remain the same. """ # Run initial checks to test if the stock item can actually be "split" - allow_production = kwargs.get('allow_production', False) # Cannot split a stock item which is in production @@ -2346,7 +2344,9 @@ class StockItem( 'STOCK_ALLOW_OUT_OF_STOCK_TRANSFER', backup_value=False, cache=False ) - if not allow_out_of_stock_transfer and not self.is_in_stock(check_status=False): + if not allow_out_of_stock_transfer and not self.is_in_stock( + check_status=False, check_in_production=False + ): raise ValidationError(_('StockItem cannot be moved as it is not in stock')) if quantity <= 0: @@ -2362,7 +2362,7 @@ class StockItem( kwargs['notes'] = notes # Split the existing StockItem in two - self.splitStock(quantity, location, user, **kwargs) + self.splitStock(quantity, location, user, allow_production=True, **kwargs) return True diff --git a/src/backend/InvenTree/stock/serializers.py b/src/backend/InvenTree/stock/serializers.py index 222f177ae8..7447307b8d 100644 --- a/src/backend/InvenTree/stock/serializers.py +++ b/src/backend/InvenTree/stock/serializers.py @@ -1602,7 +1602,7 @@ class StockAdjustmentItemSerializer(serializers.Serializer): ) if not allow_out_of_stock_transfer and not stock_item.is_in_stock( - check_status=False, check_quantity=False + check_status=False, check_quantity=False, check_in_production=False ): raise ValidationError(_('Stock item is not in stock')) elif self.require_in_stock == False: diff --git a/src/frontend/src/forms/StockForms.tsx b/src/frontend/src/forms/StockForms.tsx index f3c407eba9..d00af0a968 100644 --- a/src/frontend/src/forms/StockForms.tsx +++ b/src/frontend/src/forms/StockForms.tsx @@ -712,6 +712,9 @@ function stockTransferFields(items: any[]): ApiFormFieldSet { const records = Object.fromEntries(items.map((item) => [item.pk, item])); + // Extract all location values from the items + const locations = [...new Set(items.map((item) => item.location))]; + const fields: ApiFormFieldSet = { items: { field_type: 'table', @@ -740,6 +743,7 @@ function stockTransferFields(items: any[]): ApiFormFieldSet { ] }, location: { + value: locations.length === 1 ? locations[0] : undefined, filters: { structural: false } From 24dfbe815e0445af632afa08fd09e56f9c4eb397 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 17 Oct 2025 15:19:12 +1100 Subject: [PATCH 5/5] [UI] Manufacturer part updates (#10601) * Add filters for manufacturer parts table * Refactor * Fix typo * Additional filter options for StockList: - Filter by ManufacturerPart ID * Stock table view for ManufacturerPart * Bump API version --- .../InvenTree/InvenTree/api_version.py | 5 +- src/backend/InvenTree/stock/api.py | 8 ++- .../src/pages/company/CompanyDetail.tsx | 2 +- .../pages/company/ManufacturerPartDetail.tsx | 22 +++++++- .../src/pages/part/PartSupplierDetail.tsx | 2 +- .../src/pages/purchasing/PurchasingIndex.tsx | 2 +- .../purchasing/ManufacturerPartTable.tsx | 55 ++++++++++++++++--- 7 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 9e059dfe7b..ef1ad9482a 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,12 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 408 +INVENTREE_API_VERSION = 409 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v409 -> 2025-10-17 : https://github.com/inventree/InvenTree/pull/10601 + - Adds ability to filter StockList API by manufacturer part ID + v408 -> 2025-10-13: https://github.com/inventree/InvenTree/pull/10561 - Allow search of assembly fields in BOM API endpoint diff --git a/src/backend/InvenTree/stock/api.py b/src/backend/InvenTree/stock/api.py index bc7ce58d91..11beec18e1 100644 --- a/src/backend/InvenTree/stock/api.py +++ b/src/backend/InvenTree/stock/api.py @@ -25,7 +25,7 @@ import InvenTree.permissions import stock.serializers as StockSerializers from build.models import Build from build.serializers import BuildSerializer -from company.models import Company, SupplierPart +from company.models import Company, ManufacturerPart, SupplierPart from company.serializers import CompanySerializer from data_exporter.mixins import DataExportViewMixin from generic.states.api import StatusView @@ -553,6 +553,12 @@ class StockFilter(FilterSet): & Q(supplier_part__manufacturer_part__manufacturer=company) ) + manufacturer_part = rest_filters.ModelChoiceFilter( + label=_('Manufacturer Part'), + queryset=ManufacturerPart.objects.all(), + field_name='supplier_part__manufacturer_part', + ) + supplier = rest_filters.ModelChoiceFilter( label=_('Supplier'), queryset=Company.objects.filter(is_supplier=True), diff --git a/src/frontend/src/pages/company/CompanyDetail.tsx b/src/frontend/src/pages/company/CompanyDetail.tsx index ad52203c59..54cb0a03fb 100644 --- a/src/frontend/src/pages/company/CompanyDetail.tsx +++ b/src/frontend/src/pages/company/CompanyDetail.tsx @@ -197,7 +197,7 @@ export default function CompanyDetail(props: Readonly) { icon: , hidden: !company?.is_manufacturer, content: company?.pk && ( - + ) }, { diff --git a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx index 784191be21..dc2924b443 100644 --- a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx +++ b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx @@ -3,7 +3,8 @@ import { Grid, Skeleton, Stack } from '@mantine/core'; import { IconBuildingWarehouse, IconInfoCircle, - IconList + IconList, + IconPackages } from '@tabler/icons-react'; import { useMemo } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; @@ -42,6 +43,7 @@ import { useInstance } from '../../hooks/UseInstance'; import { useUserState } from '../../states/UserState'; import ManufacturerPartParameterTable from '../../tables/purchasing/ManufacturerPartParameterTable'; import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable'; +import { StockItemTable } from '../../tables/stock/StockItemTable'; export default function ManufacturerPartDetail() { const { id } = useParams(); @@ -171,6 +173,20 @@ export default function ManufacturerPartDetail() { ) }, + { + name: 'stock', + label: t`Received Stock`, + hidden: !user.hasViewRole(UserRoles.stock), + icon: , + content: ( + + ) + }, { name: 'suppliers', label: t`Suppliers`, @@ -194,7 +210,7 @@ export default function ManufacturerPartDetail() { model_id: manufacturerPart?.pk }) ]; - }, [manufacturerPart]); + }, [user, manufacturerPart]); const editManufacturerPartFields = useManufacturerPartFields(); @@ -278,7 +294,7 @@ export default function ManufacturerPartDetail() { > {t`Manufacturers`} - + diff --git a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx index caa10c46bc..4e77f8ee8b 100644 --- a/src/frontend/src/pages/purchasing/PurchasingIndex.tsx +++ b/src/frontend/src/pages/purchasing/PurchasingIndex.tsx @@ -108,7 +108,7 @@ export default function PurchasingIndex() { name: 'manufacturer-parts', label: t`Manufacturer Parts`, icon: , - content: + content: } ]; }, [user, purchaseOrderView]); diff --git a/src/frontend/src/tables/purchasing/ManufacturerPartTable.tsx b/src/frontend/src/tables/purchasing/ManufacturerPartTable.tsx index 71105c71fe..937a54a9f7 100644 --- a/src/frontend/src/tables/purchasing/ManufacturerPartTable.tsx +++ b/src/frontend/src/tables/purchasing/ManufacturerPartTable.tsx @@ -11,6 +11,7 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; +import type { TableFilter } from '@lib/types/Filters'; import type { TableColumn } from '@lib/types/Tables'; import { useManufacturerPartFields } from '../../forms/CompanyForms'; import { @@ -32,9 +33,27 @@ import { InvenTreeTable } from '../InvenTreeTable'; * Construct a table listing manufacturer parts */ export function ManufacturerPartTable({ - params -}: Readonly<{ params: any }>): ReactNode { - const table = useTable('manufacturerparts'); + manufacturerId, + partId +}: Readonly<{ + manufacturerId?: number; + partId?: number; +}>): ReactNode { + const tableId: string = useMemo(() => { + let tId = 'manufacturer-part'; + + if (manufacturerId) { + tId += '-manufacturer'; + } + + if (partId) { + tId += '-part'; + } + + return tId; + }, [manufacturerId, partId]); + + const table = useTable(tableId); const user = useUserState(); @@ -42,7 +61,7 @@ export function ManufacturerPartTable({ const tableColumns: TableColumn[] = useMemo(() => { return [ PartColumn({ - switchable: 'part' in params + switchable: !!partId }), { accessor: 'manufacturer', @@ -59,7 +78,7 @@ export function ManufacturerPartTable({ DescriptionColumn({}), LinkColumn({}) ]; - }, [params]); + }, [partId]); const manufacturerPartFields = useManufacturerPartFields(); @@ -73,8 +92,8 @@ export function ManufacturerPartTable({ fields: manufacturerPartFields, table: table, initialData: { - manufacturer: params?.manufacturer, - part: params?.part + manufacturer: manufacturerId, + part: partId } }); @@ -93,6 +112,24 @@ export function ManufacturerPartTable({ table: table }); + const tableFilters: TableFilter[] = useMemo(() => { + return [ + { + name: 'part_active', + label: t`Active Part`, + description: t`Show manufacturer parts for active internal parts.`, + type: 'boolean' + }, + { + name: 'manufacturer_active', + label: t`Active Manufacturer`, + active: !manufacturerId, + description: t`Show manufacturer parts for active manufacturers.`, + type: 'boolean' + } + ]; + }, [manufacturerId]); + const tableActions = useMemo(() => { const can_add = user.hasAddRole(UserRoles.purchase_order) && @@ -141,13 +178,15 @@ export function ManufacturerPartTable({ columns={tableColumns} props={{ params: { - ...params, + part: partId, + manufacturer: manufacturerId, part_detail: true, manufacturer_detail: true }, enableDownload: true, rowActions: rowActions, tableActions: tableActions, + tableFilters: tableFilters, modelType: ModelType.manufacturerpart }} />