Cleanup part allocation calculation functions

This commit is contained in:
Oliver Walters 2020-04-28 10:35:19 +10:00
parent 6bdf371490
commit fb70da0331
3 changed files with 49 additions and 40 deletions

View File

@ -25,8 +25,8 @@ from InvenTree.status_codes import BuildStatus, StockStatus
from InvenTree.fields import InvenTreeURLField
from InvenTree.helpers import decimal2string
from stock.models import StockItem
from part.models import Part, BomItem
from stock import models as StockModels
from part import models as PartModels
class Build(MPTTModel):
@ -465,7 +465,7 @@ class BuildItem(models.Model):
if self.stock_item.serial and not self.quantity == 1:
errors['quantity'] = _('Quantity must be 1 for serialized stock')
except (StockItem.DoesNotExist, Part.DoesNotExist):
except (StockModels.StockItem.DoesNotExist, PartModels.Part.DoesNotExist):
pass
if len(errors) > 0:

View File

@ -13,6 +13,7 @@ from django.urls import reverse
from django.db import models, transaction
from django.db.models import Sum
from django.db.models.functions import Coalesce
from django.db.models import prefetch_related_objects
from django.core.validators import MinValueValidator
@ -41,6 +42,8 @@ from InvenTree.helpers import decimal2string, normalize
from InvenTree.status_codes import BuildStatus, StockStatus, PurchaseOrderStatus
from build import models as BuildModels
from order import models as OrderModels
from company.models import SupplierPart
from stock import models as StockModels
@ -502,8 +505,7 @@ class Part(models.Model):
"""
total = self.total_stock
total -= self.allocation_count
total -= self.allocation_count()
return max(total, 0)
@ -594,46 +596,48 @@ class Part(models.Model):
return quantity
@property
def build_allocation(self):
""" Return list of builds to which this part is allocated
def build_order_allocations(self):
"""
Return all 'BuildItem' objects which allocate this part to Build objects
"""
builds = []
return BuildModels.BuildItem.objects.filter(stock_item__part__id=self.id)
for item in self.used_in.all().prefetch_related('part__builds'):
active = item.part.active_builds
for build in active:
b = {}
b['build'] = build
b['quantity'] = item.quantity * build.quantity
builds.append(b)
prefetch_related_objects(builds, 'build_items')
return builds
@property
def allocated_build_count(self):
""" Return the total number of this part that are allocated for builds
def build_order_allocation_count(self):
"""
Return the total amount of this part allocated to build orders
"""
return sum([a['quantity'] for a in self.build_allocation])
query = self.build_order_allocations().aggregate(total=Coalesce(Sum('quantity'), 0))
return query['total']
def sales_order_allocations(self):
"""
Return all sales-order-allocation objects which allocate this part to a SalesOrder
"""
return OrderModels.SalesOrderAllocation.objects.filter(item__part__id=self.id)
def sales_order_allocation_count(self):
"""
Return the tutal quantity of this part allocated to sales orders
"""
query = self.sales_order_allocations().aggregate(total=Coalesce(Sum('quantity'), 0))
return query['total']
@property
def allocation_count(self):
""" Return true if any of this part is allocated:
- To another build
- To a customer order
"""
Return the total quantity of stock allocated for this part,
against both build orders and sales orders.
"""
return sum([
self.allocated_build_count,
self.build_order_allocation_count(),
self.sales_order_allocation_count(),
])
@property
@ -645,7 +649,7 @@ class Part(models.Model):
- belongs_to is None
"""
return self.stock_items.filter(StockModels.StockItem.IN_STOCK_FILTER).exclude(status__in=StockStatus.UNAVAILABLE_CODES)
return self.stock_items.filter(StockModels.StockItem.IN_STOCK_FILTER)
@property
def total_stock(self):

View File

@ -31,7 +31,7 @@ from InvenTree.models import InvenTreeTree
from InvenTree.fields import InvenTreeURLField
from part import models as PartModels
from order.models import PurchaseOrder, SalesOrder
from order import models as OrderModels
class StockLocation(InvenTreeTree):
@ -134,7 +134,12 @@ class StockItem(MPTTModel):
"""
# A Query filter which will be re-used in multiple places to determine if a StockItem is actually "in stock"
IN_STOCK_FILTER = Q(sales_order=None, build_order=None, belongs_to=None)
IN_STOCK_FILTER = Q(
sales_order=None,
build_order=None,
belongs_to=None,
status__in=StockStatus.AVAILABLE_CODES
)
def save(self, *args, **kwargs):
if not self.pk:
@ -393,7 +398,7 @@ class StockItem(MPTTModel):
)
purchase_order = models.ForeignKey(
PurchaseOrder,
'order.PurchaseOrder',
on_delete=models.SET_NULL,
verbose_name=_('Source Purchase Order'),
related_name='stock_items',
@ -402,7 +407,7 @@ class StockItem(MPTTModel):
)
sales_order = models.ForeignKey(
SalesOrder,
'order.SalesOrder',
on_delete=models.SET_NULL,
verbose_name=_("Destination Sales Order"),
related_name='stock_items',