[IMP] pre-commit: first run on whole repo
This commit is contained in:
@@ -3,12 +3,12 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
'name': 'Product Generate Price Weight Barcode',
|
||||
'version': '14.0.1.0.0',
|
||||
'category': 'Extra Tools',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Add a wizard to print product barcode stickers on ZPL printer',
|
||||
'description': """
|
||||
"name": "Product Generate Price Weight Barcode",
|
||||
"version": "14.0.1.0.0",
|
||||
"category": "Extra Tools",
|
||||
"license": "AGPL-3",
|
||||
"summary": "Add a wizard to print product barcode stickers on ZPL printer",
|
||||
"description": """
|
||||
Print product barcode stickers on ZPL printer
|
||||
=============================================
|
||||
|
||||
@@ -31,21 +31,21 @@ It also allows to generate a private barcode for products without barcode. For t
|
||||
This module has been written by Alexis de Lattre from Akretion
|
||||
<alexis.delattre@akretion.com>.
|
||||
""",
|
||||
'author': 'Akretion',
|
||||
'website': 'http://www.akretion.com',
|
||||
"author": "Akretion",
|
||||
"website": "https://github.com/OCA/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)
|
||||
'depends': [
|
||||
'point_of_sale',
|
||||
'barcodes',
|
||||
'base_report_to_printer',
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/product_print_zpl_barcode_view.xml',
|
||||
'views/product.xml',
|
||||
'data/barcode_sequence.xml',
|
||||
"depends": [
|
||||
"point_of_sale",
|
||||
"barcodes",
|
||||
"base_report_to_printer",
|
||||
],
|
||||
'installable': True,
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"wizard/product_print_zpl_barcode_view.xml",
|
||||
"views/product.xml",
|
||||
"data/barcode_sequence.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
@@ -18,7 +17,7 @@ for example 623 : Brunei
|
||||
<field name="prefix">298</field>
|
||||
<field name="padding">9</field>
|
||||
<field name="number_next">1</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2020 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 odoo import _, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = "product.template"
|
||||
@@ -14,54 +14,71 @@ class ProductTemplate(models.Model):
|
||||
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.")
|
||||
"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))
|
||||
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').sudo().read()[0]
|
||||
action['context'] = {
|
||||
'active_id': self.product_variant_ids[0].id,
|
||||
'active_model': 'product.product',
|
||||
}
|
||||
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")
|
||||
.sudo()
|
||||
.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'
|
||||
_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')
|
||||
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)))
|
||||
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,
|
||||
})
|
||||
self.write(
|
||||
{
|
||||
"barcode": barcode,
|
||||
"must_print_barcode": True,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
Copyright 2020-2021 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).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="product_template_form_view" model="ir.ui.view">
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="inherit_id" ref="product.product_template_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="options" position="inside">
|
||||
<div id="must_print_barcode">
|
||||
<field name="must_print_barcode"/>
|
||||
<label for="must_print_barcode"/>
|
||||
<field name="must_print_barcode" />
|
||||
<label for="must_print_barcode" />
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
@@ -23,11 +22,22 @@
|
||||
|
||||
<record id="product_template_only_form_view" model="ir.ui.view">
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<header position="inside">
|
||||
<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)]}"/>
|
||||
<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>
|
||||
</field>
|
||||
</record>
|
||||
@@ -38,8 +48,19 @@
|
||||
<field name="inherit_id" ref="product.product_normal_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<header position="inside">
|
||||
<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)]}"/>
|
||||
<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>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -2,191 +2,218 @@
|
||||
# @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 odoo.tools import float_compare, float_is_zero
|
||||
from stdnum.ean import is_valid
|
||||
import base64
|
||||
import re
|
||||
|
||||
from stdnum.ean import is_valid
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_compare, float_is_zero
|
||||
|
||||
|
||||
class ProductPrintZplBarcode(models.TransientModel):
|
||||
_name = 'product.print.zpl.barcode'
|
||||
_description = 'Generate and print product barcodes in ZPL'
|
||||
_name = "product.print.zpl.barcode"
|
||||
_description = "Generate and print product barcodes in ZPL"
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
assert self._context.get('active_model') == 'product.product',\
|
||||
'wrong active_model, should be product.product'
|
||||
product_id = self._context.get('active_id')
|
||||
product = self.env['product.product'].browse(product_id)
|
||||
assert (
|
||||
self._context.get("active_model") == "product.product"
|
||||
), "wrong active_model, should be product.product"
|
||||
product_id = self._context.get("active_id")
|
||||
product = self.env["product.product"].browse(product_id)
|
||||
if not product:
|
||||
raise UserError(_('Missing Product'))
|
||||
raise UserError(_("Missing Product"))
|
||||
if not product.barcode:
|
||||
raise UserError(_(
|
||||
"Product '%s' doesn't have a barcode") % product.display_name)
|
||||
nomenclature = self.env.ref('barcodes.default_barcode_nomenclature')
|
||||
raise UserError(
|
||||
_("Product '%s' doesn't have a barcode") % product.display_name
|
||||
)
|
||||
nomenclature = self.env.ref("barcodes.default_barcode_nomenclature")
|
||||
company = self.env.company
|
||||
posconfig = self.env['pos.config'].sudo().search(
|
||||
[('company_id', '=', company.id)], limit=1)
|
||||
posconfig = (
|
||||
self.env["pos.config"]
|
||||
.sudo()
|
||||
.search([("company_id", "=", company.id)], limit=1)
|
||||
)
|
||||
if posconfig:
|
||||
pricelist = posconfig.pricelist_id
|
||||
else:
|
||||
pricelist = self.env['product.pricelist'].search([
|
||||
'|', ('company_id', '=', False),
|
||||
('company_id', '=', company.id),
|
||||
], limit=1)
|
||||
pricelist = self.env["product.pricelist"].search(
|
||||
[
|
||||
"|",
|
||||
("company_id", "=", False),
|
||||
("company_id", "=", company.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if not pricelist:
|
||||
raise UserError(_(
|
||||
"There are no pricelist in company %s ?") % company.name)
|
||||
raise UserError(_("There are no pricelist in company %s ?") % company.name)
|
||||
|
||||
printer = self.env['printing.printer'].get_default()
|
||||
res.update({
|
||||
'nomenclature_id': nomenclature.id,
|
||||
'pricelist_id': pricelist.id,
|
||||
'currency_id': pricelist.currency_id.id,
|
||||
'barcode': product.barcode,
|
||||
'product_name': product.name,
|
||||
'product_id': product_id,
|
||||
'zpl_printer_id': printer and printer.id or False,
|
||||
})
|
||||
printer = self.env["printing.printer"].get_default()
|
||||
res.update(
|
||||
{
|
||||
"nomenclature_id": nomenclature.id,
|
||||
"pricelist_id": pricelist.id,
|
||||
"currency_id": pricelist.currency_id.id,
|
||||
"barcode": product.barcode,
|
||||
"product_name": product.name,
|
||||
"product_id": product_id,
|
||||
"zpl_printer_id": printer and printer.id or False,
|
||||
}
|
||||
)
|
||||
return res
|
||||
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string='Product', required=True, readonly=True)
|
||||
uom_id = fields.Many2one(
|
||||
related='product_id.uom_id', readonly=True)
|
||||
"product.product", string="Product", required=True, readonly=True
|
||||
)
|
||||
uom_id = fields.Many2one(related="product_id.uom_id", readonly=True)
|
||||
# 1 line = un peu moins de 30
|
||||
product_name = fields.Char('Product Label', required=True, size=56)
|
||||
product_name = fields.Char("Product Label", required=True, size=56)
|
||||
nomenclature_id = fields.Many2one(
|
||||
'barcode.nomenclature', 'Barcode Nomenclature', required=True)
|
||||
"barcode.nomenclature", "Barcode Nomenclature", required=True
|
||||
)
|
||||
rule_id = fields.Many2one(
|
||||
'barcode.rule', string='Barcode Rule', readonly=True,
|
||||
compute='_compute_rule_id')
|
||||
"barcode.rule", string="Barcode Rule", readonly=True, compute="_compute_rule_id"
|
||||
)
|
||||
barcode_type = fields.Selection(
|
||||
related='rule_id.type', readonly=True, string="Barcode Type")
|
||||
label_size = fields.Selection([
|
||||
('38x25', '38x25 mm'),
|
||||
], required=True, default='38x25', string='Label Size')
|
||||
related="rule_id.type", readonly=True, string="Barcode Type"
|
||||
)
|
||||
label_size = fields.Selection(
|
||||
[
|
||||
("38x25", "38x25 mm"),
|
||||
],
|
||||
required=True,
|
||||
default="38x25",
|
||||
string="Label Size",
|
||||
)
|
||||
pricelist_id = fields.Many2one(
|
||||
'product.pricelist', string='Pricelist', required=True)
|
||||
currency_id = fields.Many2one(
|
||||
related='pricelist_id.currency_id', readonly=True)
|
||||
"product.pricelist", string="Pricelist", required=True
|
||||
)
|
||||
currency_id = fields.Many2one(related="pricelist_id.currency_id", readonly=True)
|
||||
# TODO: for the moment, we only support weight, but...
|
||||
quantity = fields.Float(digits='Stock Weight')
|
||||
quantity = fields.Float(digits="Stock Weight")
|
||||
price_uom = fields.Monetary(
|
||||
readonly=True, string="Price per Unit of Measure",
|
||||
compute='_compute_price') # given by pricelist
|
||||
price = fields.Monetary(compute='_compute_price', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
state = fields.Selection([
|
||||
('step1', 'Step1'),
|
||||
('step2', 'Step2'),
|
||||
], default='step1', readonly=True)
|
||||
zpl_file = fields.Binary(string='ZPL File', readonly=True)
|
||||
zpl_filename = fields.Char('ZPL Filename')
|
||||
readonly=True, string="Price per Unit of Measure", compute="_compute_price"
|
||||
) # given by pricelist
|
||||
price = fields.Monetary(compute="_compute_price", readonly=True)
|
||||
currency_id = fields.Many2one("res.currency", string="Currency")
|
||||
state = fields.Selection(
|
||||
[
|
||||
("step1", "Step1"),
|
||||
("step2", "Step2"),
|
||||
],
|
||||
default="step1",
|
||||
readonly=True,
|
||||
)
|
||||
zpl_file = fields.Binary(string="ZPL File", readonly=True)
|
||||
zpl_filename = fields.Char("ZPL Filename")
|
||||
barcode = fields.Char(readonly=True)
|
||||
copies = fields.Integer(
|
||||
string='Number of Labels', default=1, required=True)
|
||||
zpl_printer_id = fields.Many2one(
|
||||
'printing.printer', string='ZPL Printer')
|
||||
copies = fields.Integer(string="Number of Labels", default=1, required=True)
|
||||
zpl_printer_id = fields.Many2one("printing.printer", string="ZPL Printer")
|
||||
|
||||
@api.depends('pricelist_id', 'quantity', 'product_id')
|
||||
@api.depends("pricelist_id", "quantity", "product_id")
|
||||
def _compute_price(self):
|
||||
# for regular barcodes
|
||||
for wiz in self:
|
||||
if wiz.pricelist_id and wiz.product_id:
|
||||
price_uom = wiz.pricelist_id.get_product_price(
|
||||
wiz.product_id, 1, False)
|
||||
price_uom = wiz.pricelist_id.get_product_price(wiz.product_id, 1, False)
|
||||
wiz.price_uom = price_uom
|
||||
wiz.price = price_uom * wiz.quantity
|
||||
|
||||
@api.depends('nomenclature_id')
|
||||
@api.depends("nomenclature_id")
|
||||
def _compute_rule_id(self):
|
||||
for wiz in self:
|
||||
match_rule = False
|
||||
if wiz.nomenclature_id and wiz.barcode:
|
||||
for rule in wiz.nomenclature_id.rule_ids:
|
||||
match = wiz.nomenclature_id.match_pattern(
|
||||
wiz.barcode, rule.pattern)
|
||||
if match.get('match'):
|
||||
match = wiz.nomenclature_id.match_pattern(wiz.barcode, rule.pattern)
|
||||
if match.get("match"):
|
||||
match_rule = rule.id
|
||||
break
|
||||
wiz.rule_id = match_rule
|
||||
|
||||
def _prepare_price_weight_barcode_type(self):
|
||||
dpo = self.env['decimal.precision']
|
||||
bno = self.env['barcode.nomenclature']
|
||||
prec = dpo.precision_get('Stock Weight')
|
||||
dpo = self.env["decimal.precision"]
|
||||
bno = self.env["barcode.nomenclature"]
|
||||
prec = dpo.precision_get("Stock Weight")
|
||||
value = self.quantity
|
||||
pbarcode = self.barcode
|
||||
if float_is_zero(value, precision_digits=prec):
|
||||
raise UserError(_(
|
||||
"The quantity (%s) must be positive !") % value)
|
||||
raise UserError(_("The quantity (%s) must be positive !") % value)
|
||||
# check prefix
|
||||
pattern = self.rule_id.pattern
|
||||
if '{' not in pattern:
|
||||
raise UserError(_(
|
||||
"The barcode rule '%s' has a pattern '%s' which doesn't "
|
||||
"contain a integer and decimal part between '{}'.")
|
||||
% (self.rule_id.name, pattern))
|
||||
prefix = pattern.split('{')[0]
|
||||
if "{" not in pattern:
|
||||
raise UserError(
|
||||
_(
|
||||
"The barcode rule '%s' has a pattern '%s' which doesn't "
|
||||
"contain a integer and decimal part between '{}'."
|
||||
)
|
||||
% (self.rule_id.name, pattern)
|
||||
)
|
||||
prefix = pattern.split("{")[0]
|
||||
assert len(prefix) >= 1
|
||||
if len(prefix) > len(pbarcode):
|
||||
raise UserError(_(
|
||||
"The barcode of the product (%s) has %d characters, "
|
||||
"which is smaller than the %d characters of the prefix "
|
||||
"of the barcode pattern (%s).")
|
||||
% (pbarcode, len(pbarcode), len(prefix), prefix))
|
||||
barcode = pbarcode[0:len(prefix)]
|
||||
raise UserError(
|
||||
_(
|
||||
"The barcode of the product (%s) has %d characters, "
|
||||
"which is smaller than the %d characters of the prefix "
|
||||
"of the barcode pattern (%s)."
|
||||
)
|
||||
% (pbarcode, len(pbarcode), len(prefix), prefix)
|
||||
)
|
||||
barcode = pbarcode[0 : len(prefix)]
|
||||
# print "barcode=", barcode
|
||||
# print "pattern=", pattern
|
||||
m = re.search('\{N+D+\}', pattern)
|
||||
m = re.search(r"\{N+D+\}", pattern)
|
||||
# print "m=", m
|
||||
assert m
|
||||
pattern_val = m.group(0)
|
||||
pattern_val = pattern_val[1:-1]
|
||||
# print "pattern_val=", pattern_val
|
||||
max_value = 10**pattern_val.count('N')
|
||||
max_value = 10 ** pattern_val.count("N")
|
||||
if float_compare(value, max_value, precision_digits=prec) != -1:
|
||||
raise UserError(_(
|
||||
"The value to encode in the barcode (%s) is superior "
|
||||
"to the maximum value allowed by the barcode pattern (%s).")
|
||||
% (value, max_value))
|
||||
raise UserError(
|
||||
_(
|
||||
"The value to encode in the barcode (%s) is superior "
|
||||
"to the maximum value allowed by the barcode pattern (%s)."
|
||||
)
|
||||
% (value, max_value)
|
||||
)
|
||||
value_str = str(value)
|
||||
value_str_split = value_str.split('.')
|
||||
value_str_split = value_str.split(".")
|
||||
assert len(value_str_split) == 2
|
||||
value_n = value_str_split[0]
|
||||
value_d = value_str_split[1]
|
||||
assert len(value_n) <= pattern_val.count('N')
|
||||
barcode += value_n.zfill(pattern_val.count('N'))
|
||||
assert len(value_n) <= pattern_val.count("N")
|
||||
barcode += value_n.zfill(pattern_val.count("N"))
|
||||
# end fill doesn't exists... so:
|
||||
# 1) make sure we have enough 0 after
|
||||
value_d_ext = value_d + '0' * pattern_val.count('D')
|
||||
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')]
|
||||
barcode += value_d_ext[0 : pattern_val.count("D")]
|
||||
# print "barcode=", barcode
|
||||
# Add checksum
|
||||
if self.rule_id.encoding == 'ean13':
|
||||
if self.rule_id.encoding == "ean13":
|
||||
barcode = bno.sanitize_ean(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',
|
||||
'ean_no_checksum': barcode[:-1],
|
||||
'price_uom': self.price_uom,
|
||||
'price': self.price,
|
||||
'currency_symbol': self.currency_id.symbol,
|
||||
'copies': self.copies,
|
||||
'quantity': value,
|
||||
'uom_name': self.uom_id.name,
|
||||
"product_name": self.product_name,
|
||||
"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,
|
||||
"copies": self.copies,
|
||||
"quantity": value,
|
||||
"uom_name": self.uom_id.name,
|
||||
}
|
||||
zpl_bytes = zpl_unicode.encode('utf-8')
|
||||
zpl_bytes = zpl_unicode.encode("utf-8")
|
||||
vals = {
|
||||
'zpl_file': base64.encodebytes(zpl_bytes),
|
||||
'barcode': barcode,
|
||||
}
|
||||
"zpl_file": base64.encodebytes(zpl_bytes),
|
||||
"barcode": barcode,
|
||||
}
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
@@ -229,65 +256,79 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
|
||||
def _prepare_product_barcode_type(self):
|
||||
zpl_unicode = self._product_barcode_type_zpl() % {
|
||||
'product_name': self.product_name,
|
||||
'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,
|
||||
"product_name": self.product_name,
|
||||
"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,
|
||||
}
|
||||
zpl_bytes = zpl_unicode.encode('utf-8')
|
||||
zpl_bytes = zpl_unicode.encode("utf-8")
|
||||
vals = {
|
||||
'zpl_file': base64.encodebytes(zpl_bytes),
|
||||
'barcode': self.barcode, # unchanged
|
||||
}
|
||||
"zpl_file": base64.encodebytes(zpl_bytes),
|
||||
"barcode": self.barcode, # unchanged
|
||||
}
|
||||
return vals
|
||||
|
||||
def generate(self):
|
||||
assert self.barcode
|
||||
if len(self.barcode) not in (8, 13):
|
||||
raise UserError(_(
|
||||
"This wizard only supports EAN8 and EAN13 for the moment. "
|
||||
"Barcode '%s' has %d digits.") % (
|
||||
self.barcode,
|
||||
len(self.barcode)))
|
||||
raise UserError(
|
||||
_(
|
||||
"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)
|
||||
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'):
|
||||
if self.barcode_type in ("price", "weight"):
|
||||
vals = self._prepare_price_weight_barcode_type()
|
||||
elif self.barcode_type == 'product':
|
||||
elif self.barcode_type == "product":
|
||||
vals = self._prepare_product_barcode_type()
|
||||
else:
|
||||
raise UserError(_(
|
||||
"Barcode Type %s is not supported for the moment")
|
||||
% self.barcode_type)
|
||||
vals.update({
|
||||
'state': 'step2',
|
||||
'zpl_filename': 'barcode_%s.zpl' % vals['barcode'],
|
||||
})
|
||||
raise UserError(
|
||||
_("Barcode Type %s is not supported for the moment") % self.barcode_type
|
||||
)
|
||||
vals.update(
|
||||
{
|
||||
"state": "step2",
|
||||
"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.update({
|
||||
'res_id': self.id,
|
||||
'context': self._context,
|
||||
'views': False})
|
||||
action = (
|
||||
self.env.ref("product_print_zpl_barcode.product_print_zpl_barcode_action")
|
||||
.sudo()
|
||||
.read()[0]
|
||||
)
|
||||
action.update({"res_id": self.id, "context": self._context, "views": False})
|
||||
return action
|
||||
|
||||
def print_zpl(self):
|
||||
if not self.zpl_printer_id:
|
||||
raise UserError(_(
|
||||
"You must select a ZPL Printer."))
|
||||
raise UserError(_("You must select a ZPL Printer."))
|
||||
self.zpl_printer_id.print_document(
|
||||
self.zpl_filename, base64.decodebytes(self.zpl_file), format='raw')
|
||||
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.update({
|
||||
'views': False,
|
||||
'context': self._context,
|
||||
})
|
||||
if self._context.get("print_and_new"):
|
||||
action = (
|
||||
self.env.ref(
|
||||
"product_print_zpl_barcode.product_print_zpl_barcode_action"
|
||||
)
|
||||
.sudo()
|
||||
.read()[0]
|
||||
)
|
||||
action.update(
|
||||
{
|
||||
"views": False,
|
||||
"context": self._context,
|
||||
}
|
||||
)
|
||||
return action
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
Copyright 2016-2020 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).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="product_print_zpl_barcode_form" model="ir.ui.view">
|
||||
@@ -13,37 +12,91 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Generate and Print Product Barcode">
|
||||
<group name="step1" string="Configuration">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_name" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="pricelist_id" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="price_uom"/>
|
||||
<field name="label_size" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="nomenclature_id" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="rule_id"/>
|
||||
<field name="barcode_type"/>
|
||||
<field name="barcode"/>
|
||||
<field name="copies" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="state" invisible="1" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<field name="product_id" />
|
||||
<field
|
||||
name="product_name"
|
||||
attrs="{'readonly': [('state', '=', 'step2')]}"
|
||||
/>
|
||||
<field
|
||||
name="pricelist_id"
|
||||
attrs="{'readonly': [('state', '=', 'step2')]}"
|
||||
/>
|
||||
<field name="price_uom" />
|
||||
<field
|
||||
name="label_size"
|
||||
attrs="{'readonly': [('state', '=', 'step2')]}"
|
||||
/>
|
||||
<field
|
||||
name="nomenclature_id"
|
||||
attrs="{'readonly': [('state', '=', 'step2')]}"
|
||||
/>
|
||||
<field name="rule_id" />
|
||||
<field name="barcode_type" />
|
||||
<field name="barcode" />
|
||||
<field name="copies" attrs="{'readonly': [('state', '=', 'step2')]}" />
|
||||
</group>
|
||||
<group string="Enter Quantity" attrs="{'invisible': [('barcode_type', '=', 'product')]}">
|
||||
<group
|
||||
string="Enter Quantity"
|
||||
attrs="{'invisible': [('barcode_type', '=', 'product')]}"
|
||||
>
|
||||
<div name="qty_uom">
|
||||
<field name="quantity" attrs="{'readonly': [('state', '=', 'step2')]}" class="oe_inline"/>
|
||||
<field name="uom_id" class="oe_inline"/>
|
||||
<field
|
||||
name="quantity"
|
||||
attrs="{'readonly': [('state', '=', 'step2')]}"
|
||||
class="oe_inline"
|
||||
/>
|
||||
<field name="uom_id" class="oe_inline" />
|
||||
</div>
|
||||
</group>
|
||||
<group name="step2" states="step2" string="Label">
|
||||
<field name="price" attrs="{'invisible': [('barcode_type', 'not in', ('price', 'weight'))]}"/>
|
||||
<field
|
||||
name="price"
|
||||
attrs="{'invisible': [('barcode_type', 'not in', ('price', 'weight'))]}"
|
||||
/>
|
||||
<field name="zpl_file" filename="zpl_filename" />
|
||||
<field name="zpl_filename" invisible="1"/>
|
||||
<field name="zpl_printer_id" attrs="{'required': [('state', '=', 'step2')]}"/>
|
||||
<field name="zpl_filename" invisible="1" />
|
||||
<field
|
||||
name="zpl_printer_id"
|
||||
attrs="{'required': [('state', '=', 'step2')]}"
|
||||
/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="generate" type="object" string="Generate Label" class="btn-primary" states="step1"/>
|
||||
<button special="cancel" string="Cancel" class="btn-default" states="step1"/>
|
||||
<button name="print_zpl" type="object" string="Print" class="btn-primary" states="step2"/>
|
||||
<button name="print_zpl" type="object" string="Print and New" class="btn-primary" context="{'print_and_new': True}" attrs="{'invisible': ['|', ('state', '!=', 'step2'), ('barcode_type', '=', 'product')]}"/>
|
||||
<button special="cancel" string="Close" class="btn-default" states="step2"/>
|
||||
<button
|
||||
name="generate"
|
||||
type="object"
|
||||
string="Generate Label"
|
||||
class="btn-primary"
|
||||
states="step1"
|
||||
/>
|
||||
<button
|
||||
special="cancel"
|
||||
string="Cancel"
|
||||
class="btn-default"
|
||||
states="step1"
|
||||
/>
|
||||
<button
|
||||
name="print_zpl"
|
||||
type="object"
|
||||
string="Print"
|
||||
class="btn-primary"
|
||||
states="step2"
|
||||
/>
|
||||
<button
|
||||
name="print_zpl"
|
||||
type="object"
|
||||
string="Print and New"
|
||||
class="btn-primary"
|
||||
context="{'print_and_new': True}"
|
||||
attrs="{'invisible': ['|', ('state', '!=', 'step2'), ('barcode_type', '=', 'product')]}"
|
||||
/>
|
||||
<button
|
||||
special="cancel"
|
||||
string="Close"
|
||||
class="btn-default"
|
||||
states="step2"
|
||||
/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user