mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Move "getAvailableStockItems" to the build model
This commit is contained in:
parent
0752df26dc
commit
fb7d9a7edf
18
InvenTree/build/migrations/0026_auto_20201023_1228.py
Normal file
18
InvenTree/build/migrations/0026_auto_20201023_1228.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.0.7 on 2020-10-23 12:28
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stock', '0052_stockitem_is_building'),
|
||||
('build', '0025_auto_20201020_1248'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='builditem',
|
||||
unique_together={('build', 'stock_item', 'install_into')},
|
||||
),
|
||||
]
|
@ -551,6 +551,39 @@ class Build(MPTTModel):
|
||||
|
||||
return parts
|
||||
|
||||
def getAvailableStockItems(self, part=None, output=None):
|
||||
"""
|
||||
Return available stock items for the build.
|
||||
"""
|
||||
|
||||
items = StockModels.StockItem.objects.filter(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
|
||||
if part:
|
||||
# Filter items which match the given Part
|
||||
items = items.filter(part=part)
|
||||
|
||||
if output:
|
||||
# Exclude items which are already allocated to the particular build output
|
||||
|
||||
to_exclude = BuildItem.objects.filter(
|
||||
build=self,
|
||||
stock_item__part=part,
|
||||
install_into=output
|
||||
)
|
||||
|
||||
items = items.exclude(
|
||||
id__in=[item.stock_item.id for item in to_exclude.all()]
|
||||
)
|
||||
|
||||
# Limit query to stock items which are "downstream" of the source location
|
||||
if self.take_from is not None:
|
||||
items = items.filter(
|
||||
location__in=[loc for loc in self.take_from.getUniqueChildren()]
|
||||
)
|
||||
|
||||
return items
|
||||
|
||||
|
||||
@property
|
||||
def can_build(self):
|
||||
""" Return true if there are enough parts to supply build """
|
||||
@ -597,7 +630,7 @@ class BuildItem(models.Model):
|
||||
|
||||
class Meta:
|
||||
unique_together = [
|
||||
('build', 'stock_item'),
|
||||
('build', 'stock_item', 'install_into'),
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
@ -613,24 +646,34 @@ class BuildItem(models.Model):
|
||||
errors = {}
|
||||
|
||||
try:
|
||||
# Allocated part must be in the BOM for the master part
|
||||
if self.stock_item.part not in self.build.part.getRequiredParts(recursive=False):
|
||||
errors['stock_item'] = [_("Selected stock item not found in BOM for part '{p}'".format(p=self.build.part.full_name))]
|
||||
|
||||
# Allocated quantity cannot exceed available stock quantity
|
||||
if self.quantity > self.stock_item.quantity:
|
||||
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})".format(
|
||||
n=normalize(self.quantity),
|
||||
q=normalize(self.stock_item.quantity)
|
||||
))]
|
||||
|
||||
# Allocated quantity cannot cause the stock item to be over-allocated
|
||||
if self.stock_item.quantity - self.stock_item.allocation_count() + self.quantity < self.quantity:
|
||||
errors['quantity'] = _('StockItem is over-allocated')
|
||||
|
||||
# Allocated quantity must be positive
|
||||
if self.quantity <= 0:
|
||||
errors['quantity'] = _('Allocation quantity must be greater than zero')
|
||||
|
||||
# Quantity must be 1 for serialized stock
|
||||
if self.stock_item.serial and not self.quantity == 1:
|
||||
errors['quantity'] = _('Quantity must be 1 for serialized stock')
|
||||
|
||||
# Part reference must match between output stock item and built part
|
||||
if self.install_into is not None:
|
||||
if not self.install_into.part == self.build.part:
|
||||
errors['install_into'] = _('Part reference differs between build and build output')
|
||||
|
||||
except (StockModels.StockItem.DoesNotExist, PartModels.Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
|
@ -535,6 +535,10 @@ class BuildItemCreate(AjaxCreateView):
|
||||
|
||||
form = super(AjaxCreateView, self).get_form()
|
||||
|
||||
self.build = None
|
||||
self.part = None
|
||||
self.output = None
|
||||
|
||||
# If the Build object is specified, hide the input field.
|
||||
# We do not want the users to be able to move a BuildItem to a different build
|
||||
build_id = form['build'].value()
|
||||
@ -546,14 +550,13 @@ class BuildItemCreate(AjaxCreateView):
|
||||
"""
|
||||
form.fields['build'].widget = HiddenInput()
|
||||
form.fields['install_into'].queryset = StockItem.objects.filter(build=build_id, is_building=True)
|
||||
self.build = Build.objects.get(pk=build_id)
|
||||
else:
|
||||
"""
|
||||
Build has *not* been selected
|
||||
"""
|
||||
pass
|
||||
|
||||
self.output = None
|
||||
|
||||
# If the output stock item is specified, hide the input field
|
||||
output_id = form['install_into'].value()
|
||||
|
||||
@ -568,47 +571,18 @@ class BuildItemCreate(AjaxCreateView):
|
||||
# If the sub_part is supplied, limit to matching stock items
|
||||
part_id = self.get_param('part')
|
||||
|
||||
# We need to precisely control which StockItem objects the user can choose to allocate
|
||||
stock_filter = form.fields['stock_item'].queryset
|
||||
|
||||
# Restrict to only items which are "in stock"
|
||||
stock_filter = stock_filter.filter(StockItem.IN_STOCK_FILTER)
|
||||
|
||||
if part_id:
|
||||
try:
|
||||
self.part = Part.objects.get(pk=part_id)
|
||||
|
||||
# Only allow StockItem objects which match the current part
|
||||
stock_filter = stock_filter.filter(part=part_id)
|
||||
|
||||
if build_id is not None:
|
||||
try:
|
||||
build = Build.objects.get(id=build_id)
|
||||
|
||||
if build.take_from is not None:
|
||||
# Limit query to stock items that are downstream of the 'take_from' location
|
||||
stock_filter = stock_filter.filter(location__in=[loc for loc in build.take_from.getUniqueChildren()])
|
||||
|
||||
except Build.DoesNotExist:
|
||||
except (ValueError, Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Exclude StockItem objects which are already allocated to this build and part
|
||||
to_exclude = BuildItem.objects.filter(build=build_id, stock_item__part=part_id)
|
||||
if self.output:
|
||||
to_exclude = to_exclude.filter(install_into=self.output)
|
||||
if self.build and self.part:
|
||||
available_items = self.build.getAvailableStockItems(part=self.part, output=self.output)
|
||||
form.fields['stock_item'].queryset = available_items
|
||||
|
||||
stock_filter = stock_filter.exclude(id__in=[item.stock_item.id for item in to_exclude.all()])
|
||||
|
||||
except Part.DoesNotExist:
|
||||
self.part = None
|
||||
pass
|
||||
|
||||
else:
|
||||
self.part = None
|
||||
|
||||
form.fields['stock_item'].queryset = stock_filter
|
||||
|
||||
self.available_stock = stock_filter.all()
|
||||
self.available_stock = form.fields['stock_item'].queryset.all()
|
||||
|
||||
# If there is only a single stockitem available, select it!
|
||||
if len(self.available_stock) == 1:
|
||||
|
@ -139,6 +139,7 @@ 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(
|
||||
quantity__gt=0,
|
||||
sales_order=None,
|
||||
build_order=None,
|
||||
belongs_to=None,
|
||||
|
Loading…
Reference in New Issue
Block a user