Skip to content

Commit

Permalink
Merge commit 'refs/pull/137/head' of github.com:acsone/acsone-addons …
Browse files Browse the repository at this point in the history
…into 10.0-lih_master
  • Loading branch information
adrienpeiffer committed Apr 13, 2017
2 parents c47a654 + 175bdbd commit b5e963c
Show file tree
Hide file tree
Showing 14 changed files with 431 additions and 0 deletions.
26 changes: 26 additions & 0 deletions hr_holidays_working_time/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

========================
HR holidays working time
========================

This module was written to compute the duration of a leave request from the employee working time.

Credits
=======

Contributors
------------

* Adrien Peiffer <adrien.peiffer@acsone.eu>

Maintainer
----------

.. image:: https://www.acsone.eu/logo.png
:alt: ACSONE SA/NV
:target: http://www.acsone.eu

This module is maintained by ACSONE SA/NV.
1 change: 1 addition & 0 deletions hr_holidays_working_time/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
23 changes: 23 additions & 0 deletions hr_holidays_working_time/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

{
'name': "HR holidays working time",

'summary': """
Compute duration of leaves from the employee's working time""",
'author': 'ACSONE SA/NV',
'website': "http://acsone.eu",
'category': 'Human resources',
'version': '10.0.1.0.0',
'license': 'AGPL-3',
'depends': [
'hr',
'hr_holidays',
'hr_employee_current_contract',
],
'data': [
'views/hr_holidays_view.xml',
],
}
18 changes: 18 additions & 0 deletions hr_holidays_working_time/migrations/10.0.1.0.0/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-

import logging

_logger = logging.getLogger(__name__)
_logger.setLevel(logging.DEBUG)


def migrate(cr, version):
cr.execute(
"""ALTER TABLE hr_holidays ADD COLUMN "set_hours_manually" BOOLEAN""")
cr.execute("UPDATE hr_holidays set set_hours_manually='t'")
cr.execute(
"ALTER TABLE hr_holidays ADD COLUMN 'number_of_hours_temp_manual' "
"double precision")
cr.execute(
"UPDATE hr_holidays set "
"number_of_hours_temp_manual=number_of_hours_temp")
1 change: 1 addition & 0 deletions hr_holidays_working_time/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import hr_holidays
124 changes: 124 additions & 0 deletions hr_holidays_working_time/models/hr_holidays.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import models, api, fields
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from datetime import datetime, timedelta
from dateutil import rrule


class HrHolidays(models.Model):
_inherit = 'hr.holidays'

number_of_hours_temp = fields.Float(
string='Allocation Hours', compute='_compute_number_of_hours_temp',
store=True)
number_of_hours = fields.Float(
'Number of Hours', compute='_compute_number_of_hours', store=True,
track_visibility='onchange')
number_of_days_temp = fields.Float(
compute='_compute_number_of_hours_from_hours', store=True,
required=False, readonly=True)
number_of_hours_temp_manual = fields.Float(string='Allocation Hours')
set_hours_manually = fields.Boolean(track_visibility='onchange')

@api.model
def default_get(self, fields_list):
res = super(HrHolidays, self).default_get(fields_list)
if res.get('type', '') == 'add':
res['set_hours_manually'] = True
return res

@api.model
def _get_day_to_hours_factor(self):
return 8.0

@api.one
@api.depends('number_of_hours_temp')
def _compute_number_of_hours_from_hours(self):
if self.number_of_hours_temp:
self.number_of_days_temp = \
self.number_of_hours_temp / self._get_day_to_hours_factor()
else:
self.number_of_days_temp = 0

@api.one
@api.depends('type', 'number_of_hours_temp')
def _compute_number_of_hours(self):
if self.type == 'remove':
self.number_of_hours = - self.number_of_hours_temp
else:
self.number_of_hours = self.number_of_hours_temp

