78 lines
3.7 KiB
Python
78 lines
3.7 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import timedelta
|
|
from collections import defaultdict
|
|
import pytz
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.tools.intervals import Intervals
|
|
|
|
|
|
class HrVersion(models.Model):
|
|
_inherit = 'hr.version'
|
|
|
|
work_entry_source = fields.Selection(
|
|
selection_add=[('planning', 'Planning')],
|
|
ondelete={'planning': 'set default'},
|
|
)
|
|
|
|
def _get_more_vals_attendance_interval(self, interval):
|
|
result = super()._get_more_vals_attendance_interval(interval)
|
|
if interval[2]._name == 'planning.slot':
|
|
# Due to how Intervals work we might lose the right start and end time for our work entry
|
|
# i.e. [(1, 5, slot_1), (3, 10, slot_2)] will result in (1, 10, {slot_1, slot_2})
|
|
# `_get_version_work_entries_values` already takes care of unsplitting our invervals into two for that case
|
|
# But the date needs to be correct after the fact, which we do here.
|
|
# We still take the interval's date in case it was pushed by start_dt/end_dt.
|
|
slot = interval[2]
|
|
# Avoid timezone conversions
|
|
subresult = [('planning_slot_id', slot.id)]
|
|
if interval[0] < pytz.utc.localize(slot.start_datetime):
|
|
subresult.append(('date_start', slot.start_datetime))
|
|
if interval[1] > pytz.utc.localize(slot.end_datetime):
|
|
subresult.append(('date_stop', slot.end_datetime))
|
|
result.extend(subresult)
|
|
return result
|
|
|
|
def _get_attendance_intervals(self, start_dt, end_dt):
|
|
planning_based_contracts = self.filtered(lambda c: c.work_entry_source == 'planning')
|
|
search_domain = [
|
|
('employee_id', 'in', planning_based_contracts.employee_id.ids),
|
|
('state', '=', 'published'),
|
|
('start_datetime', '<', end_dt.replace(tzinfo=None)),
|
|
('end_datetime', '>', start_dt.replace(tzinfo=None)),
|
|
]
|
|
resource_ids = planning_based_contracts.employee_id.resource_id.ids
|
|
planning_slots = self.env['planning.slot'].sudo().search(search_domain) if planning_based_contracts\
|
|
else self.env['planning.slot']
|
|
intervals = defaultdict(list)
|
|
for planning_slot in planning_slots:
|
|
intervals[planning_slot.resource_id.id].append((
|
|
max(start_dt, pytz.utc.localize(planning_slot.start_datetime)),
|
|
min(end_dt, pytz.utc.localize(planning_slot.end_datetime)),
|
|
planning_slot))
|
|
mapped_intervals = {r: Intervals(intervals[r], keep_distinct=True) for r in resource_ids}
|
|
mapped_intervals.update(super()._get_attendance_intervals(start_dt, end_dt))
|
|
return mapped_intervals
|
|
|
|
@api.model
|
|
def _generate_work_entries_postprocess(self, vals_list):
|
|
new_vals_list = []
|
|
for vals in vals_list:
|
|
if not vals.get('planning_slot_id') or not vals.get('date_start') or not vals.get('date_stop'):
|
|
new_vals_list.append(vals)
|
|
continue
|
|
dt_start = vals.pop('date_start')
|
|
dt_stop = vals.pop('date_stop')
|
|
date_start = dt_start.date()
|
|
date_stop = dt_stop.date()
|
|
slot = self.env['planning.slot'].browse(vals['planning_slot_id'])
|
|
number_of_days = (date_stop - date_start).days + 1
|
|
allocated_hours = slot._get_planning_duration(dt_start, dt_stop)
|
|
for i in range(number_of_days):
|
|
new_vals = vals.copy()
|
|
new_vals['date'] = date_start + timedelta(days=i)
|
|
new_vals['duration'] = allocated_hours / number_of_days
|
|
new_vals_list.append(new_vals)
|
|
return super()._generate_work_entries_postprocess(new_vals_list)
|