mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 21:25:42 +00:00 
			
		
		
		
	Adds a BomUpload endpoint to handle upload of complete BOM
This commit is contained in:
		| @@ -23,6 +23,7 @@ | ||||
|     loadUsedInTable, | ||||
|     removeRowFromBomWizard, | ||||
|     removeColFromBomWizard, | ||||
|     submitBomTable | ||||
| */ | ||||
|  | ||||
|  | ||||
| @@ -41,6 +42,7 @@ function constructBomUploadTable(data, options={}) { | ||||
|  | ||||
|         var field_options = { | ||||
|             hideLabels: true, | ||||
|             hideClearButton: true, | ||||
|         }; | ||||
|  | ||||
|         function constructRowField(field_name) { | ||||
| @@ -53,7 +55,7 @@ function constructBomUploadTable(data, options={}) { | ||||
|  | ||||
|             field.value = row[field_name]; | ||||
|  | ||||
|             return constructField(`${field_name}_${idx}`, field, field_options); | ||||
|             return constructField(`items_${field_name}_${idx}`, field, field_options); | ||||
|  | ||||
|         } | ||||
|  | ||||
| @@ -75,8 +77,7 @@ function constructBomUploadTable(data, options={}) { | ||||
|         buttons += `</div>`; | ||||
|  | ||||
|         var html = ` | ||||
|         <tr id='bom_import_row_${idx}' class='bom-import-row'> | ||||
|             <td id='col_buttons_${idx}'>${buttons}</td> | ||||
|         <tr id='bom_import_row_${idx}' class='bom-import-row' idx='${idx}'> | ||||
|             <td id='col_sub_part_${idx}'>${sub_part}</td> | ||||
|             <td id='col_quantity_${idx}'>${quantity}</td> | ||||
|             <td id='col_reference_${idx}'>${reference}</td> | ||||
| @@ -85,6 +86,7 @@ function constructBomUploadTable(data, options={}) { | ||||
|             <td id='col_inherited_${idx}'>${inherited}</td> | ||||
|             <td id='col_optional_${idx}'>${optional}</td> | ||||
|             <td id='col_note_${idx}'>${note}</td> | ||||
|             <td id='col_buttons_${idx}'>${buttons}</td> | ||||
|         </tr>`; | ||||
|  | ||||
|         $('#bom-import-table tbody').append(html); | ||||
| @@ -92,7 +94,7 @@ function constructBomUploadTable(data, options={}) { | ||||
|         // Initialize the "part" selector for this row | ||||
|         initializeRelatedField( | ||||
|             { | ||||
|                 name: `sub_part_${idx}`, | ||||
|                 name: `items_sub_part_${idx}`, | ||||
|                 value: row.part, | ||||
|                 api_url: '{% url "api-part-list" %}', | ||||
|                 filters: { | ||||
| @@ -111,15 +113,6 @@ function constructBomUploadTable(data, options={}) { | ||||
|         $(`#button-row-remove-${idx}`).click(function() { | ||||
|             $(`#bom_import_row_${idx}`).remove(); | ||||
|         }); | ||||
|  | ||||
|         // Add callbacks for the fields which allow it | ||||
|         function addRowClearCallback(field_name) { | ||||
|             addClearCallback(`${field_name}_${idx}`, fields[field_name]); | ||||
|         } | ||||
|          | ||||
|         addRowClearCallback('reference'); | ||||
|         addRowClearCallback('overage'); | ||||
|         addRowClearCallback('note'); | ||||
|     } | ||||
|  | ||||
|     // Request API endpoint options | ||||
| @@ -134,6 +127,70 @@ function constructBomUploadTable(data, options={}) { | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Extract rows from the BOM upload table, | ||||
|  * and submit data to the server | ||||
|  */ | ||||
| function submitBomTable(part_id, options={}) { | ||||
|  | ||||
|     // Extract rows from the form | ||||
|     var rows = []; | ||||
|  | ||||
|     var idx_values = []; | ||||
|  | ||||
|     var url = '{% url "api-bom-upload" %}'; | ||||
|  | ||||
|     $('.bom-import-row').each(function() { | ||||
|         var idx = $(this).attr('idx'); | ||||
|  | ||||
|         idx_values.push(idx); | ||||
|  | ||||
|         // Extract each field from the row | ||||
|         rows.push({ | ||||
|             part: part_id, | ||||
|             sub_part: getFormFieldValue(`items_sub_part_${idx}`, {}), | ||||
|             quantity: getFormFieldValue(`items_quantity_${idx}`, {}), | ||||
|             reference: getFormFieldValue(`items_reference_${idx}`, {}), | ||||
|             overage: getFormFieldValue(`items_overage_${idx}`, {}), | ||||
|             allow_variants: getFormFieldValue(`items_allow_variants_${idx}`, {type: 'boolean'}), | ||||
|             inherited: getFormFieldValue(`items_inherited_${idx}`, {type: 'boolean'}), | ||||
|             optional: getFormFieldValue(`items_optional_${idx}`, {type: 'boolean'}), | ||||
|             note: getFormFieldValue(`items_note_${idx}`, {}), | ||||
|         }) | ||||
|     }); | ||||
|  | ||||
|     var data = { | ||||
|         items: rows, | ||||
|     }; | ||||
|  | ||||
|     var options = { | ||||
|         nested: { | ||||
|             items: idx_values, | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     getApiEndpointOptions(url, function(response) { | ||||
|         var fields = response.actions.POST; | ||||
|  | ||||
|         inventreePut(url, data, { | ||||
|             method: 'POST', | ||||
|             success: function(response) { | ||||
|                 // TODO: Return to the "bom" page | ||||
|             }, | ||||
|             error: function(xhr) { | ||||
|                 switch (xhr.status) { | ||||
|                 case 400: | ||||
|                     handleFormErrors(xhr.responseJSON, fields, options); | ||||
|                     break; | ||||
|                 default: | ||||
|                     showApiError(xhr, url); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
| function downloadBomTemplate(options={}) { | ||||
|  | ||||
|     var format = options.format; | ||||
|   | ||||
| @@ -890,12 +890,13 @@ function validateFormField(name, options) { | ||||
|  * - field: The field specification provided from the OPTIONS request | ||||
|  * - options: The original options object provided by the client | ||||
|  */ | ||||
| function getFormFieldValue(name, field, options) { | ||||
| function getFormFieldValue(name, field={}, options={}) { | ||||
|  | ||||
|     // Find the HTML element | ||||
|     var el = getFormFieldElement(name, options); | ||||
|  | ||||
|     if (!el) { | ||||
|         console.log(`ERROR: getFormFieldValue could not locate field '{name}'`); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
| @@ -981,16 +982,22 @@ function handleFormSuccess(response, options) { | ||||
| /* | ||||
|  * Remove all error text items from the form | ||||
|  */ | ||||
| function clearFormErrors(options) { | ||||
| function clearFormErrors(options={}) { | ||||
|  | ||||
|     // Remove the individual error messages | ||||
|     $(options.modal).find('.form-error-message').remove(); | ||||
|     if (options && options.modal) { | ||||
|         // Remove the individual error messages | ||||
|         $(options.modal).find('.form-error-message').remove(); | ||||
|  | ||||
|     // Remove the "has error" class | ||||
|     $(options.modal).find('.form-field-error').removeClass('form-field-error'); | ||||
|         // Remove the "has error" class | ||||
|         $(options.modal).find('.form-field-error').removeClass('form-field-error'); | ||||
|  | ||||
|     // Hide the 'non field errors' | ||||
|     $(options.modal).find('#non-field-errors').html(''); | ||||
|         // Hide the 'non field errors' | ||||
|         $(options.modal).find('#non-field-errors').html(''); | ||||
|     } else { | ||||
|         $('.form-error-message').remove(); | ||||
|         $('.form-field-errors').removeClass('form-field-error'); | ||||
|         $('#non-field-errors').html(''); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1018,7 +1025,7 @@ function clearFormErrors(options) { | ||||
|  *  | ||||
|  */ | ||||
|  | ||||
| function handleNestedErrors(errors, field_name, options) { | ||||
| function handleNestedErrors(errors, field_name, options={}) { | ||||
|  | ||||
|     var error_list = errors[field_name]; | ||||
|  | ||||
| @@ -1074,15 +1081,23 @@ function handleNestedErrors(errors, field_name, options) { | ||||
|  * - fields: The form data object | ||||
|  * - options: Form options provided by the client | ||||
|  */ | ||||
| function handleFormErrors(errors, fields, options) { | ||||
| function handleFormErrors(errors, fields={}, options={}) { | ||||
|  | ||||
|     // Reset the status of the "submit" button | ||||
|     $(options.modal).find('#modal-form-submit').prop('disabled', false); | ||||
|     if (options.modal) { | ||||
|         $(options.modal).find('#modal-form-submit').prop('disabled', false); | ||||
|     } | ||||
|  | ||||
|     // Remove any existing error messages from the form | ||||
|     clearFormErrors(options); | ||||
|  | ||||
|     var non_field_errors = $(options.modal).find('#non-field-errors'); | ||||
|     var non_field_errors = null; | ||||
|      | ||||
|     if (options.modal) { | ||||
|         non_field_errors = $(options.modal).find('#non-field-errors'); | ||||
|     } else { | ||||
|         non_field_errors = $('#non-field-errors'); | ||||
|     } | ||||
|  | ||||
|     // TODO: Display the JSON error text when hovering over the "info" icon | ||||
|     non_field_errors.append( | ||||
| @@ -1158,14 +1173,19 @@ function handleFormErrors(errors, fields, options) { | ||||
| /* | ||||
|  * Add a rendered error message to the provided field | ||||
|  */ | ||||
| function addFieldErrorMessage(name, error_text, error_idx, options) { | ||||
| function addFieldErrorMessage(name, error_text, error_idx, options={}) { | ||||
|  | ||||
|     field_name = getFieldName(name, options); | ||||
|  | ||||
|     // Add the 'form-field-error' class | ||||
|     $(options.modal).find(`#div_id_${field_name}`).addClass('form-field-error'); | ||||
|     var field_dom = null; | ||||
|  | ||||
|     var field_dom = $(options.modal).find(`#errors-${field_name}`); | ||||
|     if (options.modal) { | ||||
|         $(options.modal).find(`#div_id_${field_name}`).addClass('form-field-error'); | ||||
|         field_dom = $(options.modal).find(`#errors-${field_name}`); | ||||
|     } else { | ||||
|         $(`#div_id_${field_name}`).addClass('form-field-error'); | ||||
|         field_dom = $(`#errors-${field_name}`); | ||||
|     } | ||||
|  | ||||
|     if (field_dom) { | ||||
|  | ||||
| @@ -1492,17 +1512,20 @@ function initializeRelatedField(field, fields, options={}) { | ||||
|  | ||||
|     var parent = null; | ||||
|     var auto_width = false; | ||||
|     var width = '100%'; | ||||
|  | ||||
|     // Special considerations if the select2 input is a child of a modal | ||||
|     if (options && options.modal) { | ||||
|         parent = $(options.modal); | ||||
|         auto_width = true; | ||||
|         width = null; | ||||
|     } | ||||
|  | ||||
|     select.select2({ | ||||
|         placeholder: '', | ||||
|         dropdownParent: parent, | ||||
|         dropdownAutoWidth: auto_width, | ||||
|         width: width, | ||||
|         language: { | ||||
|             noResults: function(query) { | ||||
|                 if (field.noResults) { | ||||
| @@ -1949,7 +1972,7 @@ function constructField(name, parameters, options) { | ||||
|  | ||||
|     if (extra) { | ||||
|  | ||||
|         if (!parameters.required) { | ||||
|         if (!parameters.required && !options.hideClearButton) { | ||||
|             html += ` | ||||
|             <span class='input-group-text form-clear' id='clear_${field_name}' title='{% trans "Clear input" %}'> | ||||
|                 <span class='icon-red fas fa-backspace'></span> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user