Coverage for src/backend/InvenTree/part/migrations/0102_auto_20230314_0112.py: 26%

46 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1# Generated by Django 3.2.18 on 2023-03-14 01:12 

2 

3import hashlib 

4import logging 

5 

6from django.db import migrations 

7from jinja2 import Template 

8 

9from InvenTree.helpers import normalize 

10 

11 

12logger = logging.getLogger('inventree') 

13 

14 

15def update_bom_item(apps, schema_editor): 

16 """Update all existing BomItem instances, and cache the 'validated' field. 

17 

18 The 'validated' field denotes whether this individual BomItem has been validated, 

19 which previously was calculated on the fly (which was very expensive). 

20 """ 

21 

22 BomItem = apps.get_model('part', 'bomitem') 

23 InvenTreeSetting = apps.get_model('common', 'inventreesetting') 

24 

25 n = BomItem.objects.count() 

26 

27 if n > 0: 27 ↛ 29line 27 didn't jump to line 29 because the condition on line 27 was never true

28 

29 for item in BomItem.objects.all(): 

30 """For each item, we need to re-calculate the "checksum", based on the *old* routine. 

31 Note that as we cannot access the ORM models, we have to do this "by hand" 

32 """ 

33 

34 # Construct the 'full_name' for the sub_part (this is no longer required, but *was* required at point of migration) 

35 try: 

36 setting = InvenTreeSetting.objects.get(key='PART_NAME_FORMAT') 

37 full_name_pattern = str(setting.value) 

38 except Exception: 

39 full_name_pattern = "{{ part.IPN if part.IPN }}{{ ' | ' if part.IPN }}{{ part.name }}{{ ' | ' if part.revision }}{{ part.revision if part.revision }}" 

40 

41 template = Template(full_name_pattern) 

42 

43 full_name = template.render({'part': item.sub_part}) 

44 

45 # Calculate the OLD checksum manually for this BomItem 

46 old_hash = hashlib.md5(str(item.pk).encode()) 

47 old_hash.update(str(item.sub_part.pk).encode()) 

48 old_hash.update(str(full_name).encode()) 

49 old_hash.update(str(item.quantity).encode()) 

50 old_hash.update(str(item.note).encode()) 

51 old_hash.update(str(item.reference).encode()) 

52 old_hash.update(str(item.optional).encode()) 

53 old_hash.update(str(item.inherited).encode()) 

54 

55 if item.consumable: 

56 old_hash.update(str(item.consumable).encode()) 

57 

58 if item.allow_variants: 

59 old_hash.update(str(item.allow_variants).encode()) 

60 

61 checksum = str(old_hash.digest()) 

62 

63 # Now, update the 'validated' field based on whether the checksum is 'valid' or not 

64 item.validated = item.checksum == checksum 

65 

66 """Next, we need to update the item with a "new" hash, with the following differences: 

67 - Uses the PK of the 'part', not the BomItem itself, 

68 - Does not use the 'full_name' of the linked 'sub_part' 

69 - Does not use the 'note' field 

70 """ 

71 

72 if item.validated: 

73 

74 new_hash = hashlib.md5(''.encode()) 

75 

76 components = [ 

77 item.part.pk, 

78 item.sub_part.pk, 

79 normalize(item.quantity), 

80 item.reference, 

81 item.optional, 

82 item.inherited, 

83 item.consumable, 

84 item.allow_variants 

85 ] 

86 

87 for component in components: 

88 new_hash.update(str(component).encode()) 

89 

90 item.checksum = str(new_hash.digest()) 

91 

92 item.save() 

93 

94 logger.info(f"Updated 'validated' flag for {n} BomItem objects") 

95 

96 

97class Migration(migrations.Migration): 

98 

99 dependencies = [ 

100 ('part', '0101_bomitem_validated'), 

101 ] 

102 

103 operations = [ 

104 migrations.RunPython( 

105 update_bom_item, 

106 reverse_code=migrations.RunPython.noop 

107 ) 

108 ]