From 567c7edbaf356584567175d04baa492b2d696896 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 20 Feb 2024 21:01:59 +1100 Subject: [PATCH] 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 --- InvenTree/InvenTree/api_version.py | 7 +++++- InvenTree/part/api.py | 5 +++-- .../0122_parttesttemplate_enabled.py | 18 +++++++++++++++ InvenTree/part/models.py | 22 +++++++++++++++---- InvenTree/part/serializers.py | 1 + InvenTree/part/test_part.py | 11 ++++++++++ InvenTree/stock/api.py | 4 ++++ InvenTree/templates/js/translated/build.js | 2 ++ InvenTree/templates/js/translated/part.js | 10 +++++++++ InvenTree/templates/js/translated/stock.js | 2 ++ .../templates/js/translated/table_filters.js | 4 ++++ docs/docs/part/test.md | 4 ++++ .../src/tables/part/PartTestTemplateTable.tsx | 17 ++++++++++++-- .../tables/stock/StockItemTestResultTable.tsx | 13 ++++++++--- 14 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 InvenTree/part/migrations/0122_parttesttemplate_enabled.py diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index 4364f8e6e7..283accadde 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -1,11 +1,16 @@ """InvenTree API version information.""" # 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.""" 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 - Adds "key" as a filterable parameter to PartTestTemplate list endpoint diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 89bbe6cb39..6f4f612481 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -375,7 +375,7 @@ class PartTestTemplateFilter(rest_filters.FilterSet): """Metaclass options for this filterset.""" model = PartTestTemplate - fields = ['required', 'requires_value', 'requires_attachment', 'key'] + fields = ['enabled', 'key', 'required', 'requires_attachment', 'requires_value'] part = rest_filters.ModelChoiceFilter( queryset=Part.objects.filter(trackable=True), @@ -440,11 +440,12 @@ class PartTestTemplateList(PartTestTemplateMixin, ListCreateAPI): search_fields = ['test_name', 'description'] ordering_fields = [ - 'test_name', + 'enabled', 'required', 'requires_value', 'requires_attachment', 'results', + 'test_name', ] ordering = 'test_name' diff --git a/InvenTree/part/migrations/0122_parttesttemplate_enabled.py b/InvenTree/part/migrations/0122_parttesttemplate_enabled.py new file mode 100644 index 0000000000..f7d1fece00 --- /dev/null +++ b/InvenTree/part/migrations/0122_parttesttemplate_enabled.py @@ -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'), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index eaa2613863..b07c320626 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -2124,7 +2124,7 @@ class Part( 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. These are used for validation of a StockItem. @@ -2143,6 +2143,9 @@ class Part( if required is not None: tests = tests.filter(required=required) + if enabled is not None: + tests = tests.filter(enabled=enabled) + return tests def getTestTemplateMap(self, **kwargs): @@ -2154,9 +2157,16 @@ class Part( return templates - def getRequiredTests(self): - """Return the tests which are required by this part.""" - return self.getTestTemplates(required=True) + def getRequiredTests(self, include_parent=True, enabled=True): + """Return the tests which are required by this part. + + 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 def attachment_count(self): @@ -3466,6 +3476,10 @@ class PartTestTemplate(InvenTree.models.InvenTreeMetadataModel): help_text=_('Enter description for this test'), ) + enabled = models.BooleanField( + default=True, verbose_name=_('Enabled'), help_text=_('Is this test enabled?') + ) + required = models.BooleanField( default=True, verbose_name=_('Required'), diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index b636bca568..36ab1ba7a3 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -153,6 +153,7 @@ class PartTestTemplateSerializer(InvenTree.serializers.InvenTreeModelSerializer) 'part', 'test_name', 'description', + 'enabled', 'required', 'requires_value', 'requires_attachment', diff --git a/InvenTree/part/test_part.py b/InvenTree/part/test_part.py index 2247d034ec..3523e8e50b 100644 --- a/InvenTree/part/test_part.py +++ b/InvenTree/part/test_part.py @@ -382,6 +382,17 @@ class TestTemplateTest(TestCase): self.assertEqual(variant.getTestTemplates(include_parent=False).count(), 0) 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): """Test names must be unique for this part and also parts above.""" variant = Part.objects.get(pk=10004) diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index c5892acacd..a6bb0c9c41 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -1247,6 +1247,10 @@ class StockItemTestResultFilter(rest_filters.FilterSet): label='Required', field_name='template__required' ) + enabled = rest_filters.BooleanFilter( + label='Enabled', field_name='template__enabled' + ) + test = rest_filters.CharFilter( label='Test name (case insensitive)', method='filter_test_name' ) diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index e8cf8ac4b7..1ac5b477da 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -1127,6 +1127,8 @@ function loadBuildOutputTable(build_info, options={}) { '{% url "api-part-test-template-list" %}', { part: build_info.part, + required: true, + enabled: true, }, { async: false, diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index b24411f2e8..240734e958 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -2818,6 +2818,7 @@ function partTestTemplateFields(options={}) { required: {}, requires_value: {}, requires_attachment: {}, + enabled: {}, part: { hidden: true, } @@ -2862,6 +2863,7 @@ function loadPartTestTemplateTable(table, options) { field: 'pk', title: 'ID', visible: false, + switchable: false, }, { field: 'test_name', @@ -2884,6 +2886,14 @@ function loadPartTestTemplateTable(table, options) { field: 'description', title: '{% trans "Description" %}', }, + { + field: 'enabled', + title: '{% trans "Enabled" %}', + sortable: true, + formatter: function(value) { + return yesNoLabel(value); + } + }, { field: 'required', title: '{% trans "Required" %}', diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index 0b442fecc1..c6a1f5f2de 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -1421,6 +1421,7 @@ function loadStockTestResultsTable(table, options) { let params = { part: options.part, include_inherited: true, + enabled: true, }; var filters = loadTableFilters(filterKey, params); @@ -1558,6 +1559,7 @@ function loadStockTestResultsTable(table, options) { var query_params = { stock_item: options.stock_item, user_detail: true, + enabled: true, attachment_detail: true, template_detail: false, ordering: '-date', diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js index 0ea7215c1e..91786c3e59 100644 --- a/InvenTree/templates/js/translated/table_filters.js +++ b/InvenTree/templates/js/translated/table_filters.js @@ -476,6 +476,10 @@ function getPartTestTemplateFilters() { type: 'bool', title: '{% trans "Required" %}', }, + enabled: { + type: 'bool', + title: '{% trans "Enabled" %}', + }, }; } diff --git a/docs/docs/part/test.md b/docs/docs/part/test.md index 286e1bd42c..cab5779522 100644 --- a/docs/docs/part/test.md +++ b/docs/docs/part/test.md @@ -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. +#### 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 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. diff --git a/src/frontend/src/tables/part/PartTestTemplateTable.tsx b/src/frontend/src/tables/part/PartTestTemplateTable.tsx index 85ada56752..1a02014a1a 100644 --- a/src/frontend/src/tables/part/PartTestTemplateTable.tsx +++ b/src/frontend/src/tables/part/PartTestTemplateTable.tsx @@ -36,7 +36,12 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) { sortable: true, render: (record: any) => { return ( - {record.test_name} + + {record.test_name} + ); } }, @@ -52,6 +57,9 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) { DescriptionColumn({ switchable: false }), + BooleanColumn({ + accessor: 'enabled' + }), BooleanColumn({ accessor: 'required' }), @@ -70,6 +78,10 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) { name: 'required', description: t`Show required tests` }, + { + name: 'enabled', + description: t`Show enabled tests` + }, { name: 'requires_value', description: t`Show tests that require a value` @@ -100,7 +112,8 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) { description: {}, required: {}, requires_value: {}, - requires_attachment: {} + requires_attachment: {}, + enabled: {} }; }, [user]); diff --git a/src/frontend/src/tables/stock/StockItemTestResultTable.tsx b/src/frontend/src/tables/stock/StockItemTestResultTable.tsx index 7cd7176385..310eebba04 100644 --- a/src/frontend/src/tables/stock/StockItemTestResultTable.tsx +++ b/src/frontend/src/tables/stock/StockItemTestResultTable.tsx @@ -55,7 +55,8 @@ export default function StockItemTestResultTable({ .get(apiUrl(ApiEndpoints.part_test_template_list), { params: { part: partId, - include_inherited: true + include_inherited: true, + enabled: true } }) .then((response) => response.data) @@ -126,12 +127,17 @@ export default function StockItemTestResultTable({ sortable: true, render: (record: any) => { let required = record.required ?? record.template_detail?.required; + let enabled = record.enabled ?? record.template_detail?.enabled; let installed = record.stock_item != undefined && record.stock_item != itemId; return ( - + {!record.templateId && '- '} {record.test_name ?? record.template_detail?.test_name} @@ -419,7 +425,8 @@ export default function StockItemTestResultTable({ stock_item: itemId, user_detail: true, attachment_detail: true, - template_detail: true + template_detail: true, + enabled: true } }} />