Check "trackable" status of part

- Where a BomItem connects a trackable sub_part with a non-trackable part, force the Part to be trackable
This commit is contained in:
Oliver Walters 2020-10-26 08:29:06 +11:00
parent 767ceed698
commit 6aaf178f0b
2 changed files with 56 additions and 2 deletions

View File

@ -16,8 +16,15 @@ class PartConfig(AppConfig):
""" """
self.generate_part_thumbnails() self.generate_part_thumbnails()
self.update_trackable_status()
def generate_part_thumbnails(self): def generate_part_thumbnails(self):
"""
Generate thumbnail images for any Part that does not have one.
This function exists mainly for legacy support,
as any *new* image uploaded will have a thumbnail generated automatically.
"""
from .models import Part from .models import Part
print("InvenTree: Checking Part image thumbnails") print("InvenTree: Checking Part image thumbnails")
@ -37,4 +44,27 @@ class PartConfig(AppConfig):
part.image = None part.image = None
part.save() part.save()
except (OperationalError, ProgrammingError): except (OperationalError, ProgrammingError):
# Exception if the database has not been migrated yet
pass
def update_trackable_status(self):
"""
Check for any instances where a trackable part is used in the BOM
for a non-trackable part.
In such a case, force the top-level part to be trackable too.
"""
from .models import BomItem
try:
items = BomItem.objects.filter(part__trackable=False, sub_part__trackable=True)
for item in items:
print(f"Marking part '{item.part.name}' as trackable")
item.part.trackable = True
item.part.clean()
item.part.save()
except (OperationalError, ProgrammingError):
# Exception if the database has not been migrated yet
pass pass

View File

@ -529,10 +529,24 @@ class Part(MPTTModel):
pass pass
def clean(self): def clean(self):
""" Perform cleaning operations for the Part model """ """
Perform cleaning operations for the Part model
Update trackable status:
If this part is trackable, and it is used in the BOM
for a parent part which is *not* trackable,
then we will force the parent part to be trackable.
"""
super().clean() super().clean()
if self.trackable:
for parent_part in self.used_in.all():
if not parent_part.trackable:
parent_part.trackable = True
parent_part.clean()
parent_part.save()
name = models.CharField(max_length=100, blank=False, name = models.CharField(max_length=100, blank=False,
help_text=_('Part name'), help_text=_('Part name'),
validators=[validators.validate_part_name] validators=[validators.validate_part_name]
@ -1612,12 +1626,15 @@ class BomItem(models.Model):
return self.get_item_hash() == self.checksum return self.get_item_hash() == self.checksum
def clean(self): def clean(self):
""" Check validity of the BomItem model. """
Check validity of the BomItem model.
Performs model checks beyond simple field validation. Performs model checks beyond simple field validation.
- A part cannot refer to itself in its BOM - A part cannot refer to itself in its BOM
- A part cannot refer to a part which refers to it - A part cannot refer to a part which refers to it
- If the "sub_part" is trackable, then the "part" must be trackable too!
""" """
# If the sub_part is 'trackable' then the 'quantity' field must be an integer # If the sub_part is 'trackable' then the 'quantity' field must be an integer
@ -1627,6 +1644,13 @@ class BomItem(models.Model):
raise ValidationError({ raise ValidationError({
"quantity": _("Quantity must be integer value for trackable parts") "quantity": _("Quantity must be integer value for trackable parts")
}) })
# Force the upstream part to be trackable if the sub_part is trackable
if not self.part.trackable:
self.part.trackable = True
self.part.clean()
self.part.save()
except Part.DoesNotExist: except Part.DoesNotExist:
pass pass