Coverage for src/backend/InvenTree/stock/generators.py: 30%

50 statements  

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

1"""Generator functions for the stock app.""" 

2 

3from inspect import signature 

4from typing import Optional 

5 

6from django.core.exceptions import ValidationError 

7from django.template import Context, Template 

8 

9import common.models 

10import InvenTree.exceptions 

11import InvenTree.helpers 

12 

13 

14def generate_batch_code(**kwargs): 

15 """Generate a default 'batch code' for a new StockItem. 

16 

17 By default, this uses the value of the 'STOCK_BATCH_CODE_TEMPLATE' setting (if configured), 

18 which can be passed through a simple template. 

19 

20 Also, this function is exposed to the ValidationMixin plugin class, 

21 allowing custom plugins to be used to generate new batch code values. 

22 

23 Various kwargs can be passed to the function, which will be passed through to the plugin functions. 

24 """ 

25 # First, check if any plugins can generate batch codes 

26 from plugin import PluginMixinEnum, registry 

27 

28 now = InvenTree.helpers.current_time() 

29 

30 context = { 

31 'date': now, 

32 'year': now.year, 

33 'month': now.month, 

34 'day': now.day, 

35 'hour': now.hour, 

36 'minute': now.minute, 

37 'week': now.isocalendar()[1], 

38 **kwargs, 

39 } 

40 

41 for plugin in registry.with_mixin(PluginMixinEnum.VALIDATION): 41 ↛ 42line 41 didn't jump to line 42 because the loop on line 41 never started

42 generate = getattr(plugin, 'generate_batch_code', None) 

43 

44 if not generate: 

45 continue 

46 

47 # Check if the function signature accepts kwargs 

48 sig = signature(generate) 

49 

50 if 'kwargs' in sig.parameters: 

51 # Pass the kwargs through to the plugin 

52 try: 

53 batch = generate(**context) 

54 except Exception: 

55 InvenTree.exceptions.log_error( 

56 'generate_batch_code', plugin=plugin.slug 

57 ) 

58 continue 

59 else: 

60 # Ignore the kwargs (legacy plugin) 

61 try: 

62 batch = generate() 

63 except Exception: 

64 InvenTree.exceptions.log_error( 

65 'generate_batch_code', plugin=plugin.slug 

66 ) 

67 continue 

68 

69 # Return the first non-null value generated by a plugin 

70 if batch is not None: 

71 return batch 

72 

73 # If we get to this point, no plugin was able to generate a new batch code 

74 batch_template = common.models.InvenTreeSetting.get_setting( 

75 'STOCK_BATCH_CODE_TEMPLATE', '' 

76 ) 

77 

78 return Template(batch_template).render(Context(context)) 

79 

80 

81def generate_serial_number(part=None, quantity=1, **kwargs) -> Optional[str]: 

82 """Generate a default 'serial number' for a new StockItem.""" 

83 quantity = quantity or 1 

84 

85 if part is None: 85 ↛ 89line 85 didn't jump to line 89 because the condition on line 85 was always true

86 # Cannot generate a serial number without a part 

87 return None 

88 

89 try: 

90 quantity = int(quantity) 

91 except Exception: 

92 raise ValidationError({'quantity': 'Invalid quantity value'}) 

93 

94 if quantity < 1: 

95 raise ValidationError({'quantity': 'Quantity must be greater than zero'}) 

96 

97 # If we are here, no plugins were available to generate a serial number 

98 # In this case, we will generate a simple serial number based on the provided part 

99 sn = part.get_latest_serial_number() 

100 

101 serials = [] 

102 

103 # Generate the required quantity of serial numbers 

104 # Note that this call gets passed through to the plugin system 

105 while quantity > 0: 

106 sn = InvenTree.helpers.increment_serial_number(sn, part=part) 

107 

108 # Exit if an empty or duplicated serial is generated 

109 if not sn or sn in serials: 

110 break 

111 

112 serials.append(sn) 

113 quantity -= 1 

114 

115 return ','.join(serials)