diff --git a/printer_zpl2/README.rst b/printer_zpl2/README.rst
new file mode 100644
index 00000000000..271aec834c5
--- /dev/null
+++ b/printer_zpl2/README.rst
@@ -0,0 +1,99 @@
+.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+
+=====================
+ZPL II Label printing
+=====================
+
+This module extends the **Report to printer** (``base_report_to_printer``)
+module to add a ZPL II label printing feature.
+
+This module is meant to be used as a base for module development, and does not provide a GUI on its own.
+See below for more details.
+
+Installation
+============
+
+Nothing special, just install the module.
+
+Configuration
+=============
+
+To configure this module, you need to:
+
+#. Go to *Settings > Printing > Labels > ZPL II*
+#. Create new labels
+
+It's also possible to add a label printing wizard on any model by creating a new *ir.values* record.
+For example, to add the printing wizard on the *product.product* model :
+
+.. code-block:: xml
+
+
+ Print Product Label
+ action
+ client_action_multi
+ product.product
+
+
+
+Usage
+=====
+
+To print a label, you need to call use the label printing method from anywhere (other modules, server actions, etc.).
+
+.. code-block:: python
+
+ # Example : Print the label of a product
+ self.env['printing.label.zpl2'].browse(label_id).print_label(
+ self.env['printing.printer'].browse(printer_id),
+ self.env['product.product'].browse(product_id))
+
+You can also use the generic label printing wizard, if added on some models.
+
+.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
+ :alt: Try me on Runbot
+ :target: https://runbot.odoo-community.org/runbot/144/10.0
+
+Known issues / Roadmap
+======================
+
+* Add a button to generate the ir.values for a model
+* Develop a "Designer" view in a separate module, to allow drawing labels with simple mouse clicks/drags
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues
+`_. In case of trouble, please
+check there if your issue has already been reported. If you spotted it first,
+help us smashing it by providing a detailed and welcomed feedback.
+
+Credits
+=======
+
+Images
+------
+
+* Odoo Community Association: `Icon `_.
+
+Contributors
+------------
+
+* Sylvain Garancher
+
+Maintainer
+----------
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+This module is maintained by the OCA.
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+To contribute to this module, please visit https://odoo-community.org.
diff --git a/printer_zpl2/__init__.py b/printer_zpl2/__init__.py
new file mode 100644
index 00000000000..6b40cb02b11
--- /dev/null
+++ b/printer_zpl2/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import models
+from . import wizard
diff --git a/printer_zpl2/__manifest__.py b/printer_zpl2/__manifest__.py
new file mode 100644
index 00000000000..dcf7ec5f459
--- /dev/null
+++ b/printer_zpl2/__manifest__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Printer ZPL II',
+ 'version': '10.0.1.0.0',
+ 'category': 'Printer',
+ 'author': 'SYLEAM, Odoo Community Association (OCA)',
+ 'website': 'http://www.syleam.fr/',
+ 'license': 'AGPL-3',
+ 'external_dependencies': {
+ 'python': ['zpl2'],
+ },
+ 'depends': [
+ 'base_report_to_printer',
+ ],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/printing_label_zpl2.xml',
+ 'wizard/print_record_label.xml',
+ ],
+ 'installable': True,
+}
diff --git a/printer_zpl2/models/__init__.py b/printer_zpl2/models/__init__.py
new file mode 100644
index 00000000000..048ad6e3dc5
--- /dev/null
+++ b/printer_zpl2/models/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import printing_label_zpl2_component
+from . import printing_label_zpl2
diff --git a/printer_zpl2/models/printing_label_zpl2.py b/printer_zpl2/models/printing_label_zpl2.py
new file mode 100644
index 00000000000..76aa22033f2
--- /dev/null
+++ b/printer_zpl2/models/printing_label_zpl2.py
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+import time
+import datetime
+import logging
+from odoo import api, exceptions, fields, models
+from odoo.tools.translate import _
+from odoo.tools.safe_eval import safe_eval
+
+_logger = logging.getLogger(__name__)
+
+try:
+ import zpl2
+except ImportError:
+ _logger.debug('Cannot `import zpl2`.')
+
+
+class PrintingLabelZpl2(models.Model):
+ _name = 'printing.label.zpl2'
+ _description = 'ZPL II Label'
+
+ name = fields.Char(required=True, help='Label Name.')
+ description = fields.Char(help='Long description for this label.')
+ model_id = fields.Many2one(
+ comodel_name='ir.model', string='Model', required=True,
+ help='Model used to print this label.')
+ origin_x = fields.Integer(
+ required=True, default=10,
+ help='Origin point of the contents in the label, X coordinate.')
+ origin_y = fields.Integer(
+ required=True, default=10,
+ help='Origin point of the contents in the label, Y coordinate.')
+ width = fields.Integer(
+ required=True, default=480,
+ help='Width of the label, will be set on the printer before printing.')
+ component_ids = fields.One2many(
+ comodel_name='printing.label.zpl2.component', inverse_name='label_id',
+ string='Label Components',
+ help='Components which will be printed on the label.')
+
+ @api.multi
+ def _generate_zpl2_components_data(
+ self, label_data, record, page_number=1, page_count=1,
+ label_offset_x=0, label_offset_y=0, **extra):
+ self.ensure_one()
+
+ # Add all elements to print in a list of tuples :
+ # [(component, data, offset_x, offset_y)]
+ to_print = []
+ for component in self.component_ids:
+ eval_args = extra
+ eval_args.update({
+ 'object': record,
+ 'page_number': str(page_number + 1),
+ 'page_count': str(page_count),
+ 'time': time,
+ 'datetime': datetime,
+ })
+ data = safe_eval(component.data, eval_args) or ''
+
+ # Generate a list of elements if the component is repeatable
+ for idx in range(
+ component.repeat_offset,
+ component.repeat_offset + component.repeat_count):
+ printed_data = data
+ # Pick the right value if data is a collection
+ if isinstance(data, (list, tuple, set, models.BaseModel)):
+ # If we reached the end of data, quit the loop
+ if idx >= len(data):
+ break
+
+ # Set the real data to display
+ printed_data = data[idx]
+
+ position = idx - component.repeat_offset
+ to_print.append((
+ component, printed_data,
+ label_offset_x + component.repeat_offset_x * position,
+ label_offset_y + component.repeat_offset_y * position,
+ ))
+
+ for (component, data, offset_x, offset_y) in to_print:
+ component_offset_x = component.origin_x + offset_x
+ component_offset_y = component.origin_y + offset_y
+ if component.component_type == 'text':
+ barcode_arguments = dict([
+ (field_name, component[field_name])
+ for field_name in [
+ zpl2.ARG_FONT,
+ zpl2.ARG_ORIENTATION,
+ zpl2.ARG_HEIGHT,
+ zpl2.ARG_WIDTH,
+ zpl2.ARG_REVERSE_PRINT,
+ zpl2.ARG_IN_BLOCK,
+ zpl2.ARG_BLOCK_WIDTH,
+ zpl2.ARG_BLOCK_LINES,
+ zpl2.ARG_BLOCK_SPACES,
+ zpl2.ARG_BLOCK_JUSTIFY,
+ zpl2.ARG_BLOCK_LEFT_MARGIN,
+ ]
+ ])
+ label_data.font_data(
+ component_offset_x, component_offset_y,
+ barcode_arguments, data)
+ elif component.component_type == 'rectangle':
+ label_data.graphic_box(
+ component_offset_x, component_offset_y, {
+ zpl2.ARG_WIDTH: component.width,
+ zpl2.ARG_HEIGHT: component.height,
+ zpl2.ARG_THICKNESS: component.thickness,
+ zpl2.ARG_COLOR: component.color,
+ zpl2.ARG_ROUNDING: component.rounding,
+ })
+ elif component.component_type == 'circle':
+ label_data.graphic_circle(
+ component_offset_x, component_offset_y, {
+ zpl2.ARG_DIAMETER: component.width,
+ zpl2.ARG_THICKNESS: component.thickness,
+ zpl2.ARG_COLOR: component.color,
+ })
+ elif component.component_type == 'sublabel':
+ component_offset_x += component.sublabel_id.origin_x
+ component_offset_y += component.sublabel_id.origin_y
+ component.sublabel_id._generate_zpl2_components_data(
+ label_data, data,
+ label_offset_x=component_offset_x,
+ label_offset_y=component_offset_y)
+ else:
+ barcode_arguments = dict([
+ (field_name, component[field_name])
+ for field_name in [
+ zpl2.ARG_ORIENTATION,
+ zpl2.ARG_CHECK_DIGITS,
+ zpl2.ARG_HEIGHT,
+ zpl2.ARG_INTERPRETATION_LINE,
+ zpl2.ARG_INTERPRETATION_LINE_ABOVE,
+ zpl2.ARG_SECURITY_LEVEL,
+ zpl2.ARG_COLUMNS_COUNT,
+ zpl2.ARG_ROWS_COUNT,
+ zpl2.ARG_TRUNCATE,
+ zpl2.ARG_MODULE_WIDTH,
+ zpl2.ARG_BAR_WIDTH_RATIO,
+ ]
+ ])
+ label_data.barcode_data(
+ component.origin_x + offset_x,
+ component.origin_y + offset_y,
+ component.component_type, barcode_arguments, data)
+
+ @api.multi
+ def _generate_zpl2_data(self, record, page_count=1, **extra):
+ self.ensure_one()
+ label_data = zpl2.Zpl2()
+
+ for page_number in range(page_count):
+ # Initialize printer's configuration
+ label_data.label_start()
+ label_data.print_width(self.width)
+ label_data.label_encoding()
+
+ label_data.label_home(self.origin_x, self.origin_y)
+
+ self._generate_zpl2_components_data(
+ label_data, record, page_number=page_number,
+ page_count=page_count)
+
+ # Restore printer's configuration and end the label
+ label_data.configuration_update(zpl2.CONF_RECALL_LAST_SAVED)
+ label_data.label_end()
+
+ return label_data.output()
+
+ @api.multi
+ def print_label(self, printer, record, page_count=1, **extra):
+ for label in self:
+ if record._name != label.model_id.model:
+ raise exceptions.UserError(
+ _('This label cannot be used on {model}').format(
+ model=record._name))
+
+ # Send the label to printer
+ label_contents = label._generate_zpl2_data(
+ record, page_count=page_count, **extra)
+ printer.print_document(None, label_contents, 'raw')
+
+ return True
diff --git a/printer_zpl2/models/printing_label_zpl2_component.py b/printer_zpl2/models/printing_label_zpl2_component.py
new file mode 100644
index 00000000000..c62cad6bb0d
--- /dev/null
+++ b/printer_zpl2/models/printing_label_zpl2_component.py
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+import logging
+from odoo import fields, models
+
+_logger = logging.getLogger(__name__)
+
+try:
+ import zpl2
+except ImportError:
+ _logger.debug('Cannot `import zpl2`.')
+
+
+class PrintingLabelZpl2Component(models.Model):
+ _name = 'printing.label.zpl2.component'
+ _description = 'ZPL II Label Component'
+ _order = 'sequence'
+
+ label_id = fields.Many2one(
+ comodel_name='printing.label.zpl2', string='Label',
+ required=True, ondelete='cascade', help='Label using this component.')
+ sequence = fields.Integer(help='Order used to print the elements.')
+ name = fields.Char(required=True, help='Name of the component.')
+ origin_x = fields.Integer(
+ required=True, default=10,
+ help='Origin point of the component in the label, X coordinate.')
+ origin_y = fields.Integer(
+ required=True, default=10,
+ help='Origin point of the component in the label, Y coordinate.')
+ component_type = fields.Selection(
+ selection=[
+ ('text', 'Text'),
+ ('rectangle', 'Rectangle / Line'),
+ ('circle', 'Circle'),
+ (zpl2.BARCODE_CODE_11, 'Code 11'),
+ (zpl2.BARCODE_INTERLEAVED_2_OF_5, 'Interleaved 2 of 5'),
+ (zpl2.BARCODE_CODE_39, 'Code 39'),
+ (zpl2.BARCODE_CODE_49, 'Code 49'),
+ (zpl2.BARCODE_PDF417, 'PDF417'),
+ (zpl2.BARCODE_EAN_8, 'EAN-8'),
+ (zpl2.BARCODE_UPC_E, 'UPC-E'),
+ (zpl2.BARCODE_CODE_128, 'Code 128'),
+ (zpl2.BARCODE_EAN_13, 'EAN-13'),
+ ('sublabel', 'Sublabel'),
+ ], string='Type', required=True, default='text', oldname='type',
+ help='Type of content, simple text or barcode.')
+ font = fields.Selection(
+ selection=[
+ (zpl2.FONT_DEFAULT, 'Default'),
+ (zpl2.FONT_9X5, '9x5'),
+ (zpl2.FONT_11X7, '11x7'),
+ (zpl2.FONT_18X10, '18x10'),
+ (zpl2.FONT_28X15, '28x15'),
+ (zpl2.FONT_26X13, '26x13'),
+ (zpl2.FONT_60X40, '60x40'),
+ (zpl2.FONT_21X13, '21x13'),
+ ], required=True, default=zpl2.FONT_DEFAULT,
+ help='Font to use, for text only.')
+ thickness = fields.Integer(help='Thickness of the line to draw.')
+ color = fields.Selection(
+ selection=[
+ (zpl2.COLOR_BLACK, 'Black'),
+ (zpl2.COLOR_WHITE, 'White'),
+ ], default=zpl2.COLOR_BLACK,
+ help='Color of the line to draw.')
+ orientation = fields.Selection(
+ selection=[
+ (zpl2.ORIENTATION_NORMAL, 'Normal'),
+ (zpl2.ORIENTATION_ROTATED, 'Rotated'),
+ (zpl2.ORIENTATION_INVERTED, 'Inverted'),
+ (zpl2.ORIENTATION_BOTTOM_UP, 'Read from Bottom up'),
+ ], required=True, default=zpl2.ORIENTATION_NORMAL,
+ help='Orientation of the barcode.')
+ check_digits = fields.Boolean(
+ help='Check if you want to compute and print the check digit.')
+ height = fields.Integer(
+ help='Height of the printed component. For a text component, height '
+ 'of a single character.')
+ width = fields.Integer(
+ help='Width of the printed component. For a text component, width of '
+ 'a single character.')
+ rounding = fields.Integer(
+ help='Rounding of the printed rectangle corners.')
+ interpretation_line = fields.Boolean(
+ help='Check if you want the interpretation line to be printed.')
+ interpretation_line_above = fields.Boolean(
+ help='Check if you want the interpretation line to be printed above '
+ 'the barcode.')
+ module_width = fields.Integer(
+ default=2, help='Module width for the barcode.')
+ bar_width_ratio = fields.Float(
+ default=3.0, help='Ratio between wide bar and narrow bar.')
+ security_level = fields.Integer(help='Security level for error detection.')
+ columns_count = fields.Integer(help='Number of data columns to encode.')
+ rows_count = fields.Integer(help='Number of rows to encode.')
+ truncate = fields.Boolean(
+ help='Check if you want to truncate the barcode.')
+ data = fields.Char(
+ size=256, default='""', required=True,
+ help='Data to print on this component. Resource values can be '
+ 'inserted with %(object.field_name)s.')
+ sublabel_id = fields.Many2one(
+ comodel_name='printing.label.zpl2', string='Sublabel',
+ help='Another label to include into this one as a component. '
+ 'This allows to define reusable labels parts.')
+ repeat = fields.Boolean(
+ string='Repeatable',
+ help='Check this box to repeat this component on the label.')
+ repeat_offset = fields.Integer(
+ default=0,
+ help='Number of elements to skip when reading a list of elements.')
+ repeat_count = fields.Integer(
+ default=1,
+ help='Maximum count of repeats of the component.')
+ repeat_offset_x = fields.Integer(
+ help='X coordinate offset between each occurence of this component on '
+ 'the label.')
+ repeat_offset_y = fields.Integer(
+ help='Y coordinate offset between each occurence of this component on '
+ 'the label.')
+ reverse_print = fields.Boolean(
+ help='If checked, the data will be printed in the inverse color of '
+ 'the background.')
+ in_block = fields.Boolean(
+ help='If checked, the data will be restrected in a '
+ 'defined block on the label.')
+ block_width = fields.Integer(help='Width of the block.')
+ block_lines = fields.Integer(
+ default=1, help='Maximum number of lines to print in the block.')
+ block_spaces = fields.Integer(
+ help='Number of spaces added between lines in the block.')
+ block_justify = fields.Selection(
+ selection=[
+ (zpl2.JUSTIFY_LEFT, 'Left'),
+ (zpl2.JUSTIFY_CENTER, 'Center'),
+ (zpl2.JUSTIFY_JUSTIFIED, 'Justified'),
+ (zpl2.JUSTIFY_RIGHT, 'Right'),
+ ], string='Justify', required=True, default='L',
+ help='Choose how the text will be justified in the block.')
+ block_left_margin = fields.Integer(
+ string='Left Margin',
+ help='Left margin for the second and other lines in the block.')
diff --git a/printer_zpl2/security/ir.model.access.csv b/printer_zpl2/security/ir.model.access.csv
new file mode 100644
index 00000000000..acd42710c60
--- /dev/null
+++ b/printer_zpl2/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"printing_label_zpl2_user","Printing Label ZPL2 User","model_printing_label_zpl2","base_report_to_printer.printing_group_user",1,0,0,0
+"printing_label_zpl2_manager","Printing Label ZPL2 Manager","model_printing_label_zpl2","base_report_to_printer.printing_group_manager",1,1,1,1
+"printing_label_zpl2_component_user","Printing Label ZPL2 Component User","model_printing_label_zpl2_component","base_report_to_printer.printing_group_user",1,0,0,0
+"printing_label_zpl2_component_manager","Printing Label ZPL2 Component Manager","model_printing_label_zpl2_component","base_report_to_printer.printing_group_manager",1,1,1,1
diff --git a/printer_zpl2/tests/__init__.py b/printer_zpl2/tests/__init__.py
new file mode 100644
index 00000000000..4483773a512
--- /dev/null
+++ b/printer_zpl2/tests/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import test_printing_label_zpl2
+from . import test_wizard_print_record_label
diff --git a/printer_zpl2/tests/test_printing_label_zpl2.py b/printer_zpl2/tests/test_printing_label_zpl2.py
new file mode 100644
index 00000000000..37a88b756ac
--- /dev/null
+++ b/printer_zpl2/tests/test_printing_label_zpl2.py
@@ -0,0 +1,941 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 LasLabs Inc.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import mock
+
+from odoo import exceptions
+from odoo.tests.common import TransactionCase
+
+
+model = 'odoo.addons.base_report_to_printer.models.printing_server'
+
+
+class TestPrintingLabelZpl2(TransactionCase):
+ def setUp(self):
+ super(TestPrintingLabelZpl2, self).setUp()
+ self.Model = self.env['printing.label.zpl2']
+ self.ComponentModel = self.env['printing.label.zpl2.component']
+ self.server = self.env['printing.server'].create({})
+ self.printer = self.env['printing.printer'].create({
+ 'name': 'Printer',
+ 'server_id': self.server.id,
+ 'system_name': 'Sys Name',
+ 'default': True,
+ 'status': 'unknown',
+ 'status_message': 'Msg',
+ 'model': 'res.users',
+ 'location': 'Location',
+ 'uri': 'URI',
+ })
+ self.label_vals = {
+ 'name': 'ZPL II Label',
+ 'model_id': self.env.ref(
+ 'base_report_to_printer.model_printing_printer').id,
+ }
+ self.component_vals = {
+ 'name': 'ZPL II Label Component',
+ }
+
+ def new_label(self, vals=None):
+ values = self.label_vals.copy()
+ if vals is not None:
+ values.update(vals)
+ return self.Model.create(values)
+
+ def new_component(self, vals=None):
+ values = self.component_vals.copy()
+ if vals is not None:
+ values.update(vals)
+ return self.ComponentModel.create(values)
+
+ def test_print_on_bad_model(self):
+ """ Check that printing on the bad model raises an exception """
+ label = self.new_label()
+ with self.assertRaises(exceptions.UserError):
+ label.print_label(self.printer, label)
+
+ @mock.patch('%s.cups' % model)
+ def test_print_empty_label(self, cups):
+ """ Check that printing an empty label works """
+ label = self.new_label()
+ label.print_label(self.printer, self.printer)
+ cups.Connection().printFile.assert_called_once()
+
+ def test_empty_label_contents(self):
+ """ Check contents of an empty label """
+ label = self.new_label()
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ')
+
+ def test_sublabel_label_contents(self):
+ """ Check contents of a sublabel label component """
+ sublabel = self.new_label({
+ 'name': 'Sublabel',
+ })
+ data = 'Some text'
+ self.new_component({
+ 'label_id': sublabel.id,
+ 'data': '"' + data + '"',
+ })
+ label = self.new_label()
+ self.new_component({
+ 'label_id': label.id,
+ 'name': 'Sublabel contents',
+ 'component_type': 'sublabel',
+ 'sublabel_id': sublabel.id,
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Sublabel component position
+ # Position 30x30 because the default values are :
+ # - 10x10 for the sublabel component in the main label
+ # - 10x10 for the sublabel in the sublabel component
+ # - 10x10 for the component in the sublabel
+ '^FO30,30'
+ # Sublabel component format
+ '^A0N,10,10'
+ # Sublabel component contents
+ '^FD{contents}'
+ # Sublabel component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_repeatable_component_label_fixed_contents(self):
+ """ Check contents of a repeatable label component
+
+ Check that a fixed value is repeated each time
+ """
+ label = self.new_label({
+ 'model_id': self.env.ref(
+ 'printer_zpl2.model_printing_label_zpl2').id,
+ })
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'data': '"' + data + '"',
+ 'repeat': True,
+ 'repeat_count': 3,
+ 'repeat_offset_y': 15,
+ })
+ contents = label._generate_zpl2_data(label)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # First component position
+ '^FO10,10'
+ # First component format
+ '^A0N,10,10'
+ # First component contents
+ '^FD{contents}'
+ # First component end
+ '^FS\n'
+ # Second component position
+ '^FO10,25'
+ # Second component format
+ '^A0N,10,10'
+ # Second component contents
+ '^FD{contents}'
+ # Second component end
+ '^FS\n'
+ # Third component position
+ '^FO10,40'
+ # Third component format
+ '^A0N,10,10'
+ # Third component contents
+ '^FD{contents}'
+ # Third component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_repeatable_component_label_iterable_contents(self):
+ """ Check contents of a repeatable label component
+
+ Check that an iterable contents (list, tuple, etc.) is browsed
+ If the repeat_count is higher than the value length, all values are
+ displayed
+ """
+ label = self.new_label({
+ 'model_id': self.env.ref(
+ 'printer_zpl2.model_printing_label_zpl2').id,
+ })
+ data = ['First text', 'Second text', 'Third text']
+ self.new_component({
+ 'label_id': label.id,
+ 'data': str(data),
+ 'repeat': True,
+ 'repeat_offset': 1,
+ 'repeat_count': 3,
+ 'repeat_offset_y': 15,
+ })
+ contents = label._generate_zpl2_data(label)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # First component position
+ '^FO10,10'
+ # First component format
+ '^A0N,10,10'
+ # First component contents
+ '^FD{contents[1]}'
+ # First component end
+ '^FS\n'
+ # Second component position
+ '^FO10,25'
+ # Second component format
+ '^A0N,10,10'
+ # Second component contents
+ '^FD{contents[2]}'
+ # Second component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_repeatable_component_label_iterable_offset(self):
+ """ Check contents of a repeatable label component with an offset
+
+ Check that an iterable contents (list, tuple, etc.) is browsed
+ If the repeat_count is higher than the value length, all values are
+ displayed
+ """
+ label = self.new_label({
+ 'model_id': self.env.ref(
+ 'printer_zpl2.model_printing_label_zpl2').id,
+ })
+ data = ['Text {value}'.format(value=ind) for ind in range(20)]
+ self.new_component({
+ 'label_id': label.id,
+ 'data': str(data),
+ 'repeat': True,
+ 'repeat_offset': 10,
+ 'repeat_count': 3,
+ 'repeat_offset_y': 15,
+ })
+ contents = label._generate_zpl2_data(label)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # First component position
+ '^FO10,10'
+ # First component format
+ '^A0N,10,10'
+ # First component contents
+ '^FD{contents[10]}'
+ # First component end
+ '^FS\n'
+ # Second component position
+ '^FO10,25'
+ # Second component format
+ '^A0N,10,10'
+ # Second component contents
+ '^FD{contents[11]}'
+ # Second component end
+ '^FS\n'
+ # Third component position
+ '^FO10,40'
+ # Third component format
+ '^A0N,10,10'
+ # Third component contents
+ '^FD{contents[12]}'
+ # Third component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_repeatable_sublabel_contents(self):
+ """ Check contents of a repeatable sublabel label component """
+ sublabel = self.new_label({
+ 'name': 'Sublabel',
+ 'model_id': self.env.ref(
+ 'printer_zpl2.model_printing_label_zpl2_component').id,
+ })
+ self.new_component({
+ 'label_id': sublabel.id,
+ 'name': 'Components name',
+ 'data': 'object.name',
+ })
+ self.new_component({
+ 'label_id': sublabel.id,
+ 'name': 'Components data',
+ 'data': 'object.data',
+ 'origin_x': 50,
+ })
+ label = self.new_label({
+ 'model_id': self.env.ref(
+ 'printer_zpl2.model_printing_label_zpl2').id,
+ })
+ self.new_component({
+ 'label_id': label.id,
+ 'name': 'Label name',
+ 'data': 'object.name',
+ })
+ self.new_component({
+ 'label_id': label.id,
+ 'name': 'Label components',
+ 'component_type': 'sublabel',
+ 'origin_x': 15,
+ 'origin_y': 30,
+ 'data': 'object.component_ids',
+ 'sublabel_id': sublabel.id,
+ 'repeat': True,
+ 'repeat_count': 3,
+ 'repeat_offset_y': 15,
+ })
+ contents = label._generate_zpl2_data(label)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Label name component position
+ '^FO10,10'
+ # Label name component format
+ '^A0N,10,10'
+ # Label name component contents
+ '^FD{label.name}'
+ # Label name component end
+ '^FS\n'
+ # First component name component position
+ '^FO35,50'
+ # First component name component format
+ '^A0N,10,10'
+ # First component name component contents
+ '^FD{label.component_ids[0].name}'
+ # First component name component end
+ '^FS\n'
+ # First component data component position
+ '^FO75,50'
+ # First component data component format
+ '^A0N,10,10'
+ # First component data component contents
+ '^FD{label.component_ids[0].data}'
+ # First component data component end
+ '^FS\n'
+ # Second component name component position
+ '^FO35,65'
+ # Second component name component format
+ '^A0N,10,10'
+ # Second component name component contents
+ '^FD{label.component_ids[1].name}'
+ # Second component name component end
+ '^FS\n'
+ # Second component data component position
+ '^FO75,65'
+ # Second component data component format
+ '^A0N,10,10'
+ # Second component data component contents
+ '^FD{label.component_ids[1].data}'
+ # Second component data component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(label=label))
+
+ def test_text_label_contents(self):
+ """ Check contents of a text label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^A0N,10,10'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_reversed_text_label_contents(self):
+ """ Check contents of a text label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'data': '"' + data + '"',
+ 'reverse_print': True,
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^A0N,10,10'
+ # Reverse print argument
+ '^FR'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_block_text_label_contents(self):
+ """ Check contents of a text label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'data': '"' + data + '"',
+ 'in_block': True,
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^A0N,10,10'
+ # Block definition
+ '^FB0,1,0,L,0'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_rectangle_label_contents(self):
+ """ Check contents of a rectangle label """
+ label = self.new_label()
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'rectangle',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^GB1,1,1,B,0'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ')
+
+ def test_circle_label_contents(self):
+ """ Check contents of a circle label """
+ label = self.new_label()
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'circle',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^GC3,2,B'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ')
+
+ def test_code11_barcode_label_contents(self):
+ """ Check contents of a code 11 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'code_11',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B1N,N,0,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_2of5_barcode_label_contents(self):
+ """ Check contents of a interleaved 2 of 5 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'interleaved_2_of_5',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B2N,0,N,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_code39_barcode_label_contents(self):
+ """ Check contents of a code 39 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'code_39',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B3N,N,0,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_code49_barcode_label_contents(self):
+ """ Check contents of a code 49 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'code_49',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B4N,0,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_code49_barcode_label_contents_line(self):
+ """ Check contents of a code 49 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'code_49',
+ 'data': '"' + data + '"',
+ 'interpretation_line': True,
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B4N,0,B'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_code49_barcode_label_contents_with_above(self):
+ """ Check contents of a code 49 barconde label
+ with interpretation line above
+ """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'code_49',
+ 'data': '"' + data + '"',
+ 'interpretation_line': True,
+ 'interpretation_line_above': True,
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B4N,0,A'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_pdf417_barcode_label_contents(self):
+ """ Check contents of a pdf417 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'pdf417',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B7N,0,0,0,0,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_ean8_barcode_label_contents(self):
+ """ Check contents of a ean-8 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'ean-8',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B8N,0,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_upce_barcode_label_contents(self):
+ """ Check contents of a upc-e barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'upc-e',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^B9N,0,N,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_code128_barcode_label_contents(self):
+ """ Check contents of a code 128 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'code_128',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^BCN,0,N,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
+
+ def test_ean13_barcode_label_contents(self):
+ """ Check contents of a ean-13 barcode label """
+ label = self.new_label()
+ data = 'Some text'
+ self.new_component({
+ 'label_id': label.id,
+ 'component_type': 'ean-13',
+ 'data': '"' + data + '"',
+ })
+ contents = label._generate_zpl2_data(self.printer)
+ self.assertEqual(
+ contents,
+ # Label start
+ '^XA\n'
+ # Print width
+ '^PW480\n'
+ # UTF-8 encoding
+ '^CI28\n'
+ # Label position
+ '^LH10,10\n'
+ # Barcode default format
+ '^BY2,3.0'
+ # Component position
+ '^FO10,10'
+ # Component format
+ '^BEN,0,N,N'
+ # Component contents
+ '^FD{contents}'
+ # Component end
+ '^FS\n'
+ # Recall last saved parameters
+ '^JUR\n'
+ # Label end
+ '^XZ'.format(contents=data))
diff --git a/printer_zpl2/tests/test_wizard_print_record_label.py b/printer_zpl2/tests/test_wizard_print_record_label.py
new file mode 100644
index 00000000000..874c7d9c1dc
--- /dev/null
+++ b/printer_zpl2/tests/test_wizard_print_record_label.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 LasLabs Inc.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import mock
+
+from odoo.tests.common import TransactionCase
+
+
+model = 'odoo.addons.base_report_to_printer.models.printing_server'
+
+
+class TestWizardPrintRecordLabel(TransactionCase):
+ def setUp(self):
+ super(TestWizardPrintRecordLabel, self).setUp()
+ self.Model = self.env['wizard.print.record.label']
+ self.server = self.env['printing.server'].create({})
+ self.printer = self.env['printing.printer'].create({
+ 'name': 'Printer',
+ 'server_id': self.server.id,
+ 'system_name': 'Sys Name',
+ 'default': True,
+ 'status': 'unknown',
+ 'status_message': 'Msg',
+ 'model': 'res.users',
+ 'location': 'Location',
+ 'uri': 'URI',
+ })
+ self.label = self.env['printing.label.zpl2'].create({
+ 'name': 'ZPL II Label',
+ 'model_id': self.env.ref(
+ 'base_report_to_printer.model_printing_printer').id,
+ })
+
+ @mock.patch('%s.cups' % model)
+ def test_print_record_label(self, cups):
+ """ Check that printing a label using the generic wizard works """
+ wizard_obj = self.Model.with_context(
+ active_model='printing.printer',
+ active_id=self.printer.id,
+ active_ids=[self.printer.id],
+ )
+ wizard = wizard_obj.create({})
+ self.assertEqual(wizard.printer_id, self.printer)
+ self.assertEqual(wizard.label_id, self.label)
+ wizard.print_label()
+ cups.Connection().printFile.assert_called_once()
+
+ def test_wizard_multiple_printers_and_labels(self):
+ """ Check that printer_id and label_id are not automatically filled
+ when there are multiple possible values
+ """
+ self.env['printing.printer'].create({
+ 'name': 'Other_Printer',
+ 'server_id': self.server.id,
+ 'system_name': 'Sys Name',
+ 'default': True,
+ 'status': 'unknown',
+ 'status_message': 'Msg',
+ 'model': 'res.users',
+ 'location': 'Location',
+ 'uri': 'URI',
+ })
+ self.env['printing.label.zpl2'].create({
+ 'name': 'Other ZPL II Label',
+ 'model_id': self.env.ref(
+ 'base_report_to_printer.model_printing_printer').id,
+ })
+ wizard_obj = self.Model.with_context(
+ active_model='printing.printer',
+ active_id=self.printer.id,
+ active_ids=[self.printer.id],
+ )
+ values = wizard_obj.default_get(['printer_id', 'label_id'])
+ self.assertEqual(values.get('printer_id', False), False)
+ self.assertEqual(values.get('label_id', False), False)
+
+ def test_wizard_multiple_labels_but_on_different_models(self):
+ """ Check that label_id is automatically filled when there are multiple
+ labels, but only one on the right model
+ """
+ self.env['printing.label.zpl2'].create({
+ 'name': 'Other ZPL II Label',
+ 'model_id': self.env.ref('base.model_res_users').id,
+ })
+ wizard_obj = self.Model.with_context(
+ active_model='printing.printer',
+ active_id=self.printer.id,
+ active_ids=[self.printer.id],
+ )
+ wizard = wizard_obj.create({})
+ self.assertEqual(wizard.label_id, self.label)
diff --git a/printer_zpl2/views/printing_label_zpl2.xml b/printer_zpl2/views/printing_label_zpl2.xml
new file mode 100644
index 00000000000..24b98aa32a5
--- /dev/null
+++ b/printer_zpl2/views/printing_label_zpl2.xml
@@ -0,0 +1,142 @@
+
+
+
+
+ printing.label.zpl2.tree
+ printing.label.zpl2
+
+
+
+
+
+
+
+
+ printing.label.zpl2.form
+ printing.label.zpl2
+
+
+
+
+
+ printing.label.zpl2.search
+ printing.label.zpl2
+
+
+
+
+
+
+
+
+ ZPL II Labels
+ ir.actions.act_window
+ printing.label.zpl2
+ form
+ tree,form
+
+ []
+ {}
+
+
+
+
+ form
+
+
+
+
+
+ tree
+
+
+
+
diff --git a/printer_zpl2/wizard/__init__.py b/printer_zpl2/wizard/__init__.py
new file mode 100644
index 00000000000..5c68984b4df
--- /dev/null
+++ b/printer_zpl2/wizard/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import print_record_label
diff --git a/printer_zpl2/wizard/print_record_label.py b/printer_zpl2/wizard/print_record_label.py
new file mode 100644
index 00000000000..c1ad750d61e
--- /dev/null
+++ b/printer_zpl2/wizard/print_record_label.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 SYLEAM ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import models, api, fields
+
+
+class PrintRecordLabel(models.TransientModel):
+ _name = 'wizard.print.record.label'
+ _description = 'Print Record Label'
+
+ printer_id = fields.Many2one(
+ comodel_name='printing.printer', string='Printer', required=True,
+ help='Printer used to print the labels.')
+ label_id = fields.Many2one(
+ comodel_name='printing.label.zpl2', string='Label', required=True,
+ domain=lambda self: [
+ ('model_id.model', '=', self.env.context.get('active_model'))],
+ help='Label to print.')
+
+ @api.model
+ def default_get(self, fields_list):
+ values = super(PrintRecordLabel, self).default_get(fields_list)
+
+ # Automatically select the printer and label, if only one is available
+ printers = self.env['printing.printer'].search([])
+ if len(printers) == 1:
+ values['printer_id'] = printers.id
+
+ labels = self.env['printing.label.zpl2'].search([
+ ('model_id.model', '=', self.env.context.get('active_model')),
+ ])
+ if len(labels) == 1:
+ values['label_id'] = labels.id
+
+ return values
+
+ @api.multi
+ def print_label(self):
+ """ Prints a label per selected record """
+ record_model = self.env.context['active_model']
+ for record_id in self.env.context['active_ids']:
+ record = self.env[record_model].browse(record_id)
+ self.label_id.print_label(self.printer_id, record)
diff --git a/printer_zpl2/wizard/print_record_label.xml b/printer_zpl2/wizard/print_record_label.xml
new file mode 100644
index 00000000000..ed64c9092da
--- /dev/null
+++ b/printer_zpl2/wizard/print_record_label.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ wizard.print.record.label.form
+ wizard.print.record.label
+
+
+
+
+
+ Print Label
+ ir.actions.act_window
+ wizard.print.record.label
+ form
+ form
+ []
+ {}
+ new
+
+
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000000..91db3761ebb
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+zpl2==1.0