329 lines
17 KiB
Python
329 lines
17 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.tests import Form, tagged
|
|
|
|
from .test_project_base import TestProjectCommon
|
|
|
|
|
|
@tagged('-at_install', 'post_install')
|
|
class TestProjectMilestone(TestProjectCommon):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.milestone_pigs, cls.milestone_goats = cls.env['project.milestone'].with_context({'mail_create_nolog': True}).create([{
|
|
'name': 'Test Milestone',
|
|
'project_id': cls.project_pigs.id,
|
|
}, {
|
|
'name': 'Test Milestone Goats',
|
|
'project_id': cls.project_goats.id,
|
|
}])
|
|
|
|
def test_milestones_settings_change(self):
|
|
# To be sure the feature is disabled globally to begin the test.
|
|
self.env['res.config.settings'] \
|
|
.create({'group_project_milestone': False}) \
|
|
.execute()
|
|
self.assertFalse(self.env.user.has_group('project.group_project_milestone'), 'The "Milestones" feature should not be globally enabled by default.')
|
|
self.assertFalse(self.project_pigs.allow_milestones, 'The "Milestones" feature should not be enabled by default.')
|
|
self.env['res.config.settings'] \
|
|
.create({'group_project_milestone': True}) \
|
|
.execute()
|
|
self.assertTrue(self.env.user.has_group('project.group_project_milestone'), 'The "Milestones" feature should globally be enabled.')
|
|
self.assertTrue(self.project_pigs.allow_milestones, 'The "Milestones" feature should be enabled by default on the project when the feature is enabled.')
|
|
project = self.env['project.project'].create({'name': 'Test allow_milestones on New Project'})
|
|
self.assertTrue(project.allow_milestones, 'The "Milestones" feature should be enabled by default when the feature is enabled globally.')
|
|
|
|
with Form(self.env['project.project']) as project_form:
|
|
project_form.name = 'My Mouses Project'
|
|
self.assertTrue(project_form.allow_milestones, 'New projects allow_milestones should be True by default.')
|
|
|
|
def test_change_project_in_task(self):
|
|
""" Test when a task is linked to a milestone and when we change its project the milestone is removed (and
|
|
we fallback on the parent milestone if it belongs to the same project)
|
|
|
|
Test Case:
|
|
=========
|
|
1) Set a milestone on the task
|
|
2) Change the project of that task
|
|
3) Check no milestone is linked to the task (or the one of its parent is used if relevant)
|
|
"""
|
|
# A. No parent task
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_pigs)
|
|
|
|
|
|
self.task_1.project_id = self.project_goats
|
|
self.assertFalse(self.task_1.milestone_id, 'No milestone should be linked to the task since its project has changed')
|
|
|
|
# B. Parent task with no milestone set
|
|
task_2 = self.env['project.task'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Child MilestoneTask',
|
|
'user_ids': self.user_projectmanager,
|
|
'project_id': self.project_pigs.id,
|
|
'parent_id': self.task_1.id,
|
|
'milestone_id': self.milestone_pigs.id,
|
|
})
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
|
|
task_2.project_id = self.project_goats
|
|
self.assertFalse(task_2.milestone_id, 'No milestone should be linked to the task since its project has changed and its parent task has no milestone')
|
|
|
|
# C. Parent task with a milestone set but on a different project
|
|
self.task_1.project_id = self.project_pigs
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
task_2.project_id = self.project_pigs
|
|
task_2.milestone_id = self.milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
|
|
task_2.project_id = self.project_goats
|
|
self.assertFalse(task_2.milestone_id, 'No milestone should be linked to the task since its project has changed and its parent task belongs to another project')
|
|
|
|
# D. Parent task with a milestone set on the same project
|
|
self.task_1.project_id = self.project_goats
|
|
self.task_1.milestone_id = self.milestone_goats
|
|
task_2.project_id = self.project_pigs
|
|
task_2.milestone_id = self.milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
task_2.project_id = self.project_goats
|
|
self.assertEqual(task_2.milestone_id, self.milestone_goats,
|
|
'The milestone of the task should be replaced by the one of its parent task as they now belong to the same project')
|
|
|
|
# E. No milestone for private task
|
|
task_2.parent_id = False
|
|
self.assertEqual(task_2.milestone_id, self.milestone_goats)
|
|
task_2.project_id = False
|
|
self.assertFalse(task_2.milestone_id, 'No milestone should be linked to a private task')
|
|
|
|
def test_duplicate_project_duplicates_milestones_on_tasks(self):
|
|
"""
|
|
Test when we duplicate the project with tasks linked to its' milestones,
|
|
that the tasks in the new project are also linked to the duplicated milestones of the new project
|
|
We can't really robustly test that the mapping of task -> milestone is the same in the old and new project,
|
|
the workaround way of testing the mapping is basing ourselves on unique names and check that those are equals in the test.
|
|
"""
|
|
# original unique_names, used to map between the original -> copy
|
|
unique_name_1 = "unique_name_1"
|
|
unique_name_2 = "unique_name_2"
|
|
unique_names = [unique_name_1, unique_name_2]
|
|
project = self.env['project.project'].create({
|
|
'name': 'Test project',
|
|
'allow_milestones': True,
|
|
})
|
|
milestones = self.env['project.milestone'].create([{
|
|
'name': unique_name_1,
|
|
'project_id': project.id,
|
|
}, {
|
|
'name': unique_name_2,
|
|
'project_id': project.id,
|
|
}])
|
|
tasks = self.env['project.task'].create([{
|
|
'name': unique_name_1,
|
|
'project_id': project.id,
|
|
'milestone_id': milestones[0].id,
|
|
}, {
|
|
'name': unique_name_2,
|
|
'project_id': project.id,
|
|
'milestone_id': milestones[1].id,
|
|
}])
|
|
self.assertEqual(tasks[0].milestone_id, milestones[0])
|
|
self.assertEqual(tasks[1].milestone_id, milestones[1])
|
|
project_copy = project.copy()
|
|
self.assertNotEqual(project_copy.milestone_ids, False)
|
|
self.assertEqual(project.milestone_ids.mapped('name'), project_copy.milestone_ids.mapped('name'))
|
|
self.assertNotEqual(project_copy.task_ids, False)
|
|
for milestone in project_copy.task_ids.milestone_id:
|
|
self.assertTrue(milestone in project_copy.milestone_ids)
|
|
for unique_name in unique_names:
|
|
orig_task = project.task_ids.filtered(lambda t: t.name == unique_name)
|
|
copied_task = project_copy.task_ids.filtered(lambda t: t.name == unique_name)
|
|
self.assertEqual(orig_task.name, copied_task.name, "The copied_task should be a copy of the original task")
|
|
self.assertNotEqual(copied_task.milestone_id, False,
|
|
"We should copy the milestone and it shouldn't be reset to false from _compute_milestone_id")
|
|
self.assertEqual(orig_task.milestone_id.name, copied_task.milestone_id.name,
|
|
"the copied milestone should be a copy of the original ")
|
|
|
|
def test_basic_milestone_write(self):
|
|
""" Testing basic milestone/project write operation on task, i.e:
|
|
1. Set/change the milestone of a task
|
|
2. Change the milestone/project of a task simultaneously
|
|
3. Set/change to an invalid milestone
|
|
"""
|
|
extra_milestone_pigs = self.env['project.milestone'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Test Extra Milestone',
|
|
'project_id': self.project_pigs.id,
|
|
})
|
|
|
|
# 1. Set/change the milestone of a task
|
|
self.task_1.project_id = self.project_pigs
|
|
self.assertEqual(self.task_1.project_id, self.project_pigs)
|
|
self.assertFalse(self.task_1.milestone_id)
|
|
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_pigs,
|
|
"Assignation of a valid milestone to a task with no milestone is not working properly.")
|
|
self.task_1.milestone_id = extra_milestone_pigs
|
|
self.assertEqual(self.task_1.milestone_id, extra_milestone_pigs,
|
|
"Change of the milestone of a task to a milestone from the same project is not working properly.")
|
|
|
|
# 2. Change the milestone/project of a task simultaneously
|
|
self.assertEqual(self.task_1.project_id, self.project_pigs)
|
|
self.assertEqual(self.task_1.milestone_id, extra_milestone_pigs)
|
|
|
|
self.task_1.write({
|
|
'milestone_id': self.milestone_goats.id,
|
|
'project_id': self.project_goats.id,
|
|
})
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_goats,
|
|
"Changing the project of a task and its milestone simultaneously is not working properly.")
|
|
self.assertEqual(self.task_1.project_id, self.project_goats,
|
|
"Changing the project of a task and its milestone simultaneously is not working properly.")
|
|
|
|
# 3. Set/change to an invalid milestone
|
|
self.assertEqual(self.task_1.project_id, self.project_goats)
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_goats)
|
|
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertFalse(self.task_1.milestone_id,
|
|
"Setting the milestone of a task to an invalid value should reset the value of milestone_id.")
|
|
|
|
|
|
def test_set_milestone_parent_task(self):
|
|
""" When a milestone is set on a parent task, it is set as well on its child tasks if they have no milestone set yet and
|
|
if they belong to the same project (or they have no project set).
|
|
|
|
Test Case:
|
|
=========
|
|
1) Set a milestone on the task (or not)
|
|
2) Change the milestone of its parent
|
|
3) Check the result
|
|
"""
|
|
# A. Child task with no milestone set and belonging to the same project
|
|
task_2, task_3 = self.env['project.task'].with_context({'mail_create_nolog': True}).create([{
|
|
'name': 'Child MilestoneTask',
|
|
'user_ids': self.user_projectmanager,
|
|
'project_id': self.project_pigs.id,
|
|
'parent_id': self.task_1.id,
|
|
}, {
|
|
'name': 'Grand-child MilestoneTask',
|
|
'user_ids': self.user_projectmanager,
|
|
'project_id': self.project_pigs.id,
|
|
}])
|
|
self.assertFalse(self.task_1.milestone_id)
|
|
self.assertFalse(task_2.milestone_id)
|
|
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs,
|
|
"The milestone of the parent task should be set to its subtasks if they belong to the same project (or the subtask has not project set) and the subtask has no milestone already set.")
|
|
|
|
# B. Child task with a milestone already set
|
|
extra_milestone_pigs = self.env['project.milestone'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Test Extra Milestone',
|
|
'project_id': self.project_pigs.id,
|
|
})
|
|
self.task_1.milestone_id = False
|
|
task_2.milestone_id = extra_milestone_pigs
|
|
self.assertFalse(self.task_1.milestone_id)
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs)
|
|
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs, "The milestone of the child task should not be modified has it has already one set.")
|
|
|
|
# C. Child task with no milestone set but belonging to another project
|
|
self.task_1.project_id = self.project_goats
|
|
task_2.milestone_id = False
|
|
self.assertFalse(self.task_1.milestone_id)
|
|
self.assertFalse(task_2.milestone_id)
|
|
|
|
self.task_1.milestone_id = self.milestone_goats
|
|
self.assertFalse(task_2.milestone_id, "The milestone of the parent task should not be set to its child task has they belong to different projects.")
|
|
|
|
# D. Recursion test (grand-parent task's milestone set to grand-child task)
|
|
task_3.parent_id = task_2
|
|
self.task_1.project_id = task_2.project_id = task_3.project_id = self.project_pigs
|
|
self.task_1.milestone_id = task_2.milestone_id = task_3.milestone_id = False
|
|
self.assertFalse(task_2.milestone_id)
|
|
self.assertFalse(task_3.milestone_id)
|
|
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertEqual(task_3.milestone_id, self.milestone_pigs, "The milestone of the parent task should be set to its (grand)child tasks recursively.")
|
|
|
|
# E. Recursion test 2 (grand-child task has no milestone but first level child does)
|
|
self.task_1.milestone_id = False
|
|
task_2.milestone_id = self.milestone_pigs
|
|
task_3.milestone_id = False
|
|
self.assertFalse(self.task_1.milestone_id)
|
|
self.assertFalse(task_3.milestone_id)
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
|
|
self.task_1.milestone_id = extra_milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
self.assertFalse(task_3.milestone_id,
|
|
"The milestone of the parent task should be set to its (grand)child tasks recursively. If a child task milestone should not be updated, it stops the recursion.")
|
|
|
|
# F. Update of the parent's milestone, trigger the update of the subtask's milestone if they were the same before change
|
|
self.task_1.milestone_id = self.milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_pigs)
|
|
self.task_1.milestone_id = extra_milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs,
|
|
"If parent and child tasks share the same milestone, the update of the parent's milestone should trigger the update of its child's milestone.")
|
|
|
|
# G. Same as F but project and milestone of the parent task are changed at the same time -> The milestone of the child should not change
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs)
|
|
self.assertEqual(self.task_1.milestone_id, extra_milestone_pigs)
|
|
|
|
self.task_1.write({
|
|
'project_id': self.project_goats.id,
|
|
'milestone_id': self.milestone_goats.id,
|
|
})
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_goats)
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs,
|
|
"The child milestone should not be updated if the parent task's project is changed.")
|
|
|
|
# H. Same as G but project the project writen value is the same as the previous one -> No actual change of project_id so update the subtask milestone
|
|
self.task_1.project_id = self.project_pigs
|
|
self.task_1.milestone_id = extra_milestone_pigs
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs)
|
|
self.assertEqual(self.task_1.milestone_id, extra_milestone_pigs)
|
|
|
|
self.task_1.write({
|
|
'project_id': self.project_pigs.id,
|
|
'milestone_id': self.milestone_pigs.id,
|
|
})
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_pigs)
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs,
|
|
"The child milestone should be updated as the project of the parent task does not actually change.")
|
|
|
|
# I. Same case as G but the display_on_project is set to False on the child task -> Both project and milestone of the subtask should be updated
|
|
self.task_1.write({
|
|
'project_id': self.project_pigs.id,
|
|
'milestone_id': extra_milestone_pigs.id,
|
|
})
|
|
task_2.display_in_project = False
|
|
self.assertEqual(task_2.milestone_id, extra_milestone_pigs)
|
|
self.assertEqual(self.task_1.milestone_id, extra_milestone_pigs)
|
|
|
|
self.task_1.write({
|
|
'project_id': self.project_goats.id,
|
|
'milestone_id': self.milestone_goats.id,
|
|
})
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_goats)
|
|
self.assertEqual(task_2.project_id, self.project_goats)
|
|
self.assertEqual(task_2.milestone_id, self.milestone_goats,
|
|
"The child milestone should be updated if the parent task's project is changed only if dislay_on_project is set to False for the subtask.")
|
|
|
|
# J. Same case as F but subtask is closed -> no update of its milestone
|
|
self.task_1.project_id = task_2.project_id = self.project_pigs
|
|
self.task_1.milestone_id = task_2.milestone_id = self.milestone_pigs
|
|
task_2.state = '1_done'
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs)
|
|
self.assertEqual(self.task_1.milestone_id, self.milestone_pigs)
|
|
|
|
self.task_1.write({
|
|
'milestone_id': extra_milestone_pigs.id,
|
|
})
|
|
self.assertEqual(self.task_1.milestone_id, extra_milestone_pigs)
|
|
self.assertEqual(task_2.milestone_id, self.milestone_pigs,
|
|
"The child milestone should not be updated if it is closed.")
|