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

View File

@ -13,6 +13,7 @@ from django.urls import reverse
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Sum from django.db.models import Sum
from django.db.models.functions import Coalesce
from django.db.models import prefetch_related_objects from django.db.models import prefetch_related_objects
from django.core.validators import MinValueValidator 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 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 company.models import SupplierPart
from stock import models as StockModels from stock import models as StockModels
@ -502,8 +505,7 @@ class Part(models.Model):
""" """
total = self.total_stock total = self.total_stock
total -= self.allocation_count()
total -= self.allocation_count
return max(total, 0) return max(total, 0)
@ -594,46 +596,48 @@ class Part(models.Model):
return quantity return quantity
@property def build_order_allocations(self):
def build_allocation(self): """
""" Return list of builds to which this part is allocated 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'): def build_order_allocation_count(self):
"""
active = item.part.active_builds Return the total amount of this part allocated to build orders
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
""" """
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): def allocation_count(self):
""" Return true if any of this part is allocated: """
Return the total quantity of stock allocated for this part,
- To another build against both build orders and sales orders.
- To a customer order
""" """
return sum([ return sum([
self.allocated_build_count, self.build_order_allocation_count(),
self.sales_order_allocation_count(),
]) ])
@property @property
@ -645,7 +649,7 @@ class Part(models.Model):
- belongs_to is None - 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 @property
def total_stock(self): def total_stock(self):

View File

@ -31,7 +31,7 @@ from InvenTree.models import InvenTreeTree
from InvenTree.fields import InvenTreeURLField from InvenTree.fields import InvenTreeURLField
from part import models as PartModels from part import models as PartModels
from order.models import PurchaseOrder, SalesOrder from order import models as OrderModels
class StockLocation(InvenTreeTree): 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" # 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): def save(self, *args, **kwargs):
if not self.pk: if not self.pk:
@ -393,7 +398,7 @@ class StockItem(MPTTModel):
) )
purchase_order = models.ForeignKey( purchase_order = models.ForeignKey(
PurchaseOrder, 'order.PurchaseOrder',
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
verbose_name=_('Source Purchase Order'), verbose_name=_('Source Purchase Order'),
related_name='stock_items', related_name='stock_items',
@ -402,7 +407,7 @@ class StockItem(MPTTModel):
) )
sales_order = models.ForeignKey( sales_order = models.ForeignKey(
SalesOrder, 'order.SalesOrder',
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
verbose_name=_("Destination Sales Order"), verbose_name=_("Destination Sales Order"),
related_name='stock_items', related_name='stock_items',