mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Updates for stock allocation form
- Data submission - Nested error handling - Button callbacks to remove rows
This commit is contained in:
parent
a9d5b776d3
commit
074466f087
@ -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,
|
success: function(data) {
|
||||||
build: buildId,
|
console.log("here we go I guess");
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
parts: [
|
||||||
callback: [
|
parseInt(pk),
|
||||||
{
|
]
|
||||||
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}`,
|
||||||
min_value: 0,
|
{
|
||||||
title: '{% trans "Specify stock allocation quantity" %}',
|
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 = `
|
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, 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
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user