mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 11:10:54 +00:00
[WIP] Test result table (#6430)
* Add basic table for stock item test results * Improve custom data formatter callback * Custom data formatter for returned results * Update YesNoButton functionality - Add PassFailButton with custom text * Enhancements for stock item test result table - Render all data * Add placeholder row actions * Fix table link * Add option to filter parttesttemplate table by "inherited" * Navigate through to parent part * Update PartTestTemplate model - Save 'key' value to database - Update whenever model is saved - Custom data migration * Custom migration step in tasks.py - Add custom management command - Wraps migration step in maintenance mode * Improve uniqueness validation for PartTestTemplate * Add 'template' field to StockItemTestResult - Links to a PartTestTemplate instance - Add migrations to link existing PartTestTemplates * Add "results" count to PartTestTemplate API - Include in rendered tables * Add 'results' column to test result table - Allow filtering too * Update serializer for StockItemTestResult - Include template information - Update CUI and PUI tables * Control template_detail field with query params * Update ref in api_version.py * Update data migration - Ensure new template is created for top level assembly * Fix admin integration * Update StockItemTestResult table - Remove 'test' field - Make 'template' field non-nullable - Previous data migrations should have accounted for this * Implement "legacy" API support - Create test result by providing test name - Lookup existing template * PUI: Cleanup table * Update tasks.py - Exclude temporary settings when exporting data * Fix unique validation check * Remove duplicate code * CUI: Fix data rendering * More refactoring of PUI table * More fixes for PUI table * Get row expansion working (kinda) * Improve rendering of subtable * More PUI updates: - Edit existing results - Add new results * allow delete of test result * Fix typo * Updates for admin integration * Unit tests for stock migrations * Added migration test for PartTestTemplate * Fix for AttachmentTable - Rebuild actions when permissions are recalculated * Update test fixtures * Add ModelType information * Fix TableState * Fix dataFormatter type def * Improve table rendering * Correctly filter "edit" and "delete" buttons * Loosen requirements for dataFormatter * Fixtures for report tests * Better API filtering for StocokItemTestResult list - Add Filter class - Add option for filtering against legacy "name" data * Cleanup API filter * Fix unit tests * Further unit test fixes * Include test results for installed stock items * Improve rendering of test result table * Fix filtering for getTestResults * More unit test fixes * Fix more unit tests * FIx part unit test * More fixes * More unit test fixes * Rebuild stock item trees when merging * Helper function for adding a test result to a stock item * Set init fix * Code cleanup * Cleanup unused variables * Add docs and more unit tests * Update build unit test
This commit is contained in:
@ -68,6 +68,8 @@ function getModelRenderer(model) {
|
||||
return renderPartCategory;
|
||||
case 'partparametertemplate':
|
||||
return renderPartParameterTemplate;
|
||||
case 'parttesttemplate':
|
||||
return renderPartTestTemplate;
|
||||
case 'purchaseorder':
|
||||
return renderPurchaseOrder;
|
||||
case 'salesorder':
|
||||
@ -483,6 +485,18 @@ function renderPartParameterTemplate(data, parameters={}) {
|
||||
}
|
||||
|
||||
|
||||
function renderPartTestTemplate(data, parameters={}) {
|
||||
|
||||
return renderModel(
|
||||
{
|
||||
text: data.test_name,
|
||||
textSecondary: data.description,
|
||||
},
|
||||
parameters
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "ManufacturerPart" model
|
||||
function renderManufacturerPart(data, parameters={}) {
|
||||
|
||||
|
@ -2867,6 +2867,18 @@ function loadPartTestTemplateTable(table, options) {
|
||||
field: 'test_name',
|
||||
title: '{% trans "Test Name" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
let html = value;
|
||||
|
||||
if (row.results && row.results > 0) {
|
||||
html += `
|
||||
<span class='badge bg-dark rounded-pill float-right' title='${row.results} {% trans "results" %}'>
|
||||
${row.results}
|
||||
</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
@ -2909,7 +2921,7 @@ function loadPartTestTemplateTable(table, options) {
|
||||
} else {
|
||||
var text = '{% trans "This test is defined for a parent part" %}';
|
||||
|
||||
return renderLink(text, `/part/${row.part}/tests/`);
|
||||
return renderLink(text, `/part/${row.part}/?display=test-templates`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1381,7 +1381,11 @@ function formatDate(row) {
|
||||
/* Construct set of default fields for a StockItemTestResult */
|
||||
function stockItemTestResultFields(options={}) {
|
||||
let fields = {
|
||||
test: {},
|
||||
template: {
|
||||
filters: {
|
||||
include_inherited: true,
|
||||
}
|
||||
},
|
||||
result: {},
|
||||
value: {},
|
||||
attachment: {},
|
||||
@ -1393,6 +1397,10 @@ function stockItemTestResultFields(options={}) {
|
||||
},
|
||||
};
|
||||
|
||||
if (options.part) {
|
||||
fields.template.filters.part = options.part;
|
||||
}
|
||||
|
||||
if (options.stock_item) {
|
||||
fields.stock_item.value = options.stock_item;
|
||||
}
|
||||
@ -1412,6 +1420,7 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
let params = {
|
||||
part: options.part,
|
||||
include_inherited: true,
|
||||
};
|
||||
|
||||
var filters = loadTableFilters(filterKey, params);
|
||||
@ -1424,17 +1433,16 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
let html = '';
|
||||
|
||||
if (row.requires_attachment == false && row.requires_value == false && !row.result) {
|
||||
if (row.parent != parent_node && row.requires_attachment == false && row.requires_value == false && !row.result) {
|
||||
// Enable a "quick tick" option for this test result
|
||||
html += makeIconButton('fa-check-circle icon-green', 'button-test-tick', row.test_name, '{% trans "Pass test" %}');
|
||||
}
|
||||
|
||||
html += makeIconButton('fa-plus icon-green', 'button-test-add', row.test_name, '{% trans "Add test result" %}');
|
||||
html += makeIconButton('fa-plus icon-green', 'button-test-add', row.templateId, '{% trans "Add test result" %}');
|
||||
|
||||
if (!grouped && row.result != null) {
|
||||
var pk = row.pk;
|
||||
html += makeEditButton('button-test-edit', pk, '{% trans "Edit test result" %}');
|
||||
html += makeDeleteButton('button-test-delete', pk, '{% trans "Delete test result" %}');
|
||||
html += makeEditButton('button-test-edit', row.testId, '{% trans "Edit test result" %}');
|
||||
html += makeDeleteButton('button-test-delete', row.testId, '{% trans "Delete test result" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(html);
|
||||
@ -1532,9 +1540,14 @@ function loadStockTestResultsTable(table, options) {
|
||||
],
|
||||
onLoadSuccess: function(tableData) {
|
||||
|
||||
// Set "parent" for each existing row
|
||||
tableData.forEach(function(item, idx) {
|
||||
tableData[idx].parent = parent_node;
|
||||
// Construct an initial dataset based on the returned templates
|
||||
let results = tableData.map((template) => {
|
||||
return {
|
||||
...template,
|
||||
templateId: template.pk,
|
||||
parent: parent_node,
|
||||
results: []
|
||||
};
|
||||
});
|
||||
|
||||
// Once the test template data are loaded, query for test results
|
||||
@ -1545,6 +1558,7 @@ function loadStockTestResultsTable(table, options) {
|
||||
stock_item: options.stock_item,
|
||||
user_detail: true,
|
||||
attachment_detail: true,
|
||||
template_detail: false,
|
||||
ordering: '-date',
|
||||
};
|
||||
|
||||
@ -1561,54 +1575,40 @@ function loadStockTestResultsTable(table, options) {
|
||||
query_params,
|
||||
{
|
||||
success: function(data) {
|
||||
// Iterate through the returned test data
|
||||
data.forEach(function(item) {
|
||||
|
||||
var match = false;
|
||||
var override = false;
|
||||
data.sort((a, b) => {
|
||||
return a.pk < b.pk;
|
||||
}).forEach((row) => {
|
||||
let idx = results.findIndex((template) => {
|
||||
return template.templateId == row.template;
|
||||
});
|
||||
|
||||
// Extract the simplified test key
|
||||
var key = item.key;
|
||||
if (idx > -1) {
|
||||
|
||||
// Attempt to associate this result with an existing test
|
||||
for (var idx = 0; idx < tableData.length; idx++) {
|
||||
results[idx].results.push(row);
|
||||
|
||||
var row = tableData[idx];
|
||||
|
||||
if (key == row.key) {
|
||||
|
||||
item.test_name = row.test_name;
|
||||
item.test_description = row.description;
|
||||
item.required = row.required;
|
||||
|
||||
if (row.result == null) {
|
||||
item.parent = parent_node;
|
||||
tableData[idx] = item;
|
||||
override = true;
|
||||
} else {
|
||||
item.parent = row.pk;
|
||||
}
|
||||
|
||||
match = true;
|
||||
|
||||
break;
|
||||
// Check if a test result is already recorded
|
||||
if (results[idx].testId) {
|
||||
// Push this result into the results array
|
||||
results.push({
|
||||
...results[idx],
|
||||
...row,
|
||||
parent: results[idx].templateId,
|
||||
testId: row.pk,
|
||||
});
|
||||
} else {
|
||||
// First result - update the parent row
|
||||
results[idx] = {
|
||||
...row,
|
||||
...results[idx],
|
||||
testId: row.pk,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// No match could be found
|
||||
if (!match) {
|
||||
item.test_name = item.test;
|
||||
item.parent = parent_node;
|
||||
}
|
||||
|
||||
if (!override) {
|
||||
tableData.push(item);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Push data back into the table
|
||||
table.bootstrapTable('load', tableData);
|
||||
table.bootstrapTable('load', results);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -1645,25 +1645,17 @@ function loadStockTestResultsTable(table, options) {
|
||||
$(table).on('click', '.button-test-add', function() {
|
||||
var button = $(this);
|
||||
|
||||
var test_name = button.attr('pk');
|
||||
var templateId = button.attr('pk');
|
||||
|
||||
let fields = stockItemTestResultFields();
|
||||
|
||||
fields['stock_item']['value'] = options.stock_item;
|
||||
fields['template']['value'] = templateId;
|
||||
fields['template']['filters']['part'] = options.part;
|
||||
|
||||
constructForm('{% url "api-stock-test-result-list" %}', {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
test: {
|
||||
value: test_name,
|
||||
},
|
||||
result: {},
|
||||
value: {},
|
||||
attachment: {},
|
||||
notes: {
|
||||
icon: 'fa-sticky-note',
|
||||
},
|
||||
stock_item: {
|
||||
value: options.stock_item,
|
||||
hidden: true,
|
||||
}
|
||||
},
|
||||
fields: fields,
|
||||
title: '{% trans "Add Test Result" %}',
|
||||
onSuccess: reloadTestTable,
|
||||
});
|
||||
@ -1692,11 +1684,9 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
var url = `/api/stock/test/${pk}/`;
|
||||
|
||||
var row = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
<strong>{% trans "Delete test result" %}:</strong> ${row.test_name || row.test || row.key}
|
||||
<strong>{% trans "Delete test result" %}</strong>
|
||||
</div>`;
|
||||
|
||||
constructForm(url, {
|
||||
|
Reference in New Issue
Block a user