mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-10 14:47:20 +00:00
* Adjust get_bom_hash function: - Optionally skip inclusion of part names - Skip 'piece_count' if default value set * Fall back to legacy comparison * Add per-line-item check * Add regression tests
This commit is contained in:
@@ -2069,11 +2069,15 @@ class Part(
|
|||||||
"""Return the number of part BOMs that this part appears in."""
|
"""Return the number of part BOMs that this part appears in."""
|
||||||
return len(self.get_used_in())
|
return len(self.get_used_in())
|
||||||
|
|
||||||
def get_bom_hash(self) -> str:
|
def get_bom_hash(self, include_part_names: bool = False) -> str:
|
||||||
"""Return a checksum hash for the BOM for this part.
|
"""Return a checksum hash for the BOM for this part.
|
||||||
|
|
||||||
Used to determine if the BOM has changed (and needs to be signed off!)
|
Used to determine if the BOM has changed (and needs to be signed off!)
|
||||||
The hash is calculated by hashing each line item in the BOM. Returns a string representation of a hash object which can be compared with a stored value
|
The hash is calculated by hashing each line item in the BOM. Returns a string representation of a hash object which can be compared with a stored value
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
include_part_names: If True, include the part names in the hash calculation (default = False, legacy support).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
result_hash = hashlib.md5(str(self.id).encode())
|
result_hash = hashlib.md5(str(self.id).encode())
|
||||||
|
|
||||||
@@ -2088,7 +2092,9 @@ class Part(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for item in bom_items:
|
for item in bom_items:
|
||||||
result_hash.update(str(item.get_item_hash()).encode())
|
result_hash.update(
|
||||||
|
str(item.get_item_hash(include_part_names=include_part_names)).encode()
|
||||||
|
)
|
||||||
|
|
||||||
return str(result_hash.digest())
|
return str(result_hash.digest())
|
||||||
|
|
||||||
@@ -2106,7 +2112,11 @@ class Part(
|
|||||||
# If there is no BOM checksum, then the BOM is not valid
|
# If there is no BOM checksum, then the BOM is not valid
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return self.get_bom_hash() == self.bom_checksum
|
# Fallback to legacy BOM hash if the primary calculation does not match
|
||||||
|
return (
|
||||||
|
self.get_bom_hash() == self.bom_checksum
|
||||||
|
or self.get_bom_hash(include_part_names=True) == self.bom_checksum
|
||||||
|
)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def validate_bom(self, user, valid: bool = True):
|
def validate_bom(self, user, valid: bool = True):
|
||||||
@@ -4076,12 +4086,20 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
|||||||
'allow_variants',
|
'allow_variants',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_item_hash(self) -> str:
|
def get_item_hash(self, include_part_names: bool = False) -> str:
|
||||||
"""Calculate the checksum hash of this BOM line item."""
|
"""Calculate the checksum hash of this BOM line item.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
include_part_names: If True, include the names of the parts in the hash calculation (default = False, legacy support)
|
||||||
|
"""
|
||||||
# Seed the hash with the ID of this BOM item
|
# Seed the hash with the ID of this BOM item
|
||||||
result_hash = hashlib.md5(b'')
|
result_hash = hashlib.md5(b'')
|
||||||
|
|
||||||
for field in self.hash_fields():
|
for field in self.hash_fields():
|
||||||
|
# Skip the str representation of the parts unless explicitly requested
|
||||||
|
if not include_part_names and field in ['part', 'sub_part']:
|
||||||
|
continue
|
||||||
|
|
||||||
# Get the value of the field
|
# Get the value of the field
|
||||||
value = getattr(self, field, None)
|
value = getattr(self, field, None)
|
||||||
|
|
||||||
@@ -4132,7 +4150,11 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
|||||||
if len(self.checksum) == 0:
|
if len(self.checksum) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return self.get_item_hash() == self.checksum
|
# Fallback to legacy item hash if the primary calculation does not match
|
||||||
|
return (
|
||||||
|
self.get_item_hash() == self.checksum
|
||||||
|
or self.get_item_hash(include_part_names=True) == self.checksum
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_consumable(self) -> bool:
|
def is_consumable(self) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user