1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/project_timesheet_forecast/models/project_forecast.py
2024-12-10 09:04:09 +07:00

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