diff --git a/sequence_python/README.rst b/sequence_python/README.rst new file mode 100644 index 00000000000..a1bd209a16b --- /dev/null +++ b/sequence_python/README.rst @@ -0,0 +1,125 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +=============================== +Sequence from Python expression +=============================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:2577b1699eaf32186615d1426d69a461c185cd70423a76700e336c09cf5c7056 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/19.0/sequence_python + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-19-0/server-tools-19-0-sequence_python + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to generate a sequence by a Python formula +expression. + +Besides common Python functions and operators, it provides several +functions like 'random' and 'uuid' in the expression, as well as +variables such as the next number in the sequence. These are also listed +next to the input field on the sequence form view. + +If you want to add more variables for use in the expression, you can +extend this module. + +Use cases for this module could be: + +- You want to generate alphanumeric numbering +- You want to apply some math to the number to prevent customers from + knowing their place in the sequence +- You want to use UUID + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +- Go to the form view of an ir.sequence record +- Go to the Python tab +- Enable the 'Use Python' checkbox +- Change the default 'number' expression to something more fancy. + +Examples: + +.. code:: python + + # To separate the Odoo-generated number with hyphens eg. 0-0-0-0-1 + '-'.join(number_padded) + + # To have an UUID as the sequence value + uuid.uuid4().hex + + # To use an 8-digit binary number + '{0:#010b}'.format(number + 300)[2:] + +And so on. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Sunflower IT + +Contributors +------------ + +- Tom Blauwendraat +- Francesco Apruzzese +- `Heliconia Solutions Pvt. Ltd. `__ + + - Bhavesh Heliconia + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sequence_python/__init__.py b/sequence_python/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/sequence_python/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sequence_python/__manifest__.py b/sequence_python/__manifest__.py new file mode 100644 index 00000000000..9e557596de5 --- /dev/null +++ b/sequence_python/__manifest__.py @@ -0,0 +1,13 @@ +# Copyright 2020 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Sequence from Python expression", + "summary": """Calculate a sequence number from a Python expression""", + "author": "Sunflower IT,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-tools", + "version": "19.0.1.0.0", + "license": "AGPL-3", + "category": "Generic Modules", + "depends": ["base"], + "data": ["views/ir_sequence.xml"], +} diff --git a/sequence_python/i18n/ca.po b/sequence_python/i18n/ca.po new file mode 100644 index 00000000000..904e5b917f3 --- /dev/null +++ b/sequence_python/i18n/ca.po @@ -0,0 +1,129 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sequence_python +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number: The next number of the sequence (integer)" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"number_padded: Padded string of the next number of the sequence" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"random: The Python random module, eg. to use " +"random.randint(0, 9)" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "sequence: Odoo record of the sequence being used" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"string: The Python string module, eg. to use " +"random.choices(string.ascii_letters + string.digits, k=4)" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"uuid: The Python uuid module, eg. to use " +"uuid.uuid4()" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Aside from this, you may use several" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__display_name +msgid "Display Name" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Help with Python expressions" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__id +msgid "ID" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence____last_update +msgid "Last Modified on" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code_preview +msgid "Preview" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Python Code" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code +msgid "Python expression" +msgstr "" + +#. module: sequence_python +#: model:ir.model,name:sequence_python.model_ir_sequence +msgid "Sequence" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__smart_search +msgid "Smart Search" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"The expression you type here will be evaluated as the next number. The " +"following variables can be used:" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__use_python_code +msgid "Use Python" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,help:sequence_python.field_ir_sequence__python_code +msgid "Write Python code that generates the sequence body." +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "builtin Python functions" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number" +msgstr "" diff --git a/sequence_python/i18n/es_AR.po b/sequence_python/i18n/es_AR.po new file mode 100644 index 00000000000..22bc069b2bd --- /dev/null +++ b/sequence_python/i18n/es_AR.po @@ -0,0 +1,141 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sequence_python +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-09-05 01:07+0000\n" +"Last-Translator: Ignacio Buioli \n" +"Language-Team: none\n" +"Language: es_AR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number: The next number of the sequence (integer)" +msgstr "number: El siguiente número de la secuencia (entero)" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"number_padded: Padded string of the next number of the sequence" +msgstr "" +"number_padded: Relleno del siguiente número de la secuencia" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"random: The Python random module, eg. to use " +"random.randint(0, 9)" +msgstr "" +"random: El módulo aleatorio de Python, ej. para utilizar " +"random.randint(0, 9)" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "sequence: Odoo record of the sequence being used" +msgstr "" +"sequence: Registro Odoo de la secuencia que se está utilizando" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"string: The Python string module, eg. to use " +"random.choices(string.ascii_letters + string.digits, k=4)" +msgstr "" +"string: El módulo string de Python, ej. para utilizar " +"random.choices(string.ascii_letters + string.digits, k=4)" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"uuid: The Python uuid module, eg. to use " +"uuid.uuid4()" +msgstr "" +"uuid: El módulo uuid de Python, ej. para utilizar uuid." +"uuid4()" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Aside from this, you may use several" +msgstr "Aparte de esto, puede utilizar varios" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Help with Python expressions" +msgstr "Ayuda con expresiones Python" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__id +msgid "ID" +msgstr "ID" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code_preview +msgid "Preview" +msgstr "Previsualización" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Python Code" +msgstr "Código de Python" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code +msgid "Python expression" +msgstr "Expresión de Python" + +#. module: sequence_python +#: model:ir.model,name:sequence_python.model_ir_sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__smart_search +msgid "Smart Search" +msgstr "Búsqueda Inteligente" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"The expression you type here will be evaluated as the next number. The " +"following variables can be used:" +msgstr "" +"La expresión que escriba acá se evaluará como el siguiente número. Se pueden " +"utilizar las siguientes variables:" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__use_python_code +msgid "Use Python" +msgstr "Usar Python" + +#. module: sequence_python +#: model:ir.model.fields,help:sequence_python.field_ir_sequence__python_code +msgid "Write Python code that generates the sequence body." +msgstr "Escriba el código de Python que genera el cuerpo de la secuencia." + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "builtin Python functions" +msgstr "Funciones de Python incorporadas" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number" +msgstr "number" diff --git a/sequence_python/i18n/it.po b/sequence_python/i18n/it.po new file mode 100644 index 00000000000..b524452beb5 --- /dev/null +++ b/sequence_python/i18n/it.po @@ -0,0 +1,126 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sequence_python +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-11-13 10:06+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number: The next number of the sequence (integer)" +msgstr "number: il numero successivo della sequenza (intero)" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"number_padded: Padded string of the next number of the sequence" +msgstr "" +"number_padded: stringa riempita del numero successivo della " +"sequenza" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"random: The Python random module, eg. to use " +"random.randint(0, 9)" +msgstr "" +"random: il modulo random Python, es. da usare random." +"randint(0, 9)" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "sequence: Odoo record of the sequence being used" +msgstr "sequence: record Odoo della sequenza in uso" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"string: The Python string module, eg. to use " +"random.choices(string.ascii_letters + string.digits, k=4)" +msgstr "" +"string: il modulo stringa Python, es.da usare random." +"choices(string.ascii_letters + string.digits, k=4)" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"uuid: The Python uuid module, eg. to use " +"uuid.uuid4()" +msgstr "" +"uuid: il modulo uuid Python da usare, es. da usare uuid." +"uuid4()" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Aside from this, you may use several" +msgstr "Oltre a questo, si possono usare diversi" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Help with Python expressions" +msgstr "Aiuto con espressioni Python" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code_preview +msgid "Preview" +msgstr "Anteprima" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Python Code" +msgstr "Codice Python" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code +msgid "Python expression" +msgstr "Espressione Python" + +#. module: sequence_python +#: model:ir.model,name:sequence_python.model_ir_sequence +msgid "Sequence" +msgstr "Sequenza" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__smart_search +msgid "Smart Search" +msgstr "Ricerca intelligente" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"The expression you type here will be evaluated as the next number. The " +"following variables can be used:" +msgstr "" +"L'espressione inserita qui verrà valutata come il numero successivo. Si " +"possono usare le seguenti variabili:" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__use_python_code +msgid "Use Python" +msgstr "Usa Python" + +#. module: sequence_python +#: model:ir.model.fields,help:sequence_python.field_ir_sequence__python_code +msgid "Write Python code that generates the sequence body." +msgstr "Scrivere il codice Python che genera il corpo della sequenza." + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "builtin Python functions" +msgstr "funzioni Python incorporate" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number" +msgstr "numero" diff --git a/sequence_python/i18n/sequence_python.pot b/sequence_python/i18n/sequence_python.pot new file mode 100644 index 00000000000..23379e65e50 --- /dev/null +++ b/sequence_python/i18n/sequence_python.pot @@ -0,0 +1,113 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sequence_python +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number: The next number of the sequence (integer)" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"number_padded: Padded string of the next number of the sequence" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"random: The Python random module, eg. to use " +"random.randint(0, 9)" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "sequence: Odoo record of the sequence being used" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"string: The Python string module, eg. to use " +"random.choices(string.ascii_letters + string.digits, k=4)" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"uuid: The Python uuid module, eg. to use " +"uuid.uuid4()" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Aside from this, you may use several" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Help with Python expressions" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code_preview +msgid "Preview" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "Python Code" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__python_code +msgid "Python expression" +msgstr "" + +#. module: sequence_python +#: model:ir.model,name:sequence_python.model_ir_sequence +msgid "Sequence" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__smart_search +msgid "Smart Search" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "" +"The expression you type here will be evaluated as the next number. The " +"following variables can be used:" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,field_description:sequence_python.field_ir_sequence__use_python_code +msgid "Use Python" +msgstr "" + +#. module: sequence_python +#: model:ir.model.fields,help:sequence_python.field_ir_sequence__python_code +msgid "Write Python code that generates the sequence body." +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "builtin Python functions" +msgstr "" + +#. module: sequence_python +#: model_terms:ir.ui.view,arch_db:sequence_python.sequence_view +msgid "number" +msgstr "" diff --git a/sequence_python/models/__init__.py b/sequence_python/models/__init__.py new file mode 100644 index 00000000000..5b015772ab2 --- /dev/null +++ b/sequence_python/models/__init__.py @@ -0,0 +1 @@ +from . import ir_sequence diff --git a/sequence_python/models/ir_sequence.py b/sequence_python/models/ir_sequence.py new file mode 100644 index 00000000000..061ae21670e --- /dev/null +++ b/sequence_python/models/ir_sequence.py @@ -0,0 +1,90 @@ +# Copyright 2020 Sunflower IT () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging +import random +import string +import uuid +from inspect import getmembers, isclass, isfunction + +from odoo import fields, models +from odoo.tools.safe_eval import safe_eval, wrap_module + +_logger = logging.getLogger(__name__) + + +# Pre-compute UUID safe members to avoid recomputation on each call +_UUID_SAFE_MEMBERS = [ + name for name, _ in getmembers(uuid, lambda m: isfunction(m) or isclass(m)) +] + + +DEFAULT_PYTHON_CODE = "number_padded" + + +class IrSequence(models.Model): + """ + Inherit standard ir.sequence to let the possibility of using a Python formula + to calculate the sequence from input variables such as the sequence number. + This allows obfuscation of the order in which sequences are given out, + but can also be used for any other purpose. + """ + + _inherit = "ir.sequence" + + # Python code + use_python_code = fields.Boolean(string="Use Python", default=False) + python_code = fields.Text( + string="Python expression", + default=DEFAULT_PYTHON_CODE, + help="Write Python code that generates the sequence body.", + ) + python_code_preview = fields.Char("Preview", compute="_compute_python_code_preview") + + def _get_python_eval_context(self, number_next): + """ + Get the eval context to evaluate the Python code with. + The format is (variable name, description, value) + You can inherit this in your custom module. + :return: dict + """ + wrap_random = wrap_module(random, random.__all__) + wrap_uuid = wrap_module(uuid, _UUID_SAFE_MEMBERS) + wrap_string = wrap_module(string, string.__all__) + if isinstance(number_next, tuple): + number_next = number_next[0] + return { + "number": number_next, + "number_padded": f"{number_next:0{self.padding}d}", + "sequence": self, + "random": wrap_random, + "uuid": wrap_uuid, + "string": wrap_string, + } + + def _get_python_value(self, number_next): + """ + Use the python formula to get the value. + :return: string + """ + eval_context = self._get_python_eval_context(number_next) + return safe_eval(self.python_code.strip(), eval_context) + + def _compute_python_code_preview(self): + for this in self: + try: + this.python_code_preview = self.get_next_char( + (self.number_next_actual,) + ) + except Exception as e: # noqa + this.python_code_preview = str(e) + + def get_next_char(self, number_next): + if self.use_python_code: + interpolated_prefix, interpolated_suffix = self._get_prefix_suffix() + result = self._get_python_value(number_next) + # Convert to string to prevent TypeError when concatenating with strings + # Only convert if it's not already a string to preserve original behavior + result_str = str(result) if not isinstance(result, str) else result + return interpolated_prefix + result_str + interpolated_suffix + else: + return super().get_next_char(number_next) diff --git a/sequence_python/pyproject.toml b/sequence_python/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/sequence_python/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sequence_python/readme/CONTRIBUTORS.md b/sequence_python/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..3c777dad2d6 --- /dev/null +++ b/sequence_python/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- Tom Blauwendraat \ +- Francesco Apruzzese \ +- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) + - Bhavesh Heliconia diff --git a/sequence_python/readme/DESCRIPTION.md b/sequence_python/readme/DESCRIPTION.md new file mode 100644 index 00000000000..70419b92170 --- /dev/null +++ b/sequence_python/readme/DESCRIPTION.md @@ -0,0 +1,17 @@ +This module allows to generate a sequence by a Python formula +expression. + +Besides common Python functions and operators, it provides several +functions like 'random' and 'uuid' in the expression, as well as +variables such as the next number in the sequence. These are also listed +next to the input field on the sequence form view. + +If you want to add more variables for use in the expression, you can +extend this module. + +Use cases for this module could be: + +- You want to generate alphanumeric numbering +- You want to apply some math to the number to prevent customers from + knowing their place in the sequence +- You want to use UUID diff --git a/sequence_python/readme/USAGE.md b/sequence_python/readme/USAGE.md new file mode 100644 index 00000000000..88d75e19dc0 --- /dev/null +++ b/sequence_python/readme/USAGE.md @@ -0,0 +1,21 @@ +To use this module, you need to: + +- Go to the form view of an ir.sequence record +- Go to the Python tab +- Enable the 'Use Python' checkbox +- Change the default 'number' expression to something more fancy. + +Examples: + +``` python +# To separate the Odoo-generated number with hyphens eg. 0-0-0-0-1 +'-'.join(number_padded) + +# To have an UUID as the sequence value +uuid.uuid4().hex + +# To use an 8-digit binary number +'{0:#010b}'.format(number + 300)[2:] +``` + +And so on. diff --git a/sequence_python/static/description/icon.png b/sequence_python/static/description/icon.png new file mode 100644 index 00000000000..1dcc49c24f3 Binary files /dev/null and b/sequence_python/static/description/icon.png differ diff --git a/sequence_python/static/description/index.html b/sequence_python/static/description/index.html new file mode 100644 index 00000000000..fa1bbaa7ec4 --- /dev/null +++ b/sequence_python/static/description/index.html @@ -0,0 +1,471 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Sequence from Python expression

