From b162c97226509add86369177828728ae4ca36196 Mon Sep 17 00:00:00 2001 From: maxbachmann Date: Sun, 22 Mar 2020 19:54:36 +0100 Subject: [PATCH 1/8] use rapidfuzz instead of fuzzywuzzy --- InvenTree/part/bom.py | 2 +- InvenTree/part/models.py | 2 +- InvenTree/part/views.py | 2 +- requirements.txt | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 85d7e23dbb..16a8b0b16d 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -3,7 +3,7 @@ Functionality for Bill of Material (BOM) management. Primarily BOM upload tools. """ -from fuzzywuzzy import fuzz +from rapidfuzz import fuzz import tablib import os diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 4e579ea44c..d22e6e7be1 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -29,7 +29,7 @@ from mptt.models import TreeForeignKey from decimal import Decimal from datetime import datetime -from fuzzywuzzy import fuzz +from rapidfuzz import fuzz import hashlib from InvenTree import helpers diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 01f8ff41bb..da4b757009 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -18,7 +18,7 @@ from django.conf import settings import os -from fuzzywuzzy import fuzz +from rapidfuzz import fuzz from decimal import Decimal from .models import PartCategory, Part, PartAttachment diff --git a/requirements.txt b/requirements.txt index f2aef7135a..73d05fec4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,5 +17,4 @@ django-qr-code==1.1.0 # Generate QR codes flake8==3.3.0 # PEP checking coverage==4.0.3 # Unit test coverage python-coveralls==2.9.1 # Coveralls linking (for Travis) -fuzzywuzzy==0.17.0 # Fuzzy string matching -python-Levenshtein==0.12.0 # Required for fuzzywuzzy +rapidfuzz==0.2.1 # Fuzzy string matching From dae45875fbd1352b50c6d78cde313196ea6a4493 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 14:46:23 +1100 Subject: [PATCH 2/8] Add 'on_order' quantity to the part list API --- InvenTree/part/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index a121553409..d24f0dc6ee 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -172,6 +172,7 @@ class PartList(generics.ListCreateAPIView): 'active', ).annotate( in_stock=Sum('stock_items__quantity'), + on_order=Sum('supplier_parts__purchase_order_line_items__quantity'), ) # TODO - Annotate total being built From 99efbd4c40fb59432118bbb732d287b56814809d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 14:46:40 +1100 Subject: [PATCH 3/8] If a part has no stock but is on order, display an "on-order" badge --- .../InvenTree/static/script/inventree/part.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js index 8ad934760b..c94c512d6f 100644 --- a/InvenTree/InvenTree/static/script/inventree/part.js +++ b/InvenTree/InvenTree/static/script/inventree/part.js @@ -182,17 +182,27 @@ function loadPartTable(table, url, options={}) { searchable: false, sortable: true, formatter: function(value, row, index, field) { + console.log("On order:", row.on_order); + + var html = ""; + var link = "stock"; + if (value) { if (row.units) { value += ' ' + row.units + ''; } - return renderLink(value, '/part/' + row.pk + '/stock/'); - } - else { - return "No Stock"; + html = value; + + } else if (row.on_order) { + value = "On Order : " + row.on_order + ""; + link = "orders"; + } else { + value ="No Stock"; } + + return renderLink(value, '/part/' + row.pk + "/" + link + "/"); } }); From 57123283f4ef651058655e638f1bc5394e47b647 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 17:08:01 +1100 Subject: [PATCH 4/8] Better filtering of annotations for Part-list API --- .../InvenTree/static/script/inventree/part.js | 4 +--- InvenTree/InvenTree/status_codes.py | 10 ++++++++-- InvenTree/part/api.py | 18 ++++++++++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js index c94c512d6f..daa82485b7 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"; 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/part/api.py b/InvenTree/part/api.py index d24f0dc6ee..2218c240c1 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 from InvenTree.views import TreeSerializer from InvenTree.helpers import str2bool @@ -153,6 +154,15 @@ 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) + + # Set of fields we wish to serialize data = queryset.values( 'pk', 'category', @@ -171,12 +181,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) ) # TODO - Annotate total being built - # TODO - Annotate total on order # Reduce the number of lookups we need to do for the part categories categories = {} From 41bbbdcd4374bf0ed6ce9380f430528c817e5d80 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 17:31:59 +1100 Subject: [PATCH 5/8] Improve query speed when calculating how many parts are on order --- InvenTree/part/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index d22e6e7be1..7103ee808f 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -919,7 +919,15 @@ 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')) + + quantity = orders['quantity'] + + if quantity is None: + quantity = 0 + + return quantity def get_parameters(self): """ Return all parameters for this part, ordered by name """ From 6a78f6d451a80e9162335a71509b3b05b57f968e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 17:43:02 +1100 Subject: [PATCH 6/8] Include quantity currently being build in Part API --- InvenTree/part/api.py | 10 ++++++---- InvenTree/part/models.py | 7 ++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 2218c240c1..1d6aaa84aa 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -25,7 +25,7 @@ from .models import PartParameter, PartParameterTemplate from . import serializers as part_serializers -from InvenTree.status_codes import OrderStatus, StockStatus +from InvenTree.status_codes import OrderStatus, StockStatus, BuildStatus from InvenTree.views import TreeSerializer from InvenTree.helpers import str2bool @@ -162,6 +162,9 @@ class PartList(generics.ListCreateAPIView): # "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', @@ -183,11 +186,10 @@ class PartList(generics.ListCreateAPIView): ).annotate( # 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) + 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 - # 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 7103ee808f..16d6e30b9f 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): From 713d7960a80cde40c03fd6e04cd417247ac70d75 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 17:56:44 +1100 Subject: [PATCH 7/8] Fix on_order calculation - Take into account the number "received" - Also fix unit tests --- InvenTree/order/tests.py | 5 +++-- InvenTree/part/models.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) 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/models.py b/InvenTree/part/models.py index 16d6e30b9f..58ee8bbd2c 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -925,14 +925,20 @@ class Part(models.Model): """ Return the total number of items on order for this part. """ orders = self.supplier_parts.filter(purchase_order_line_items__order__status__in=OrderStatus.OPEN).aggregate( - quantity=Sum('purchase_order_line_items__quantity')) + 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 - return quantity + if received is None: + received = 0 + + return quantity - received def get_parameters(self): """ Return all parameters for this part, ordered by name """ From c0650ba7f4d1c7a12a2a18295b778a6ceb91ed89 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 26 Mar 2020 17:57:49 +1100 Subject: [PATCH 8/8] Add "buiding" icon in part list if no stock and none on order --- InvenTree/InvenTree/static/script/inventree/part.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js index daa82485b7..693a4d1faa 100644 --- a/InvenTree/InvenTree/static/script/inventree/part.js +++ b/InvenTree/InvenTree/static/script/inventree/part.js @@ -196,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"; }