From 6c6ebe70c27a940f22bcfaa89fbb94fac60846f4 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 29 Apr 2022 00:27:27 +1000 Subject: [PATCH] Update progress bars for build output allocation --- InvenTree/templates/js/translated/build.js | 227 ++++++++++++------- InvenTree/templates/js/translated/helpers.js | 40 ++-- 2 files changed, 171 insertions(+), 96 deletions(-) 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';