[IMP] *_timesheet: don't modify past timesheet when transfering task

Impacted modules: hr_timesheet, sale_timesheet

When task is changing project or sale order line, we
don't want anymore to change the linked timesheet.
We now consider a timesheet is done in a specific time
at certain condition that does not have to change when
the environment is modified afterwards.

So, a timesheet linked to an analytic account for a
certain project on a task (and mybe linked to a SO line)
will still be linked to the same object if the task is
billed with another SO or move into a new project.

The idea is task actual configuration (project, AA, SO
line) are the default value at timesheet creation. As
consequences, timesheet from another project can be linked
to a task that does not belong to that project.
Project and task can be modified thougth the UI of timesheet
app. SO line and analytic account of a timesheet can not
be modified as they are more sensitive field (in a business
point of view), especially for invoiced timesheets.

This commit revert opw-1803811, as at that time the
'inheritS' between project and AA was still effective,
and there was only one billable type (task rate) for
task. The introduction of 'employee rate' billing type
will complicate timesheet transfert.

Tasks #34703
This commit is contained in:
jem-odoo
2018-08-13 17:32:22 +02:00
parent 02d565cc0e
commit 20cc64f79e
4 changed files with 14 additions and 33 deletions
+7 -13
View File
@@ -135,22 +135,16 @@ class Task(models.Model):
for task in self:
task.subtask_effective_hours = sum(child_task.effective_hours + child_task.subtask_effective_hours for child_task in task.child_ids)
# ---------------------------------------------------------
# ORM
# ---------------------------------------------------------
@api.multi
def write(self, values):
result = super(Task, self).write(values)
# reassign project_id on related timesheet lines
if 'project_id' in values:
project_id = values.get('project_id')
# a timesheet must have an analytic account (and a project)
if self and not project_id:
# a timesheet must have an analytic account (and a project)
if 'project_id' in values and self and not values.get('project_id'):
raise UserError(_('This task must be part of a project because they some timesheets are linked to it.'))
account_id = self.env['project.project'].browse(project_id).sudo().analytic_account_id
self.sudo().mapped('timesheet_ids').write({
'project_id': project_id,
'account_id': account_id.id,
'company_id': account_id.company_id.id,
})
return result
return super(Task, self).write(values)
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
+4 -4
View File
@@ -146,7 +146,7 @@ class TestTimesheet(TestCommonTimesheet):
self.assertEquals(timesheet1.user_id, self.user_employee2, 'Changing timesheet employee should change the related user')
def test_transfert_project(self):
""" Test transfert task with timesheet to another project """
""" Transfert task with timesheet to another project should not modified past timesheets (they are still linked to old project. """
Timesheet = self.env['account.analytic.line']
# create a second project
self.project_customer2 = self.env['project.project'].create({
@@ -174,9 +174,9 @@ class TestTimesheet(TestCommonTimesheet):
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
self.assertEquals(timesheet_count1, 0, "No timesheet in project 1")
self.assertEquals(timesheet_count2, 1, "One timesheet in project 2")
self.assertEquals(len(self.task1.timesheet_ids), 1, "The timesheet should be linked to task 1")
self.assertEquals(timesheet_count1, 1, "Still one timesheet in project 1")
self.assertEquals(timesheet_count2, 0, "No timesheet in project 2")
self.assertEquals(len(self.task1.timesheet_ids), 1, "The timesheet still should be linked to task 1")
# it is forbidden to set a task with timesheet without project
with self.assertRaises(UserError):
-10
View File
@@ -92,16 +92,6 @@ class ProjectTask(models.Model):
if not task.sale_line_id.is_service or task.sale_line_id.is_expense:
raise ValidationError(_('You cannot link the order item %s - %s to this task because it is a re-invoiced expense.' % (task.sale_line_id.order_id.id, task.sale_line_id.product_id.name)))
@api.multi
def write(self, values):
result = super(ProjectTask, self).write(values)
# reassign SO line on related timesheet lines
if 'sale_line_id' in values:
self.sudo().mapped('timesheet_ids').write({
'so_line': values['sale_line_id']
})
return result
@api.multi
def unlink(self):
if any(task.sale_line_id for task in self):
@@ -160,11 +160,8 @@ class TestSaleService(TestCommonSaleTimesheetNoChart):
# make task non billable
task_serv2.write({'sale_line_id': False})
self.assertTrue(all([billing_type == 'non_billable' for billing_type in timesheets.mapped('timesheet_invoice_type')]), "Timesheet to a non billable task should be non billable too")
# make task billable again
task_serv2.write({'sale_line_id': so_line_deliver_global_project.id})
self.assertTrue(all([billing_type == 'billable_time' for billing_type in timesheets.mapped('timesheet_invoice_type')]), "Timesheet to a billable time task should be billable")
self.assertTrue(all([billing_type == 'billable_time' for billing_type in timesheets.mapped('timesheet_invoice_type')]), "billable type of timesheet should not change when tranfering task into another project")
self.assertEqual(task_serv2.timesheet_ids.mapped('so_line'), so_line_deliver_global_project, "Old timesheet are not modified when changing the task SO line")
# invoice SO, and validate invoice
invoice_id = self.sale_order.action_invoice_create()[0]
@@ -173,7 +170,7 @@ class TestSaleService(TestCommonSaleTimesheetNoChart):
# try to update timesheets, catch error 'You cannot modify invoiced timesheet'
with self.assertRaises(UserError):
task_serv2.write({'sale_line_id': False})
timesheets.write({'so_line': False})
def test_delivered_quantity(self):
# create SO line and confirm it