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 }} />