mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Bug fixes for BuildItemCreate view:
- Add option to calculate required quantity against a particular build output, not just the build
This commit is contained in:
parent
076d5c4f7f
commit
0752df26dc
@ -473,8 +473,13 @@ class Build(MPTTModel):
|
||||
|
||||
return self.getAllocatedQuantity(part) >= self.getRequiredQuantity(part)
|
||||
|
||||
def getRequiredQuantity(self, part):
|
||||
""" Calculate the quantity of <part> required to make this build.
|
||||
def getRequiredQuantity(self, part, output=None):
|
||||
"""
|
||||
Calculate the quantity of <part> required to make this build.
|
||||
|
||||
Args:
|
||||
part: The 'Part' archetype reference
|
||||
output: A particular build output (StockItem) (or None to specify the entire build)
|
||||
"""
|
||||
|
||||
try:
|
||||
@ -483,27 +488,51 @@ class Build(MPTTModel):
|
||||
except PartModels.BomItem.DoesNotExist:
|
||||
q = 0
|
||||
|
||||
return q * self.quantity
|
||||
if output:
|
||||
return q * output.quantity
|
||||
else:
|
||||
return q * self.quantity
|
||||
|
||||
def getAllocatedQuantity(self, part):
|
||||
""" Calculate the total number of <part> currently allocated to this build
|
||||
def getAllocatedQuantity(self, part, output=None):
|
||||
"""
|
||||
Calculate the total number of <part> currently allocated to this build.
|
||||
|
||||
Args:
|
||||
part: The 'Part' archetype reference
|
||||
output: A particular build output (StockItem) (or None to specify the entire build)
|
||||
"""
|
||||
|
||||
allocated = BuildItem.objects.filter(build=self.id, stock_item__part=part.id).aggregate(q=Coalesce(Sum('quantity'), 0))
|
||||
allocations = BuildItem.objects.filter(
|
||||
build=self.id,
|
||||
stock_item__part=part.id
|
||||
)
|
||||
|
||||
# Optionally, filter by the specified build output StockItem
|
||||
if output is not None:
|
||||
allocations = allocations.filter(
|
||||
install_into=output
|
||||
)
|
||||
|
||||
allocated = allocations.aggregate(q=Coalesce(Sum('quantity'), 0))
|
||||
|
||||
return allocated['q']
|
||||
|
||||
def getUnallocatedQuantity(self, part):
|
||||
""" Calculate the quantity of <part> which still needs to be allocated to this build.
|
||||
def getUnallocatedQuantity(self, part, output=None):
|
||||
"""
|
||||
Calculate the quantity of <part> which still needs to be allocated to this build.
|
||||
|
||||
Args:
|
||||
Part - the part to be tested
|
||||
part - the part to be tested
|
||||
output - A particular build output (StockItem) (or None to specify the entire build)
|
||||
|
||||
Returns:
|
||||
The remaining allocated quantity
|
||||
"""
|
||||
|
||||
return max(self.getRequiredQuantity(part) - self.getAllocatedQuantity(part), 0)
|
||||
required = self.getRequiredQuantity(part, output=output)
|
||||
allocated = self.getAllocatedQuantity(part, output=output)
|
||||
|
||||
return max(required-allocated, 0)
|
||||
|
||||
@property
|
||||
def required_parts(self):
|
||||
|
@ -552,6 +552,8 @@ class BuildItemCreate(AjaxCreateView):
|
||||
"""
|
||||
pass
|
||||
|
||||
self.output = None
|
||||
|
||||
# If the output stock item is specified, hide the input field
|
||||
output_id = form['install_into'].value()
|
||||
|
||||
@ -591,11 +593,11 @@ class BuildItemCreate(AjaxCreateView):
|
||||
pass
|
||||
|
||||
# Exclude StockItem objects which are already allocated to this build and part
|
||||
stock_filter = stock_filter.exclude(
|
||||
id__in=[
|
||||
item.stock_item.id for item in BuildItem.objects.filter(build=build_id, stock_item__part=part_id)
|
||||
]
|
||||
)
|
||||
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)
|
||||
|
||||
stock_filter = stock_filter.exclude(id__in=[item.stock_item.id for item in to_exclude.all()])
|
||||
|
||||
except Part.DoesNotExist:
|
||||
self.part = None
|
||||
@ -654,15 +656,26 @@ class BuildItemCreate(AjaxCreateView):
|
||||
except Build.DoesNotExist:
|
||||
pass
|
||||
|
||||
# If the output has been specified
|
||||
if output_id:
|
||||
try:
|
||||
output = StockItem.objects.get(pk=output_id)
|
||||
initials['install_into'] = output
|
||||
except (ValueError, StockItem.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Work out how much stock is required
|
||||
if build and part:
|
||||
required_quantity = build.getUnallocatedQuantity(part, output=output)
|
||||
else:
|
||||
required_quantity = None
|
||||
|
||||
quantity = self.request.GET.get('quantity', None)
|
||||
|
||||
if quantity is not None:
|
||||
quantity = float(quantity)
|
||||
|
||||
if quantity is None:
|
||||
# Work out how many parts remain to be alloacted for the build
|
||||
if part:
|
||||
quantity = build.getUnallocatedQuantity(part)
|
||||
elif required_quantity is not None:
|
||||
quantity = required_quantity
|
||||
|
||||
item_id = self.get_param('item')
|
||||
|
||||
@ -686,14 +699,6 @@ class BuildItemCreate(AjaxCreateView):
|
||||
else:
|
||||
quantity = min(quantity, item.unallocated_quantity())
|
||||
|
||||
# If the output has been specified
|
||||
if output_id:
|
||||
try:
|
||||
output = StockItem.objects.get(pk=output_id)
|
||||
initials['install_into'] = output
|
||||
except (ValueError, StockItem.DoesNotExist):
|
||||
pass
|
||||
|
||||
if quantity is not None:
|
||||
initials['quantity'] = quantity
|
||||
|
||||
|
@ -28,10 +28,12 @@ class BomItemTest(TestCase):
|
||||
self.assertEqual(self.bob.bom_count, 4)
|
||||
|
||||
def test_in_bom(self):
|
||||
parts = self.bob.required_parts()
|
||||
parts = self.bob.getRequiredParts()
|
||||
|
||||
self.assertIn(self.orphan, parts)
|
||||
|
||||
# TODO: Tests for multi-level BOMs
|
||||
|
||||
def test_used_in(self):
|
||||
self.assertEqual(self.bob.used_in_count, 0)
|
||||
self.assertEqual(self.orphan.used_in_count, 1)
|
||||
|
Loading…
Reference in New Issue
Block a user