Skip to content

Commit

Permalink
Merge pull request #107 from hbrunn/15.0-75-vacation_wizard
Browse files Browse the repository at this point in the history
[ADD] #75 wizard for allocation vacations
  • Loading branch information
albig authored Mar 4, 2024
2 parents 868dfc8 + 6e08740 commit 0f45072
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 0 deletions.
1 change: 1 addition & 0 deletions verdigado_attendance/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import models
from . import wizards
1 change: 1 addition & 0 deletions verdigado_attendance/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"views/hr_menu_human_resources_configuration.xml",
"views/menu.xml",
"views/res_config_settings.xml",
"wizards/verdigado_holidays_wizard.xml",
],
"demo": [
"demo/res_users.xml",
Expand Down
1 change: 1 addition & 0 deletions verdigado_attendance/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ access_hr_attendance_report_verdigado,hr.attendance.report.verdigado,hr_attendan
access_hr_attendance_user_verdigado,hr.attendance.user.verdigado,hr_attendance.model_hr_attendance,hr_attendance.group_hr_attendance,1,1,1,1
access_resource_calendar_officer_verdigado,resource.calendar.system,resource.model_resource_calendar,hr.group_hr_manager,1,1,1,1
access_resource_calendar_attendance_officer_verdigado,resource.calendar.attendance.system,resource.model_resource_calendar_attendance,hr.group_hr_manager,1,1,1,1
access_verdigado_holidays_wizard,access_verdigado_holidays_wizard,verdigado_attendance.model_verdigado_holidays_wizard,hr.group_hr_manager,1,1,1,1
hr_attendance_break.access_hr_attendance_break,access_hr_attendance_break,model_hr_attendance_break,base.group_user,1,1,1,1
1 change: 1 addition & 0 deletions verdigado_attendance/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from . import hr_case
from . import test_holidays
from . import test_holiday_wizard
from . import test_hr_access
from . import test_overtime_calculation
from . import test_misc
Expand Down
206 changes: 206 additions & 0 deletions verdigado_attendance/tests/test_holiday_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Copyright 2023 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


from dateutil.relativedelta import relativedelta

from odoo import fields
from odoo.tests.common import TransactionCase


class TestHolidayWizard(TransactionCase):
def setUp(self):
super().setUp()
self.employee = self.env.ref("hr.employee_qdp")
self.leave_type = self.env.ref("hr_holidays.holiday_status_cl")

def _test_holidays_wizard(self):
wizard = (
self.env["verdigado.holidays.wizard"]
.with_context(
active_model="hr.employee",
active_ids=self.employee.ids,
active_id=self.employee.id,
)
.create({})
)
action = wizard.button_assign_vacation()
return self.env[action["res_model"]].search(action["domain"])

def test_no_validation(self):
"""Test that the holidays wizard creates allocations with slightly changed defaults"""
self.leave_type.allocation_validation_type = "no"
self.employee.calendar_ids.unlink()
allocation = self._test_holidays_wizard()
self.assertFalse(allocation)

def test_25h_week(self):
"""Test an employee with a 2 day week"""
calendar_25h = self.env["resource.calendar"].create(
{
"name": "25h week",
"attendance_ids": [
(
0,
0,
{
"name": "Monday",
"dayofweek": "0",
"hour_from": 8,
"hour_to": 16,
},
),
(
0,
0,
{
"name": "Tuesday",
"dayofweek": "1",
"hour_from": 8,
"hour_to": 16,
},
),
(
0,
0,
{
"name": "Wednesday",
"dayofweek": "2",
"hour_from": 8,
"hour_to": 17,
},
),
],
}
)
self.employee.write(
{
"calendar_ids": [
(6, 0, []),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=1, day=1, years=1),
"date_end": fields.Date.today()
+ relativedelta(month=6, day=30, years=1),
"calendar_id": calendar_25h.id,
},
),
],
}
)
allocation = self._test_holidays_wizard()
self.assertEqual(allocation.employee_id, self.employee)
self.assertEqual(allocation.number_of_days, 9)

def test_multi_calendar_2_times5days(self):
"""Two times 5d week"""
self.employee.write(
{
"calendar_ids": [
(6, 0, []),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=1, day=1, years=1),
"date_end": fields.Date.today()
+ relativedelta(month=6, day=30, years=1),
"calendar_id": self.env.ref(
"resource.resource_calendar_std"
).id,
},
),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=7, day=1, years=1),
"date_end": fields.Date.today()
+ relativedelta(month=12, day=31, years=1),
"calendar_id": self.env.ref(
"resource.resource_calendar_std"
).id,
},
),
],
}
)
allocation = self._test_holidays_wizard()
self.assertEqual(allocation.number_of_days, 30)

