Merge pull request #679 from SchrodingersGat/on-order-fix

Better filtering of annotations for Part-list API
This commit is contained in:
Oliver 2020-03-26 18:07:53 +11:00 committed by GitHub
commit 00eada2c3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 15 deletions

View File

@ -182,8 +182,6 @@ function loadPartTable(table, url, options={}) {
searchable: false, searchable: false,
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
console.log("On order:", row.on_order);
var html = ""; var html = "";
var link = "stock"; var link = "stock";
@ -198,6 +196,9 @@ function loadPartTable(table, url, options={}) {
} else if (row.on_order) { } else if (row.on_order) {
value = "<span class='label label-primary'>On Order : " + row.on_order + "</span>"; value = "<span class='label label-primary'>On Order : " + row.on_order + "</span>";
link = "orders"; link = "orders";
} else if (row.building) {
value = "<span class='label label-info'>Building : " + row.building + "</span>";
link = "builds";
} else { } else {
value ="<span class='label label-warning'>No Stock</span>"; value ="<span class='label label-warning'>No Stock</span>";
} }

View File

@ -71,11 +71,17 @@ class StockStatus(StatusCode):
LOST: _("Lost"), 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 = [ AVAILABLE_CODES = [
OK, OK,
ATTENTION, ATTENTION,
DAMAGED DAMAGED,
]
# The following codes correspond to parts that are 'unavailable'
UNAVAILABLE_CODES = [
DESTROYED,
LOST,
] ]

View File

@ -130,9 +130,10 @@ class OrderTest(TestCase):
order.receive_line_item(line, loc, 50, user=None) order.receive_line_item(line, loc, 50, user=None)
line = PurchaseOrderLineItem.objects.get(id=2) 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) self.assertEqual(order.status, OrderStatus.PLACED)
for line in order.pending_line_items(): for line in order.pending_line_items():

View File

@ -8,7 +8,7 @@ from __future__ import unicode_literals
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from django.conf import settings 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 import status
from rest_framework.response import Response from rest_framework.response import Response
@ -25,6 +25,7 @@ from .models import PartParameter, PartParameterTemplate
from . import serializers as part_serializers from . import serializers as part_serializers
from InvenTree.status_codes import OrderStatus, StockStatus, BuildStatus
from InvenTree.views import TreeSerializer from InvenTree.views import TreeSerializer
from InvenTree.helpers import str2bool from InvenTree.helpers import str2bool
@ -153,6 +154,18 @@ class PartList(generics.ListCreateAPIView):
queryset = self.filter_queryset(self.get_queryset()) 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( data = queryset.values(
'pk', 'pk',
'category', 'category',
@ -171,13 +184,12 @@ class PartList(generics.ListCreateAPIView):
'salable', 'salable',
'active', 'active',
).annotate( ).annotate(
in_stock=Sum('stock_items__quantity'), # Quantity of items which are "in stock"
on_order=Sum('supplier_parts__purchase_order_line_items__quantity'), 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 # Reduce the number of lookups we need to do for the part categories
categories = {} categories = {}

View File

@ -569,7 +569,12 @@ class Part(models.Model):
""" Return the current number of parts currently being built """ 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 @property
def build_allocation(self): def build_allocation(self):
@ -919,7 +924,21 @@ class Part(models.Model):
def on_order(self): def on_order(self):
""" Return the total number of items on order for this part. """ """ 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): def get_parameters(self):
""" Return all parameters for this part, ordered by name """ """ Return all parameters for this part, ordered by name """