diff --git a/barcodes_generate/README.rst b/barcodes_generate/README.rst index a4865abe..255ffd73 100644 --- a/barcodes_generate/README.rst +++ b/barcodes_generate/README.rst @@ -11,9 +11,9 @@ depending on a given barcode rule. For exemple, a typical pattern for products is "23.....{NNNDD}" that means that: -* the EAN13 code will begin by '23' ; -* followed by 5 digits (named Barcode Base in this module, ) ; -* and after 5 others digits to define the variable price ; +* the EAN13 code will begin by '23' +* followed by 5 digits (named Barcode Base in this module) +* and after 5 others digits to define the variable price * a 13 digit control With this module, it is possible to: @@ -21,8 +21,17 @@ With this module, it is possible to: * Affect a pattern (barcode.rule) to a product.product or a res.partner * Generate the next Barcode base of a pattern. (to avoid duplicate barcode) + * Generate a barcode, based on a pattern and a barcode base +Installation +============ + +This module use an extra python librairy named 'pyBarcode' you should install +to make barcode generation works properly. + +sudo pip install pyBarcode + Configuration ============= @@ -63,10 +72,32 @@ Try this module on Runbot :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/184/9.0 +Inheritance +=========== + +If you want to generate barcode for another model, you can create a custom +module that inherits on 'barcodes_generate' and inherit your model like that: + +class MyModel(models.Model): + _name = 'my.model' + _inherit = ['my.model', 'barcode.generate.mixin'] + +class barcode_rule(models.Model): + _inherit = 'barcode.rule' + + generate_model = fields.Selection(selection_add=[('my.model', 'My Model')]) + +Finally, you should inherit your model view adding buttons and fields. + +Note +---- + +Your model should have a field 'barcode' defined. + Known issues / Roadmap ====================== -Dependency to point_of_sale is required because barcode field, defined in 'base' +1. Dependency to point_of_sale is required because barcode field, defined in 'base' module (in the res.partner model), is defined in a 'point_of_sale' view. Furthermore, barcode nomenclature menu is available on Point Of Sale submenu. @@ -76,6 +107,9 @@ mostly in a Point of Sale context. You could comment 'point_of_sale' dependencies if you want to use this module without point of sale installed. +2. On barcode.rule model, constraint and domain system could be set between +'type' and 'generate_model' fields. + Bug Tracker =========== diff --git a/barcodes_generate/__openerp__.py b/barcodes_generate/__openerp__.py index 9b131278..7dc2a1ed 100644 --- a/barcodes_generate/__openerp__.py +++ b/barcodes_generate/__openerp__.py @@ -28,6 +28,7 @@ 'demo': [ 'demo/res_users.xml', 'demo/barcode_rule.xml', + 'demo/ir_sequence.xml', ], 'images': [ 'static/description/barcode_rule.png' diff --git a/barcodes_generate/demo/barcode.rule.xml b/barcodes_generate/demo/barcode.rule.xml deleted file mode 100644 index b37e4458..00000000 --- a/barcodes_generate/demo/barcode.rule.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - Partner (Generated Barcode) - - client - ean13 - 042......... - 999 - - - - diff --git a/barcodes_generate/demo/barcode_rule.xml b/barcodes_generate/demo/barcode_rule.xml index f5f152db..a0d998ad 100644 --- a/barcodes_generate/demo/barcode_rule.xml +++ b/barcodes_generate/demo/barcode_rule.xml @@ -8,19 +8,26 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - - - - Partner (Generated Barcode) - client + 998 ean13 - 042......... - 999 + 20.......... + + + + Product (Generated Barcode) + + weight + 999 + ean13 + 042......... + + + diff --git a/barcodes_generate/demo/ir_sequence.xml b/barcodes_generate/demo/ir_sequence.xml new file mode 100644 index 00000000..65f56f41 --- /dev/null +++ b/barcodes_generate/demo/ir_sequence.xml @@ -0,0 +1,16 @@ + + + + + + + Partner Sequence (Generated Barcode) + 10 + + + diff --git a/barcodes_generate/demo/res_users.xml b/barcodes_generate/demo/res_users.xml index b90eb9d5..e222065c 100644 --- a/barcodes_generate/demo/res_users.xml +++ b/barcodes_generate/demo/res_users.xml @@ -10,8 +10,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). diff --git a/barcodes_generate/i18n/fr.po b/barcodes_generate/i18n/fr.po index fa7cdd25..5e043223 100644 --- a/barcodes_generate/i18n/fr.po +++ b/barcodes_generate/i18n/fr.po @@ -1,13 +1,13 @@ -# Translation of OpenERP Server. +# Translation of Odoo Server. # This file contains the translation of the following modules: # * barcodes_generate # msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 7.0\n" +"Project-Id-Version: Odoo Server 9.0c\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-21 00:43+0000\n" -"PO-Revision-Date: 2016-09-21 00:43+0000\n" +"POT-Creation-Date: 2016-11-05 15:15+0000\n" +"PO-Revision-Date: 2016-11-05 15:15+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,95 +16,190 @@ msgstr "" "Plural-Forms: \n" #. module: barcodes_generate -#: field:barcode.rule,is_partner_available:0 -msgid "Available for Partners" -msgstr "Disponible pour les partenaires" +#: model:ir.model.fields,help:barcodes_generate.field_barcode_generate_mixin_generate_type +#: model:ir.model.fields,help:barcodes_generate.field_barcode_rule_generate_type +#: model:ir.model.fields,help:barcodes_generate.field_product_product_generate_type +#: model:ir.model.fields,help:barcodes_generate.field_res_partner_generate_type +msgid "Allow to generate barcode, including a number (a base) in the final barcode.\n" +" 'Base Set Manually' : User should set manually the value of the barcode base\n" +" 'Base managed by Sequence': User will use a button to generate a new base. This base will be generated by a sequence" +msgstr "Autorise à générer des codes barre en incluant un numéro (base du code barre) dans le code barre final.\n" +" 'Base indiquée manuellement' : L'utilisateur devra indiquer manuellement la base du barcode\n" +" 'Base gérée via une séquence': L'utilisateur devra utiliser un boutton pour générer une nouvelle base. Cette base sera générée par une séquence" #. module: barcodes_generate -#: field:barcode.rule,is_product_available:0 -msgid "Available for Products" -msgstr "Disponible pour les articles" +#: model:ir.model.fields,field_description:barcodes_generate.field_product_product_available_in_pos +msgid "Available in the Point of Sale" +msgstr "Disponible dans le point de vente" #. module: barcodes_generate -#: field:barcode.generate.mixin,barcode_base:0 -#: field:product.product,barcode_base:0 -#: field:res.partner,barcode_base:0 +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_generate_mixin_barcode_base +#: model:ir.model.fields,field_description:barcodes_generate.field_product_product_barcode_base +#: model:ir.model.fields,field_description:barcodes_generate.field_res_partner_barcode_base msgid "Barcode Base" msgstr "Base du code Barre" #. module: barcodes_generate -#: field:barcode.generate.mixin,barcode_rule_id:0 -#: field:product.product,barcode_rule_id:0 -#: field:res.partner,barcode_rule_id:0 +#: model:ir.ui.view,arch_db:barcodes_generate.view_barcode_rule_form +msgid "Barcode Generation" +msgstr "Génération d'un code barre" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_generate_mixin_barcode_rule_id +#: model:ir.model.fields,field_description:barcodes_generate.field_product_product_barcode_rule_id +#: model:ir.model.fields,field_description:barcodes_generate.field_res_partner_barcode_rule_id msgid "Barcode Rule" msgstr "Règle de code barre" #. module: barcodes_generate -#: constraint:res.partner:0 -msgid "Error: Invalid ean code" -msgstr "Erreur : code EAN incorrect" +#: selection:barcode.rule,generate_type:0 +msgid "Base managed by Sequence" +msgstr "Base gérée via une séquence" #. module: barcodes_generate -#: view:product.product:0 -#: view:res.partner:0 -msgid "Generate Barcode" -msgstr "Générer un code barre" +#: selection:barcode.rule,generate_type:0 +msgid "Base set Manually" +msgstr "Base indiquée manuellement" #. module: barcodes_generate -#: model:res.groups,name:barcodes_generate.generate_partner_barcode -msgid "Generate Barcodes for Partners" -msgstr "Générer un code barre pour les partenaires" +#: model:ir.model.fields,help:barcodes_generate.field_product_product_to_weight +msgid "Check if the product should be weighted using the hardware scale integration" +msgstr "Cocher si le produit peut être pesé par une balance éléctronique" #. module: barcodes_generate -#: model:res.groups,name:barcodes_generate.generate_product_barcode -msgid "Generate Barcodes for Products" -msgstr "Générer un code barre pour les articles" +#: model:ir.model.fields,help:barcodes_generate.field_product_product_available_in_pos +msgid "Check if you want this product to appear in the Point of Sale" +msgstr "Cochez si vous voulez que cet article apparaisse dans le point de vente" #. module: barcodes_generate -#: help:barcode.rule,is_partner_available:0 -msgid "If checked, users with specific access right will have the possibility to generate barcodes with this pattern for the partners." -msgstr "Si cette case est cochée, les utilisateurs avec une autorisation adéquate auront la possibilité de générer des codes barres en suivant ce motif, pour les partenaires." +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_generate_mixin_display_name +msgid "Display Name" +msgstr "Nom affiché" #. module: barcodes_generate -#: help:barcode.rule,is_product_available:0 -msgid "If checked, users with specific access right will have the possibility to generate barcodes with this pattern for the products." -msgstr "Si cette case est cochée, les utilisateurs avec une autorisation adéquate auront la possibilité de générer des codes barres en suivant ce motif, pour les articles." +#: model:ir.ui.view,arch_db:barcodes_generate.view_product_product_form +#: model:ir.ui.view,arch_db:barcodes_generate.view_res_partner_form +msgid "Generate Barcode (Using Barcode Rule)" +msgstr "Générer un code barre (via règle de codes barre)" #. module: barcodes_generate -#: code:_description:0 -#: model:ir.model,name:barcodes_generate.model_res_partner +#: model:res.groups,name:barcodes_generate.generate_barcode +msgid "Generate Barcodes" +msgstr "Générer des codes barre" + +#. module: barcodes_generate +#: model:ir.ui.view,arch_db:barcodes_generate.view_product_product_form +#: model:ir.ui.view,arch_db:barcodes_generate.view_res_partner_form +msgid "Generate Base (Using Sequence)" +msgstr "Générer une base (via une séquence)" + +#. module: barcodes_generate +#: code:addons/barcodes_generate/models/barcode_generate_mixin.py:41 #, python-format +msgid "Generate Base can be used only with barcode rule with 'Generate Type' set to 'Base managed by Sequence'" +msgstr "Générer une base de code barre peut être utilisée seulement si la règle de code barre est de type 'gérée via une séquence'" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_rule_generate_model +msgid "Generate Model" +msgstr "Modèle" + +#. module: barcodes_generate +#: model:ir.ui.view,arch_db:barcodes_generate.view_barcode_rule_form +msgid "Generate Sequence" +msgstr "Séquence" + +#. module: barcodes_generate +#: code:addons/barcodes_generate/models/barcode_rule.py:68 +#, python-format +msgid "Generate Sequence is possible only if 'Generate Type' is set to 'Base managed by Sequence'" +msgstr "Générer une séquence possible seulement si le type de génération est définie à 'Gérée via une séquence'" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_generate_mixin_generate_type +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_rule_generate_type +#: model:ir.model.fields,field_description:barcodes_generate.field_product_product_generate_type +#: model:ir.model.fields,field_description:barcodes_generate.field_res_partner_generate_type +msgid "Generate Type" +msgstr "Type de génération" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_generate_mixin_id +msgid "ID" +msgstr "ID" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_generate_mixin___last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: barcodes_generate +#: selection:barcode.rule,generate_type:0 +msgid "No generation" +msgstr "Pas de génération" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_rule_padding +msgid "Padding" +msgstr "Taille" + +#. module: barcodes_generate +#: model:ir.model,name:barcodes_generate.model_res_partner msgid "Partner" msgstr "Partenaire" #. module: barcodes_generate -#: code:_description:0 +#: selection:barcode.rule,generate_model:0 +msgid "Partners" +msgstr "Partenaires" + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_product_product_pos_categ_id +msgid "Point of Sale Category" +msgstr "Catégorie du point de vente" + +#. module: barcodes_generate #: model:ir.model,name:barcodes_generate.model_product_product -#, python-format msgid "Product" msgstr "Article" #. module: barcodes_generate -#: constraint:res.partner:0 -msgid "You cannot create recursive Partner hierarchies." -msgstr "Vous ne pouvez pas créer de hiérarchies de partenaires récursives." +#: selection:barcode.rule,generate_model:0 +msgid "Products" +msgstr "Variantes d'article" #. module: barcodes_generate -#: constraint:product.product:0 -msgid "You provided an invalid \"EAN13 Barcode\" reference. You may use the \"Internal Reference\" field instead." -msgstr "Vous avez indiqué un code barre EAN13 erroné. Vous pouvez utiliser le champ \"Référence interne\" à la place." +#: model:ir.model.fields,field_description:barcodes_generate.field_barcode_rule_sequence_id +msgid "Sequence" +msgstr "Séquence" #. module: barcodes_generate -#: code:_description:0 -#: model:ir.model,name:barcodes_generate.model_barcode_generate_mixin +#: code:addons/barcodes_generate/models/barcode_rule.py:78 #, python-format +msgid "Sequence - %s" +msgstr "Séquence - %s" + +#. module: barcodes_generate +#: model:ir.model.fields,help:barcodes_generate.field_product_product_pos_categ_id +msgid "Those categories are used to group similar products for point of sale." +msgstr "Ces catégories sont utilisées pour grouper des produits semblables." + +#. module: barcodes_generate +#: model:ir.model.fields,field_description:barcodes_generate.field_product_product_to_weight +msgid "To Weigh With Scale" +msgstr "A peser avec une balance" + +#. module: barcodes_generate +#: model:ir.model,name:barcodes_generate.model_barcode_generate_mixin msgid "barcode.generate.mixin" msgstr "barcode.generate.mixin" #. module: barcodes_generate -#: code:_description:0 #: model:ir.model,name:barcodes_generate.model_barcode_rule -#, python-format msgid "barcode.rule" msgstr "barcode.rule" +#. module: barcodes_generate +#: model:ir.model.fields,help:barcodes_generate.field_barcode_rule_generate_model +msgid "if 'Generate Type' is set, mention the model related to this rule." +msgstr "Si le type de génération est défini, veuillez indiquer un modèle sur lequel l'appliquer." diff --git a/barcodes_generate/models/barcode_generate_mixin.py b/barcodes_generate/models/barcode_generate_mixin.py index 43d9abf4..96b29a4b 100644 --- a/barcodes_generate/models/barcode_generate_mixin.py +++ b/barcodes_generate/models/barcode_generate_mixin.py @@ -6,7 +6,10 @@ import logging -from openerp import models, fields, api, exceptions +from openerp import models, fields, api, exceptions, _ + +from openerp.addons.barcodes_generate.models.barcode_rule import\ + _GENERATE_TYPE _logger = logging.getLogger(__name__) @@ -24,80 +27,44 @@ class barcode_generate_mixin(models.AbstractModel): barcode_rule_id = fields.Many2one( string='Barcode Rule', comodel_name='barcode.rule') - barcode_base = fields.Char(string='Barcode Base') + barcode_base = fields.Integer(string='Barcode Base') - # Constrains Section - @api.multi - @api.constrains('barcode_base') - def _constrains_barcode_base(self): - for item in self: - if item.barcode_base and not item.barcode_base.isdigit(): - raise exceptions.Warning(_("Barcode Base should be numeric")) + generate_type = fields.Selection( + string='Generate Type', selection=_GENERATE_TYPE, readonly=True, + related='barcode_rule_id.generate_type') # View Section @api.multi def generate_base(self): for item in self: - padding = item.barcode_rule_id.pattern.count('.') - full_padding = item.barcode_rule_id.pattern.count('.') +\ - item.barcode_rule_id.pattern.count('N') +\ - item.barcode_rule_id.pattern.count('D') - generic_code = self._get_custom_barcode(item) - full_generic_code = self._get_custom_barcode(item, True) - if generic_code: - generic_code = generic_code.replace( - '.' * padding, '_' * padding) - full_generic_code = full_generic_code.replace( - '.' * full_padding, '_' * full_padding) - reserved_barcodes = self.search( - [('barcode', 'ilike', full_generic_code)]).mapped( - 'barcode') - next_base = str(self._get_next_integer_base( - item, generic_code, reserved_barcodes)).rjust(padding, '0') - item.barcode_base = next_base + if item.generate_type != 'sequence': + raise exceptions.UserError(_( + "Generate Base can be used only with barcode rule with" + " 'Generate Type' set to 'Base managed by Sequence'")) + else: + item.barcode_base =\ + item.barcode_rule_id.sequence_id.next_by_id() @api.multi def generate_barcode(self): for item in self: - padding = item.barcode_rule_id.pattern.count('.') - full_base = str(item.barcode_base).rjust(padding, '0') + padding = item.barcode_rule_id.padding + str_base = str(item.barcode_base).rjust(padding, '0') custom_code = self._get_custom_barcode(item) if custom_code: - custom_code = custom_code.replace('.' * padding, full_base) + custom_code = custom_code.replace('.' * padding, str_base) barcode_class = barcode.get_barcode_class( item.barcode_rule_id.encoding) item.barcode = barcode_class(custom_code) - @api.multi - def generate_base_barcode(self): - for item in self: - if not item.barcode_base: - item.generate_base() - item.generate_barcode() - # Custom Section @api.model - def _get_next_integer_base(self, item, generic_code, reserved_barcodes): - """Given a list of reserved_barcodes, This will return the next" - base barcode. By default, return the max barcode base + 1. - Overload / Overwrite this function to provide custom behaviour. - (specially, fill gaps functionnality). - generic_code should have the '_' pattern. - """ - if not reserved_barcodes: - return 1 - max_barcode = sorted(reserved_barcodes)[len(reserved_barcodes) - 1] - begin = generic_code.find('_') - end = begin + generic_code.count('_') - return int(max_barcode[begin:end]) + 1 - - @api.model - def _get_custom_barcode(self, item, full=False): + def _get_custom_barcode(self, item): """ if the pattern is '23.....{NNNDD}' this function will return '23.....00000' - or return '23..........' - if 'full' is set to True + Note : Overload _get_replacement_char to have another char + instead that replace 'N' and 'D' char. """ if not item.barcode_rule_id: return False @@ -105,13 +72,10 @@ class barcode_generate_mixin(models.AbstractModel): # Define barcode custom_code = item.barcode_rule_id.pattern custom_code = custom_code.replace('{', '').replace('}', '') - if not full: - custom_code = custom_code.replace( - 'D', self._get_replacement_char('D')) - return custom_code.replace( - 'N', self._get_replacement_char('N')) - else: - return custom_code.replace('D', '.').replace('N', '.') + custom_code = custom_code.replace( + 'D', self._get_replacement_char('D')) + return custom_code.replace( + 'N', self._get_replacement_char('N')) @api.model def _get_replacement_char(self, char): diff --git a/barcodes_generate/models/barcode_rule.py b/barcodes_generate/models/barcode_rule.py index ef57c695..a1482282 100644 --- a/barcodes_generate/models/barcode_rule.py +++ b/barcodes_generate/models/barcode_rule.py @@ -4,18 +4,77 @@ # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, fields +from openerp import models, fields, api, _ + +_GENERATE_TYPE = [ + ('no', 'No generation'), + ('manual', 'Base set Manually'), + ('sequence', 'Base managed by Sequence'), +] class barcode_rule(models.Model): _inherit = 'barcode.rule' - is_product_available = fields.Boolean( - string='Available for Products', help="If checked, users with" - " specific access right will have the possibility to generate" - " barcodes with this pattern for the products.") + _GENERATE_MODEL = [ + ('res.partner', 'Partners'), + ('product.product', 'Products'), + ] + + # Column Section + generate_type = fields.Selection( + string='Generate Type', selection=_GENERATE_TYPE, + required=True, default='no', + help="Allow to generate barcode, including a number" + " (a base) in the final barcode.\n" + " 'Base Set Manually' : User should set manually the value of the" + " barcode base\n" + " 'Base managed by Sequence': User will use a button to generate a" + " new base. This base will be generated by a sequence") + + generate_model = fields.Selection( + string='Generate Model', selection=_GENERATE_MODEL, + help="if 'Generate Type' is set, mention the model related to this" + " rule.") + + padding = fields.Integer( + string='Padding', compute='_compute_padding', readonly=True, + store=True) + + sequence_id = fields.Many2one( + string='Sequence', comodel_name='ir.sequence') + + # Compute Section + @api.depends('pattern') + @api.multi + def _compute_padding(self): + for rule in self: + rule.padding = rule.pattern.count('.') + + # On Change Section + @api.onchange('generate_type') + @api.multi + def onchange_generate_type(self): + for rule in self: + if rule.generate_type == 'no': + rule.generate_model = False + + # View Section + @api.multi + def generate_sequence(self): + sequence_obj = self.env['ir.sequence'] + for rule in self: + if rule.generate_type != 'sequence': + raise exceptions.UserError(_( + "Generate Sequence is possible only if 'Generate Type'" + " is set to 'Base managed by Sequence'")) + sequence = sequence_obj.create(self._prepare_sequence(rule)) + rule.sequence_id = sequence.id - is_partner_available = fields.Boolean( - string='Available for Partners', help="If checked, users with" - " specific access right will have the possibility to generate" - " barcodes with this pattern for the partners.") + # Custom Section + @api.model + def _prepare_sequence(self, rule): + return { + 'name': _('Sequence - %s') % rule.name, + 'padding': rule.padding, + } diff --git a/barcodes_generate/security/res_groups.xml b/barcodes_generate/security/res_groups.xml index a9522bde..0698f551 100644 --- a/barcodes_generate/security/res_groups.xml +++ b/barcodes_generate/security/res_groups.xml @@ -8,14 +8,8 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - - Generate Barcodes for Products - - - - - Generate Barcodes for Partners - + + Generate Barcodes diff --git a/barcodes_generate/views/view_barcode_rule.xml b/barcodes_generate/views/view_barcode_rule.xml index 98e39484..c1b3853d 100644 --- a/barcodes_generate/views/view_barcode_rule.xml +++ b/barcodes_generate/views/view_barcode_rule.xml @@ -12,9 +12,19 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). barcode.rule - - - + + + + + + + +