mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Update progress bars for build output allocation
This commit is contained in:
		| @@ -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. |  * Display a "build output" table for a particular build. | ||||||
|  * |  * | ||||||
| @@ -919,6 +948,32 @@ function loadBuildOutputTable(build_info, options={}) { | |||||||
|                     rows.forEach(function(row) { |                     rows.forEach(function(row) { | ||||||
|                         row.allocations = allocations[row.pk] || []; |                         row.allocations = allocations[row.pk] || []; | ||||||
|                         $(table).bootstrapTable('updateByUniqueId', row.pk, row, true); |                         $(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, |                 visible: true, | ||||||
|                 switchable: false, |                 switchable: false, | ||||||
|                 formatter: function(value, row) { |                 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; |                         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 `<div id='output-progress-${row.pk}'><span class='fas fa-spin fa-spinner'></span></div>`; |                     var progressBar = makeProgressBar( | ||||||
|  |                         n_completed_lines, | ||||||
|  |                         n_bom_lines, | ||||||
|  |                         { | ||||||
|  |                             max_width: '150px', | ||||||
|  |                         } | ||||||
|  |                     ); | ||||||
|  |  | ||||||
|  |                     return `<div id='output-progress-${row.pk}'>${progressBar}</div>`; | ||||||
|                 }, |                 }, | ||||||
|                 sorter: function(value_a, value_b, row_a, row_b) { |                 sorter: function(value_a, value_b, row_a, row_b) { | ||||||
|                     // TODO: Custom sorter for "allocated stock" column |                     // TODO: Custom sorter for "allocated stock" column | ||||||
| @@ -1108,7 +1182,7 @@ function loadBuildOutputTable(build_info, options={}) { | |||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 field: 'tests', |                 field: 'tests', | ||||||
|                 title: '{% trans "Tests" %}', |                 title: '{% trans "Completed Tests" %}', | ||||||
|                 sortable: true, |                 sortable: true, | ||||||
|                 switchable: true, |                 switchable: true, | ||||||
|                 formatter: function(value, row) { |                 formatter: function(value, row) { | ||||||
| @@ -1186,6 +1260,26 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|         outputId = 'untracked'; |         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; |     var table = options.table; | ||||||
|  |  | ||||||
|     if (options.table == null) { |     if (options.table == null) { | ||||||
| @@ -1209,6 +1303,42 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|  |  | ||||||
|     var allocated_items = output == null ? null : output.allocations; |     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) { |     function reloadAllocationData(async=true) { | ||||||
|         // Reload stock allocation data for this particular build output |         // Reload stock allocation data for this particular build output | ||||||
|  |  | ||||||
| @@ -1225,32 +1355,18 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|                 success: function(response) { |                 success: function(response) { | ||||||
|                     allocated_items = 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) { |     if (allocated_items == null) { | ||||||
|  |  | ||||||
|         // No allocation data provided? Request from server (blocking) |         // No allocation data provided? Request from server (blocking) | ||||||
|         reloadAllocationData(false); |         reloadAllocationData(false); | ||||||
|  |     } else { | ||||||
|  |         redrawAllocationData(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function reloadTable() { |     function reloadTable() { | ||||||
| @@ -1293,38 +1409,15 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return available; |         return available; | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function getAllocationsForRow(row) { |     function allocatedQuantity(row) { | ||||||
|         var part_id = row.sub_part; |         row.allocated = sumAllocationsForBomRow(row, allocated_items); | ||||||
|  |  | ||||||
|         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)); |  | ||||||
|  |  | ||||||
|         return row.allocated; |         return row.allocated; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function isRowFullyAllocated(row) { |     function isRowFullyAllocated(row) { | ||||||
|         return sumAllocations(row) >= requiredQuantity(row); |         return allocatedQuantity(row) >= requiredQuantity(row); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function setupCallbacks() { |     function setupCallbacks() { | ||||||
| @@ -1386,7 +1479,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|             newBuildOrder({ |             newBuildOrder({ | ||||||
|                 part: pk, |                 part: pk, | ||||||
|                 parent: buildId, |                 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 |     // Load table of BOM items | ||||||
|     $(table).inventreeTable({ |     $(table).inventreeTable({ | ||||||
|         data: bom_items, |         data: bom_items, | ||||||
| @@ -1446,7 +1519,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|         showColumns: false, |         showColumns: false, | ||||||
|         detailView: true, |         detailView: true, | ||||||
|         detailFilter: function(index, row) { |         detailFilter: function(index, row) { | ||||||
|             return sumAllocations(row) > 0; |             return allocatedQuantity(row) > 0; | ||||||
|         }, |         }, | ||||||
|         detailFormatter: function(index, row, element) { |         detailFormatter: function(index, row, element) { | ||||||
|             // Contruct an 'inner table' which shows which stock items have been allocated |             // Contruct an 'inner table' which shows which stock items have been allocated | ||||||
| @@ -1460,7 +1533,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|             var subTable = $(`#${subTableId}`); |             var subTable = $(`#${subTableId}`); | ||||||
|  |  | ||||||
|             subTable.bootstrapTable({ |             subTable.bootstrapTable({ | ||||||
|                 data: getAllocationsForRow(row), |                 data: getAllocationsForBomRow(row, allocated_items), | ||||||
|                 showHeader: true, |                 showHeader: true, | ||||||
|                 columns: [ |                 columns: [ | ||||||
|                     { |                     { | ||||||
| @@ -1660,15 +1733,15 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|                 title: '{% trans "Allocated" %}', |                 title: '{% trans "Allocated" %}', | ||||||
|                 sortable: true, |                 sortable: true, | ||||||
|                 formatter: function(value, row) { |                 formatter: function(value, row) { | ||||||
|                     var allocated = sumAllocations(row); |                     var allocated = allocatedQuantity(row); | ||||||
|                     var required = requiredQuantity(row) |                     var required = requiredQuantity(row) | ||||||
|                     return makeProgressBar(allocated, required); |                     return makeProgressBar(allocated, required); | ||||||
|                 }, |                 }, | ||||||
|                 sorter: function(valA, valB, rowA, rowB) { |                 sorter: function(valA, valB, rowA, rowB) { | ||||||
|                     // Custom sorting function for progress bars |                     // Custom sorting function for progress bars | ||||||
|                      |                      | ||||||
|                     var aA = sumAllocations(rowA); |                     var aA = allocatedQuantity(rowA); | ||||||
|                     var aB = sumAllocations(rowB); |                     var aB = allocatedQuantity(rowB); | ||||||
|  |  | ||||||
|                     var qA = requiredQuantity(rowA); |                     var qA = requiredQuantity(rowA); | ||||||
|                     var qB = requiredQuantity(rowB); |                     var qB = requiredQuantity(rowB); | ||||||
| @@ -1703,7 +1776,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | |||||||
|                     // Generate action buttons for this build output |                     // Generate action buttons for this build output | ||||||
|                     var html = `<div class='btn-group float-right' role='group'>`; |                     var html = `<div class='btn-group float-right' role='group'>`; | ||||||
|  |  | ||||||
|                     if (sumAllocations(row) < requiredQuantity(row)) { |                     if (allocatedQuantity(row) < requiredQuantity(row)) { | ||||||
|                         if (row.sub_part_detail.assembly) { |                         if (row.sub_part_detail.assembly) { | ||||||
|                             html += makeIconButton('fa-tools icon-blue', 'button-build', row.sub_part, '{% trans "Build stock" %}'); |                             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, |                         'fa-minus-circle icon-red', 'button-unallocate', row.sub_part, | ||||||
|                         '{% trans "Unallocate stock" %}', |                         '{% trans "Unallocate stock" %}', | ||||||
|                         { |                         { | ||||||
|                             disabled: sumAllocations(row) == 0, |                             disabled: allocatedQuantity(row) == 0, | ||||||
|                         } |                         } | ||||||
|                     ); |                     ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -163,27 +163,29 @@ function makeProgressBar(value, maximum, opts={}) { | |||||||
|  |  | ||||||
|     var style = options.style || ''; |     var style = options.style || ''; | ||||||
|  |  | ||||||
|     var text = ''; |     var text = options.text; | ||||||
|  |      | ||||||
|  |     if (!text) { | ||||||
|  |         if (style == 'percent') { | ||||||
|  |             // Display e.g. "50%" | ||||||
|  |  | ||||||
|     if (style == 'percent') { |             text = `${percent}%`; | ||||||
|         // Display e.g. "50%" |         } 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}%`; |             text = `${value} / ${maximum}`; | ||||||
|     } 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}`; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     var id = options.id || 'progress-bar'; |     var id = options.id || 'progress-bar'; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user