Add unit tests for the auto_allocate_stock method

This commit is contained in:
Oliver 2022-03-04 16:27:50 +11:00
parent 71cd6c93d1
commit 0bb0047fcd
3 changed files with 119 additions and 10 deletions

View File

@ -826,7 +826,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
self.save()
@transaction.atomic
def auto_allocate_stock(self, user, **kwargs):
def auto_allocate_stock(self, **kwargs):
"""
Automatically allocate stock items against this build order,
following a number of 'guidelines':
@ -847,9 +847,8 @@ class Build(MPTTModel, ReferenceIndexingMixin):
# Get a list of all 'untracked' BOM items
for bom_item in self.untracked_bom_items:
variant_parts = bom_item.sub_part.get_descendants(include_self=False)
substitute_parts = [p for p in bom_item.substitutes.all()]
unallocated_quantity = self.unallocated_quantity(bom_item)
@ -891,7 +890,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
return 2
else:
return 3
available_stock = sorted(available_stock, key=stock_sort)
if len(available_stock) == 0:

View File

@ -746,11 +746,9 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
data = self.validated_data
request = self.context['request']
build = self.context['build']
build.auto_allocate_stock(
request.user,
location=data.get('location', None),
interchangeable=data['interchangeable'],
substitutes=data['substitutes'],

View File

@ -8,11 +8,11 @@ from django.db.utils import IntegrityError
from InvenTree import status_codes as status
from build.models import Build, BuildItem, get_next_build_number
from part.models import Part, BomItem
from part.models import Part, BomItem, BomItemSubstitute
from stock.models import StockItem
class BuildTest(TestCase):
class BuildTestBase(TestCase):
"""
Run some tests to ensure that the Build model is working properly.
"""
@ -107,13 +107,20 @@ class BuildTest(TestCase):
)
# Create some stock items to assign to the build
self.stock_1_1 = StockItem.objects.create(part=self.sub_part_1, quantity=1000)
self.stock_1_1 = StockItem.objects.create(part=self.sub_part_1, quantity=3)
self.stock_1_2 = StockItem.objects.create(part=self.sub_part_1, quantity=100)
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5000)
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_3_1 = StockItem.objects.create(part=self.sub_part_3, quantity=1000)
class BuildTest(BuildTestBase):
def test_ref_int(self):
"""
Test the "integer reference" field used for natural sorting
@ -369,3 +376,108 @@ class BuildTest(TestCase):
for output in outputs:
self.assertFalse(output.is_building)
class AutoAllocationTests(BuildTestBase):
"""
Tests for auto allocating stock against a build order
"""
def setUp(self):
super().setUp()
# Add a "substitute" part for bom_item_2
alt_part = Part.objects.create(
name="alt part",
description="An alternative part!",
component=True,
)
BomItemSubstitute.objects.create(
bom_item=self.bom_item_2,
part=alt_part,
)
StockItem.objects.create(
part=alt_part,
quantity=500,
)
def test_auto_allocate(self):
"""
Run the 'auto-allocate' function. What do we expect to happen?
There are two "untracked" parts:
- sub_part_1 (quantity 5 per BOM = 50 required total) / 103 in stock (2 items)
- sub_part_2 (quantity 3 per BOM = 30 required total) / 25 in stock (5 items)
A "fully auto" allocation should allocate *all* of these stock items to the build
"""
# No build item allocations have been made against the build
self.assertEqual(self.build.allocated_stock.count(), 0)
self.assertFalse(self.build.are_untracked_parts_allocated())
# Stock is not interchangeable, nothing will happen
self.build.auto_allocate_stock(
interchangeable=False,
substitutes=False,
)
self.assertFalse(self.build.are_untracked_parts_allocated())
self.assertEqual(self.build.allocated_stock.count(), 0)
self.assertFalse(self.build.is_bom_item_allocated(self.bom_item_1))
self.assertFalse(self.build.is_bom_item_allocated(self.bom_item_2))
self.assertEqual(self.build.unallocated_quantity(self.bom_item_1), 50)
self.assertEqual(self.build.unallocated_quantity(self.bom_item_2), 30)
# This time we expect stock to be allocated!
self.build.auto_allocate_stock(
interchangeable=True,
substitutes=False,
)
self.assertFalse(self.build.are_untracked_parts_allocated())
self.assertEqual(self.build.allocated_stock.count(), 7)
self.assertTrue(self.build.is_bom_item_allocated(self.bom_item_1))
self.assertFalse(self.build.is_bom_item_allocated(self.bom_item_2))
self.assertEqual(self.build.unallocated_quantity(self.bom_item_1), 0)
self.assertEqual(self.build.unallocated_quantity(self.bom_item_2), 5)
# This time, allow substitue parts to be used!
self.build.auto_allocate_stock(
interchangeable=True,
substitutes=True,
)
# self.assertTrue(self.build.are_untracked_parts_allocated())
# self.assertEqual(self.build.allocated_stock.count(), 8)
self.assertEqual(self.build.unallocated_quantity(self.bom_item_1), 0)
self.assertEqual(self.build.unallocated_quantity(self.bom_item_2), 0)
self.assertTrue(self.build.is_bom_item_allocated(self.bom_item_1))
self.assertTrue(self.build.is_bom_item_allocated(self.bom_item_2))
def test_fully_auto(self):
"""
We should be able to auto-allocate against a build in a single go
"""
self.build.auto_allocate_stock(
interchangeable=True,
substitutes=True
)
self.assertTrue(self.build.are_untracked_parts_allocated())
self.assertEqual(self.build.unallocated_quantity(self.bom_item_1), 0)
self.assertEqual(self.build.unallocated_quantity(self.bom_item_2), 0)