diff --git a/project_sequence/README.rst b/project_sequence/README.rst new file mode 100644 index 0000000000..badb572717 --- /dev/null +++ b/project_sequence/README.rst @@ -0,0 +1,128 @@ +================ +Project Sequence +================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:254873ec5944d34982a952bdfcad35ec3a7e1204a6dc3b547b53da8b252b12a6 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github + :target: https://github.com/OCA/project/tree/17.0/project_sequence + :alt: OCA/project +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/project-17-0/project-17-0-project_sequence + :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/project&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Add a sequence field to projects, filled automatically and add a code +sequence filter in tree view project. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To change the project display name pattern, follow these steps: + +1. Go to *Project > Configuration > Settings*. + +2. Edit the *Project display name pattern* field. + + The default format is ``%(sequence_code)s - %(name)s``. You can use + those same placeholders to customize the pattern. + +Usage +===== + +To use this module, you need to: + +1. Go to the project icon. +2. Click the button "create" to create a new project +3. Fill in the field Project name and click the "create" button +4. Now in the Kanban view see the project name when you are created +5. Repeat this operation creating another project without the name. + +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 +------- + +* Moduon + +Contributors +------------ + +- Andrea Cattalani (`Moduon `__) +- Jairo Llopis (`Moduon `__) +- Nils Coenen +- `360ERP `__: + + - Andrea Stirpe + +Other credits +------------- + +The development of this module has been financially supported by: + +- Moduon + +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. + +.. |maintainer-yajo| image:: https://github.com/yajo.png?size=40px + :target: https://github.com/yajo + :alt: yajo +.. |maintainer-anddago78| image:: https://github.com/anddago78.png?size=40px + :target: https://github.com/anddago78 + :alt: anddago78 + +Current `maintainers `__: + +|maintainer-yajo| |maintainer-anddago78| + +This module is part of the `OCA/project `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/project_sequence/__init__.py b/project_sequence/__init__.py new file mode 100644 index 0000000000..aee8895e7a --- /dev/null +++ b/project_sequence/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/project_sequence/__manifest__.py b/project_sequence/__manifest__.py new file mode 100644 index 0000000000..20d5f0a8ad --- /dev/null +++ b/project_sequence/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) + +{ + "name": "Project Sequence", + "summary": "Add a sequence field to projects, filled automatically", + "version": "17.0.1.0.0", + "development_status": "Alpha", + "category": "Services/Project", + "website": "https://github.com/OCA/project", + "author": "Moduon, Odoo Community Association (OCA)", + "maintainers": ["yajo", "anddago78"], + "license": "LGPL-3", + "application": False, + "installable": True, + "depends": ["project"], + "data": [ + "data/ir_sequence.xml", + "views/project_project.xml", + "wizards/res_config_settings_view.xml", + ], +} diff --git a/project_sequence/data/ir_sequence.xml b/project_sequence/data/ir_sequence.xml new file mode 100644 index 0000000000..935d009508 --- /dev/null +++ b/project_sequence/data/ir_sequence.xml @@ -0,0 +1,13 @@ + + + + + Project sequence + project.sequence + %(range_y)s- + True + 5 + + + diff --git a/project_sequence/i18n/de.po b/project_sequence/i18n/de.po new file mode 100644 index 0000000000..b594d36575 --- /dev/null +++ b/project_sequence/i18n/de.po @@ -0,0 +1,75 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_sequence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-08-08 15:08+0000\n" +"Last-Translator: Nils Coenen \n" +"Language-Team: none\n" +"Language: de\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.17\n" + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_res_config_settings +msgid "Config Settings" +msgstr "Konfigurationseinstellungen" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__name +msgid "Name" +msgstr "Name" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_project_project__name +msgid "" +"Name of your project. It can be anything you want e.g. the name of a " +"customer or a service." +msgstr "" +"Name Ihres Projekts. Es kann alles sein, was Sie wollen, z.B. der Name eines " +"Kunden oder einer Dienstleistung." + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_project_project +msgid "Project" +msgstr "Projekt" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "Project Display Name Pattern" +msgstr "Muster für den Projektanzeigenamen" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_name_required +msgid "Project name is required" +msgstr "Projektname ist erforderlich" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__sequence_code +msgid "Sequence Code" +msgstr "Sequenzcode" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_sequence_code_unique +msgid "Sequence code must be unique" +msgstr "Der Sequenzcode muss eindeutig sein" + +#. module: project_sequence +#: model_terms:ir.ui.view,arch_db:project_sequence.project_sequence_form_view +msgid "Sequence code:" +msgstr "Sequenzcode:" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "" +"Use %(sequence_code)s and %(name)s to include the sequence code and the name " +"of the project in the display name." +msgstr "" +"Verwenden Sie %(sequence_code)s und %(name)s, um den Sequenzcode und den " +"Namen des Projekts in den Anzeigenamen aufzunehmen." diff --git a/project_sequence/i18n/es.po b/project_sequence/i18n/es.po new file mode 100644 index 0000000000..f370257f04 --- /dev/null +++ b/project_sequence/i18n/es.po @@ -0,0 +1,88 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_sequence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-04-10 10:29+0000\n" +"PO-Revision-Date: 2023-09-03 13:36+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_res_config_settings +msgid "Config Settings" +msgstr "Configuración de Ajustes" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__name +msgid "Name" +msgstr "Nombre" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_project_project__name +msgid "" +"Name of your project. It can be anything you want e.g. the name of a " +"customer or a service." +msgstr "" +"Nombre de su proyecto. Puede ser cualquier cosa, por ejemplo, el nombre de " +"un cliente o de un servicio." + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_project_project +msgid "Project" +msgstr "Proyecto" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "Project Display Name Pattern" +msgstr "Patrón de nombre para mostrar del proyecto" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_name_required +msgid "Project name is required" +msgstr "El nombre del proyecto es obligatorio" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__sequence_code +msgid "Sequence Code" +msgstr "Código secuencial" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_sequence_code_unique +msgid "Sequence code must be unique" +msgstr "El código secuencial debe ser único" + +#. module: project_sequence +#: model_terms:ir.ui.view,arch_db:project_sequence.project_sequence_form_view +msgid "Sequence code:" +msgstr "Código secuencial:" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "" +"Use %(sequence_code)s and %(name)s to include the sequence code and the name " +"of the project in the display name." +msgstr "" +"Utilice %(sequence_code)s y %(name)s para incluir el código de secuencia y " +"el nombre del proyecto en el nombre de visualización." + +#~ msgid "Display Name" +#~ msgstr "Nombre" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Última modificación el" + +#~ msgid "Code" +#~ msgstr "Código" diff --git a/project_sequence/i18n/it.po b/project_sequence/i18n/it.po new file mode 100644 index 0000000000..96244d827f --- /dev/null +++ b/project_sequence/i18n/it.po @@ -0,0 +1,78 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_sequence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-07-31 10:11+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 4.17\n" + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_res_config_settings +msgid "Config Settings" +msgstr "Impostazioni configurazione" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__name +msgid "Name" +msgstr "Nome" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_project_project__name +msgid "" +"Name of your project. It can be anything you want e.g. the name of a " +"customer or a service." +msgstr "" +"Nome del progetto. Può essere qualsiasi cosa es. il nome di un cliente o di " +"un servizio." + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_project_project +msgid "Project" +msgstr "Progetto" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "Project Display Name Pattern" +msgstr "Nome visualizzato schema progetto" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_name_required +msgid "Project name is required" +msgstr "È richiesto il nome progetto" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__sequence_code +msgid "Sequence Code" +msgstr "Codice sequenza" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_sequence_code_unique +msgid "Sequence code must be unique" +msgstr "Il codice sequenza deve essere univoco" + +#. module: project_sequence +#: model_terms:ir.ui.view,arch_db:project_sequence.project_sequence_form_view +msgid "Sequence code:" +msgstr "Codice sequenza:" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "" +"Use %(sequence_code)s and %(name)s to include the sequence code and the name " +"of the project in the display name." +msgstr "" +"Utilizzare %(sequence_code)s e %(name)s per includere il codice sequenza e " +"il nome del progetto nel nome visualizzato." + +#~ msgid "WBS element" +#~ msgstr "Elemento WBS" diff --git a/project_sequence/i18n/nl.po b/project_sequence/i18n/nl.po new file mode 100644 index 0000000000..45ecd255b4 --- /dev/null +++ b/project_sequence/i18n/nl.po @@ -0,0 +1,75 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_sequence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-09 02:52+0000\n" +"Last-Translator: \"Jan Tapper [Onestein]\" \n" +"Language-Team: none\n" +"Language: nl\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.17\n" + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_res_config_settings +msgid "Config Settings" +msgstr "Configuratie-instellingen" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__name +msgid "Name" +msgstr "Naam" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_project_project__name +msgid "" +"Name of your project. It can be anything you want e.g. the name of a " +"customer or a service." +msgstr "" +"Naam van uw project. Het kan alles zijn wat je wilt, b.v. de naam van een " +"klant of dienst." + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_project_project +msgid "Project" +msgstr "" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "Project Display Name Pattern" +msgstr "Patroon weergavenaam project" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_name_required +msgid "Project name is required" +msgstr "Projectnaam is vereist" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__sequence_code +msgid "Sequence Code" +msgstr "Sequentiecode" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_sequence_code_unique +msgid "Sequence code must be unique" +msgstr "De sequentiecode moet uniek zijn" + +#. module: project_sequence +#: model_terms:ir.ui.view,arch_db:project_sequence.project_sequence_form_view +msgid "Sequence code:" +msgstr "Sequentiecode:" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "" +"Use %(sequence_code)s and %(name)s to include the sequence code and the name " +"of the project in the display name." +msgstr "" +"Gebruik %(sequence_code)s en %(name)s om de sequentiecode en de naam van het " +"project op te nemen in de weergavenaam." diff --git a/project_sequence/i18n/project_sequence.pot b/project_sequence/i18n/project_sequence.pot new file mode 100644 index 0000000000..f551dcca93 --- /dev/null +++ b/project_sequence/i18n/project_sequence.pot @@ -0,0 +1,68 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_sequence +# +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: project_sequence +#: model:ir.model,name:project_sequence.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__name +msgid "Name" +msgstr "" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_project_project__name +msgid "" +"Name of your project. It can be anything you want e.g. the name of a " +"customer or a service." +msgstr "" + +#. module: project_sequence +#: model:ir.model,name:project_sequence.model_project_project +msgid "Project" +msgstr "" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "Project Display Name Pattern" +msgstr "" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_name_required +msgid "Project name is required" +msgstr "" + +#. module: project_sequence +#: model:ir.model.fields,field_description:project_sequence.field_project_project__sequence_code +msgid "Sequence Code" +msgstr "" + +#. module: project_sequence +#: model:ir.model.constraint,message:project_sequence.constraint_project_project_sequence_code_unique +msgid "Sequence code must be unique" +msgstr "" + +#. module: project_sequence +#: model_terms:ir.ui.view,arch_db:project_sequence.project_sequence_form_view +msgid "Sequence code:" +msgstr "" + +#. module: project_sequence +#: model:ir.model.fields,help:project_sequence.field_res_config_settings__project_display_name_pattern +msgid "" +"Use %(sequence_code)s and %(name)s to include the sequence code and the name" +" of the project in the display name." +msgstr "" diff --git a/project_sequence/models/__init__.py b/project_sequence/models/__init__.py new file mode 100644 index 0000000000..56545d0d4f --- /dev/null +++ b/project_sequence/models/__init__.py @@ -0,0 +1 @@ +from . import project_project diff --git a/project_sequence/models/project_project.py b/project_sequence/models/project_project.py new file mode 100644 index 0000000000..75763c8559 --- /dev/null +++ b/project_sequence/models/project_project.py @@ -0,0 +1,96 @@ +# Copyright 2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) + + +from odoo import api, fields, models + + +class ProjectProject(models.Model): + _inherit = "project.project" + _sql_constraints = [ + # Ensure compatibility with other modules that always expect a value in name + ("name_required", "CHECK(name IS NOT NULL)", "Project name is required"), + ( + "sequence_code_unique", + "UNIQUE(sequence_code)", + "Sequence code must be unique", + ), + ] + + sequence_code = fields.Char( + copy=False, + readonly=True, + ) + name = fields.Char( + # We actually require it with the SQL constraint, but it is disabled + # here to let users create/write projects without name, and let this module + # add a default name if needed + required=False, + ) + + def _sync_analytic_account_name(self): + """Set analytic account name equal to project's display name.""" + for rec in self: + if not rec.analytic_account_id: + continue + rec.analytic_account_id.name = rec.display_name + + @api.depends("sequence_code", "name") + def _compute_display_name(self): + res = super()._compute_display_name() + sequence_pattern = ( + self.env["ir.config_parameter"] + .sudo() + .get_param( + "project_sequence.display_name_pattern", + default="%(sequence_code)s - %(name)s", + ) + ) + for project in self.filtered( + lambda pr: pr.sequence_code and pr.sequence_code != pr.name + ): + project.display_name = sequence_pattern % { + "name": project.name, + "sequence_code": project.sequence_code, + } + return res + + @api.model + def name_search(self, name="", args=None, operator="ilike", limit=100): + """Allow searching by sequence code by default.""" + # Do not add any domain when user just clicked on search widget + if not (name == "" and operator == "ilike"): + # The dangling | is needed to combine with the domain added by super() + args = (args or []) + ["|", ("sequence_code", operator, name)] + return super().name_search(name, args, operator, limit) + + @api.model_create_multi + def create(self, vals_list): + """Apply sequence code and a default name if not set.""" + # It is important to set sequence_code before calling super() because + # other modules such as hr_timesheet expect the name to always have a value + for vals in vals_list: + if "sequence_code" not in vals: + vals["sequence_code"] = self.env["ir.sequence"].next_by_code( + "project.sequence" + ) + if not vals.get("name"): + vals["name"] = vals["sequence_code"] + res = super().create(vals_list) + # The analytic account is created with just the project name, but + # it is more useful to let it contain the project sequence too + res._sync_analytic_account_name() + return res + + def write(self, vals): + """Sync name and analytic account name when name is changed.""" + # If name isn't changing, nothing special to do + if "name" not in vals and "sequence_name" not in vals: + return super().write(vals) + # When changing name, we need to update the analytic account name too + for one in self: + sequence_code = vals.get("sequence_code", one.sequence_code) + name = vals.get("name") or sequence_code + super().write(dict(vals, name=name)) + self._sync_analytic_account_name() + return True diff --git a/project_sequence/pyproject.toml b/project_sequence/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/project_sequence/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/project_sequence/readme/CONFIGURE.md b/project_sequence/readme/CONFIGURE.md new file mode 100644 index 0000000000..b187d8ca02 --- /dev/null +++ b/project_sequence/readme/CONFIGURE.md @@ -0,0 +1,8 @@ +To change the project display name pattern, follow these steps: + +1. Go to *Project \> Configuration \> Settings*. + +2. Edit the *Project display name pattern* field. + + The default format is `%(sequence_code)s - %(name)s`. You can use + those same placeholders to customize the pattern. diff --git a/project_sequence/readme/CONTRIBUTORS.md b/project_sequence/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..ae5e5de7df --- /dev/null +++ b/project_sequence/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Andrea Cattalani ([Moduon](https://www.moduon.team/)) +- Jairo Llopis ([Moduon](https://www.moduon.team/)) +- Nils Coenen \<\> +- [360ERP](https://www.360erp.com): + - Andrea Stirpe diff --git a/project_sequence/readme/CREDITS.md b/project_sequence/readme/CREDITS.md new file mode 100644 index 0000000000..af32fa1208 --- /dev/null +++ b/project_sequence/readme/CREDITS.md @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +- Moduon diff --git a/project_sequence/readme/DESCRIPTION.md b/project_sequence/readme/DESCRIPTION.md new file mode 100644 index 0000000000..738b4c2e33 --- /dev/null +++ b/project_sequence/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +Add a sequence field to projects, filled automatically and add a code +sequence filter in tree view project. diff --git a/project_sequence/readme/USAGE.md b/project_sequence/readme/USAGE.md new file mode 100644 index 0000000000..b34bf1b39e --- /dev/null +++ b/project_sequence/readme/USAGE.md @@ -0,0 +1,7 @@ +To use this module, you need to: + +1. Go to the project icon. +2. Click the button "create" to create a new project +3. Fill in the field Project name and click the "create" button +4. Now in the Kanban view see the project name when you are created +5. Repeat this operation creating another project without the name. diff --git a/project_sequence/static/description/icon.png b/project_sequence/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/project_sequence/static/description/icon.png differ diff --git a/project_sequence/static/description/index.html b/project_sequence/static/description/index.html new file mode 100644 index 0000000000..83616f8d92 --- /dev/null +++ b/project_sequence/static/description/index.html @@ -0,0 +1,469 @@ + + + + + + +Project Sequence + + + +
+

