Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2020-03-26 18:08:34 +11:00
commit 5b77ff4a4c
8 changed files with 70 additions and 21 deletions

View File

@ -181,18 +181,29 @@ function loadPartTable(table, url, options={}) {
title: 'Stock', title: 'Stock',
searchable: false, searchable: false,
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var html = "";
var link = "stock";
if (value) { if (value) {
if (row.units) { if (row.units) {
value += ' <i><small>' + row.units + '</small></i>'; value += ' <i><small>' + row.units + '</small></i>';
} }
return renderLink(value, '/part/' + row.pk + '/stock/'); html = value;
}
else { } else if (row.on_order) {
return "<span class='label label-warning'>No Stock</span>"; value = "<span class='label label-primary'>On Order : " + row.on_order + "</span>";
link = "orders";
} else if (row.building) {
value = "<span class='label label-info'>Building : " + row.building + "</span>";
link = "builds";
} else {
value ="<span class='label label-warning'>No Stock</span>";
} }
return renderLink(value, '/part/' + row.pk + "/" + link + "/");
} }
}); });

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,12 +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"
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

@ -3,7 +3,7 @@ Functionality for Bill of Material (BOM) management.
Primarily BOM upload tools. Primarily BOM upload tools.
""" """
from fuzzywuzzy import fuzz from rapidfuzz import fuzz
import tablib import tablib
import os import os

View File

@ -29,7 +29,7 @@ from mptt.models import TreeForeignKey
from decimal import Decimal from decimal import Decimal
from datetime import datetime from datetime import datetime
from fuzzywuzzy import fuzz from rapidfuzz import fuzz
import hashlib import hashlib
from InvenTree import helpers from InvenTree import helpers
@ -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 """

View File

@ -18,7 +18,7 @@ from django.conf import settings
import os import os
from fuzzywuzzy import fuzz from rapidfuzz import fuzz
from decimal import Decimal from decimal import Decimal
from .models import PartCategory, Part, PartAttachment from .models import PartCategory, Part, PartAttachment

View File

@ -17,5 +17,4 @@ django-qr-code==1.1.0 # Generate QR codes
flake8==3.3.0 # PEP checking flake8==3.3.0 # PEP checking
coverage==4.0.3 # Unit test coverage coverage==4.0.3 # Unit test coverage
python-coveralls==2.9.1 # Coveralls linking (for Travis) python-coveralls==2.9.1 # Coveralls linking (for Travis)
fuzzywuzzy==0.17.0 # Fuzzy string matching rapidfuzz==0.2.1 # Fuzzy string matching
python-Levenshtein==0.12.0 # Required for fuzzywuzzy