Use one-shot tasks for label print batches (#12939)

* Disable retries for label print batch tasks

* Remove label failure retry guard for one-shot jobs
This commit is contained in:
Petr Ledvina
2026-09-28 07:02:24 +10:00
committed by GitHub
parent d3c23f37ca
commit 5d0c4d9646
3 changed files with 33 additions and 53 deletions
+1
View File
@@ -226,6 +226,7 @@ class LabelPrint(GenericAPIView):
output.pk, output.pk,
user.pk if user else None, user.pk if user else None,
plugin.slug, plugin.slug,
retry=False,
options=(plugin_serializer.data if plugin_serializer else {}), options=(plugin_serializer.data if plugin_serializer else {}),
) )
-7
View File
@@ -118,13 +118,6 @@ def print_labels(
) )
return return
if output.errors:
# A failed job may have printed some labels already - do not print them again
logger.info(
'DataOutput %s has already failed - skipping print_labels task', output_id
)
return
# Fetch user information # Fetch user information
user = None user = None
+32 -46
View File
@@ -549,6 +549,38 @@ class LabelTest(InvenTreeAPITestCase):
output = plugin.get_generated_file(**kwargs) output = plugin.get_generated_file(**kwargs)
self.assertEqual(output.name, expected) self.assertEqual(output.name, expected)
def test_print_task_single_shot(self):
"""API print jobs acknowledge failures without retrying the whole label batch."""
from django_q.models import OrmQ
OrmQ.objects.all().delete()
template = LabelTemplate.objects.filter(enabled=True, model_type='part').first()
part = Part.objects.first()
with patch('InvenTree.status.is_worker_running', return_value=True):
response = self.post(
reverse('api-label-print'),
data={'template': template.pk, 'items': [part.pk]},
expected_code=201,
)
task = OrmQ.objects.get()
self.assertEqual(task.func(), 'report.tasks.print_labels')
self.assertTrue(task.q_options().get('ack_failure'))
self.assertEqual(
task.args(),
(
template.pk,
[part.pk],
response.data['pk'],
self.user.pk,
'inventreelabel',
),
)
self.assertEqual(task.kwargs(), {'options': {}})
self.assertFalse(response.data['complete'])
def test_print_failure(self): def test_print_failure(self):
"""Printing failures retain their details and propagate to the caller.""" """Printing failures retain their details and propagate to the caller."""
template = LabelTemplate.objects.filter(enabled=True, model_type='part').first() template = LabelTemplate.objects.filter(enabled=True, model_type='part').first()
@@ -604,35 +636,6 @@ class LabelTest(InvenTreeAPITestCase):
'Error printing labels: printer unavailable', 'Error printing labels: printer unavailable',
) )
if worker:
original = (
report_models.DataOutput.objects
.filter(pk=output.pk)
.values()
.get()
)
with patch.object(
registry, 'get_plugin', return_value=plugin
) as get_plugin:
print_labels(
template.pk,
[part.pk for part in parts],
output.pk,
self.user.pk,
plugin.slug,
options={},
)
get_plugin.assert_not_called()
plugin.print_labels.assert_called_once()
self.assertEqual(
report_models.DataOutput.objects
.filter(pk=output.pk)
.values()
.get(),
original,
)
def test_print_unavailable_plugin(self): def test_print_unavailable_plugin(self):
"""An unavailable worker plugin records a failure instead of hanging.""" """An unavailable worker plugin records a failure instead of hanging."""
template = LabelTemplate.objects.filter(enabled=True, model_type='part').first() template = LabelTemplate.objects.filter(enabled=True, model_type='part').first()
@@ -660,23 +663,6 @@ class LabelTest(InvenTreeAPITestCase):
{'error': "Label printing plugin 'unavailable-label' not found"}, {'error': "Label printing plugin 'unavailable-label' not found"},
) )
original = report_models.DataOutput.objects.filter(pk=output.pk).values().get()
plugin = Mock(spec=['slug', 'print_labels'], slug=output.plugin)
with (
patch.object(registry, 'get_plugin', return_value=plugin) as get_plugin,
patch.object(LabelTemplate, 'print') as print_template,
):
print_labels(
template.pk, [], output.pk, self.user.pk, output.plugin, options={}
)
get_plugin.assert_not_called()
print_template.assert_not_called()
self.assertEqual(
report_models.DataOutput.objects.filter(pk=output.pk).values().get(),
original,
)
def test_print_task_duplicate(self): def test_print_task_duplicate(self):
"""Duplicate tasks skip completed or deleted outputs before plugin lookup.""" """Duplicate tasks skip completed or deleted outputs before plugin lookup."""
template = LabelTemplate.objects.filter(enabled=True, model_type='part').first() template = LabelTemplate.objects.filter(enabled=True, model_type='part').first()