@api.multi
@api.depends('date_to', 'date_from', 'employee_id',
'number_of_hours_temp_manual', 'set_hours_manually')
def _compute_number_of_hours_temp(self):
for record in self:
if not record.set_hours_manually:
if record.date_to and record.date_from and\
record.date_from <= record.date_to:
record.number_of_hours_temp =\
record._get_duration_from_working_time(
record.date_to, record.date_from,
record.employee_id)
else:
record.number_of_hours_temp =\
record.number_of_hours_temp_manual

@api.model
def _no_current_contract(self, day):
return False

@api.model
def _no_working_time(self, day):
return False

@api.multi
def _get_duration_from_working_time(self, date_to, date_from, employee):
if employee:
if employee.id:
contract_obj = self.env['hr.contract']
start_dt = datetime.strptime(date_from,
DEFAULT_SERVER_DATETIME_FORMAT)
end_dt = datetime.strptime(date_to,
DEFAULT_SERVER_DATETIME_FORMAT)
hours = 0.0
for day in rrule.rrule(
rrule.DAILY, dtstart=start_dt,
until=(end_dt + timedelta(days=1))
.replace(hour=0, minute=0, second=0),
byweekday=[0, 1, 2, 3, 4, 5, 6]):
day_start_dt = day.replace(hour=0, minute=0, second=0)
day_str = fields.Date.to_string(day_start_dt)
current_contract_id =\
employee.sudo()._get_current_contract(day_str)
if not current_contract_id:
if not self._no_current_contract(day_str):
continue
current_contract =\
contract_obj.sudo().browse([current_contract_id])
working_time = current_contract.working_hours
if not working_time.id:
if not self._no_working_time(day_str):
continue
if start_dt and day.date() == start_dt.date():
day_start_dt = start_dt
day_end_dt = day.replace(hour=23, minute=59, second=59)
if end_dt and day.date() == end_dt.date():
day_end_dt = end_dt
hours += working_time.get_working_hours_of_date(
start_dt=day_start_dt, end_dt=day_end_dt,
compute_leaves=True, resource_id=None,
default_interval=None)
return hours
return False

@api.model
def _prepare_create_by_category(self, record, employee):
res = super(HrHolidays, self)._prepare_create_by_category(
record, employee)
res['set_hours_manually'] = True
res['number_of_hours_temp_manual'] = record.number_of_hours_temp_manual
return res
1 change: 1 addition & 0 deletions hr_holidays_working_time/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_hr_holidays_working_time
155 changes: 155 additions & 0 deletions hr_holidays_working_time/tests/test_hr_holidays_working_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
# Copyright 2015-2017 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo.tests import common
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT,\
DEFAULT_SERVER_DATETIME_FORMAT
from datetime import datetime, timedelta
from odoo import fields
import pytz


def create_simple_contract(self, employee, date_start, date_end=False):
vals = {
'name': 'Test',
'employee_id': employee.id,
'date_start': date_start,
'date_end': date_end,
'wage': 1.0,
}
return self.contract_obj.create(vals)


def create_resource_leave(self, date_from, date_to, calendar_id):
vals = {
'name': 'Test',
'date_from': date_from,
'date_to': date_to,
'calendar_id': calendar_id,
'resource_id': False,
}
return self.env['resource.calendar.leaves'].create(vals)


def create_full_working_time(self):
vals = {
'name': 'Test Full Time',
'attendance_ids': [(0, 0, {'name': 'Test0',
'dayofweek': '0',
'hour_from': 8,
'hour_to': 16}),
(0, 0, {'name': 'Test1',
'dayofweek': '1',
'hour_from': 8,
'hour_to': 16}),
(0, 0, {'name': 'Test2',
'dayofweek': '2',
'hour_from': 8,
'hour_to': 16}),
(0, 0, {'name': 'Test3',
'dayofweek': '3',
'hour_from': 8,
'hour_to': 16}),
(0, 0, {'name': 'Test4',
'dayofweek': '4',
'hour_from': 8,
'hour_to': 16})]
}
return self.calendar_obj.create(vals)


