diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 3f65312691..9880662e63 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -22,7 +22,7 @@ from .settings import MEDIA_URL, STATIC_URL def generateTestKey(test_name): """ Generate a test 'key' for a given test name. - This must not have spaces as it will be used for dict lookup in a template. + This must not have illegal chars as it will be used for dict lookup in a template. Tests must be named such that they will have unique keys. """ @@ -30,6 +30,9 @@ def generateTestKey(test_name): key = test_name.strip().lower() key = key.replace(" ", "") + # Remove any characters that cannot be used to represent a variable + key = re.sub(r'[^a-zA-Z0-9]', '', key) + return key diff --git a/InvenTree/stock/templates/stock/item_tests.html b/InvenTree/stock/templates/stock/item_tests.html index bd16ee8d29..77531714e0 100644 --- a/InvenTree/stock/templates/stock/item_tests.html +++ b/InvenTree/stock/templates/stock/item_tests.html @@ -7,7 +7,7 @@ {% include "stock/tabs.html" with tab='tests' %} -

{% trans "Test Results" %}

+

{% trans "Test Data" %}


@@ -20,8 +20,8 @@
- -
+ +
{% endblock %} @@ -30,11 +30,8 @@ loadStockTestResultsTable( $("#test-result-table"), { - params: { - stock_item: {{ item.id }}, - user_detail: true, - attachment_detail: true, - }, + part: {{ item.part.id }}, + stock_item: {{ item.id }}, } ); @@ -53,6 +50,21 @@ $("#add-test-result").click(function() { ); }); +$("#test-result-table").on('click', '.button-test-add', function() { + var button = $(this); + + var test_name = button.attr('pk'); + + launchModalForm( + "{% url 'stock-item-test-create' %}", { + data: { + stock_item: {{ item.id }}, + test: test_name + }, + } + ); +}); + $("#test-result-table").on('click', '.button-test-edit', function() { var button = $(this); diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 11c2065c35..29a57f5926 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -254,6 +254,8 @@ class StockItemTestResultCreate(AjaxCreateView): except (ValueError, StockItem.DoesNotExist): pass + initials['test'] = self.request.GET.get('test', '') + return initials def get_form(self): diff --git a/InvenTree/templates/js/stock.html b/InvenTree/templates/js/stock.html index af65782bef..963ccf525e 100644 --- a/InvenTree/templates/js/stock.html +++ b/InvenTree/templates/js/stock.html @@ -22,79 +22,98 @@ function removeStockRow(e) { function passFailBadge(result) { if (result) { - return `{% trans "PASS" %}`; + return `{% trans "PASS" %}`; } else { - return `{% trans "FAIL" %}`; + return `{% trans "FAIL" %}`; } } +function noResultBadge() { + return `{% trans "NO RESULT" %}`; +} + +function testKey(test_name) { + // Convert test name to a unique key without any illegal chars + + test_name = test_name.trim().toLowerCase(); + test_name = test_name.replace(' ', ''); + + test_name = test_name.replace(/[^0-9a-z]/gi, ''); + + return test_name; +} + function loadStockTestResultsTable(table, options) { /* * Load StockItemTestResult table */ - var params = options.params || {}; - - // HTML element to setup the filtering - var filterListElement = options.filterList || '#filter-list-stocktests'; + function formatDate(row) { + // Function for formatting date field + var html = row.date; - var filters = {}; + if (row.user_detail) { + html += `${row.user_detail.username}`; + } - filters = loadTableFilters("stocktests"); + if (row.attachment_detail) { + html += ``; + } - var original = {}; - - for (var key in params) { - original[key] = params[key]; + return html; } - setupFilterList("stocktests", table, filterListElement); + function makeButtons(row, grouped) { + var html = `
`; - // Override the default values, or add new ones - for (var key in params) { - filters[key] = params[key]; + html += makeIconButton('fa-plus icon-green', 'button-test-add', row.test_name, '{% trans "Add test result" %}'); + + if (!grouped) { + var pk = row.pk; + html += makeIconButton('fa-edit icon-blue', 'button-test-edit', pk, '{% trans "Edit test result" %}'); + html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}'); + } + + html += "
"; + + return html; } + // First, load all the test templates table.inventreeTable({ + url: "{% url 'api-part-test-template-list' %}", method: 'get', formatNoMatches: function() { - return '{% trans "No test results matching query" %}'; + return "{% trans 'No test results found' %}"; + }, + queryParams: { + part: options.part, }, - url: "{% url 'api-stock-test-result-list' %}", - queryParams: filters, - original: original, columns: [ { field: 'pk', title: 'ID', - visible: false + visible: false, }, { - field: 'test', - title: '{% trans "Test" %}', + field: 'test_name', + title: "{% trans "Test Name" %}", sortable: true, formatter: function(value, row) { var html = value; - if (row.attachment_detail) { - html += ``; + if (row.result == null) { + html += noResultBadge(); + } else { + html += passFailBadge(row.result); } return html; - }, - }, - { - field: 'result', - title: "{% trans "Result" %}", - sortable: true, - formatter: function(value) { - return passFailBadge(value); } }, { field: 'value', - title: "{% trans "Value" %}", - sortable: true, + title: '{% trans "Value" %}', }, { field: 'notes', @@ -102,35 +121,85 @@ function loadStockTestResultsTable(table, options) { }, { field: 'date', - title: '{% trans "Uploaded" %}', - sortable: true, + title: '{% trans "Test Date" %}', formatter: function(value, row) { - var html = value; - - if (row.user_detail) { - html += `${row.user_detail.username}`; - } - - return html; + return formatDate(row); } }, { field: 'buttons', formatter: function(value, row) { - - var pk = row.pk; - - var html = `
`; - - html += makeIconButton('fa-edit icon-blue', 'button-test-edit', pk, '{% trans "Edit test result" %}'); - html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}'); - - html += `
`; - - return html; + return makeButtons(row, false); } }, - ] + ], + groupBy: true, + groupByField: 'test_name', + groupByFormatter: function(field, id, data) { + + // Extract the "latest" row (data are returned in date order from the server) + var latest = data[data.length-1]; + + switch (field) { + case 'test_name': + return latest.test_name + ` (${data.length})` + passFailBadge(latest.result); + case 'value': + return latest.value; + case 'notes': + return latest.notes; + case 'date': + return formatDate(latest); + case 'buttons': + // Buttons are done differently for grouped rows + return makeButtons(latest, true); + default: + return "---"; + } + }, + onLoadSuccess: function(tableData) { + // Once the test template data are loaded, query for results + inventreeGet( + "{% url 'api-stock-test-result-list' %}", + { + stock_item: options.stock_item, + user_detail: true, + attachment_detail: true, + }, + { + success: function(data) { + + // Iterate through the returned test result data, and group by test + data.forEach(function(item) { + var match = false; + + var key = testKey(item.test); + + // Try to associate this result with a test row + tableData.forEach(function(row) { + // The result matches the test template row + if (key == testKey(row.test_name)) { + + // Force the names to be the same! + item.test_name = row.test_name; + match = true; + } + }); + + // No match could be found (this is a new test!) + if (!match) { + + item.test_name = item.test; + } + + tableData.push(item); + }); + + // Finally, push the data back into the table! + table.bootstrapTable("load", tableData); + } + }, + ); + } }); }