Skip to content

Commit c04a2de

Browse files
committed
[IMP] project_task_code: parameter for unicity constraint
SQL constraint may break runboat builds if data is inconsistent. The constraint is now checked in python and only if unique_task_code parameter is set to True.
1 parent 842ac68 commit c04a2de

13 files changed

+178
-56
lines changed

project_task_code/README.rst

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
Sequential Code for Tasks
33
=========================
44

5-
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
67
!! This file is generated by oca-gen-addon-readme !!
78
!! changes will be overwritten. !!
89
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:28fcd535608cf7941d0c7cae839e6e0a5b226e3bce1968cde58bdac4eaced570
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
912
1013
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
1114
:target: https://odoo-community.org/page/development-status
@@ -20,10 +23,10 @@ Sequential Code for Tasks
2023
:target: https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_task_code
2124
:alt: Translate me on Weblate
2225
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
23-
:target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/project&target_branch=16.0
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/project&target_branch=16.0
2427
:alt: Try me on Runboat
2528

26-
|badge1| |badge2| |badge3| |badge4| |badge5|
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
2730

2831
This module adds a sequential code for tasks.
2932

@@ -35,11 +38,8 @@ This module adds a sequential code for tasks.
3538
Configuration
3639
=============
3740

38-
To change the task code sequence, you must:
39-
40-
#. Activate the developer mode.
41-
#. Go to Settings > Technical > Sequences & Identifiers > Sequences.
42-
#. Click on "Task code" sequence to edit.
41+
To check the task code unicity (recommended) check "Unique Task Code"
42+
in the settings of the Project app.
4343

4444
Usage
4545
=====
@@ -55,7 +55,7 @@ Bug Tracker
5555

