# 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, is_valid from barcode import EAN13, EAN8 from barcode.writer import ImageWriter, SVGWriter import base64 import io 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() 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: 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, })