Files

185 lines
9.1 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
_inherit = ['helpdesk.ticket', 'timer.parent.mixin']
def _default_team_id(self):
if project_id := self.env.context.get('default_project_id'):
if team_id := self.env['helpdesk.team'].search([('project_id', '=', project_id)], limit=1).id:
return team_id
return super()._default_team_id()
team_id = fields.Many2one(domain="[('use_helpdesk_timesheet', '=', True)] if context.get('default_project_id') else []")
project_id = fields.Many2one(
"project.project", related="team_id.project_id", readonly=True, store=True, index='btree_not_null')
timesheet_ids = fields.One2many('account.analytic.line', 'helpdesk_ticket_id', 'Timesheets',
help="Time spent on this ticket. By default, your timesheets will be linked to the sales order item of your ticket.\n"
"Remove the sales order item to make your timesheet entries non billable.")
use_helpdesk_timesheet = fields.Boolean('Timesheet activated on Team', related='team_id.use_helpdesk_timesheet', readonly=True)
display_timesheet_timer = fields.Boolean("Display Timesheet Time", compute='_compute_display_timesheet_timer')
timesheet_unit_amount = fields.Float(compute='_compute_timesheet_unit_amount')
total_hours_spent = fields.Float("Time Spent", compute='_compute_total_hours_spent', default=0, compute_sudo=True, store=True, aggregator="avg")
encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days', export_string_translation=False)
analytic_account_id = fields.Many2one('account.analytic.account',
compute='_compute_analytic_account_id', store=True, readonly=False,
string='Analytic Account', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
def _compute_encode_uom_in_days(self):
self.encode_uom_in_days = self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day')
@api.depends('use_helpdesk_timesheet', 'timesheet_ids', 'encode_uom_in_days')
def _compute_display_timesheet_timer(self):
for ticket in self:
ticket.display_timesheet_timer = ticket.use_helpdesk_timesheet and not ticket.encode_uom_in_days
@api.depends('user_timer_id')
def _compute_timesheet_unit_amount(self):
if not any(self._ids):
for ticket in self:
unit_amount = 0.0
if ticket.user_timer_id:
unit_amount = ticket.filtered(lambda t: (t.id or t.origin.id) == ticket.user_timer_id.id).unit_amount or 0.0
ticket.timesheet_unit_amount = unit_amount
return
timesheet_id_per_timer_id = {ticket.user_timer_id.id: ticket.user_timer_id.res_id for ticket in self}
timesheet_read = self.env['account.analytic.line'].search_read(
[('id', 'in', list(timesheet_id_per_timer_id.values()))],
['unit_amount'],
)
unit_amount_per_timesheet_id = {res['id']: res['unit_amount'] for res in timesheet_read}
for ticket in self:
timesheet_id = ticket.user_timer_id.id or timesheet_id_per_timer_id.get(ticket.user_timer_id.id, False)
if timesheet_id:
ticket.timesheet_unit_amount = unit_amount_per_timesheet_id.get(timesheet_id, 0.0)
else:
ticket.timesheet_unit_amount = 0.0
@api.depends('timesheet_ids.unit_amount')
def _compute_total_hours_spent(self):
if not any(self._ids):
for ticket in self:
ticket.total_hours_spent = sum(ticket.timesheet_ids.mapped('unit_amount'))
return
timesheet_read_group = self.env['account.analytic.line']._read_group(
[('helpdesk_ticket_id', 'in', self.ids)],
['helpdesk_ticket_id'],
['unit_amount:sum'],
)
timesheets_per_ticket = {helpdesk_ticket.id: unit_amount_sum for helpdesk_ticket, unit_amount_sum in timesheet_read_group}
for ticket in self:
ticket.total_hours_spent = timesheets_per_ticket.get(ticket.id, 0.0)
@api.onchange('team_id')
def _onchange_team_id(self):
# If the new helpdesk team has no timesheet feature AND ticket has non-validated timesheets, show a warning message
if (
self.timesheet_ids and
not self.team_id.use_helpdesk_timesheet and
not all(t.validated for t in self.timesheet_ids)
):
return {
'warning': {
'title': _("Warning"),
'message': _("Moving this task to a helpdesk team without timesheet support will retain timesheet drafts in the original helpdesk team. "
"Although they won't be visible here, you can still edit them using the Timesheets app."),
'type': "notification",
},
}
@api.model_create_multi
def create(self, vals_list):
default_project_id = self.env.context.get('default_project_id')
for vals in vals_list:
project_id = vals.get('project_id') or default_project_id
if not vals.get('team_id') and project_id:
project = self.env['project.project'].browse(project_id)
if project.helpdesk_team:
vals['team_id'] = project.helpdesk_team[0].id
return super().create(vals_list)
@api.depends('project_id')
def _compute_analytic_account_id(self):
for ticket in self:
ticket.analytic_account_id = ticket.project_id.account_id
@api.depends('use_helpdesk_timesheet')
def _compute_display_extra_info(self):
if self.env.user.has_group('analytic.group_analytic_accounting'):
show_analytic_account_id_records = self.filtered('use_helpdesk_timesheet')
show_analytic_account_id_records.display_extra_info = True
super(HelpdeskTicket, self - show_analytic_account_id_records)._compute_display_extra_info()
else:
super()._compute_display_extra_info()
def write(self, vals):
res = super().write(vals)
if vals.get('team_id'):
if not self.team_id.use_helpdesk_timesheet:
timers = self.env['timer.timer'].search([
('parent_res_model', '=', 'helpdesk.ticket'),
('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()
timesheet_read_group = self.env['account.analytic.line']._read_group(
[('project_id', '!=', False), ('helpdesk_ticket_id', 'in', self.ids), ('validated', '=', False)],
['helpdesk_ticket_id'],
['id:recordset'],
)
for ticket, timesheets in timesheet_read_group:
if ticket.use_helpdesk_timesheet and ticket.project_id:
timesheets_to_update = timesheets.filtered(lambda t: t.project_id != ticket.project_id)
timesheets_to_update.project_id = ticket.project_id
else:
timesheets.helpdesk_ticket_id = False
return res
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 _create_record_to_start_timer(self):
""" Create a timesheet to launch a timer """
return self.env['account.analytic.line'].create({
'helpdesk_ticket_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()