Skip to content

Commit

Permalink
[ADD] Add printer_zpl2 module
Browse files Browse the repository at this point in the history
  • Loading branch information
Sylvain GARANCHER committed Feb 17, 2017
1 parent f2c45bd commit 31c6dda
Show file tree
Hide file tree
Showing 15 changed files with 1,735 additions and 0 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -41,6 +41,7 @@ install:
- pip install pycups==1.9.66
- pip install PyPDF2==1.18
- pip install requests
- pip install zpl2
- git clone https://github.com/OCA/reporting-engine -b ${VERSION} $HOME/reporting-engine

script:
Expand Down
99 changes: 99 additions & 0 deletions 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
<record model="ir.values" id="wizard_wizard_print_product_label">
<field name="name">Print Product Label</field>
<field name="key">action</field>
<field name="key2">client_action_multi</field>
<field name="model">product.product</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('printer_zpl2.action_wizard_print_record_label_view'))"/>
</record>
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/9.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
<https://github.com/OCA/report-print-send/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 <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* Sylvain Garancher <sylvain.garancher@syleam.fr>

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.
6 changes: 6 additions & 0 deletions printer_zpl2/__init__.py
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import models
from . import wizard
24 changes: 24 additions & 0 deletions printer_zpl2/__openerp__.py
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
'name': 'Printer ZPL II',
'version': '9.0.1.0.0',
'category': 'Printer',
'author': 'SYLEAM, Odoo Community Association (OCA)',
'website': 'http://www.syleam.fr/',
'license': 'AGPL-3',
'external_dependancies': {
'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,
}
6 changes: 6 additions & 0 deletions printer_zpl2/models/__init__.py
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import printing_label_zpl2_component
from . import printing_label_zpl2
188 changes: 188 additions & 0 deletions printer_zpl2/models/printing_label_zpl2.py
@@ -0,0 +1,188 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import time
import datetime
import logging
from openerp import api, exceptions, fields, models
from openerp.tools.translate import _
from openerp.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='With 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

0 comments on commit 31c6dda

Please sign in to comment.