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)
|
return self.getAllocatedQuantity(part) >= self.getRequiredQuantity(part)
|
||||||
|
|
||||||
def getRequiredQuantity(self, part):
|
def getRequiredQuantity(self, part, output=None):
|
||||||
""" Calculate the quantity of <part> required to make this build.
|
"""
|
||||||
|
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:
|
try:
|
||||||
@ -483,27 +488,51 @@ class Build(MPTTModel):
|
|||||||
except PartModels.BomItem.DoesNotExist:
|
except PartModels.BomItem.DoesNotExist:
|
||||||
q = 0
|
q = 0
|
||||||
|
|
||||||
return q * self.quantity
|
if output:
|
||||||
|
return q * output.quantity
|
||||||
|
else:
|
||||||
|
return q * self.quantity
|
||||||
|
|
||||||
def getAllocatedQuantity(self, part):
|
def getAllocatedQuantity(self, part, output=None):
|
||||||
""" Calculate the total number of <part> currently allocated to this build
|
"""
|
||||||
|
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']
|
return allocated['q']
|
||||||
|
|
||||||
def getUnallocatedQuantity(self, part):
|
def getUnallocatedQuantity(self, part, output=None):
|
||||||
""" Calculate the quantity of <part> which still needs to be allocated to this build.
|
"""
|
||||||
|
Calculate the quantity of <part> which still needs to be allocated to this build.
|
||||||
|
|
||||||
Args:
|
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:
|
Returns:
|
||||||
The remaining allocated quantity
|
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
|
@property
|
||||||
def required_parts(self):
|
def required_parts(self):
|
||||||
|
@ -552,6 +552,8 @@ class BuildItemCreate(AjaxCreateView):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
self.output = None
|
||||||
|
|
||||||
# If the output stock item is specified, hide the input field
|
# If the output stock item is specified, hide the input field
|
||||||
output_id = form['install_into'].value()
|
output_id = form['install_into'].value()
|
||||||
|
|
||||||
@ -591,11 +593,11 @@ class BuildItemCreate(AjaxCreateView):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Exclude StockItem objects which are already allocated to this build and part
|
# Exclude StockItem objects which are already allocated to this build and part
|
||||||
stock_filter = stock_filter.exclude(
|
to_exclude = BuildItem.objects.filter(build=build_id, stock_item__part=part_id)
|
||||||
id__in=[
|
if self.output:
|
||||||
item.stock_item.id for item in BuildItem.objects.filter(build=build_id, stock_item__part=part_id)
|
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:
|
except Part.DoesNotExist:
|
||||||
self.part = None
|
self.part = None
|
||||||
@ -654,15 +656,26 @@ class BuildItemCreate(AjaxCreateView):
|
|||||||
except Build.DoesNotExist:
|
except Build.DoesNotExist:
|
||||||
pass
|
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)
|
quantity = self.request.GET.get('quantity', None)
|
||||||
|
|
||||||
if quantity is not None:
|
if quantity is not None:
|
||||||
quantity = float(quantity)
|
quantity = float(quantity)
|
||||||
|
elif required_quantity is not None:
|
||||||
if quantity is None:
|
quantity = required_quantity
|
||||||
# Work out how many parts remain to be alloacted for the build
|
|
||||||
if part:
|
|
||||||
quantity = build.getUnallocatedQuantity(part)
|
|
||||||
|
|
||||||
item_id = self.get_param('item')
|
item_id = self.get_param('item')
|
||||||
|
|
||||||
@ -686,14 +699,6 @@ class BuildItemCreate(AjaxCreateView):
|
|||||||
else:
|
else:
|
||||||
quantity = min(quantity, item.unallocated_quantity())
|
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:
|
if quantity is not None:
|
||||||
initials['quantity'] = quantity
|
initials['quantity'] = quantity
|
||||||
|
|
||||||
|
@ -28,10 +28,12 @@ class BomItemTest(TestCase):
|
|||||||
self.assertEqual(self.bob.bom_count, 4)
|
self.assertEqual(self.bob.bom_count, 4)
|
||||||
|
|
||||||
def test_in_bom(self):
|
def test_in_bom(self):
|
||||||
parts = self.bob.required_parts()
|
parts = self.bob.getRequiredParts()
|
||||||
|
|
||||||
self.assertIn(self.orphan, parts)
|
self.assertIn(self.orphan, parts)
|
||||||
|
|
||||||
|
# TODO: Tests for multi-level BOMs
|
||||||
|
|
||||||
def test_used_in(self):
|
def test_used_in(self):
|
||||||
self.assertEqual(self.bob.used_in_count, 0)
|
self.assertEqual(self.bob.used_in_count, 0)
|
||||||
self.assertEqual(self.orphan.used_in_count, 1)
|
self.assertEqual(self.orphan.used_in_count, 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user