diff options
| author | Rémy Baranx (bar) <bar@odoo.com> | 2020-02-12 12:37:58 +0000 |
|---|---|---|
| committer | Yannick Tivisse <yti@odoo.com> | 2020-06-23 08:57:08 +0000 |
| commit | 972e097dbe7cdc7afdae722391692f9b3bf063b8 (patch) | |
| tree | 045692a9fbb2c4a051e55b9d5cc72b080715f39f /addons | |
| parent | cd4e854e417cec1ae489a068f7bd362edaed789f (diff) | |
[IMP] project: convert onchange to stored-editable computed fields.
The 'stage_id' field is now a stored-editable computed field.
The 'company_id' has been converted to a stored-editable computed field.
The modification of the 'partner_id' field in the onchange method has been
moved and merged into the _compute_partner_id method.
Remove the empty 'onchange' method from the project.task model and also remove
this method call from the sale_timesheet module, which inherits from the project.task model.
Adapt the _compute_partner_id to retrieve exactly the behaviour before the onchange
method deletion.
Update code and tests of the task/subtask partner_id management after discussion with Alexandra to clarify the behavior.
Add the 'multi_edit' attribute to tree views.
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/project/models/project.py | 49 | ||||
| -rw-r--r-- | addons/project/tests/test_project_base.py | 3 | ||||
| -rw-r--r-- | addons/project/tests/test_project_flow.py | 105 | ||||
| -rw-r--r-- | addons/project/views/project_views.xml | 2 | ||||
| -rw-r--r-- | addons/sale_timesheet/models/project.py | 1 |
5 files changed, 129 insertions, 31 deletions
diff --git a/addons/project/models/project.py b/addons/project/models/project.py index dee727dfcfb..5136aa13116 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -520,7 +520,8 @@ class Task(models.Model): ], default='0', index=True, string="Priority") sequence = fields.Integer(string='Sequence', index=True, default=10, help="Gives the sequence order when displaying a list of tasks.") - stage_id = fields.Many2one('project.task.type', string='Stage', ondelete='restrict', tracking=True, index=True, + stage_id = fields.Many2one('project.task.type', string='Stage', compute='_compute_stage_id', + store=True, readonly=False, ondelete='restrict', tracking=True, index=True, default=_get_default_stage_id, group_expand='_read_group_stage_ids', domain="[('project_ids', '=', project_id)]", copy=False) tag_ids = fields.Many2many('project.tags', string='Tags') @@ -563,7 +564,9 @@ class Task(models.Model): ribbon_message = fields.Char('Ribbon message', compute='_compute_ribbon_message') partner_city = fields.Char(related='partner_id.city', readonly=False) manager_id = fields.Many2one('res.users', string='Project Manager', related='project_id.user_id', readonly=True) - company_id = fields.Many2one('res.company', string='Company', required=True, default=_default_company_id) + company_id = fields.Many2one( + 'res.company', string='Company', compute='_compute_company_id', store=True, readonly=False, + required=True, copy=True, default=_default_company_id) color = fields.Integer(string='Color Index') user_email = fields.Char(related='user_id.email', string='User Email', readonly=True, related_sudo=False) attachment_ids = fields.One2many('ir.attachment', compute='_compute_attachment_ids', string="Main Attachments", @@ -722,25 +725,26 @@ class Task(models.Model): for task in self: task.subtask_count = len(self._get_all_subtasks()) - @api.onchange('project_id') - def _onchange_project(self): - if self.project_id: - # find partner - if self.project_id.partner_id: - self.partner_id = self.project_id.partner_id - # find stage - if self.project_id not in self.stage_id.project_ids: - self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False), ('is_closed', '=', False)]) - # keep multi company consistency - self.company_id = self.project_id.company_id - else: - self.stage_id = False - @api.onchange('company_id') def _onchange_task_company(self): if self.project_id.company_id != self.company_id: self.project_id = False + @api.depends('project_id.company_id') + def _compute_company_id(self): + for task in self.filtered(lambda task: task.project_id): + task.company_id = task.project_id.company_id + + @api.depends('project_id') + def _compute_stage_id(self): + for task in self: + if task.project_id: + if task.project_id not in task.stage_id.project_ids: + task.stage_id = task.stage_find(task.project_id.id, [ + ('fold', '=', False), ('is_closed', '=', False)]) + else: + task.stage_id = False + @api.constrains('parent_id', 'child_ids') def _check_subtask_level(self): for task in self: @@ -865,9 +869,18 @@ class Task(models.Model): @api.depends('parent_id.partner_id', 'project_id.partner_id') def _compute_partner_id(self): + """ + If a task has no partner_id, use the project partner_id if any, or else the parent task partner_id. + Once the task partner_id has been set: + 1) if the project partner_id changes, the task partner_id is automatically changed also. + 2) if the parent task partner_id changes, the task partner_id remains the same. + """ for task in self: - if not task.partner_id: - task.partner_id = task.parent_id.partner_id or task.project_id.partner_id + if task.partner_id: + if task.project_id.partner_id: + task.partner_id = task.project_id.partner_id + else: + task.partner_id = task.project_id.partner_id or task.parent_id.partner_id @api.depends('partner_id.email', 'parent_id.email_from') def _compute_email_from(self): diff --git a/addons/project/tests/test_project_base.py b/addons/project/tests/test_project_base.py index c38f72561a2..72350b1af20 100644 --- a/addons/project/tests/test_project_base.py +++ b/addons/project/tests/test_project_base.py @@ -19,6 +19,9 @@ class TestProjectCommon(SavepointCase): cls.partner_2 = cls.env['res.partner'].create({ 'name': 'Valid Poilvache', 'email': 'valid.other@gmail.com'}) + cls.partner_3 = cls.env['res.partner'].create({ + 'name': 'Valid Poilboeuf', + 'email': 'valid.poilboeuf@gmail.com'}) # Test users to use through the various tests Users = cls.env['res.users'].with_context({'no_reset_password': True}) diff --git a/addons/project/tests/test_project_flow.py b/addons/project/tests/test_project_flow.py index fdb5450e12a..8712afdca3f 100644 --- a/addons/project/tests/test_project_flow.py +++ b/addons/project/tests/test_project_flow.py @@ -96,8 +96,20 @@ class TestProjectFlow(TestProjectCommon): self.assertEqual(task.stage_id.sequence, 1, "project_task: should have a stage with sequence=1") def test_subtask_process(self): - """ Check subtask mecanism and change it from project. """ + """ + Check subtask mecanism and change it from project. + + For this test, 2 projects are used: + - the 'pigs' project which has a partner_id + - the 'goats' project where the partner_id is removed at the beginning of the tests and then restored. + + 2 parent tasks are also used to be able to switch the parent task of a sub-task: + - 'parent_task' linked to the partner_2 + - 'another_parent_task' linked to the partner_3 + """ + Task = self.env['project.task'].with_context({'tracking_disable': True}) + parent_task = Task.create({ 'name': 'Mother Task', 'user_id': self.user_projectuser.id, @@ -105,23 +117,94 @@ class TestProjectFlow(TestProjectCommon): 'partner_id': self.partner_2.id, 'planned_hours': 12, }) - child_task = Task.create({ - 'name': 'Task Child', - 'parent_id': parent_task.id, + + another_parent_task = Task.create({ + 'name': 'Another Mother Task', + 'user_id': self.user_projectuser.id, 'project_id': self.project_pigs.id, + 'partner_id': self.partner_3.id, + 'planned_hours': 0, + }) + + # remove the partner_id of the 'goats' project + goats_partner_id = self.project_goats.partner_id + + self.project_goats.write({ + 'partner_id': False + }) + + # the child task 1 is linked to a project without partner_id (goats project) + child_task_1 = Task.create({ + 'name': 'Task Child with project', + 'parent_id': parent_task.id, + 'project_id': self.project_goats.id, 'planned_hours': 3, }) - self.assertEqual(parent_task.partner_id, child_task.partner_id, "Subtask should have the same partner than its parent") - self.assertEqual(parent_task.subtask_count, 1, "Parent task should have 1 child") - self.assertEqual(parent_task.subtask_planned_hours, 3, "Planned hours of subtask should impact parent task") + # the child task 2 is linked to a project with a partner_id (pigs project) + child_task_2 = Task.create({ + 'name': 'Task Child without project', + 'parent_id': parent_task.id, + 'project_id': self.project_pigs.id, + 'planned_hours': 5, + }) + + self.assertEqual( + child_task_1.partner_id, child_task_1.parent_id.partner_id, + "When no project partner_id has been set, a subtask should have the same partner than its parent") + + self.assertEqual( + child_task_2.partner_id, child_task_2.project_id.partner_id, + "When a project partner_id has been set, a subtask should have the same partner than its project") + + self.assertEqual( + parent_task.subtask_count, 2, + "Parent task should have 2 children") + + self.assertEqual( + parent_task.subtask_planned_hours, 8, + "Planned hours of subtask should impact parent task") + + # change the parent of a subtask without a project partner_id + child_task_1.write({ + 'parent_id': another_parent_task.id + }) + + self.assertEqual( + child_task_1.partner_id, parent_task.partner_id, + "When changing the parent task of a subtask with no project partner_id, the partner_id should remain the same.") + + # change the parent of a subtask with a project partner_id + child_task_2.write({ + 'parent_id': another_parent_task.id + }) + + self.assertEqual( + child_task_2.partner_id, child_task_2.project_id.partner_id, + "When changing the parent task of a subtask with a project, the partner_id should remain the same.") + + # set a project with partner_id to a subtask without project partner_id + child_task_1.write({ + 'project_id': self.project_pigs.id + }) + + self.assertEqual( + child_task_1.partner_id, self.project_pigs.partner_id, + "When the project changes, the subtask should have the same partner id than the new project.") + + # restore the partner_id of the 'goats' project + self.project_goats.write({ + 'partner_id': goats_partner_id + }) - # change project - child_task.write({ - 'project_id': self.project_goats.id # customer is partner_1 + # set a project with partner_id to a subtask with a project partner_id + child_task_2.write({ + 'project_id': self.project_goats.id }) - self.assertEqual(parent_task.partner_id, child_task.partner_id, "Subtask partner should not change when changing project") + self.assertEqual( + child_task_2.partner_id, self.project_goats.partner_id, + "When the project changes, the subtask should have the same partner id than the new project.") def test_rating(self): """Check if rating works correctly even when task is changed from project A to project B""" diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml index 86c26faa1fd..661a0ea955f 100644 --- a/addons/project/views/project_views.xml +++ b/addons/project/views/project_views.xml @@ -856,7 +856,7 @@ <field name="name">project.task.tree.activity</field> <field name="model">project.task</field> <field name="arch" type="xml"> - <tree string="Next Activities" decoration-danger="not is_closed and activity_date_deadline < current_date" default_order="activity_date_deadline"> + <tree string="Next Activities" decoration-danger="not is_closed and activity_date_deadline < current_date" default_order="activity_date_deadline" multi_edit="1"> <field name="is_closed"/> <field name="name"/> <field name="project_id"/> diff --git a/addons/sale_timesheet/models/project.py b/addons/sale_timesheet/models/project.py index b31f89dad69..e27af7c4ff4 100644 --- a/addons/sale_timesheet/models/project.py +++ b/addons/sale_timesheet/models/project.py @@ -250,7 +250,6 @@ class ProjectTask(models.Model): @api.onchange('project_id') def _onchange_project(self): - super(ProjectTask, self)._onchange_project() if self.project_id: if self.project_id.billable_type == 'employee_rate': if not self.partner_id: |
