Purpose of this commit ====================== The Burndown Chart report was very slow on big databases as it was not possible for Postgresql to optimize the query as it was based on a view that was using several generate series. This commit aims to improve the performance by injecting the constraints at a lower level than it was in the past, lowering the amount of data processed in the higher level of the query. /!\ Important note ------------------ Overwriting the `read_group_raw` is really not a good practice and should be avoided in most case. If you fall on this implementation by grepping the source code, please be advised that this is not the right way of doing things. Implementation details ---------------------- - The report is now run by generating the `SQL` that is executed by the `read_group_raw`. This allows inserting `SQL` constraints at a lower level and simnifically improves performance. As there is no other way to do it, the code is unfortunately a modified copy of the actual `read_group_raw`. - The pivot view has been removed as it had no meaning and was creating confusing data. - The `Group By` menu has been limited to `stage_id` and `date` as bringing more data trough the different `GROUP BY` statements up to the higher level is costly. Further more, additional `Group By` did not bring added value as the Chart was less readable. - The JS code has been adapted in order to force a group by both `stage_id` and `date` so that the date displayed is always making sense. - The sort ascending and descending options have been removed as creating confusing data. - The compare with previous period has also been removed as the chart only really make sense when seen chronologically. - A lot of tests have been added in order to ensure that changes that would be harmful for the report will trigger test fails. task-2845729 Co-authored-by: Yannick Tivisse <yti@odoo.com>
112 lines
4.7 KiB
Python
112 lines
4.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from odoo.tests.common import TransactionCase
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class TestProjectCommon(TransactionCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestProjectCommon, cls).setUpClass()
|
|
|
|
user_group_employee = cls.env.ref('base.group_user')
|
|
user_group_project_user = cls.env.ref('project.group_project_user')
|
|
user_group_project_manager = cls.env.ref('project.group_project_manager')
|
|
|
|
cls.partner_1 = cls.env['res.partner'].create({
|
|
'name': 'Valid Lelitre',
|
|
'email': 'valid.lelitre@agrolait.com'})
|
|
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})
|
|
cls.user_public = Users.create({
|
|
'name': 'Bert Tartignole',
|
|
'login': 'bert',
|
|
'email': 'b.t@example.com',
|
|
'signature': 'SignBert',
|
|
'notification_type': 'email',
|
|
'groups_id': [(6, 0, [cls.env.ref('base.group_public').id])]})
|
|
cls.user_portal = Users.create({
|
|
'name': 'Chell Gladys',
|
|
'login': 'chell',
|
|
'email': 'chell@gladys.portal',
|
|
'signature': 'SignChell',
|
|
'notification_type': 'email',
|
|
'groups_id': [(6, 0, [cls.env.ref('base.group_portal').id])]})
|
|
cls.user_projectuser = Users.create({
|
|
'name': 'Armande ProjectUser',
|
|
'login': 'armandel',
|
|
'password': 'armandel',
|
|
'email': 'armande.projectuser@example.com',
|
|
'groups_id': [(6, 0, [user_group_employee.id, user_group_project_user.id])]
|
|
})
|
|
cls.user_projectmanager = Users.create({
|
|
'name': 'Bastien ProjectManager',
|
|
'login': 'bastien',
|
|
'email': 'bastien.projectmanager@example.com',
|
|
'groups_id': [(6, 0, [user_group_employee.id, user_group_project_manager.id])]})
|
|
|
|
# Test 'Pigs' project
|
|
cls.project_pigs = cls.env['project.project'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Pigs',
|
|
'privacy_visibility': 'employees',
|
|
'alias_name': 'project+pigs',
|
|
'partner_id': cls.partner_1.id})
|
|
# Already-existing tasks in Pigs
|
|
cls.task_1 = cls.env['project.task'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Pigs UserTask',
|
|
'user_ids': cls.user_projectuser,
|
|
'project_id': cls.project_pigs.id})
|
|
cls.task_2 = cls.env['project.task'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Pigs ManagerTask',
|
|
'user_ids': cls.user_projectmanager,
|
|
'project_id': cls.project_pigs.id})
|
|
|
|
# Test 'Goats' project, same as 'Pigs', but with 2 stages
|
|
cls.project_goats = cls.env['project.project'].with_context({'mail_create_nolog': True}).create({
|
|
'name': 'Goats',
|
|
'privacy_visibility': 'followers',
|
|
'alias_name': 'project+goats',
|
|
'partner_id': cls.partner_1.id,
|
|
'type_ids': [
|
|
(0, 0, {
|
|
'name': 'New',
|
|
'sequence': 1,
|
|
}),
|
|
(0, 0, {
|
|
'name': 'Won',
|
|
'sequence': 10,
|
|
})]
|
|
})
|
|
|
|
def format_and_process(self, template, to='groups@example.com, other@gmail.com', subject='Frogs',
|
|
extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
|
|
cc='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>',
|
|
model=None, target_model='project.task', target_field='name'):
|
|
self.assertFalse(self.env[target_model].search([(target_field, '=', subject)]))
|
|
mail = template.format(to=to, subject=subject, cc=cc, extra=extra, email_from=email_from, msg_id=msg_id)
|
|
self.env['mail.thread'].message_process(model, mail)
|
|
return self.env[target_model].search([(target_field, '=', subject)])
|
|
|
|
|
|
class TestProjectBase(TestProjectCommon):
|
|
|
|
def test_delete_project_with_tasks(self):
|
|
"""User should never be able to delete a project with tasks"""
|
|
|
|
with self.assertRaises(UserError):
|
|
self.project_pigs.unlink()
|
|
|
|
# click on the archive button
|
|
self.project_pigs.write({'active': False})
|
|
|
|
with self.assertRaises(UserError):
|
|
self.project_pigs.unlink()
|