def create_leave(self, date_from, date_to, employee):
vals = {
'employee_id': employee.id,
'date_from': date_from,
'date_to': date_to,
'holiday_status_id': self.env.ref('hr_holidays.holiday_status_sl').id
}
return self.holidays_obj.create(vals)


class TestHrHolidaysWorkingTime(common.TransactionCase):

def setUp(self):
super(TestHrHolidaysWorkingTime, self).setUp()
self.holidays_obj = self.env['hr.holidays']
self.employee_obj = self.env['hr.employee']
self.contract_obj = self.env['hr.contract']
self.calendar_obj = self.env['resource.calendar']
self.employee01 = self.env.ref('hr.employee_vad')
self.today = datetime.now()
# Get the first day of the current week
self.first_day_start_dt =\
self.today - timedelta(days=self.today.weekday())
self.contract01 = create_simple_contract(
self, self.employee01, self.first_day_start_dt)
self.working_time01 = create_full_working_time(self)
self.contract01.working_hours = self.working_time01
self._context = self.env['res.users'].context_get()

def test_holidays_working_time_one_day(self):
date_from_dt = self.first_day_start_dt.replace(hour=8, minute=0,
second=0)
tz_info = fields.Datetime.context_timestamp(self, date_from_dt).tzinfo
date_from_dt = date_from_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_to_dt = self.first_day_start_dt.replace(hour=16, minute=0,
second=0)
tz_info = fields.Datetime.context_timestamp(self, date_to_dt).tzinfo
date_to_dt = date_to_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_from = date_from_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
date_to = date_to_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
holiday = create_leave(self, date_from, date_to, self.employee01)
self.assertEqual(holiday.number_of_hours_temp, 8)

def test_holidays_working_time_complete_week(self):
date_from_dt = self.first_day_start_dt.replace(hour=8, minute=0,
second=0)
tz_info = fields.Datetime.context_timestamp(self, date_from_dt).tzinfo
date_from_dt = date_from_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_to_dt = self.first_day_start_dt + timedelta(days=4)
date_to_dt = date_to_dt.replace(hour=16, minute=0, second=0)
tz_info = fields.Datetime.context_timestamp(self, date_to_dt).tzinfo
date_to_dt = date_to_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_from = date_from_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
date_to = date_to_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
holiday = create_leave(self, date_from, date_to, self.employee01)
self.assertEqual(holiday.number_of_hours_temp, 40)

def test_holidays_working_time_weekend(self):
date_from_dt = self.first_day_start_dt + timedelta(days=5)
date_from_dt = date_from_dt.replace(hour=8, minute=0, second=0)
tz_info = fields.Datetime.context_timestamp(self, date_from_dt).tzinfo
date_from_dt = date_from_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_to_dt = self.first_day_start_dt + timedelta(days=6)
date_to_dt = date_to_dt.replace(hour=16, minute=0, second=0)
tz_info = fields.Datetime.context_timestamp(self, date_to_dt).tzinfo
date_to_dt = date_to_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_from = date_from_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
date_to = date_to_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
holiday = create_leave(self, date_from, date_to, self.employee01)
self.assertEqual(holiday.number_of_hours_temp, 0)

def test_holidays_working_time_leave(self):
date_from_dt = self.first_day_start_dt.replace(hour=8, minute=0,
second=0)
tz_info = fields.Datetime.context_timestamp(self, date_from_dt).tzinfo
date_from_dt = date_from_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_to_dt = self.first_day_start_dt.replace(hour=16, minute=0,
second=0)
tz_info = fields.Datetime.context_timestamp(self, date_to_dt).tzinfo
date_to_dt = date_to_dt.replace(tzinfo=tz_info)\
.astimezone(pytz.UTC).replace(tzinfo=None)
date_from = date_from_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
date_to = date_to_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
# I create a resource leave
create_resource_leave(self, date_from, date_to, self.working_time01.id)
holiday = create_leave(self, date_from, date_to, self.employee01)
self.assertEqual(holiday.number_of_hours_temp, 0)

0 comments on commit b5e963c

Please sign in to comment.