forked from Mapan/odoo17e
152 lines
6.9 KiB
Python
152 lines
6.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import pytz
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.osv import expression
|
|
|
|
|
|
class Forecast(models.Model):
|
|
_inherit = 'planning.slot'
|
|
|
|
allow_timesheets = fields.Boolean("Allow timesheets", related='project_id.allow_timesheets', help="Timesheets can be logged on this slot.", readonly=True)
|
|
effective_hours = fields.Float("Effective Hours", compute='_compute_effective_hours', compute_sudo=True, store=True,
|
|
help="Number of hours the employee recorded on their Timesheetes for this task (and its sub-tasks) for the period of this shift.")
|
|
timesheet_ids = fields.Many2many('account.analytic.line', compute='_compute_timesheet_ids', compute_sudo=True)
|
|
can_open_timesheets = fields.Boolean(compute='_compute_can_open_timesheet')
|
|
percentage_hours = fields.Float("Progress", compute='_compute_percentage_hours', compute_sudo=True, store=True)
|
|
encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days')
|
|
|
|
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('allocated_hours', 'effective_hours')
|
|
def _compute_percentage_hours(self):
|
|
for forecast in self:
|
|
if forecast.allocated_hours:
|
|
forecast.percentage_hours = forecast.effective_hours / forecast.allocated_hours * 100
|
|
else:
|
|
forecast.percentage_hours = 0
|
|
|
|
def _get_timesheet_domain(self):
|
|
'''
|
|
Returns the domain used to fetch the timesheets, None is returned in case there would be no match
|
|
'''
|
|
self.ensure_one()
|
|
if not self.project_id:
|
|
return None
|
|
domain = [
|
|
('employee_id', '=', self.employee_id.id),
|
|
('date', '>=', self.start_datetime.date()),
|
|
('date', '<=', self.end_datetime.date())
|
|
]
|
|
if self.project_id:
|
|
domain = expression.AND([[('project_id', '=', self.project_id.id)], domain])
|
|
return domain
|
|
|
|
@api.depends('timesheet_ids')
|
|
def _compute_effective_hours(self):
|
|
for forecast in self:
|
|
forecast.effective_hours = sum(
|
|
timesheet.unit_amount
|
|
for timesheet in forecast.timesheet_ids
|
|
)
|
|
|
|
@api.depends('employee_id', 'start_datetime', 'end_datetime', 'project_id.timesheet_ids.unit_amount')
|
|
def _compute_timesheet_ids(self):
|
|
self.timesheet_ids = False
|
|
Timesheet = self.env['account.analytic.line']
|
|
for forecast in self:
|
|
if forecast.project_id and forecast.start_datetime and forecast.end_datetime:
|
|
domain = forecast._get_timesheet_domain()
|
|
if domain:
|
|
forecast.timesheet_ids = Timesheet.search(domain)
|
|
|
|
def _read_group_fields_nullify(self):
|
|
return super()._read_group_fields_nullify() + ['effective_hours', 'effective_hours_cost', 'percentage_hours']
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('user_id', 'timesheet_ids')
|
|
def _compute_can_open_timesheet(self):
|
|
# A timesheet approver will be able to open any slot's timesheets, however
|
|
# a regular employee will need to be a timesheet user AND be assigned to this slot
|
|
# to be able to open them.
|
|
is_approver = self.user_has_groups('hr_timesheet.group_hr_timesheet_approver')
|
|
is_user = is_approver or self.user_has_groups('hr_timesheet.group_hr_timesheet_user')
|
|
if not is_user:
|
|
self.can_open_timesheets = False
|
|
else:
|
|
for slot in self:
|
|
if (is_approver or (is_user and self.env.user == slot.user_id)):
|
|
slot.can_open_timesheets = True
|
|
else:
|
|
slot.can_open_timesheets = False
|
|
|
|
def _gantt_progress_bar_project_id(self, res_ids, start, stop):
|
|
planning_read_group = self.env['planning.slot']._read_group(
|
|
[('project_id', 'in', res_ids), ('start_datetime', '<=', stop), ('end_datetime', '>=', start)],
|
|
['project_id'],
|
|
['allocated_hours:sum'],
|
|
)
|
|
dict_values_per_project = {
|
|
project.id: {
|
|
'value': allocated_hours_sum,
|
|
'max_value': project.sudo().allocated_hours
|
|
}
|
|
for project, allocated_hours_sum in planning_read_group
|
|
}
|
|
project_dict = {
|
|
project.id: project.allocated_hours
|
|
for project in self.env['project.project'].sudo().search([('id', 'in', res_ids)])
|
|
}
|
|
for project_id, allocated_hours in project_dict.items():
|
|
if project_id not in dict_values_per_project:
|
|
dict_values_per_project[project_id] = {
|
|
'value': 0.0,
|
|
'max_value': allocated_hours,
|
|
}
|
|
return dict_values_per_project
|
|
|
|
def _gantt_progress_bar(self, field, res_ids, start, stop):
|
|
if field == 'project_id':
|
|
return dict(
|
|
self._gantt_progress_bar_project_id(res_ids, start, stop),
|
|
warning=_("This project isn't expected to have slot during this period. Planned hours :"),
|
|
)
|
|
return super()._gantt_progress_bar(field, res_ids, start, stop)
|
|
|
|
def action_open_timesheets(self):
|
|
self.ensure_one()
|
|
action = self.env['ir.actions.act_window']._for_xml_id('hr_timesheet.timesheet_action_all')
|
|
# Remove all references to the original action, to avoid studio and be able to change the action name
|
|
action.pop('id', None)
|
|
action.pop('xml_id', None)
|
|
action.pop('display_name', None)
|
|
action.update({
|
|
'name': _('Timesheets'),
|
|
'domain': self._get_timesheet_domain(),
|
|
'view_mode': 'tree,grid,kanban,pivot,graph,form',
|
|
'mobile_view_mode': 'grid',
|
|
'views': [
|
|
[self.env.ref('hr_timesheet.timesheet_view_tree_user').id, 'tree'],
|
|
[self.env.ref('timesheet_grid.timesheet_view_grid_by_employee').id, 'grid'],
|
|
[self.env.ref('hr_timesheet.view_kanban_account_analytic_line').id, 'kanban'],
|
|
[self.env.ref('hr_timesheet.view_hr_timesheet_line_pivot').id, 'pivot'],
|
|
[self.env.ref('hr_timesheet.view_hr_timesheet_line_graph_all').id, 'graph'],
|
|
[self.env.ref('hr_timesheet.hr_timesheet_line_form').id, 'form'],
|
|
],
|
|
})
|
|
action['context'] = {
|
|
'default_date': self.start_datetime.date()\
|
|
if self.start_datetime < fields.Datetime.now() else fields.Date.today(),
|
|
'default_employee_id': self.employee_id.id,
|
|
'default_project_id': self.project_id.id,
|
|
'grid_anchor': self.start_datetime.date(),
|
|
}
|
|
if self.duration < 24:
|
|
action['context']['default_unit_amount'] = self.allocated_hours
|
|
return action
|