diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bfc2314a9f..b6cbc60ba8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: check-yaml - id: mixed-line-ending - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.0 + rev: v0.3.3 hooks: - id: ruff-format args: [--preview] @@ -60,7 +60,7 @@ repos: - "prettier@^2.4.1" - "@trivago/prettier-plugin-sort-imports" - repo: https://github.com/pre-commit/mirrors-eslint - rev: "v9.0.0-beta.1" + rev: "v9.0.0-beta.2" hooks: - id: eslint additional_dependencies: diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index 1f12b4d7bc..bc69e28ae8 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 183 +INVENTREE_API_VERSION = 184 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v184 - 2024-03-17 : https://github.com/inventree/InvenTree/pull/10464 + - Add additional fields for tests (start/end datetime, test station) + v183 - 2024-03-14 : https://github.com/inventree/InvenTree/pull/5972 - Adds "category_default_location" annotated field to part serializer - Adds "part_detail.category_default_location" annotated field to stock item serializer diff --git a/InvenTree/stock/migrations/0109_add_additional_test_fields.py b/InvenTree/stock/migrations/0109_add_additional_test_fields.py new file mode 100644 index 0000000000..7366b1136e --- /dev/null +++ b/InvenTree/stock/migrations/0109_add_additional_test_fields.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.23 on 2023-12-18 18:52 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0108_auto_20240219_0252'), + ] + + operations = [ + migrations.AddField( + model_name='stockitemtestresult', + name='finished_datetime', + field=models.DateTimeField(blank=True, default=datetime.datetime.now, help_text='The timestamp of the test finish', verbose_name='Finished'), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='started_datetime', + field=models.DateTimeField(blank=True, default=datetime.datetime.now, help_text='The timestamp of the test start', verbose_name='Started'), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='test_station', + field=models.CharField(blank=True, help_text='The identifier of the test station where the test was performed', max_length=500, verbose_name='Test station'), + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 99d3ae9eee..749a2ca6fd 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -2363,6 +2363,9 @@ class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel): value: Recorded test output value (optional) attachment: Link to StockItem attachment (optional) notes: Extra user notes related to the test (optional) + test_station: the name of the test station where the test was performed + started_datetime: Date when the test was started + finished_datetime: Date when the test was finished user: User who uploaded the test result date: Date the test result was recorded """ @@ -2453,4 +2456,27 @@ class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel): user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True) + test_station = models.CharField( + blank=True, + max_length=500, + verbose_name=_('Test station'), + help_text=_('The identifier of the test station where the test was performed'), + ) + + started_datetime = models.DateTimeField( + default=datetime.now, + blank=True, + verbose_name=_('Started'), + help_text=_('The timestamp of the test start'), + ) + + finished_datetime = models.DateTimeField( + default=datetime.now, + blank=True, + verbose_name=_('Finished'), + help_text=_('The timestamp of the test finish'), + ) + + user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True) + date = models.DateTimeField(auto_now_add=True, editable=False) diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 8d1be0f805..81baee7440 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -64,6 +64,9 @@ class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializ 'value', 'attachment', 'notes', + 'test_station', + 'started_datetime', + 'finished_datetime', 'user', 'user_detail', 'date', @@ -137,7 +140,17 @@ class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializ part=stock_item.part, test_name=test_name ) - return super().validate(data) + data = super().validate(data) + + started = data.get('started_datetime') + finished = data.get('finished_datetime') + if started is not None and finished is not None and started > finished: + raise ValidationError({ + 'finished_datetime': _( + 'The test finished time cannot be earlier than the test started time' + ) + }) + return data class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer): diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index 6d448a0d14..3f5539b7d8 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -2590,6 +2590,9 @@ function constructInput(name, parameters, options={}) { case 'date': func = constructDateInput; break; + case 'datetime': + func = constructDateTimeInput; + break; case 'candy': func = constructCandyInput; break; @@ -2860,6 +2863,19 @@ function constructDateInput(name, parameters) { } +/* + * Construct a field for a datetime input + */ +function constructDateTimeInput(name, parameters) { + + return constructInputOptions( + name, + 'datetimeinput form-control', + 'datetime', + parameters + ); +} + /* * Construct a "candy" field input * No actual field data! diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index ddb65c8b5c..89dc953c00 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -1367,11 +1367,11 @@ function noResultBadge() { return `{% trans "NO RESULT" %}`; } -function formatDate(row) { +function formatDate(row, date, options={}) { // Function for formatting date field - var html = renderDate(row.date); + var html = renderDate(date, options); - if (row.user_detail) { + if (row.user_detail && !options.no_user_detail) { html += `${row.user_detail.username}`; } @@ -1392,6 +1392,13 @@ function stockItemTestResultFields(options={}) { notes: { icon: 'fa-sticky-note', }, + test_station: {}, + started_datetime: { + icon: 'fa-calendar-alt', + }, + finished_datetime: { + icon: 'fa-calendar-alt', + }, stock_item: { hidden: true, }, @@ -1530,7 +1537,30 @@ function loadStockTestResultsTable(table, options) { title: '{% trans "Test Date" %}', sortable: true, formatter: function(value, row) { - return formatDate(row); + return formatDate(row, row.date); + }, + }, + { + field: 'test_station', + title: '{% trans "Test station" %}', + visible: false, + }, + { + field: 'started_timestamp', + title: '{% trans "Test started" %}', + sortable: true, + visible: false, + formatter: function(value, row) { + return formatDate(row, row.started_datetime, {showTime: true, no_user_detail: true}); + }, + }, + { + field: 'finished_timestamp', + title: '{% trans "Test finished" %}', + sortable: true, + visible: false, + formatter: function(value, row) { + return formatDate(row, row.finished_datetime, {showTime: true, no_user_detail: true}); }, }, { @@ -1655,6 +1685,8 @@ function loadStockTestResultsTable(table, options) { fields['stock_item']['value'] = options.stock_item; fields['template']['value'] = templateId; fields['template']['filters']['part'] = options.part; + fields['template']['started_datetime']['icon'] = 'fa-calendar-alt'; + fields['template']['finished_datetime']['icon'] = 'fa-calendar-alt'; constructForm('{% url "api-stock-test-result-list" %}', { method: 'POST',