2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 04:25:42 +00:00

Order table variants (#8295)

* BuildOrderTable: Show variants

- Allow filtering of build orders by part variant

* Add "include_variants" filter for SalesOrder table

- A bit tricker!

* Add "include_variants" filter to PartPurchaseOrdersTable

* Enable filtering ReturnOrder by "part" attribute

* Add similiar functionality for SalesOrderAllocation

* Add similar filter for BuildAllocation table

* Add migration file
This commit is contained in:
Oliver
2024-10-16 20:19:11 +11:00
committed by GitHub
parent 0c9e986796
commit 59fa3bb4ff
13 changed files with 338 additions and 40 deletions

View File

@ -223,8 +223,10 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
label: t`Return Orders`,
icon: <IconTruckReturn />,
hidden: !company?.is_customer,
content: company.pk && (
<ReturnOrderTable params={{ customer: company.pk }} />
content: company.pk ? (
<ReturnOrderTable customerId={company.pk} />
) : (
<Skeleton />
)
},
{

View File

@ -29,6 +29,7 @@ import {
IconTestPipe,
IconTools,
IconTruckDelivery,
IconTruckReturn,
IconVersions
} from '@tabler/icons-react';
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
@ -43,7 +44,6 @@ import { DetailsField, DetailsTable } from '../../components/details/Details';
import DetailsBadge from '../../components/details/DetailsBadge';
import { DetailsImage } from '../../components/details/DetailsImage';
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
import NotesEditor from '../../components/editors/NotesEditor';
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
import { Thumbnail } from '../../components/images/Thumbnail';
import {
@ -98,6 +98,7 @@ import { PartVariantTable } from '../../tables/part/PartVariantTable';
import { RelatedPartTable } from '../../tables/part/RelatedPartTable';
import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartTable';
import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
import { ReturnOrderTable } from '../../tables/sales/ReturnOrderTable';
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';
import { SalesOrderTable } from '../../tables/sales/SalesOrderTable';
import { StockItemTable } from '../../tables/stock/StockItemTable';
@ -606,6 +607,7 @@ export default function PartDetail() {
modelField="build"
modelTarget={ModelType.build}
showBuildInfo
showPartInfo
allowEdit
/>
</Accordion.Panel>
@ -698,6 +700,13 @@ export default function PartDetail() {
hidden: !part.salable,
content: part.pk ? <SalesOrderTable partId={part.pk} /> : <Skeleton />
},
{
name: 'return_orders',
label: t`Return Orders`,
icon: <IconTruckReturn />,
hidden: !part.salable || !globalSettings.isSet('RETURNORDER_ENABLED'),
content: part.pk ? <ReturnOrderTable partId={part.pk} /> : <Skeleton />
},
{
name: 'stocktake',
label: t`Stock History`,
@ -1093,7 +1102,7 @@ export default function PartDetail() {
badges={badges}
breadcrumbs={breadcrumbs}
breadcrumbAction={() => {
setTreeOpen(true);
setTreeOpen(true); // Open the category tree
}}
editAction={editPart.open}
editEnabled={user.hasChangeRole(UserRoles.part)}

View File

@ -45,17 +45,30 @@ export default function BuildAllocatedStockTable({
modelField?: string;
}>) {
const user = useUserState();
const table = useTable('buildallocatedstock');
const table = useTable(
!!partId ? 'buildallocatedstock-part' : 'buildallocatedstock'
);
const tableFilters: TableFilter[] = useMemo(() => {
return [
let filters: TableFilter[] = [
{
name: 'tracked',
label: t`Allocated to Output`,
description: t`Show items allocated to a build output`
}
];
}, []);
if (!!partId) {
filters.push({
name: 'include_variants',
type: 'boolean',
label: t`Include Variants`,
description: t`Include orders for part variants`
});
}
return filters;
}, [partId]);
const tableColumns: TableColumn[] = useMemo(() => {
return [

View File

@ -46,6 +46,8 @@ export function BuildOrderTable({
parentBuildId?: number;
salesOrderId?: number;
}>) {
const table = useTable(!!partId ? 'buildorder-part' : 'buildorder-index');
const tableColumns = useMemo(() => {
return [
ReferenceColumn({}),
@ -110,7 +112,7 @@ export function BuildOrderTable({
const ownerFilters = useOwnerFilters();
const tableFilters: TableFilter[] = useMemo(() => {
return [
let filters: TableFilter[] = [
{
name: 'active',
type: 'boolean',
@ -147,12 +149,22 @@ export function BuildOrderTable({
choices: ownerFilters.choices
}
];
}, [parentBuildId, projectCodeFilters.choices, ownerFilters.choices]);
// If we are filtering on a specific part, we can include the "include variants" filter
if (!!partId) {
filters.push({
name: 'include_variants',
type: 'boolean',
label: t`Include Variants`,
description: t`Include orders for part variants`
});
}
return filters;
}, [partId, projectCodeFilters.choices, ownerFilters.choices]);
const user = useUserState();
const table = useTable('buildorder');
const buildOrderFields = useBuildOrderFields({ create: true });
const newBuild = useCreateApiFormModal({

View File

@ -123,6 +123,12 @@ export default function PartPurchaseOrdersTable({
label: t`Order Status`,
description: t`Filter by order status`,
choiceFunction: StatusFilterOptions(ModelType.purchaseorder)
},
{
name: 'include_variants',
type: 'boolean',
label: t`Include Variants`,
description: t`Include orders for part variants`
}
];
}, []);

View File

@ -35,15 +35,21 @@ import {
} from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable';
export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
const table = useTable('return-orders');
export function ReturnOrderTable({
partId,
customerId
}: Readonly<{
partId?: number;
customerId?: number;
}>) {
const table = useTable(!!partId ? 'returnorders-part' : 'returnorders-index');
const user = useUserState();
const projectCodeFilters = useProjectCodeFilters();
const responsibleFilters = useOwnerFilters();
const tableFilters: TableFilter[] = useMemo(() => {
return [
let filters: TableFilter[] = [
{
name: 'status',
label: t`Status`,
@ -69,7 +75,18 @@ export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
choices: responsibleFilters.choices
}
];
}, [projectCodeFilters.choices, responsibleFilters.choices]);
if (!!partId) {
filters.push({
name: 'include_variants',
type: 'boolean',
label: t`Include Variants`,
description: t`Include orders for part variants`
});
}
return filters;
}, [partId, projectCodeFilters.choices, responsibleFilters.choices]);
const tableColumns = useMemo(() => {
return [
@ -143,7 +160,8 @@ export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
columns={tableColumns}
props={{
params: {
...params,
part: partId,
customer: customerId,
customer_detail: true
},
tableFilters: tableFilters,

View File

@ -47,17 +47,30 @@ export default function SalesOrderAllocationTable({
modelField?: string;
}>) {
const user = useUserState();
const table = useTable('salesorderallocations');
const table = useTable(
!!partId ? 'salesorderallocations-part' : 'salesorderallocations'
);
const tableFilters: TableFilter[] = useMemo(() => {
return [
let filters: TableFilter[] = [
{
name: 'outstanding',
label: t`Outstanding`,
description: t`Show outstanding allocations`
}
];
}, []);
if (!!partId) {
filters.push({
name: 'include_variants',
type: 'boolean',
label: t`Include Variants`,
description: t`Include orders for part variants`
});
}
return filters;
}, [partId]);
const tableColumns: TableColumn[] = useMemo(() => {
return [

View File

@ -43,14 +43,14 @@ export function SalesOrderTable({
partId?: number;
customerId?: number;
}>) {
const table = useTable('sales-order');
const table = useTable(!!partId ? 'salesorder-part' : 'salesorder-index');
const user = useUserState();
const projectCodeFilters = useProjectCodeFilters();
const responsibleFilters = useOwnerFilters();
const tableFilters: TableFilter[] = useMemo(() => {
return [
let filters: TableFilter[] = [
{
name: 'status',
label: t`Status`,
@ -76,7 +76,18 @@ export function SalesOrderTable({
choices: responsibleFilters.choices
}
];
}, [projectCodeFilters.choices, responsibleFilters.choices]);
if (!!partId) {
filters.push({
name: 'include_variants',
type: 'boolean',
label: t`Include Variants`,
description: t`Include orders for part variants`
});
}
return filters;
}, [partId, projectCodeFilters.choices, responsibleFilters.choices]);
const salesOrderFields = useSalesOrderFields({});