mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
5b77ff4a4c
@ -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 + "/");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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():
|
||||||
|
@ -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 = {}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 """
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user