Files
odoo_source/addons/project/tests/test_burndown_chart.py
T
Xavier Bol (xbo) de7d0b17c5 [IMP] {sale_}project,{hr_,sale_}timesheet: remove display_project_id field
Before this commit, the `display_project_id` field was a bit confusing
for the user to know what is the goal of this field. Also this field is
available when the user wants to import his data into Odoo, if he does not
know the goal of this field then he could be lost to know which field he
should for his data to import (or even export).

This commit removes the display_project_id field and so a task will be
private one if the project and the parent fields are not set. The
project to set to the timesheet will be the one set on the task or the
one set on one of its parent tasks.

task-3230063

X-original-commit: 22eda5a5ddcfcda9fa6597d6fe21eb20f525799f
Part-of: odoo/odoo#115781
2023-03-24 02:23:37 +01:00

327 lines
18 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from freezegun import freeze_time
from datetime import datetime
from odoo import Command
from odoo.osv.expression import AND, OR
from odoo.tests.common import tagged, HttpCase
from .test_project_base import TestProjectCommon
class TestBurndownChartCommon(TestProjectCommon):
@classmethod
def set_create_date(cls, table, res_id, create_date):
cls.env.cr.execute("UPDATE {} SET create_date=%s WHERE id=%s".format(table), (create_date, res_id))
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.current_year = datetime.now().year
create_date = datetime(cls.current_year - 1, 1, 1)
Stage = cls.env['project.task.type']
cls.todo_stage = Stage.create({
'sequence': 1,
'name': 'TODO',
})
cls.set_create_date('project_task_type', cls.todo_stage.id, create_date)
cls.in_progress_stage = Stage.create({
'sequence': 10,
'name': 'In Progress',
})
cls.set_create_date('project_task_type', cls.in_progress_stage.id, create_date)
cls.testing_stage = Stage.create({
'sequence': 20,
'name': 'Testing',
})
cls.set_create_date('project_task_type', cls.testing_stage.id, create_date)
cls.done_stage = Stage.create({
'sequence': 30,
'name': 'Done',
})
cls.set_create_date('project_task_type', cls.done_stage.id, create_date)
cls.stages = cls.todo_stage + cls.in_progress_stage + cls.testing_stage + cls.done_stage
cls.project = cls.env['project.project'].create({
'name': 'Burndown Chart Test',
'privacy_visibility': 'employees',
'alias_name': 'project+burndown_chart',
'type_ids': [Command.link(stage_id) for stage_id in cls.stages.ids],
})
cls.set_create_date('project_project', cls.project.id, create_date)
cls.project.invalidate_model()
cls.milestone = cls.env['project.milestone'].with_context({'mail_create_nolog': True}).create({
'name': 'Test Milestone',
'project_id': cls.project_pigs.id,
})
cls.task_a = cls.env['project.task'].create({
'name': 'Task A',
'priority': 0,
'project_id': cls.project.id,
'stage_id': cls.todo_stage.id,
})
cls.set_create_date('project_task', cls.task_a.id, create_date)
cls.task_b = cls.task_a.copy({
'name': 'Task B',
'user_ids': [Command.set([cls.user_projectuser.id, cls.user_projectmanager.id])],
})
cls.set_create_date('project_task', cls.task_b.id, create_date)
cls.task_c = cls.task_a.copy({
'name': 'Task C',
'partner_id': cls.partner_1.id,
'user_ids': [Command.link(cls.user_projectuser.id)],
})
cls.set_create_date('project_task', cls.task_c.id, create_date)
cls.task_d = cls.task_a.copy({
'name': 'Task D',
'milestone_id': cls.milestone.id,
'user_ids': [Command.link(cls.user_projectmanager.id)],
})
cls.set_create_date('project_task', cls.task_d.id, create_date)
cls.task_e = cls.task_a.copy({
'name': 'Task E',
'partner_id': cls.partner_1.id,
})
cls.set_create_date('project_task', cls.task_e.id, create_date)
# Create a new task to check if a task without changing its stage is taken into account
task_f = cls.env['project.task'].create({
'name': 'Task F',
'priority': 0,
'project_id': cls.project.id,
'milestone_id': cls.milestone.id,
'stage_id': cls.todo_stage.id,
})
cls.set_create_date('project_task', task_f.id, datetime(cls.current_year - 1, 12, 20))
cls.project_2 = cls.env['project.project'].create({
'name': 'Burndown Chart Test 2 mySearchTag',
'privacy_visibility': 'employees',
'alias_name': 'project+burndown_chart+2',
'type_ids': [Command.link(stage_id) for stage_id in cls.stages.ids],
})
cls.set_create_date('project_project', cls.project_2.id, create_date)
cls.project.invalidate_model()
cls.task_g = cls.env['project.task'].create({
'name': 'Task G',
'priority': 0,
'project_id': cls.project_2.id,
'stage_id': cls.todo_stage.id,
'user_ids': [Command.link(cls.user_projectuser.id)],
})
cls.set_create_date('project_task', cls.task_g.id, create_date)
cls.task_h = cls.task_g.copy({
'name': 'Task H',
'user_ids': [Command.link(cls.user_projectmanager.id)],
})
cls.set_create_date('project_task', cls.task_h.id, create_date)
# Precommit to have the records in db and allow to rollback at the end of test
cls.env.cr.flush()
with freeze_time('%s-02-10' % (cls.current_year - 1)):
(cls.task_a + cls.task_b).write({'stage_id': cls.in_progress_stage.id})
cls.env.cr.flush()
with freeze_time('%s-02-20' % (cls.current_year - 1)):
cls.task_c.write({'stage_id': cls.in_progress_stage.id})
cls.env.cr.flush()
with freeze_time('%s-03-15' % (cls.current_year - 1)):
(cls.task_d + cls.task_e).write({'stage_id': cls.in_progress_stage.id})
cls.env.cr.flush()
with freeze_time('%s-04-10' % (cls.current_year - 1)):
(cls.task_a + cls.task_b).write({'stage_id': cls.testing_stage.id})
cls.env.cr.flush()
with freeze_time('%s-05-12' % (cls.current_year - 1)):
cls.task_c.write({'stage_id': cls.testing_stage.id})
cls.env.cr.flush()
with freeze_time('%s-06-25' % (cls.current_year - 1)):
cls.task_d.write({'stage_id': cls.testing_stage.id})
cls.env.cr.flush()
with freeze_time('%s-07-25' % (cls.current_year - 1)):
cls.task_e.write({'stage_id': cls.testing_stage.id})
cls.env.cr.flush()
with freeze_time('%s-08-01' % (cls.current_year - 1)):
cls.task_a.write({'stage_id': cls.done_stage.id})
cls.env.cr.flush()
with freeze_time('%s-09-10' % (cls.current_year - 1)):
cls.task_b.write({'stage_id': cls.done_stage.id})
cls.env.cr.flush()
with freeze_time('%s-10-05' % (cls.current_year - 1)):
cls.task_c.write({'stage_id': cls.done_stage.id})
cls.env.cr.flush()
with freeze_time('%s-11-25' % (cls.current_year - 1)):
cls.task_d.write({'stage_id': cls.done_stage.id})
cls.env.cr.flush()
with freeze_time('%s-12-12' % (cls.current_year - 1)):
cls.task_e.write({'stage_id': cls.done_stage.id})
cls.env.cr.flush()
class TestBurndownChart(TestBurndownChartCommon):
def map_read_group_result(self, read_group_result):
return {(res['date:month'], res['stage_id'][0]): res['__count'] for res in read_group_result if res['stage_id'][1]}
def check_read_group_results(self, domain, expected_results_dict):
stages_dict = {stage.id: stage.name for stage in self.stages}
read_group_result = self.env['project.task.burndown.chart.report'].read_group(
domain, ['date', 'stage_id'], ['date:month', 'stage_id'], lazy=False)
read_group_result_dict = self.map_read_group_result(read_group_result)
for (month, stage_id), __count in read_group_result_dict.items():
expected_count = expected_results_dict.get((month, stage_id), 100000)
self.assertEqual(
__count,
expected_count,
'In %s, the number of tasks should be equal to %s in %s stage.' % (month, expected_count, stages_dict.get(stage_id, 'Unknown'))
)
def test_burndown_chart(self):
burndown_chart_domain = [('project_id', '!=', False)]
project_domain = [('project_id', '=', self.project.id)]
# Check that we get the expected results for the complete data of `self.project`.
project_expected_dict = {
('January %s' % (self.current_year - 1), self.todo_stage.id): 5,
('January %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('January %s' % (self.current_year - 1), self.testing_stage.id): 0,
('January %s' % (self.current_year - 1), self.done_stage.id): 0,
('February %s' % (self.current_year - 1), self.todo_stage.id): 2,
('February %s' % (self.current_year - 1), self.in_progress_stage.id): 3,
('February %s' % (self.current_year - 1), self.testing_stage.id): 0,
('February %s' % (self.current_year - 1), self.done_stage.id): 0,
('March %s' % (self.current_year - 1), self.todo_stage.id): 0,
('March %s' % (self.current_year - 1), self.in_progress_stage.id): 5,
('March %s' % (self.current_year - 1), self.testing_stage.id): 0,
('March %s' % (self.current_year - 1), self.done_stage.id): 0,
('April %s' % (self.current_year - 1), self.todo_stage.id): 0,
('April %s' % (self.current_year - 1), self.in_progress_stage.id): 3,
('April %s' % (self.current_year - 1), self.testing_stage.id): 2,
('April %s' % (self.current_year - 1), self.done_stage.id): 0,
('May %s' % (self.current_year - 1), self.todo_stage.id): 0,
('May %s' % (self.current_year - 1), self.in_progress_stage.id): 2,
('May %s' % (self.current_year - 1), self.testing_stage.id): 3,
('May %s' % (self.current_year - 1), self.done_stage.id): 0,
('June %s' % (self.current_year - 1), self.todo_stage.id): 0,
('June %s' % (self.current_year - 1), self.in_progress_stage.id): 1,
('June %s' % (self.current_year - 1), self.testing_stage.id): 4,
('June %s' % (self.current_year - 1), self.done_stage.id): 0,
('July %s' % (self.current_year - 1), self.todo_stage.id): 0,
('July %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('July %s' % (self.current_year - 1), self.testing_stage.id): 5,
('July %s' % (self.current_year - 1), self.done_stage.id): 0,
('August %s' % (self.current_year - 1), self.todo_stage.id): 0,
('August %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('August %s' % (self.current_year - 1), self.testing_stage.id): 4,
('August %s' % (self.current_year - 1), self.done_stage.id): 1,
('September %s' % (self.current_year - 1), self.todo_stage.id): 0,
('September %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('September %s' % (self.current_year - 1), self.testing_stage.id): 3,
('September %s' % (self.current_year - 1), self.done_stage.id): 2,
('October %s' % (self.current_year - 1), self.todo_stage.id): 0,
('October %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('October %s' % (self.current_year - 1), self.testing_stage.id): 2,
('October %s' % (self.current_year - 1), self.done_stage.id): 3,
('November %s' % (self.current_year - 1), self.todo_stage.id): 0,
('November %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('November %s' % (self.current_year - 1), self.testing_stage.id): 1,
('November %s' % (self.current_year - 1), self.done_stage.id): 4,
('December %s' % (self.current_year - 1), self.todo_stage.id): 0,
('December %s' % (self.current_year - 1), self.in_progress_stage.id): 0,
('December %s' % (self.current_year - 1), self.done_stage.id): 5,
('December %s' % (self.current_year - 1), self.todo_stage.id): 1,
('January %s' % (self.current_year), self.todo_stage.id): 0,
('January %s' % (self.current_year), self.in_progress_stage.id): 0,
('January %s' % (self.current_year), self.done_stage.id): 5,
('January %s' % (self.current_year), self.todo_stage.id): 1,
('February %s' % (self.current_year), self.todo_stage.id): 0,
('February %s' % (self.current_year), self.in_progress_stage.id): 0,
('February %s' % (self.current_year), self.done_stage.id): 5,
('February %s' % (self.current_year), self.todo_stage.id): 1,
('March %s' % (self.current_year), self.todo_stage.id): 0,
('March %s' % (self.current_year), self.in_progress_stage.id): 0,
('March %s' % (self.current_year), self.done_stage.id): 5,
('March %s' % (self.current_year), self.todo_stage.id): 1,
('April %s' % (self.current_year), self.todo_stage.id): 0,
('April %s' % (self.current_year), self.in_progress_stage.id): 0,
('April %s' % (self.current_year), self.done_stage.id): 5,
('April %s' % (self.current_year), self.todo_stage.id): 1,
('May %s' % (self.current_year), self.todo_stage.id): 0,
('May %s' % (self.current_year), self.in_progress_stage.id): 0,
('May %s' % (self.current_year), self.done_stage.id): 5,
('May %s' % (self.current_year), self.todo_stage.id): 1,
('June %s' % (self.current_year), self.todo_stage.id): 0,
('June %s' % (self.current_year), self.in_progress_stage.id): 0,
('June %s' % (self.current_year), self.done_stage.id): 5,
('June %s' % (self.current_year), self.todo_stage.id): 1,
('July %s' % (self.current_year), self.todo_stage.id): 0,
('July %s' % (self.current_year), self.in_progress_stage.id): 0,
('July %s' % (self.current_year), self.done_stage.id): 5,
('July %s' % (self.current_year), self.todo_stage.id): 1,
('August %s' % (self.current_year), self.todo_stage.id): 0,
('August %s' % (self.current_year), self.in_progress_stage.id): 0,
('August %s' % (self.current_year), self.done_stage.id): 5,
('August %s' % (self.current_year), self.todo_stage.id): 1,
('September %s' % (self.current_year), self.todo_stage.id): 0,
('September %s' % (self.current_year), self.in_progress_stage.id): 0,
('September %s' % (self.current_year), self.done_stage.id): 5,
('September %s' % (self.current_year), self.todo_stage.id): 1,
('October %s' % (self.current_year), self.todo_stage.id): 0,
('October %s' % (self.current_year), self.in_progress_stage.id): 0,
('October %s' % (self.current_year), self.done_stage.id): 5,
('October %s' % (self.current_year), self.todo_stage.id): 1,
('November %s' % (self.current_year), self.todo_stage.id): 0,
('November %s' % (self.current_year), self.in_progress_stage.id): 0,
('November %s' % (self.current_year), self.done_stage.id): 5,
('November %s' % (self.current_year), self.todo_stage.id): 1,
('December %s' % (self.current_year), self.todo_stage.id): 0,
('December %s' % (self.current_year), self.in_progress_stage.id): 0,
('December %s' % (self.current_year), self.done_stage.id): 5,
('December %s' % (self.current_year), self.todo_stage.id): 1,
}
self.check_read_group_results(AND([burndown_chart_domain, project_domain]), project_expected_dict)
# Check that we get the expected results for the complete data of `self.project` & `self.project_2` using an
# `ilike` in the domain.
all_projects_domain_with_ilike = OR([project_domain, [('project_id', 'ilike', 'mySearchTag')]])
project_expected_dict = {key: val if key[1] != self.todo_stage.id else val + 2 for key, val in project_expected_dict.items()}
self.check_read_group_results(AND([burndown_chart_domain, all_projects_domain_with_ilike]), project_expected_dict)
date_from, date_to = ('%s-01-01' % (self.current_year - 1), '%s-02-01' % (self.current_year - 1))
date_and_user_domain = [('date', '>=', date_from), ('date', '<', date_to), ('user_ids', 'ilike', 'ProjectUser')]
complex_domain_expected_dict = {
('January %s' % (self.current_year - 1), self.todo_stage.id): 3,
('February %s' % (self.current_year - 1), self.todo_stage.id): 1,
('February %s' % (self.current_year - 1), self.in_progress_stage.id): 2,
}
complex_domain = AND([burndown_chart_domain, all_projects_domain_with_ilike, date_and_user_domain])
self.check_read_group_results(complex_domain, complex_domain_expected_dict)
date_and_user_domain = [('date', '>=', date_from), ('date', '<', date_to), ('user_ids', 'ilike', 'ProjectManager')]
milestone_domain = [('milestone_id', 'ilike', 'Test')]
complex_domain = AND([burndown_chart_domain, all_projects_domain_with_ilike, date_and_user_domain, milestone_domain])
complex_domain_expected_dict = {
('January %s' % (self.current_year - 1), self.todo_stage.id): 1,
('February %s' % (self.current_year - 1), self.todo_stage.id): 1,
}
self.check_read_group_results(complex_domain, complex_domain_expected_dict)
@tagged('-at_install', 'post_install')
class TestBurndownChartTour(HttpCase, TestBurndownChartCommon):
def test_burndown_chart_tour(self):
# Test customizing personal stages as a project user
self.start_tour('/web', 'burndown_chart_tour', login="admin")