2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-10-21 16:37:39 +00:00

[UI] Manufacturer part updates (#10601)

* Add filters for manufacturer parts table

* Refactor <ManufacturerPartTable />

* Fix typo

* Additional filter options for StockList:

- Filter by ManufacturerPart ID

* Stock table view for ManufacturerPart

* Bump API version
This commit is contained in:
Oliver
2025-10-17 15:19:12 +11:00
committed by GitHub
parent 759c882a95
commit 24dfbe815e
7 changed files with 80 additions and 16 deletions

View File

@@ -1,12 +1,15 @@
"""InvenTree API version information.""" """InvenTree API version information."""
# InvenTree API version # 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.""" """Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """ 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 v408 -> 2025-10-13: https://github.com/inventree/InvenTree/pull/10561
- Allow search of assembly fields in BOM API endpoint - Allow search of assembly fields in BOM API endpoint

View File

@@ -25,7 +25,7 @@ import InvenTree.permissions
import stock.serializers as StockSerializers import stock.serializers as StockSerializers
from build.models import Build from build.models import Build
from build.serializers import BuildSerializer from build.serializers import BuildSerializer
from company.models import Company, SupplierPart from company.models import Company, ManufacturerPart, SupplierPart
from company.serializers import CompanySerializer from company.serializers import CompanySerializer
from data_exporter.mixins import DataExportViewMixin from data_exporter.mixins import DataExportViewMixin
from generic.states.api import StatusView from generic.states.api import StatusView
@@ -553,6 +553,12 @@ class StockFilter(FilterSet):
& Q(supplier_part__manufacturer_part__manufacturer=company) & 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( supplier = rest_filters.ModelChoiceFilter(
label=_('Supplier'), label=_('Supplier'),
queryset=Company.objects.filter(is_supplier=True), queryset=Company.objects.filter(is_supplier=True),

View File

@@ -197,7 +197,7 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
icon: <IconBuildingWarehouse />, icon: <IconBuildingWarehouse />,
hidden: !company?.is_manufacturer, hidden: !company?.is_manufacturer,
content: company?.pk && ( content: company?.pk && (
<ManufacturerPartTable params={{ manufacturer: company.pk }} /> <ManufacturerPartTable manufacturerId={company.pk} />
) )
}, },
{ {

View File

@@ -3,7 +3,8 @@ import { Grid, Skeleton, Stack } from '@mantine/core';
import { import {
IconBuildingWarehouse, IconBuildingWarehouse,
IconInfoCircle, IconInfoCircle,
IconList IconList,
IconPackages
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
@@ -42,6 +43,7 @@ import { useInstance } from '../../hooks/UseInstance';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import ManufacturerPartParameterTable from '../../tables/purchasing/ManufacturerPartParameterTable'; import ManufacturerPartParameterTable from '../../tables/purchasing/ManufacturerPartParameterTable';
import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable'; import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
import { StockItemTable } from '../../tables/stock/StockItemTable';
export default function ManufacturerPartDetail() { export default function ManufacturerPartDetail() {
const { id } = useParams(); const { id } = useParams();
@@ -171,6 +173,20 @@ export default function ManufacturerPartDetail() {
<Skeleton /> <Skeleton />
) )
}, },
{
name: 'stock',
label: t`Received Stock`,
hidden: !user.hasViewRole(UserRoles.stock),
icon: <IconPackages />,
content: (
<StockItemTable
tableName='manufacturer-part-stock'
params={{
manufacturer_part: id
}}
/>
)
},
{ {
name: 'suppliers', name: 'suppliers',
label: t`Suppliers`, label: t`Suppliers`,
@@ -194,7 +210,7 @@ export default function ManufacturerPartDetail() {
model_id: manufacturerPart?.pk model_id: manufacturerPart?.pk
}) })
]; ];
}, [manufacturerPart]); }, [user, manufacturerPart]);
const editManufacturerPartFields = useManufacturerPartFields(); const editManufacturerPartFields = useManufacturerPartFields();

View File

