Coverage for src/backend/InvenTree/plugin/builtin/barcodes/inventree_barcode.py: 56%
65 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"""The InvenTreeBarcodePlugin validates barcodes generated by InvenTree itself. It can be used as a template for developing third-party barcode plugins.
3The data format is very simple, and maps directly to database objects,
4via the "id" parameter.
6Parsing an InvenTree barcode simply involves validating that the
7references model objects actually exist in the database.
8"""
10import json
11import re
12from typing import cast
14from django.utils.translation import gettext_lazy as _
16import plugin.base.barcodes.helper
17from InvenTree.helpers import hash_barcode
18from InvenTree.models import InvenTreeBarcodeMixin
19from plugin import InvenTreePlugin
20from plugin.mixins import BarcodeMixin, SettingsMixin
23class InvenTreeInternalBarcodePlugin(SettingsMixin, BarcodeMixin, InvenTreePlugin):
24 """Builtin BarcodePlugin for matching and generating internal barcodes."""
26 NAME = 'InvenTreeBarcode'
27 TITLE = _('InvenTree Barcodes')
28 DESCRIPTION = _('Provides native support for barcodes')
29 VERSION = '2.2.0'
30 AUTHOR = _('InvenTree contributors')
32 SETTINGS = {
33 'INTERNAL_BARCODE_FORMAT': {
34 'name': _('Internal Barcode Format'),
35 'description': _('Select an internal barcode format'),
36 'choices': [
37 ('json', _('JSON barcodes (human readable)')),
38 ('short', _('Short barcodes (space optimized)')),
39 ],
40 'default': 'short',
41 },
42 'SHORT_BARCODE_PREFIX': {
43 'name': _('Short Barcode Prefix'),
44 'description': _(
45 'Customize the prefix used for short barcodes, may be useful for environments with multiple InvenTree instances'
46 ),
47 'default': 'INV-',
48 },
49 }
51 def format_matched_response(self, label, model, instance, user, **kwargs):
52 """Format a response for the scanned data."""
53 return {label: instance.format_matched_response(user=user, **kwargs)}
55 def scan(self, barcode_data, user, **kwargs):
56 """Scan a barcode against this plugin.
58 Here we are looking for a dict object which contains a reference to a particular InvenTree database object
59 """
60 # Internal Barcodes - Short Format
61 # Attempt to match the barcode data against the short barcode format
62 prefix = cast(str, self.get_setting('SHORT_BARCODE_PREFIX'))
63 if type(barcode_data) is str and ( 63 ↛ 68line 63 didn't jump to line 68 because the condition on line 63 was never true
64 m := re.match(
65 f'^{re.escape(prefix)}([0-9A-Z $%*+-.\\/:]{"{2}"})(\\d+)$', barcode_data
66 )
67 ):
68 model_type_code, pk = m.groups()
70 supported_models_map = (
71 plugin.base.barcodes.helper.get_supported_barcode_model_codes_map()
72 )
73 model = supported_models_map.get(model_type_code, None)
75 if model is None:
76 return None
78 label = model.barcode_model_type()
80 try:
81 instance = model.objects.get(pk=int(pk))
82 return self.format_matched_response(label, model, instance, user=user)
83 except (ValueError, model.DoesNotExist):
84 pass
86 # Internal Barcodes - JSON Format
87 # Attempt to coerce the barcode data into a dict object
88 # This is the internal JSON barcode representation that InvenTree uses
89 barcode_dict = None
91 if type(barcode_data) is dict: 91 ↛ 92line 91 didn't jump to line 92 because the condition on line 91 was never true
92 barcode_dict = barcode_data
93 elif type(barcode_data) is str: 93 ↛ 99line 93 didn't jump to line 99 because the condition on line 93 was always true
94 try:
95 barcode_dict = json.loads(barcode_data)
96 except json.JSONDecodeError:
97 pass
99 supported_models = plugin.base.barcodes.helper.get_supported_barcode_models()
101 succcess_message = _('Found matching item')
103 if barcode_dict is not None and type(barcode_dict) is dict: 103 ↛ 105line 103 didn't jump to line 105 because the condition on line 103 was never true
104 # Look for various matches. First good match will be returned
105 for model in supported_models:
106 label = model.barcode_model_type()
108 if label in barcode_dict:
109 try:
110 pk = int(barcode_dict[label])
111 instance = model.objects.get(pk=pk)
113 return {
114 **self.format_matched_response(
115 label, model, instance, user=user
116 ),
117 'success': succcess_message,
118 }
119 except (ValueError, model.DoesNotExist):
120 pass
122 # External Barcodes (Linked barcodes)
123 # Create hash from raw barcode data
124 barcode_hash = hash_barcode(barcode_data)
126 # If no "direct" hits are found, look for assigned third-party barcodes
127 for model in supported_models:
128 label = model.barcode_model_type()
130 instance = model.lookup_barcode(barcode_hash)
132 if instance is not None: 132 ↛ 133line 132 didn't jump to line 133 because the condition on line 132 was never true
133 return {
134 **self.format_matched_response(label, model, instance, user=user),
135 'success': succcess_message,
136 }
138 def generate(self, model_instance: InvenTreeBarcodeMixin):
139 """Generate a barcode for a given model instance."""
140 barcode_format = self.get_setting(
141 'INTERNAL_BARCODE_FORMAT', backup_value='json'
142 )
144 if barcode_format == 'short': 144 ↛ 151line 144 didn't jump to line 151 because the condition on line 144 was always true
145 prefix = self.get_setting('SHORT_BARCODE_PREFIX')
146 model_type_code = model_instance.barcode_model_type_code()
148 return f'{prefix}{model_type_code}{model_instance.pk}'
149 else:
150 # Default = JSON format
151 return json.dumps({model_instance.barcode_model_type(): model_instance.pk})