diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index 160642281a..cd4dbbd95c 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -165,6 +165,16 @@ class BuildItemList(generics.ListCreateAPIView): serializer_class = BuildItemSerializer + def get_serializer(self, *args, **kwargs): + + params = self.request.query_params + + kwargs['part_detail'] = str2bool(params.get('part_detail', False)) + kwargs['build_detail'] = str2bool(params.get('build_detail', False)) + kwargs['location_detail'] = str2bool(params.get('location_detail', False)) + + return self.serializer_class(*args, **kwargs) + def get_queryset(self): """ Override the queryset method, to allow filtering by stock_item.part diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py index 629422f6e5..d8573cfa70 100644 --- a/InvenTree/build/serializers.py +++ b/InvenTree/build/serializers.py @@ -13,7 +13,8 @@ from rest_framework import serializers from InvenTree.serializers import InvenTreeModelSerializer from stock.serializers import StockItemSerializerBrief -from part.serializers import PartBriefSerializer +from stock.serializers import LocationSerializer +from part.serializers import PartSerializer, PartBriefSerializer from .models import Build, BuildItem @@ -99,22 +100,45 @@ class BuildItemSerializer(InvenTreeModelSerializer): bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True) part = serializers.IntegerField(source='stock_item.part.pk', read_only=True) - part_name = serializers.CharField(source='stock_item.part.full_name', read_only=True) - part_thumb = serializers.CharField(source='getStockItemThumbnail', read_only=True) + location = serializers.IntegerField(source='stock_item.location.pk', read_only=True) + + # Extra (optional) detail fields + part_detail = PartSerializer(source='stock_item.part', many=False, read_only=True) + build_detail = BuildSerializer(source='build', many=False, read_only=True) stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True) + location_detail = LocationSerializer(source='stock_item.location', read_only=True) quantity = serializers.FloatField() + def __init__(self, *args, **kwargs): + + build_detail = kwargs.pop('build_detail', False) + part_detail = kwargs.pop('part_detail', False) + location_detail = kwargs.pop('location_detail', False) + + super().__init__(*args, **kwargs) + + if not build_detail: + self.fields.pop('build_detail') + + if not part_detail: + self.fields.pop('part_detail') + + if not location_detail: + self.fields.pop('location_detail') + class Meta: model = BuildItem fields = [ 'pk', 'bom_part', 'build', + 'build_detail', 'install_into', + 'location', + 'location_detail', 'part', - 'part_name', - 'part_thumb', + 'part_detail', 'stock_item', 'stock_item_detail', 'quantity' diff --git a/InvenTree/templates/js/build.js b/InvenTree/templates/js/build.js index 9523d24d39..ab787e207d 100644 --- a/InvenTree/templates/js/build.js +++ b/InvenTree/templates/js/build.js @@ -155,6 +155,85 @@ function makeBuildOutputActionButtons(output, buildInfo, lines) { } +function loadBuildOrderAllocationTable(table, options={}) { + /** + * Load a table showing all the BuildOrder allocations for a given part + */ + + options.params['part_detail'] = true; + options.params['build_detail'] = true; + options.params['location_detail'] = true; + + var filters = loadTableFilters("buildorderallocation"); + + for (var key in options.params) { + filters[key] = options.params[key]; + } + + setupFilterList("buildorderallocation", $(table)); + + $(table).inventreeTable({ + url: '{% url "api-build-item-list" %}', + queryParams: filters, + name: 'buildorderallocation', + groupBy: false, + original: options.params, + formatNoMatches: function() { + return '{% trans "No build order allocations found" %}' + }, + columns: [ + { + field: 'pk', + visible: false, + switchable: false, + }, + { + field: 'build', + title: '{% trans "Build Order" %}', + formatter: function(value, row) { + var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}"; + + var ref = `${prefix}${row.build_detail.reference}`; + + return renderLink(ref, `/build/${row.build}/`); + } + }, + { + field: 'item', + title: '{% trans "Stock Item" %}', + formatter: function(value, row) { + // Render a link to the particular stock item + + var link = `/stock/item/${row.stock_item}/`; + var text = `{% trans "Stock Item" %} ${row.stock_item}`; + + return renderLink(text, link); + } + }, + { + field: 'location', + title: '{% trans "Location" %}', + formatter: function(value, row) { + + if (!value) { + return '{% trans "Location not specified" %}'; + } + + var link = `/stock/location/${value}`; + var text = row.location_detail.description; + + return renderLink(text, link); + } + }, + { + field: 'quantity', + title: '{% trans "Quantity" %}', + } + ] + }); +} + + function loadBuildOutputAllocationTable(buildInfo, output, options={}) { /* * Load the "allocation table" for a particular build output. @@ -347,6 +426,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { var params = { build: buildId, + part_detail: true, + location_detail: true, } if (output) { @@ -466,8 +547,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { title: '{% trans "Part" %}', formatter: function(value, row) { - var html = imageHoverIcon(row.part_thumb); - html += renderLink(row.part_name, `/part/${value}/`); + var html = imageHoverIcon(row.part_detail.thumbnail); + html += renderLink(row.part_detail.full_name, `/part/${value}/`); return html; } },