@@ -23,7 +23,7 @@ export default function PartSupplierDetail({
<StylishText size='lg'>{t`Manufacturers`}</StylishText> <StylishText size='lg'>{t`Manufacturers`}</StylishText>
</Accordion.Control> </Accordion.Control>
<Accordion.Panel> <Accordion.Panel>
<ManufacturerPartTable params={{ part: partId }} /> <ManufacturerPartTable partId={partId} />
</Accordion.Panel> </Accordion.Panel>
</Accordion.Item> </Accordion.Item>
</Accordion> </Accordion>

View File

@@ -108,7 +108,7 @@ export default function PurchasingIndex() {
name: 'manufacturer-parts', name: 'manufacturer-parts',
label: t`Manufacturer Parts`, label: t`Manufacturer Parts`,
icon: <IconBuildingWarehouse />, icon: <IconBuildingWarehouse />,
content: <ManufacturerPartTable params={{}} /> content: <ManufacturerPartTable />
} }
]; ];
}, [user, purchaseOrderView]); }, [user, purchaseOrderView]);

View File

@@ -11,6 +11,7 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { ModelType } from '@lib/enums/ModelType'; import { ModelType } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles'; import { UserRoles } from '@lib/enums/Roles';
import { apiUrl } from '@lib/functions/Api'; import { apiUrl } from '@lib/functions/Api';
import type { TableFilter } from '@lib/types/Filters';
import type { TableColumn } from '@lib/types/Tables'; import type { TableColumn } from '@lib/types/Tables';
import { useManufacturerPartFields } from '../../forms/CompanyForms'; import { useManufacturerPartFields } from '../../forms/CompanyForms';
import { import {
@@ -32,9 +33,27 @@ import { InvenTreeTable } from '../InvenTreeTable';
* Construct a table listing manufacturer parts * Construct a table listing manufacturer parts
*/ */
export function ManufacturerPartTable({ export function ManufacturerPartTable({
params manufacturerId,
}: Readonly<{ params: any }>): ReactNode { partId
const table = useTable('manufacturerparts'); }: 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(); const user = useUserState();
@@ -42,7 +61,7 @@ export function ManufacturerPartTable({
const tableColumns: TableColumn[] = useMemo(() => { const tableColumns: TableColumn[] = useMemo(() => {
return [ return [
PartColumn({ PartColumn({
switchable: 'part' in params switchable: !!partId
}), }),
{ {
accessor: 'manufacturer', accessor: 'manufacturer',
@@ -59,7 +78,7 @@ export function ManufacturerPartTable({
DescriptionColumn({}), DescriptionColumn({}),
LinkColumn({}) LinkColumn({})
]; ];
}, [params]); }, [partId]);
const manufacturerPartFields = useManufacturerPartFields(); const manufacturerPartFields = useManufacturerPartFields();
@@ -73,8 +92,8 @@ export function ManufacturerPartTable({
fields: manufacturerPartFields, fields: manufacturerPartFields,
table: table, table: table,
initialData: { initialData: {
manufacturer: params?.manufacturer, manufacturer: manufacturerId,
part: params?.part part: partId
} }
}); });
@@ -93,6 +112,24 @@ export function ManufacturerPartTable({
table: table 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 tableActions = useMemo(() => {
const can_add = const can_add =
user.hasAddRole(UserRoles.purchase_order) && user.hasAddRole(UserRoles.purchase_order) &&
@@ -141,13 +178,15 @@ export function ManufacturerPartTable({
columns={tableColumns} columns={tableColumns}
props={{ props={{
params: { params: {
...params, part: partId,
manufacturer: manufacturerId,
part_detail: true, part_detail: true,
manufacturer_detail: true manufacturer_detail: true
}, },
enableDownload: true, enableDownload: true,
rowActions: rowActions, rowActions: rowActions,
tableActions: tableActions, tableActions: tableActions,
tableFilters: tableFilters,
modelType: ModelType.manufacturerpart modelType: ModelType.manufacturerpart
}} }}
/> />