def test_multi_calendar_short_4day_long_5day(self):
"""4d week in jan/feb, 5d rest"""
four_day_week = self.env.ref("resource.resource_calendar_std_38h")
four_day_week.attendance_ids.filtered(lambda x: x.dayofweek == "4").unlink()
self.employee.write(
{
"calendar_ids": [
(6, 0, []),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=1, day=1, years=1),
"date_end": fields.Date.today()
+ relativedelta(month=2, day=28, years=1),
"calendar_id": four_day_week.id,
},
),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=3, day=1, years=1),
"calendar_id": self.env.ref(
"resource.resource_calendar_std"
).id,
},
),
],
}
)
allocation = self._test_holidays_wizard()
self.assertEqual(allocation.number_of_days, 29)

def test_multi_calendar_short_4day_long_5day_no_month_boundary(self):
"""4d week in jan/feb, 5d rest without ending at a month end"""
four_day_week = self.env.ref("resource.resource_calendar_std_38h")
four_day_week.attendance_ids.filtered(lambda x: x.dayofweek == "4").unlink()
self.employee.write(
{
"calendar_ids": [
(6, 0, []),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=1, day=1, years=1),
"date_end": fields.Date.today()
+ relativedelta(month=2, day=15, years=1),
"calendar_id": four_day_week.id,
},
),
(
0,
0,
{
"date_start": fields.Date.today()
+ relativedelta(month=2, day=16, years=1),
"calendar_id": self.env.ref(
"resource.resource_calendar_std"
).id,
},
),
],
}
)
allocation = self._test_holidays_wizard()
self.assertEqual(allocation.number_of_days, 29)
2 changes: 2 additions & 0 deletions verdigado_attendance/wizards/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import verdigado_holidays_wizard
120 changes: 120 additions & 0 deletions verdigado_attendance/wizards/verdigado_holidays_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

import math
from datetime import date

from dateutil.relativedelta import relativedelta

from odoo import _, fields, models


class VerdigadoHolidaysWizard(models.TransientModel):
_name = "verdigado.holidays.wizard"
_description = "Create holidays allocations"

full_vacation = fields.Float(
default=30,
string="Vacation days (100%)",
required=True,
help="Vacation of a FTE in days",
)
year = fields.Selection(
lambda self: [
(year, year)
for year in range(fields.Date.today().year, fields.Date.today().year + 3)
],
default=lambda self: fields.Date.today().year + 1,
required=True,
)
date_start = fields.Date(
required=True,
string="Validity start",
default=lambda self: fields.Date.today()
+ relativedelta(month=1, day=1, years=1),
)
date_end = fields.Date(
string="Validity end",
default=lambda self: fields.Date.today()
+ relativedelta(month=1, day=1, years=1),
)
leave_type_id = fields.Many2one(
"hr.leave.type",
required=True,
default=lambda self: self.env.ref("hr_holidays.holiday_status_cl", False),
)
employee_ids = fields.Many2many("hr.employee", string="Employees")

def button_assign_vacation(self):
interval_start = date(int(self.year), 1, 1)
interval_end = interval_start.replace(month=12, day=31)
days = (interval_end - interval_start).days
allocations = self.env["hr.leave.allocation"].browse([])
for employee in self.employee_ids or self.env["hr.employee"].browse(
self.env.context.get("active_ids", [])
):
percentage = 0.0
for calendar in employee.calendar_ids:
if (
calendar.date_start
and calendar.date_start >= interval_end
or calendar.date_end
and calendar.date_end <= interval_start
):
continue
week_days = len(
set(calendar.calendar_id.mapped("attendance_ids.dayofweek"))
)
# use month precision if calendar starts and ends on a month boundary
# use day precision otherwise
if (
max(calendar.date_start or interval_start, interval_start).day == 1
and (
min(calendar.date_end or interval_end, interval_end)
+ relativedelta(days=1)
).month
!= min(calendar.date_end or interval_end, interval_end).month
):
interval_percentage = round(
float(
min(calendar.date_end or interval_end, interval_end).month
- max(
calendar.date_start or interval_start, interval_start
).month
+ 1
)
/ (interval_end.month - interval_start.month + 1),
2,
)
else:
interval_percentage = round(
(
min(calendar.date_end or interval_end, interval_end)
- max(calendar.date_start or interval_start, interval_start)
).days
/ days,
2,
)
percentage += interval_percentage * round(float(week_days) / 5, 2)

if percentage:
allocations += self.env["hr.leave.allocation"].create(
{
"name": str(self.date_start.year),
"employee_id": employee.id,
"holiday_status_id": self.leave_type_id.id,
"date_from": self.date_start,
"date_to": self.date_end,
"number_of_days": math.ceil(percentage * self.full_vacation),
}
)
allocations.filtered(lambda x: x.state == "draft").action_confirm()
allocations.filtered(
lambda x: x.state in ("confirm", "validate1")
).action_validate()
return {
"type": "ir.actions.act_window",
"res_model": "hr.leave.allocation",
"views": [(False, "list"), (False, "form")],
"domain": [("id", "in", allocations.ids)],
"name": _("Created allocations"),
}
Loading

0 comments on commit 0f45072

Please sign in to comment.