mirror of
https://github.com/bringout/oca-ocb-test.git
synced 2026-04-21 19:22:00 +02:00
19.0 vanilla
This commit is contained in:
parent
38c6088dcc
commit
d9452d2060
243 changed files with 30797 additions and 10815 deletions
|
|
@ -1,5 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_resource
|
||||
from . import test_performance
|
||||
from . import (
|
||||
test_calendar,
|
||||
test_mixin,
|
||||
test_performance,
|
||||
test_resource,
|
||||
test_resource_errors,
|
||||
test_timezones,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,104 +1,174 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import datetime
|
||||
|
||||
from pytz import timezone, utc
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestResourceCommon(TransactionCase):
|
||||
@classmethod
|
||||
def datetime_tz(cls, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
|
||||
""" Return a `datetime` object with a given timezone (if given). """
|
||||
dt = datetime(year, month, day, hour, minute, second, microsecond)
|
||||
return timezone(tzinfo).localize(dt) if tzinfo else dt
|
||||
|
||||
@classmethod
|
||||
def datetime_str(cls, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
|
||||
""" Return a fields.Datetime value with the given timezone. """
|
||||
dt = datetime(year, month, day, hour, minute, second, microsecond)
|
||||
if tzinfo:
|
||||
dt = timezone(tzinfo).localize(dt).astimezone(utc)
|
||||
return fields.Datetime.to_string(dt)
|
||||
|
||||
@classmethod
|
||||
def _define_calendar(cls, name, attendances, tz):
|
||||
return cls.env['resource.calendar'].create({
|
||||
'name': name,
|
||||
'tz': tz,
|
||||
'attendance_ids': [
|
||||
(0, 0, {
|
||||
'name': '%s_%d' % (name, index),
|
||||
'hour_from': att[0],
|
||||
'hour_to': att[1],
|
||||
'dayofweek': str(att[2]),
|
||||
})
|
||||
for index, att in enumerate(attendances)
|
||||
],
|
||||
})
|
||||
return cls.env["resource.calendar"].create(
|
||||
{
|
||||
"name": name,
|
||||
"tz": tz,
|
||||
"attendance_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "%s_%d" % (name, index),
|
||||
"hour_from": att[0],
|
||||
"hour_to": att[1],
|
||||
"dayofweek": str(att[2]),
|
||||
"duration_days": att[3],
|
||||
},
|
||||
)
|
||||
for index, att in enumerate(attendances)
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _define_calendar_2_weeks(cls, name, attendances, tz):
|
||||
return cls.env['resource.calendar'].create({
|
||||
'name': name,
|
||||
'tz': tz,
|
||||
'two_weeks_calendar': True,
|
||||
'attendance_ids': [
|
||||
(0, 0, {
|
||||
'name': '%s_%d' % (name, index),
|
||||
'hour_from': att[0],
|
||||
'hour_to': att[1],
|
||||
'dayofweek': str(att[2]),
|
||||
'week_type': att[3],
|
||||
'display_type': att[4],
|
||||
'sequence': att[5],
|
||||
})
|
||||
for index, att in enumerate(attendances)
|
||||
],
|
||||
})
|
||||
return cls.env["resource.calendar"].create(
|
||||
{
|
||||
"name": name,
|
||||
"tz": tz,
|
||||
"two_weeks_calendar": True,
|
||||
"attendance_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "%s_%d" % (name, index),
|
||||
"hour_from": att[0],
|
||||
"hour_to": att[1],
|
||||
"dayofweek": str(att[2]),
|
||||
"week_type": att[3],
|
||||
},
|
||||
)
|
||||
for index, att in enumerate(attendances)
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestResourceCommon, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.env.company.resource_calendar_id.tz = "Europe/Brussels"
|
||||
|
||||
# UTC+1 winter, UTC+2 summer
|
||||
cls.calendar_jean = cls._define_calendar('40 Hours', [(8, 16, i) for i in range(5)], 'Europe/Brussels')
|
||||
cls.calendar_jean = cls._define_calendar(
|
||||
"40 Hours", [(8, 16, i, 1) for i in range(5)], "Europe/Brussels",
|
||||
)
|
||||
# UTC+6
|
||||
cls.calendar_patel = cls._define_calendar('38 Hours', sum([((9, 12, i), (13, 17, i)) for i in range(5)], ()), 'Etc/GMT-6')
|
||||
cls.calendar_patel = cls._define_calendar(
|
||||
"38 Hours",
|
||||
sum((((9, 12, i, 3 / 7), (13, 17, i, 4 / 7)) for i in range(5)), ()),
|
||||
"Etc/GMT-6",
|
||||
)
|
||||
# UTC-8 winter, UTC-7 summer
|
||||
cls.calendar_john = cls._define_calendar('8+12 Hours', [(8, 16, 1), (8, 13, 4), (16, 23, 4)], 'America/Los_Angeles')
|
||||
cls.calendar_john = cls._define_calendar(
|
||||
"8+12 Hours",
|
||||
[(8, 16, 1, 1), (8, 13, 4, 5 / 12), (16, 23, 4, 7 / 12)],
|
||||
"America/Los_Angeles",
|
||||
)
|
||||
# UTC+1 winter, UTC+2 summer
|
||||
cls.calendar_jules = cls._define_calendar_2_weeks('Week 1: 30 Hours - Week 2: 16 Hours', [
|
||||
(0, 0, 0, '0', 'line_section', 0), (8, 16, 0, '0', False, 1), (9, 17, 1, '0', False, 2),
|
||||
(0, 0, 0, '1', 'line_section', 10), (8, 16, 0, '1', False, 11), (7, 15, 2, '1', False, 12),
|
||||
(8, 16, 3, '1', False, 13), (10, 16, 4, '1', False, 14)], 'Europe/Brussels')
|
||||
cls.calendar_jules = cls._define_calendar_2_weeks(
|
||||
"Week 1: 30 Hours - Week 2: 16 Hours",
|
||||
[
|
||||
(8, 16, 0, "0"),
|
||||
(9, 17, 1, "0"),
|
||||
(8, 16, 0, "1"),
|
||||
(7, 15, 2, "1"),
|
||||
(8, 16, 3, "1"),
|
||||
(10, 16, 4, "1"),
|
||||
],
|
||||
"Europe/Brussels",
|
||||
)
|
||||
|
||||
cls.calendar_paul = cls._define_calendar('Morning and evening shifts', sum([((2, 7, i), (10, 16, i)) for i in range(5)], ()), 'America/Noronha')
|
||||
cls.calendar_paul = cls._define_calendar(
|
||||
"Morning and evening shifts",
|
||||
sum((((2, 7, i, 0.5), (10, 16, i, 0.5)) for i in range(5)), ()),
|
||||
"America/Noronha",
|
||||
)
|
||||
|
||||
cls.calendar_bob = cls._define_calendar(
|
||||
"Calendar with adjacent attendances",
|
||||
sum((((8, 12, i, 0.5), (12, 16, i, 0.5)) for i in range(5)), ()),
|
||||
"Europe/Brussels",
|
||||
)
|
||||
|
||||
# Employee is linked to a resource.resource via resource.mixin
|
||||
cls.jean = cls.env['resource.test'].create({
|
||||
'name': 'Jean',
|
||||
'resource_calendar_id': cls.calendar_jean.id,
|
||||
})
|
||||
cls.patel = cls.env['resource.test'].create({
|
||||
'name': 'Patel',
|
||||
'resource_calendar_id': cls.calendar_patel.id,
|
||||
})
|
||||
cls.john = cls.env['resource.test'].create({
|
||||
'name': 'John',
|
||||
'resource_calendar_id': cls.calendar_john.id,
|
||||
})
|
||||
cls.jules = cls.env['resource.test'].create({
|
||||
'name': 'Jules',
|
||||
'resource_calendar_id': cls.calendar_jules.id,
|
||||
})
|
||||
cls.jean = cls.env["resource.test"].create(
|
||||
{
|
||||
"name": "Jean",
|
||||
"resource_calendar_id": cls.calendar_jean.id,
|
||||
},
|
||||
)
|
||||
cls.patel = cls.env["resource.test"].create(
|
||||
{
|
||||
"name": "Patel",
|
||||
"resource_calendar_id": cls.calendar_patel.id,
|
||||
},
|
||||
)
|
||||
cls.john = cls.env["resource.test"].create(
|
||||
{
|
||||
"name": "John",
|
||||
"resource_calendar_id": cls.calendar_john.id,
|
||||
},
|
||||
)
|
||||
cls.jules = cls.env["resource.test"].create(
|
||||
{
|
||||
"name": "Jules",
|
||||
"resource_calendar_id": cls.calendar_jules.id,
|
||||
},
|
||||
)
|
||||
|
||||
cls.paul = cls.env['resource.test'].create({
|
||||
'name': 'Paul',
|
||||
'resource_calendar_id': cls.calendar_paul.id,
|
||||
})
|
||||
cls.paul = cls.env["resource.test"].create(
|
||||
{
|
||||
"name": "Paul",
|
||||
"resource_calendar_id": cls.calendar_paul.id,
|
||||
},
|
||||
)
|
||||
|
||||
cls.bob = cls.env["resource.test"].create(
|
||||
{
|
||||
"name": "Bob",
|
||||
"resource_calendar_id": cls.calendar_bob.id,
|
||||
},
|
||||
)
|
||||
|
||||
cls.two_weeks_resource = cls._define_calendar_2_weeks(
|
||||
'Two weeks resource',
|
||||
"Two weeks resource",
|
||||
[
|
||||
(0, 0, 0, '0', 'line_section', 0),
|
||||
(8, 16, 0, '0', False, 1),
|
||||
(8, 16, 1, '0', False, 2),
|
||||
(8, 16, 2, '0', False, 3),
|
||||
(8, 16, 3, '0', False, 4),
|
||||
(8, 16, 4, '0', False, 5),
|
||||
(0, 0, 0, '1', 'line_section', 10),
|
||||
(8, 16, 0, '1', False, 11),
|
||||
(8, 16, 1, '1', False, 12),
|
||||
(8, 16, 2, '1', False, 13),
|
||||
(8, 16, 3, '1', False, 14),
|
||||
(8, 16, 4, '1', False, 15)
|
||||
(8, 16, 0, "0"),
|
||||
(8, 16, 1, "0"),
|
||||
(8, 16, 2, "0"),
|
||||
(8, 16, 3, "0"),
|
||||
(8, 16, 4, "0"),
|
||||
(8, 16, 0, "1"),
|
||||
(8, 16, 1, "1"),
|
||||
(8, 16, 2, "1"),
|
||||
(8, 16, 3, "1"),
|
||||
(8, 16, 4, "1"),
|
||||
],
|
||||
'Europe/Brussels'
|
||||
"Europe/Brussels",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,485 @@
|
|||
from datetime import date, datetime
|
||||
|
||||
from pytz import timezone
|
||||
|
||||
from odoo import fields
|
||||
|
||||
from odoo.addons.test_resource.tests.common import TestResourceCommon
|
||||
|
||||
|
||||
class TestCalendar(TestResourceCommon):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def test_get_work_hours_count(self):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Time Off',
|
||||
'resource_id': False,
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'leave for Jean',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 5, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 5, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
)
|
||||
self.assertEqual(hours, 32)
|
||||
|
||||
hours = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
compute_leaves=False,
|
||||
)
|
||||
self.assertEqual(hours, 40)
|
||||
|
||||
# leave of size 0
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero_length',
|
||||
'calendar_id': self.calendar_patel.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_patel.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(hours, 35)
|
||||
|
||||
# leave of medium size
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero_length',
|
||||
'calendar_id': self.calendar_patel.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 9, 0, 0, tzinfo=self.patel.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 12, 0, 0, tzinfo=self.patel.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_patel.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(hours, 32)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# leave of very small size
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero_length',
|
||||
'calendar_id': self.calendar_patel.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 0, 0, 10, tzinfo=self.patel.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_patel.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(hours, 35)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# no timezone given should be converted to UTC
|
||||
# Should equal to a leave between 2018/04/03 10:00:00 and 2018/04/04 10:00:00
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'no timezone',
|
||||
'calendar_id': self.calendar_patel.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 4, 0, 0),
|
||||
'date_to': self.datetime_str(2018, 4, 4, 4, 0, 0),
|
||||
})
|
||||
|
||||
hours = self.calendar_patel.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(hours, 28)
|
||||
|
||||
hours = self.calendar_patel.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(hours, 0)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# 2 weeks calendar week 1
|
||||
hours = self.calendar_jules.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)
|
||||
self.assertEqual(hours, 30)
|
||||
|
||||
# 2 weeks calendar week 1
|
||||
hours = self.calendar_jules.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 16, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 20, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)
|
||||
self.assertEqual(hours, 30)
|
||||
|
||||
# 2 weeks calendar week 2
|
||||
hours = self.calendar_jules.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)
|
||||
self.assertEqual(hours, 16)
|
||||
|
||||
# 2 weeks calendar week 2, leave during a day where he doesn't work this week
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Time Off Jules week 2',
|
||||
'calendar_id': self.calendar_jules.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 11, 4, 0, 0, tzinfo=self.jules.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 13, 4, 0, 0, tzinfo=self.jules.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_jules.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)
|
||||
self.assertEqual(hours, 16)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# 2 weeks calendar week 2, leave during a day where he works this week
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Time Off Jules week 2',
|
||||
'calendar_id': self.calendar_jules.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 9, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 9, 23, 59, 0, tzinfo=self.jules.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_jules.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)
|
||||
self.assertEqual(hours, 8)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# leave without calendar, should count for anyone in the company
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'small leave',
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 9, 0, 0, tzinfo=self.patel.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 12, 0, 0, tzinfo=self.patel.tz),
|
||||
})
|
||||
|
||||
hours = self.calendar_patel.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(hours, 32)
|
||||
|
||||
def test_calendar_working_hours_count(self):
|
||||
calendar = self.env['resource.calendar'].create({
|
||||
'name': 'Standard 35 hours/week',
|
||||
'company_id': self.env.company.id,
|
||||
'tz': 'UTC',
|
||||
'attendance_ids': [(5, 0, 0),
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Lunch', 'dayofweek': '1', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Lunch', 'dayofweek': '2', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Lunch', 'dayofweek': '3', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Friday Lunch', 'dayofweek': '4', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
],
|
||||
})
|
||||
res = calendar.get_work_hours_count(
|
||||
fields.Datetime.from_string('2017-05-03 14:03:00'), # Wednesday (8:00-12:00, 13:00-16:00)
|
||||
fields.Datetime.from_string('2017-05-04 11:03:00'), # Thursday (8:00-12:00, 13:00-16:00)
|
||||
compute_leaves=False)
|
||||
self.assertEqual(res, 5.0)
|
||||
|
||||
def test_calendar_working_hours_24(self):
|
||||
self.att_4 = self.env['resource.calendar.attendance'].create({
|
||||
'name': 'Att4',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'dayofweek': '2',
|
||||
'hour_from': 0,
|
||||
'hour_to': 24,
|
||||
})
|
||||
res = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 6, 19, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 6, 21, 1, 0, 0, tzinfo=self.jean.tz),
|
||||
compute_leaves=True)
|
||||
self.assertAlmostEqual(res, 24.0)
|
||||
|
||||
def test_plan_hours(self):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'global',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 11, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 11, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
time = self.calendar_jean.plan_hours(2, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 10, 10, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
time = self.calendar_jean.plan_hours(20, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 12, 12, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
time = self.calendar_jean.plan_hours(5, self.datetime_tz(2018, 4, 10, 15, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 12, 12, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
# negative planning
|
||||
time = self.calendar_jean.plan_hours(-10, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 6, 14, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
# zero planning with holidays
|
||||
time = self.calendar_jean.plan_hours(0, self.datetime_tz(2018, 4, 11, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 12, 8, 0, 0, tzinfo=self.jean.tz))
|
||||
time = self.calendar_jean.plan_hours(0, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 10, 8, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
# very small planning
|
||||
time = self.calendar_jean.plan_hours(0.0002, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 10, 8, 0, 0, 720000, tzinfo=self.jean.tz))
|
||||
|
||||
# huge planning
|
||||
time = self.calendar_jean.plan_hours(3000, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, self.datetime_tz(2019, 9, 16, 16, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
def test_plan_days(self):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'global',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 11, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 11, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
time = self.calendar_jean.plan_days(1, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 10, 16, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
time = self.calendar_jean.plan_days(3, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 12, 16, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
time = self.calendar_jean.plan_days(4, self.datetime_tz(2018, 4, 10, 16, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 17, 16, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
# negative planning
|
||||
time = self.calendar_jean.plan_days(-10, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 3, 27, 8, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
# zero planning
|
||||
time = self.calendar_jean.plan_days(0, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz))
|
||||
|
||||
# very small planning returns False in this case
|
||||
# TODO: decide if this behaviour is alright
|
||||
time = self.calendar_jean.plan_days(0.0002, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=True)
|
||||
self.assertEqual(time, False)
|
||||
|
||||
# huge planning
|
||||
# TODO: Same as above
|
||||
# NOTE: Maybe allow to set a max limit to the method
|
||||
time = self.calendar_jean.plan_days(3000, self.datetime_tz(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz), compute_leaves=False)
|
||||
self.assertEqual(time, False)
|
||||
|
||||
def test_closest_time(self):
|
||||
# Calendar:
|
||||
# Tuesdays 8-16
|
||||
# Fridays 8-13 and 16-23
|
||||
dt = self.datetime_tz(2020, 4, 2, 7, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt)
|
||||
self.assertFalse(calendar_dt, "It should not return any value for unattended days")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 7, 0, 0, tzinfo=self.john.tz)
|
||||
range_start = self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz)
|
||||
range_end = self.datetime_tz(2020, 4, 3, 19, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt, search_range=(range_start, range_end))
|
||||
self.assertFalse(calendar_dt, "It should not return any value if dt outside of range")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 7, 0, 0, tzinfo=self.john.tz) # before
|
||||
start = self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt)
|
||||
self.assertEqual(calendar_dt, start, "It should return the start of the day")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 10, 0, 0, tzinfo=self.john.tz) # after
|
||||
start = self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt)
|
||||
self.assertEqual(calendar_dt, start, "It should return the start of the closest attendance")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 7, 0, 0, tzinfo=self.john.tz) # before
|
||||
end = self.datetime_tz(2020, 4, 3, 13, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt, match_end=True)
|
||||
self.assertEqual(calendar_dt, end, "It should return the end of the closest attendance")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 14, 0, 0, tzinfo=self.john.tz) # after
|
||||
end = self.datetime_tz(2020, 4, 3, 13, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt, match_end=True)
|
||||
self.assertEqual(calendar_dt, end, "It should return the end of the closest attendance")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 0, 0, 0, tzinfo=self.john.tz)
|
||||
start = self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt)
|
||||
self.assertEqual(calendar_dt, start, "It should return the start of the closest attendance")
|
||||
|
||||
dt = self.datetime_tz(2020, 4, 3, 23, 59, 59, tzinfo=self.john.tz)
|
||||
end = self.datetime_tz(2020, 4, 3, 23, 0, 0, tzinfo=self.john.tz)
|
||||
calendar_dt = self.calendar_john._get_closest_work_time(dt, match_end=True)
|
||||
self.assertEqual(calendar_dt, end, "It should return the end of the closest attendance")
|
||||
|
||||
def test_attendance_interval_edge_tz(self):
|
||||
# When genereting the attendance intervals in an edge timezone, the last interval shouldn't
|
||||
# be truncated if the timezone is correctly set
|
||||
self.env.user.tz = "America/Los_Angeles"
|
||||
self.calendar_jean.tz = "America/Los_Angeles"
|
||||
attendances = self.calendar_jean._attendance_intervals_batch(
|
||||
datetime.combine(date(2023, 1, 1), datetime.min.time(), tzinfo=timezone("UTC")),
|
||||
datetime.combine(date(2023, 1, 31), datetime.max.time(), tzinfo=timezone("UTC")))
|
||||
last_attendance = list(attendances[False])[-1]
|
||||
self.assertEqual(last_attendance[0].replace(tzinfo=None), datetime(2023, 1, 31, 8))
|
||||
self.assertEqual(last_attendance[1].replace(tzinfo=None), datetime(2023, 1, 31, 15, 59, 59, 999999))
|
||||
|
||||
attendances = self.calendar_jean._attendance_intervals_batch(
|
||||
datetime.combine(date(2023, 1, 1), datetime.min.time(), tzinfo=timezone("America/Los_Angeles")),
|
||||
datetime.combine(date(2023, 1, 31), datetime.max.time(), tzinfo=timezone("America/Los_Angeles")))
|
||||
last_attendance = list(attendances[False])[-1]
|
||||
self.assertEqual(last_attendance[0].replace(tzinfo=None), datetime(2023, 1, 31, 8))
|
||||
self.assertEqual(last_attendance[1].replace(tzinfo=None), datetime(2023, 1, 31, 16))
|
||||
|
||||
def test_resource_calendar_update(self):
|
||||
""" Ensure leave calendar gets set correctly when updating resource calendar. """
|
||||
holiday = self.env['resource.calendar.leaves'].create({
|
||||
'name': "May Day",
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'date_from': self.datetime_str(2024, 5, 1, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2024, 5, 1, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
# Jean takes a leave
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': "Jean is AFK",
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2024, 5, 10, 8, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2024, 5, 10, 16, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
# Jean changes working schedule to Jules'
|
||||
self.jean.resource_calendar_id = self.calendar_jules
|
||||
self.assertEqual(leave.calendar_id, self.calendar_jules, "Leave calendar should update")
|
||||
self.assertEqual(holiday.calendar_id, self.calendar_jean, "Global leave shouldn't change")
|
||||
|
||||
def test_compute_work_time_rate_with_one_week_calendar(self):
|
||||
"""Test Case: check if the computation of the work time rate in the resource.calendar is correct."""
|
||||
# Define a mid time
|
||||
resource_calendar = self.env['resource.calendar'].create({
|
||||
'name': 'Calendar Mid-Time',
|
||||
'tz': "Europe/Brussels",
|
||||
'two_weeks_calendar': False,
|
||||
'full_time_required_hours': 40,
|
||||
'attendance_ids': [
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Lunch', 'dayofweek': '1', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
],
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 50, 2)
|
||||
|
||||
# Define a 4/5
|
||||
resource_calendar.write({
|
||||
'name': 'Calendar (4 / 5)',
|
||||
'attendance_ids': [
|
||||
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Lunch', 'dayofweek': '3', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
],
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 80, 2)
|
||||
|
||||
# Define a 9/10
|
||||
resource_calendar.write({
|
||||
'name': 'Calendar (9 / 10)',
|
||||
'attendance_ids': [(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'})],
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 90, 2)
|
||||
|
||||
# Define a Full-Time
|
||||
resource_calendar.write({
|
||||
'name': 'Calendar Full-Time',
|
||||
'attendance_ids': [(0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})],
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 100, 2)
|
||||
|
||||
def test_compute_work_time_rate_with_two_weeks_calendar(self):
|
||||
"""Test Case: check if the computation of the work time rate in the resource.calendar is correct."""
|
||||
def create_attendance_ids(attendance_list):
|
||||
return [(0, 0, {'week_type': str(i), **attendance}) for i in range(0, 2) for attendance in attendance_list]
|
||||
|
||||
attendance_list = [
|
||||
{'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'},
|
||||
{'name': 'Monday Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'},
|
||||
{'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'},
|
||||
{'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'},
|
||||
{'name': 'Tuesday Lunch', 'dayofweek': '1', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'},
|
||||
{'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'},
|
||||
{'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'},
|
||||
]
|
||||
|
||||
# Define a mid time
|
||||
resource_calendar = self.env['resource.calendar'].create({
|
||||
'name': 'Calendar Mid-Time',
|
||||
'tz': "Europe/Brussels",
|
||||
'two_weeks_calendar': True,
|
||||
'full_time_required_hours': 40,
|
||||
'attendance_ids': create_attendance_ids(attendance_list),
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 50, 2)
|
||||
|
||||
attendance_list = [
|
||||
{'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'},
|
||||
{'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'},
|
||||
{'name': 'Thursday Lunch', 'dayofweek': '3', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'},
|
||||
{'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'},
|
||||
]
|
||||
|
||||
# Define a 4/5
|
||||
resource_calendar.write({
|
||||
'name': 'Calendar (4 / 5)',
|
||||
'attendance_ids': create_attendance_ids(attendance_list),
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 80, 2)
|
||||
|
||||
# Define a 9/10
|
||||
resource_calendar.write({
|
||||
'name': 'Calendar (9 / 10)',
|
||||
'attendance_ids': create_attendance_ids([{'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}]),
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 90, 2)
|
||||
|
||||
# Define a Full-Time
|
||||
resource_calendar.write({
|
||||
'name': 'Calendar Full-Time',
|
||||
'attendance_ids': create_attendance_ids([{'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}]),
|
||||
})
|
||||
self.assertAlmostEqual(resource_calendar.work_time_rate, 100, 2)
|
||||
|
|
@ -0,0 +1,517 @@
|
|||
from datetime import date, datetime
|
||||
|
||||
from odoo.addons.test_resource.tests.common import TestResourceCommon
|
||||
|
||||
|
||||
class TestResMixin(TestResourceCommon):
|
||||
|
||||
def test_adjust_calendar(self):
|
||||
# Calendar:
|
||||
# Tuesdays 8-16
|
||||
# Fridays 8-13 and 16-23
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 3, 9, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 14, 0, 0, tzinfo=self.john.tz),
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 13, 0, 0, tzinfo=self.john.tz),
|
||||
))
|
||||
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 3, 13, 1, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 14, 0, 0, tzinfo=self.john.tz),
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
self.datetime_tz(2020, 4, 3, 16, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 23, 0, 0, tzinfo=self.john.tz),
|
||||
))
|
||||
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 4, 9, 0, 0, tzinfo=self.john.tz), # both a day without attendance
|
||||
self.datetime_tz(2020, 4, 4, 14, 0, 0, tzinfo=self.john.tz),
|
||||
)
|
||||
self.assertEqual(result[self.john], (None, None))
|
||||
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 4, 14, 0, 0, tzinfo=self.john.tz), # day without attendance
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 23, 0, 0, tzinfo=self.john.tz),
|
||||
))
|
||||
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 2, 8, 0, 0, tzinfo=self.john.tz), # day without attendance
|
||||
self.datetime_tz(2020, 4, 3, 14, 0, 0, tzinfo=self.john.tz),
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
self.datetime_tz(2020, 4, 3, 8, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 13, 0, 0, tzinfo=self.john.tz),
|
||||
))
|
||||
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 3, 31, 0, 0, 0, tzinfo=self.john.tz), # search between Tuesday and Thursday
|
||||
self.datetime_tz(2020, 4, 2, 23, 59, 59, tzinfo=self.john.tz),
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
self.datetime_tz(2020, 3, 31, 8, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 3, 31, 16, 0, tzinfo=self.john.tz),
|
||||
))
|
||||
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 3, 31, 0, 0, 0, tzinfo=self.john.tz), # search between Tuesday and Friday
|
||||
self.datetime_tz(2020, 4, 3, 23, 59, 59, tzinfo=self.john.tz),
|
||||
)
|
||||
result = self.john._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 3, 31, 8, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2020, 4, 3, 23, 0, 0, tzinfo=self.john.tz),
|
||||
)
|
||||
# It should find the start and end within the search range
|
||||
result = self.paul._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 2, 2, 0, 0, tzinfo='UTC'),
|
||||
self.datetime_tz(2020, 4, 3, 1, 59, 59, tzinfo='UTC'),
|
||||
)
|
||||
|
||||
self.assertEqual(result[self.paul], (
|
||||
self.datetime_tz(2020, 4, 2, 4, 0, tzinfo='UTC'),
|
||||
self.datetime_tz(2020, 4, 2, 18, 0, tzinfo='UTC'),
|
||||
), "It should have found the start and end of the shift on the same day on April 2nd, 2020")
|
||||
|
||||
def test_adjust_calendar_timezone_before(self):
|
||||
# Calendar:
|
||||
# Every day 8-16
|
||||
self.jean.tz = 'Asia/Tokyo'
|
||||
self.calendar_jean.tz = 'Europe/Brussels'
|
||||
|
||||
result = self.jean._adjust_to_calendar(
|
||||
self.datetime_tz(2020, 4, 1, 0, 0, 0, tzinfo='Asia/Tokyo'),
|
||||
self.datetime_tz(2020, 4, 1, 23, 59, 59, tzinfo='Asia/Tokyo'),
|
||||
)
|
||||
self.assertEqual(result[self.jean], (
|
||||
self.datetime_tz(2020, 4, 1, 8, 0, 0, tzinfo='Asia/Tokyo'),
|
||||
self.datetime_tz(2020, 4, 1, 16, 0, 0, tzinfo='Asia/Tokyo'),
|
||||
), "It should have found a starting time the 1st")
|
||||
|
||||
def test_adjust_calendar_timezone_after(self):
|
||||
# Calendar:
|
||||
# Tuesdays 8-16
|
||||
# Fridays 8-13 and 16-23
|
||||
tz = 'Europe/Brussels'
|
||||
self.john.tz = tz
|
||||
result = self.john._adjust_to_calendar(
|
||||
datetime(2020, 4, 2, 23, 0, 0), # The previous day in UTC, but the 3rd in Europe/Brussels
|
||||
datetime(2020, 4, 3, 20, 0, 0),
|
||||
)
|
||||
self.assertEqual(result[self.john], (
|
||||
datetime(2020, 4, 3, 6, 0, 0),
|
||||
datetime(2020, 4, 3, 21, 0, 0),
|
||||
), "It should have found a starting time the 3rd")
|
||||
|
||||
def test_work_days_data(self):
|
||||
# Looking at Jean's calendar
|
||||
|
||||
# Viewing it as Jean
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 16, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 5, 'hours': 40})
|
||||
|
||||
# Viewing it as Bob
|
||||
data = self.bob._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.bob.tz),
|
||||
self.datetime_tz(2018, 4, 6, 16, 0, 0, tzinfo=self.bob.tz),
|
||||
)[self.bob.id]
|
||||
self.assertEqual(data, {'days': 5, 'hours': 40})
|
||||
|
||||
# Viewing it as Patel
|
||||
# Views from 2018/04/01 20:00:00 to 2018/04/06 12:00:00
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 16, 0, 0, tzinfo=self.patel.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 4.5, 'hours': 36}) # We see only 36 hours
|
||||
|
||||
# Viewing it as John
|
||||
# Views from 2018/04/02 09:00:00 to 2018/04/07 02:00:00
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 6, 16, 0, 0, tzinfo=self.john.tz),
|
||||
)[self.jean.id]
|
||||
# still showing as 5 days because of rounding, but we see only 39 hours
|
||||
self.assertEqual(data, {'days': 4.875, 'hours': 39})
|
||||
|
||||
# Looking at John's calendar
|
||||
|
||||
# Viewing it as Jean
|
||||
# Views from 2018/04/01 15:00:00 to 2018/04/06 14:00:00
|
||||
data = self.john._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.john.id]
|
||||
self.assertEqual(data, {'days': 1.417, 'hours': 13})
|
||||
|
||||
# Viewing it as Patel
|
||||
# Views from 2018/04/01 11:00:00 to 2018/04/06 10:00:00
|
||||
data = self.john._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.patel.tz),
|
||||
)[self.john.id]
|
||||
self.assertEqual(data, {'days': 1.167, 'hours': 10})
|
||||
|
||||
# Viewing it as John
|
||||
data = self.john._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.john.tz),
|
||||
)[self.john.id]
|
||||
self.assertEqual(data, {'days': 2, 'hours': 20})
|
||||
|
||||
# using Jean as a timezone reference
|
||||
data = self.john._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.john.tz),
|
||||
calendar=self.calendar_jean,
|
||||
)[self.john.id]
|
||||
self.assertEqual(data, {'days': 5, 'hours': 40})
|
||||
|
||||
# half days
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'half',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 14, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 4.5, 'hours': 36})
|
||||
|
||||
# using John as a timezone reference, leaves are outside attendances
|
||||
data = self.john._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.john.tz),
|
||||
calendar=self.calendar_jean,
|
||||
)[self.john.id]
|
||||
self.assertEqual(data, {'days': 5, 'hours': 40})
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# leave size 0
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 5, 'hours': 40})
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# leave very small size
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'small',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 1, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data['days'], 5)
|
||||
self.assertAlmostEqual(data['hours'], 40, 2)
|
||||
|
||||
def test_leaves_days_data(self):
|
||||
# Jean takes a leave
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Jean is visiting India',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 10, 8, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 10, 16, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
# John takes a leave for Jean
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Jean is comming in USA',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 12, 8, 0, 0, tzinfo=self.john.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 12, 16, 0, 0, tzinfo=self.john.tz),
|
||||
})
|
||||
|
||||
# Jean asks to see how much leave he has taken
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
# Sees only 1 day and 8 hours because, as john is in UTC-7 the second leave is not in
|
||||
# the attendances of Jean
|
||||
self.assertEqual(data, {'days': 1, 'hours': 8})
|
||||
|
||||
# Patel Asks to see when Jean has taken some leaves
|
||||
# Patel should see the same
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 1, 'hours': 8})
|
||||
|
||||
# use Patel as a resource, jean's leaves are not visible
|
||||
datas = self.patel._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.patel.tz),
|
||||
calendar=self.calendar_jean,
|
||||
)[self.patel.id]
|
||||
self.assertEqual(datas['days'], 0)
|
||||
self.assertEqual(datas['hours'], 0)
|
||||
|
||||
# Jean takes a leave for John
|
||||
# Gives 3 hours (3/8 of a day)
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'John is sick',
|
||||
'calendar_id': self.john.resource_calendar_id.id,
|
||||
'resource_id': self.john.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 10, 20, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
# John takes a leave
|
||||
# Gives all day (12 hours)
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'John goes to holywood',
|
||||
'calendar_id': self.john.resource_calendar_id.id,
|
||||
'resource_id': self.john.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 13, 7, 0, 0, tzinfo=self.john.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 13, 18, 0, 0, tzinfo=self.john.tz),
|
||||
})
|
||||
|
||||
# John asks how much leaves he has
|
||||
# He sees that he has only 15 hours of leave in his attendances
|
||||
data = self.john._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.john.tz),
|
||||
)[self.john.id]
|
||||
self.assertEqual(data, {'days': 0.958, 'hours': 10})
|
||||
|
||||
# half days
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'half',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 14, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 0.5, 'hours': 4})
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# leave size 0
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 0, 'hours': 0})
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# leave very small size
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'small',
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 1, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data['days'], 0)
|
||||
self.assertAlmostEqual(data['hours'], 0, 2)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
def test_list_leaves(self):
|
||||
jean_leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': "Jean's son is sick",
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': False,
|
||||
'date_from': self.datetime_str(2018, 4, 10, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 10, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.jean.tz),
|
||||
)
|
||||
self.assertEqual(leaves, [(date(2018, 4, 10), 8, jean_leave)])
|
||||
|
||||
# half days
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'half',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 14, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)
|
||||
self.assertEqual(leaves, [(date(2018, 4, 2), 4, leave)])
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# very small size
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'small',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 1, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)
|
||||
self.assertEqual(len(leaves), 1)
|
||||
self.assertEqual(leaves[0][0], date(2018, 4, 2))
|
||||
self.assertAlmostEqual(leaves[0][1], 0, 2)
|
||||
self.assertEqual(leaves[0][2].id, leave.id)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# size 0
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)
|
||||
self.assertEqual(leaves, [])
|
||||
|
||||
leave.unlink()
|
||||
|
||||
def test_list_work_time_per_day(self):
|
||||
working_time = self.john._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.john.tz),
|
||||
)[self.john.id]
|
||||
self.assertEqual(working_time, [
|
||||
(date(2018, 4, 10), 8),
|
||||
(date(2018, 4, 13), 12),
|
||||
])
|
||||
|
||||
# change john's resource's timezone
|
||||
self.john.resource_id.tz = 'Europe/Brussels'
|
||||
self.assertEqual(self.john.tz, 'Europe/Brussels')
|
||||
self.assertEqual(self.calendar_john.tz, 'America/Los_Angeles')
|
||||
working_time = self.john._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 9, 0, 0, 0, tzinfo=self.john.tz),
|
||||
self.datetime_tz(2018, 4, 13, 23, 59, 59, tzinfo=self.john.tz),
|
||||
)[self.john.id]
|
||||
self.assertEqual(working_time, [
|
||||
(date(2018, 4, 10), 8),
|
||||
(date(2018, 4, 13), 12),
|
||||
])
|
||||
|
||||
# half days
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'small',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 14, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
working_time = self.jean._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(working_time, [
|
||||
(date(2018, 4, 2), 4),
|
||||
(date(2018, 4, 3), 8),
|
||||
(date(2018, 4, 4), 8),
|
||||
(date(2018, 4, 5), 8),
|
||||
(date(2018, 4, 6), 8),
|
||||
])
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# very small size
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'small',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 1, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
working_time = self.jean._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(len(working_time), 5)
|
||||
self.assertEqual(working_time[0][0], date(2018, 4, 2))
|
||||
self.assertAlmostEqual(working_time[0][1], 8, 2)
|
||||
|
||||
leave.unlink()
|
||||
|
||||
# size 0
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'zero',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 2, 10, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
working_time = self.jean._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(working_time, [
|
||||
(date(2018, 4, 2), 8),
|
||||
(date(2018, 4, 3), 8),
|
||||
(date(2018, 4, 4), 8),
|
||||
(date(2018, 4, 5), 8),
|
||||
(date(2018, 4, 6), 8),
|
||||
])
|
||||
|
||||
leave.unlink()
|
||||
|
|
@ -32,7 +32,7 @@ class TestResourcePerformance(TransactionCase):
|
|||
start = pytz.utc.localize(datetime.now() + relativedelta(month=1, day=1))
|
||||
stop = pytz.utc.localize(datetime.now() + relativedelta(month=12, day=31))
|
||||
start_time = time.time()
|
||||
calendar._attendance_intervals_batch(start, stop, resources=resources)
|
||||
calendar._attendance_intervals_batch(start, stop, resources=resources.resource_id)
|
||||
_logger.info('Attendance Intervals Batch (100): --- %s seconds ---', time.time() - start_time)
|
||||
# Before
|
||||
#INFO master test_performance: Attendance Intervals Batch (100): --- 2.0667169094085693 seconds ---
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,28 @@
|
|||
from odoo.exceptions import ValidationError
|
||||
|
||||
from odoo.addons.test_resource.tests.common import TestResourceCommon
|
||||
|
||||
|
||||
class TestErrors(TestResourceCommon):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def test_create_negative_leave(self):
|
||||
# from > to
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'error cannot return in the past',
|
||||
'resource_id': False,
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 20, 0, 0, tzinfo=self.jean.tz),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
})
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'error caused by timezones',
|
||||
'resource_id': False,
|
||||
'calendar_id': self.calendar_jean.id,
|
||||
'date_from': self.datetime_str(2018, 4, 3, 10, 0, 0, tzinfo='UTC'),
|
||||
'date_to': self.datetime_str(2018, 4, 3, 12, 0, 0, tzinfo='Etc/GMT-6'),
|
||||
})
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
from datetime import date, datetime
|
||||
|
||||
from freezegun import freeze_time
|
||||
from pytz import utc
|
||||
|
||||
from odoo.addons.test_resource.tests.common import TestResourceCommon
|
||||
|
||||
|
||||
class TestTimezones(TestResourceCommon):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.tz1 = 'Etc/GMT+6'
|
||||
self.tz2 = 'Europe/Brussels'
|
||||
self.tz3 = 'Etc/GMT-10'
|
||||
self.tz4 = 'Etc/GMT+10'
|
||||
|
||||
def test_work_hours_count(self):
|
||||
# When no timezone => UTC
|
||||
count = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 10, 8, 0, 0),
|
||||
self.datetime_tz(2018, 4, 10, 12, 0, 0),
|
||||
)
|
||||
self.assertEqual(count, 4)
|
||||
|
||||
# This timezone is not the same as the calendar's one
|
||||
count = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 10, 8, 0, 0, tzinfo=self.tz1),
|
||||
self.datetime_tz(2018, 4, 10, 12, 0, 0, tzinfo=self.tz1),
|
||||
)
|
||||
self.assertEqual(count, 0)
|
||||
|
||||
# Using two different timezones
|
||||
# 10-04-2018 06:00:00 - 10-04-2018 02:00:00
|
||||
count = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 10, 8, 0, 0, tzinfo=self.tz2),
|
||||
self.datetime_tz(2018, 4, 10, 12, 0, 0, tzinfo=self.tz3),
|
||||
)
|
||||
self.assertEqual(count, 0)
|
||||
|
||||
# Using two different timezones
|
||||
# 2018-04-10 06:00:00 - 2018-04-10 22:00:00
|
||||
count = self.calendar_jean.get_work_hours_count(
|
||||
self.datetime_tz(2018, 4, 10, 8, 0, 0, tzinfo=self.tz2),
|
||||
self.datetime_tz(2018, 4, 10, 12, 0, 0, tzinfo=self.tz4),
|
||||
)
|
||||
self.assertEqual(count, 8)
|
||||
|
||||
def test_plan_hours(self):
|
||||
dt = self.calendar_jean.plan_hours(10, self.datetime_tz(2018, 4, 10, 8, 0, 0))
|
||||
self.assertEqual(dt, self.datetime_tz(2018, 4, 11, 10, 0, 0))
|
||||
|
||||
dt = self.calendar_jean.plan_hours(10, self.datetime_tz(2018, 4, 10, 8, 0, 0, tzinfo=self.tz4))
|
||||
self.assertEqual(dt, self.datetime_tz(2018, 4, 11, 22, 0, 0, tzinfo=self.tz4))
|
||||
|
||||
def test_plan_days(self):
|
||||
dt = self.calendar_jean.plan_days(2, self.datetime_tz(2018, 4, 10, 8, 0, 0))
|
||||
self.assertEqual(dt, self.datetime_tz(2018, 4, 11, 14, 0, 0))
|
||||
|
||||
# We lose one day because of timezone
|
||||
dt = self.calendar_jean.plan_days(2, self.datetime_tz(2018, 4, 10, 8, 0, 0, tzinfo=self.tz4))
|
||||
self.assertEqual(dt, self.datetime_tz(2018, 4, 12, 4, 0, 0, tzinfo=self.tz4))
|
||||
|
||||
def test_work_data(self):
|
||||
# 09-04-2018 10:00:00 - 13-04-2018 18:00:00
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 4.75, 'hours': 38})
|
||||
|
||||
# 09-04-2018 00:00:00 - 13-04-2018 08:00:00
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz3),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz3),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 4, 'hours': 32})
|
||||
|
||||
# 09-04-2018 08:00:00 - 14-04-2018 12:00:00
|
||||
data = self.jean._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz2),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz4),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 5, 'hours': 40})
|
||||
|
||||
# Jules with 2 weeks calendar
|
||||
# 02-04-2018 00:00:00 - 6-04-2018 23:59:59
|
||||
data = self.jules._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 6, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)[self.jules.id]
|
||||
self.assertEqual(data, {'days': 4, 'hours': 30})
|
||||
|
||||
# Jules with 2 weeks calendar
|
||||
# 02-04-2018 00:00:00 - 14-04-2018 23:59:59
|
||||
data = self.jules._get_work_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2018, 4, 14, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)[self.jules.id]
|
||||
self.assertEqual(data, {'days': 6, 'hours': 46})
|
||||
|
||||
# Jules with 2 weeks calendar
|
||||
# 12-29-2014 00:00:00 - 27-12-2019 23:59:59 => 261 weeks
|
||||
# 130 weeks type 1: 131*4 = 524 days and 131*30 = 3930 hours
|
||||
# 131 weeks type 2: 130*2 = 260 days and 130*16 = 2080 hours
|
||||
data = self.jules._get_work_days_data_batch(
|
||||
self.datetime_tz(2014, 12, 29, 0, 0, 0, tzinfo=self.jules.tz),
|
||||
self.datetime_tz(2019, 12, 27, 23, 59, 59, tzinfo=self.jules.tz),
|
||||
)[self.jules.id]
|
||||
self.assertEqual(data, {'days': 784, 'hours': 6010})
|
||||
|
||||
def test_leave_data(self):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': '',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 9, 8, 0, 0, tzinfo=self.tz2),
|
||||
'date_to': self.datetime_str(2018, 4, 9, 14, 0, 0, tzinfo=self.tz2),
|
||||
})
|
||||
|
||||
# 09-04-2018 10:00:00 - 13-04-2018 18:00:00
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 0.5, 'hours': 4})
|
||||
|
||||
# 09-04-2018 00:00:00 - 13-04-2018 08:00:00
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz3),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz3),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 0.75, 'hours': 6})
|
||||
|
||||
# 09-04-2018 08:00:00 - 14-04-2018 12:00:00
|
||||
data = self.jean._get_leave_days_data_batch(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz2),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz4),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(data, {'days': 0.75, 'hours': 6})
|
||||
|
||||
def test_leaves(self):
|
||||
leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': '',
|
||||
'calendar_id': self.jean.resource_calendar_id.id,
|
||||
'resource_id': self.jean.resource_id.id,
|
||||
'date_from': self.datetime_str(2018, 4, 9, 8, 0, 0, tzinfo=self.tz2),
|
||||
'date_to': self.datetime_str(2018, 4, 9, 14, 0, 0, tzinfo=self.tz2),
|
||||
})
|
||||
|
||||
# 09-04-2018 10:00:00 - 13-04-2018 18:00:00
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0),
|
||||
)
|
||||
self.assertEqual(leaves, [(date(2018, 4, 9), 4, leave)])
|
||||
|
||||
# 09-04-2018 00:00:00 - 13-04-2018 08:00:00
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz3),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz3),
|
||||
)
|
||||
self.assertEqual(leaves, [(date(2018, 4, 9), 6, leave)])
|
||||
|
||||
# 09-04-2018 08:00:00 - 14-04-2018 12:00:00
|
||||
leaves = self.jean.list_leaves(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz2),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz4),
|
||||
)
|
||||
self.assertEqual(leaves, [(date(2018, 4, 9), 6, leave)])
|
||||
|
||||
def test_works(self):
|
||||
work = self.jean._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(work, [
|
||||
(date(2018, 4, 9), 6),
|
||||
(date(2018, 4, 10), 8),
|
||||
(date(2018, 4, 11), 8),
|
||||
(date(2018, 4, 12), 8),
|
||||
(date(2018, 4, 13), 8),
|
||||
])
|
||||
|
||||
work = self.jean._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz3),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz3),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(len(work), 4)
|
||||
self.assertEqual(work, [
|
||||
(date(2018, 4, 9), 8),
|
||||
(date(2018, 4, 10), 8),
|
||||
(date(2018, 4, 11), 8),
|
||||
(date(2018, 4, 12), 8),
|
||||
])
|
||||
|
||||
work = self.jean._list_work_time_per_day(
|
||||
self.datetime_tz(2018, 4, 9, 8, 0, 0, tzinfo=self.tz2),
|
||||
self.datetime_tz(2018, 4, 13, 16, 0, 0, tzinfo=self.tz4),
|
||||
)[self.jean.id]
|
||||
self.assertEqual(work, [
|
||||
(date(2018, 4, 9), 8),
|
||||
(date(2018, 4, 10), 8),
|
||||
(date(2018, 4, 11), 8),
|
||||
(date(2018, 4, 12), 8),
|
||||
(date(2018, 4, 13), 8),
|
||||
])
|
||||
|
||||
@freeze_time("2022-09-21 15:30:00", tz_offset=-10)
|
||||
def test_unavailable_intervals(self):
|
||||
resource = self.env['resource.resource'].create({
|
||||
'name': 'resource',
|
||||
'tz': self.tz3,
|
||||
})
|
||||
intervals = resource._get_unavailable_intervals(datetime(2022, 9, 21), datetime(2022, 9, 22))
|
||||
self.assertEqual(next(iter(intervals.values())), [
|
||||
(datetime(2022, 9, 21, 0, 0, tzinfo=utc), datetime(2022, 9, 21, 6, 0, tzinfo=utc)),
|
||||
(datetime(2022, 9, 21, 10, 0, tzinfo=utc), datetime(2022, 9, 21, 11, 0, tzinfo=utc)),
|
||||
(datetime(2022, 9, 21, 15, 0, tzinfo=utc), datetime(2022, 9, 22, 0, 0, tzinfo=utc)),
|
||||
])
|
||||
|
||||
def test_flexible_resource_leave_interval(self):
|
||||
"""
|
||||
Test whole day off for a flexible resource.
|
||||
The standard 8 - 17 leave should be converted to a whole day leave interval for the flexible resource.
|
||||
"""
|
||||
|
||||
flexible_calendar = self.env['resource.calendar'].create({
|
||||
'name': 'Flex Calendar',
|
||||
'tz': 'UTC',
|
||||
'flexible_hours': True,
|
||||
'full_time_required_hours': 40,
|
||||
'hours_per_day': 8,
|
||||
'hours_per_week': 40,
|
||||
})
|
||||
flex_resource = self.env['resource.resource'].create({
|
||||
'name': 'Test FlexResource',
|
||||
'calendar_id': flexible_calendar.id,
|
||||
})
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Standard Time Off',
|
||||
'calendar_id': flexible_calendar.id,
|
||||
'resource_id': flex_resource.id,
|
||||
'date_from': '2025-03-07 08:00:00',
|
||||
'date_to': '2025-03-07 17:00:00',
|
||||
})
|
||||
|
||||
start_dt = datetime(2025, 3, 7, 8, 0, 0, tzinfo=utc)
|
||||
end_dt = datetime(2025, 3, 7, 16, 00, 00, 00, tzinfo=utc)
|
||||
|
||||
intervals = flexible_calendar._leave_intervals_batch(start_dt, end_dt, [flex_resource])
|
||||
intervals_list = list(intervals[flex_resource.id])
|
||||
self.assertEqual(len(intervals_list), 1, "There should be one leave interval")
|
||||
interval = intervals_list[0]
|
||||
self.assertEqual(interval[0], start_dt, "The start of the interval should be 08:00:00")
|
||||
self.assertEqual(interval[1], end_dt, "The end of the interval should be 16:00:00")
|
||||
Loading…
Add table
Add a link
Reference in a new issue