oca-ocb-project/odoo-bringout-oca-ocb-project_timesheet_holidays/project_timesheet_holidays/models/hr_leave.py
Ernad Husremovic 4a4d12c333 19.0 vanilla
2026-03-09 09:31:56 +01:00

132 lines
6 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, _
import pytz
class HrLeave(models.Model):
_inherit = "hr.leave"
timesheet_ids = fields.One2many('account.analytic.line', 'holiday_id', string="Analytic Lines")
def _validate_leave_request(self):
self._generate_timesheets()
return super()._validate_leave_request()
def _generate_timesheets(self, ignored_resource_calendar_leaves=None):
""" Timesheet will be generated on leave validation
internal_project_id and leave_timesheet_task_id are used.
The generated timesheet will be attached to this project/task.
"""
vals_list = []
leave_ids = []
calendar_leaves_data = self.env['resource.calendar.leaves']._read_group([('holiday_id', 'in', self.ids)], ['holiday_id'], ['id:array_agg'])
mapped_calendar_leaves = {leave: calendar_leave_ids[0] for leave, calendar_leave_ids in calendar_leaves_data}
for leave in self:
project, task = leave.employee_id.company_id.internal_project_id, leave.employee_id.company_id.leave_timesheet_task_id
if not project or not task or leave.holiday_status_id.time_type == 'other':
continue
leave_ids.append(leave.id)
if not leave.employee_id:
continue
calendar = leave.employee_id.resource_calendar_id
calendar_timezone = pytz.timezone((calendar or leave.employee_id).tz)
if calendar.flexible_hours and (leave.request_unit_hours or leave.request_unit_half or leave.date_from.date() == leave.date_to.date()):
leave_date = leave.date_from.astimezone(calendar_timezone).date()
if leave.request_unit_hours:
hours = leave.request_hour_to - leave.request_hour_from
elif leave.request_unit_half:
hours = calendar.hours_per_day / 2
else: # Single-day leave
hours = calendar.hours_per_day
work_hours_data = [(leave_date, hours)]
else:
ignored_resource_calendar_leaves = ignored_resource_calendar_leaves or []
if leave in mapped_calendar_leaves:
ignored_resource_calendar_leaves.append(mapped_calendar_leaves[leave])
work_hours_data = leave.employee_id._list_work_time_per_day(
leave.date_from,
leave.date_to,
domain=[('id', 'not in', ignored_resource_calendar_leaves)] if ignored_resource_calendar_leaves else None)[leave.employee_id.id]
for index, (day_date, work_hours_count) in enumerate(work_hours_data):
vals_list.append(leave._timesheet_prepare_line_values(index, work_hours_data, day_date, work_hours_count, project, task))
# Unlink previous timesheets to avoid doublon (shouldn't happen on the interface but meh). Necessary when the function is called to regenerate timesheets.
old_timesheets = self.env["account.analytic.line"].sudo().search([('project_id', '!=', False), ('holiday_id', 'in', leave_ids)])
if old_timesheets:
old_timesheets.holiday_id = False
old_timesheets.unlink()
self.env['account.analytic.line'].sudo().create(vals_list)
def _timesheet_prepare_line_values(self, index, work_hours_data, day_date, work_hours_count, project, task):
self.ensure_one()
return {
'name': _("Time Off (%(index)s/%(total)s)", index=index + 1, total=len(work_hours_data)),
'project_id': project.id,
'task_id': task.id,
'account_id': project.sudo().account_id.id,
'unit_amount': work_hours_count,
'user_id': self.employee_id.user_id.id,
'date': day_date,
'holiday_id': self.id,
'employee_id': self.employee_id.id,
'company_id': task.sudo().company_id.id or project.sudo().company_id.id,
}
def _check_missing_global_leave_timesheets(self):
if not self:
return
min_date = min(self.mapped('date_from'))
max_date = max(self.mapped('date_to'))
global_leaves = self.env['resource.calendar.leaves'].search([
("resource_id", "=", False),
("date_to", ">=", min_date),
("date_from", "<=", max_date),
("company_id.internal_project_id", "!=", False),
("company_id.leave_timesheet_task_id", "!=", False),
])
if global_leaves:
global_leaves._generate_public_time_off_timesheets(self.employee_id)
def action_refuse(self):
""" Remove the timesheets linked to the refused holidays """
result = super().action_refuse()
timesheets = self.sudo().mapped('timesheet_ids')
timesheets.write({'holiday_id': False})
timesheets.unlink()
self._check_missing_global_leave_timesheets()
return result
def _action_user_cancel(self, reason=None):
res = super()._action_user_cancel(reason)
timesheets = self.sudo().timesheet_ids
timesheets.write({'holiday_id': False})
timesheets.unlink()
self._check_missing_global_leave_timesheets()
return res
def _force_cancel(self, *args, **kwargs):
super()._force_cancel(*args, **kwargs)
# override this method to reevaluate timesheets after the leaves are updated via force cancel
timesheets = self.sudo().timesheet_ids
timesheets.holiday_id = False
timesheets.unlink()
def write(self, vals):
res = super().write(vals)
# reevaluate timesheets after the leaves are wrote in order to remove empty timesheets
timesheet_ids_to_remove = []
for leave in self:
if leave.number_of_days == 0 and leave.sudo().timesheet_ids:
leave.sudo().timesheet_ids.holiday_id = False
timesheet_ids_to_remove.extend(leave.timesheet_ids)
self.env['account.analytic.line'].browse(set(timesheet_ids_to_remove)).sudo().unlink()
return res