mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-01 21:16:46 +00:00
430 lines
12 KiB
JavaScript
430 lines
12 KiB
JavaScript
{% load i18n %}
|
|
|
|
/* BOM management functions.
|
|
* Requires follwing files to be loaded first:
|
|
* - api.js
|
|
* - part.js
|
|
* - modals.js
|
|
*/
|
|
|
|
|
|
function reloadBomTable(table, options) {
|
|
|
|
table.bootstrapTable('refresh');
|
|
}
|
|
|
|
|
|
function removeRowFromBomWizard(e) {
|
|
/* Remove a row from BOM upload wizard
|
|
*/
|
|
|
|
e = e || window.event;
|
|
|
|
var src = e.target || e.srcElement;
|
|
|
|
var table = $(src).closest('table');
|
|
|
|
// Which column was clicked?
|
|
var row = $(src).closest('tr');
|
|
|
|
row.remove();
|
|
|
|
var rowNum = 1;
|
|
var colNum = 0;
|
|
|
|
table.find('tr').each(function() {
|
|
|
|
colNum++;
|
|
|
|
if (colNum >= 3) {
|
|
var cell = $(this).find('td:eq(1)');
|
|
cell.text(rowNum++);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function removeColFromBomWizard(e) {
|
|
/* Remove a column from BOM upload wizard
|
|
*/
|
|
|
|
e = e || window.event;
|
|
|
|
var src = e.target || e.srcElement;
|
|
|
|
// Which column was clicked?
|
|
var col = $(src).closest('th').index();
|
|
|
|
var table = $(src).closest('table');
|
|
|
|
table.find('tr').each(function() {
|
|
this.removeChild(this.cells[col]);
|
|
});
|
|
}
|
|
|
|
|
|
function newPartFromBomWizard(e) {
|
|
/* Create a new part directly from the BOM wizard.
|
|
*/
|
|
|
|
e = e || window.event;
|
|
|
|
var src = e.target || e.srcElement;
|
|
|
|
var row = $(src).closest('tr');
|
|
|
|
launchModalForm('/part/new/', {
|
|
data: {
|
|
'description': row.attr('part-description'),
|
|
'name': row.attr('part-name'),
|
|
},
|
|
success: function(response) {
|
|
/* A new part has been created! Push it as an option.
|
|
*/
|
|
|
|
var select = row.attr('part-select');
|
|
|
|
var option = new Option(response.text, response.pk, true, true);
|
|
$(select).append(option).trigger('change');
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function loadBomTable(table, options) {
|
|
/* Load a BOM table with some configurable options.
|
|
*
|
|
* Following options are available:
|
|
* editable - Should the BOM table be editable?
|
|
* bom_url - Address to request BOM data from
|
|
* part_url - Address to request Part data from
|
|
* parent_id - Parent ID of the owning part
|
|
*
|
|
* BOM data are retrieved from the server via AJAX query
|
|
*/
|
|
|
|
// Construct the table columns
|
|
|
|
var cols = [];
|
|
|
|
if (options.editable) {
|
|
cols.push({
|
|
field: 'ID',
|
|
title: '',
|
|
checkbox: true,
|
|
visible: true,
|
|
switchable: false,
|
|
});
|
|
}
|
|
|
|
// Part column
|
|
cols.push(
|
|
{
|
|
field: 'sub_part',
|
|
title: '{% trans "Part" %}',
|
|
sortable: true,
|
|
formatter: function(value, row, index, field) {
|
|
var url = `/part/${row.sub_part}/`;
|
|
var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url);
|
|
|
|
// Display an extra icon if this part is an assembly
|
|
if (row.sub_part_detail.assembly) {
|
|
var text = `<span title='{% trans "Open subassembly" %}' class='fas fa-stream label-right'></span>`;
|
|
|
|
html += renderLink(text, `/part/${row.sub_part}/bom/`);
|
|
}
|
|
|
|
return html;
|
|
}
|
|
}
|
|
);
|
|
|
|
|
|
// Part description
|
|
cols.push(
|
|
{
|
|
field: 'sub_part_detail.description',
|
|
title: '{% trans "Description" %}',
|
|
}
|
|
);
|
|
|
|
// Part reference
|
|
cols.push({
|
|
field: 'reference',
|
|
title: '{% trans "Reference" %}',
|
|
searchable: true,
|
|
sortable: true,
|
|
});
|
|
|
|
// Part quantity
|
|
cols.push({
|
|
field: 'quantity',
|
|
title: '{% trans "Quantity" %}',
|
|
searchable: false,
|
|
sortable: true,
|
|
formatter: function(value, row, index, field) {
|
|
var text = value;
|
|
|
|
// The 'value' is a text string with (potentially) multiple trailing zeros
|
|
// Let's make it a bit more pretty
|
|
text = parseFloat(text);
|
|
|
|
if (row.optional) {
|
|
text += " ({% trans "Optional" %})";
|
|
}
|
|
|
|
if (row.overage) {
|
|
text += "<small> (+" + row.overage + ") </small>";
|
|
}
|
|
|
|
return text;
|
|
},
|
|
});
|
|
|
|
if (!options.editable) {
|
|
cols.push(
|
|
{
|
|
field: 'sub_part_detail.stock',
|
|
title: '{% trans "Available" %}',
|
|
searchable: false,
|
|
sortable: true,
|
|
formatter: function(value, row, index, field) {
|
|
|
|
var url = `/part/${row.sub_part_detail.pk}/stock/`;
|
|
var text = value;
|
|
|
|
if (value == null || value <= 0) {
|
|
text = `<span class='label label-warning'>{% trans "No Stock" %}</span>`;
|
|
}
|
|
|
|
return renderLink(text, url);
|
|
}
|
|
});
|
|
|
|
cols.push(
|
|
{
|
|
field: 'price_range',
|
|
title: '{% trans "Price" %}',
|
|
sortable: true,
|
|
formatter: function(value, row, index, field) {
|
|
if (value) {
|
|
return value;
|
|
} else {
|
|
return "<span class='warning-msg'>{% trans "No pricing available" %}</span>";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Part notes
|
|
cols.push(
|
|
{
|
|
field: 'note',
|
|
title: '{% trans "Notes" %}',
|
|
searchable: true,
|
|
sortable: true,
|
|
}
|
|
);
|
|
|
|
if (options.editable) {
|
|
|
|
cols.push({
|
|
title: '{% trans "Actions" %}',
|
|
switchable: false,
|
|
field: 'pk',
|
|
visible: true,
|
|
formatter: function(value, row, index, field) {
|
|
|
|
if (row.part == options.parent_id) {
|
|
|
|
var bValidate = `<button title='{% trans "Validate BOM Item" %}' class='bom-validate-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-check-circle icon-blue'/></button>`;
|
|
|
|
var bValid = `<span title='{% trans "This line has been validated" %}' class='fas fa-check-double icon-green'/>`;
|
|
|
|
var bEdit = `<button title='{% trans "Edit BOM Item" %}' class='bom-edit-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-edit'></span></button>`;
|
|
|
|
var bDelt = `<button title='{% trans "Delete BOM Item" %}' class='bom-delete-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-trash-alt icon-red'></span></button>`;
|
|
|
|
var html = "<div class='btn-group' role='group'>";
|
|
|
|
html += bEdit;
|
|
html += bDelt;
|
|
|
|
if (!row.validated) {
|
|
html += bValidate;
|
|
} else {
|
|
html += bValid;
|
|
}
|
|
|
|
html += "</div>";
|
|
|
|
return html;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Configure the table (bootstrap-table)
|
|
|
|
var params = {
|
|
part: options.parent_id,
|
|
ordering: 'name',
|
|
}
|
|
|
|
if (options.part_detail) {
|
|
params.part_detail = true;
|
|
}
|
|
|
|
if (options.sub_part_detail) {
|
|
params.sub_part_detail = true;
|
|
}
|
|
|
|
// Function to request BOM data for sub-items
|
|
// This function may be called recursively for multi-level BOMs
|
|
function requestSubItems(bom_pk, part_pk) {
|
|
|
|
inventreeGet(
|
|
options.bom_url,
|
|
{
|
|
part: part_pk,
|
|
sub_part_detail: true,
|
|
},
|
|
{
|
|
success: function(response) {
|
|
for (var idx = 0; idx < response.length; idx++) {
|
|
|
|
response[idx].parentId = bom_pk;
|
|
|
|
if (response[idx].sub_part_detail.assembly) {
|
|
requestSubItems(response[idx].pk, response[idx].sub_part)
|
|
}
|
|
}
|
|
|
|
table.bootstrapTable('append', response);
|
|
|
|
table.treegrid('collapseAll');
|
|
},
|
|
error: function() {
|
|
console.log('Error requesting BOM for part=' + part_pk);
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
table.inventreeTable({
|
|
treeEnable: !options.editable,
|
|
rootParentId: options.parent_id,
|
|
idField: 'pk',
|
|
//uniqueId: 'pk',
|
|
parentIdField: 'parentId',
|
|
treeShowField: 'sub_part',
|
|
showColumns: true,
|
|
name: 'bom',
|
|
sortable: true,
|
|
search: true,
|
|
rowStyle: function(row, index) {
|
|
if (row.validated) {
|
|
return {classes: 'rowvalid'};
|
|
} else {
|
|
return {classes: 'rowinvalid'};
|
|
}
|
|
},
|
|
formatNoMatches: function() { return "{% trans "No BOM items found" %}"; },
|
|
clickToSelect: true,
|
|
queryParams: params,
|
|
columns: cols,
|
|
url: options.bom_url,
|
|
onPostBody: function() {
|
|
|
|
if (!options.editable) {
|
|
table.treegrid({
|
|
treeColumn: 0,
|
|
onExpand: function() {
|
|
}
|
|
});
|
|
}
|
|
},
|
|
onLoadSuccess: function() {
|
|
|
|
if (options.editable) {
|
|
table.bootstrapTable('uncheckAll');
|
|
} else {
|
|
|
|
var data = table.bootstrapTable('getData');
|
|
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
var row = data[idx];
|
|
|
|
// If a row already has a parent ID set, it's already been updated!
|
|
if (row.parentId) {
|
|
continue;
|
|
}
|
|
|
|
// Set the parent ID of the top-level rows
|
|
row.parentId = options.parent_id;
|
|
|
|
table.bootstrapTable('updateRow', idx, row, true);
|
|
|
|
if (row.sub_part_detail.assembly) {
|
|
requestSubItems(row.pk, row.sub_part);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
// In editing mode, attached editables to the appropriate table elements
|
|
if (options.editable) {
|
|
|
|
table.on('click', '.bom-delete-button', function() {
|
|
|
|
var pk = $(this).attr('pk');
|
|
var url = `/part/bom/${pk}/delete/`;
|
|
|
|
launchModalForm(
|
|
url,
|
|
{
|
|
success: function() {
|
|
reloadBomTable(table);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
|
|
table.on('click', '.bom-edit-button', function() {
|
|
|
|
var pk = $(this).attr('pk');
|
|
var url = `/part/bom/${pk}/edit/`;
|
|
|
|
launchModalForm(
|
|
url,
|
|
{
|
|
success: function() {
|
|
reloadBomTable(table);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
|
|
table.on('click', '.bom-validate-button', function() {
|
|
|
|
var pk = $(this).attr('pk');
|
|
var url = `/api/bom/${pk}/validate/`;
|
|
|
|
inventreePut(
|
|
url,
|
|
{
|
|
valid: true
|
|
},
|
|
{
|
|
method: 'PATCH',
|
|
success: function() {
|
|
reloadBomTable(table);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
}
|
|
} |