mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-17 12:35:46 +00:00
[PUI] Sales order actions (#7837)
* Create build order from sales order table * Allow creation of child build order from build page * Add production and purcahse order quantitres to sales order item serializer * Bump API version * Fix playwright test
This commit is contained in:
@ -247,11 +247,7 @@ export default function BuildDetail() {
|
||||
label: t`Line Items`,
|
||||
icon: <IconListNumbers />,
|
||||
content: build?.pk ? (
|
||||
<BuildLineTable
|
||||
params={{
|
||||
build: id
|
||||
}}
|
||||
/>
|
||||
<BuildLineTable buildId={build.pk} />
|
||||
) : (
|
||||
<Skeleton />
|
||||
)
|
||||
|
@ -543,7 +543,7 @@ export default function PartDetail() {
|
||||
label: t`Variants`,
|
||||
icon: <IconVersions />,
|
||||
hidden: !part.is_template,
|
||||
content: <PartVariantTable partId={String(id)} />
|
||||
content: <PartVariantTable part={part} />
|
||||
},
|
||||
{
|
||||
name: 'allocations',
|
||||
|
@ -5,11 +5,14 @@ import {
|
||||
IconShoppingCart,
|
||||
IconTool
|
||||
} from '@tabler/icons-react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { ProgressBar } from '../../components/items/ProgressBar';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { useBuildOrderFields } from '../../forms/BuildForms';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
@ -19,7 +22,13 @@ import { TableFilter } from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { TableHoverCard } from '../TableHoverCard';
|
||||
|
||||
export default function BuildLineTable({ params = {} }: { params?: any }) {
|
||||
export default function BuildLineTable({
|
||||
buildId,
|
||||
params = {}
|
||||
}: {
|
||||
buildId: number;
|
||||
params?: any;
|
||||
}) {
|
||||
const table = useTable('buildline');
|
||||
const user = useUserState();
|
||||
|
||||
@ -213,6 +222,19 @@ export default function BuildLineTable({ params = {} }: { params?: any }) {
|
||||
];
|
||||
}, []);
|
||||
|
||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||
|
||||
const [initialData, setInitialData] = useState<any>({});
|
||||
|
||||
const newBuildOrder = useCreateApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Create Build Order`,
|
||||
fields: buildOrderFields,
|
||||
initialData: initialData,
|
||||
follow: true,
|
||||
modelType: ModelType.build
|
||||
});
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any) => {
|
||||
let part = record.part_detail;
|
||||
@ -243,8 +265,16 @@ export default function BuildLineTable({ params = {} }: { params?: any }) {
|
||||
{
|
||||
icon: <IconTool />,
|
||||
title: t`Build Stock`,
|
||||
hidden: !part?.assembly,
|
||||
color: 'blue'
|
||||
hidden: !part?.assembly || !user.hasAddRole(UserRoles.build),
|
||||
color: 'blue',
|
||||
onClick: () => {
|
||||
setInitialData({
|
||||
part: record.part,
|
||||
parent: buildId,
|
||||
quantity: record.quantity - record.allocated
|
||||
});
|
||||
newBuildOrder.open();
|
||||
}
|
||||
}
|
||||
];
|
||||
},
|
||||
@ -252,21 +282,25 @@ export default function BuildLineTable({ params = {} }: { params?: any }) {
|
||||
);
|
||||
|
||||
return (
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.build_line_list)}
|
||||
tableState={table}
|
||||
columns={tableColumns}
|
||||
props={{
|
||||
params: {
|
||||
...params,
|
||||
part_detail: true
|
||||
},
|
||||
tableFilters: tableFilters,
|
||||
rowActions: rowActions,
|
||||
modelType: ModelType.part,
|
||||
modelField: 'part_detail.pk',
|
||||
enableDownload: true
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
{newBuildOrder.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.build_line_list)}
|
||||
tableState={table}
|
||||
columns={tableColumns}
|
||||
props={{
|
||||
params: {
|
||||
...params,
|
||||
build: buildId,
|
||||
part_detail: true
|
||||
},
|
||||
tableFilters: tableFilters,
|
||||
rowActions: rowActions,
|
||||
modelType: ModelType.part,
|
||||
modelField: 'part_detail.pk',
|
||||
enableDownload: true
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -303,20 +303,28 @@ function partTableFilters(): TableFilter[] {
|
||||
* @param {Object} params - The query parameters to pass to the API
|
||||
* @returns
|
||||
*/
|
||||
export function PartListTable({ props }: { props: InvenTreeTableProps }) {
|
||||
export function PartListTable({
|
||||
props,
|
||||
defaultPartData
|
||||
}: {
|
||||
props: InvenTreeTableProps;
|
||||
defaultPartData?: any;
|
||||
}) {
|
||||
const tableColumns = useMemo(() => partTableColumns(), []);
|
||||
const tableFilters = useMemo(() => partTableFilters(), []);
|
||||
|
||||
const table = useTable('part-list');
|
||||
const user = useUserState();
|
||||
|
||||
const initialPartData = useMemo(() => {
|
||||
return defaultPartData ?? props.params ?? {};
|
||||
}, [defaultPartData, props.params]);
|
||||
|
||||
const newPart = useCreateApiFormModal({
|
||||
url: ApiEndpoints.part_list,
|
||||
title: t`Add Part`,
|
||||
fields: usePartFields({ create: true }),
|
||||
initialData: {
|
||||
...(props.params ?? {})
|
||||
},
|
||||
initialData: initialPartData,
|
||||
follow: true,
|
||||
modelType: ModelType.part
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ import { PartListTable } from './PartTable';
|
||||
/**
|
||||
* Display variant parts for the specified parent part
|
||||
*/
|
||||
export function PartVariantTable({ partId }: { partId: string }) {
|
||||
export function PartVariantTable({ part }: { part: any }) {
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -39,9 +39,14 @@ export function PartVariantTable({ partId }: { partId: string }) {
|
||||
enableDownload: false,
|
||||
tableFilters: tableFilters,
|
||||
params: {
|
||||
ancestor: partId
|
||||
ancestor: part.pk
|
||||
}
|
||||
}}
|
||||
defaultPartData={{
|
||||
...part,
|
||||
variant_of: part.pk,
|
||||
is_template: false
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { formatCurrency } from '../../defaults/formatters';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { useBuildOrderFields } from '../../forms/BuildForms';
|
||||
import { useSalesOrderLineItemFields } from '../../forms/SalesOrderForms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
@ -122,6 +123,22 @@ export default function SalesOrderLineItemTable({
|
||||
extra.push(<Text size="sm">{t`Includes variant stock`}</Text>);
|
||||
}
|
||||
|
||||
if (record.building > 0) {
|
||||
extra.push(
|
||||
<Text size="sm">
|
||||
{t`In production`}: {record.building}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (record.on_order > 0) {
|
||||
extra.push(
|
||||
<Text size="sm">
|
||||
{t`On order`}: {record.on_order}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableHoverCard
|
||||
value={<Text color={color}>{text}</Text>}
|
||||
@ -199,6 +216,17 @@ export default function SalesOrderLineItemTable({
|
||||
table: table
|
||||
});
|
||||
|
||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||
|
||||
const newBuildOrder = useCreateApiFormModal({
|
||||
url: ApiEndpoints.build_order_list,
|
||||
title: t`Create Build Order`,
|
||||
fields: buildOrderFields,
|
||||
initialData: initialData,
|
||||
follow: true,
|
||||
modelType: ModelType.build
|
||||
});
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
return [
|
||||
<AddItemButton
|
||||
@ -235,7 +263,15 @@ export default function SalesOrderLineItemTable({
|
||||
!record?.part_detail?.assembly,
|
||||
title: t`Build stock`,
|
||||
icon: <IconTools />,
|
||||
color: 'blue'
|
||||
color: 'blue',
|
||||
onClick: () => {
|
||||
setInitialData({
|
||||
part: record.part,
|
||||
quantity: (record?.quantity ?? 1) - (record?.allocated ?? 0),
|
||||
sales_order: orderId
|
||||
});
|
||||
newBuildOrder.open();
|
||||
}
|
||||
},
|
||||
{
|
||||
hidden:
|
||||
@ -277,6 +313,7 @@ export default function SalesOrderLineItemTable({
|
||||
{editLine.modal}
|
||||
{deleteLine.modal}
|
||||
{newLine.modal}
|
||||
{newBuildOrder.modal}
|
||||
<InvenTreeTable
|
||||
url={apiUrl(ApiEndpoints.sales_order_line_list)}
|
||||
tableState={table}
|
||||
|
@ -11,7 +11,7 @@ test('PUI - Pages - Build Order', async ({ page }) => {
|
||||
await page.getByRole('tab', { name: 'Build', exact: true }).click();
|
||||
|
||||
// We have now loaded the "Build Order" table. Check for some expected texts
|
||||
await page.getByText('On Hold').waitFor();
|
||||
await page.getByText('On Hold').first().waitFor();
|
||||
await page.getByText('Pending').first().waitFor();
|
||||
|
||||
// Load a particular build order
|
||||
|
Reference in New Issue
Block a user