{% endblock %}
{% block js_ready %}
{{ block.super }}
var buildTable = $("#build-item-list");
// Calculate sum of allocations for a particular table row
function sumAllocations(row) {
if (row.allocations == null) {
return 0;
}
var quantity = 0;
row.allocations.forEach(function(item) {
quantity += item.quantity;
});
return quantity;
}
function getUnallocated(row) {
// Return the number of items remaining to be allocated for a given row
return {{ build.quantity }} * row.quantity - sumAllocations(row);
}
function reloadTable() {
// Reload the build allocation table
buildTable.bootstrapTable('refresh');
}
function setupCallbacks() {
// Register button callbacks once the table data are loaded
buildTable.find(".button-add").click(function() {
var pk = $(this).attr('pk');
// Extract row data from the table
var idx = $(this).closest('tr').attr('data-index');
var row = buildTable.bootstrapTable('getData')[idx];
launchModalForm('/build/item/new/', {
success: reloadTable,
data: {
part: row.sub_part,
build: {{ build.id }},
quantity: getUnallocated(row),
},
secondary: [
{
field: 'stock_item',
label: '{% trans "New Stock Item" %}',
title: '{% trans "Create new Stock Item"',
url: '{% url "stock-item-create" %}',
data: {
part: row.sub_part,
},
},
]
});
});
buildTable.find(".button-build").click(function() {
// Start a new build for the sub_part
var pk = $(this).attr('pk');
// Extract row data from the table
var idx = $(this).closest('tr').attr('data-index');
var row = buildTable.bootstrapTable('getData')[idx];
launchModalForm('/build/new/', {
follow: true,
data: {
part: row.sub_part,
parent: {{ build.id }},
quantity: getUnallocated(row),
},
});
});
buildTable.find(".button-buy").click(function() {
var pk = $(this).attr('pk');
// Extract row data from the table
var idx = $(this).closest('tr').attr('data-index');
var row = buildTable.bootstrapTable('getData')[idx];
launchModalForm("{% url 'order-parts' %}", {
data: {
parts: [row.sub_part],
},
});
});
}
buildTable.inventreeTable({
uniqueId: 'sub_part',
url: "{% url 'api-bom-list' %}",
onPostBody: setupCallbacks,
detailViewByClick: true,
detailView: true,
detailFilter: function(index, row) {
return row.allocations != null;
},
detailFormatter: function(index, row, element) {
// Construct an 'inner table' which shows the stock allocations
var subTableId = `allocation-table-${row.pk}`;
var html = `
`;
element.html(html);
var lineItem = row;
var subTable = $(`#${subTableId}`);
subTable.bootstrapTable({
data: row.allocations,
showHeader: false,
columns: [
{
width: '50%',
field: 'quantity',
title: 'Quantity',
formatter: function(value, row, index, field) {
var text = '';
var url = '';
if (row.serial && row.quantity == 1) {
text = `{% trans "Serial Number" %}: ${row.serial}`;
} else {
text = `{% trans "Quantity" %}: ${row.quantity}`;
}
{% if build.status == BuildStatus.COMPLETE %}
url = `/stock/item/${row.pk}/`;
{% else %}
url = `/stock/item/${row.stock_item}/`;
{% endif %}
return renderLink(text, url);
},
},
{
field: 'location',
title: '{% trans "Location" %}',
formatter: function(value, row, index, field) {
{% if build.status == BuildStatus.COMPLETE %}
var text = row.location_detail.pathstring;
var url = `/stock/location/${row.location}/`;
{% else %}
var text = row.stock_item_detail.location_name;
var url = `/stock/location/${row.stock_item_detail.location}/`;
{% endif %}
return renderLink(text, url);
}
},
{% if build.status == BuildStatus.PENDING %}
{
field: 'buttons',
title: 'Actions',
formatter: function(value, row) {
var pk = row.pk;
var html = `
`;
{% if build.status == BuildStatus.PENDING %}
html += makeIconButton('fa-edit', 'button-allocation-edit', pk, '{% trans "Edit stock allocation" %}');
html += makeIconButton('fa-trash-alt', 'button-allocation-delete', pk, '{% trans "Delete stock allocation" %}');
{% endif %}
html += `
`;
return html;
},
},
{% endif %}
]
});
// Assign button callbacks to the newly created allocation buttons
subTable.find(".button-allocation-edit").click(function() {
var pk = $(this).attr('pk');
launchModalForm(`/build/item/${pk}/edit/`, {
success: reloadTable,
});
});
subTable.find('.button-allocation-delete').click(function() {
var pk = $(this).attr('pk');
launchModalForm(`/build/item/${pk}/delete/`, {
success: reloadTable,
});
});
},
formatNoMatches: function() { return "{% trans 'No BOM items found' %}"; },
onLoadSuccess: function(tableData) {
// Once the BOM data are loaded, request allocation data for the build
{% if build.status == BuildStatus.COMPLETE %}
// Request StockItem which have been assigned to this build
inventreeGet('/api/stock/',
{
build_order: {{ build.id }},
location_detail: true,
},
{
success: function(data) {
// Iterate through the returned data, group by "part",
var allocations = {};
data.forEach(function(item) {
// Group allocations by referenced 'part'
var key = parseInt(item.part);
if (!(key in allocations)) {
allocations[key] = new Array();
}
allocations[key].push(item);
});
for (var key in allocations) {
var tableRow = buildTable.bootstrapTable('getRowByUniqueId', key);
tableRow.allocations = allocations[key];
buildTable.bootstrapTable('updateByUniqueId', key, tableRow, true);
}
},
},
);
{% else %}
inventreeGet('/api/build/item/',
{
build: {{ build.id }},
},
{
success: function(data) {
// Iterate through the returned data, and group by "part"
var allocations = {};
data.forEach(function(item) {
// Group allocations by referenced 'part'
var part = item.part;
var key = parseInt(part);
if (!(key in allocations)) {
allocations[key] = new Array();
}
// Add the allocation to the list
allocations[key].push(item);
});
for (var key in allocations) {
// Select the associated row in the table
var tableRow = buildTable.bootstrapTable('getRowByUniqueId', key);
// Set the allocations for the row
tableRow.allocations = allocations[key];
// And push the updated row back into the main table
buildTable.bootstrapTable('updateByUniqueId', key, tableRow, true);
}
}
},
);
{% endif %}
},
queryParams: {
part: {{ build.part.id }},
sub_part_detail: 1,
},
columns: [
{
field: 'id',
visible: false,
},
{
sortable: true,
field: 'sub_part',
title: '{% trans "Part" %}',
formatter: function(value, row, index, field) {
return imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, `/part/${row.sub_part}/`);
},
},
{
sortable: true,
field: 'sub_part_detail.description',
title: '{% trans "Description" %}',
},
{
sortable: true,
field: 'reference',
title: '{% trans "Reference" %}',
},
{
sortable: true,
field: 'quantity',
title: '{% trans "Required" %}',
formatter: function(value, row) {
return value * {{ build.quantity }};
},
},
{
sortable: true,
field: 'allocated',
{% if build.status == BuildStatus.COMPLETE %}
title: '{% trans "Assigned" %}',
{% else %}
title: '{% trans "Allocated" %}',
{% endif %}
formatter: function(value, row) {
var allocated = sumAllocations(row);
return makeProgressBar(allocated, row.quantity * {{ build.quantity }});
},
sorter: function(valA, valB, rowA, rowB) {
var aA = sumAllocations(rowA);
var aB = sumAllocations(rowB);
var qA = rowA.quantity * {{ build.quantity }};
var qB = rowB.quantity * {{ build.quantity }};
if (aA == 0 && aB == 0) {
return (qA > qB) ? 1 : -1;
}
var progressA = parseFloat(aA) / qA;
var progressB = parseFloat(aB) / qB;
return (progressA < progressB) ? 1 : -1;
}
},
{% if build.status == BuildStatus.PENDING %}
{
field: 'buttons',
formatter: function(value, row, index, field) {
var html = `
`;
var pk = row.sub_part;
{% if build.status == BuildStatus.PENDING %}
if (row.sub_part_detail.purchaseable) {
html += makeIconButton('fa-shopping-cart', 'button-buy', pk, '{% trans "Buy parts" %}');
}
if (row.sub_part_detail.assembly) {
html += makeIconButton('fa-tools', 'button-build', pk, '{% trans "Build parts" %}');
}
html += makeIconButton('fa-plus', 'button-add', pk, '{% trans "Allocate stock" %}');
{% endif %}
html += '