Skip to content

Commit

Permalink
[MIG] hr_holidays_natural_period: Migration to 17.0
Browse files Browse the repository at this point in the history
  • Loading branch information
peluko00 authored and carlos-lopez-tecnativa committed Sep 16, 2024
1 parent d0d6364 commit df9187e
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 68 deletions.
5 changes: 5 additions & 0 deletions hr_holidays_natural_period/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ Contributors

- Víctor Martínez
- Pedro Baeza
- Carlos López

- APSL-Nagarro <https://www.apsl.tech>

- Antoni Marroig <[email protected]>

Maintainers
-----------
Expand Down
2 changes: 1 addition & 1 deletion hr_holidays_natural_period/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Holidays natural period",
"summary": "Apply natural days in holidays",
"version": "16.0.1.0.1",
"version": "17.0.1.0.0",
"category": "Human Resources",
"website": "https://github.com/OCA/hr-holidays",
"author": "Tecnativa, Odoo Community Association (OCA)",
Expand Down
2 changes: 1 addition & 1 deletion hr_holidays_natural_period/demo/hr_leave_type_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<record id="hr_leave_type_natural_day_test" model="hr.leave.type">
<field name="name">Test Time Off (natural day)</field>
<field name="request_unit">natural_day</field>
<field name="responsible_id" ref="base.user_admin" />
<field name="responsible_ids" eval="[(4, ref('base.user_admin'))]" />
<field name="employee_requests">yes</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions hr_holidays_natural_period/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from . import hr_leave_type
from . import hr_leave
from . import resource_calendar
from . import hr_leave_allocation
from . import hr_employee
22 changes: 22 additions & 0 deletions hr_holidays_natural_period/models/hr_employee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 APSL-Nagarro - Antoni Marroig Campomar
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import models


class HrEmployee(models.Model):
_inherit = "hr.employee"

def _get_consumed_leaves(self, leave_types, target_date=False, ignore_future=False):
"""We need to set request_unit as 'day' to avoid the calculations being done
as hours.
"""
old_request_unit_data = {}
for item in leave_types.filtered(lambda x: x.request_unit == "natural_day"):
old_request_unit_data[item.id] = item.request_unit
item.sudo().request_unit = "day"
res = super()._get_consumed_leaves(leave_types, target_date, ignore_future)
for item in leave_types:
if item.id in old_request_unit_data:
item.sudo().request_unit = old_request_unit_data[item.id]
return res
29 changes: 14 additions & 15 deletions hr_holidays_natural_period/models/hr_leave.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
# Copyright 2020-2024 Tecnativa - Víctor Martínez
# Copyright 2024 Tecnativa - Carlos Lopez
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models
from odoo import models


class HrLeave(models.Model):
_inherit = "hr.leave"

