mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Build test results (#7777)
* Skeleton for "test results" panel on build detail page * Generate table columns based on test templates * Fill out test result table in build panel * Fix for form submission with files attached - Better determination of "hasFiles" - Ignore undefined values * Add modal form to create a new test result * Add button for creating a new test result * Fix for build output table * Add extra API filtering options to BuildLine API endpoint * Improve table rendering * Adjust form fields * Account for multiple test results * Add "location" column * Docs updates * playwright tests
This commit is contained in:
parent
3cbfcc11cb
commit
97bef77d56
BIN
docs/docs/assets/images/build/build_panel_allocated_stock.png
Normal file
BIN
docs/docs/assets/images/build/build_panel_allocated_stock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
docs/docs/assets/images/build/build_panel_details.png
Normal file
BIN
docs/docs/assets/images/build/build_panel_details.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
docs/docs/assets/images/build/build_panel_line_items.png
Normal file
BIN
docs/docs/assets/images/build/build_panel_line_items.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
BIN
docs/docs/assets/images/build/build_panel_test_results.png
Normal file
BIN
docs/docs/assets/images/build/build_panel_test_results.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
BIN
docs/docs/assets/images/build/build_panel_test_statistics.png
Normal file
BIN
docs/docs/assets/images/build/build_panel_test_statistics.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
68
docs/docs/build/build.md
vendored
68
docs/docs/build/build.md
vendored
@ -103,25 +103,44 @@ For further information, refer to the [stock allocation documentation](./allocat
|
|||||||
|
|
||||||
## Build Order Display
|
## Build Order Display
|
||||||
|
|
||||||
The detail view for a single build order provides multiple display tabs, as follows:
|
The detail view for a single build order provides multiple display panels, as follows:
|
||||||
|
|
||||||
### Build Details
|
### Build Details
|
||||||
|
|
||||||
The *Build Details* tab provides an overview of the Build Order:
|
The *Build Details* panel provides an overview of the Build Order:
|
||||||
|
|
||||||
{% with id="build_details", url="build/build_details.png", description="Details tab" %}
|
{% with id="build_details", url="build/build_panel_details.png", description="Build details panel" %}
|
||||||
{% include "img.html" %}
|
{% include "img.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
### Line Items
|
### Line Items
|
||||||
|
|
||||||
The *Line Items* tab provides an interface to allocate required stock (as specified by the BOM) to the build:
|
The *Line Items* panel displays all the line items (as defined by the [bill of materials](./bom.md)) required to complete the build order.
|
||||||
|
|
||||||
{% with id="build_allocate", url="build/build_allocate.png", description="Allocation tab" %}
|
{% with id="build_allocate", url="build/build_panel_line_items.png", description="Build line items panel" %}
|
||||||
{% include "img.html" %}
|
{% include "img.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
The allocation table (as shown above) shows the stock allocation progress for this build. In the example above, there are two BOM lines, which have been partially allocated.
|
The allocation table (as shown above) provides an interface to allocate required stock, and also shows the stock allocation progress for each line item in the build.
|
||||||
|
|
||||||
|
### Incomplete Outputs
|
||||||
|
|
||||||
|
The *Incomplete Outputs* panel shows the list of in-progress [build outputs](./output.md) (created stock items) associated with this build.
|
||||||
|
|
||||||
|
{% with id="build_outputs", url="build/build_outputs.png", description="Outputs tab" %}
|
||||||
|
{% include "img.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
!!! info "Example: Build Outputs"
|
||||||
|
In the example image above, a single output (serial number 2) has been completed, while serial numbers 1 and 4 are still in progress.
|
||||||
|
|
||||||
|
- Build outputs can be created from this screen, by selecting the *Create New Output* button
|
||||||
|
- Outputs which are "in progress" can be completed or cancelled
|
||||||
|
- Completed outputs (which are simply *stock items*) can be viewed in the stock table at the bottom of the screen
|
||||||
|
|
||||||
|
### Completed Outputs
|
||||||
|
|
||||||
|
This panel displays all the completed build outputs (stock items) which have been created by this build order:
|
||||||
|
|
||||||
### Allocated Stock
|
### Allocated Stock
|
||||||
|
|
||||||
@ -138,28 +157,29 @@ The *Consumed Stock* tab displays all stock items which have been *consumed* by
|
|||||||
- [Tracked stock items](./allocate.md#tracked-stock) are consumed by specific build outputs
|
- [Tracked stock items](./allocate.md#tracked-stock) are consumed by specific build outputs
|
||||||
- [Untracked stock items](./allocate.md#untracked-stock) are consumed by the build order
|
- [Untracked stock items](./allocate.md#untracked-stock) are consumed by the build order
|
||||||
|
|
||||||
### Build Outputs
|
|
||||||
|
|
||||||
The *Build Outputs* tab shows the [build outputs](./output.md) (created stock items) associated with this build.
|
|
||||||
|
|
||||||
As shown below, there are separate panels for *incomplete* and *completed* build outputs.
|
|
||||||
|
|
||||||
{% with id="build_outputs", url="build/build_outputs.png", description="Outputs tab" %}
|
|
||||||
{% include "img.html" %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
!!! info "Example: Build Outputs"
|
|
||||||
In the example image above, a single output (serial number 2) has been completed, while serial numbers 1 and 4 are still in progress.
|
|
||||||
|
|
||||||
- Build outputs can be created from this screen, by selecting the *Create New Output* button
|
|
||||||
- Outputs which are "in progress" can be completed or cancelled
|
|
||||||
- Completed outputs (which are simply *stock items*) can be viewed in the stock table at the bottom of the screen
|
|
||||||
|
|
||||||
### Child Builds
|
### Child Builds
|
||||||
|
|
||||||
If there exist any build orders which are *children* of the selected build order, they are displayed in the *Child Builds* tab:
|
If there exist any build orders which are *children* of the selected build order, they are displayed in the *Child Builds* tab:
|
||||||
|
|
||||||
{% with id="build_childs", url="build/build_childs.png", description="Child builds tab" %}
|
{% with id="build_childs", url="build/build_childs.png", description="Child builds panel" %}
|
||||||
|
{% include "img.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
### Test Results
|
||||||
|
|
||||||
|
For *trackable* parts, test results can be recorded against each build output. These results are displayed in the *Test Results* panel:
|
||||||
|
|
||||||
|
{% with id="build_test_results", url="build/build_panel_test_results.png", description="Test Results panel" %}
|
||||||
|
{% include "img.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
This table provides a summary of the test results for each build output, and allows test results to be quickly added for each build output.
|
||||||
|
|
||||||
|
### Test Statistics
|
||||||
|
|
||||||
|
For *trackable* parts, this panel displays a summary of the test results for all build outputs:
|
||||||
|
|
||||||
|
{% with id="build_test_stats", url="build/build_panel_test_statistics.png", description="Test Statistics panel" %}
|
||||||
{% include "img.html" %}
|
{% include "img.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
@ -54,6 +54,8 @@ The following basic options are available:
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* |
|
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* |
|
||||||
| INVENTREE_DEBUG | debug | Enable [debug mode](./intro.md#debug-mode) | True |
|
| INVENTREE_DEBUG | debug | Enable [debug mode](./intro.md#debug-mode) | True |
|
||||||
|
| INVENTREE_DEBUG_QUERYCOUNT | debug_querycount | Enable [query count logging](https://github.com/bradmontgomery/django-querycount) in the terminal | False |
|
||||||
|
| INVENTREE_DEBUG_SHELL | debug_shell | Enable [administrator shell](https://github.com/djk2/django-admin-shell) (only in debug mode) | False |
|
||||||
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING |
|
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING |
|
||||||
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
|
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
|
||||||
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
|
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import yaml
|
import yaml
|
||||||
|
@ -359,6 +359,8 @@ class BuildLineList(BuildLineEndpoint, DataExportViewMixin, ListCreateAPI):
|
|||||||
'unit_quantity',
|
'unit_quantity',
|
||||||
'available_stock',
|
'available_stock',
|
||||||
'trackable',
|
'trackable',
|
||||||
|
'allow_variants',
|
||||||
|
'inherited',
|
||||||
]
|
]
|
||||||
|
|
||||||
ordering_field_aliases = {
|
ordering_field_aliases = {
|
||||||
@ -368,6 +370,8 @@ class BuildLineList(BuildLineEndpoint, DataExportViewMixin, ListCreateAPI):
|
|||||||
'consumable': 'bom_item__consumable',
|
'consumable': 'bom_item__consumable',
|
||||||
'optional': 'bom_item__optional',
|
'optional': 'bom_item__optional',
|
||||||
'trackable': 'bom_item__sub_part__trackable',
|
'trackable': 'bom_item__sub_part__trackable',
|
||||||
|
'allow_variants': 'bom_item__allow_variants',
|
||||||
|
'inherited': 'bom_item__inherited',
|
||||||
}
|
}
|
||||||
|
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
@ -122,11 +122,11 @@ function NameBadge({ pk, type }: { pk: string | number; type: BadgeType }) {
|
|||||||
case 200:
|
case 200:
|
||||||
return response.data;
|
return response.data;
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return {};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return undefined;
|
return {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -398,7 +398,7 @@ export function ApiForm({
|
|||||||
let value: any = data[key];
|
let value: any = data[key];
|
||||||
let field_type = fields[key]?.field_type;
|
let field_type = fields[key]?.field_type;
|
||||||
|
|
||||||
if (field_type == 'file upload') {
|
if (field_type == 'file upload' && !!value) {
|
||||||
hasFiles = true;
|
hasFiles = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +413,9 @@ export function ApiForm({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataForm.append(key, value);
|
if (value != undefined) {
|
||||||
|
dataForm.append(key, value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return api({
|
return api({
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
useSerialNumberGenerator
|
useSerialNumberGenerator
|
||||||
} from '../hooks/UseGenerator';
|
} from '../hooks/UseGenerator';
|
||||||
import { apiUrl } from '../states/ApiState';
|
import { apiUrl } from '../states/ApiState';
|
||||||
|
import { useGlobalSettingsState } from '../states/SettingsState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a StockItem instance
|
* Construct a set of fields for creating / editing a StockItem instance
|
||||||
@ -932,6 +933,13 @@ export function useTestResultFields({
|
|||||||
// Field type for the "value" input
|
// Field type for the "value" input
|
||||||
const [fieldType, setFieldType] = useState<'string' | 'choice'>('string');
|
const [fieldType, setFieldType] = useState<'string' | 'choice'>('string');
|
||||||
|
|
||||||
|
const settings = useGlobalSettingsState.getState();
|
||||||
|
|
||||||
|
const includeTestStation = useMemo(
|
||||||
|
() => settings.isSet('TEST_STATION_DATA'),
|
||||||
|
[settings]
|
||||||
|
);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
return {
|
||||||
stock_item: {
|
stock_item: {
|
||||||
@ -972,8 +980,15 @@ export function useTestResultFields({
|
|||||||
},
|
},
|
||||||
attachment: {},
|
attachment: {},
|
||||||
notes: {},
|
notes: {},
|
||||||
started_datetime: {},
|
started_datetime: {
|
||||||
finished_datetime: {}
|
hidden: !includeTestStation
|
||||||
|
},
|
||||||
|
finished_datetime: {
|
||||||
|
hidden: !includeTestStation
|
||||||
|
},
|
||||||
|
test_station: {
|
||||||
|
hidden: !includeTestStation
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [choices, fieldType, partId, itemId]);
|
}, [choices, fieldType, partId, itemId, includeTestStation]);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Grid, Skeleton, Stack } from '@mantine/core';
|
import { Grid, Skeleton, Stack } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconChecklist,
|
||||||
IconClipboardCheck,
|
IconClipboardCheck,
|
||||||
IconClipboardList,
|
IconClipboardList,
|
||||||
IconDots,
|
IconDots,
|
||||||
@ -51,6 +52,7 @@ import { useUserState } from '../../states/UserState';
|
|||||||
import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTable';
|
import BuildAllocatedStockTable from '../../tables/build/BuildAllocatedStockTable';
|
||||||
import BuildLineTable from '../../tables/build/BuildLineTable';
|
import BuildLineTable from '../../tables/build/BuildLineTable';
|
||||||
import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
|
import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
|
||||||
|
import BuildOrderTestTable from '../../tables/build/BuildOrderTestTable';
|
||||||
import BuildOutputTable from '../../tables/build/BuildOutputTable';
|
import BuildOutputTable from '../../tables/build/BuildOutputTable';
|
||||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||||
@ -307,6 +309,17 @@ export default function BuildDetail() {
|
|||||||
<Skeleton />
|
<Skeleton />
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'test-results',
|
||||||
|
label: t`Test Results`,
|
||||||
|
icon: <IconChecklist />,
|
||||||
|
hidden: !build.part_detail?.trackable,
|
||||||
|
content: build.pk ? (
|
||||||
|
<BuildOrderTestTable buildId={build.pk} partId={build.part} />
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'test-statistics',
|
name: 'test-statistics',
|
||||||
label: t`Test Statistics`,
|
label: t`Test Statistics`,
|
||||||
|
256
src/frontend/src/tables/build/BuildOrderTestTable.tsx
Normal file
256
src/frontend/src/tables/build/BuildOrderTestTable.tsx
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { ActionIcon, Badge, Group, Text, Tooltip } from '@mantine/core';
|
||||||
|
import { IconCirclePlus } from '@tabler/icons-react';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { api } from '../../App';
|
||||||
|
import { PassFailButton } from '../../components/buttons/YesNoButton';
|
||||||
|
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
||||||
|
import { RenderUser } from '../../components/render/User';
|
||||||
|
import { formatDate } from '../../defaults/formatters';
|
||||||
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { useTestResultFields } from '../../forms/StockForms';
|
||||||
|
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||||
|
import { useTable } from '../../hooks/UseTable';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import { TableColumn } from '../Column';
|
||||||
|
import { LocationColumn } from '../ColumnRenderers';
|
||||||
|
import { TableFilter } from '../Filter';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table which displays all "test results" for the outputs generated by a build order.
|
||||||
|
*/
|
||||||
|
export default function BuildOrderTestTable({
|
||||||
|
buildId,
|
||||||
|
partId
|
||||||
|
}: {
|
||||||
|
buildId: number;
|
||||||
|
partId: number;
|
||||||
|
}) {
|
||||||
|
const table = useTable('build-tests');
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
// Fetch the test templates required for this build order
|
||||||
|
const { data: testTemplates } = useQuery({
|
||||||
|
queryKey: ['build-test-templates', partId, buildId],
|
||||||
|
queryFn: async () => {
|
||||||
|
if (!partId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return api
|
||||||
|
.get(apiUrl(ApiEndpoints.part_test_template_list), {
|
||||||
|
params: {
|
||||||
|
part: partId,
|
||||||
|
include_inherited: true,
|
||||||
|
enabled: true,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => res.data)
|
||||||
|
.catch((err) => []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reload the table data whenever the set of templates changes
|
||||||
|
useEffect(() => {
|
||||||
|
table.refreshTable();
|
||||||
|
}, [testTemplates]);
|
||||||
|
|
||||||
|
const [selectedOutput, setSelectedOutput] = useState<number>(0);
|
||||||
|
const [selectedTemplate, setSelectedTemplate] = useState<number>(0);
|
||||||
|
|
||||||
|
const testResultFields: ApiFormFieldSet = useTestResultFields({
|
||||||
|
partId: partId,
|
||||||
|
itemId: selectedOutput
|
||||||
|
});
|
||||||
|
|
||||||
|
const createTestResult = useCreateApiFormModal({
|
||||||
|
url: apiUrl(ApiEndpoints.stock_test_result_list),
|
||||||
|
title: t`Add Test Result`,
|
||||||
|
fields: testResultFields,
|
||||||
|
initialData: {
|
||||||
|
template: selectedTemplate,
|
||||||
|
result: true
|
||||||
|
},
|
||||||
|
onFormSuccess: () => table.refreshTable(),
|
||||||
|
successMessage: t`Test result added`
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate a table column for each test template
|
||||||
|
const testColumns: TableColumn[] = useMemo(() => {
|
||||||
|
if (!testTemplates || testTemplates.length == 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return testTemplates.map((template: any) => {
|
||||||
|
return {
|
||||||
|
accessor: `test_${template.pk}`,
|
||||||
|
title: template.test_name,
|
||||||
|
sortable: false,
|
||||||
|
switchable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
let tests = record.tests || [];
|
||||||
|
|
||||||
|
// Find the most recent test result (highest primary key)
|
||||||
|
let test = tests
|
||||||
|
.filter((test: any) => test.template == template.pk)
|
||||||
|
.sort((a: any, b: any) => b.pk - a.pk)
|
||||||
|
.shift();
|
||||||
|
|
||||||
|
// No test result recorded
|
||||||
|
if (!test || test.result === undefined) {
|
||||||
|
return (
|
||||||
|
<Group gap="xs" wrap="nowrap" justify="space-between">
|
||||||
|
<Badge color="lightblue" variant="filled">{t`No Result`}</Badge>
|
||||||
|
<Tooltip label={t`Add Test Result`}>
|
||||||
|
<ActionIcon
|
||||||
|
size="xs"
|
||||||
|
color="green"
|
||||||
|
variant="transparent"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedOutput(record.pk);
|
||||||
|
setSelectedTemplate(template.pk);
|
||||||
|
createTestResult.open();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconCirclePlus />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let extra: ReactNode[] = [];
|
||||||
|
|
||||||
|
if (test.value) {
|
||||||
|
extra.push(
|
||||||
|
<Text key="value" size="sm">
|
||||||
|
{t`Value`}: {test.value}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test.notes) {
|
||||||
|
extra.push(
|
||||||
|
<Text key="notes" size="sm">
|
||||||
|
{t`Notes`}: {test.notes}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test.date) {
|
||||||
|
extra.push(
|
||||||
|
<Text key="date" size="sm">
|
||||||
|
{t`Date`}: {formatDate(test.date)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test.user_detail) {
|
||||||
|
extra.push(<RenderUser key="user" instance={test.user_detail} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHoverCard
|
||||||
|
value={<PassFailButton value={test.result} />}
|
||||||
|
title={template.test_name}
|
||||||
|
extra={extra}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [testTemplates]);
|
||||||
|
|
||||||
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
|
// Fixed columns
|
||||||
|
let columns: TableColumn[] = [
|
||||||
|
{
|
||||||
|
accessor: 'stock',
|
||||||
|
title: t`Build Output`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: false,
|
||||||
|
render: (record: any) => {
|
||||||
|
if (record.serial) {
|
||||||
|
return `# ${record.serial}`;
|
||||||
|
} else {
|
||||||
|
let extra: ReactNode[] = [];
|
||||||
|
|
||||||
|
if (record.batch) {
|
||||||
|
extra.push(
|
||||||
|
<Text key="batch" size="sm">
|
||||||
|
{t`Batch Code`}: {record.batch}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHoverCard
|
||||||
|
value={
|
||||||
|
<Text>
|
||||||
|
{t`Quantity`}: {record.quantity}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
title={t`Build Output`}
|
||||||
|
extra={extra}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
LocationColumn({
|
||||||
|
accessor: 'location_detail'
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
return [...columns, ...testColumns];
|
||||||
|
}, [testColumns]);
|
||||||
|
|
||||||
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'is_building',
|
||||||
|
label: t`In Production`,
|
||||||
|
description: t`Show build outputs currently in production`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const tableActions = useMemo(() => {
|
||||||
|
return [];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const rowActions = useCallback(
|
||||||
|
(record: any) => {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{createTestResult.modal}
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiEndpoints.stock_item_list)}
|
||||||
|
tableState={table}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
part_detail: true,
|
||||||
|
location_detail: true,
|
||||||
|
tests: true,
|
||||||
|
build: buildId
|
||||||
|
},
|
||||||
|
rowActions: rowActions,
|
||||||
|
tableFilters: tableFilters,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -47,9 +47,9 @@ export default function BuildOutputTable({ build }: { build: any }) {
|
|||||||
|
|
||||||
// Fetch the test templates associated with the partId
|
// Fetch the test templates associated with the partId
|
||||||
const { data: testTemplates } = useQuery({
|
const { data: testTemplates } = useQuery({
|
||||||
queryKey: ['buildoutputtests', build.part],
|
queryKey: ['buildoutputtests', partId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!partId) {
|
if (!partId || partId < 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,7 +322,7 @@ export default function BuildOutputTable({ build }: { build: any }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [buildId, partId]);
|
}, [buildId, partId, testTemplates]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -24,6 +24,16 @@ test('PUI - Pages - Build Order', async ({ page }) => {
|
|||||||
.getByRole('cell', { name: 'R38, R39, R40, R41, R42, R43' })
|
.getByRole('cell', { name: 'R38, R39, R40, R41, R42, R43' })
|
||||||
.waitFor();
|
.waitFor();
|
||||||
|
|
||||||
|
// Check "test results"
|
||||||
|
await page.getByRole('tab', { name: 'Test Results' }).click();
|
||||||
|
await page.getByText('Quantity: 25').waitFor();
|
||||||
|
await page.getByText('Continuity Checks').waitFor();
|
||||||
|
await page
|
||||||
|
.getByRole('row', { name: 'Quantity: 16 No location set' })
|
||||||
|
.getByRole('button')
|
||||||
|
.hover();
|
||||||
|
await page.getByText('Add Test Result').waitFor();
|
||||||
|
|
||||||
// Click through to the "parent" build
|
// Click through to the "parent" build
|
||||||
await page.getByRole('tab', { name: 'Build Details' }).click();
|
await page.getByRole('tab', { name: 'Build Details' }).click();
|
||||||
await page.getByRole('link', { name: 'BO0010' }).click();
|
await page.getByRole('link', { name: 'BO0010' }).click();
|
||||||
|
Loading…
Reference in New Issue
Block a user