5656
Bugs are tracked on `GitHub Issues <https://github.com/OCA/project/issues>`_.
5757
In case of trouble, please check there if your issue has already been reported.
58-
If you spotted it first, help us smashing it by providing a detailed and welcomed
58+
If you spotted it first, help us to smash it by providing a detailed and welcomed
5959
`feedback <https://github.com/OCA/project/issues/new?body=module:%20project_task_code%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
6060

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

project_task_code/__manifest__.py

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"data": [
1818
"data/task_sequence.xml",
1919
"views/project_view.xml",
20+
"views/res_config_settings_view.xml",
2021
],
2122
"installable": True,
2223
"pre_init_hook": "pre_init_hook",

project_task_code/hooks.py

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
from odoo import SUPERUSER_ID, api
55

6+
# todo add parameter constraint in migration
7+
# add param to view
8+
69

710
def pre_init_hook(cr):
811
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2023 Coop IT Easy
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo import SUPERUSER_ID, api
5+
6+
7+
def migrate(cr, version):
8+
# In previous version of the module, the unicity constraint
9+
# was checked in sql. It is now checked with python if unique_task_code
10+
# is True.
11+
env = api.Environment(cr, SUPERUSER_ID, {})
12+
env["ir.config_parameter"].set_param("project_task_code.unique_task_code", True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2023 Coop IT Easy
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
5+
def migrate(cr, version):
6+
cr.execute(
7+
"""
8+
ALTER TABLE project_task
9+
DROP CONSTRAINT IF EXISTS
10+
project_task_project_task_unique_code
11+
"""
12+
)

project_task_code/models/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
22

33
from . import project_task
4+
from . import res_config_settings

project_task_code/models/project_task.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
33

44
from odoo import _, api, fields, models
5+
from odoo.exceptions import ValidationError
56

67

78
class ProjectTask(models.Model):
@@ -15,10 +16,6 @@ class ProjectTask(models.Model):
1516
copy=False,
1617
)
1718

18-
_sql_constraints = [
19-
("project_task_unique_code", "UNIQUE (code)", _("The code must be unique!")),
20-
]
21-
2219
@api.model_create_multi
2320
def create(self, vals_list):
2421
for vals in vals_list:
@@ -37,3 +34,28 @@ def name_get(self):
3734
name = "[{}] {}".format(rec.code, task[1])
3835
new_result.append((rec.id, name))
3936
return new_result
37+
38+
@api.constrains("code")
39+
def _check_project_task_unique_code(self):
40+
unique_task_code = bool(
41+
self.env["ir.config_parameter"]
42+
.sudo()
43+
.get_param("project_task_code.unique_task_code")
44+
)
45+
if not unique_task_code:
46+
return
47+
48+
for task in self:
49+
if (
50+
task.code
51+
and task.code != "/"
52+
and self.search(
53+
[
54+
("code", "=", task.code),
55+
("id", "!=", task.id),
56+
("company_id", "=", task.company_id.id),
57+
],
58+
limit=1,
59+
)
60+
):
61+
raise ValidationError(_("The code must be unique!"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2023 Coop IT Easy
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo import fields, models
5+
6+
7+
class ResConfigSettings(models.TransientModel):
8+
_inherit = "res.config.settings"
9+
10+
unique_task_code = fields.Boolean(
11+
default=False,
12+
config_parameter="project_task_code.unique_task_code",
13+
)
+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
To change the task code sequence, you must:
2-
3-
#. Activate the developer mode.
4-
#. Go to Settings > Technical > Sequences & Identifiers > Sequences.
5-
#. Click on "Task code" sequence to edit.
1+
To check the task code unicity (recommended) check "Unique Task Code"
2+
in the settings of the Project app.

project_task_code/static/description/index.html

+24-26
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
<?xml version="1.0" encoding="utf-8" ?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
33
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
44
<head>
55
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6-
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
6+
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
77
<title>Sequential Code for Tasks</title>
88
<style type="text/css">
99

1010
/*
1111
:Author: David Goodger ([email protected])
12-
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
12+
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
1313
:Copyright: This stylesheet has been placed in the public domain.
1414
1515
Default cascading style sheet for the HTML output of Docutils.
1616
17-
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
17+
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
1818
customize this style sheet.
1919
*/
2020

@@ -366,59 +366,57 @@ <h1 class="title">Sequential Code for Tasks</h1>
366366
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
367367
!! This file is generated by oca-gen-addon-readme !!
368368
!! changes will be overwritten. !!
369+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
370+
!! source digest: sha256:28fcd535608cf7941d0c7cae839e6e0a5b226e3bce1968cde58bdac4eaced570
369371
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
370-
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/project/tree/16.0/project_task_code"><img alt="OCA/project" src="https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_task_code"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runboat.odoo-community.org/webui/builds.html?repo=OCA/project&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
372+
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/project/tree/16.0/project_task_code"><img alt="OCA/project" src="https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_task_code"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/project&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
371373
<p>This module adds a sequential code for tasks.</p>
372374
<p><strong>Table of contents</strong></p>
373375
<div class="contents local topic" id="contents">
374376
<ul class="simple">
375-
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li>
376-
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
377-
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
378-
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
379-
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
380-
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
381-
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
377+
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
378+
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
379+
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
380+
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
381+
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
382+
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
383+
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
382384
</ul>
383385
</li>
384386
</ul>
385387
</div>
386388
<div class="section" id="configuration">
387-
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
388-
<p>To change the task code sequence, you must:</p>
389-
<ol class="arabic simple">
390-
<li>Activate the developer mode.</li>
391-
<li>Go to Settings &gt; Technical &gt; Sequences &amp; Identifiers &gt; Sequences.</li>
392-
<li>Click on “Task code” sequence to edit.</li>
393-
</ol>
389+
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
390+
<p>To check the task code unicity (recommended) check “Unique Task Code”
391+
in the settings of the Project app.</p>
394392
</div>
395393
<div class="section" id="usage">
396-
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
394+
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
397395
<p>To use this module, you need to:</p>
398396
<p>#. Go to menu Project &gt; Search &gt; Tasks and create a new task, and you get a
399397
new code saving it.
400398
#. If you duplicate a task, you will get a new code for the new task.</p>
401399
</div>
402400
<div class="section" id="bug-tracker">
403-
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
401+
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
404402
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/project/issues">GitHub Issues</a>.
405403
In case of trouble, please check there if your issue has already been reported.
406-
If you spotted it first, help us smashing it by providing a detailed and welcomed
404+
If you spotted it first, help us to smash it by providing a detailed and welcomed
407405
<a class="reference external" href="https://github.com/OCA/project/issues/new?body=module:%20project_task_code%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
408406
<p>Do not contact contributors directly about support or help with technical issues.</p>
409407
</div>
410408
<div class="section" id="credits">
411-
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
409+
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
412410
<div class="section" id="authors">
413-
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
411+
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
414412
<ul class="simple">
415413
<li>OdooMRP team</li>
416414
<li>AvanzOSC</li>
417415
<li>Tecnativa</li>
418416
</ul>
419417
</div>
420418
<div class="section" id="contributors">
421-
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
419+
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
422420
<ul class="simple">
423421
<li>Oihane Crucelaegui &lt;<a class="reference external" href="mailto:oihanecrucelaegi&#64;avanzosc.es">oihanecrucelaegi&#64;avanzosc.es</a>&gt;</li>
424422
<li>Pedro M. Baeza &lt;<a class="reference external" href="mailto:pedro.baeza&#64;serviciosbaeza.com">pedro.baeza&#64;serviciosbaeza.com</a>&gt;</li>
@@ -436,7 +434,7 @@ <h2><a class="toc-backref" href="#id6">Contributors</a></h2>
436434
</ul>
437435
</div>
438436
<div class="section" id="maintainers">
439-
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
437+
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
440438
<p>This module is maintained by the OCA.</p>
441439
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
442440
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose

project_task_code/tests/test_project_task_code.py

+33-6
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
33

44
import odoo.tests.common as common
5+
from odoo.exceptions import ValidationError
56

67

78
class TestProjectTaskCode(common.TransactionCase):
8-
def setUp(self):
9-
super().setUp()
10-
self.project_task_model = self.env["project.task"]
11-
self.ir_sequence_model = self.env["ir.sequence"]
12-
self.task_sequence = self.env.ref("project_task_code.sequence_task")
13-
self.project_task = self.env.ref("project.project_1_task_1")
9+
@classmethod
10+
def setUpClass(cls):
11+
super().setUpClass()
12+
cls.project_task_model = cls.env["project.task"]
13+
cls.ir_sequence_model = cls.env["ir.sequence"]
14+
cls.task_sequence = cls.env.ref("project_task_code.sequence_task")
15+
cls.project_task = cls.env.ref("project.project_1_task_1")
1416

1517
def test_old_task_code_assign(self):
1618
project_tasks = self.project_task_model.search([])
@@ -38,3 +40,28 @@ def test_name_get(self):
3840
)
3941
result = project_task.name_get()
4042
self.assertEqual(result[0][1], "[%s] Task Testing Get Name" % code)
43+
44+
def test_unique_task_code(self):
45+
task_1 = self.project_task_model.create(
46+
{
47+
"name": "Task 1",
48+
}
49+
)
50+
task_2 = self.project_task_model.create(
51+
{
52+
"name": "Task 2",
53+
}
54+
)
55+
task_3 = self.project_task_model.create(
56+
{
57+
"name": "Task 3",
58+
}
59+
)
60+
# by default, unicity is not checked
61+
task_2.code = task_1.code
62+
63+
self.env["ir.config_parameter"].sudo().set_param(
64+
"project_task_code.unique_task_code", True
65+
)
66+
with self.assertRaises(ValidationError, msg="The code must be unique!"):
67+
task_3.code = task_1.code

project_task_code/views/project_view.xml

+17-6
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@
66
<field name="inherit_id" ref="project.view_task_form2" />
77
<field name="arch" type="xml">
88
<field name="name" position="before">
9-
<field name="code" class="oe_inline" />
10-
<span class="oe_inline"> - </span>
9+
<field
10+
name="code"
11+
class="oe_inline"
12+
attrs="{'invisible': [('code', 'in', ['/', False])]}"
13+
/>
14+
<span
15+
class="oe_inline"
16+
attrs="{'invisible': [('code', 'in', ['/', False])]}"
17+
> - </span>
1118
</field>
1219
</field>
1320
</record>
@@ -17,7 +24,7 @@
1724
<field name="inherit_id" ref="project.view_task_tree2" />
1825
<field name="arch" type="xml">
1926
<field name="name" position="before">
20-
<field name="code" />
27+
<field name="code" optional="hide" />
2128
</field>
2229
</field>
2330
</record>
@@ -26,9 +33,13 @@
2633
<field name="model">project.task</field>
2734
<field name="inherit_id" ref="project.view_task_kanban" />
2835
<field name="arch" type="xml">
29-
<field name="name" position="before">
30-
<field name="code" />
31-
</field>
36+
<xpath expr="//field[@name='name']/.." position="before">
37+
<field
38+
name="code"
39+
class="me-2"
40+
attrs="{'invisible': [('code', '=', '/')]}"
41+
/>
42+
</xpath>
3243
</field>
3344
</record>
3445
<record id="project_task_code_search_view" model="ir.ui.view">

0 commit comments

Comments
 (0)