diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js index c94c512d6f..693a4d1faa 100644 --- a/InvenTree/InvenTree/static/script/inventree/part.js +++ b/InvenTree/InvenTree/static/script/inventree/part.js @@ -181,9 +181,7 @@ function loadPartTable(table, url, options={}) { title: 'Stock', searchable: false, sortable: true, - formatter: function(value, row, index, field) { - console.log("On order:", row.on_order); - + formatter: function(value, row, index, field) { var html = ""; var link = "stock"; @@ -198,6 +196,9 @@ function loadPartTable(table, url, options={}) { } else if (row.on_order) { value = "On Order : " + row.on_order + ""; link = "orders"; + } else if (row.building) { + value = "Building : " + row.building + ""; + link = "builds"; } else { value ="No Stock"; } diff --git a/InvenTree/InvenTree/status_codes.py b/InvenTree/InvenTree/status_codes.py index e49f9a9824..cbc9f3565f 100644 --- a/InvenTree/InvenTree/status_codes.py +++ b/InvenTree/InvenTree/status_codes.py @@ -71,11 +71,17 @@ class StockStatus(StatusCode): LOST: _("Lost"), } - # The following codes correspond to parts that are 'available' + # The following codes correspond to parts that are 'available' or 'in stock' AVAILABLE_CODES = [ OK, ATTENTION, - DAMAGED + DAMAGED, + ] + + # The following codes correspond to parts that are 'unavailable' + UNAVAILABLE_CODES = [ + DESTROYED, + LOST, ] diff --git a/InvenTree/order/tests.py b/InvenTree/order/tests.py index 351c152472..35cf8909be 100644 --- a/InvenTree/order/tests.py +++ b/InvenTree/order/tests.py @@ -130,9 +130,10 @@ class OrderTest(TestCase): order.receive_line_item(line, loc, 50, user=None) line = PurchaseOrderLineItem.objects.get(id=2) - order.receive_line_item(line, loc, 2 * line.quantity, user=None) - self.assertEqual(part.on_order, 1100) + order.receive_line_item(line, loc, 500, user=None) + + self.assertEqual(part.on_order, 800) self.assertEqual(order.status, OrderStatus.PLACED) for line in order.pending_line_items(): diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index d24f0dc6ee..1d6aaa84aa 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -8,7 +8,7 @@ from __future__ import unicode_literals from django_filters.rest_framework import DjangoFilterBackend from django.conf import settings -from django.db.models import Sum, Count +from django.db.models import Q, Sum, Count from rest_framework import status from rest_framework.response import Response @@ -25,6 +25,7 @@ from .models import PartParameter, PartParameterTemplate from . import serializers as part_serializers +from InvenTree.status_codes import OrderStatus, StockStatus, BuildStatus from InvenTree.views import TreeSerializer from InvenTree.helpers import str2bool @@ -153,6 +154,18 @@ class PartList(generics.ListCreateAPIView): queryset = self.filter_queryset(self.get_queryset()) + # Filters for annotations + + # "in_stock" count should only sum stock items which are "in stock" + stock_filter = Q(stock_items__status__in=StockStatus.AVAILABLE_CODES) + + # "on_order" items should only sum orders which are currently outstanding + order_filter = Q(supplier_parts__purchase_order_line_items__order__status__in=OrderStatus.OPEN) + + # "building" should only reference builds which are active + build_filter = Q(builds__status__in=BuildStatus.ACTIVE_CODES) + + # Set of fields we wish to serialize data = queryset.values( 'pk', 'category', @@ -171,13 +184,12 @@ class PartList(generics.ListCreateAPIView): 'salable', 'active', ).annotate( - in_stock=Sum('stock_items__quantity'), - on_order=Sum('supplier_parts__purchase_order_line_items__quantity'), + # Quantity of items which are "in stock" + in_stock=Sum('stock_items__quantity', filter=stock_filter), + on_order=Sum('supplier_parts__purchase_order_line_items__quantity', filter=order_filter), + building=Sum('builds__quantity', filter=build_filter), ) - # TODO - Annotate total being built - # TODO - Annotate total on order - # Reduce the number of lookups we need to do for the part categories categories = {} diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index d22e6e7be1..58ee8bbd2c 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -569,7 +569,12 @@ class Part(models.Model): """ Return the current number of parts currently being built """ - return sum([b.quantity for b in self.active_builds]) + quantity = self.active_builds.aggregate(quantity=Sum('quantity'))['quantity'] + + if quantity is None: + quantity = 0 + + return quantity @property def build_allocation(self): @@ -919,7 +924,21 @@ class Part(models.Model): def on_order(self): """ Return the total number of items on order for this part. """ - return sum([part.on_order() for part in self.supplier_parts.all().prefetch_related('purchase_order_line_items')]) + orders = self.supplier_parts.filter(purchase_order_line_items__order__status__in=OrderStatus.OPEN).aggregate( + quantity=Sum('purchase_order_line_items__quantity'), + received=Sum('purchase_order_line_items__received') + ) + + quantity = orders['quantity'] + received = orders['received'] + + if quantity is None: + quantity = 0 + + if received is None: + received = 0 + + return quantity - received def get_parameters(self): """ Return all parameters for this part, ordered by name """