Project Sequence

+ + +

Alpha License: LGPL-3 OCA/project Translate me on Weblate Try me on Runboat

+

Add a sequence field to projects, filled automatically and add a code +sequence filter in tree view project.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Configuration

+

To change the project display name pattern, follow these steps:

+
    +
  1. Go to Project > Configuration > Settings.

    +
  2. +
  3. Edit the Project display name pattern field.

    +

    The default format is %(sequence_code)s - %(name)s. You can use +those same placeholders to customize the pattern.

    +
  4. +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  1. Go to the project icon.
  2. +
  3. Click the button “create” to create a new project
  4. +
  5. Fill in the field Project name and click the “create” button
  6. +
  7. Now in the Kanban view see the project name when you are created
  8. +
  9. Repeat this operation creating another project without the name.
  10. +
+
+
+

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

+
    +
  • Moduon
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Moduon
  • +
+
+
+

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.

+

Current maintainers:

+

yajo anddago78

+

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

+

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

+
+
+
+ + diff --git a/project_sequence/tests/__init__.py b/project_sequence/tests/__init__.py new file mode 100644 index 0000000000..fd6799dc71 --- /dev/null +++ b/project_sequence/tests/__init__.py @@ -0,0 +1 @@ +from . import test_project_sequence diff --git a/project_sequence/tests/test_project_sequence.py b/project_sequence/tests/test_project_sequence.py new file mode 100644 index 0000000000..117573fa3b --- /dev/null +++ b/project_sequence/tests/test_project_sequence.py @@ -0,0 +1,184 @@ +# Copyright 2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +from freezegun import freeze_time +from psycopg2 import IntegrityError + +from odoo import fields +from odoo.tests.common import Form, TransactionCase, new_test_user, users +from odoo.tools import mute_logger + + +@freeze_time("2023-01-01 12:00:00") +class TestProjectSequence(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + new_test_user( + cls.env, + "manager", + "project.group_project_manager,analytic.group_analytic_accounting", + ) + cls.pjr_seq = cls.env.ref("project_sequence.seq_project_sequence") + cls.pjr_seq.date_range_ids.unlink() + default_plan_id = cls.env["account.analytic.plan"].search([], limit=1) + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "aaa", + "plan_id": default_plan_id.id, + "company_id": cls.env.company.id, + "create_uid": cls.env.uid, + "write_uid": cls.env.uid, + "create_date": fields.Datetime.now(), + "write_date": fields.Datetime.now(), + } + ) + + def setUp(self): + super().setUp() + self.pjr_seq._get_current_sequence().number_next = 11 + + @users("manager") + def test_sequence_after_creation(self): + """Sequence is applied only after project creation.""" + prj_f = Form(self.env["project.project"]) + self.assertFalse(prj_f.name) + self.assertFalse(prj_f.sequence_code) + proj = prj_f.save() + self.assertTrue(proj.sequence_code) + self.assertEqual(proj.name, proj.sequence_code) + self.assertEqual(proj.sequence_code, "23-00011") + self.assertEqual(proj.display_name, "23-00011") + + def test_analytic_account_after_creation_no_name(self): + """Project's analytic account is named like project's default name.""" + proj = self.env["project.project"].create( + {"analytic_account_id": self.analytic_account.id} + ) + self.assertEqual(proj.sequence_code, "23-00011") + self.assertEqual(proj.name, "23-00011") + self.assertEqual(proj.display_name, "23-00011") + self.assertEqual(proj.analytic_account_id.name, "23-00011") + + def test_analytic_account_after_creation_named(self): + """Project's analytic account is named like project's display name.""" + proj = self.env["project.project"].create( + {"name": "whatever", "analytic_account_id": self.analytic_account.id} + ) + self.assertEqual(proj.sequence_code, "23-00011") + self.assertEqual(proj.name, "whatever") + self.assertEqual(proj.display_name, "23-00011 - whatever") + self.assertEqual(proj.analytic_account_id.name, "23-00011 - whatever") + + @users("manager") + def test_sequence_copied_to_name_if_emptied(self): + """Sequence is copied to project name if user removes it.""" + proj = self.env["project.project"].create( + {"name": "whatever", "analytic_account_id": self.analytic_account.id} + ) + self.assertEqual(proj.name, "whatever") + self.assertEqual(proj.sequence_code, "23-00011") + self.assertEqual(proj.display_name, "23-00011 - whatever") + self.assertEqual(proj.analytic_account_id.name, "23-00011 - whatever") + with Form(proj) as prj_f: + prj_f.name = False + self.assertEqual(proj.name, "23-00011") + self.assertEqual(proj.sequence_code, "23-00011") + self.assertEqual(proj.display_name, "23-00011") + self.assertEqual(proj.analytic_account_id.name, "23-00011") + + @users("manager") + def test_sequence_not_copied_to_another_project(self): + """Sequence is not duplicated to another project.""" + proj1 = self.env["project.project"].create({"name": "whatever"}) + proj2 = proj1.copy() + self.assertEqual(proj1.sequence_code, "23-00011") + self.assertEqual(proj2.sequence_code, "23-00012") + + @users("manager") + @mute_logger("odoo.sql_db") + def test_sequence_unique(self): + """Sequence cannot have duplicates.""" + proj1 = self.env["project.project"].create({"name": "one"}) + self.assertEqual(proj1.sequence_code, "23-00011") + self.pjr_seq._get_current_sequence().number_next = 11 + with self.assertRaises(IntegrityError), self.env.cr.savepoint(): + proj1 = self.env["project.project"].create({"name": "two"}) + + @users("manager") + def test_project_without_sequence(self): + """Preexisting projects had no sequence, and they should display fine.""" + proj1 = self.env["project.project"].create( + {"name": "one", "sequence_code": False} + ) + self.assertEqual(proj1.display_name, "one") + self.assertFalse(proj1.sequence_code) + # Make sure that the sequence is not increased + proj2 = self.env["project.project"].create({"name": "two"}) + self.assertEqual(proj2.sequence_code, "23-00011") + self.assertEqual(proj2.display_name, "23-00011 - two") + + def test_custom_pattern(self): + """Display name pattern can be customized.""" + self.env["ir.config_parameter"].set_param( + "project_sequence.display_name_pattern", "%(name)s/%(sequence_code)s" + ) + proj = self.env["project.project"].create({"name": "one"}) + self.assertEqual(proj.display_name, "one/23-00011") + self.assertEqual(proj.sequence_code, "23-00011") + self.env["ir.config_parameter"].set_param( + "project_sequence.display_name_pattern", "%(name)s" + ) + proj = self.env["project.project"].create({"name": "two"}) + self.assertEqual(proj.display_name, "two") + self.assertEqual(proj.sequence_code, "23-00012") + self.env["ir.config_parameter"].set_param( + "project_sequence.display_name_pattern", "%(sequence_code)s" + ) + proj = self.env["project.project"].create({"name": "three"}) + self.assertEqual(proj.display_name, "23-00013") + self.assertEqual(proj.sequence_code, "23-00013") + + def test_sync_analytic_account_name(self): + """Set analytic account name equal to project's display name.""" + proj = self.env["project.project"].create({"name": "one"}) + default_plan_id = self.env["account.analytic.plan"].search([], limit=1) + analytic_account = self.env["account.analytic.account"].create( + { + "name": proj.display_name, + "plan_id": default_plan_id.id, + "company_id": self.env.company.id, + "create_uid": self.env.uid, + "write_uid": self.env.uid, + "create_date": fields.Datetime.now(), + "write_date": fields.Datetime.now(), + } + ) + proj.analytic_account_id = analytic_account + proj._sync_analytic_account_name() + self.assertEqual(proj.analytic_account_id.name, proj.display_name) + + # Test when analytic_account_id is not set + proj.analytic_account_id = False + proj._sync_analytic_account_name() + self.assertTrue(True) # Placeholder assertion to ensure the code execution + + def test_name_search(self): + """Allow searching by sequence code by default.""" + proj1 = self.env["project.project"].create({"name": "one"}) + self.assertEqual(proj1.sequence_code, "23-00011") + proj2 = self.env["project.project"].create({"name": "two"}) + self.assertEqual(proj2.sequence_code, "23-00012") + proj3 = self.env["project.project"].create({"name": "three"}) + self.assertEqual(proj3.sequence_code, "23-00013") + + # Search by name + results = self.env["project.project"].name_search("two") + self.assertIn((proj2.id, "23-00012 - two"), results) + self.assertNotIn((proj1.id, "23-00011 - one"), results) + self.assertNotIn((proj3.id, "23-00013 - three"), results) + + # Search by sequence code + results = self.env["project.project"].name_search("23-00012") + self.assertIn((proj2.id, "23-00012 - two"), results) + self.assertNotIn((proj1.id, "23-00011 - one"), results) + self.assertNotIn((proj3.id, "23-00013 - three"), results) diff --git a/project_sequence/views/project_project.xml b/project_sequence/views/project_project.xml new file mode 100644 index 0000000000..0b8f8ae2f6 --- /dev/null +++ b/project_sequence/views/project_project.xml @@ -0,0 +1,68 @@ + + + + + Project.sequence.project.edit + project.project + + + +
+
+
+
+
+
+
+ + Project_sequence_project_view + project.project + + + + + + + + + Project.sequence.project.kanban + project.project + + + + + + + 1 + + + + + + + + Project.sequence.project.view.search + project.project + + + + ['|', ('name', 'ilike', self), ('sequence_code', 'ilike', self)] + + + +
diff --git a/project_sequence/wizards/__init__.py b/project_sequence/wizards/__init__.py new file mode 100644 index 0000000000..0deb68c468 --- /dev/null +++ b/project_sequence/wizards/__init__.py @@ -0,0 +1 @@ +from . import res_config_settings diff --git a/project_sequence/wizards/res_config_settings.py b/project_sequence/wizards/res_config_settings.py new file mode 100644 index 0000000000..172c315e81 --- /dev/null +++ b/project_sequence/wizards/res_config_settings.py @@ -0,0 +1,18 @@ +# Copyright 2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) + + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + project_display_name_pattern = fields.Char( + config_parameter="project_sequence.display_name_pattern", + default="%(sequence_code)s - %(name)s", + help=( + "Use %(sequence_code)s and %(name)s to include the sequence code " + "and the name of the project in the display name." + ), + ) diff --git a/project_sequence/wizards/res_config_settings_view.xml b/project_sequence/wizards/res_config_settings_view.xml new file mode 100644 index 0000000000..67a935045d --- /dev/null +++ b/project_sequence/wizards/res_config_settings_view.xml @@ -0,0 +1,17 @@ + + + + + Configure project display name + res.config.settings + + + + + + + + + +