diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py index 8f76f3e603..a18f58fb76 100644 --- a/InvenTree/build/serializers.py +++ b/InvenTree/build/serializers.py @@ -309,6 +309,8 @@ class BuildAllocationItemSerializer(serializers.Serializer): def validate_bom_item(self, bom_item): + # TODO: Fix this validation - allow for variants and substitutes! + build = self.context['build'] # BomItem must point to the same 'part' as the parent build diff --git a/InvenTree/build/templates/build/detail.html b/InvenTree/build/templates/build/detail.html index 908a7dfa4a..f87eec90a0 100644 --- a/InvenTree/build/templates/build/detail.html +++ b/InvenTree/build/templates/build/detail.html @@ -523,6 +523,10 @@ $('#allocate-selected-items').click(function() { var bom_items = $("#allocation-table-untracked").bootstrapTable("getSelections"); + if (bom_items.length == 0) { + bom_items = $("#allocation-table-untracked").bootstrapTable('getData'); + } + allocateStockToBuild( {{ build.pk }}, {{ build.part.pk }}, diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index 0c291dd8da..b6c98fc49e 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -1404,6 +1404,24 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) { render_part_detail: true, render_location_detail: true, auto_fill: true, + onSelect: function(data, field, opts) { + // Adjust the 'quantity' field based on availability + + if (!('quantity' in data)) { + return; + } + + // Quantity remaining to be allocated + var remaining = Math.max((bom_item.required || 0) - (bom_item.allocated || 0), 0); + + // Calculate the available quantity + var available = Math.max((data.quantity || 0) - (data.allocated || 0), 0); + + // Maximum amount that we need + var desired = Math.min(available, remaining); + + updateFieldValue(`items_quantity_${bom_item.pk}`, desired, {}, opts); + }, adjustFilters: function(filters) { // Restrict query to the selected location var location = getFormFieldValue( diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index 1bfe196286..db2c8e46cc 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -1426,6 +1426,11 @@ function initializeRelatedField(field, fields, options) { data = item.element.instance; } + // Run optional callback function + if (field.onSelect && data) { + field.onSelect(data, field, options); + } + if (!data.pk) { return field.placeholder || ''; } diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index 7cadfe453d..67fef0b853 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -1641,6 +1641,13 @@ function loadSalesOrderLineItemTable(table, options={}) { var line_item = $(table).bootstrapTable('getRowByUniqueId', pk); + // Quantity remaining to be allocated + var remaining = (line_item.quantity || 0) - (line_item.allocated || 0); + + if (remaining < 0) { + remaining = 0; + } + var fields = { // SalesOrderLineItem reference line: { @@ -1654,9 +1661,26 @@ function loadSalesOrderLineItemTable(table, options={}) { in_stock: true, part: line_item.part, exclude_so_allocation: options.order, - } + }, + auto_fill: true, + onSelect: function(data, field, opts) { + // Quantity available from this stock item + + if (!('quantity' in data)) { + return; + } + + // Calculate the available quantity + var available = Math.max((data.quantity || 0) - (data.allocated || 0), 0); + + // Maximum amount that we need + var desired = Math.min(available, remaining); + + updateFieldValue('quantity', desired, {}, opts); + } }, quantity: { + value: remaining, }, }; @@ -1752,7 +1776,7 @@ function loadSalesOrderLineItemTable(table, options={}) { showFooter: true, uniqueId: 'pk', detailView: show_detail, - detailViewByClick: show_detail, + detailViewByClick: false, detailFilter: function(index, row) { if (pending) { // Order is pending