diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js
index 3376cb21a0..2c0b56c518 100644
--- a/InvenTree/templates/js/translated/build.js
+++ b/InvenTree/templates/js/translated/build.js
@@ -726,6 +726,35 @@ function loadBuildOrderAllocationTable(table, options={}) {
}
+/* Internal helper functions for performing calculations on BOM data */
+
+// Iterate through a list of allocations, returning *only* those which match a particular BOM row
+function getAllocationsForBomRow(bom_row, allocations) {
+ var part_id = bom_row.sub_part;
+
+ var matching_allocations = [];
+
+ allocations.forEach(function(allocation) {
+ if (allocation.bom_part == part_id) {
+ matching_allocations.push(allocation);
+ }
+ });
+
+ return matching_allocations;
+}
+
+// Sum the allocation quantity for a given BOM row
+function sumAllocationsForBomRow(bom_row, allocations) {
+ var quantity = 0;
+
+ getAllocationsForBomRow(bom_row, allocations).forEach(function(allocation) {
+ quantity += allocation.quantity;
+ });
+
+ return parseFloat(quantity).toFixed(15);
+}
+
+
/*
* Display a "build output" table for a particular build.
*
@@ -919,6 +948,32 @@ function loadBuildOutputTable(build_info, options={}) {
rows.forEach(function(row) {
row.allocations = allocations[row.pk] || [];
$(table).bootstrapTable('updateByUniqueId', row.pk, row, true);
+
+ var n_completed_lines = 0;
+
+ // Check how many BOM lines have been completely allocated for this build output
+ bom_items.forEach(function(bom_item) {
+
+ var required_quantity = bom_item.quantity * row.quantity;
+
+ if (sumAllocationsForBomRow(bom_item, row.allocations) >= required_quantity) {
+ n_completed_lines += 1;
+ }
+
+ var output_progress_bar = $(`#output-progress-${row.pk}`);
+
+ if (output_progress_bar.exists()) {
+ output_progress_bar.html(
+ makeProgressBar(
+ n_completed_lines,
+ bom_items.length,
+ {
+ max_width: '150px',
+ }
+ )
+ );
+ }
+ });
});
}
}
@@ -1092,14 +1147,33 @@ function loadBuildOutputTable(build_info, options={}) {
visible: true,
switchable: false,
formatter: function(value, row) {
- // Display a progress bar which shows how many rows have been allocated
- var n_bom_lines = 0;
+
+ // Display a progress bar which shows how many BOM lines have been fully allocated
+ var n_bom_lines = 1;
+ var n_completed_lines = 0;
- if (bom_items) {
+ // Work out how many lines have been allocated for this build output
+ if (bom_items && row.allocations) {
n_bom_lines = bom_items.length;
+
+ bom_items.forEach(function(bom_row) {
+ var required_quantity = row.quantity * bom_row.quantity;
+
+ if (sumAllocationsForBomRow(bom_row, row.allocations) >= required_quantity) {
+ n_completed_lines += 1;
+ }
+ })
}
- return `lines: ${n_bom_lines}`;
- return `
`;
+
+ var progressBar = makeProgressBar(
+ n_completed_lines,
+ n_bom_lines,
+ {
+ max_width: '150px',
+ }
+ );
+
+ return `${progressBar}
`;
},
sorter: function(value_a, value_b, row_a, row_b) {
// TODO: Custom sorter for "allocated stock" column
@@ -1108,7 +1182,7 @@ function loadBuildOutputTable(build_info, options={}) {
},
{
field: 'tests',
- title: '{% trans "Tests" %}',
+ title: '{% trans "Completed Tests" %}',
sortable: true,
switchable: true,
formatter: function(value, row) {
@@ -1186,6 +1260,26 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
outputId = 'untracked';
}
+ var bom_items = buildInfo.bom_items || null;
+
+ // If BOM items have not been provided, load via the API
+ if (bom_items == null) {
+ inventreeGet(
+ '{% url "api-bom-list" %}',
+ {
+ part: partId,
+ sub_part_detail: true,
+ sub_part_trackable: trackable,
+ },
+ {
+ async: false,
+ success: function(results) {
+ bom_items = results;
+ }
+ }
+ );
+ }
+
var table = options.table;
if (options.table == null) {
@@ -1209,6 +1303,42 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
var allocated_items = output == null ? null : output.allocations;
+ function redrawAllocationData() {
+ // Force a refresh of each row in the table
+ // Note we cannot call 'refresh' because we are passing data from memory
+ // var rows = $(table).bootstrapTable('getData');
+
+ // How many rows are fully allocated?
+ var allocated_rows = 0;
+
+ bom_items.forEach(function(row) {
+ $(table).bootstrapTable('updateByUniqueId', row.pk, row, true);
+
+ if (isRowFullyAllocated(row)) {
+ allocated_rows += 1;
+ }
+ });
+
+ // Find the top-level progess bar for this build output
+ var output_progress_bar = $(`#output-progress-${outputId}`);
+
+ if (output_progress_bar.exists()) {
+ if (bom_items.length > 0) {
+ output_progress_bar.html(
+ makeProgressBar(
+ allocated_rows,
+ bom_items.length,
+ {
+ max_width: '150px',
+ }
+ )
+ );
+ }
+ } else {
+ console.warn(`Could not find progress bar for output '${outputId}'`);
+ }
+ }
+
function reloadAllocationData(async=true) {
// Reload stock allocation data for this particular build output
@@ -1225,32 +1355,18 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
success: function(response) {
allocated_items = response;
- if (async) {
+ redrawAllocationData();
- // Force a refresh of each row in the table
- // Note we cannot call 'refresh' because we are passing data from memory
- var rows = $(table).bootstrapTable('getData');
-
- // How many rows are fully allocated?
- var allocated_rows = 0;
-
- rows.forEach(function(row) {
- $(table).bootstrapTable('updateByUniqueId', row.pk, row, true);
-
- if (isRowFullyAllocated(row)) {
- allocated_rows += 1;
- }
- });
- }
}
}
);
}
if (allocated_items == null) {
-
// No allocation data provided? Request from server (blocking)
reloadAllocationData(false);
+ } else {
+ redrawAllocationData();
}
function reloadTable() {
@@ -1293,38 +1409,15 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
}
return available;
-
}
- function getAllocationsForRow(row) {
- var part_id = row.sub_part;
-
- var allocations = [];
-
- allocated_items.forEach(function(allocation) {
- if (allocation.bom_part == part_id) {
- allocations.push(allocation);
- }
- });
-
- return allocations;
- }
-
- function sumAllocations(row) {
-
- var allocated_quantity = 0;
-
- getAllocationsForRow(row).forEach(function(allocation) {
- allocated_quantity += allocation.quantity;
- });
-
- row.allocated = parseFloat(allocated_quantity.toFixed(15));
-
+ function allocatedQuantity(row) {
+ row.allocated = sumAllocationsForBomRow(row, allocated_items);
return row.allocated;
}
function isRowFullyAllocated(row) {
- return sumAllocations(row) >= requiredQuantity(row);
+ return allocatedQuantity(row) >= requiredQuantity(row);
}
function setupCallbacks() {
@@ -1386,7 +1479,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
newBuildOrder({
part: pk,
parent: buildId,
- quantity: requiredQuantity(row) - sumAllocations(row),
+ quantity: requiredQuantity(row) - allocatedQuantity(row),
});
});
@@ -1408,26 +1501,6 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
});
}
- var bom_items = buildInfo.bom_items || null;
-
- // If BOM items have not been provided, load via the API
- if (bom_items == null) {
- inventreeGet(
- '{% url "api-bom-list" %}',
- {
- part: partId,
- sub_part_detail: true,
- sub_part_trackable: trackable,
- },
- {
- async: false,
- success: function(results) {
- bom_items = results;
- }
- }
- );
- }
-
// Load table of BOM items
$(table).inventreeTable({
data: bom_items,
@@ -1446,7 +1519,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
showColumns: false,
detailView: true,
detailFilter: function(index, row) {
- return sumAllocations(row) > 0;
+ return allocatedQuantity(row) > 0;
},
detailFormatter: function(index, row, element) {
// Contruct an 'inner table' which shows which stock items have been allocated
@@ -1460,7 +1533,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
var subTable = $(`#${subTableId}`);
subTable.bootstrapTable({
- data: getAllocationsForRow(row),
+ data: getAllocationsForBomRow(row, allocated_items),
showHeader: true,
columns: [
{
@@ -1660,15 +1733,15 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
title: '{% trans "Allocated" %}',
sortable: true,
formatter: function(value, row) {
- var allocated = sumAllocations(row);
+ var allocated = allocatedQuantity(row);
var required = requiredQuantity(row)
return makeProgressBar(allocated, required);
},
sorter: function(valA, valB, rowA, rowB) {
// Custom sorting function for progress bars
- var aA = sumAllocations(rowA);
- var aB = sumAllocations(rowB);
+ var aA = allocatedQuantity(rowA);
+ var aB = allocatedQuantity(rowB);
var qA = requiredQuantity(rowA);
var qB = requiredQuantity(rowB);
@@ -1703,7 +1776,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
// Generate action buttons for this build output
var html = ``;
- if (sumAllocations(row) < requiredQuantity(row)) {
+ if (allocatedQuantity(row) < requiredQuantity(row)) {
if (row.sub_part_detail.assembly) {
html += makeIconButton('fa-tools icon-blue', 'button-build', row.sub_part, '{% trans "Build stock" %}');
}
@@ -1719,7 +1792,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
'fa-minus-circle icon-red', 'button-unallocate', row.sub_part,
'{% trans "Unallocate stock" %}',
{
- disabled: sumAllocations(row) == 0,
+ disabled: allocatedQuantity(row) == 0,
}
);
diff --git a/InvenTree/templates/js/translated/helpers.js b/InvenTree/templates/js/translated/helpers.js
index c464ad3645..dc40d1e30c 100644
--- a/InvenTree/templates/js/translated/helpers.js
+++ b/InvenTree/templates/js/translated/helpers.js
@@ -163,27 +163,29 @@ function makeProgressBar(value, maximum, opts={}) {
var style = options.style || '';
- var text = '';
+ var text = options.text;
+
+ if (!text) {
+ if (style == 'percent') {
+ // Display e.g. "50%"
- if (style == 'percent') {
- // Display e.g. "50%"
+ text = `${percent}%`;
+ } else if (style == 'max') {
+ // Display just the maximum value
+ text = `${maximum}`;
+ } else if (style == 'value') {
+ // Display just the current value
+ text = `${value}`;
+ } else if (style == 'blank') {
+ // No display!
+ text = '';
+ } else {
+ /* Default style
+ * Display e.g. "5 / 10"
+ */
- text = `${percent}%`;
- } else if (style == 'max') {
- // Display just the maximum value
- text = `${maximum}`;
- } else if (style == 'value') {
- // Display just the current value
- text = `${value}`;
- } else if (style == 'blank') {
- // No display!
- text = '';
- } else {
- /* Default style
- * Display e.g. "5 / 10"
- */
-
- text = `${value} / ${maximum}`;
+ text = `${value} / ${maximum}`;
+ }
}
var id = options.id || 'progress-bar';