mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 15:15: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
 | 
					        required=True
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def validate_quantity(self, quantity):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if quantity <= 0:
 | 
				
			||||||
 | 
					            raise ValidationError(_("Quantity must be greater than zero"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return quantity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    output = serializers.PrimaryKeyRelatedField(
 | 
					    output = serializers.PrimaryKeyRelatedField(
 | 
				
			||||||
        queryset=StockItem.objects.filter(is_building=True),
 | 
					        queryset=StockItem.objects.filter(is_building=True),
 | 
				
			||||||
        many=False,
 | 
					        many=False,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -378,52 +378,18 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
 | 
				
			|||||||
            // Primary key of the 'sub_part'
 | 
					            // Primary key of the 'sub_part'
 | 
				
			||||||
            var pk = $(this).attr('pk');
 | 
					            var pk = $(this).attr('pk');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Launch form to allocate new stock against this output
 | 
					            allocateStockToBuild(
 | 
				
			||||||
            launchModalForm('{% url "build-item-create" %}', {
 | 
					                buildId,
 | 
				
			||||||
                success: reloadTable,
 | 
					                partId,
 | 
				
			||||||
                data: {
 | 
					 | 
				
			||||||
                    part: pk,
 | 
					 | 
				
			||||||
                    build: buildId,
 | 
					 | 
				
			||||||
                    install_into: outputId,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                secondary: [
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                        field: 'stock_item',
 | 
					                    success: function(data) {
 | 
				
			||||||
                        label: '{% trans "New Stock Item" %}',
 | 
					                        console.log("here we go I guess");
 | 
				
			||||||
                        title: '{% trans "Create new Stock Item" %}',
 | 
					 | 
				
			||||||
                        url: '{% url "stock-item-create" %}',
 | 
					 | 
				
			||||||
                        data: {
 | 
					 | 
				
			||||||
                            part: pk,
 | 
					 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    },
 | 
					                    parts: [
 | 
				
			||||||
                ],
 | 
					                        parseInt(pk),
 | 
				
			||||||
                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);
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                ]
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Callback for 'buy' button
 | 
					        // 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)
 | 
					 *  - outputId: ID / PK of the associated build output (or null for untracked items)
 | 
				
			||||||
 *  - parts: List of ID values for filtering against specific sub parts
 | 
					 *  - 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)
 | 
					    // 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)
 | 
					    // Extract list of BOM items (or empty list)
 | 
				
			||||||
    var subPartIds = options.parts || [];
 | 
					    var sub_part_ids = options.parts || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var bomItemQueryParams = {
 | 
					    var query_params = {
 | 
				
			||||||
        part: partId,
 | 
					        part: part_id,
 | 
				
			||||||
        sub_part_detail: true,
 | 
					        sub_part_detail: true,
 | 
				
			||||||
        sub_part_trackable: outputId != null
 | 
					        sub_part_trackable: output_id != null
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function renderBomItemRow(bom_item, quantity) {
 | 
					    function renderBomItemRow(bom_item, quantity) {
 | 
				
			||||||
@@ -856,29 +822,50 @@ function allocateStockToBuild(buildId, partId, options={}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        delete_button += makeIconButton(
 | 
					        delete_button += makeIconButton(
 | 
				
			||||||
            'fa-times icon-red',
 | 
					            'fa-times icon-red',
 | 
				
			||||||
            'button-part-remove',
 | 
					            'button-row-remove',
 | 
				
			||||||
            pk,
 | 
					            pk,
 | 
				
			||||||
            '{% trans "Remove row" %}',
 | 
					            '{% trans "Remove row" %}',
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        delete_button += `</div>`;
 | 
					        delete_button += `</div>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var quantity_input = constructNumberInput(pk, {
 | 
					        var quantity_input = constructField(
 | 
				
			||||||
            value: quantity || 0,
 | 
					            `items_quantity_${pk}`,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                type: 'decimal',
 | 
				
			||||||
                min_value: 0,
 | 
					                min_value: 0,
 | 
				
			||||||
 | 
					                value: quantity || 0,
 | 
				
			||||||
                title: '{% trans "Specify stock allocation quantity" %}',
 | 
					                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 = `
 | 
					        var html = `
 | 
				
			||||||
        <tr id='part_row_${pk}' class='part-allocation-row'>
 | 
					        <tr id='allocation_row_${pk}' class='part-allocation-row'>
 | 
				
			||||||
            <td id='part_${pk}'>
 | 
					            <td id='part_${pk}'>
 | 
				
			||||||
                ${thumb} ${sub_part.full_name}
 | 
					                ${thumb} ${sub_part.full_name}
 | 
				
			||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
            <td id='stock_item_${pk}'>
 | 
					            <td id='stock_item_${pk}'>
 | 
				
			||||||
                ${stock_input}
 | 
					                ${stock_input}
 | 
				
			||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
 | 
					            <td id='allocated_${pk}'>
 | 
				
			||||||
 | 
					            </td>
 | 
				
			||||||
            <td id='quantity_${pk}'>
 | 
					            <td id='quantity_${pk}'>
 | 
				
			||||||
                ${quantity_input}
 | 
					                ${quantity_input}
 | 
				
			||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
@@ -893,7 +880,7 @@ function allocateStockToBuild(buildId, partId, options={}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    inventreeGet(
 | 
					    inventreeGet(
 | 
				
			||||||
        '{% url "api-bom-list" %}',
 | 
					        '{% url "api-bom-list" %}',
 | 
				
			||||||
        bomItemQueryParams,
 | 
					        query_params,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            success: function(response) {
 | 
					            success: function(response) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -905,10 +892,10 @@ function allocateStockToBuild(buildId, partId, options={}) {
 | 
				
			|||||||
                for (var idx = 0; idx < response.length; idx++) {
 | 
					                for (var idx = 0; idx < response.length; idx++) {
 | 
				
			||||||
                    var item = response[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
 | 
					                    // 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;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -930,16 +917,14 @@ function allocateStockToBuild(buildId, partId, options={}) {
 | 
				
			|||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var modal = createNewModal({
 | 
					                // Create table of parts
 | 
				
			||||||
                    title: '{% trans "Allocate Stock to Build" %}',
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
                var html = `
 | 
					                var html = `
 | 
				
			||||||
                <table class='table table-striped table-condensed' id='stock-allocation-table'>
 | 
					                <table class='table table-striped table-condensed' id='stock-allocation-table'>
 | 
				
			||||||
                    <thead>
 | 
					                    <thead>
 | 
				
			||||||
                        <tr>
 | 
					                        <tr>
 | 
				
			||||||
                            <th>{% trans "Part" %}</th>
 | 
					                            <th>{% trans "Part" %}</th>
 | 
				
			||||||
                            <th style='min-width: 250px;'>{% trans "Stock Item" %}</th>
 | 
					                            <th style='min-width: 250px;'>{% trans "Stock Item" %}</th>
 | 
				
			||||||
 | 
					                            <th>{% trans "Allocated" %}</th>
 | 
				
			||||||
                            <th>{% trans "Quantity" %}</th>
 | 
					                            <th>{% trans "Quantity" %}</th>
 | 
				
			||||||
                            <th></th>
 | 
					                            <th></th>
 | 
				
			||||||
                        </tr>
 | 
					                        </tr>
 | 
				
			||||||
@@ -950,18 +935,20 @@ function allocateStockToBuild(buildId, partId, options={}) {
 | 
				
			|||||||
                </table>
 | 
					                </table>
 | 
				
			||||||
                `;
 | 
					                `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                constructFormBody({}, {
 | 
					                constructForm(`/api/build/${build_id}/allocate/`, {
 | 
				
			||||||
                    preFormContent: html,
 | 
					                    method: 'POST',
 | 
				
			||||||
                    fields: {},
 | 
					                    fields: {},
 | 
				
			||||||
 | 
					                    preFormContent: html,
 | 
				
			||||||
                    confirm: true,
 | 
					                    confirm: true,
 | 
				
			||||||
                    confirmMessage: '{% trans "Confirm Stock Allocation" %}',
 | 
					                    confirmMessage: '{% trans "Confirm stock allocation" %}',
 | 
				
			||||||
                    modal: modal,
 | 
					                    title: '{% trans "Allocate Stock Items to Build Order" %}',
 | 
				
			||||||
                    afterRender: function(fields, options) {
 | 
					                    afterRender: function(fields, options) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Initialize select2 fields
 | 
				
			||||||
                        bom_items.forEach(function(bom_item) {
 | 
					                        bom_items.forEach(function(bom_item) {
 | 
				
			||||||
                            initializeRelatedField(
 | 
					                            initializeRelatedField(
 | 
				
			||||||
                                {
 | 
					                                {
 | 
				
			||||||
                                    name: `stock_query_${bom_item.pk}`,
 | 
					                                    name: `items_stock_item_${bom_item.pk}`,
 | 
				
			||||||
                                    api_url: '{% url "api-stock-list" %}',
 | 
					                                    api_url: '{% url "api-stock-list" %}',
 | 
				
			||||||
                                    filters: {
 | 
					                                    filters: {
 | 
				
			||||||
                                        part: bom_item.sub_part,
 | 
					                                        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) {
 | 
					                    onSubmit: function(fields, options) {
 | 
				
			||||||
                        // TODO
 | 
					
 | 
				
			||||||
 | 
					                        // 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;
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user