Skip to content

Commit

Permalink
Merge PR #1101 into 14.0
Browse files Browse the repository at this point in the history
Signed-off-by rafaelbn
  • Loading branch information
OCA-git-bot committed May 11, 2023
2 parents 15c0557 + 6ac7e10 commit 4b02952
Show file tree
Hide file tree
Showing 20 changed files with 426 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .copier-answers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ include_wkhtmltopdf: false
odoo_version: 14.0
org_name: Odoo Community Association (OCA)
org_slug: OCA
rebel_module_groups: []
rebel_module_groups:
- project_sequence
repo_description: 'TODO: add repo description.'
repo_name: project
repo_slug: project
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,18 @@ jobs:
matrix:
include:
- container: ghcr.io/oca/oca-ci/py3.6-odoo14.0:latest
include: "project_sequence"
makepot: "true"
name: test with Odoo
- container: ghcr.io/oca/oca-ci/py3.6-ocb14.0:latest
include: "project_sequence"
name: test with OCB
- container: ghcr.io/oca/oca-ci/py3.6-odoo14.0:latest
exclude: "project_sequence"
makepot: "true"
name: test with Odoo
- container: ghcr.io/oca/oca-ci/py3.6-ocb14.0:latest
exclude: "project_sequence"
name: test with OCB
services:
postgres:
Expand All @@ -49,6 +58,9 @@ jobs:
POSTGRES_DB: odoo
ports:
- 5432:5432
env:
INCLUDE: "${{ matrix.include }}"
EXCLUDE: "${{ matrix.exclude }}"
steps:
- uses: actions/checkout@v2
with:
Expand Down
35 changes: 35 additions & 0 deletions project_sequence/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
**This file is going to be generated by oca-gen-addon-readme.**

*Manual changes will be overwritten.*

Please provide content in the ``readme`` directory:

* **DESCRIPTION.rst** (required)
* INSTALL.rst (optional)
* CONFIGURE.rst (optional)
* **USAGE.rst** (optional, highly recommended)
* DEVELOP.rst (optional)
* ROADMAP.rst (optional)
* HISTORY.rst (optional, recommended)
* **CONTRIBUTORS.rst** (optional, highly recommended)
* CREDITS.rst (optional)

Content of this README will also be drawn from the addon manifest,
from keys such as name, authors, maintainers, development_status,
and license.

A good, one sentence summary in the manifest is also highly recommended.


Automatic changelog generation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`HISTORY.rst` can be auto generated using `towncrier <https://pypi.org/project/towncrier>`_.

Just put towncrier compatible changelog fragments into `readme/newsfragments`
and the changelog file will be automatically generated and updated when a new fragment is added.

Please refer to `towncrier` documentation to know more.

NOTE: the changelog will be automatically generated when using `/ocabot merge $option`.
If you need to run it manually, refer to `OCA/maintainer-tools README <https://github.com/OCA/maintainer-tools>`_.
1 change: 1 addition & 0 deletions project_sequence/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
21 changes: 21 additions & 0 deletions project_sequence/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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": "14.0.0.1.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",
],
}
13 changes: 13 additions & 0 deletions project_sequence/data/ir_sequence.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2023 Moduon Team S.L.
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) -->
<data noupdate="1">
<record id="seq_project_sequence" model="ir.sequence">
<field name="name">Project sequence</field>
<field name="code">project.sequence</field>
<field name="prefix">%(range_y)s-</field>
<field name="use_date_range">True</field>
<field name="padding">5</field>
<field name="company_id" eval="False" />
</record>
</data>
48 changes: 48 additions & 0 deletions project_sequence/i18n/es.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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-04-19 11:22+0200\n"
"Last-Translator: Andrea Cattalani <[email protected]>\n"
"Language-Team: \n"
"Language: es_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: Poedit 3.1.1\n"

#. module: project_sequence
#: model:ir.model.fields,field_description:project_sequence.field_project_project__code
msgid "Code"
msgstr "Código"

#. module: project_sequence
#: model:ir.model.fields,field_description:project_sequence.field_project_project__display_name
msgid "Display Name"
msgstr "Nombre"

#. module: project_sequence
#: model:ir.model.fields,field_description:project_sequence.field_project_project__id
msgid "ID"
msgstr "ID"

#. module: project_sequence
#: model:ir.model.fields,field_description:project_sequence.field_project_project____last_update
msgid "Last Modified on"
msgstr "Última modificación el"

#. 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,name:project_sequence.model_project_project
msgid "Project"
msgstr "Proyecto"
1 change: 1 addition & 0 deletions project_sequence/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import project_project
87 changes: 87 additions & 0 deletions project_sequence/models/project_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 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

def name_get(self):
"""Prefix name with sequence code if they are different."""
old_result = super().name_get()
result = []
for id_, name in old_result:
project = self.browse(id_)
if project.sequence_code and project.sequence_code != name:
name = "{} - {}".format(project.sequence_code, name)
result.append((id_, name))
return result

@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
2 changes: 2 additions & 0 deletions project_sequence/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* Andrea Cattalani (`Moduon <https://www.moduon.team/>`__)
* Jairo Llopis (`Moduon <https://www.moduon.team/>`__)
6 changes: 6 additions & 0 deletions project_sequence/readme/CREDITS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. This file is optional and contains additional credits, other than
authors, contributors, and maintainers.
The development of this module has been financially supported by:

* Moduon
4 changes: 4 additions & 0 deletions project_sequence/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. This file must be max 2-3 paragraphs, and is required.
It should explain *why* this module exists.
Add a sequence field to projects, filled automatically and add a code sequence filter in tree view project.
15 changes: 15 additions & 0 deletions project_sequence/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.. This file must be present. It contains the usage instructions
for end-users. As all other rst files included in the README,
it MUST NOT contain reStructuredText sections
only body text (paragraphs, lists, tables, etc). Should you need
a more elaborate structure to explain the addon, please create a
Sphinx documentation (which may include this file as a "quick start"
section).
To use this module, you need to:

#. Go to the project icon.
#. Click the button "create" to create a new project
#. Fill in the field Project name and click the "create" button
#. Now in the Kanban view see the project name when you are created
#. Repeat this operation creating another project without the name.
Binary file added project_sequence/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions project_sequence/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_project_sequence
108 changes: 108 additions & 0 deletions project_sequence/tests/test_project_sequence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# 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.tests.common import Form, SavepointCase, new_test_user, users
from odoo.tools import mute_logger


@freeze_time("2023-01-01 12:00:00")
class TestProjectSequence(SavepointCase):
@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()
cls.analytic_account = cls.env["account.analytic.account"].create(
{"name": "aaa"}
)

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")
Loading

0 comments on commit 4b02952

Please sign in to comment.