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
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Generator functions for the stock app."""
3from inspect import signature
4from typing import Optional
6from django.core.exceptions import ValidationError
7from django.template import Context, Template
9import common.models
10import InvenTree.exceptions
11import InvenTree.helpers
14def generate_batch_code(**kwargs):
15 """Generate a default 'batch code' for a new StockItem.
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.
20 Also, this function is exposed to the ValidationMixin plugin class,
21 allowing custom plugins to be used to generate new batch code values.
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
28 now = InvenTree.helpers.current_time()
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 }
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)
44 if not generate:
45 continue
47 # Check if the function signature accepts kwargs
48 sig = signature(generate)
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
69 # Return the first non-null value generated by a plugin
70 if batch is not None:
71 return batch
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 )
78 return Template(batch_template).render(Context(context))
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
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
89 try:
90 quantity = int(quantity)
91 except Exception:
92 raise ValidationError({'quantity': 'Invalid quantity value'})
94 if quantity < 1:
95 raise ValidationError({'quantity': 'Quantity must be greater than zero'})
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()
101 serials = []
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)
108 # Exit if an empty or duplicated serial is generated
109 if not sn or sn in serials:
110 break
112 serials.append(sn)
113 quantity -= 1
115 return ','.join(serials)