Coverage for src/backend/InvenTree/stock/migrations/0121_auto_20260526_0849.py: 33%

40 statements  

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

1# Generated by Django 5.2.14 on 2026-05-26 08:49 

2 

3import datetime 

4from tqdm import tqdm 

5 

6from django.db import migrations 

7 

8 

9def set_creation_date(apps, schema_editor): 

10 """Set the creation_date field for existing StockItem entries.""" 

11 

12 StockItem = apps.get_model('stock', 'StockItem') 

13 StockItemTracking = apps.get_model('stock', 'StockItemTracking') 

14 

15 # First pass - find all StockHistoryCode.CREATED entries 

16 # Note: As of 2026-05-26, the relevant status codes were: 

17 # CREATED = 1 

18 

19 creation_entries = StockItemTracking.objects.filter(tracking_type=1) 

20 

21 items_to_update = [] 

22 

23 def process_item(item): 

24 """Process a StockItem entry for update. 

25  

26 As we are iterating over a potentially large number of StockItem entries, 

27 we periodically write updates to the database in batches to avoid memory issues. 

28 """ 

29 nonlocal items_to_update 

30 

31 items_to_update.append(item) 

32 

33 if len(items_to_update) >= 250: 

34 StockItem.objects.bulk_update(items_to_update, ['creation_date']) 

35 items_to_update = [] 

36 

37 if creation_entries.count() > 0: 37 ↛ 38line 37 didn't jump to line 38 because the condition on line 37 was never true

38 progress = tqdm(total=creation_entries.count(), desc='stock.0121: Setting creation_date for StockItem entries') 

39 

40 

41 for entry in creation_entries: 

42 item = StockItem.objects.filter(pk=entry.item_id).first() 

43 if item and item.creation_date is None: 

44 item.creation_date = entry.date 

45 process_item(item) 

46 

47 progress.update(1) 

48 

49 progress.close() 

50 

51 # Next: Find any StockItem entries that have a null creation_date 

52 items = StockItem.objects.filter(creation_date__isnull=True) 

53 

54 if items.count() > 0: 54 ↛ 56line 54 didn't jump to line 56 because the condition on line 54 was never true

55 

56 progress = tqdm(total=items.count(), desc='stock.0121: Setting creation_date for remaining StockItem entries') 

57 

58 for item in items: 

59 # Source the creation_date from the stock item 

60 earliest_entry = StockItemTracking.objects.filter(item_id=item.pk).order_by('date').first() 

61 

62 # Gather potential datetime sources for the creation_date field 

63 utc = datetime.timezone.utc 

64 

65 def make_aware(dt): 

66 """Ensure a datetime is UTC-aware for safe cross-type comparison.""" 

67 return dt.replace(tzinfo=utc) if dt.tzinfo is None else dt.astimezone(utc) 

68 

69 raw_options = [ 

70 item.updated, 

71 datetime.datetime(item.stocktake_date.year, item.stocktake_date.month, item.stocktake_date.day, tzinfo=utc) if item.stocktake_date else None, 

72 earliest_entry.date if earliest_entry else None, 

73 ] 

74 date_options = [make_aware(d) for d in raw_options if d is not None] 

75 

76 if date_options: 

77 item.creation_date = min(date_options) 

78 process_item(item) 

79 

80 progress.update(1) 

81 

82 # Update the remaining items 

83 StockItem.objects.bulk_update(items_to_update, ['creation_date'], batch_size=250) 

84 

85 

86class Migration(migrations.Migration): 

87 """This migration extracts the 'creation_date' field for each StockItem entry. 

88  

89 - If a CREATED entry exists in the StockItemTracking table, this is used as the source for the creation_date field. 

90 - If no CREATED entry exists, the creation_date is sourced from the earliest of the following 

91 - The 'updated' field of the StockItem entry 

92 - The 'stocktake_date' field of the StockItem entry (converted to a datetime) 

93 - The earliest entry in the StockItemTracking table for that item 

94 """ 

95 

96 dependencies = [ 

97 ("stock", "0120_stockitem_creation_date"), 

98 ] 

99 

100 operations = [ 

101 migrations.RunPython(set_creation_date, reverse_code=migrations.RunPython.noop), 

102 ]