mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-11 01:38:49 +00:00
Control build allocation of optional BOM items (#4090)
* Control build allocation of optional BOM items - User can specify in form whether optional items are allocated - Default = False * Updated unit test
This commit is contained in:
parent
d9e99e38d2
commit
0e8563ebee
@ -832,6 +832,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
exclude_location = kwargs.get('exclude_location', None)
|
exclude_location = kwargs.get('exclude_location', None)
|
||||||
interchangeable = kwargs.get('interchangeable', False)
|
interchangeable = kwargs.get('interchangeable', False)
|
||||||
substitutes = kwargs.get('substitutes', True)
|
substitutes = kwargs.get('substitutes', True)
|
||||||
|
optional_items = kwargs.get('optional_items', False)
|
||||||
|
|
||||||
def stock_sort(item, bom_item, variant_parts):
|
def stock_sort(item, bom_item, variant_parts):
|
||||||
if item.part == bom_item.sub_part:
|
if item.part == bom_item.sub_part:
|
||||||
@ -848,6 +849,10 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
# Do not auto-allocate stock to consumable BOM items
|
# Do not auto-allocate stock to consumable BOM items
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if bom_item.optional and not optional_items:
|
||||||
|
# User has specified that optional_items are to be ignored
|
||||||
|
continue
|
||||||
|
|
||||||
variant_parts = bom_item.sub_part.get_descendants(include_self=False)
|
variant_parts = bom_item.sub_part.get_descendants(include_self=False)
|
||||||
|
|
||||||
unallocated_quantity = self.unallocated_quantity(bom_item)
|
unallocated_quantity = self.unallocated_quantity(bom_item)
|
||||||
|
@ -812,6 +812,7 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
|||||||
'exclude_location',
|
'exclude_location',
|
||||||
'interchangeable',
|
'interchangeable',
|
||||||
'substitutes',
|
'substitutes',
|
||||||
|
'optional_items',
|
||||||
]
|
]
|
||||||
|
|
||||||
location = serializers.PrimaryKeyRelatedField(
|
location = serializers.PrimaryKeyRelatedField(
|
||||||
@ -844,6 +845,12 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
|||||||
help_text=_('Allow allocation of substitute parts'),
|
help_text=_('Allow allocation of substitute parts'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
optional_items = serializers.BooleanField(
|
||||||
|
default=False,
|
||||||
|
label=_('Optional Items'),
|
||||||
|
help_text=_('Allocate optional BOM items to build order'),
|
||||||
|
)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Perform the auto-allocation step"""
|
"""Perform the auto-allocation step"""
|
||||||
data = self.validated_data
|
data = self.validated_data
|
||||||
@ -855,6 +862,7 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
|||||||
exclude_location=data.get('exclude_location', None),
|
exclude_location=data.get('exclude_location', None),
|
||||||
interchangeable=data['interchangeable'],
|
interchangeable=data['interchangeable'],
|
||||||
substitutes=data['substitutes'],
|
substitutes=data['substitutes'],
|
||||||
|
optional_items=data['optional_items'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +82,8 @@ class BuildTestBase(TestCase):
|
|||||||
self.bom_item_2 = BomItem.objects.create(
|
self.bom_item_2 = BomItem.objects.create(
|
||||||
part=self.assembly,
|
part=self.assembly,
|
||||||
sub_part=self.sub_part_2,
|
sub_part=self.sub_part_2,
|
||||||
quantity=3
|
quantity=3,
|
||||||
|
optional=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# sub_part_3 is trackable!
|
# sub_part_3 is trackable!
|
||||||
@ -626,6 +627,7 @@ class AutoAllocationTests(BuildTestBase):
|
|||||||
self.build.auto_allocate_stock(
|
self.build.auto_allocate_stock(
|
||||||
interchangeable=True,
|
interchangeable=True,
|
||||||
substitutes=False,
|
substitutes=False,
|
||||||
|
optional_items=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertFalse(self.build.are_untracked_parts_allocated())
|
self.assertFalse(self.build.are_untracked_parts_allocated())
|
||||||
@ -646,17 +648,18 @@ class AutoAllocationTests(BuildTestBase):
|
|||||||
|
|
||||||
# self.assertEqual(self.build.allocated_stock.count(), 8)
|
# 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_1), 0)
|
||||||
self.assertEqual(self.build.unallocated_quantity(self.bom_item_2), 0)
|
self.assertEqual(self.build.unallocated_quantity(self.bom_item_2), 5.0)
|
||||||
|
|
||||||
self.assertTrue(self.build.is_bom_item_allocated(self.bom_item_1))
|
self.assertTrue(self.build.is_bom_item_allocated(self.bom_item_1))
|
||||||
self.assertTrue(self.build.is_bom_item_allocated(self.bom_item_2))
|
self.assertFalse(self.build.is_bom_item_allocated(self.bom_item_2))
|
||||||
|
|
||||||
def test_fully_auto(self):
|
def test_fully_auto(self):
|
||||||
"""We should be able to auto-allocate against a build in a single go"""
|
"""We should be able to auto-allocate against a build in a single go"""
|
||||||
|
|
||||||
self.build.auto_allocate_stock(
|
self.build.auto_allocate_stock(
|
||||||
interchangeable=True,
|
interchangeable=True,
|
||||||
substitutes=True
|
substitutes=True,
|
||||||
|
optional_items=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(self.build.are_untracked_parts_allocated())
|
self.assertTrue(self.build.are_untracked_parts_allocated())
|
||||||
|
@ -2422,6 +2422,9 @@ function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
|
|||||||
substitutes: {
|
substitutes: {
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
|
optional_items: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructForm(`/api/build/${build_id}/auto-allocate/`, {
|
constructForm(`/api/build/${build_id}/auto-allocate/`, {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user