mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Add unit tests for the auto_allocate_stock method
This commit is contained in:
		| @@ -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: | ||||
|   | ||||
| @@ -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'], | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user