From 5496aa38f84ea003732adaa1b216c8cd4221d5cc Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 22 Jan 2021 15:00:14 +0100 Subject: [PATCH] product_print_zpl_barcode: Add support for barcode generation Add support for EAN-8 (in additional to EAN-13) --- product_print_zpl_barcode/__init__.py | 3 +- product_print_zpl_barcode/__manifest__.py | 1 + .../data/barcode_sequence.xml | 26 +++++++ product_print_zpl_barcode/models/__init__.py | 1 + product_print_zpl_barcode/models/product.py | 67 +++++++++++++++++++ product_print_zpl_barcode/views/product.xml | 31 ++++++++- .../wizard/product_print_zpl_barcode.py | 25 ++++--- 7 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 product_print_zpl_barcode/data/barcode_sequence.xml create mode 100644 product_print_zpl_barcode/models/__init__.py create mode 100644 product_print_zpl_barcode/models/product.py diff --git a/product_print_zpl_barcode/__init__.py b/product_print_zpl_barcode/__init__.py index 3b4c3ed..9b42961 100644 --- a/product_print_zpl_barcode/__init__.py +++ b/product_print_zpl_barcode/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- - +from . import models from . import wizard diff --git a/product_print_zpl_barcode/__manifest__.py b/product_print_zpl_barcode/__manifest__.py index 23bcabb..ae746ef 100644 --- a/product_print_zpl_barcode/__manifest__.py +++ b/product_print_zpl_barcode/__manifest__.py @@ -43,6 +43,7 @@ This module has been written by Alexis de Lattre from Akretion 'security/ir.model.access.csv', 'wizard/product_print_zpl_barcode_view.xml', 'views/product.xml', + 'data/barcode_sequence.xml', ], 'installable': True, } diff --git a/product_print_zpl_barcode/data/barcode_sequence.xml b/product_print_zpl_barcode/data/barcode_sequence.xml new file mode 100644 index 0000000..840c760 --- /dev/null +++ b/product_print_zpl_barcode/data/barcode_sequence.xml @@ -0,0 +1,26 @@ + + + + + + + + Private Product Barcode + private.product.barcode + 298 + 9 + 1 + + + + + + diff --git a/product_print_zpl_barcode/models/__init__.py b/product_print_zpl_barcode/models/__init__.py new file mode 100644 index 0000000..9649db7 --- /dev/null +++ b/product_print_zpl_barcode/models/__init__.py @@ -0,0 +1 @@ +from . import product diff --git a/product_print_zpl_barcode/models/product.py b/product_print_zpl_barcode/models/product.py new file mode 100644 index 0000000..908d84e --- /dev/null +++ b/product_print_zpl_barcode/models/product.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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 + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + must_print_barcode = fields.Boolean( + string="Must Print Barcode", + help="Enable that option for products for which you must " + "print a barcode upon reception in stock.") + + def generate_barcode_from_product_template(self): + self.ensure_one() + if self.product_variant_count != 1: + raise UserError(_( + "You cannot call the method " + "generate_barcode_from_product_template on product '%s' " + "because it has %d variants and not just one.") + % (self.display_name, self.product_variant_count)) + return self.product_variant_ids[0].generate_barcode_from_product_product() + + def print_zpl_barcode_from_product_template(self): + self.ensure_one() + if self.product_variant_count != 1: + raise UserError(_( + "You cannot call the method " + "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').read()[0] + action['context'] = { + 'active_id': self.product_variant_ids[0].id, + 'active_model': 'product.product', + } + return action + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + def generate_barcode_from_product_product(self): + self.ensure_one() + if self.barcode: + raise UserError(_( + "The product '%s' already has a barcode.") % self.display_name) + barcode_without_checksum = self.env['ir.sequence'].next_by_code( + 'private.product.barcode') + if len(barcode_without_checksum) not in (7, 12): + raise UserError(_( + "The sequence 'private.product.barcode' is not properly " + "configured. The generated sequence should have 7 digits " + "(for EAN-8) or 12 digits (for EAN-13). " + "It currently has %d digits." % len(barcode_without_checksum))) + checksum = calc_check_digit(barcode_without_checksum) + barcode = barcode_without_checksum + str(checksum) + self.write({ + 'barcode': barcode, + 'must_print_barcode': True, + }) diff --git a/product_print_zpl_barcode/views/product.xml b/product_print_zpl_barcode/views/product.xml index 3153508..9bd40fb 100644 --- a/product_print_zpl_barcode/views/product.xml +++ b/product_print_zpl_barcode/views/product.xml @@ -1,20 +1,45 @@ + + product.template + + +
+
+ +
+
+
+
+ + + + product.template + + +
+
+
+
+ - generate.weight.price.barcode.product.product.form 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 fe682d6..26be01a 100644 --- a/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py +++ b/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py @@ -5,6 +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 import base64 import re @@ -172,7 +173,8 @@ class ProductPrintZplBarcode(models.TransientModel): # print "barcode FINAL=", barcode zpl_unicode = self._price_weight_barcode_type_zpl() % { 'product_name': self.product_name, - 'ean13_no_checksum': barcode[:12], + 'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE', + 'ean_no_checksum': barcode[:-1], 'price_uom': self.price_uom, 'price': self.price, 'currency_symbol': self.currency_id.symbol, @@ -189,7 +191,7 @@ class ProductPrintZplBarcode(models.TransientModel): @api.model def _price_weight_barcode_type_zpl(self): - label = u""" + label = """ ^XA ^CI28 ^PW304 @@ -201,7 +203,7 @@ class ProductPrintZplBarcode(models.TransientModel): ^FO15,30^FB270,3,0,C^FD%(product_name)s^FS ^CF0,25 ^FO15,75^FB270,1,0,C^FD%(quantity).3f %(uom_name)s %(price_uom).2f %(currency_symbol)s/%(uom_name)s^FS -^FO60,110^BEN,50^FD%(ean13_no_checksum)s^FS +^FO60,110^%(ean_zpl_command)sN,50^FD%(ean_no_checksum)s^FS ^PQ%(copies)s ^XZ """ @@ -209,7 +211,7 @@ class ProductPrintZplBarcode(models.TransientModel): @api.model def _product_barcode_type_zpl(self): - label = u""" + label = """ ^XA ^CI28 ^PW304 @@ -219,7 +221,7 @@ class ProductPrintZplBarcode(models.TransientModel): ^FO15,0^FB270,1,0,C^FD%(price_uom).2f %(currency_symbol)s^FS ^CF0,20 ^FO15,30^FB270,3,0,C^FD%(product_name)s^FS -^FO60,100^BEN,60^FD%(ean13_no_checksum)s^FS +^FO60,100^%(ean_zpl_command)sN,60^FD%(ean_no_checksum)s^FS ^PQ%(copies)s ^XZ """ @@ -228,7 +230,8 @@ class ProductPrintZplBarcode(models.TransientModel): def _prepare_product_barcode_type(self): zpl_unicode = self._product_barcode_type_zpl() % { 'product_name': self.product_name, - 'ean13_no_checksum': self.barcode[:12], + 'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE', + 'ean_no_checksum': self.barcode[:-1], 'price_uom': self.price_uom, 'currency_symbol': self.currency_id.symbol, # symbol is a required field 'copies': self.copies, @@ -242,12 +245,16 @@ class ProductPrintZplBarcode(models.TransientModel): def generate(self): assert self.barcode - if len(self.barcode) != 13: + if len(self.barcode) not in (8, 13): raise UserError(_( - "This wizard only supports EAN13 for the moment. Barcode '%s' " - "has %d digits instead of 13") % ( + "This wizard only supports EAN8 and EAN13 for the moment. " + "Barcode '%s' has %d digits.") % ( self.barcode, len(self.barcode))) + if not is_valid(self.barcode): + raise UserError(_( + "The barcode '%s' is not a valid EAN barcode " + "(wrong checksum).") % self.barcode) if not self.copies: raise UserError(_("The number of copies cannot be 0")) if self.barcode_type in ('price', 'weight'):