mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Test template disable (#6526)
* Add new field to PartTestTemplate model - 'enabled' field - default = True (backwards compatibility) - Allows tests to be disabled - Retains test results but disables test * Update PartTestTemplate API - Expose new field - Enable filtering by field * CUI updates - Add to PartTestTemplate table * PUI: Update PartTestTemplateTable * Update getRequiredTests - By default, filter out tests which are "disabled" * Update StockItemTestResult table - Only display "enabled" tests - Update CUI - UPdate PUI * Update existing build output table * Bump API version * Docs updates * Updated unit tests
This commit is contained in:
parent
3eb1914f1e
commit
567c7edbaf
@ -1,11 +1,16 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 171
|
INVENTREE_API_VERSION = 172
|
||||||
"""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 = """
|
||||||
|
|
||||||
|
v172 - 2024-02-20 : https://github.com/inventree/InvenTree/pull/6526
|
||||||
|
- Adds "enabled" field to the PartTestTemplate API endpoint
|
||||||
|
- Adds "enabled" filter to the PartTestTemplate list
|
||||||
|
- Adds "enabled" filter to the StockItemTestResult list
|
||||||
|
|
||||||
v171 - 2024-02-19 : https://github.com/inventree/InvenTree/pull/6516
|
v171 - 2024-02-19 : https://github.com/inventree/InvenTree/pull/6516
|
||||||
- Adds "key" as a filterable parameter to PartTestTemplate list endpoint
|
- Adds "key" as a filterable parameter to PartTestTemplate list endpoint
|
||||||
|
|
||||||
|
@ -375,7 +375,7 @@ class PartTestTemplateFilter(rest_filters.FilterSet):
|
|||||||
"""Metaclass options for this filterset."""
|
"""Metaclass options for this filterset."""
|
||||||
|
|
||||||
model = PartTestTemplate
|
model = PartTestTemplate
|
||||||
fields = ['required', 'requires_value', 'requires_attachment', 'key']
|
fields = ['enabled', 'key', 'required', 'requires_attachment', 'requires_value']
|
||||||
|
|
||||||
part = rest_filters.ModelChoiceFilter(
|
part = rest_filters.ModelChoiceFilter(
|
||||||
queryset=Part.objects.filter(trackable=True),
|
queryset=Part.objects.filter(trackable=True),
|
||||||
@ -440,11 +440,12 @@ class PartTestTemplateList(PartTestTemplateMixin, ListCreateAPI):
|
|||||||
search_fields = ['test_name', 'description']
|
search_fields = ['test_name', 'description']
|
||||||
|
|
||||||
ordering_fields = [
|
ordering_fields = [
|
||||||
'test_name',
|
'enabled',
|
||||||
'required',
|
'required',
|
||||||
'requires_value',
|
'requires_value',
|
||||||
'requires_attachment',
|
'requires_attachment',
|
||||||
'results',
|
'results',
|
||||||
|
'test_name',
|
||||||
]
|
]
|
||||||
|
|
||||||
ordering = 'test_name'
|
ordering = 'test_name'
|
||||||
|
18
InvenTree/part/migrations/0122_parttesttemplate_enabled.py
Normal file
18
InvenTree/part/migrations/0122_parttesttemplate_enabled.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.10 on 2024-02-20 01:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0121_auto_20240207_0344'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='parttesttemplate',
|
||||||
|
name='enabled',
|
||||||
|
field=models.BooleanField(default=True, help_text='Is this test enabled?', verbose_name='Enabled'),
|
||||||
|
),
|
||||||
|
]
|
@ -2124,7 +2124,7 @@ class Part(
|
|||||||
|
|
||||||
parameter.save()
|
parameter.save()
|
||||||
|
|
||||||
def getTestTemplates(self, required=None, include_parent=True):
|
def getTestTemplates(self, required=None, include_parent=True, enabled=None):
|
||||||
"""Return a list of all test templates associated with this Part.
|
"""Return a list of all test templates associated with this Part.
|
||||||
|
|
||||||
These are used for validation of a StockItem.
|
These are used for validation of a StockItem.
|
||||||
@ -2143,6 +2143,9 @@ class Part(
|
|||||||
if required is not None:
|
if required is not None:
|
||||||
tests = tests.filter(required=required)
|
tests = tests.filter(required=required)
|
||||||
|
|
||||||
|
if enabled is not None:
|
||||||
|
tests = tests.filter(enabled=enabled)
|
||||||
|
|
||||||
return tests
|
return tests
|
||||||
|
|
||||||
def getTestTemplateMap(self, **kwargs):
|
def getTestTemplateMap(self, **kwargs):
|
||||||
@ -2154,9 +2157,16 @@ class Part(
|
|||||||
|
|
||||||
return templates
|
return templates
|
||||||
|
|
||||||
def getRequiredTests(self):
|
def getRequiredTests(self, include_parent=True, enabled=True):
|
||||||
"""Return the tests which are required by this part."""
|
"""Return the tests which are required by this part.
|
||||||
return self.getTestTemplates(required=True)
|
|
||||||
|
Arguments:
|
||||||
|
include_parent: If True, include tests which are defined for parent parts
|
||||||
|
enabled: If set (either True or False), filter by template "enabled" status
|
||||||
|
"""
|
||||||
|
return self.getTestTemplates(
|
||||||
|
required=True, enabled=enabled, include_parent=include_parent
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attachment_count(self):
|
def attachment_count(self):
|
||||||
@ -3466,6 +3476,10 @@ class PartTestTemplate(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
help_text=_('Enter description for this test'),
|
help_text=_('Enter description for this test'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
enabled = models.BooleanField(
|
||||||
|
default=True, verbose_name=_('Enabled'), help_text=_('Is this test enabled?')
|
||||||
|
)
|
||||||
|
|
||||||
required = models.BooleanField(
|
required = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name=_('Required'),
|
verbose_name=_('Required'),
|
||||||
|
@ -153,6 +153,7 @@ class PartTestTemplateSerializer(InvenTree.serializers.InvenTreeModelSerializer)
|
|||||||
'part',
|
'part',
|
||||||
'test_name',
|
'test_name',
|
||||||
'description',
|
'description',
|
||||||
|
'enabled',
|
||||||
'required',
|
'required',
|
||||||
'requires_value',
|
'requires_value',
|
||||||
'requires_attachment',
|
'requires_attachment',
|
||||||
|
@ -382,6 +382,17 @@ class TestTemplateTest(TestCase):
|
|||||||
self.assertEqual(variant.getTestTemplates(include_parent=False).count(), 0)
|
self.assertEqual(variant.getTestTemplates(include_parent=False).count(), 0)
|
||||||
self.assertEqual(variant.getTestTemplates(required=True).count(), 5)
|
self.assertEqual(variant.getTestTemplates(required=True).count(), 5)
|
||||||
|
|
||||||
|
# Test the 'enabled' status check
|
||||||
|
self.assertEqual(variant.getTestTemplates(enabled=True).count(), 6)
|
||||||
|
self.assertEqual(variant.getTestTemplates(enabled=False).count(), 0)
|
||||||
|
|
||||||
|
template = variant.getTestTemplates().first()
|
||||||
|
template.enabled = False
|
||||||
|
template.save()
|
||||||
|
|
||||||
|
self.assertEqual(variant.getTestTemplates(enabled=True).count(), 5)
|
||||||
|
self.assertEqual(variant.getTestTemplates(enabled=False).count(), 1)
|
||||||
|
|
||||||
def test_uniqueness(self):
|
def test_uniqueness(self):
|
||||||
"""Test names must be unique for this part and also parts above."""
|
"""Test names must be unique for this part and also parts above."""
|
||||||
variant = Part.objects.get(pk=10004)
|
variant = Part.objects.get(pk=10004)
|
||||||
|
@ -1247,6 +1247,10 @@ class StockItemTestResultFilter(rest_filters.FilterSet):
|
|||||||
label='Required', field_name='template__required'
|
label='Required', field_name='template__required'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
enabled = rest_filters.BooleanFilter(
|
||||||
|
label='Enabled', field_name='template__enabled'
|
||||||
|
)
|
||||||
|
|
||||||
test = rest_filters.CharFilter(
|
test = rest_filters.CharFilter(
|
||||||
label='Test name (case insensitive)', method='filter_test_name'
|
label='Test name (case insensitive)', method='filter_test_name'
|
||||||
)
|
)
|
||||||
|
@ -1127,6 +1127,8 @@ function loadBuildOutputTable(build_info, options={}) {
|
|||||||
'{% url "api-part-test-template-list" %}',
|
'{% url "api-part-test-template-list" %}',
|
||||||
{
|
{
|
||||||
part: build_info.part,
|
part: build_info.part,
|
||||||
|
required: true,
|
||||||
|
enabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
async: false,
|
async: false,
|
||||||
|
@ -2818,6 +2818,7 @@ function partTestTemplateFields(options={}) {
|
|||||||
required: {},
|
required: {},
|
||||||
requires_value: {},
|
requires_value: {},
|
||||||
requires_attachment: {},
|
requires_attachment: {},
|
||||||
|
enabled: {},
|
||||||
part: {
|
part: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
}
|
}
|
||||||
@ -2862,6 +2863,7 @@ function loadPartTestTemplateTable(table, options) {
|
|||||||
field: 'pk',
|
field: 'pk',
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
visible: false,
|
visible: false,
|
||||||
|
switchable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'test_name',
|
field: 'test_name',
|
||||||
@ -2884,6 +2886,14 @@ function loadPartTestTemplateTable(table, options) {
|
|||||||
field: 'description',
|
field: 'description',
|
||||||
title: '{% trans "Description" %}',
|
title: '{% trans "Description" %}',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'enabled',
|
||||||
|
title: '{% trans "Enabled" %}',
|
||||||
|
sortable: true,
|
||||||
|
formatter: function(value) {
|
||||||
|
return yesNoLabel(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'required',
|
field: 'required',
|
||||||
title: '{% trans "Required" %}',
|
title: '{% trans "Required" %}',
|
||||||
|
@ -1421,6 +1421,7 @@ function loadStockTestResultsTable(table, options) {
|
|||||||
let params = {
|
let params = {
|
||||||
part: options.part,
|
part: options.part,
|
||||||
include_inherited: true,
|
include_inherited: true,
|
||||||
|
enabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var filters = loadTableFilters(filterKey, params);
|
var filters = loadTableFilters(filterKey, params);
|
||||||
@ -1558,6 +1559,7 @@ function loadStockTestResultsTable(table, options) {
|
|||||||
var query_params = {
|
var query_params = {
|
||||||
stock_item: options.stock_item,
|
stock_item: options.stock_item,
|
||||||
user_detail: true,
|
user_detail: true,
|
||||||
|
enabled: true,
|
||||||
attachment_detail: true,
|
attachment_detail: true,
|
||||||
template_detail: false,
|
template_detail: false,
|
||||||
ordering: '-date',
|
ordering: '-date',
|
||||||
|
@ -476,6 +476,10 @@ function getPartTestTemplateFilters() {
|
|||||||
type: 'bool',
|
type: 'bool',
|
||||||
title: '{% trans "Required" %}',
|
title: '{% trans "Required" %}',
|
||||||
},
|
},
|
||||||
|
enabled: {
|
||||||
|
type: 'bool',
|
||||||
|
title: '{% trans "Enabled" %}',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,10 @@ If this flag is set, then a corresponding test result against a stock item must
|
|||||||
|
|
||||||
If this flag is set, then a corresponding test result against a stock item must provide a file attachment uploaded.
|
If this flag is set, then a corresponding test result against a stock item must provide a file attachment uploaded.
|
||||||
|
|
||||||
|
#### Enabled
|
||||||
|
|
||||||
|
Tests can be *disabled* by setting the *enabled* flag to `False`. This can be useful if a test is no longer required, but the test template should be retained for historical purposes. Note that *deleting* a test template will also delete any associated test results. So, if a test template is no longer required, it is better to disable it rather than delete it.
|
||||||
|
|
||||||
### Test Results
|
### Test Results
|
||||||
|
|
||||||
Individual stock item objects can have test results associated with them which correspond to test templates. Refer to the [stock test result](../stock/test.md) documentation for further information.
|
Individual stock item objects can have test results associated with them which correspond to test templates. Refer to the [stock test result](../stock/test.md) documentation for further information.
|
||||||
|
@ -36,7 +36,12 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
render: (record: any) => {
|
render: (record: any) => {
|
||||||
return (
|
return (
|
||||||
<Text weight={record.required && 700}>{record.test_name}</Text>
|
<Text
|
||||||
|
weight={record.required && 700}
|
||||||
|
color={record.enabled ? undefined : 'red'}
|
||||||
|
>
|
||||||
|
{record.test_name}
|
||||||
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -52,6 +57,9 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
DescriptionColumn({
|
DescriptionColumn({
|
||||||
switchable: false
|
switchable: false
|
||||||
}),
|
}),
|
||||||
|
BooleanColumn({
|
||||||
|
accessor: 'enabled'
|
||||||
|
}),
|
||||||
BooleanColumn({
|
BooleanColumn({
|
||||||
accessor: 'required'
|
accessor: 'required'
|
||||||
}),
|
}),
|
||||||
@ -70,6 +78,10 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
name: 'required',
|
name: 'required',
|
||||||
description: t`Show required tests`
|
description: t`Show required tests`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'enabled',
|
||||||
|
description: t`Show enabled tests`
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'requires_value',
|
name: 'requires_value',
|
||||||
description: t`Show tests that require a value`
|
description: t`Show tests that require a value`
|
||||||
@ -100,7 +112,8 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
description: {},
|
description: {},
|
||||||
required: {},
|
required: {},
|
||||||
requires_value: {},
|
requires_value: {},
|
||||||
requires_attachment: {}
|
requires_attachment: {},
|
||||||
|
enabled: {}
|
||||||
};
|
};
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
@ -55,7 +55,8 @@ export default function StockItemTestResultTable({
|
|||||||
.get(apiUrl(ApiEndpoints.part_test_template_list), {
|
.get(apiUrl(ApiEndpoints.part_test_template_list), {
|
||||||
params: {
|
params: {
|
||||||
part: partId,
|
part: partId,
|
||||||
include_inherited: true
|
include_inherited: true,
|
||||||
|
enabled: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((response) => response.data)
|
.then((response) => response.data)
|
||||||
@ -126,12 +127,17 @@ export default function StockItemTestResultTable({
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
render: (record: any) => {
|
render: (record: any) => {
|
||||||
let required = record.required ?? record.template_detail?.required;
|
let required = record.required ?? record.template_detail?.required;
|
||||||
|
let enabled = record.enabled ?? record.template_detail?.enabled;
|
||||||
let installed =
|
let installed =
|
||||||
record.stock_item != undefined && record.stock_item != itemId;
|
record.stock_item != undefined && record.stock_item != itemId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group position="apart">
|
<Group position="apart">
|
||||||
<Text italic={installed} fw={required && 700}>
|
<Text
|
||||||
|
italic={installed}
|
||||||
|
fw={required && 700}
|
||||||
|
color={enabled ? undefined : 'red'}
|
||||||
|
>
|
||||||
{!record.templateId && '- '}
|
{!record.templateId && '- '}
|
||||||
{record.test_name ?? record.template_detail?.test_name}
|
{record.test_name ?? record.template_detail?.test_name}
|
||||||
</Text>
|
</Text>
|
||||||
@ -419,7 +425,8 @@ export default function StockItemTestResultTable({
|
|||||||
stock_item: itemId,
|
stock_item: itemId,
|
||||||
user_detail: true,
|
user_detail: true,
|
||||||
attachment_detail: true,
|
attachment_detail: true,
|
||||||
template_detail: true
|
template_detail: true,
|
||||||
|
enabled: true
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user