Skip to content

Commit

Permalink
[FIX] hr_timesheet: display correct remaining hours on project
Browse files Browse the repository at this point in the history
Steps to reproduce the bug:

  - Install hr_timesheet module
  - Create a project
  - Create task and set planned hours to 2 minutes
  - Add a new timesheet line for the task with duration of 2 minutes
  - Create a subtask and set planned hours to 2 minutes
  - Add a new timesheet line for the sub-task with duration of 2 minutes
  - Go back to project (kanban view)

Issue:

  Wrong remaining hours displayed on project (00:00 instead of 00:02).

Cause:

  Doing the sum of the remaining hours of all project's tasks while the
  parent tasks (that are in the project) should include remaining hours
  of all sub tasks.

Solution:

  Use planned hours (from tasks) minus timesheets unit_amount
  (from project) to calculate remaining hours.

opw-2916546

closes odoo#99444

Signed-off-by: Xavier <xbo@odoo.com>
  • Loading branch information
nboulif committed Sep 28, 2022
1 parent f0085db commit d8418e5
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 13 deletions.
28 changes: 15 additions & 13 deletions addons/hr_timesheet/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,23 @@ def _search_is_internal_project(self, operator, value):
operator_new = 'not inselect'
return [('id', operator_new, (query, ()))]

@api.depends('allow_timesheets', 'task_ids.planned_hours', 'task_ids.remaining_hours')
@api.depends('allow_timesheets', 'task_ids.planned_hours', 'timesheet_ids')
def _compute_remaining_hours(self):
group_read = self.env['project.task'].read_group(
domain=[('planned_hours', '!=', False), ('project_id', 'in', self.filtered('allow_timesheets').ids),
'|', ('stage_id.fold', '=', False), ('stage_id', '=', False)],
fields=['planned_hours:sum', 'remaining_hours:sum'], groupby='project_id')
group_per_project_id = {group['project_id'][0]: group for group in group_read}
timesheet_read_group = self.env['account.analytic.line'].read_group(
domain=[('project_id', 'in', self.filtered('allow_timesheets').ids), ('task_id', '!=', False),
'|', ('task_id.stage_id.fold', '=', False), ('task_id.stage_id', '=', False)],
fields=['effective_hours:sum(unit_amount)'], groupby='project_id')
task_read_group = self.env['project.task'].read_group(
domain=[('planned_hours', '!=', 0.0), ('project_id', 'in', self.filtered('allow_timesheets').ids),
('parent_id', '=', False), '|', ('stage_id.fold', '=', False), ('stage_id', '=', False)],
fields=['planned_hours:sum', ], groupby='project_id')
effective_hours_per_project_id = {res['project_id'][0]: res['effective_hours'] for res in timesheet_read_group}
planned_hours_per_project_id = {res['project_id'][0]: res['planned_hours'] for res in task_read_group}
for project in self:
group = group_per_project_id.get(project.id)
if group:
project.remaining_hours = group.get('remaining_hours')
project.has_planned_hours_tasks = bool(group.get('planned_hours'))
else:
project.remaining_hours = 0
project.has_planned_hours_tasks = False
planned_hours = planned_hours_per_project_id.get(project.id, 0.0)
effective_hours = effective_hours_per_project_id.get(project.id, 0.0)
project.remaining_hours = planned_hours - effective_hours if planned_hours else 0.0
project.has_planned_hours_tasks = project.id in planned_hours_per_project_id

@api.constrains('allow_timesheets', 'analytic_account_id')
def _check_allow_timesheet(self):
Expand Down
29 changes: 29 additions & 0 deletions addons/hr_timesheet/tests/test_timesheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ def test_ensure_product_uom_set_in_timesheet(self):
timesheet1.unit_amount + timesheet2.unit_amount,
'The total timesheet time of this project should be equal to 4.'
)

def test_create_timesheet_with_archived_employee(self):
''' the timesheet can be created or edited only with an active employee
'''
Expand All @@ -438,3 +439,31 @@ def test_create_timesheet_with_archived_employee(self):

with self.assertRaises(UserError):
timesheet.employee_id = self.empl_employee2

def test_remaining_hours_on_project(self):
''' Test the remaining hours on the project '''
self.task1.planned_hours = 4.0

self.assertEqual(self.project_customer.remaining_hours, 4.0, 'Remaining hours should be 4.0 before adding timesheet')

self.env['account.analytic.line'].create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'test',
'unit_amount': 2,
})
self.assertEqual(self.project_customer.remaining_hours, 2.0, 'Remaining hours should be 2.0 after adding timesheet')

task_child = self.env['project.task'].create({
'name': 'Task Child',
'parent_id': self.task1.id,
})
self.assertEqual(self.project_customer.remaining_hours, 2.0, 'Remaining hours should be 2.0 after adding a child task with no timesheets')

self.env['account.analytic.line'].create({
'project_id': self.project_customer.id,
'task_id': task_child.id,
'name': 'test',
'unit_amount': 2,
})
self.assertEqual(self.project_customer.remaining_hours, 0.0, 'Remaining hours should be 0.0 after adding timesheet to child task')

0 comments on commit d8418e5

Please sign in to comment.