diff --git a/InvenTree/InvenTree/static/script/inventree/bom.js b/InvenTree/InvenTree/static/script/inventree/bom.js index 55337e3fa8..e4b90a9f26 100644 --- a/InvenTree/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/InvenTree/static/script/inventree/bom.js @@ -133,11 +133,14 @@ function loadBomTable(table, options) { title: 'Part', sortable: true, formatter: function(value, row, index, field) { - var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, row.sub_part_detail.url); + var url = `/part/${row.sub_part}/`; + var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url); // Display an extra icon if this part is an assembly if (row.sub_part_detail.assembly) { - html += ""; + var text = ``; + + html += renderLink(text, `/part/${row.sub_part}/bom/`); } return html; @@ -185,26 +188,20 @@ function loadBomTable(table, options) { if (!options.editable) { cols.push( { - field: 'sub_part_detail.total_stock', + field: 'sub_part_detail.stock', title: 'Available', searchable: false, sortable: true, formatter: function(value, row, index, field) { - var text = ""; - - if (row.quantity < row.sub_part_detail.total_stock) - { - text = "" + value + ""; + + var url = `/part/${row.sub_part_detail.pk}/stock/`; + var text = value; + + if (value == null || value <= 0) { + text = `No Stock`; } - else - { - if (!value) { - value = 'No Stock'; - } - text = "" + value + ""; - } - - return renderLink(text, row.sub_part_detail.url + "stock/"); + + return renderLink(text, url); } }); diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 51a4a35938..310cf3b94f 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -468,15 +468,15 @@ class BomList(generics.ListCreateAPIView): # Do we wish to include extra detail? try: - part_detail = str2bool(self.request.GET.get('part_detail', None)) - sub_part_detail = str2bool(self.request.GET.get('sub_part_detail', None)) + kwargs['part_detail'] = str2bool(self.request.GET.get('part_detail', None)) except AttributeError: - part_detail = None - sub_part_detail = None - - kwargs['part_detail'] = part_detail - kwargs['sub_part_detail'] = sub_part_detail + pass + try: + kwargs['sub_part_detail'] = str2bool(self.request.GET.get('sub_part_detail', None)) + except AttributeError: + pass + # Ensure the request context is passed through! kwargs['context'] = self.get_serializer_context() @@ -486,6 +486,12 @@ class BomList(generics.ListCreateAPIView): queryset = BomItem.objects.all() queryset = self.get_serializer_class().setup_eager_loading(queryset) + return queryset + + def filter_queryset(self, queryset): + + queryset = super().filter_queryset(queryset) + # Filter by part? part = self.request.query_params.get('part', None) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 021695f9bf..6f6b0d7ddf 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -1242,6 +1242,17 @@ class BomItem(models.Model): child=self.sub_part.full_name, n=helpers.decimal2string(self.quantity)) + def available_stock(self): + """ + Return the available stock items for the referenced sub_part + """ + + query = self.sub_part.stock_items.filter(StockModels.StockItem.IN_STOCK_FILTER).aggregate( + available=Coalesce(Sum('quantity'), 0) + ) + + return query['available'] + def get_overage_quantity(self, quantity): """ Calculate overage quantity """ diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 8cb3584664..e713de455f 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -54,6 +54,8 @@ class PartBriefSerializer(InvenTreeModelSerializer): thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True) + stock = serializers.FloatField(source='total_stock') + class Meta: model = Part fields = [ @@ -65,6 +67,7 @@ class PartBriefSerializer(InvenTreeModelSerializer): 'assembly', 'purchaseable', 'salable', + 'stock', 'virtual', ] diff --git a/InvenTree/part/templates/part/used_in.html b/InvenTree/part/templates/part/used_in.html index 1d3589a0d3..ce0491a165 100644 --- a/InvenTree/part/templates/part/used_in.html +++ b/InvenTree/part/templates/part/used_in.html @@ -1,10 +1,10 @@ {% extends "part/part_base.html" %} - +{% load i18n %} {% block details %} {% include 'part/tabs.html' with tab='used' %} -

Assemblies

+

{% trans "Assemblies" %}


@@ -35,10 +35,11 @@ title: 'Part', sortable: true, formatter: function(value, row, index, field) { - var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value.full_name, value.url + 'bom/'); + var link = `/part/${value.pk}/bom/`; + var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value.full_name, link); if (!row.part_detail.active) { - html += "INACTIVE"; + html += "{% trans "INACTIVE" %}"; } return html; diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 36e8064138..f514eea3da 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -137,7 +137,6 @@ class StockItem(MPTTModel): sales_order=None, build_order=None, belongs_to=None, - status__in=StockStatus.AVAILABLE_CODES ) def save(self, *args, **kwargs):