Coverage for src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py: 25%
80 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"""Label printing plugin that provides support for printing using a label printer machine."""
3from typing import cast
5from django.conf import settings
6from django.utils.translation import gettext_lazy as _
8from rest_framework import serializers
10from common.models import InvenTreeUserSetting
11from InvenTree.serializers import DependentField
12from InvenTree.tasks import offload_task
13from machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine
14from plugin import InvenTreePlugin
15from plugin.machine import call_machine_function, registry
16from plugin.mixins import LabelPrintingMixin
17from report.models import LabelTemplate
20def get_machine_and_driver(machine_pk: str):
21 """Get the driver by machine pk and ensure that it is a label printing driver."""
22 machine = registry.get_machine(machine_pk)
24 # machine should be valid due to the machine select field validator
25 if machine is None: # pragma: no cover
26 return None, None
28 if machine.SLUG != 'label-printer': # pragma: no cover
29 return None, None
31 machine = cast(LabelPrinterMachine, machine)
32 driver = machine.driver
34 if driver is None: # pragma: no cover
35 return machine, None
37 return machine, cast(LabelPrinterBaseDriver, driver)
40def get_last_used_printers(user):
41 """Get the last used printers for a specific user."""
42 return [
43 printer
44 for printer in cast(
45 str,
46 InvenTreeUserSetting.get_setting(
47 'LAST_USED_PRINTING_MACHINES', '', user=user
48 ),
49 ).split(',')
50 if printer
51 ]
54class InvenTreeLabelPlugin(LabelPrintingMixin, InvenTreePlugin):
55 """Builtin plugin for machine label printing.
57 This enables machines to print labels.
58 """
60 NAME = 'InvenTreeLabelMachine'
61 TITLE = _('InvenTree machine label printer')
62 DESCRIPTION = _('Provides support for printing using a machine')
63 VERSION = '1.0.0'
64 AUTHOR = _('InvenTree contributors')
66 def print_labels(self, label: LabelTemplate, output, items, request, **kwargs):
67 """Print labels implementation that calls the correct machine driver print_labels method."""
68 machine, driver = get_machine_and_driver(
69 kwargs['printing_options'].get('machine', '')
70 )
72 # the driver and machine should be valid due to the machine select field validator
73 if driver is None or machine is None: # pragma: no cover
74 return None
76 print_kwargs = {
77 **kwargs,
78 'printing_options': kwargs['printing_options'].get('driver_options', {}),
79 }
81 if output:
82 user = output.user
83 elif request:
84 user = request.user
85 else:
86 user = None
88 # Save the current used printer as last used printer
89 # Only the last ten used printers are saved so that this list doesn't grow infinitely
90 if user and user.is_authenticated:
91 last_used_printers = get_last_used_printers(user)
92 machine_pk = str(machine.pk)
93 if machine_pk in last_used_printers:
94 last_used_printers.remove(machine_pk)
95 last_used_printers.insert(0, machine_pk)
96 InvenTreeUserSetting.set_setting(
97 'LAST_USED_PRINTING_MACHINES',
98 ','.join(last_used_printers[:10]),
99 user=user,
100 )
102 offload_task(
103 call_machine_function,
104 machine.pk,
105 'print_labels',
106 label,
107 items,
108 output=output,
109 force_sync=settings.TESTING or driver.USE_BACKGROUND_WORKER,
110 group='plugin',
111 **print_kwargs,
112 )
114 # Inform the user that the process has been offloaded to the printer
115 if output:
116 output.mark_complete()
118 class PrintingOptionsSerializer(serializers.Serializer):
119 """Printing options serializer that adds a machine select and the machines options."""
121 def __init__(self, *args, **kwargs):
122 """Custom __init__ method to dynamically override the machine choices based on the request."""
123 super().__init__(*args, **kwargs)
125 # TODO @matmair Re-enable this when the need is clear
126 # view = kwargs['context']['view']
127 template = None # view.get_object()
128 items_to_print = None # view.get_items()
130 # get all available printers for each driver
131 machines: list[LabelPrinterMachine] = []
132 for driver in cast(
133 list[LabelPrinterBaseDriver], registry.get_drivers('label-printer')
134 ):
135 machines.extend(
136 driver.get_printers(
137 template, items_to_print, request=kwargs['context']['request']
138 )
139 )
141 # sort the last used printers for the user to the top
142 user = kwargs['context']['request'].user
143 last_used_printers = get_last_used_printers(user)[::-1]
144 machines = sorted(
145 machines,
146 key=lambda m: (
147 last_used_printers.index(str(m.pk))
148 if str(m.pk) in last_used_printers
149 else -1
150 ),
151 reverse=True,
152 )
154 choices = [(str(m.pk), self.get_printer_name(m)) for m in machines]
156 # if there are choices available, use the first as default
157 if len(choices) > 0:
158 self.fields['machine'].default = choices[0][0]
160 # add 'last used' flag to the first choice
161 if choices[0][0] in last_used_printers:
162 choices[0] = (
163 choices[0][0],
164 choices[0][1] + ' (' + _('last used') + ')',
165 )
167 self.fields['machine'].choices = choices
169 def get_printer_name(self, machine: LabelPrinterMachine):
170 """Construct the printers name."""
171 name = machine.name
173 if machine.location:
174 name += f' @ {machine.location.name}'
176 return name
178 machine = serializers.ChoiceField(choices=[])
180 driver_options = DependentField(
181 label=_('Options'),
182 depends_on=['machine'],
183 field_serializer='get_driver_options',
184 required=False,
185 )
187 def get_driver_options(self, fields):
188 """Returns the selected machines serializer."""
189 _, driver = get_machine_and_driver(fields['machine'])
191 if driver is None:
192 return None
194 return driver.get_printing_options_serializer(
195 self.context['request'], context=self.context
196 )