mirror of
https://github.com/inventree/InvenTree.git
synced 2026-04-25 04:23:33 +00:00
[bug] BOM validation fixes (#11783)
* Ensure a new BOM item triggers check_bom_valid * Ensure BOM validation is properly recomputed on a BOM item update * Add unit tests * Tweak unit tests * Update 'clear_bom' method
This commit is contained in:
@@ -2058,8 +2058,13 @@ class Part(
|
||||
|
||||
Note: Does *NOT* delete inherited BOM items!
|
||||
"""
|
||||
import part.tasks as part_tasks
|
||||
|
||||
self.bom_items.all().delete()
|
||||
|
||||
# Offload task to re-validate the BOM for this assembly
|
||||
InvenTree.tasks.offload_task(part_tasks.check_bom_valid, self.pk, group='part')
|
||||
|
||||
def getRequiredParts(self, recursive=False, parts=None):
|
||||
"""Return a list of parts required to make this part (i.e. BOM items).
|
||||
|
||||
@@ -3923,9 +3928,12 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
||||
assemblies = set()
|
||||
|
||||
if db_instance:
|
||||
# Find all assemblies which use this BomItem *after* we save
|
||||
# Find all assemblies which use this BomItem *before* we save
|
||||
assemblies.update(db_instance.get_assemblies())
|
||||
|
||||
# Update the set of assemblies to include those which use this BomItem *after* we save
|
||||
assemblies.update(self.get_assemblies())
|
||||
|
||||
for assembly in assemblies:
|
||||
# Offload task to update the checksum for this assembly
|
||||
InvenTree.tasks.offload_task(
|
||||
@@ -4079,7 +4087,9 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
||||
These fields are used to calculate the checksum hash of this BOM item.
|
||||
"""
|
||||
return [
|
||||
'part',
|
||||
'part_id',
|
||||
'sub_part',
|
||||
'sub_part_id',
|
||||
'quantity',
|
||||
'setup_quantity',
|
||||
|
||||
@@ -390,3 +390,83 @@ class BomItemTest(TestCase):
|
||||
|
||||
# Delete the new BOM item
|
||||
bom_item.delete()
|
||||
|
||||
def test_bom_validated(self):
|
||||
"""Test for caching of 'bom_validated' property."""
|
||||
from part.tasks import validate_bom
|
||||
|
||||
assembly = Part.objects.create(
|
||||
name='Assembly1', description='An assembly part', assembly=True
|
||||
)
|
||||
|
||||
assembly_2 = Part.objects.create(
|
||||
name='Assembly2', description='An assembly part', assembly=True
|
||||
)
|
||||
|
||||
def check(valid: bool = True):
|
||||
"""Helper function to check the BOM for this assembly."""
|
||||
nonlocal assembly
|
||||
assembly.refresh_from_db()
|
||||
self.assertEqual(assembly.bom_validated, valid)
|
||||
|
||||
def validate(valid: bool = True):
|
||||
"""Helper function to validate the BOM for this assembly."""
|
||||
nonlocal assembly
|
||||
validate_bom(assembly.pk, valid)
|
||||
check(valid)
|
||||
|
||||
check(valid=False)
|
||||
validate()
|
||||
|
||||
sub_part_1 = Part.objects.create(
|
||||
name='SubPart1', description='A sub-part', component=True
|
||||
)
|
||||
|
||||
sub_part_2 = Part.objects.create(
|
||||
name='SubPart2', description='A sub-part', component=True
|
||||
)
|
||||
|
||||
# Still valid at this stage - we have not made any changes to the BOM
|
||||
check(valid=True)
|
||||
|
||||
# Creating a *new* BOM item should invalidate the bom_validated cache
|
||||
bom_item = BomItem.objects.create(
|
||||
part=assembly, sub_part=sub_part_1, quantity=1
|
||||
)
|
||||
|
||||
check(valid=False)
|
||||
|
||||
# Editing the BOM item should also invalidate the bom_validated cache
|
||||
validate()
|
||||
bom_item.quantity = 2
|
||||
bom_item.save()
|
||||
check(valid=False)
|
||||
|
||||
# Editing the BOM item without changing any relevant fields should not invalidate the bom_validated cache
|
||||
validate()
|
||||
bom_item.description = 'This is a description'
|
||||
bom_item.save()
|
||||
check(valid=True)
|
||||
|
||||
# Point the BOM item to a different component
|
||||
validate()
|
||||
bom_item.sub_part = sub_part_2
|
||||
bom_item.save()
|
||||
check(valid=False)
|
||||
|
||||
# Point the BOM to a different assembly
|
||||
validate()
|
||||
bom_item.part = assembly_2
|
||||
bom_item.save()
|
||||
check(valid=False)
|
||||
|
||||
# Check a partial restore - returning to previous state should re-validate
|
||||
bom_item.part = assembly
|
||||
bom_item.save()
|
||||
check(valid=True)
|
||||
|
||||
# Now, delete the BomItem entirely
|
||||
bom_item.delete()
|
||||
check(valid=False)
|
||||
|
||||
self.assertIsNotNone(assembly.bom_checked_date)
|
||||
|
||||
Reference in New Issue
Block a user