[MIG] product_print_zpl_barcode to v16
This commit is contained in:
@@ -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
|
||||
<alexis.delattre@akretion.com>.
|
||||
""",
|
||||
'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,
|
||||
}
|
||||
|
||||
@@ -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 <alexis.delattre@akretion.com>
|
||||
# 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:
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<header position="inside">
|
||||
<button name="action_open_label_layout" position="after">
|
||||
<button name="generate_barcode_from_product_template" type="object" string="Generate Barcode" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '!=', False)]}"/>
|
||||
<button name="print_zpl_barcode_from_product_template" type="object" string="Print Barcode" groups="base_report_to_printer.printing_group_user" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '=', False)]}"/>
|
||||
</header>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<header position="inside">
|
||||
<button name="action_open_label_layout" position="after">
|
||||
<button name="generate_barcode_from_product_product" type="object" string="Generate Barcode" attrs="{'invisible': [('barcode', '!=', False)]}"/>
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcode" groups="base_report_to_printer.printing_group_user" attrs="{'invisible': [('barcode', '=', False)]}"/>
|
||||
</header>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
<field name="copies" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
</group>
|
||||
<group string="Enter Quantity" attrs="{'invisible': [('barcode_type', '=', 'product')]}">
|
||||
<div name="qty_uom">
|
||||
<label for="quantity"/>
|
||||
<div name="qty_uom" class="o_row">
|
||||
<field name="quantity" attrs="{'readonly': [('state', '=', 'step2')]}" class="oe_inline"/>
|
||||
<field name="uom_id" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="uom_id" class="oe_inline" style="margin-left: 5px"/> </div>
|
||||
</group>
|
||||
<group name="step2" states="step2" string="Label">
|
||||
<field name="price" attrs="{'invisible': [('barcode_type', 'not in', ('price', 'weight'))]}"/>
|
||||
|
||||
Reference in New Issue
Block a user