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',