Odoo18-Base/addons/hr_work_entry_holidays/models/hr_contract.py
2025-03-10 11:12:23 +07:00

139 lines
6.6 KiB
Python

# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
import pytz
from datetime import date, datetime
from odoo import api, models
from odoo.osv.expression import OR
class HrContract(models.Model):
_inherit = 'hr.contract'
_description = 'Employee Contract'
@api.constrains('date_start', 'date_end', 'state')
def _check_contracts(self):
self._get_leaves()._check_contracts()
def _get_leaves(self):
return self.env['hr.leave'].search([
('state', '!=', 'refuse'),
('employee_id', 'in', self.mapped('employee_id.id')),
('date_from', '<=', max([end or date.max for end in self.mapped('date_end')])),
('date_to', '>=', min(self.mapped('date_start'))),
])
# override to add work_entry_type from leave
def _get_leave_work_entry_type(self, leave):
if leave.holiday_id:
return leave.holiday_id.holiday_status_id.work_entry_type_id
else:
return leave.work_entry_type_id
def _get_more_vals_leave_interval(self, interval, leaves):
result = super()._get_more_vals_leave_interval(interval, leaves)
for leave in leaves:
if interval[0] >= leave[0] and interval[1] <= leave[1]:
result.append(('leave_id', leave[2].holiday_id.id))
return result
def _get_interval_leave_work_entry_type(self, interval, leaves, bypassing_codes):
# returns the work entry time related to the leave that
# includes the whole interval.
# Overriden in hr_work_entry_contract_holiday to select the
# global time off first (eg: Public Holiday > Home Working)
self.ensure_one()
if 'work_entry_type_id' in interval[2] and interval[2].work_entry_type_id.code in bypassing_codes:
return interval[2].work_entry_type_id
interval_start = interval[0].astimezone(pytz.utc).replace(tzinfo=None)
interval_stop = interval[1].astimezone(pytz.utc).replace(tzinfo=None)
including_rcleaves = [l[2] for l in leaves if l[2] and interval_start >= l[2].date_from and interval_stop <= l[2].date_to]
including_global_rcleaves = [l for l in including_rcleaves if not l.holiday_id]
including_holiday_rcleaves = [l for l in including_rcleaves if l.holiday_id]
rc_leave = False
# Example: In CP200: Long term sick > Public Holidays (which is global)
if bypassing_codes:
bypassing_rc_leave = [l for l in including_holiday_rcleaves if l.holiday_id.holiday_status_id.work_entry_type_id.code in bypassing_codes]
else:
bypassing_rc_leave = []
if bypassing_rc_leave:
rc_leave = bypassing_rc_leave[0]
elif including_global_rcleaves:
rc_leave = including_global_rcleaves[0]
elif including_holiday_rcleaves:
rc_leave = including_holiday_rcleaves[0]
if rc_leave:
return self._get_leave_work_entry_type_dates(rc_leave, interval_start, interval_stop, self.employee_id)
return self.env.ref('hr_work_entry_contract.work_entry_type_leave')
def _get_sub_leave_domain(self):
domain = super()._get_sub_leave_domain()
return OR([
domain,
[('holiday_id.employee_id', 'in', self.employee_id.ids)] # see https://github.com/odoo/enterprise/pull/15091
])
def write(self, vals):
# Special case when setting a contract as running:
# If there is already a validated time off over another contract
# with a different schedule, split the time off, before the
# _check_contracts raises an issue.
if 'state' not in vals or vals['state'] != 'open':
return super().write(vals)
specific_contracts = self.env['hr.contract']
all_new_leave_origin = []
all_new_leave_vals = []
leaves_state = {}
for contract in self:
leaves = contract._get_leaves()
for leave in leaves:
overlapping_contracts = leave._get_overlapping_contracts(contract_states=[('state', '!=', 'cancel')])
if len(overlapping_contracts.resource_calendar_id) <= 1:
continue
if leave.id not in leaves_state:
leaves_state[leave.id] = leave.state
if leave.state != 'refuse':
leave.action_refuse()
super(HrContract, contract).write(vals)
specific_contracts += contract
for overlapping_contract in overlapping_contracts:
# Exclude other draft contracts that are not set to running on this
# transaction
if overlapping_contract.state == 'draft' and overlapping_contract not in self:
continue
new_date_from = max(leave.date_from, datetime.combine(overlapping_contract.date_start, datetime.min.time()))
new_date_to = min(leave.date_to, datetime.combine(overlapping_contract.date_end or date.max, datetime.max.time()))
new_leave_vals = leave.copy_data({
'date_from': new_date_from,
'date_to': new_date_to,
'state': leaves_state[leave.id],
})[0]
new_leave = self.env['hr.leave'].new(new_leave_vals)
new_leave._compute_date_from_to()
new_leave._compute_number_of_days()
# Could happen for part-time contract, that time off is not necessary
# anymore.
if new_leave.date_from < new_leave.date_to:
all_new_leave_origin.append(leave)
all_new_leave_vals.append(new_leave._convert_to_write(new_leave._cache))
if all_new_leave_vals:
new_leaves = self.env['hr.leave'].with_context(
tracking_disable=True,
mail_activity_automation_skip=True,
leave_fast_create=True,
leave_skip_state_check=True
).create(all_new_leave_vals)
new_leaves.filtered(lambda l: l.state in 'validate')._validate_leave_request()
for index, new_leave in enumerate(new_leaves):
subtype_note = self.env.ref('mail.mt_note')
new_leave.message_post_with_view(
'mail.message_origin_link',
values={'self': new_leave, 'origin': all_new_leave_origin[index]},
subtype_id=subtype_note.id)
return super(HrContract, self - specific_contracts).write(vals)