mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Updates for stock allocation form
- Data submission - Nested error handling - Button callbacks to remove rows
This commit is contained in:
		| @@ -154,6 +154,13 @@ class BuildAllocationItemSerializer(serializers.Serializer): | ||||
|         required=True | ||||
|     ) | ||||
|  | ||||
|     def validate_quantity(self, quantity): | ||||
|  | ||||
|         if quantity <= 0: | ||||
|             raise ValidationError(_("Quantity must be greater than zero")) | ||||
|  | ||||
|         return quantity | ||||
|  | ||||
|     output = serializers.PrimaryKeyRelatedField( | ||||
|         queryset=StockItem.objects.filter(is_building=True), | ||||
|         many=False, | ||||
|   | ||||
| @@ -378,52 +378,18 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | ||||
|             // Primary key of the 'sub_part' | ||||
|             var pk = $(this).attr('pk'); | ||||
|  | ||||
|             // Launch form to allocate new stock against this output | ||||
|             launchModalForm('{% url "build-item-create" %}', { | ||||
|                 success: reloadTable, | ||||
|                 data: { | ||||
|                     part: pk, | ||||
|                     build: buildId, | ||||
|                     install_into: outputId, | ||||
|                 }, | ||||
|                 secondary: [ | ||||
|                     { | ||||
|                         field: 'stock_item', | ||||
|                         label: '{% trans "New Stock Item" %}', | ||||
|                         title: '{% trans "Create new Stock Item" %}', | ||||
|                         url: '{% url "stock-item-create" %}', | ||||
|                         data: { | ||||
|                             part: pk, | ||||
|                         }, | ||||
|             allocateStockToBuild( | ||||
|                 buildId, | ||||
|                 partId, | ||||
|                 { | ||||
|                     success: function(data) { | ||||
|                         console.log("here we go I guess"); | ||||
|                     }, | ||||
|                 ], | ||||
|                 callback: [ | ||||
|                     { | ||||
|                         field: 'stock_item', | ||||
|                         action: function(value) { | ||||
|                             inventreeGet( | ||||
|                                 `/api/stock/${value}/`, {}, | ||||
|                                 { | ||||
|                                     success: function(response) { | ||||
|  | ||||
|                                         // How many items are actually available for the given stock item? | ||||
|                                         var available = response.quantity - response.allocated; | ||||
|  | ||||
|                                         var field = getFieldByName('#modal-form', 'quantity'); | ||||
|  | ||||
|                                         // Allocation quantity initial value | ||||
|                                         var initial = field.attr('value'); | ||||
|  | ||||
|                                         if (available < initial) { | ||||
|                                             field.val(available); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                             ); | ||||
|                         } | ||||
|                     } | ||||
|                 ] | ||||
|             }); | ||||
|                     parts: [ | ||||
|                         parseInt(pk), | ||||
|                     ] | ||||
|                 } | ||||
|             ); | ||||
|         }); | ||||
|  | ||||
|         // Callback for 'buy' button | ||||
| @@ -831,18 +797,18 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { | ||||
|  *  - outputId: ID / PK of the associated build output (or null for untracked items) | ||||
|  *  - parts: List of ID values for filtering against specific sub parts | ||||
|  */ | ||||
| function allocateStockToBuild(buildId, partId, options={}) { | ||||
| function allocateStockToBuild(build_id, part_id, options={}) { | ||||
|  | ||||
|     // ID of the associated "build output" (or null) | ||||
|     var outputId = options.output || null; | ||||
|     var output_id = options.output || null; | ||||
|  | ||||
|     // Extract list of BOM items (or empty list) | ||||
|     var subPartIds = options.parts || []; | ||||
|     var sub_part_ids = options.parts || []; | ||||
|  | ||||
|     var bomItemQueryParams = { | ||||
|         part: partId, | ||||
|     var query_params = { | ||||
|         part: part_id, | ||||
|         sub_part_detail: true, | ||||
|         sub_part_trackable: outputId != null | ||||
|         sub_part_trackable: output_id != null | ||||
|     }; | ||||
|  | ||||
|     function renderBomItemRow(bom_item, quantity) { | ||||
| @@ -856,29 +822,50 @@ function allocateStockToBuild(buildId, partId, options={}) { | ||||
|  | ||||
|         delete_button += makeIconButton( | ||||
|             'fa-times icon-red', | ||||
|             'button-part-remove', | ||||
|             'button-row-remove', | ||||
|             pk, | ||||
|             '{% trans "Remove row" %}', | ||||
|         ); | ||||
|  | ||||
|         delete_button += `</div>`; | ||||
|  | ||||
|         var quantity_input = constructNumberInput(pk, { | ||||
|             value: quantity || 0, | ||||
|             min_value: 0, | ||||
|             title: '{% trans "Specify stock allocation quantity" %}', | ||||
|         }); | ||||
|         var quantity_input = constructField( | ||||
|             `items_quantity_${pk}`, | ||||
|             { | ||||
|                 type: 'decimal', | ||||
|                 min_value: 0, | ||||
|                 value: quantity || 0, | ||||
|                 title: '{% trans "Specify stock allocation quantity" %}', | ||||
|                 required: true, | ||||
|             }, | ||||
|             { | ||||
|                 hideLabels: true, | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         var stock_input = constructRelatedFieldInput(`stock_query_${pk}`); | ||||
|         var stock_input = constructField( | ||||
|             `items_stock_item_${pk}`, | ||||
|             { | ||||
|                 type: 'related field', | ||||
|                 required: 'true', | ||||
|             }, | ||||
|             { | ||||
|                 hideLabels: true, | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         // var stock_input = constructRelatedFieldInput(`items_stock_item_${pk}`); | ||||
|  | ||||
|         var html = ` | ||||
|         <tr id='part_row_${pk}' class='part-allocation-row'> | ||||
|         <tr id='allocation_row_${pk}' class='part-allocation-row'> | ||||
|             <td id='part_${pk}'> | ||||
|                 ${thumb} ${sub_part.full_name} | ||||
|             </td> | ||||
|             <td id='stock_item_${pk}'> | ||||
|                 ${stock_input} | ||||
|             </td> | ||||
|             <td id='allocated_${pk}'> | ||||
|             </td> | ||||
|             <td id='quantity_${pk}'> | ||||
|                 ${quantity_input} | ||||
|             </td> | ||||
| @@ -893,7 +880,7 @@ function allocateStockToBuild(buildId, partId, options={}) { | ||||
|  | ||||
|     inventreeGet( | ||||
|         '{% url "api-bom-list" %}', | ||||
|         bomItemQueryParams, | ||||
|         query_params, | ||||
|         { | ||||
|             success: function(response) { | ||||
|  | ||||
| @@ -905,10 +892,10 @@ function allocateStockToBuild(buildId, partId, options={}) { | ||||
|                 for (var idx = 0; idx < response.length; idx++) { | ||||
|                     var item = response[idx]; | ||||
|  | ||||
|                     var subPartId = item.sub_part; | ||||
|                     var sub_part_id = item.sub_part; | ||||
|  | ||||
|                     // Check if we are interested in this item | ||||
|                     if (subPartIds.length > 0 && !subPartIds.includes(subPartId)) { | ||||
|                     if (sub_part_ids.length > 0 && !sub_part_ids.includes(sub_part_id)) { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
| @@ -930,16 +917,14 @@ function allocateStockToBuild(buildId, partId, options={}) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 var modal = createNewModal({ | ||||
|                     title: '{% trans "Allocate Stock to Build" %}', | ||||
|                 }); | ||||
|              | ||||
|                 // Create table of parts | ||||
|                 var html = ` | ||||
|                 <table class='table table-striped table-condensed' id='stock-allocation-table'> | ||||
|                     <thead> | ||||
|                         <tr> | ||||
|                             <th>{% trans "Part" %}</th> | ||||
|                             <th style='min-width: 250px;'>{% trans "Stock Item" %}</th> | ||||
|                             <th>{% trans "Allocated" %}</th> | ||||
|                             <th>{% trans "Quantity" %}</th> | ||||
|                             <th></th> | ||||
|                         </tr> | ||||
| @@ -950,18 +935,20 @@ function allocateStockToBuild(buildId, partId, options={}) { | ||||
|                 </table> | ||||
|                 `; | ||||
|  | ||||
|                 constructFormBody({}, { | ||||
|                     preFormContent: html, | ||||
|                 constructForm(`/api/build/${build_id}/allocate/`, { | ||||
|                     method: 'POST', | ||||
|                     fields: {}, | ||||
|                     preFormContent: html, | ||||
|                     confirm: true, | ||||
|                     confirmMessage: '{% trans "Confirm Stock Allocation" %}', | ||||
|                     modal: modal, | ||||
|                     confirmMessage: '{% trans "Confirm stock allocation" %}', | ||||
|                     title: '{% trans "Allocate Stock Items to Build Order" %}', | ||||
|                     afterRender: function(fields, options) { | ||||
|                          | ||||
|  | ||||
|                         // Initialize select2 fields | ||||
|                         bom_items.forEach(function(bom_item) { | ||||
|                             initializeRelatedField( | ||||
|                                 { | ||||
|                                     name: `stock_query_${bom_item.pk}`, | ||||
|                                     name: `items_stock_item_${bom_item.pk}`, | ||||
|                                     api_url: '{% url "api-stock-list" %}', | ||||
|                                     filters: { | ||||
|                                         part: bom_item.sub_part, | ||||
| @@ -979,10 +966,83 @@ function allocateStockToBuild(buildId, partId, options={}) { | ||||
|                             ); | ||||
|                         }); | ||||
|  | ||||
|                         // Add button callbacks | ||||
|                         $(options.modal).find('.button-row-remove').click(function() { | ||||
|                             var pk = $(this).attr('pk'); | ||||
|  | ||||
|                             $(options.modal).find(`#allocation_row_${pk}`).remove(); | ||||
|                         }); | ||||
|                     }, | ||||
|                     onSubmit: function(fields, options) { | ||||
|  | ||||
|                         // Extract elements from the form | ||||
|                         var data = { | ||||
|                             items: [] | ||||
|                         }; | ||||
|  | ||||
|                         var item_pk_values = []; | ||||
|  | ||||
|                         bom_items.forEach(function(item) { | ||||
|  | ||||
|                             var quantity = getFormFieldValue( | ||||
|                                 `items_quantity_${item.pk}`, | ||||
|                                 {}, | ||||
|                                 { | ||||
|                                     modal: options.modal, | ||||
|                                 }, | ||||
|                             ); | ||||
|  | ||||
|                             var stock_item = getFormFieldValue( | ||||
|                                 `items_stock_item_${item.pk}`, | ||||
|                                 {}, | ||||
|                                 { | ||||
|                                     modal: options.modal, | ||||
|                                 } | ||||
|                             ); | ||||
|  | ||||
|                             if (quantity != null) { | ||||
|                                 data.items.push({ | ||||
|                                     bom_item: item.pk, | ||||
|                                     stock_item: stock_item, | ||||
|                                     quantity: quantity | ||||
|                                 }); | ||||
|  | ||||
|                                 item_pk_values.push(item.pk); | ||||
|                             } | ||||
|                         }); | ||||
|  | ||||
|                         // Provide nested values | ||||
|                         options.nested = { | ||||
|                             "items": item_pk_values | ||||
|                         }; | ||||
|  | ||||
|                         inventreePut( | ||||
|                             options.url, | ||||
|                             data, | ||||
|                             { | ||||
|                                 method: 'POST', | ||||
|                                 success: function(response) { | ||||
|                                     // Hide the modal | ||||
|                                     $(options.modal).modal('hide'); | ||||
|  | ||||
|                                     if (options.success) { | ||||
|                                         options.success(response); | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 error: function(xhr) { | ||||
|                                     switch (xhr.status) { | ||||
|                                         case 400: | ||||
|                                             handleFormErrors(xhr.responseJSON, fields, options); | ||||
|                                             break; | ||||
|                                         default: | ||||
|                                             $(options.modal).modal('hide'); | ||||
|                                             showApiError(xhr); | ||||
|                                             break; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         ); | ||||
|                     }, | ||||
|                     onSubmit: function(fields) { | ||||
|                         // TODO | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user