mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-01 04:56:45 +00:00
Add unit tests for the auto_allocate_stock method
This commit is contained in:
parent
71cd6c93d1
commit
0bb0047fcd
@ -826,7 +826,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def auto_allocate_stock(self, user, **kwargs):
|
def auto_allocate_stock(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Automatically allocate stock items against this build order,
|
Automatically allocate stock items against this build order,
|
||||||
following a number of 'guidelines':
|
following a number of 'guidelines':
|
||||||
@ -847,9 +847,8 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
|
|
||||||
# Get a list of all 'untracked' BOM items
|
# Get a list of all 'untracked' BOM items
|
||||||
for bom_item in self.untracked_bom_items:
|
for bom_item in self.untracked_bom_items:
|
||||||
|
|
||||||
variant_parts = bom_item.sub_part.get_descendants(include_self=False)
|
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)
|
unallocated_quantity = self.unallocated_quantity(bom_item)
|
||||||
|
|
||||||
@ -891,7 +890,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
return 2
|
return 2
|
||||||
else:
|
else:
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
available_stock = sorted(available_stock, key=stock_sort)
|
available_stock = sorted(available_stock, key=stock_sort)
|
||||||
|
|
||||||
if len(available_stock) == 0:
|
if len(available_stock) == 0:
|
||||||
|
@ -746,11 +746,9 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
data = self.validated_data
|
data = self.validated_data
|
||||||
|
|
||||||
request = self.context['request']
|
|
||||||
build = self.context['build']
|
build = self.context['build']
|
||||||
|
|
||||||
build.auto_allocate_stock(
|
build.auto_allocate_stock(
|
||||||
request.user,
|
|
||||||
location=data.get('location', None),
|
location=data.get('location', None),
|
||||||
interchangeable=data['interchangeable'],
|
interchangeable=data['interchangeable'],
|
||||||
substitutes=data['substitutes'],
|
substitutes=data['substitutes'],
|
||||||
|
@ -8,11 +8,11 @@ from django.db.utils import IntegrityError
|
|||||||
from InvenTree import status_codes as status
|
from InvenTree import status_codes as status
|
||||||
|
|
||||||
from build.models import Build, BuildItem, get_next_build_number
|
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
|
from stock.models import StockItem
|
||||||
|
|
||||||
|
|
||||||
class BuildTest(TestCase):
|
class BuildTestBase(TestCase):
|
||||||
"""
|
"""
|
||||||
Run some tests to ensure that the Build model is working properly.
|
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
|
# 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_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)
|
self.stock_3_1 = StockItem.objects.create(part=self.sub_part_3, quantity=1000)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildTest(BuildTestBase):
|
||||||
|
|
||||||
def test_ref_int(self):
|
def test_ref_int(self):
|
||||||
"""
|
"""
|
||||||
Test the "integer reference" field used for natural sorting
|
Test the "integer reference" field used for natural sorting
|
||||||
@ -369,3 +376,108 @@ class BuildTest(TestCase):
|
|||||||
|
|
||||||
for output in outputs:
|
for output in outputs:
|
||||||
self.assertFalse(output.is_building)
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user