Coverage for src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py: 10%

115 statements  

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

1# Generated by Django 3.2 on 2021-05-10 23:11 

2 

3import re 

4 

5from django.db import migrations 

6 

7from stock.status_codes import StockHistoryCode 

8 

9 

10def update_history(apps, schema_editor): 

11 """ 

12 Update each existing StockItemTracking object, 

13 convert the recorded "quantity" to a delta 

14 """ 

15 

16 StockItem = apps.get_model('stock', 'stockitem') 

17 StockItemTracking = apps.get_model('stock', 'stockitemtracking') 

18 StockLocation = apps.get_model('stock', 'stocklocation') 

19 

20 update_count = 0 

21 

22 locations = StockLocation.objects.all() 

23 

24 for location in locations: # pragma: no cover 24 ↛ 28line 24 didn't jump to line 28 because the loop on line 24 never started

25 # Pre-calculate pathstring 

26 # Note we cannot use the 'pathstring' function here as we don't have access to model functions! 

27 

28 path = [location.name] 

29 

30 loc = location 

31 

32 while loc.parent: 

33 loc = loc.parent 

34 path = [loc.name] + path 

35 

36 location._path = '/'.join(path) 

37 

38 for item in StockItem.objects.all(): # pragma: no cover 38 ↛ 40line 38 didn't jump to line 40 because the loop on line 38 never started

39 

40 history = StockItemTracking.objects.filter(item=item).order_by('date') 

41 

42 if history.count() == 0: 

43 continue 

44 

45 quantity = history[0].quantity 

46 

47 for idx, entry in enumerate(history): 

48 

49 deltas = {} 

50 updated = False 

51 

52 q = entry.quantity 

53 

54 if idx == 0 or q != quantity: 

55 

56 try: 

57 deltas['quantity']= float(q) 

58 updated = True 

59 except Exception: 

60 print(f"WARNING: Error converting quantity '{q}'") 

61 

62 

63 quantity = q 

64 

65 # Try to "guess" the "type" of tracking entry, based on the title 

66 title = entry.title.lower() 

67 

68 tracking_type = None 

69 

70 if 'completed build' in title: 

71 tracking_type = StockHistoryCode.BUILD_OUTPUT_COMPLETED 

72 

73 elif 'removed' in title and 'item' in title: 

74 

75 if entry.notes.lower().startswith('split '): 

76 tracking_type = StockHistoryCode.SPLIT_CHILD_ITEM 

77 else: 

78 tracking_type = StockHistoryCode.STOCK_REMOVE 

79 

80 # Extract the number of removed items 

81 result = re.search(r"^removed ([\d\.]+) items", title) 

82 

83 if result: 

84 

85 removed = result.groups()[0] 

86 

87 try: 

88 deltas['removed'] = float(removed) 

89 

90 # Ensure that 'quantity' is stored too in this case 

91 deltas['quantity'] = float(q) 

92 except Exception: 

93 print(f"WARNING: Error converting removed quantity '{removed}'") 

94 else: 

95 print(f"Could not decode '{title}'") 

96 

97 elif 'split from existing' in title: 

98 tracking_type = StockHistoryCode.SPLIT_FROM_PARENT 

99 

100 deltas['quantity'] = float(q) 

101 

102 elif 'moved to' in title: 

103 tracking_type = StockHistoryCode.STOCK_MOVE 

104 

105 result = re.search(r'^Moved to (.*)( - )*(.*) \(from.*$', entry.title) 

106 

107 if result: 

108 # Legacy tracking entries recorded the location in multiple ways, because.. why not? 

109 text = result.groups()[0] 

110 

111 matches = set() 

112 

113 for location in locations: 

114 

115 # Direct match for pathstring 

116 if text == location._path: 

117 matches.add(location) 

118 

119 # Direct match for name 

120 if text == location.name: 

121 matches.add(location) 

122 

123 # Match for "name - description" 

124 compare = f"{location.name} - {location.description}" 

125 

126 if text == compare: 

127 matches.add(location) 

128 

129 # Match for "pathstring - description" 

130 compare = f"{location._path} - {location.description}" 

131 

132 if text == compare: 

133 matches.add(location) 

134 

135 if len(matches) == 1: 

136 location = list(matches)[0] 

137 

138 deltas['location'] = location.pk 

139 

140 else: 

141 print(f"No location match: '{text}'") 

142 break 

143 

144 elif 'created stock item' in title: 

145 tracking_type = StockHistoryCode.CREATED 

146 

147 elif 'add serial number' in title: 

148 tracking_type = StockHistoryCode.ASSIGNED_SERIAL 

149 

150 elif 'returned from customer' in title: 

151 tracking_type = StockHistoryCode.RETURNED_FROM_CUSTOMER 

152 

153 elif 'counted' in title: 

154 tracking_type = StockHistoryCode.STOCK_COUNT 

155 

156 elif 'added' in title: 

157 tracking_type = StockHistoryCode.STOCK_ADD 

158 

159 # Extract the number of added items 

160 result = re.search(r"^added ([\d\.]+) items", title) 

161 

162 if result: 

163 

164 added = result.groups()[0] 

165 

166 try: 

167 deltas['added'] = float(added) 

168 

169 # Ensure that 'quantity' is stored too in this case 

170 deltas['quantity'] = float(q) 

171 except Exception: 

172 print(f"WARNING: Error converting added quantity '{added}'") 

173 

174 else: 

175 print(f"Could not decode '{title}'") 

176 

177 elif 'assigned to customer' in title: 

178 tracking_type = StockHistoryCode.SENT_TO_CUSTOMER 

179 

180 elif 'installed into stock item' in title: 

181 tracking_type = StockHistoryCode.INSTALLED_INTO_ASSEMBLY 

182 

183 elif 'uninstalled into location' in title: 

184 tracking_type = StockHistoryCode.REMOVED_FROM_ASSEMBLY 

185 

186 elif 'installed stock item' in title: 

187 tracking_type = StockHistoryCode.INSTALLED_CHILD_ITEM 

188 

189 elif 'received items' in title: 

190 tracking_type = StockHistoryCode.RECEIVED_AGAINST_PURCHASE_ORDER 

191 

192 if tracking_type is not None: 

193 entry.tracking_type = tracking_type.value 

194 updated = True 

195 

196 if updated: 

197 entry.deltas = deltas 

198 entry.save() 

199 update_count += 1 

200 

201 

202 if update_count > 0: 202 ↛ 203line 202 didn't jump to line 203 because the condition on line 202 was never true

203 print(f"\n==========================\nUpdated {update_count} StockItemHistory entries") # pragma: no cover 

204 

205 

206class Migration(migrations.Migration): 

207 

208 dependencies = [ 

209 ('stock', '0060_auto_20210511_1713'), 

210 ] 

211 

212 operations = [ 

213 migrations.RunPython(update_history, reverse_code=migrations.RunPython.noop) 

214 ]