From 0508c2dcaf1d763890b0852b91821a5d28b57e7b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 5 Sep 2019 13:10:26 +1000 Subject: [PATCH] Use the hash for each line item to calculate the total BOM hash --- InvenTree/part/models.py | 53 +++++++++++++++++++++++++++-------- InvenTree/part/serializers.py | 2 ++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 2eb8f9f842..5627375a48 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -631,24 +631,15 @@ class Part(models.Model): """ Return a checksum hash for the BOM for this part. Used to determine if the BOM has changed (and needs to be signed off!) - For hash is calculated from the following fields of each BOM item: + The hash is calculated by hashing each line item in the BOM. - - Part.full_name (if the part name changes, the BOM checksum is invalidated) - - Quantity - - Reference field - - Note field - returns a string representation of a hash object which can be compared with a stored value """ hash = hashlib.md5(str(self.id).encode()) for item in self.bom_items.all().prefetch_related('sub_part'): - hash.update(str(item.sub_part.id).encode()) - hash.update(str(item.sub_part.full_name).encode()) - hash.update(str(item.quantity).encode()) - hash.update(str(item.note).encode()) - hash.update(str(item.reference).encode()) + hash.update(str(item.get_item_hash()).encode()) return str(hash.digest()) @@ -667,6 +658,10 @@ class Part(models.Model): - Saves the current date and the checking user """ + # Validate each line item too + for item in self.bom_items.all(): + item.validate_hash() + self.bom_checksum = self.get_bom_hash() self.bom_checked_by = user self.bom_checked_date = datetime.now().date() @@ -1157,6 +1152,42 @@ class BomItem(models.Model): checksum = models.CharField(max_length=128, blank=True, help_text='BOM line checksum') + def get_item_hash(self): + """ Calculate the checksum hash of this BOM line item: + + The hash is calculated from the following fields: + + - Part.full_name (if the part name changes, the BOM checksum is invalidated) + - Quantity + - Reference field + - Note field + + """ + + # Seed the hash with the ID of this BOM item + hash = hashlib.md5(str(self.id).encode()) + + # Update the hash based on line information + hash.update(str(self.sub_part.id).encode()) + hash.update(str(self.sub_part.full_name).encode()) + hash.update(str(self.quantity).encode()) + hash.update(str(self.note).encode()) + hash.update(str(self.reference).encode()) + + return str(hash.digest()) + + def validate_hash(self): + """ Mark this item as 'valid' (store the checksum hash) """ + + self.checksum = str(self.get_item_hash()) + self.save() + + @property + def is_line_valid(self): + """ Check if this line item has been validated by the user """ + + return self.get_item_hash() == self.checksum + def clean(self): """ Check validity of the BomItem model. diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 47b34b292f..a8d0df5954 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -131,6 +131,7 @@ class BomItemSerializer(InvenTreeModelSerializer): part_detail = PartBriefSerializer(source='part', many=False, read_only=True) sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True) price_range = serializers.CharField(read_only=True) + validated = serializers.BooleanField(read_only=True, source='is_line_valid') def __init__(self, *args, **kwargs): # part_detail and sub_part_detail serializers are only included if requested. @@ -171,4 +172,5 @@ class BomItemSerializer(InvenTreeModelSerializer): 'price_range', 'overage', 'note', + 'validated', ]