diff --git a/InvenTree/label/api.py b/InvenTree/label/api.py index 99e51b48b4..0a06e76b9e 100644 --- a/InvenTree/label/api.py +++ b/InvenTree/label/api.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from io import BytesIO +from PIL import Image + from django.utils.translation import ugettext_lazy as _ from django.conf import settings from django.conf.urls import url, include from django.core.exceptions import ValidationError, FieldError -from django.http import HttpResponse +from django.http import HttpResponse, JsonResponse from django_filters.rest_framework import DjangoFilterBackend @@ -14,6 +17,7 @@ from rest_framework import generics, filters from rest_framework.response import Response import InvenTree.helpers +from InvenTree.tasks import offload_task import common.models from plugin.registry import registry @@ -102,6 +106,8 @@ class LabelPrintMixin: label_name = "label.pdf" + label_names = [] + # Merge one or more PDF files into a single download for item in items_to_print: label = self.get_object() @@ -109,6 +115,8 @@ class LabelPrintMixin: label_name = label.generate_filename(request) + label_names.append(label_name) + if debug_mode: outputs.append(label.render_as_string(request)) else: @@ -117,7 +125,46 @@ class LabelPrintMixin: if not label_name.endswith(".pdf"): label_name += ".pdf" - if debug_mode: + if plugin is not None: + """ + Label printing is to be handled by a plugin, + rather than being exported to PDF. + + In this case, we do the following: + + - Individually generate each label, exporting as an image file + - Pass all the images through to the label printing plugin + - Return a JSON response indicating that the printing has been offloaded + + """ + + for output in outputs: + """ + For each output, we generate a temporary image file, + which will then get sent to the printer + """ + + # Generate a png image at 300dpi + (img_data, w, h) = output.get_document().write_png(resolution=300) + + # Construct a BytesIO object, which can be read by pillow + img_bytes = BytesIO(img_data) + + image = Image.open(img_bytes) + + # Offload a background task to print the provided label + offload_task( + 'plugin.events.print_label', + plugin.plugin_slug(), + image + ) + + return JsonResponse({ + 'plugin': plugin.plugin_slug(), + 'labels': label_names, + }) + + elif debug_mode: """ Contatenate all rendered templates into a single HTML string, and return the string as a HTML response. @@ -126,6 +173,7 @@ class LabelPrintMixin: html = "\n".join(outputs) return HttpResponse(html) + else: """ Concatenate all rendered pages into a single PDF object, diff --git a/InvenTree/plugin/builtin/integration/mixins.py b/InvenTree/plugin/builtin/integration/mixins.py index 2d56e61929..2f0cfb02ce 100644 --- a/InvenTree/plugin/builtin/integration/mixins.py +++ b/InvenTree/plugin/builtin/integration/mixins.py @@ -415,15 +415,6 @@ class LabelPrintingMixin: def get_printer_name(self): return self.PRINTER_NAME - def print_labels(self, labels, **kwargs): - """ - Print multiple labels. - Default implementation is to call print_label() for each label, - but it can be overridden if desired. - """ - for label in labels: - self.print_label(label, **kwargs) - def print_label(self, label, **kwargs): """ Callback to print a single label diff --git a/InvenTree/plugin/events.py b/InvenTree/plugin/events.py index 049c8626c5..25a693a4f8 100644 --- a/InvenTree/plugin/events.py +++ b/InvenTree/plugin/events.py @@ -95,7 +95,11 @@ def process_event(plugin_slug, event, *args, **kwargs): logger.info(f"Plugin '{plugin_slug}' is processing triggered event '{event}'") - plugin = registry.plugins[plugin_slug] + plugin = registry.plugins.get(plugin_slug, None) + + if plugin is None: + logger.error(f"Could not find matching plugin for '{plugin_slug}'") + return plugin.process_event(event, *args, **kwargs) @@ -186,3 +190,25 @@ def after_delete(sender, instance, **kwargs): model=sender.__name__, table=table, ) + + +def print_label(plugin_slug, label_image, **kwargs): + """ + Print label with the provided plugin. + + This task is nominally handled by the background worker. + + Arguments: + plugin_slug: The unique slug (key) of the plugin + label_image: A PIL.Image image object to be printed + """ + + logger.info(f"Plugin '{plugin_slug}' is printing a label") + + plugin = registry.plugins.get(plugin_slug, None) + + if plugin is None: + logger.error(f"Could not find matching plugin for '{plugin_slug}'") + return + + plugin.print_label(label_image) diff --git a/InvenTree/templates/js/translated/label.js b/InvenTree/templates/js/translated/label.js index f91d9c2431..93484d48d2 100644 --- a/InvenTree/templates/js/translated/label.js +++ b/InvenTree/templates/js/translated/label.js @@ -14,11 +14,41 @@ */ /* exported + printLabels, printPartLabels, printStockItemLabels, printStockLocationLabels, */ + +/* + * Perform the "print" action. + */ +function printLabels(url, plugin=null) { + + if (plugin) { + // If a plugin is provided, do not redirect the browser. + // Instead, perform an API request and display a message + + url = url + `plugin=${plugin}`; + + inventreeGet(url, {}, { + success: function(response) { + showMessage( + '{% trans "Labels sent to printer" %}', + { + style: 'success', + } + ); + } + }); + } else { + window.location.href = url; + } + +} + + function printStockItemLabels(items) { /** * Print stock item labels for the given stock items @@ -67,11 +97,7 @@ function printStockItemLabels(items) { href += `items[]=${item}&`; }); - if (data.plugin) { - href += `plugin=${data.plugin}`; - } - - window.location.href = href; + printLabels(href, data.plugin); } } ); @@ -80,6 +106,7 @@ function printStockItemLabels(items) { ); } + function printStockLocationLabels(locations) { if (locations.length == 0) { @@ -124,11 +151,7 @@ function printStockLocationLabels(locations) { href += `locations[]=${location}&`; }); - if (data.plugin) { - href += `plugin=${data.plugin}`; - } - - window.location.href = href; + printLabels(href, data.plugin); } } ); @@ -186,11 +209,7 @@ function printPartLabels(parts) { url += `parts[]=${part}&`; }); - if (data.plugin) { - href += `plugin=${data.plugin}`; - } - - window.location.href = url; + printLabels(href, data.plugin); } } ); @@ -234,7 +253,6 @@ function selectLabel(labels, items, options={}) { var plugin_selection = ''; - if (plugins.length > 0) { plugin_selection =`