Files
odoo_source/addons/resource/models/resource_calendar_attendance.py
T
Rob Coekaerts a9b804b1ad [IMP] resource: add duration to resource_calendar_attendance
Before this commit, conversion between worked hours and days in
resource_calendar_attendance was calculated but in reality there is no
unambigous way to do this. Eg in Belgium the morning working period is
4 hours, the one in the afternoon is 3 hours 36 minutes, while both of
them are still counted as half days.

To mediate this, the duration in days is explicitely added to
resource.calendar.attendance, with sensible default being provided
(half a day for morning and afternoon periods, 0 for lunch).

task-3131517

Part-of: odoo/odoo#133145
2023-10-24 12:38:09 +00:00

109 lines
4.9 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import math
from odoo import api, fields, models, _
class ResourceCalendarAttendance(models.Model):
_name = "resource.calendar.attendance"
_description = "Work Detail"
_order = 'sequence, week_type, dayofweek, hour_from'
name = fields.Char(required=True)
dayofweek = fields.Selection([
('0', 'Monday'),
('1', 'Tuesday'),
('2', 'Wednesday'),
('3', 'Thursday'),
('4', 'Friday'),
('5', 'Saturday'),
('6', 'Sunday')
], 'Day of Week', required=True, index=True, default='0')
date_from = fields.Date(string='Starting Date')
date_to = fields.Date(string='End Date')
hour_from = fields.Float(string='Work from', required=True, index=True,
help="Start and End time of working.\n"
"A specific value of 24:00 is interpreted as 23:59:59.999999.")
hour_to = fields.Float(string='Work to', required=True)
# For the hour duration, the compute function is used to compute the value
# unambiguously, while the duration in days is computed for the default
# value based on the day_period but can be manually overridden.
duration_hours = fields.Float(compute='_compute_duration_hours', string='Duration (hours)')
duration_days = fields.Float(compute='_compute_duration_days', string='Duration (days)', store=True, readonly=False)
calendar_id = fields.Many2one("resource.calendar", string="Resource's Calendar", required=True, ondelete='cascade')
day_period = fields.Selection([
('morning', 'Morning'),
('lunch', 'Break'),
('afternoon', 'Afternoon')], required=True, default='morning')
resource_id = fields.Many2one('resource.resource', 'Resource')
week_type = fields.Selection([
('1', 'Second'),
('0', 'First')
], 'Week Number', default=False)
two_weeks_calendar = fields.Boolean("Calendar in 2 weeks mode", related='calendar_id.two_weeks_calendar')
display_type = fields.Selection([
('line_section', "Section")], default=False, help="Technical field for UX purpose.")
sequence = fields.Integer(default=10,
help="Gives the sequence of this line when displaying the resource calendar.")
@api.onchange('hour_from', 'hour_to')
def _onchange_hours(self):
# avoid negative or after midnight
self.hour_from = min(self.hour_from, 23.99)
self.hour_from = max(self.hour_from, 0.0)
self.hour_to = min(self.hour_to, 24)
self.hour_to = max(self.hour_to, 0.0)
# avoid wrong order
self.hour_to = max(self.hour_to, self.hour_from)
@api.model
def get_week_type(self, date):
# week_type is defined by
# * counting the number of days from January 1 of year 1
# (extrapolated to dates prior to the first adoption of the Gregorian calendar)
# * converted to week numbers and then the parity of this number is asserted.
# It ensures that an even week number always follows an odd week number. With classical week number,
# some years have 53 weeks. Therefore, two consecutive odd week number follow each other (53 --> 1).
return int(math.floor((date.toordinal() - 1) / 7) % 2)
@api.depends('hour_from', 'hour_to')
def _compute_duration_hours(self):
for attendance in self:
attendance.duration_hours = (attendance.hour_to - attendance.hour_from) if attendance.day_period != 'lunch' else 0
@api.depends('day_period', 'hour_from', 'hour_to')
def _compute_duration_days(self):
for attendance in self:
if attendance.day_period == 'lunch':
attendance.duration_days = 0
else:
attendance.duration_days = 0.5 if attendance.duration_hours <= attendance.calendar_id.hours_per_day * 3 / 4 else 1
@api.depends('week_type')
def _compute_display_name(self):
super()._compute_display_name()
this_week_type = str(self.get_week_type(fields.Date.context_today(self)))
section_names = {'0': _('First week'), '1': _('Second week')}
section_info = {True: _('this week'), False: _('other week')}
for record in self.filtered(lambda l: l.display_type == 'line_section'):
section_name = f"{section_names[record.week_type]} ({section_info[this_week_type == record.week_type]})"
record.display_name = section_name
def _copy_attendance_vals(self):
self.ensure_one()
return {
'name': self.name,
'dayofweek': self.dayofweek,
'date_from': self.date_from,
'date_to': self.date_to,
'hour_from': self.hour_from,
'hour_to': self.hour_to,
'day_period': self.day_period,
'week_type': self.week_type,
'display_type': self.display_type,
'sequence': self.sequence,
}