mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Working towards better build allocation:
- Improve data serialization in API - Javascript bug fixes - Load the build allocation table using jQuery
This commit is contained in:
parent
c11b433d94
commit
b0891c921c
@ -137,7 +137,4 @@ class BarcodePluginView(APIView):
|
||||
# Include the original barcode data
|
||||
response['barcode_data'] = barcode_data
|
||||
|
||||
print("Response:")
|
||||
print(response)
|
||||
|
||||
return Response(response)
|
||||
|
@ -25,7 +25,6 @@ function inventreeGet(url, filters={}, options={}) {
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
success: function(response) {
|
||||
console.log('Success GET data at ' + url);
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
}
|
||||
@ -64,7 +63,6 @@ function inventreeFormDataUpload(url, data, options={}) {
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(data, status, xhr) {
|
||||
console.log('Form data upload success');
|
||||
if (options.success) {
|
||||
options.success(data, status, xhr);
|
||||
}
|
||||
@ -97,7 +95,6 @@ function inventreePut(url, data={}, options={}) {
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
success: function(response, status) {
|
||||
console.log(method + ' - ' + url + ' : result = ' + status);
|
||||
if (options.success) {
|
||||
options.success(response, status);
|
||||
}
|
||||
@ -114,25 +111,3 @@ function inventreePut(url, data={}, options={}) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Return list of parts with optional filters
|
||||
function getParts(filters={}, options={}) {
|
||||
return inventreeGet('/api/part/', filters, options);
|
||||
}
|
||||
|
||||
// Return list of part categories with optional filters
|
||||
function getPartCategories(filters={}, options={}) {
|
||||
return inventreeGet('/api/part/category/', filters, options);
|
||||
}
|
||||
|
||||
function getCompanies(filters={}, options={}) {
|
||||
return inventreeGet('/api/company/', filters, options);
|
||||
}
|
||||
|
||||
function updateStockItem(pk, data, final=false) {
|
||||
return inventreePut('/api/stock/' + pk + '/', data, final);
|
||||
}
|
||||
|
||||
function updatePart(pk, data, final=false) {
|
||||
return inventreePut('/api/part/' + pk + '/', data, final);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
function loadBuildTable(table, options) {
|
||||
// Display a table of Build objects
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
|
@ -121,7 +121,7 @@ function makeProgressBar(value, maximum, opts) {
|
||||
extraclass = 'progress-bar-under';
|
||||
}
|
||||
|
||||
var id = opts.id || 'progress-bar';
|
||||
var id = options.id || 'progress-bar';
|
||||
|
||||
return `
|
||||
<div id='${id}' class='progress'>
|
||||
|
@ -95,20 +95,25 @@ class BuildItemList(generics.ListCreateAPIView):
|
||||
to allow filtering by stock_item.part
|
||||
"""
|
||||
|
||||
# Does the user wish to filter by part?
|
||||
part_pk = self.request.query_params.get('part', None)
|
||||
|
||||
query = BuildItem.objects.all()
|
||||
|
||||
query = query.select_related('stock_item')
|
||||
query = query.prefetch_related('stock_item__part')
|
||||
query = query.prefetch_related('stock_item__part__category')
|
||||
|
||||
if part_pk:
|
||||
query = query.filter(stock_item__part=part_pk)
|
||||
|
||||
return query
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
# Does the user wish to filter by part?
|
||||
part_pk = self.request.query_params.get('part', None)
|
||||
|
||||
if part_pk:
|
||||
queryset = queryset.filter(stock_item__part=part_pk)
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
@ -128,7 +133,7 @@ build_item_api_urls = [
|
||||
]
|
||||
|
||||
build_api_urls = [
|
||||
url(r'^item/?', include(build_item_api_urls)),
|
||||
url(r'^item/', include(build_item_api_urls)),
|
||||
|
||||
url(r'^(?P<pk>\d+)/', BuildDetail.as_view(), name='api-build-detail'),
|
||||
|
||||
|
@ -21,6 +21,8 @@ class BuildSerializer(InvenTreeModelSerializer):
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
|
||||
quantity = serializers.FloatField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
part_detail = kwargs.pop('part_detail', False)
|
||||
|
||||
@ -63,6 +65,8 @@ class BuildItemSerializer(InvenTreeModelSerializer):
|
||||
part_image = serializers.CharField(source='stock_item.part.image', read_only=True)
|
||||
stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True)
|
||||
|
||||
quantity = serializers.FloatField()
|
||||
|
||||
class Meta:
|
||||
model = BuildItem
|
||||
fields = [
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends "build/build_base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block page_title %}
|
||||
@ -27,6 +28,141 @@ InvenTree | Allocate Parts
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
|
||||
var buildTable = $("#build-item-list");
|
||||
|
||||
buildTable.inventreeTable({
|
||||
uniqueId: 'sub_part',
|
||||
url: "{% url 'api-bom-list' %}",
|
||||
formatNoMatches: function() { return "{% trans 'No BOM items found' %}"; },
|
||||
onLoadSuccess: function(tableData) {
|
||||
// Once the BOM data are loaded, request allocation data for the build
|
||||
inventreeGet('/api/build/item/', {
|
||||
build: {{ build.id }},
|
||||
},
|
||||
{
|
||||
success: function(data) {
|
||||
|
||||
// Iterate through the returned data, and group by "part"
|
||||
var allocations = {};
|
||||
|
||||
data.forEach(function(item) {
|
||||
|
||||
// Group allocations by referenced 'part'
|
||||
var part = item.part;
|
||||
var key = parseInt(part);
|
||||
|
||||
if (!(key in allocations)) {
|
||||
allocations[key] = new Array();
|
||||
}
|
||||
|
||||
// Add the allocation to the list
|
||||
allocations[key].push(item);
|
||||
});
|
||||
|
||||
for (var key in allocations) {
|
||||
|
||||
// Select the associated row in the table
|
||||
var tableRow = buildTable.bootstrapTable('getRowByUniqueId', key);
|
||||
|
||||
// Set the allocations for the row
|
||||
tableRow.allocations = allocations[key];
|
||||
|
||||
// And push the updated row back into the main table
|
||||
buildTable.bootstrapTable('updateByUniqueId', key, tableRow, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
queryParams: {
|
||||
part: {{ build.part.id }},
|
||||
sub_part_detail: 1,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'id',
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'sub_part',
|
||||
title: '{% trans "Part" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, `/part/${row.sub_part}/`);
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'sub_part_detail.description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'quantity',
|
||||
title: '{% trans "Required" %}',
|
||||
formatter: function(value, row) {
|
||||
return value * {{ build.quantity }};
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'allocated',
|
||||
title: '{% trans "Allocated" %}',
|
||||
formatter: function(value, row) {
|
||||
var progress = value || 0;
|
||||
|
||||
return makeProgressBar(progress, row.quantity * {{ build.quantity }});
|
||||
},
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
|
||||
var aA = rowA.allocated || 0;
|
||||
var aB = rowB.allocated || 0;
|
||||
|
||||
var qA = rowA.quantity * {{ build.quantity }};
|
||||
var qB = rowB.quantity * {{ build.quantity }};
|
||||
|
||||
if (aA == 0 && aB == 0) {
|
||||
return (qA > qB) ? 1 : -1;
|
||||
}
|
||||
|
||||
var progressA = parseFloat(aA) / qA;
|
||||
var progressB = parseFloat(aB) / qB;
|
||||
|
||||
return (progressA < progressB) ? 1 : -1;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'buttons',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
var pk = row.sub_part;
|
||||
|
||||
{% if build.status == BuildStatus.PENDING %}
|
||||
if (row.sub_part_detail.purchaseable) {
|
||||
html += makeIconButton('fa-shopping-cart', 'button-buy', pk, '{% trans "Buy parts" %}');
|
||||
}
|
||||
|
||||
if (row.sub_part.assembly) {
|
||||
html += makeIconButton('fa-tools', 'button-build', pk, '{% trans "Build parts" %}');
|
||||
}
|
||||
|
||||
html += makeIconButton('fa-plus', 'button-add', pk, '{% trans "Allocate stock" %}');
|
||||
{% endif %}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
},
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
{% if editing %}
|
||||
|
||||
{% for bom_item in bom_items.all %}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
<h4>{% trans "Required Parts" %}</h4>
|
||||
<h4>{% trans "Allocated Parts" %}</h4>
|
||||
<hr>
|
||||
|
||||
<div id='build-item-toolbar'>
|
||||
@ -11,7 +11,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='build-list' data-sorting='true' data-toolbar='#build-item-toolbar'>
|
||||
<table class='table table-striped table-condensed' id='build-item-list' data-toolbar='#build-item-toolbar'></table>
|
||||
|
||||
<table class='table table-striped table-condensed' id='build-list' data-sorting='true'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable='true'>{% trans "Part" %}</th>
|
||||
|
@ -4,13 +4,13 @@
|
||||
<li{% if tab == 'details' %} class='active'{% endif %}>
|
||||
<a href="{% url 'build-detail' build.id %}">{% trans "Details" %}</a>
|
||||
</li>
|
||||
<li{% if tab == 'allocate' %} class='active'{% endif %}>
|
||||
<a href="{% url 'build-allocate' build.id %}">{% trans "Allocated Parts" %}</a>
|
||||
</li>
|
||||
<li{% if tab == 'output' %} class='active'{% endif %}>
|
||||
<a href="{% url 'build-output' build.id %}">{% trans "Outputs" %}{% if build.output_count > 0%}<span class='badge'>{{ build.output_count }}</span>{% endif %}</a>
|
||||
<a href="{% url 'build-output' build.id %}">{% trans "Build Outputs" %}{% if build.output_count > 0%}<span class='badge'>{{ build.output_count }}</span>{% endif %}</a>
|
||||
</li>
|
||||
<li{% if tab == 'notes' %} class='active'{% endif %}>
|
||||
<a href="{% url 'build-notes' build.id %}">{% trans "Notes" %}{% if build.notes %} <span class='glyphicon glyphicon-small glyphicon-info-sign'></span>{% endif %}</a>
|
||||
</li>
|
||||
<li{% if tab == 'allocate' %} class='active'{% endif %}>
|
||||
<a href="{% url 'build-allocate' build.id %}">{% trans "Assign Parts" %}</a>
|
||||
</li>
|
||||
</ul>
|
@ -234,6 +234,8 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
||||
""" Serializer for BomItem object """
|
||||
|
||||
price_range = serializers.CharField(read_only=True)
|
||||
|
||||
quantity = serializers.FloatField()
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True)
|
||||
|
Loading…
Reference in New Issue
Block a user