mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Parameter filtering (#4823)
* adds new field 'parameter type' to PartParameterTemplate model * Move part parameter settings onto their own page * Add "choices" and "regex" template types * Adds validation for PartParameter based on template type * javascript cleanup * Fix for serializers.py * Add unit testing for parameter validation * Add filters * Rename "type" field to "param_type" - Should have seen that one coming * Coerce 'boolean' value to True/False * table update * js linting * Add requirement for "pint" package * Add validator for physical unit types - Revert a previous migration which adds "parameter type" and "validator" fields - These will get implemented later, too much scope creep for this PR - Add unit test for validation of "units" field * Update PartParameter model - Add data_numeric field (will be used later) - Add MinLengthValidator to data field * Run validation for part parameter data - Ensure it can be converted to internal units * Update admin interface to display partparameter values inline for a part * Adds validation of part parameter data value - Also converts to base units, and stores as "numeric" value - Display "numeric" value in tables - Create new file conversion.py for data conversion * Update unit tests and fix some bugs * Update docstring * Add units to parameter columns in parameteric part table * Allow part list to be ordered by a particular parameter value - Annotate queryset with new "order_by_parameter" method - Skeleton method for future work * Bump API version * Adds unit testing for sorting parts by parameter value * Update historical data migrations - Turns out RunPython.noop is a thing? * Cache the unit registry - Creating the unit registry takes a significant amount of time - Construct when first called, and then cache for subsequent hits - Massive improvement in performance * Throw error on empty values when converting between units * Data migration for converting existing part parameter values * Handle more error cases * Show parameteric table on top-level part page too * Unit test for data migration * Update credits in docs * Improved error checking * WIP docs updates * Fix parameteric table filtering * remove zoom property * Fix for import path * Update parameter docs * Run background task to rebuild parameters when template changes * Make "data_numeric" field nullable - Defaulting to zero is not appropriate, as the actual value may be zero - Sorting still seems to work just fine * Fixes for unit test * More unit test fixes * Further fixes for unit tests --------- Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
@ -55,19 +55,4 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<span class='d-flex flex-span'>
|
||||
<h4>{% trans "Part Parameter Templates" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
<button class='btn btn-success' id='new-param'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='param-table' data-toolbar='#param-buttons'>
|
||||
</table>
|
||||
|
||||
{% endblock content %}
|
||||
|
25
InvenTree/templates/InvenTree/settings/part_parameters.html
Normal file
25
InvenTree/templates/InvenTree/settings/part_parameters.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "panel.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block label %}part-parameters{% endblock label %}
|
||||
|
||||
{% block heading %}
|
||||
{% trans "Part Parameter Templates" %}
|
||||
{% endblock heading %}
|
||||
|
||||
{% block actions %}
|
||||
<button class='btn btn-success' id='new-param'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
|
||||
</button>
|
||||
{% endblock actions %}
|
||||
|
||||
{% block content %}
|
||||
<div id='param-buttons'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="parameter-templates" %}
|
||||
</div>
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='param-table' data-toolbar='#param-buttons'>
|
||||
</table>
|
||||
|
||||
{% endblock content %}
|
@ -36,6 +36,7 @@
|
||||
{% include "InvenTree/settings/label.html" %}
|
||||
{% include "InvenTree/settings/report.html" %}
|
||||
{% include "InvenTree/settings/part.html" %}
|
||||
{% include "InvenTree/settings/part_parameters.html" %}
|
||||
{% include "InvenTree/settings/part_stocktake.html" %}
|
||||
{% include "InvenTree/settings/category.html" %}
|
||||
{% include "InvenTree/settings/pricing.html" %}
|
||||
|
@ -302,50 +302,10 @@ onPanelLoad('category', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Javascript for the Part settings panel
|
||||
onPanelLoad('parts', function() {
|
||||
$("#param-table").inventreeTable({
|
||||
url: "{% url 'api-part-parameter-template-list' %}",
|
||||
queryParams: {
|
||||
ordering: 'name',
|
||||
},
|
||||
formatNoMatches: function() { return '{% trans "No part parameter templates found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: '{% trans "ID" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'units',
|
||||
title: '{% trans "Units" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
formatter: function(value, row, index, field) {
|
||||
var bEdit = "<button title='{% trans "Edit Template" %}' class='template-edit btn btn-outline-secondary' type='button' pk='" + row.pk + "'><span class='fas fa-edit icon-green'></span></button>";
|
||||
var bDel = "<button title='{% trans "Delete Template" %}' class='template-delete btn btn-outline-secondary' type='button' pk='" + row.pk + "'><span class='fas fa-trash-alt icon-red'></span></button>";
|
||||
// Javascript for the Part parameters settings panel
|
||||
onPanelLoad('part-parameters', function() {
|
||||
|
||||
var html = "<div class='btn-group float-right' role='group'>" + bEdit + bDel + "</div>";
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
loadPartParameterTemplateTable("#param-table", {});
|
||||
|
||||
$("#new-param").click(function() {
|
||||
constructForm('{% url "api-part-parameter-template-list" %}', {
|
||||
@ -359,45 +319,10 @@ onPanelLoad('parts', function() {
|
||||
refreshTable: '#param-table',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$("#param-table").on('click', '.template-edit', function() {
|
||||
var button = $(this);
|
||||
var pk = button.attr('pk');
|
||||
|
||||
constructForm(
|
||||
`/api/part/parameter/template/${pk}/`,
|
||||
{
|
||||
fields: {
|
||||
name: {},
|
||||
units: {},
|
||||
description: {},
|
||||
},
|
||||
title: '{% trans "Edit Part Parameter Template" %}',
|
||||
refreshTable: '#param-table',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$("#param-table").on('click', '.template-delete', function() {
|
||||
var button = $(this);
|
||||
var pk = button.attr('pk');
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "Any parameters which reference this template will also be deleted" %}
|
||||
</div>`;
|
||||
|
||||
constructForm(
|
||||
`/api/part/parameter/template/${pk}/`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
preFormContent: html,
|
||||
title: '{% trans "Delete Part Parameter Template" %}',
|
||||
refreshTable: '#param-table',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Javascript for the Part settings panel
|
||||
onPanelLoad('parts', function() {
|
||||
$("#import-part").click(function() {
|
||||
launchModalForm("{% url 'api-part-import' %}?reset", {});
|
||||
});
|
||||
|
@ -44,6 +44,8 @@
|
||||
{% include "sidebar_item.html" with label='category' text=text icon="fa-sitemap" %}
|
||||
{% trans "Parts" as text %}
|
||||
{% include "sidebar_item.html" with label='parts' text=text icon="fa-shapes" %}
|
||||
{% trans "Part Parameters" as text %}
|
||||
{% include "sidebar_item.html" with label='part-parameters' text=text icon="fa-th-list" %}
|
||||
{% trans "Stock" as text %}
|
||||
{% include "sidebar_item.html" with label='stock' text=text icon="fa-boxes" %}
|
||||
{% trans "Stocktake" as text %}
|
||||
|
@ -31,6 +31,7 @@
|
||||
loadParametricPartTable,
|
||||
loadPartCategoryTable,
|
||||
loadPartParameterTable,
|
||||
loadPartParameterTemplateTable,
|
||||
loadPartPurchaseOrderTable,
|
||||
loadPartTable,
|
||||
loadPartTestTemplateTable,
|
||||
@ -1286,11 +1287,27 @@ function loadPartParameterTable(table, options) {
|
||||
return row.template_detail.name;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
switchable: true,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
return row.template_detail.description;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'data',
|
||||
title: '{% trans "Value" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (row.data_numeric && row.template_detail.units) {
|
||||
return `<span title='${row.data_numeric} ${row.template_detail.units}'>${row.data}</span>`;
|
||||
} else {
|
||||
return row.data;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'units',
|
||||
@ -1345,6 +1362,107 @@ function loadPartParameterTable(table, options) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a table showing a list of part parameter templates
|
||||
*/
|
||||
function loadPartParameterTemplateTable(table, options={}) {
|
||||
|
||||
let params = options.params || {};
|
||||
|
||||
params.ordering = 'name';
|
||||
|
||||
let filters = loadTableFilters('part-parameter-templates', params);
|
||||
|
||||
let filterTarget = options.filterTarget || '#filter-list-parameter-templates';
|
||||
|
||||
setupFilterList('part-parameter-templates', $(table), filterTarget);
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-part-parameter-template-list" %}',
|
||||
original: params,
|
||||
queryParams: filters,
|
||||
name: 'part-parameter-templates',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No part parameter templates found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: '{% trans "ID" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'units',
|
||||
title: '{% trans "Units" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton('template-edit', row.pk, '{% trans "Edit Template" %}');
|
||||
buttons += makeDeleteButton('template-delete', row.pk, '{% trans "Delete Template" %}');
|
||||
|
||||
return wrapButtons(buttons);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
$(table).on('click', '.template-edit', function() {
|
||||
var button = $(this);
|
||||
var pk = button.attr('pk');
|
||||
|
||||
constructForm(
|
||||
`/api/part/parameter/template/${pk}/`,
|
||||
{
|
||||
fields: {
|
||||
name: {},
|
||||
units: {},
|
||||
description: {},
|
||||
},
|
||||
title: '{% trans "Edit Part Parameter Template" %}',
|
||||
refreshTable: table,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(table).on('click', '.template-delete', function() {
|
||||
var button = $(this);
|
||||
var pk = button.attr('pk');
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "Any parameters which reference this template will also be deleted" %}
|
||||
</div>`;
|
||||
|
||||
constructForm(
|
||||
`/api/part/parameter/template/${pk}/`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
preFormContent: html,
|
||||
title: '{% trans "Delete Part Parameter Template" %}',
|
||||
refreshTable: table,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a table showing a list of purchase orders for a given part.
|
||||
*
|
||||
@ -1663,6 +1781,12 @@ function loadRelatedPartsTable(table, part_id, options={}) {
|
||||
*/
|
||||
function loadParametricPartTable(table, options={}) {
|
||||
|
||||
options.params = options.params || {};
|
||||
|
||||
options.params['parameters'] = true;
|
||||
|
||||
let filters = loadTableFilters('parameters', options.params);
|
||||
|
||||
setupFilterList('parameters', $(table), '#filter-list-parameters');
|
||||
|
||||
var columns = [
|
||||
@ -1691,11 +1815,18 @@ function loadParametricPartTable(table, options={}) {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
for (var template of response) {
|
||||
|
||||
let template_name = template.name;
|
||||
|
||||
if (template.units) {
|
||||
template_name += ` [${template.units}]`;
|
||||
}
|
||||
|
||||
columns.push({
|
||||
field: `parameter_${template.pk}`,
|
||||
title: template.name,
|
||||
title: template_name,
|
||||
switchable: true,
|
||||
sortable: false,
|
||||
sortable: true,
|
||||
filterControl: 'input',
|
||||
});
|
||||
}
|
||||
@ -1703,20 +1834,21 @@ function loadParametricPartTable(table, options={}) {
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: Re-enable filter control for parameter values
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-part-list" %}',
|
||||
queryParams: {
|
||||
category: options.category,
|
||||
cascade: true,
|
||||
parameters: true,
|
||||
},
|
||||
queryParams: filters,
|
||||
original: options.params,
|
||||
groupBy: false,
|
||||
name: options.name || 'part-parameters',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No parts found" %}';
|
||||
},
|
||||
// TODO: Re-enable filter control for parameter values
|
||||
// Ref: https://github.com/inventree/InvenTree/issues/4851
|
||||
// filterControl: true,
|
||||
// showFilterControlSwitch: true,
|
||||
// sortSelectOptions: true,
|
||||
columns: columns,
|
||||
showColumns: true,
|
||||
sidePagination: 'server',
|
||||
@ -1751,8 +1883,8 @@ function loadParametricPartTable(table, options={}) {
|
||||
}
|
||||
|
||||
|
||||
// Generate a "grid tile" view for a particular part
|
||||
function partGridTile(part) {
|
||||
// Generate a "grid tile" view for a particular part
|
||||
|
||||
// Rows for table view
|
||||
var rows = '';
|
||||
@ -1822,6 +1954,8 @@ function partGridTile(part) {
|
||||
*/
|
||||
function loadPartTable(table, url, options={}) {
|
||||
|
||||
options.params = options.params || {};
|
||||
|
||||
// Ensure category detail is included
|
||||
options.params['category_detail'] = true;
|
||||
|
||||
|
@ -55,7 +55,6 @@ function renderStatusLabel(key, codes, options={}) {
|
||||
return `<span class='${classes}'>${text}</span>`;
|
||||
}
|
||||
|
||||
|
||||
{% include "status_codes.html" with label='stock' data=StockStatus.list %}
|
||||
{% include "status_codes.html" with label='stockHistory' data=StockHistoryCode.list %}
|
||||
{% include "status_codes.html" with label='build' data=BuildStatus.list %}
|
||||
|
@ -700,6 +700,19 @@ function getCompanyFilters() {
|
||||
}
|
||||
|
||||
|
||||
// Return a dictionary of filters for the "part parameter template" table
|
||||
function getPartParameterTemplateFilters() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// Return a dictionary of filters for the "parameteric part" table
|
||||
function getParametricPartTableFilters() {
|
||||
let filters = getPartTableFilters();
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
// Return a dictionary of filters for a given table, based on the name of the table
|
||||
function getAvailableTableFilters(tableKey) {
|
||||
@ -723,6 +736,10 @@ function getAvailableTableFilters(tableKey) {
|
||||
return getBuildItemTableFilters();
|
||||
case 'location':
|
||||
return getStockLocationFilters();
|
||||
case 'parameters':
|
||||
return getParametricPartTableFilters();
|
||||
case 'part-parameter-templates':
|
||||
return getPartParameterTemplateFilters();
|
||||
case 'parts':
|
||||
return getPartTableFilters();
|
||||
case 'parttests':
|
||||
|
Reference in New Issue
Block a user