166 lines
7.5 KiB
Python
166 lines
7.5 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import models, fields, api, _
|
|
|
|
|
|
class ProjectTask(models.Model):
|
|
_name = 'project.task'
|
|
_inherit = ["project.task", "timer.parent.mixin", "timesheet.grid.mixin"]
|
|
|
|
timer_start = fields.Datetime(groups='hr_timesheet.group_hr_timesheet_user')
|
|
timer_pause = fields.Datetime(groups='hr_timesheet.group_hr_timesheet_user')
|
|
is_timer_running = fields.Boolean(groups='hr_timesheet.group_hr_timesheet_user')
|
|
user_timer_id = fields.One2many(groups='hr_timesheet.group_hr_timesheet_user')
|
|
timesheet_unit_amount = fields.Float(compute='_compute_timesheet_unit_amount', groups='hr_timesheet.group_hr_timesheet_user')
|
|
display_timesheet_timer = fields.Boolean("Display Timesheet Time", compute='_compute_display_timesheet_timer', export_string_translation=False, groups='hr_timesheet.group_hr_timesheet_user')
|
|
|
|
@api.depends('user_timer_id')
|
|
def _compute_timesheet_unit_amount(self):
|
|
if not any(self._ids):
|
|
for task in self:
|
|
unit_amount = 0.0
|
|
if task.user_timer_id:
|
|
unit_amount = task.filtered(lambda t: (t.id or t.origin.id) == task.user_timer_id.id).unit_amount or 0.0
|
|
task.timesheet_unit_amount = unit_amount
|
|
return
|
|
timesheet_read = self.env['account.analytic.line'].search_read(
|
|
[('id', 'in', self.user_timer_id.mapped('res_id'))],
|
|
['unit_amount'],
|
|
)
|
|
unit_amount_per_timesheet_id = {res['id']: res['unit_amount'] for res in timesheet_read}
|
|
for task in self:
|
|
timesheet_id = task.user_timer_id.res_id
|
|
if timesheet_id:
|
|
task.timesheet_unit_amount = unit_amount_per_timesheet_id.get(timesheet_id, 0.0)
|
|
else:
|
|
task.timesheet_unit_amount = 0.0
|
|
|
|
@api.depends('allow_timesheets', 'analytic_account_active')
|
|
def _compute_display_timesheet_timer(self):
|
|
user_has_employee_or_only_one = None
|
|
for task in self:
|
|
display_timesheet_timer = task.allow_timesheets and task.analytic_account_active and not task.encode_uom_in_days
|
|
if display_timesheet_timer:
|
|
if user_has_employee_or_only_one is None:
|
|
user_has_employee_or_only_one = bool(self.env.user.employee_id) \
|
|
or self.env['hr.employee'].sudo().search_count([('user_id', '=', self.env.uid)]) == 1
|
|
display_timesheet_timer = user_has_employee_or_only_one
|
|
task.display_timesheet_timer = display_timesheet_timer
|
|
|
|
def _compute_allocated_hours(self):
|
|
# Only change values when creating a new record from the gantt view
|
|
# or the existing tasks that doesn't allow timesheets
|
|
timsheeted_tasks = self.filtered(lambda task: task._origin and task.allow_timesheets)
|
|
super(ProjectTask, self - timsheeted_tasks)._compute_allocated_hours()
|
|
|
|
@api.onchange('project_id')
|
|
def _onchange_project_id(self):
|
|
# If task has non-validated timesheets AND new project has not the timesheets feature enabled, raise a warning notification
|
|
if not all(t.validated for t in self.timesheet_ids) and not self.project_id.allow_timesheets:
|
|
return {
|
|
'warning': {
|
|
'title': _("Warning"),
|
|
'message': _("Moving this task to a project without timesheet support will retain timesheet drafts in the original project. "
|
|
"Although they won't be visible here, you can still edit them using the Timesheets app."),
|
|
'type': "notification",
|
|
},
|
|
}
|
|
|
|
def write(self, vals):
|
|
res = super().write(vals)
|
|
if vals.get('project_id') and not self.project_id.sudo().allow_timesheets:
|
|
timers = self.env['timer.timer'].sudo().search([
|
|
('parent_res_model', '=', 'project.task'),
|
|
('parent_res_id', 'in', self.ids),
|
|
('res_model', '=', 'account.analytic.line'),
|
|
])
|
|
if timers:
|
|
timesheets = self.env['account.analytic.line'].browse(timers.mapped('res_id')).sudo()
|
|
timers.unlink()
|
|
if timesheets_to_remove := timesheets.filtered(lambda t: t.unit_amount == 0):
|
|
timesheets_to_remove.unlink()
|
|
return res
|
|
|
|
def _set_allocated_hours_for_tasks(self):
|
|
super(ProjectTask, self.filtered(lambda task: not task.allow_timesheets))._set_allocated_hours_for_tasks()
|
|
|
|
def _gantt_progress_bar_project_id(self, res_ids):
|
|
timesheet_read_group = self.env['account.analytic.line'].sudo()._read_group(
|
|
[('project_id', 'in', res_ids)],
|
|
['project_id'],
|
|
['unit_amount:sum'],
|
|
)
|
|
return {
|
|
project.id: {
|
|
'value': unit_amount_sum,
|
|
'max_value': project.sudo().allocated_hours,
|
|
}
|
|
for project, unit_amount_sum in timesheet_read_group
|
|
}
|
|
|
|
def _gantt_progress_bar(self, field, res_ids, start, stop):
|
|
if field == 'project_id':
|
|
return dict(
|
|
self._gantt_progress_bar_project_id(res_ids),
|
|
warning=_("This project isn't expected to have task during this period."),
|
|
)
|
|
return super()._gantt_progress_bar(field, res_ids, start, stop)
|
|
|
|
def action_view_subtask_timesheet(self):
|
|
action = super().action_view_subtask_timesheet()
|
|
grid_view_id = self.env.ref('timesheet_grid.timesheet_view_grid_by_employee').id
|
|
action['views'] = [
|
|
[grid_view_id, view_mode] if view_mode == 'grid' else [view_id, view_mode]
|
|
for view_id, view_mode in action['views']
|
|
]
|
|
return action
|
|
|
|
def action_timer_start(self):
|
|
if self.display_timesheet_timer:
|
|
super().action_timer_start()
|
|
|
|
def action_timer_stop(self):
|
|
# timer was either running or paused
|
|
if self.display_timesheet_timer and self.user_timer_id:
|
|
timesheet = self._get_record_with_timer_running()
|
|
if timesheet:
|
|
return {
|
|
"name": _("Confirm Time Spent"),
|
|
"type": "ir.actions.act_window",
|
|
"res_model": 'hr.timesheet.stop.timer.confirmation.wizard',
|
|
'context': {
|
|
'default_timesheet_id': timesheet.id,
|
|
'dialog_size': 'medium',
|
|
},
|
|
"views": [[self.env.ref('timesheet_grid.hr_timesheet_stop_timer_confirmation_wizard_view_form').id, "form"]],
|
|
"target": 'new',
|
|
}
|
|
else:
|
|
return super().action_timer_stop()
|
|
return False
|
|
|
|
def get_allocated_hours_field(self):
|
|
return 'allocated_hours'
|
|
|
|
def get_worked_hours_fields(self):
|
|
return ['effective_hours', 'subtask_effective_hours']
|
|
|
|
def _get_hours_to_plan(self):
|
|
return self.remaining_hours
|
|
|
|
def _create_record_to_start_timer(self):
|
|
""" Create a timesheet to launch a timer """
|
|
return self.env['account.analytic.line'].create({
|
|
'task_id': self.id,
|
|
'project_id': self.project_id.id,
|
|
'date': fields.Date.context_today(self),
|
|
'name': '/',
|
|
'user_id': self.env.uid,
|
|
})
|
|
|
|
def _action_interrupt_user_timers(self):
|
|
""" Call action interrupt user timers to launch a new one
|
|
Stop the existing runnning timer before launching a new one.
|
|
"""
|
|
self.action_timer_stop()
|