mirror of
https://github.com/OCA/report-print-send.git
synced 2025-02-16 07:11:31 +02:00
[MIG] printer_zpl2 to v13 (continue)
This commit is contained in:
@@ -1,29 +1,10 @@
|
||||
==============
|
||||
Printer ZPL II
|
||||
==============
|
||||
.. 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
|
||||
|
||||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freport--print--send-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/report-print-send/tree/13.0/printer_zpl2
|
||||
:alt: OCA/report-print-send
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/report-print-send-13-0/report-print-send-13-0-printer_zpl2
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/144/13.0
|
||||
:alt: Try me on Runbot
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
=====================
|
||||
ZPL II Label printing
|
||||
=====================
|
||||
|
||||
This module extends the **Report to printer** (``base_report_to_printer``)
|
||||
module to add a ZPL II label printing feature.
|
||||
@@ -31,11 +12,6 @@ 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.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
@@ -79,59 +55,45 @@ You can also use the generic label printing wizard, if added on some models.
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/144/12.0
|
||||
|
||||
Changelog
|
||||
=========
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
13.0.1.0.0 (2019-09-30)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [RELEASE] Port from V12.
|
||||
* Selection lists do not support integers any longer
|
||||
* Binary field now returns False when empty instead of none,
|
||||
change tests to reflect this
|
||||
* work around an appels vs oranges warning
|
||||
* 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 <https://github.com/OCA/report-print-send/issues/new?body=module:%20printer_zpl2%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
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
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
Images
|
||||
------
|
||||
|
||||
* SUBTENO-IT
|
||||
* FLorent de Labarre
|
||||
* Apertoso NV
|
||||
* 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>
|
||||
* Florent de Labarre
|
||||
* Jos De Graeve <Jos.DeGraeve@apertoso.be>
|
||||
* Rod Schouteden <rod.schouteden@dynapps.be>
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module is maintained by the OCA.
|
||||
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.
|
||||
|
||||
This module is part of the `OCA/report-print-send <https://github.com/OCA/report-print-send/tree/13.0/printer_zpl2>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
||||
|
||||
@@ -68,7 +68,7 @@ class PrintingLabelZpl2(models.Model):
|
||||
test_print_mode = fields.Boolean(string="Mode Print")
|
||||
test_labelary_mode = fields.Boolean(string="Mode Labelary")
|
||||
record_id = fields.Integer(string="Record ID", default=1)
|
||||
extra = fields.Text(default="{}")
|
||||
extra = fields.Text(string="Extra", default="{}")
|
||||
printer_id = fields.Many2one(comodel_name="printing.printer", string="Printer")
|
||||
labelary_image = fields.Binary(string="Image from Labelary", readonly=True)
|
||||
labelary_dpmm = fields.Selection(
|
||||
@@ -84,172 +84,68 @@ class PrintingLabelZpl2(models.Model):
|
||||
)
|
||||
labelary_width = fields.Float(string="Width in mm", default=140)
|
||||
labelary_height = fields.Float(string="Height in mm", default=70)
|
||||
data_type = fields.Selection(
|
||||
string="Labels components data type",
|
||||
selection=[("und", "Undefined")],
|
||||
help="This allows to specify the type of the data encoded in label components",
|
||||
)
|
||||
|
||||
def _generate_zpl2_components_data_repeatable(
|
||||
self, component, data, label_offset_x, label_offset_y
|
||||
@api.model
|
||||
def _get_component_data(self, component, eval_args):
|
||||
return safe_eval(component.data, eval_args) or ""
|
||||
|
||||
def _get_to_data_to_print(
|
||||
self,
|
||||
record,
|
||||
page_number=1,
|
||||
page_count=1,
|
||||
label_offset_x=0,
|
||||
label_offset_y=0,
|
||||
**extra
|
||||
):
|
||||
to_print = []
|
||||
# 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 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 = self._get_component_data(component, eval_args)
|
||||
if data == "component_not_show":
|
||||
continue
|
||||
|
||||
# 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,
|
||||
)
|
||||
)
|
||||
return to_print
|
||||
|
||||
def _component_type_selector(
|
||||
self,
|
||||
component,
|
||||
label_data,
|
||||
component_offset_x,
|
||||
component_offset_y,
|
||||
offset_x,
|
||||
offset_y,
|
||||
data,
|
||||
):
|
||||
if component.component_type == "text":
|
||||
barcode_arguments = {
|
||||
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 == "zpl2_raw":
|
||||
label_data._write_command(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 == "diagonal":
|
||||
label_data.graphic_diagonal_line(
|
||||
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_DIAGONAL_ORIENTATION: component.diagonal_orientation,
|
||||
},
|
||||
)
|
||||
elif component.component_type == "graphic":
|
||||
# During the on_change don't take the bin_size
|
||||
image = (
|
||||
component.with_context(bin_size_graphic_image=False).graphic_image
|
||||
or data
|
||||
)
|
||||
try:
|
||||
pil_image = Image.open(io.BytesIO(base64.b64decode(image))).convert(
|
||||
"RGB"
|
||||
)
|
||||
except Exception:
|
||||
return
|
||||
if component.width and component.height:
|
||||
pil_image = pil_image.resize((component.width, component.height))
|
||||
|
||||
# Invert the colors
|
||||
if component.reverse_print:
|
||||
pil_image = ImageOps.invert(pil_image)
|
||||
|
||||
# Rotation (PIL rotates counter clockwise)
|
||||
if component.orientation == zpl2.ORIENTATION_ROTATED:
|
||||
pil_image = pil_image.transpose(Image.ROTATE_270)
|
||||
elif component.orientation == zpl2.ORIENTATION_INVERTED:
|
||||
pil_image = pil_image.transpose(Image.ROTATE_180)
|
||||
elif component.orientation == zpl2.ORIENTATION_BOTTOM_UP:
|
||||
pil_image = pil_image.transpose(Image.ROTATE_90)
|
||||
|
||||
label_data.graphic_field(component_offset_x, component_offset_y, pil_image)
|
||||
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:
|
||||
if component.component_type == zpl2.BARCODE_QR_CODE:
|
||||
# Adding Control Arguments to QRCode data Label
|
||||
data = "{}A,{}".format(component.error_correction, data)
|
||||
|
||||
barcode_arguments = {
|
||||
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,
|
||||
zpl2.ARG_MODEL,
|
||||
zpl2.ARG_MAGNIFICATION_FACTOR,
|
||||
zpl2.ARG_ERROR_CORRECTION,
|
||||
zpl2.ARG_MASK_VALUE,
|
||||
]
|
||||
}
|
||||
label_data.barcode_data(
|
||||
component.origin_x + offset_x,
|
||||
component.origin_y + offset_y,
|
||||
component.component_type,
|
||||
barcode_arguments,
|
||||
data,
|
||||
)
|
||||
|
||||
# flake8: noqa: C901
|
||||
def _generate_zpl2_components_data(
|
||||
self,
|
||||
label_data,
|
||||
@@ -264,38 +160,140 @@ class PrintingLabelZpl2(models.Model):
|
||||
|
||||
# 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 ""
|
||||
|
||||
if isinstance(data, str) and data == "component_not_show":
|
||||
continue
|
||||
to_print = self._generate_zpl2_components_data_repeatable(
|
||||
component, data, label_offset_x, label_offset_y
|
||||
)
|
||||
to_print = self._get_to_data_to_print(
|
||||
record, page_number, page_count, label_offset_x, label_offset_y, **extra
|
||||
)
|
||||
|
||||
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
|
||||
self._component_type_selector(
|
||||
component,
|
||||
label_data,
|
||||
component_offset_x,
|
||||
component_offset_y,
|
||||
offset_x,
|
||||
offset_y,
|
||||
data,
|
||||
)
|
||||
if component.component_type == "text":
|
||||
barcode_arguments = {
|
||||
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 == "zpl2_raw":
|
||||
label_data._write_command(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 == "diagonal":
|
||||
label_data.graphic_diagonal_line(
|
||||
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_DIAGONAL_ORIENTATION: component.diagonal_orientation,
|
||||
},
|
||||
)
|
||||
elif component.component_type == "graphic":
|
||||
# During the on_change don't take the bin_size
|
||||
image = (
|
||||
component.with_context(bin_size_graphic_image=False).graphic_image
|
||||
or data
|
||||
)
|
||||
try:
|
||||
pil_image = Image.open(io.BytesIO(base64.b64decode(image))).convert(
|
||||
"RGB"
|
||||
)
|
||||
except Exception:
|
||||
continue
|
||||
if component.width and component.height:
|
||||
pil_image = pil_image.resize((component.width, component.height))
|
||||
|
||||
# Invert the colors
|
||||
if component.reverse_print:
|
||||
pil_image = ImageOps.invert(pil_image)
|
||||
|
||||
# Rotation (PIL rotates counter clockwise)
|
||||
if component.orientation == zpl2.ORIENTATION_ROTATED:
|
||||
pil_image = pil_image.transpose(Image.ROTATE_270)
|
||||
elif component.orientation == zpl2.ORIENTATION_INVERTED:
|
||||
pil_image = pil_image.transpose(Image.ROTATE_180)
|
||||
elif component.orientation == zpl2.ORIENTATION_BOTTOM_UP:
|
||||
pil_image = pil_image.transpose(Image.ROTATE_90)
|
||||
|
||||
label_data.graphic_field(
|
||||
component_offset_x, component_offset_y, pil_image
|
||||
)
|
||||
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:
|
||||
if component.component_type == zpl2.BARCODE_QR_CODE:
|
||||
# Adding Control Arguments to QRCode data Label
|
||||
data = "{}A,{}".format(component.error_correction, data)
|
||||
|
||||
barcode_arguments = {
|
||||
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,
|
||||
zpl2.ARG_MODEL,
|
||||
zpl2.ARG_MAGNIFICATION_FACTOR,
|
||||
zpl2.ARG_ERROR_CORRECTION,
|
||||
zpl2.ARG_MASK_VALUE,
|
||||
]
|
||||
}
|
||||
label_data.barcode_data(
|
||||
component.origin_x + offset_x,
|
||||
component.origin_y + offset_y,
|
||||
component.component_type,
|
||||
barcode_arguments,
|
||||
data,
|
||||
)
|
||||
|
||||
def _generate_zpl2_data(self, record, page_count=1, **extra):
|
||||
self.ensure_one()
|
||||
@@ -326,20 +324,39 @@ class PrintingLabelZpl2(models.Model):
|
||||
|
||||
return label_data.output()
|
||||
|
||||
def fill_component(self, line):
|
||||
for component in self.component_ids:
|
||||
json = {
|
||||
"product_barcode": line.product_barcode,
|
||||
"lot_barcode": line.lot_barcode,
|
||||
"uom": str(line.product_qty) + " " + line.product_id.uom_id.name,
|
||||
"package_barcode": line.package_barcode,
|
||||
"product_qty": line.product_qty,
|
||||
}
|
||||
component.data = json
|
||||
|
||||
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(
|
||||
report=None, content=label_contents, doc_format="raw"
|
||||
)
|
||||
if label.data_type == "und":
|
||||
for component in self.component_ids:
|
||||
eval_args = extra
|
||||
eval_args.update(
|
||||
{"object": record, "time": time, "datetime": datetime}
|
||||
)
|
||||
data = safe_eval(component.data, eval_args) or ""
|
||||
if data == "component_not_show":
|
||||
continue
|
||||
# Send the label to printer
|
||||
label_contents = label._generate_zpl2_data(
|
||||
record, page_count=1, **extra
|
||||
)
|
||||
printer.print_document(
|
||||
report=None, content=label_contents, doc_format="raw"
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -364,6 +381,7 @@ class PrintingLabelZpl2(models.Model):
|
||||
def import_zpl2(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"res_model": "wizard.import.zpl2",
|
||||
"type": "ir.actions.act_window",
|
||||
|
||||
@@ -56,16 +56,16 @@ class PrintingLabelZpl2Component(models.Model):
|
||||
("diagonal", "Diagonal Line"),
|
||||
("circle", "Circle"),
|
||||
("graphic", "Graphic"),
|
||||
(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"),
|
||||
(zpl2.BARCODE_QR_CODE, "QR Code"),
|
||||
(str(zpl2.BARCODE_CODE_11), "Code 11"),
|
||||
(str(zpl2.BARCODE_INTERLEAVED_2_OF_5), "Interleaved 2 of 5"),
|
||||
(str(zpl2.BARCODE_CODE_39), "Code 39"),
|
||||
(str(zpl2.BARCODE_CODE_49), "Code 49"),
|
||||
(str(zpl2.BARCODE_PDF417), "PDF417"),
|
||||
(str(zpl2.BARCODE_EAN_8), "EAN-8"),
|
||||
(str(zpl2.BARCODE_UPC_E), "UPC-E"),
|
||||
(str(zpl2.BARCODE_CODE_128), "Code 128"),
|
||||
(str(zpl2.BARCODE_EAN_13), "EAN-13"),
|
||||
(str(zpl2.BARCODE_QR_CODE), "QR Code"),
|
||||
("sublabel", "Sublabel"),
|
||||
("zpl2_raw", "ZPL2"),
|
||||
],
|
||||
@@ -76,42 +76,42 @@ class PrintingLabelZpl2Component(models.Model):
|
||||
)
|
||||
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"),
|
||||
(str(zpl2.FONT_DEFAULT), "Default"),
|
||||
(str(zpl2.FONT_9X5), "9x5"),
|
||||
(str(zpl2.FONT_11X7), "11x7"),
|
||||
(str(zpl2.FONT_18X10), "18x10"),
|
||||
(str(zpl2.FONT_28X15), "28x15"),
|
||||
(str(zpl2.FONT_26X13), "26x13"),
|
||||
(str(zpl2.FONT_60X40), "60x40"),
|
||||
(str(zpl2.FONT_21X13), "21x13"),
|
||||
],
|
||||
required=True,
|
||||
default=zpl2.FONT_DEFAULT,
|
||||
default=str(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,
|
||||
selection=[(str(zpl2.COLOR_BLACK), "Black"), (str(zpl2.COLOR_WHITE), "White")],
|
||||
default=str(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"),
|
||||
(str(zpl2.ORIENTATION_NORMAL), "Normal"),
|
||||
(str(zpl2.ORIENTATION_ROTATED), "Rotated"),
|
||||
(str(zpl2.ORIENTATION_INVERTED), "Inverted"),
|
||||
(str(zpl2.ORIENTATION_BOTTOM_UP), "Read from Bottom up"),
|
||||
],
|
||||
required=True,
|
||||
default=zpl2.ORIENTATION_NORMAL,
|
||||
default=str(zpl2.ORIENTATION_NORMAL),
|
||||
help="Orientation of the barcode.",
|
||||
)
|
||||
diagonal_orientation = fields.Selection(
|
||||
selection=[
|
||||
(zpl2.DIAGONAL_ORIENTATION_LEFT, "Left (\\)"),
|
||||
(zpl2.DIAGONAL_ORIENTATION_RIGHT, "Right (/)"),
|
||||
(str(zpl2.DIAGONAL_ORIENTATION_LEFT), "Left (\\)"),
|
||||
(str(zpl2.DIAGONAL_ORIENTATION_RIGHT), "Right (/)"),
|
||||
],
|
||||
default=zpl2.DIAGONAL_ORIENTATION_LEFT,
|
||||
default=str(zpl2.DIAGONAL_ORIENTATION_LEFT),
|
||||
help="Orientation of the diagonal line.",
|
||||
)
|
||||
check_digits = fields.Boolean(
|
||||
@@ -152,15 +152,16 @@ class PrintingLabelZpl2Component(models.Model):
|
||||
magnification_factor = fields.Integer(
|
||||
default=1, help="Magnification Factor, from 1 to 10."
|
||||
)
|
||||
only_product_barcode = fields.Boolean("Only product barcode data")
|
||||
error_correction = fields.Selection(
|
||||
selection=[
|
||||
(zpl2.ERROR_CORRECTION_ULTRA_HIGH, "Ultra-high Reliability Level"),
|
||||
(zpl2.ERROR_CORRECTION_HIGH, "High Reliability Level"),
|
||||
(zpl2.ERROR_CORRECTION_STANDARD, "Standard Level"),
|
||||
(zpl2.ERROR_CORRECTION_HIGH_DENSITY, "High Density Level"),
|
||||
(str(zpl2.ERROR_CORRECTION_ULTRA_HIGH), "Ultra-high Reliability Level"),
|
||||
(str(zpl2.ERROR_CORRECTION_HIGH), "High Reliability Level"),
|
||||
(str(zpl2.ERROR_CORRECTION_STANDARD), "Standard Level"),
|
||||
(str(zpl2.ERROR_CORRECTION_HIGH_DENSITY), "High Density Level"),
|
||||
],
|
||||
required=True,
|
||||
default=zpl2.ERROR_CORRECTION_HIGH,
|
||||
default=str(zpl2.ERROR_CORRECTION_HIGH),
|
||||
help="Error correction for some barcode types like QR Code.",
|
||||
)
|
||||
mask_value = fields.Integer(default=7, help="Mask Value, from 0 to 7.")
|
||||
@@ -211,10 +212,10 @@ class PrintingLabelZpl2Component(models.Model):
|
||||
)
|
||||
block_justify = fields.Selection(
|
||||
selection=[
|
||||
(zpl2.JUSTIFY_LEFT, "Left"),
|
||||
(zpl2.JUSTIFY_CENTER, "Center"),
|
||||
(zpl2.JUSTIFY_JUSTIFIED, "Justified"),
|
||||
(zpl2.JUSTIFY_RIGHT, "Right"),
|
||||
(str(zpl2.JUSTIFY_LEFT), "Left"),
|
||||
(str(zpl2.JUSTIFY_CENTER), "Center"),
|
||||
(str(zpl2.JUSTIFY_JUSTIFIED), "Justified"),
|
||||
(str(zpl2.JUSTIFY_RIGHT), "Right"),
|
||||
],
|
||||
string="Justify",
|
||||
required=True,
|
||||
|
||||
@@ -67,8 +67,11 @@ class TestPrintingLabelZpl2(TransactionCase):
|
||||
def test_print_empty_label(self, cups):
|
||||
""" Check that printing an empty label works """
|
||||
label = self.new_label()
|
||||
file_name = 'test.zpl'
|
||||
label.print_label(self.printer, self.printer)
|
||||
cups.Connection().printFile.assert_called_once()
|
||||
cups.Connection().printFile.assert_called_once_with(
|
||||
printer.system_name, file_name, file_name, options={}
|
||||
)
|
||||
|
||||
def test_empty_label_contents(self):
|
||||
""" Check contents of an empty label """
|
||||
|
||||
@@ -51,8 +51,11 @@ class TestWizardPrintRecordLabel(TransactionCase):
|
||||
self.label.test_print_mode = True
|
||||
self.label.printer_id = self.printer
|
||||
self.label.record_id = 10
|
||||
file_name = 'test.zpl'
|
||||
self.label.print_test_label()
|
||||
cups.Connection().printFile.assert_called_once()
|
||||
cups.Connection().printFile.assert_called_once_with(
|
||||
self.printer.system_name, file_name, file_name, options={}
|
||||
)
|
||||
|
||||
def test_emulation_without_params(self):
|
||||
""" Check if not execute next if not in this mode """
|
||||
|
||||
@@ -48,7 +48,10 @@ class TestWizardPrintRecordLabel(TransactionCase):
|
||||
self.assertEqual(wizard.printer_id, self.printer)
|
||||
self.assertEqual(wizard.label_id, self.label)
|
||||
wizard.print_label()
|
||||
cups.Connection().printFile.assert_called_once()
|
||||
file_name = 'test.zpl'
|
||||
cups.Connection().printFile.assert_called_once_with(
|
||||
self.printer.system_name, file_name, file_name, options={}
|
||||
)
|
||||
|
||||
def test_wizard_multiple_printers_and_labels(self):
|
||||
""" Check that printer_id and label_id are not automatically filled
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<record id="view_printing_label_zpl2_tree" model="ir.ui.view">
|
||||
<field name="model">printing.label.zpl2</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<tree string="ZPL II Label">
|
||||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
</tree>
|
||||
@@ -21,13 +21,17 @@
|
||||
<button name="import_zpl2" string="Import ZPL2" type="object"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<widget
|
||||
name="web_ribbon"
|
||||
title="Archived"
|
||||
bg_color="bg-danger"
|
||||
attrs="{'invisible': [('active', '=', True)]}"
|
||||
/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="action_window_id" invisible="1"/>
|
||||
<button name="create_action" string="Add in the 'Action' menu" type="object" attrs="{'invisible':[('action_window_id','!=',False)]}" icon="fa-plus-square" help="Display an option on related documents." class="oe_stat_button"/>
|
||||
<button name="unlink_action" string="Remove from the 'Action' menu" type="object" attrs="{'invisible':[('action_window_id','=',False)]}" icon="fa-minus-square" help="Remove the contextual action." class="oe_stat_button"/>
|
||||
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
|
||||
<field name="active" widget="boolean_button" options="{"terminology": "archive"}"/>
|
||||
</button>
|
||||
</div>
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
@@ -36,6 +40,7 @@
|
||||
<field name="width"/>
|
||||
<field name="origin_x"/>
|
||||
<field name="origin_y"/>
|
||||
<field name="data_type"/>
|
||||
<field name="restore_saved_config"/>
|
||||
</group>
|
||||
<group attrs="{'invisible':[('test_print_mode', '=', False)]}">
|
||||
@@ -44,7 +49,7 @@
|
||||
<notebook>
|
||||
<page string="Components">
|
||||
<field name="component_ids" nolabel="1" colspan="4">
|
||||
<tree>
|
||||
<tree string="Label Component">
|
||||
<field name="sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="component_type"/>
|
||||
@@ -77,6 +82,7 @@
|
||||
<page string="Format" attrs="{'invisible': [('component_type', 'in', ('sublabel', 'qr_code', 'zpl2_raw'))]}">
|
||||
<group>
|
||||
<field name="height"/>
|
||||
<field name="only_product_barcode"/>
|
||||
<field name="width" attrs="{'invisible': [('component_type', 'not in', ('text', 'rectangle', 'diagonal', 'circle', 'graphic'))]}"/>
|
||||
<field name="reverse_print"/>
|
||||
<field name="orientation" attrs="{'invisible': [('component_type', 'in', ('rectangle', 'diagonal', 'circle'))]}"/>
|
||||
|
||||
@@ -9,7 +9,7 @@ import re
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from odoo import _ as translate, fields, models
|
||||
from odoo import _, fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -376,7 +376,7 @@ class WizardImportZPl2(models.TransientModel):
|
||||
|
||||
args = line.split("^")
|
||||
for arg in args:
|
||||
for _, code in SUPPORTED_CODE.items():
|
||||
for _key, code in SUPPORTED_CODE.items():
|
||||
component_arg = code["method"](arg)
|
||||
if component_arg:
|
||||
if code.get("default", False):
|
||||
@@ -398,8 +398,9 @@ class WizardImportZPl2(models.TransientModel):
|
||||
seq = sequence + i * 10
|
||||
vals.update(
|
||||
{
|
||||
"name": translate("Import %s") % seq,
|
||||
"name": _("Import %s") % seq,
|
||||
"sequence": seq,
|
||||
"model": str(zpl2.MODEL_ENHANCED),
|
||||
"label_id": self.label_id.id,
|
||||
}
|
||||
)
|
||||
@@ -423,5 +424,7 @@ class WizardImportZPl2(models.TransientModel):
|
||||
value = True
|
||||
if field_type in ("integer", "float"):
|
||||
value = float(value)
|
||||
if field == "model":
|
||||
value = int(float(value))
|
||||
component.update({field: value})
|
||||
return component
|
||||
|
||||
Reference in New Issue
Block a user