From 542b4113a108f61c875577b5772afc6df14cee3f Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@gmail.com>
Date: Sun, 17 Oct 2021 21:37:10 +1100
Subject: [PATCH] Improvements for build output completion

- Check if the output is fully allocated (throw error if not)
- Reload tables after actions performed
---
 InvenTree/build/serializers.py              |  4 +++
 InvenTree/build/templates/build/detail.html |  4 +++
 InvenTree/templates/js/translated/build.js  | 32 ++++++++++++++++-----
 InvenTree/templates/js/translated/forms.js  | 13 +++++++++
 4 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py
index cf7c8065fe..8f76f3e603 100644
--- a/InvenTree/build/serializers.py
+++ b/InvenTree/build/serializers.py
@@ -152,6 +152,10 @@ class BuildOutputSerializer(serializers.Serializer):
         if not output.is_building:
             raise ValidationError(_("This build output has already been completed"))
 
+        # The build output must have all tracked parts allocated
+        if not build.isFullyAllocated(output):
+            raise ValidationError(_("This build output is not fully allocated"))
+
         return output
 
     class Meta:
diff --git a/InvenTree/build/templates/build/detail.html b/InvenTree/build/templates/build/detail.html
index e2bf2358de..fcd60c2edd 100644
--- a/InvenTree/build/templates/build/detail.html
+++ b/InvenTree/build/templates/build/detail.html
@@ -364,7 +364,11 @@ inventreeGet(
                     outputs,
                     {
                         success: function() {
+                            // Reload the "in progress" table
                             $('#build-output-table').bootstrapTable('refresh');
+
+                            // Reload the "completed" table
+                            $('#build-stock-table').bootstrapTable('refresh');
                         }
                     }
                 );
diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js
index 245904f5ca..40a5acee62 100644
--- a/InvenTree/templates/js/translated/build.js
+++ b/InvenTree/templates/js/translated/build.js
@@ -220,12 +220,12 @@ function completeBuildOutputs(build_id, outputs, options={}) {
     function renderBuildOutput(output, opts={}) {
         var pk = output.pk;
 
-        var quantity = '';
+        var output_html = imageHoverIcon(output.part_detail.thumbnail);
 
         if (output.quantity == 1 && output.serial) {
-            quantity = `{% trans "Serial Number" %}: ${output.serial}`;
+            output_html += `{% trans "Serial Number" %}: ${output.serial}`;
         } else {
-            quantity = `{% trans "Quantity" %}: ${output.quantity}`;
+            output_html += `{% trans "Quantity" %}: ${output.quantity}`;
         }
 
         var buttons = `<div class='btn-group float-right' role='group'>`;
@@ -234,9 +234,21 @@ function completeBuildOutputs(build_id, outputs, options={}) {
 
         buttons += '</div>';
 
+        var field = constructField(
+            `outputs_output_${pk}`,
+            {
+                type: 'raw',
+                html: output_html,
+            },
+            {
+                hideLabels: true,
+            }
+        );
+
         var html = `
         <tr id='output_row_${pk}'>
-            <td>${quantity}</td>
+            <td>${field}</td>
+            <td>${output.part_detail.full_name}</td>
             <td>${buttons}</td>
         </tr>`;
 
@@ -253,7 +265,7 @@ function completeBuildOutputs(build_id, outputs, options={}) {
     var html = `
     <table class='table table-striped table-condensed' id='build-complete-table'>
         <thead>
-            <th>{% trans "Output" %}</th>
+            <th colspan='2'>{% trans "Output" %}</th>
             <th><!-- Actions --></th>
         </thead>
         <tbody>
@@ -481,6 +493,9 @@ function loadBuildOutputTable(build_info, options={}) {
                     rows,
                     {
                         output: pk,
+                        success: function() {
+                            $(table).bootstrapTable('refresh');
+                        }
                     }
                 );
             } else {
@@ -1296,10 +1311,13 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
             remaining = 0;
         }
 
-        table_entries += renderBomItemRow(bom_item, remaining);
+        // We only care about entries which are not yet fully allocated
+        if (remaining > 0) {
+            table_entries += renderBomItemRow(bom_item, remaining);
+        }
     }
 
-    if (bom_items.length == 0) {
+    if (table_entries.length == 0) {
 
         showAlertDialog(
             '{% trans "Select Parts" %}',
diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js
index 2483263219..1bfe196286 100644
--- a/InvenTree/templates/js/translated/forms.js
+++ b/InvenTree/templates/js/translated/forms.js
@@ -1843,6 +1843,8 @@ function constructInput(name, parameters, options) {
     case 'candy':
         func = constructCandyInput;
         break;
+    case 'raw':
+        func = constructRawInput;
     default:
         // Unsupported field type!
         break;
@@ -2086,6 +2088,17 @@ function constructCandyInput(name, parameters) {
 }
 
 
+/*
+ * Construct a "raw" field input
+ * No actual field data!
+ */
+function constructRawInput(name, parameters) {
+
+    return parameters.html;
+
+}
+
+
 /*
  * Construct a 'help text' div based on the field parameters
  *