From ff732d01aa35c8b2b6c52631f188516ceba31b83 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 4 Sep 2026 13:16:40 +1000 Subject: [PATCH] Ignore 'piece_count' if default value (#12781) * Ignore 'piece_count' if default value * Add regression testing --- src/backend/InvenTree/part/models.py | 5 +++ src/backend/InvenTree/part/test_bom_item.py | 50 +++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/backend/InvenTree/part/models.py b/src/backend/InvenTree/part/models.py index 0f713451a7..9e588b5d29 100644 --- a/src/backend/InvenTree/part/models.py +++ b/src/backend/InvenTree/part/models.py @@ -4102,6 +4102,11 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel): if value is None: value = '' + if field == 'piece_count' and value == 1: + # Ignore the piece_count field if it is 1 (default value) + # This is to ensure backwards compatibility with BOM checksums calculated before this field was added + continue + # Normalize decimal values to ensure consistent representation # These values are only included if they are non-zero # This is to provide some backwards compatibility from before these fields were added diff --git a/src/backend/InvenTree/part/test_bom_item.py b/src/backend/InvenTree/part/test_bom_item.py index 3e08bea0a9..98731df81e 100644 --- a/src/backend/InvenTree/part/test_bom_item.py +++ b/src/backend/InvenTree/part/test_bom_item.py @@ -704,3 +704,53 @@ class BomItemTest(TestCase): # piece_count = 100 is a valid value item.piece_count = 100 item.full_clean() # Should not raise + + def test_item_hash_piece_count_default(self): + """Regression test: a default 'piece_count' must not affect the checksum. + + The 'piece_count' field is ignored by get_item_hash() when it holds its + default value (1), so that checksums calculated before this field was + added remain valid. Simulate a "legacy" checksum (calculated without + 'piece_count' in the hashed fields) and confirm it still matches the + checksum calculated by the current code for a default piece_count. + """ + item = BomItem.objects.get(part=100, sub_part=50) + item.piece_count = 1 + item.save() + + current_hash = item.get_item_hash() + + legacy_fields = [f for f in item.hash_fields() if f != 'piece_count'] + + with mock.patch.object(BomItem, 'hash_fields', return_value=legacy_fields): + legacy_hash = item.get_item_hash() + + self.assertEqual(current_hash, legacy_hash) + + # A checksum validated under the "legacy" hashing scheme must still + # validate correctly against the current (piece_count aware) scheme + item.checksum = legacy_hash + item.save() + self.assertTrue(item.is_line_valid) + + def test_item_hash_piece_count_non_default(self): + """A non-default 'piece_count' value must change the checksum hash.""" + item = BomItem.objects.get(part=100, sub_part=50) + item.piece_count = 1 + item.save() + + # Validate the BOM item hash with the default piece_count + item.validate_hash() + self.assertTrue(item.is_line_valid) + + # Changing piece_count away from the default must invalidate the checksum + item.piece_count = 5 + item.save() + self.assertFalse(item.is_line_valid) + + h_non_default = item.get_item_hash() + + item.piece_count = 1 + h_default = item.get_item_hash() + + self.assertNotEqual(h_default, h_non_default)