[FIX] hr_timesheet: correctly propagates project

Steps to reproduce:

- Create 2 projects: 'Project1' and 'Project2'
- Enable sub-tasks
- In Project1 create a task, name it '1'.
- In task 1 add a subtask, name it '1:1'.
- In task 1:1 add another subtask, name it '1:1:1'.
- Go to Timesheet app > add lines for each task.
- The lines display name are "Project1 / 1", ...
- Go back to task 1, edit the task project to Project2 and save.
- The subtasks will have their project_id also set to Project2.
- Go to Timesheet app.

Issue:
Tasks 1 and 1:1 will have their display names updated to "Project2 / 1"
and "Project2 / 1:1". However, task 1:1:1 is displayed as
"Project1 / 1:1:1", meaning it's project_id was never updated.

This happens because of the way changed fields propagate to immediate
children, but not to grandchildren, which is intended behaviour.

Solution:

`_compute_project_id` is also called onchange, meaning that we can use it
to update account lines with an outdated project value with the new value.

opw-3018301

closes odoo/odoo#109352

X-original-commit: 91b42c806ce2029d9ef678d145ced3138e35bec0
Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
Signed-off-by: Stefan-Calin Crainiciuc (stcc) <stcc@odoo.com>
This commit is contained in:
Stefan-Calin Crainiciuc (stcc)
2023-01-07 09:39:00 +01:00
parent 6059a7e20c
commit 56813501ea
3 changed files with 49 additions and 16 deletions
+3 -1
View File
@@ -95,7 +95,9 @@ class AccountAnalyticLine(models.Model):
@api.depends('task_id', 'task_id.project_id')
def _compute_project_id(self):
for line in self.filtered(lambda line: not line.project_id):
for line in self:
if not line.task_id.project_id or line.project_id == line.task_id.project_id:
continue
line.project_id = line.task_id.project_id
@api.depends('project_id')
+42 -15
View File
@@ -4,7 +4,7 @@
from lxml import etree
from odoo.fields import Command
from odoo.tests.common import TransactionCase
from odoo.tests.common import TransactionCase, Form
from odoo.exceptions import AccessError, UserError, ValidationError
@@ -238,36 +238,63 @@ class TestTimesheet(TestCommonTimesheet):
def test_transfert_project(self):
""" Transfert task with timesheet to another project. """
Timesheet = self.env['account.analytic.line']
# create nested subtasks
task_child = self.env['project.task'].create({
'name': 'Task Child',
'parent_id': self.task1.id,
})
task_grandchild = self.env['project.task'].create({
'name': 'Task Grandchild',
'parent_id': task_child.id,
})
# create a second project
self.project_customer2 = self.env['project.project'].create({
'name': 'Project NUMBER DEUX',
'allow_timesheets': True,
})
# employee 1 log some timesheet on task 1
Timesheet.create({
# employee 1 log some timesheet on task 1 and its subtasks
Timesheet.create([{
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'my first timesheet',
'unit_amount': 4,
'user_id': self.user_employee.id,
})
'employee_id': self.empl_employee.id,
}, {
'project_id': self.project_customer.id,
'task_id': task_child.id,
'name': 'my second timesheet',
'unit_amount': 4,
'employee_id': self.empl_employee.id,
}, {
'project_id': self.project_customer.id,
'task_id': task_grandchild.id,
'name': 'my third timesheet',
'unit_amount': 4,
'employee_id': self.empl_employee.id,
}])
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
self.assertEqual(timesheet_count1, 1, "One timesheet in project 1")
self.assertEqual(timesheet_count2, 0, "No timesheet in project 2")
self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet should be linked to task 1")
self.assertEqual(timesheet_count1, 3, "3 timesheets should be linked to Project1")
self.assertEqual(timesheet_count2, 0, "No timesheets should be linked to Project2")
self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet should be linked to task1")
self.assertEqual(len(task_child.timesheet_ids), 1, "The timesheet should be linked to task_child")
self.assertEqual(len(task_grandchild.timesheet_ids), 1, "The timesheet should be linked to task_grandchild")
# change project of task 1
self.task1.write({
'project_id': self.project_customer2.id
})
# change project of task 1 from form to trigger onchange
with Form(self.task1) as task_form:
task_form.project_id = self.project_customer2
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
self.assertEqual(timesheet_count1, 0, "No timesheet in project 1")
self.assertEqual(timesheet_count2, 1, "Still one timesheet in project 2")
self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet still should be linked to task 1")
self.assertEqual(timesheet_count1, 0, "There are still timesheets linked to Project1")
self.assertEqual(timesheet_count2, 3, "3 timesheets should be linked to Project2")
self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet still should be linked to task1")
self.assertEqual(len(task_child.timesheet_ids), 1, "The timesheet still should be linked to task_child")
self.assertEqual(len(task_grandchild.timesheet_ids), 1, "The timesheet still should be linked to task_grandchild")
# it is forbidden to set a task with timesheet without project
with self.assertRaises(UserError):
+4
View File
@@ -75,6 +75,10 @@ class AccountAnalyticLine(models.Model):
def _compute_partner_id(self):
super(AccountAnalyticLine, self.filtered(lambda t: t._is_not_billed()))._compute_partner_id()
@api.depends('timesheet_invoice_id.state')
def _compute_project_id(self):
super(AccountAnalyticLine, self.filtered(lambda t: t._is_not_billed()))._compute_project_id()
def _is_not_billed(self):
self.ensure_one()
return not self.timesheet_invoice_id or self.timesheet_invoice_id.state == 'cancel'