mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Cleanup part allocation calculation functions
This commit is contained in:
parent
6bdf371490
commit
fb70da0331
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user