diff --git a/InvenTree/InvenTree/status_codes.py b/InvenTree/InvenTree/status_codes.py index 9207e28c7b..930242f396 100644 --- a/InvenTree/InvenTree/status_codes.py +++ b/InvenTree/InvenTree/status_codes.py @@ -32,6 +32,19 @@ class OrderStatus(StatusCode): RETURNED: _("Returned"), } + # Open orders + OPEN = [ + PENDING, + PLACED, + ] + + # Failed orders + FAILED = [ + CANCELLED, + LOST, + RETURNED + ] + class StockStatus(StatusCode): diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index d054aff040..324e5dcf59 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -10,9 +10,10 @@ import os import math from django.core.validators import MinValueValidator +from django.db import models +from django.db.models import Sum from django.apps import apps -from django.db import models from django.urls import reverse from django.conf import settings from django.contrib.staticfiles.templatetags.staticfiles import static @@ -132,10 +133,7 @@ class Company(models.Model): def outstanding_purchase_orders(self): """ Return purchase orders which are 'outstanding' """ - return self.purchase_orders.filter(status__in=[ - OrderStatus.PENDING, - OrderStatus.PLACED - ]) + return self.purchase_orders.filter(status__in=OrderStatus.OPEN) def complete_purchase_orders(self): return self.purchase_orders.filter(status=OrderStatus.COMPLETE) @@ -143,12 +141,7 @@ class Company(models.Model): def failed_purchase_orders(self): """ Return any purchase orders which were not successful """ - return self.purchase_orders.filter(status__in=[ - OrderStatus.CANCELLED, - OrderStatus.LOST, - OrderStatus.RETURNED - ]) - + return self.purchase_orders.filter(status__in=OrderStatus.FAILED) class Contact(models.Model): """ A Contact represents a person who works at a particular company. @@ -307,6 +300,24 @@ class SupplierPart(models.Model): else: return None + def open_orders(self): + """ Return a database query for PO line items for this SupplierPart, + limited to purchase orders that are open / outstanding. + """ + + return self.purchase_order_line_items.prefetch_related('order').filter(order__status__in=OrderStatus.OPEN) + + def on_order(self): + """ Return the total quantity of items currently on order. + + Subtract partially received stock as appropriate + """ + + totals = self.open_orders().aggregate(Sum('quantity'), Sum('received')) + + return totals['quantity__sum'] - totals['received__sum'] + + def purchase_orders(self): """ Returns a list of purchase orders relating to this supplier part """ diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 9a2486798d..f1294a972d 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -806,6 +806,11 @@ class Part(models.Model): return orders + 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()]) + def attach_file(instance, filename): """ Function for storing a file for a PartAttachment diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 9c68cfe69f..2661497c74 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -96,6 +96,12 @@ {{ part.allocation_count }} {% endif %} + {% if part.on_order > 0 %} + + On Order + {{ part.on_order }} + + {% endif %}