+ +

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

+

This module allows to generate a sequence by a Python formula +expression.

+

Besides common Python functions and operators, it provides several +functions like ‘random’ and ‘uuid’ in the expression, as well as +variables such as the next number in the sequence. These are also listed +next to the input field on the sequence form view.

+

If you want to add more variables for use in the expression, you can +extend this module.

+

Use cases for this module could be:

+
    +
  • You want to generate alphanumeric numbering
  • +
  • You want to apply some math to the number to prevent customers from +knowing their place in the sequence
  • +
  • You want to use UUID
  • +
+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  • Go to the form view of an ir.sequence record
  • +
  • Go to the Python tab
  • +
  • Enable the ‘Use Python’ checkbox
  • +
  • Change the default ‘number’ expression to something more fancy.
  • +
+

Examples:

+
+# To separate the Odoo-generated number with hyphens eg. 0-0-0-0-1
+'-'.join(number_padded)
+
+# To have an UUID as the sequence value
+uuid.uuid4().hex
+
+# To use an 8-digit binary number
+'{0:#010b}'.format(number + 300)[2:]
+
+

And so on.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Sunflower IT
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/server-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/sequence_python/tests/__init__.py b/sequence_python/tests/__init__.py new file mode 100644 index 00000000000..b32ac6c6ab7 --- /dev/null +++ b/sequence_python/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2020 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_ir_sequence diff --git a/sequence_python/tests/test_ir_sequence.py b/sequence_python/tests/test_ir_sequence.py new file mode 100644 index 00000000000..0de683d764c --- /dev/null +++ b/sequence_python/tests/test_ir_sequence.py @@ -0,0 +1,63 @@ +# Copyright 2020 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase + + +class TestIrSequence(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.Sequence = cls.env["ir.sequence"] + cls.sequence = cls.Sequence.create( + { + "name": "Test sequence", + "implementation": "standard", + "code": "test.python.sequence", + "prefix": "A", + "padding": 2, + "number_next": 1, + "number_increment": 1, + "company_id": False, + "use_python_code": True, + "python_code": "'B' + number_padded + 'C'", + } + ) + + def test_standard_sequence(self): + self.assertEqual(self.sequence.python_code_preview, "AB01C") + next_number = self.sequence._next() + self.assertEqual(next_number, "AB01C") + next_number = self.sequence._next() + self.assertEqual(next_number, "AB02C") + + def test_nogap_sequence(self): + self.sequence.write(dict(implementation="no_gap")) + next_number = self.sequence._next() + self.assertEqual(next_number, "AB01C") + next_number = self.sequence._next() + self.assertEqual(next_number, "AB02C") + + def test_python_code_returns_int(self): + # Create a separate sequence for this test to avoid interference + seq = self.Sequence.create( + { + "name": "Test sequence for int", + "implementation": "standard", + "code": "test.python.sequence.int", + "prefix": "A", + "padding": 0, # No padding for this test + "number_next": 1, + "number_increment": 1, + "company_id": False, + "use_python_code": True, + "python_code": "number", # This returns an integer + } + ) + self.assertEqual(seq.python_code_preview, "A1") + next_number = seq._next() + self.assertEqual(next_number, "A1") + + def test_python_code_with_error(self): + self.sequence.write({"python_code": "1 / 0"}) + self.assertIn("division by zero", self.sequence.python_code_preview) diff --git a/sequence_python/views/ir_sequence.xml b/sequence_python/views/ir_sequence.xml new file mode 100644 index 00000000000..228efcb8dd1 --- /dev/null +++ b/sequence_python/views/ir_sequence.xml @@ -0,0 +1,52 @@ + + + + ir.sequence + + + + + + + + + +
+

Help with Python expressions

+

The expression you type here will be evaluated as the next number. The following variables can be used:

+
    +
  • number: The next number of the sequence (integer)
  • +
  • number_padded: Padded string of the next number of the sequence
  • +
  • sequence: Odoo record of the sequence being used
  • +
  • uuid: The Python uuid module, eg. to use uuid.uuid4()
  • +
  • random: The Python random module, eg. to use random.randint(0, 9)
  • +
  • string: The Python string module, eg. to use random.choices(string.ascii_letters + string.digits, k=4)
  • +
+

Aside from this, you may use several builtin Python functions

+
+
+
+
+
+