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.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:
|
||||||
|
@ -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):
|
||||||
|
@ -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',
|
||||||
|
Loading…
Reference in New Issue
Block a user