def _get_number_of_days(self, date_from, date_to, employee_id):
instance = self.with_context(
natural_period=bool(self.holiday_status_id.request_unit == "natural_day")
def _get_duration(self, check_leave_type=True, resource_calendar=None):
# We need to set request_unit as 'day'
# to avoid the calculations being done as hours.
is_request_unit_natural_day = (
self.holiday_status_id.request_unit == "natural_day"
)
return super(HrLeave, instance)._get_number_of_days(
date_from, date_to, employee_id
instance = self.with_context(natural_period=is_request_unit_natural_day)
if is_request_unit_natural_day:
self.holiday_status_id.sudo().request_unit = "day"
res = super(HrLeave, instance)._get_duration(
check_leave_type=check_leave_type, resource_calendar=resource_calendar
)

@api.model_create_multi
def create(self, vals_list):
"""Only in UX an incorrect value is set, recalculate.
https://github.com/OCA/hr-holidays/issues/105."""
res = super().create(vals_list)
res.filtered(
lambda x: x.holiday_status_id.request_unit == "natural_day"
)._compute_number_of_days()
if is_request_unit_natural_day:
self.holiday_status_id.sudo().request_unit = "natural_day"
return res
13 changes: 13 additions & 0 deletions hr_holidays_natural_period/models/hr_leave_allocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2024 APSL-Nagarro - Antoni Marroig Campomar
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class HrLeaveAllocation(models.Model):
_inherit = "hr.leave.allocation"

# Added option to this field because it is now computed (was related before).
type_request_unit = fields.Selection(
selection_add=[("natural_day", "Natural day")],
)
19 changes: 0 additions & 19 deletions hr_holidays_natural_period/models/hr_leave_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,3 @@ class HrLeaveType(models.Model):
selection_add=[("natural_day", "Natural day")],
ondelete={"natural_day": "set default"},
)

def _get_employees_days_per_allocation(self, employee_ids, date=None):
"""We need to set request_unit as 'day' to avoid the calculations being done
as hours.
Related code:
hr_holidays/models/hr_leave_type.py#L326
hr_holidays/models/hr_leave_type.py#L389
"""
old_request_unit_data = {}
for item in self.filtered(lambda x: x.request_unit == "natural_day"):
old_request_unit_data[item.id] = item.request_unit
item.sudo().request_unit = "day"
res = super()._get_employees_days_per_allocation(
employee_ids=employee_ids, date=date
)
for item in self:
if item.id in old_request_unit_data:
item.sudo().request_unit = old_request_unit_data[item.id]
return res
46 changes: 43 additions & 3 deletions hr_holidays_natural_period/models/resource_calendar.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
# Copyright 2020-2021 Tecnativa - Víctor Martínez
# Copyright 2024 Tecnativa - Carlos Lopez
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from collections import defaultdict
from datetime import datetime, time

from dateutil import rrule
from pytz import timezone

from odoo import models
from odoo.tools.float_utils import float_round

from odoo.addons.resource.models.resource import Intervals
from odoo.addons.resource.models.utils import Intervals


class ResourceCalendar(models.Model):
_inherit = "resource.calendar"

def _get_attendance_intervals_days_data(self, attendance_intervals):
# replace function to avoid division by zero
# it occurs when the calendar has no attendance intervals
# i.e no working days(weekends)
# meta is a recordset of resource.calendar.attendance
# but in the function _natural_period_intervals_batch
# we are passing an empty recordset
if not self.env.context.get("natural_period"):
return super()._get_attendance_intervals_days_data(attendance_intervals)
day_hours = defaultdict(float)
day_days = defaultdict(float)
for start, stop, meta in attendance_intervals:
interval_hours = (stop - start).total_seconds() / 3600
if meta:
interval_days = (
sum(meta.mapped("duration_days"))
* interval_hours
/ sum(meta.mapped("duration_hours"))
)
else:
interval_days = interval_hours / 24 # hours on a day

Check warning on line 40 in hr_holidays_natural_period/models/resource_calendar.py

View check run for this annotation

Codecov / codecov/patch

hr_holidays_natural_period/models/resource_calendar.py#L40

Added line #L40 was not covered by tests
day_hours[start.date()] += interval_hours
day_days[start.date()] += interval_days

return {
# Round the number of days to the closest 16th of a day.
"days": float_round(
sum(day_days[day] for day in day_days), precision_rounding=0.001
),
"hours": sum(day_hours.values()),
}

def _exist_interval_in_date(self, intervals, date):
for interval in intervals:
if interval[0].date() == date:
Expand Down Expand Up @@ -41,10 +76,15 @@ def _natural_period_intervals_batch(self, start_dt, end_dt, intervals, resources
return intervals

def _attendance_intervals_batch(
self, start_dt, end_dt, resources=None, domain=None, tz=None
self, start_dt, end_dt, resources=None, domain=None, tz=None, lunch=False
):
res = super()._attendance_intervals_batch(
start_dt=start_dt, end_dt=end_dt, resources=resources, domain=domain, tz=tz
start_dt=start_dt,
end_dt=end_dt,
resources=resources,
domain=domain,
tz=tz,
lunch=lunch,
)
if self.env.context.get("natural_period"):
return self._natural_period_intervals_batch(
Expand Down
5 changes: 5 additions & 0 deletions hr_holidays_natural_period/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@

> - Víctor Martínez
> - Pedro Baeza
> - Carlos López
- APSL-Nagarro \<<https://www.apsl.tech>\>

> - Antoni Marroig \<<[email protected]>\>
8 changes: 8 additions & 0 deletions hr_holidays_natural_period/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,14 @@ <h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Víctor Martínez</li>
<li>Pedro Baeza</li>
<li>Carlos López</li>
</ul>
</blockquote>
</li>
<li><p class="first">APSL-Nagarro &lt;<a class="reference external" href="https://www.apsl.tech">https://www.apsl.tech</a>&gt;</p>
<blockquote>
<ul class="simple">
<li>Antoni Marroig &lt;<a class="reference external" href="mailto:amarroig&#64;apsl.net">amarroig&#64;apsl.net</a>&gt;</li>
</ul>
</blockquote>
</li>
Expand Down
56 changes: 27 additions & 29 deletions hr_holidays_natural_period/tests/test_hr_leave.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,17 @@
from freezegun import freeze_time

from odoo import fields
from odoo.tests import Form, common, new_test_user
from odoo.tests import Form, new_test_user
from odoo.tests.common import users

from odoo.addons.base.tests.common import BaseCommon


@freeze_time("2023-01-01", tick=True)
class TestHrLeave(common.TransactionCase):
class TestHrLeave(BaseCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(
context=dict(
cls.env.context,
mail_create_nolog=True,
mail_create_nosubscribe=True,
mail_notrack=True,
no_reset_password=True,
)
)
cls.HrLeave = cls.env["hr.leave"]
cls.leave_type = cls.env.ref(
"hr_holidays_natural_period.hr_leave_type_natural_day_test"
)
Expand All @@ -43,15 +35,15 @@ def setUpClass(cls):
partner = cls.env["res.partner"].create(
{
"name": "Test employee",
"type": "private",
"type": "other",
"country_id": cls.env.ref("base.es").id,
}
)
cls.user = new_test_user(cls.env, login="test-user")
cls.employee = cls.env["hr.employee"].create(
{
"name": "Test employee",
"address_home_id": partner.id,
"address_id": partner.id,
"resource_calendar_id": calendar.id,
"user_id": cls.user.id,
}
Expand All @@ -64,7 +56,7 @@ def _create_leave_allocation(self, leave_type, days):
default_date_to="%s-12-31" % (fields.Date.today().year),
)
)
leave_allocation_form.name = "TEST"

leave_allocation_form.holiday_status_id = leave_type
leave_allocation_form.number_of_days_display = days
return leave_allocation_form.save()
Expand All @@ -79,14 +71,17 @@ def _create_hr_leave(self, leave_type, date_from, date_to):
@users("test-user")
def test_hr_leave_natural_day(self):
leave_allocation = self._create_leave_allocation(self.leave_type, 5)
leave_allocation.action_confirm()
leave_allocation.sudo().action_validate()
res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
self.assertEqual(res_leave_type["remaining_leaves"], "5")
self.assertEqual(res_leave_type["virtual_remaining_leaves"], "5")
self.assertEqual(res_leave_type["max_leaves"], "5")
self.assertEqual(res_leave_type["leaves_taken"], "0")
self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
res_leave_type = (
self.env["hr.leave.type"]
.with_company(self.env.company)
.get_allocation_data_request()[0][1]
)
self.assertEqual(res_leave_type["remaining_leaves"], 5)
self.assertEqual(res_leave_type["virtual_remaining_leaves"], 5)
self.assertEqual(res_leave_type["max_leaves"], 5)
self.assertEqual(res_leave_type["leaves_taken"], 0)
self.assertEqual(res_leave_type["virtual_leaves_taken"], 0)
self.assertEqual(res_leave_type["request_unit"], "natural_day")
leave = self._create_hr_leave(self.leave_type, "2023-01-02", "2023-01-05")
self.assertEqual(leave.number_of_days, 4.0)
Expand All @@ -95,14 +90,17 @@ def test_hr_leave_natural_day(self):
@users("test-user")
def test_hr_leave_day(self):
leave_allocation = self._create_leave_allocation(self.leave_type_day, 5)
leave_allocation.action_confirm()
leave_allocation.sudo().action_validate()
res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
self.assertEqual(res_leave_type["remaining_leaves"], "5")
self.assertEqual(res_leave_type["virtual_remaining_leaves"], "5")
self.assertEqual(res_leave_type["max_leaves"], "5")
self.assertEqual(res_leave_type["leaves_taken"], "0")
self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
res_leave_type = (
self.env["hr.leave.type"]
.with_company(self.env.company)
.get_allocation_data_request()[0][1]
)
self.assertEqual(res_leave_type["remaining_leaves"], 5)
self.assertEqual(res_leave_type["virtual_remaining_leaves"], 5)
self.assertEqual(res_leave_type["max_leaves"], 5)
self.assertEqual(res_leave_type["leaves_taken"], 0)
self.assertEqual(res_leave_type["virtual_leaves_taken"], 0)
self.assertEqual(res_leave_type["request_unit"], "day")
leave = self._create_hr_leave(self.leave_type_day, "2023-01-08", "2023-01-15")
self.assertEqual(leave.number_of_days, 5)
Expand Down

0 comments on commit df9187e

Please sign in to comment.