mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 04:55:44 +00:00
Merge branch 'inventree:master' into matmair/issue2279
This commit is contained in:
@ -40,12 +40,6 @@ function constructBomUploadTable(data, options={}) {
|
||||
function constructRow(row, idx, fields) {
|
||||
// Construct an individual row from the provided data
|
||||
|
||||
var errors = {};
|
||||
|
||||
if (data.errors && data.errors.length > idx) {
|
||||
errors = data.errors[idx];
|
||||
}
|
||||
|
||||
var field_options = {
|
||||
hideLabels: true,
|
||||
hideClearButton: true,
|
||||
@ -60,7 +54,7 @@ function constructBomUploadTable(data, options={}) {
|
||||
return `Cannot render field '${field_name}`;
|
||||
}
|
||||
|
||||
field.value = row[field_name];
|
||||
field.value = row.data[field_name];
|
||||
|
||||
return constructField(`items_${field_name}_${idx}`, field, field_options);
|
||||
|
||||
@ -99,19 +93,19 @@ function constructBomUploadTable(data, options={}) {
|
||||
$('#bom-import-table tbody').append(html);
|
||||
|
||||
// Handle any errors raised by initial data import
|
||||
if (errors.part) {
|
||||
addFieldErrorMessage(`items_sub_part_${idx}`, errors.part);
|
||||
if (row.data.errors.part) {
|
||||
addFieldErrorMessage(`items_sub_part_${idx}`, row.data.errors.part);
|
||||
}
|
||||
|
||||
if (errors.quantity) {
|
||||
addFieldErrorMessage(`items_quantity_${idx}`, errors.quantity);
|
||||
if (row.data.errors.quantity) {
|
||||
addFieldErrorMessage(`items_quantity_${idx}`, row.data.errors.quantity);
|
||||
}
|
||||
|
||||
// Initialize the "part" selector for this row
|
||||
initializeRelatedField(
|
||||
{
|
||||
name: `items_sub_part_${idx}`,
|
||||
value: row.part,
|
||||
value: row.data.part,
|
||||
api_url: '{% url "api-part-list" %}',
|
||||
filters: {
|
||||
component: true,
|
||||
@ -140,7 +134,12 @@ function constructBomUploadTable(data, options={}) {
|
||||
});
|
||||
|
||||
// Prettify the original import data
|
||||
var pretty = JSON.stringify(row, undefined, 4);
|
||||
var pretty = JSON.stringify(
|
||||
{
|
||||
columns: data.columns,
|
||||
row: row.original,
|
||||
}, undefined, 4
|
||||
);
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block'>
|
||||
@ -176,7 +175,7 @@ function submitBomTable(part_id, options={}) {
|
||||
|
||||
var idx_values = [];
|
||||
|
||||
var url = '{% url "api-bom-upload" %}';
|
||||
var url = '{% url "api-bom-import-submit" %}';
|
||||
|
||||
$('.bom-import-row').each(function() {
|
||||
var idx = $(this).attr('idx');
|
||||
|
@ -31,6 +31,7 @@
|
||||
setFormInputPlaceholder,
|
||||
setFormGroupVisibility,
|
||||
showFormInput,
|
||||
selectImportFields,
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -895,8 +896,8 @@ function getFormFieldValue(name, field={}, options={}) {
|
||||
// Find the HTML element
|
||||
var el = getFormFieldElement(name, options);
|
||||
|
||||
if (!el) {
|
||||
console.log(`ERROR: getFormFieldValue could not locate field '{name}'`);
|
||||
if (!el.exists()) {
|
||||
console.log(`ERROR: getFormFieldValue could not locate field '${name}'`);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1219,7 +1220,7 @@ function addFieldErrorMessage(name, error_text, error_idx=0, options={}) {
|
||||
|
||||
field_dom.append(error_html);
|
||||
} else {
|
||||
console.log(`WARNING: addFieldErrorMessage could not locate field '${field_name}`);
|
||||
console.log(`WARNING: addFieldErrorMessage could not locate field '${field_name}'`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2080,7 +2081,7 @@ function constructLabel(name, parameters) {
|
||||
* - parameters: Field parameters returned by the OPTIONS method
|
||||
*
|
||||
*/
|
||||
function constructInput(name, parameters, options) {
|
||||
function constructInput(name, parameters, options={}) {
|
||||
|
||||
var html = '';
|
||||
|
||||
@ -2422,3 +2423,117 @@ function constructHelpText(name, parameters) {
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a dialog to select import fields
|
||||
*/
|
||||
function selectImportFields(url, data={}, options={}) {
|
||||
|
||||
if (!data.model_fields) {
|
||||
console.log(`WARNING: selectImportFields is missing 'model_fields'`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.file_fields) {
|
||||
console.log(`WARNING: selectImportFields is missing 'file_fields'`);
|
||||
return;
|
||||
}
|
||||
|
||||
var choices = [];
|
||||
|
||||
// Add an "empty" value
|
||||
choices.push({
|
||||
value: '',
|
||||
display_name: '-----',
|
||||
});
|
||||
|
||||
for (const [name, field] of Object.entries(data.model_fields)) {
|
||||
choices.push({
|
||||
value: name,
|
||||
display_name: field.label || name,
|
||||
});
|
||||
}
|
||||
|
||||
var rows = '';
|
||||
|
||||
var field_names = Object.keys(data.file_fields);
|
||||
|
||||
for (var idx = 0; idx < field_names.length; idx++) {
|
||||
|
||||
var field_name = field_names[idx];
|
||||
|
||||
var choice_input = constructInput(
|
||||
`column_${idx}`,
|
||||
{
|
||||
type: 'choice',
|
||||
label: field_name,
|
||||
value: data.file_fields[field_name].value,
|
||||
choices: choices,
|
||||
}
|
||||
);
|
||||
|
||||
rows += `<tr><td><em>${field_name}</em></td><td>${choice_input}</td></tr>`;
|
||||
}
|
||||
|
||||
var headers = `<tr><th>{% trans "File Column" %}</th><th>{% trans "Field Name" %}</th></tr>`;
|
||||
|
||||
var html = '';
|
||||
|
||||
if (options.preamble) {
|
||||
html += options.preamble;
|
||||
}
|
||||
|
||||
html += `<table class='table table-condensed'>${headers}${rows}</table>`;
|
||||
|
||||
constructForm(url, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Select Columns" %}',
|
||||
fields: {},
|
||||
preFormContent: html,
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
var columns = [];
|
||||
|
||||
for (var idx = 0; idx < field_names.length; idx++) {
|
||||
columns.push(getFormFieldValue(`column_${idx}`, {}, opts));
|
||||
}
|
||||
|
||||
$(opts.modal).find('#modal-progress-spinner').show();
|
||||
|
||||
inventreePut(
|
||||
opts.url,
|
||||
{
|
||||
columns: columns,
|
||||
rows: data.rows,
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
success: function(response) {
|
||||
handleFormSuccess(response, opts);
|
||||
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
|
||||
$(opts.modal).find('#modal-progress-spinner').hide();
|
||||
|
||||
switch (xhr.status) {
|
||||
case 400:
|
||||
handleFormErrors(xhr.responseJSON, fields, opts);
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
console.log(`upload error at ${opts.url}`);
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -893,6 +893,9 @@ function handleModalForm(url, options) {
|
||||
// Re-enable the modal
|
||||
modalEnable(modal, true);
|
||||
if ('form_valid' in response) {
|
||||
// Get visibility option of error message
|
||||
var hideErrorMessage = (options.hideErrorMessage === undefined) ? true : options.hideErrorMessage;
|
||||
|
||||
// Form data was validated correctly
|
||||
if (response.form_valid) {
|
||||
$(modal).modal('hide');
|
||||
@ -901,7 +904,7 @@ function handleModalForm(url, options) {
|
||||
// Form was returned, invalid!
|
||||
|
||||
// Disable error message with option or response
|
||||
if (!options.hideErrorMessage && !response.hideErrorMessage) {
|
||||
if (!hideErrorMessage && !response.hideErrorMessage) {
|
||||
var warningDiv = $(modal).find('#form-validation-warning');
|
||||
warningDiv.css('display', 'block');
|
||||
}
|
||||
|
@ -46,6 +46,7 @@
|
||||
editStockLocation,
|
||||
exportStock,
|
||||
findStockItemBySerialNumber,
|
||||
installStockItem,
|
||||
loadInstalledInTable,
|
||||
loadStockAllocationTable,
|
||||
loadStockLocationTable,
|
||||
@ -1227,14 +1228,42 @@ function formatDate(row) {
|
||||
return html;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load StockItemTestResult table
|
||||
*/
|
||||
function loadStockTestResultsTable(table, options) {
|
||||
/*
|
||||
* Load StockItemTestResult table
|
||||
*/
|
||||
|
||||
// Setup filters for the table
|
||||
var filterTarget = options.filterTarget || '#filter-list-stocktests';
|
||||
|
||||
var filterKey = options.filterKey || options.name || 'stocktests';
|
||||
|
||||
var filters = loadTableFilters(filterKey);
|
||||
|
||||
var params = {
|
||||
part: options.part,
|
||||
};
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var k in params) {
|
||||
original[k] = params[k];
|
||||
filters[k] = params[k];
|
||||
}
|
||||
|
||||
setupFilterList(filterKey, table, filterTarget);
|
||||
|
||||
function makeButtons(row, grouped) {
|
||||
|
||||
// Helper function for rendering buttons
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
if (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" %}');
|
||||
|
||||
if (!grouped && row.result != null) {
|
||||
@ -1258,14 +1287,13 @@ function loadStockTestResultsTable(table, options) {
|
||||
rootParentId: parent_node,
|
||||
parentIdField: 'parent',
|
||||
idField: 'pk',
|
||||
uniqueId: 'key',
|
||||
uniqueId: 'pk',
|
||||
treeShowField: 'test_name',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No test results found" %}';
|
||||
},
|
||||
queryParams: {
|
||||
part: options.part,
|
||||
},
|
||||
queryParams: filters,
|
||||
original: original,
|
||||
onPostBody: function() {
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
@ -1401,6 +1429,102 @@ function loadStockTestResultsTable(table, options) {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/* Register button callbacks */
|
||||
|
||||
function reloadTestTable(response) {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
|
||||
// "tick" a test result
|
||||
$(table).on('click', '.button-test-tick', function() {
|
||||
var button = $(this);
|
||||
|
||||
var test_name = button.attr('pk');
|
||||
|
||||
inventreePut(
|
||||
'{% url "api-stock-test-result-list" %}',
|
||||
{
|
||||
test: test_name,
|
||||
result: true,
|
||||
stock_item: options.stock_item,
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
success: reloadTestTable,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Add a test result
|
||||
$(table).on('click', '.button-test-add', function() {
|
||||
var button = $(this);
|
||||
|
||||
var test_name = button.attr('pk');
|
||||
|
||||
constructForm('{% url "api-stock-test-result-list" %}', {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
test: {
|
||||
value: test_name,
|
||||
},
|
||||
result: {},
|
||||
value: {},
|
||||
attachment: {},
|
||||
notes: {},
|
||||
stock_item: {
|
||||
value: options.stock_item,
|
||||
hidden: true,
|
||||
}
|
||||
},
|
||||
title: '{% trans "Add Test Result" %}',
|
||||
onSuccess: reloadTestTable,
|
||||
});
|
||||
});
|
||||
|
||||
// Edit a test result
|
||||
$(table).on('click', '.button-test-edit', function() {
|
||||
var button = $(this);
|
||||
|
||||
var pk = button.attr('pk');
|
||||
|
||||
var url = `/api/stock/test/${pk}/`;
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
test: {},
|
||||
result: {},
|
||||
value: {},
|
||||
attachment: {},
|
||||
notes: {},
|
||||
},
|
||||
title: '{% trans "Edit Test Result" %}',
|
||||
onSuccess: reloadTestTable,
|
||||
});
|
||||
});
|
||||
|
||||
// Delete a test result
|
||||
$(table).on('click', '.button-test-delete', function() {
|
||||
var button = $(this);
|
||||
|
||||
var pk = button.attr('pk');
|
||||
|
||||
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}
|
||||
</div>`;
|
||||
|
||||
constructForm(url, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Test Result" %}',
|
||||
onSuccess: reloadTestTable,
|
||||
preFormContent: html,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -2837,3 +2961,67 @@ function loadInstalledInTable(table, options) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch a dialog to install a stock item into another stock item
|
||||
*/
|
||||
function installStockItem(stock_item_id, part_id, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
<strong>{% trans "Install another stock item into this item" %}</strong><br>
|
||||
{% trans "Stock items can only be installed if they meet the following criteria" %}:<br>
|
||||
<ul>
|
||||
<li>{% trans "The Stock Item links to a Part which is the BOM for this Stock Item" %}</li>
|
||||
<li>{% trans "The Stock Item is currently available in stock" %}</li>
|
||||
<li>{% trans "The Stock Item is serialized and does not belong to another item" %}</li>
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
constructForm(
|
||||
`/api/stock/${stock_item_id}/install/`,
|
||||
{
|
||||
method: 'POST',
|
||||
fields: {
|
||||
part: {
|
||||
type: 'related field',
|
||||
required: 'true',
|
||||
label: '{% trans "Part" %}',
|
||||
help_text: '{% trans "Select part to install" %}',
|
||||
model: 'part',
|
||||
api_url: '{% url "api-part-list" %}',
|
||||
auto_fill: true,
|
||||
filters: {
|
||||
trackable: true,
|
||||
in_bom_for: part_id,
|
||||
}
|
||||
},
|
||||
stock_item: {
|
||||
filters: {
|
||||
part_detail: true,
|
||||
in_stock: true,
|
||||
serialized: true,
|
||||
},
|
||||
adjustFilters: function(filters, opts) {
|
||||
var part = getFormFieldValue('part', {}, opts);
|
||||
|
||||
if (part) {
|
||||
filters.part = part;
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
},
|
||||
confirm: true,
|
||||
title: '{% trans "Install Stock Item" %}',
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -265,12 +265,7 @@ function getAvailableTableFilters(tableKey) {
|
||||
|
||||
// Filters for the 'stock test' table
|
||||
if (tableKey == 'stocktests') {
|
||||
return {
|
||||
result: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Test result" %}',
|
||||
},
|
||||
};
|
||||
return {};
|
||||
}
|
||||
|
||||
// Filters for the 'part test template' table
|
||||
|
Reference in New Issue
Block a user