From 21f5b53f8b0998eeb7b7e45ac49032c1f72d2ceb Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 1 Mar 2023 23:19:50 +0100 Subject: [PATCH] [MIG] product_print_zpl_barcode to v16 --- product_print_zpl_barcode/__manifest__.py | 7 ++- product_print_zpl_barcode/models/product.py | 57 +++++++++++++++++-- product_print_zpl_barcode/views/product.xml | 8 +-- .../wizard/product_print_zpl_barcode.py | 39 +++++++------ .../wizard/product_print_zpl_barcode_view.xml | 6 +- 5 files changed, 84 insertions(+), 33 deletions(-) diff --git a/product_print_zpl_barcode/__manifest__.py b/product_print_zpl_barcode/__manifest__.py index 614af43..cc5a260 100644 --- a/product_print_zpl_barcode/__manifest__.py +++ b/product_print_zpl_barcode/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Product Generate Price Weight Barcode', - 'version': '14.0.1.0.0', + 'version': '16.0.1.0.0', 'category': 'Extra Tools', 'license': 'AGPL-3', 'summary': 'Add a wizard to print product barcode stickers on ZPL printer', @@ -32,7 +32,7 @@ This module has been written by Alexis de Lattre from Akretion . """, 'author': 'Akretion', - 'website': 'http://www.akretion.com', + 'website': 'https://github.com/akretion/odoo-usability', # We depend on point_of_sale and not only 'product' # because the price barcode rule is added by the point_of_sale module # (the weight barcode rule is added by the stock module) @@ -41,11 +41,12 @@ This module has been written by Alexis de Lattre from Akretion 'barcodes', 'base_report_to_printer', ], + 'external_dependencies': {'python': ['python-barcode>=0.14.0']}, 'data': [ 'security/ir.model.access.csv', 'wizard/product_print_zpl_barcode_view.xml', 'views/product.xml', 'data/barcode_sequence.xml', ], - 'installable': False, + 'installable': True, } diff --git a/product_print_zpl_barcode/models/product.py b/product_print_zpl_barcode/models/product.py index 6853de6..5ea3767 100644 --- a/product_print_zpl_barcode/models/product.py +++ b/product_print_zpl_barcode/models/product.py @@ -1,11 +1,14 @@ -# -*- coding: utf-8 -*- -# Copyright 2020 Akretion France (http://www.akretion.com/) +# Copyright 2020-2023 Akretion France (http://www.akretion.com/) # @author: Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models, _ from odoo.exceptions import UserError -from stdnum.ean import calc_check_digit +from stdnum.ean import calc_check_digit, is_valid +from barcode import EAN13, EAN8 +from barcode.writer import ImageWriter, SVGWriter +import base64 +import io class ProductTemplate(models.Model): @@ -34,8 +37,8 @@ class ProductTemplate(models.Model): "print_zpl_barcode_from_product_template on product '%s' " "because it has %d variants and not just one.") % (self.display_name, self.product_variant_count)) - action = self.env.ref( - 'product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0] + action = self.env["ir.actions.actions"]._for_xml_id( + 'product_print_zpl_barcode.product_print_zpl_barcode_action') action['context'] = { 'active_id': self.product_variant_ids[0].id, 'active_model': 'product.product', @@ -46,6 +49,50 @@ class ProductTemplate(models.Model): class ProductProduct(models.Model): _inherit = 'product.product' + # Not useful for ZPL, but it is often useful to have a barcode image field + barcode_image_png = fields.Binary( + compute='_compute_barcode_image_png', + string='PNG Barcode Image') + barcode_image_svg = fields.Binary( + compute='_compute_barcode_image_svg', + string='SVG Barcode Image') + + def _get_barcode_image(self, img_format): + self.ensure_one() + barcode = self.barcode + if not barcode: + return False + res = False + if isinstance(barcode, str) and len(barcode) in (8, 13) and is_valid(barcode): + barcode_obj = False + if img_format == 'svg': + writer = SVGWriter() + elif img_format == 'png': + writer = ImageWriter() + else: + return False + if len(barcode) == 13: + barcode_obj = EAN13(barcode, writer=writer, guardbar=True) + elif len(barcode) == 8: + barcode_obj = EAN8(barcode, writer=writer, guardbar=True) + if barcode_obj: + barcode_file = io.BytesIO() + barcode_obj.write(barcode_file) + barcode_file.seek(0) + barcode_img = barcode_file.read() + res = base64.b64encode(barcode_img) + return res + + @api.depends('barcode') + def _compute_barcode_image_svg(self): + for product in self: + product.barcode_image_svg = product._get_barcode_image('svg') + + @api.depends('barcode') + def _compute_barcode_image_png(self): + for product in self: + product.barcode_image_png = product._get_barcode_image('png') + def generate_barcode_from_product_product(self): self.ensure_one() if self.barcode: diff --git a/product_print_zpl_barcode/views/product.xml b/product_print_zpl_barcode/views/product.xml index 9bd40fb..65c62be 100644 --- a/product_print_zpl_barcode/views/product.xml +++ b/product_print_zpl_barcode/views/product.xml @@ -25,10 +25,10 @@ product.template -
+
+
@@ -37,10 +37,10 @@ product.product -
+
+
diff --git a/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py b/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py index 45a05b1..f48277d 100644 --- a/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py +++ b/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py @@ -5,7 +5,7 @@ from odoo import api, fields, models, _ from odoo.exceptions import UserError from odoo.tools import float_compare, float_is_zero -from stdnum.ean import is_valid +from stdnum.ean import is_valid, calc_check_digit import base64 import re @@ -55,8 +55,7 @@ class ProductPrintZplBarcode(models.TransientModel): product_id = fields.Many2one( 'product.product', string='Product', required=True, readonly=True) - uom_id = fields.Many2one( - related='product_id.uom_id', readonly=True) + uom_id = fields.Many2one(related='product_id.uom_id') # 1 line = un peu moins de 30 product_name = fields.Char('Product Label', required=True, size=56) nomenclature_id = fields.Many2one( @@ -64,15 +63,13 @@ class ProductPrintZplBarcode(models.TransientModel): rule_id = fields.Many2one( 'barcode.rule', string='Barcode Rule', readonly=True, compute='_compute_rule_id') - barcode_type = fields.Selection( - related='rule_id.type', readonly=True, string="Barcode Type") + barcode_type = fields.Selection(related='rule_id.type', string="Barcode Type") label_size = fields.Selection([ ('38x25', '38x25 mm'), - ], required=True, default='38x25', string='Label Size') + ], required=True, default='38x25') pricelist_id = fields.Many2one( 'product.pricelist', string='Pricelist', required=True) - currency_id = fields.Many2one( - related='pricelist_id.currency_id', readonly=True) + currency_id = fields.Many2one(related='pricelist_id.currency_id') # TODO: for the moment, we only support weight, but... quantity = fields.Float(digits='Stock Weight') price_uom = fields.Monetary( @@ -97,7 +94,7 @@ class ProductPrintZplBarcode(models.TransientModel): # for regular barcodes for wiz in self: if wiz.pricelist_id and wiz.product_id: - price_uom = wiz.pricelist_id.get_product_price( + price_uom = wiz.pricelist_id._get_product_price( wiz.product_id, 1, False) wiz.price_uom = price_uom wiz.price = price_uom * wiz.quantity @@ -140,14 +137,14 @@ class ProductPrintZplBarcode(models.TransientModel): "of the barcode pattern (%s).") % (pbarcode, len(pbarcode), len(prefix), prefix)) barcode = pbarcode[0:len(prefix)] - # print "barcode=", barcode - # print "pattern=", pattern + # print("barcode=", barcode) + # print("pattern=", pattern) m = re.search('\{N+D+\}', pattern) - # print "m=", m + # print("m=", m) assert m pattern_val = m.group(0) pattern_val = pattern_val[1:-1] - # print "pattern_val=", pattern_val + # print("pattern_val=", pattern_val) max_value = 10**pattern_val.count('N') if float_compare(value, max_value, precision_digits=prec) != -1: raise UserError(_( @@ -166,11 +163,15 @@ class ProductPrintZplBarcode(models.TransientModel): value_d_ext = value_d + '0' * pattern_val.count('D') # 2) cut at the right size barcode += value_d_ext[0:pattern_val.count('D')] - # print "barcode=", barcode + # print("barcode=", barcode) # Add checksum if self.rule_id.encoding == 'ean13': - barcode = bno.sanitize_ean(barcode) - # print "barcode FINAL=", barcode + # I don't call bno.sanitize_ean() due to this bug: + # https://github.com/odoo/odoo/pull/114112 + barcode = barcode + calc_check_digit(barcode) + assert len(barcode) == 13 + assert is_valid(barcode) + # print("barcode FINAL=", barcode) zpl_unicode = self._price_weight_barcode_type_zpl() % { 'product_name': self.product_name, 'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE', @@ -270,7 +271,8 @@ class ProductPrintZplBarcode(models.TransientModel): 'zpl_filename': 'barcode_%s.zpl' % vals['barcode'], }) self.write(vals) - action = self.env.ref('product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0] + action = self.env["ir.actions.actions"]._for_xml_id( + 'product_print_zpl_barcode.product_print_zpl_barcode_action') action.update({ 'res_id': self.id, 'context': self._context, @@ -285,7 +287,8 @@ class ProductPrintZplBarcode(models.TransientModel): self.zpl_filename, base64.decodebytes(self.zpl_file), format='raw') action = True if self._context.get('print_and_new'): - action = self.env.ref('product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0] + action = self.env["ir.actions.actions"]._for_xml_id( + 'product_print_zpl_barcode.product_print_zpl_barcode_action') action.update({ 'views': False, 'context': self._context, diff --git a/product_print_zpl_barcode/wizard/product_print_zpl_barcode_view.xml b/product_print_zpl_barcode/wizard/product_print_zpl_barcode_view.xml index f838593..bd04db6 100644 --- a/product_print_zpl_barcode/wizard/product_print_zpl_barcode_view.xml +++ b/product_print_zpl_barcode/wizard/product_print_zpl_barcode_view.xml @@ -27,10 +27,10 @@ -
+