diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3387143..9abafc6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -49,12 +49,9 @@ repos:
$(git rev-parse --show-toplevel))"'
- id: oca-gen-addon-readme
entry:
- bash -c 'oca-gen-addon-readme
- --addons-dir=.
- --branch=$(git symbolic-ref
+ bash -c 'oca-gen-addon-readme --addons-dir=. --branch=$(git symbolic-ref
refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@")
- --repo-name=$(basename $(git rev-parse --show-toplevel))
- --org-name="Elabore"
+ --repo-name=$(basename $(git rev-parse --show-toplevel)) --org-name="Elabore"
--if-source-changed --keep-source-digest'
- repo: https://github.com/OCA/odoo-pre-commit-hooks
diff --git a/hr_holidays_timeoff_analysis/.gitignore b/hr_holidays_timeoff_analysis/.gitignore
index df8a896..6da5887 100644
--- a/hr_holidays_timeoff_analysis/.gitignore
+++ b/hr_holidays_timeoff_analysis/.gitignore
@@ -1,2 +1,2 @@
*.*~
-*pyc
\ No newline at end of file
+*pyc
diff --git a/hr_holidays_timeoff_analysis/__init__.py b/hr_holidays_timeoff_analysis/__init__.py
index f707f93..0650744 100644
--- a/hr_holidays_timeoff_analysis/__init__.py
+++ b/hr_holidays_timeoff_analysis/__init__.py
@@ -1,4 +1 @@
from . import models
-from . import tests
-
-
diff --git a/hr_holidays_timeoff_analysis/__manifest__.py b/hr_holidays_timeoff_analysis/__manifest__.py
index 63aaeaf..cfc7d3b 100644
--- a/hr_holidays_timeoff_analysis/__manifest__.py
+++ b/hr_holidays_timeoff_analysis/__manifest__.py
@@ -5,14 +5,16 @@
"name": "hr_holidays_timeoff_analysis",
"version": "18.0.1.0.0",
"author": "Elabore",
- "website": "https://elabore.coop",
+ "website": "https://git.elabore.coop/elabore/hr-tools",
"maintainer": "Elabore",
"license": "AGPL-3",
"category": "HR",
- "summary": "indicate day by day if a date is timeoff or not and generate an analyses pivot by employee",
+ "summary": "indicate day by day if a date is timeoff or not and "
+ "generate an analyses pivot by employee",
# any module necessary for this one to work correctly
"depends": [
- "base","hr_holidays",
+ "base",
+ "hr_holidays",
],
"qweb": [],
"external_dependencies": {
diff --git a/hr_holidays_timeoff_analysis/data/hr_leave_timeoff_day_cron.xml b/hr_holidays_timeoff_analysis/data/hr_leave_timeoff_day_cron.xml
index c966027..151e71f 100644
--- a/hr_holidays_timeoff_analysis/data/hr_leave_timeoff_day_cron.xml
+++ b/hr_holidays_timeoff_analysis/data/hr_leave_timeoff_day_cron.xml
@@ -1,14 +1,12 @@
-
-
-
- Create and update Timeoff Days
-
- code
- model.cron_manage_timeoff_days()
- 1
- days
- -1
- True
-
-
-
\ No newline at end of file
+
+
+ Create and update Timeoff Days
+
+ code
+ model.cron_manage_timeoff_days()
+ 1
+ days
+ -1
+ True
+
+
diff --git a/hr_holidays_timeoff_analysis/models/__init__.py b/hr_holidays_timeoff_analysis/models/__init__.py
index 6af958c..a9b879f 100644
--- a/hr_holidays_timeoff_analysis/models/__init__.py
+++ b/hr_holidays_timeoff_analysis/models/__init__.py
@@ -1 +1 @@
-from . import hr_leave_timeoff_day
\ No newline at end of file
+from . import hr_leave_timeoff_day
diff --git a/hr_holidays_timeoff_analysis/models/hr_leave_timeoff_day.py b/hr_holidays_timeoff_analysis/models/hr_leave_timeoff_day.py
index 943537a..0bd65f8 100644
--- a/hr_holidays_timeoff_analysis/models/hr_leave_timeoff_day.py
+++ b/hr_holidays_timeoff_analysis/models/hr_leave_timeoff_day.py
@@ -1,20 +1,25 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import fields, models, api
from datetime import timedelta
+from odoo import api, fields, models
+
+
class TimeOffDay(models.Model):
- _name = 'hr.leave.timeoff.day'
- _description = 'Timeoff Day'
- _order = 'date desc'
+ _name = "hr.leave.timeoff.day"
+ _description = "Timeoff Day"
+ _order = "date desc"
date = fields.Date()
- employee_id = fields.Many2one('hr.employee')
- hr_leave_id = fields.Many2one('hr.leave')
- hr_leave_type = fields.Many2one(related='hr_leave_id.holiday_status_id', store=True)
+ employee_id = fields.Many2one("hr.employee")
+ hr_leave_id = fields.Many2one("hr.leave")
+ hr_leave_type = fields.Many2one(related="hr_leave_id.holiday_status_id", store=True)
leave_duration_by_day = fields.Float()
def employee_is_scheduled_to_work_this_day(self, date, employee):
- """ Check if the employee is scheduled to work on this day according to his calendar """
+ """
+ Check if the employee is scheduled to work on this day according to his
+ calendar.
+ """
calendar = employee.resource_calendar_id
if not calendar or not calendar.attendance_ids:
return False
@@ -23,21 +28,28 @@ class TimeOffDay(models.Model):
lambda att: att.dayofweek == day_of_week
)
return bool(attendances)
-
+
def is_a_public_holiday(self, date, employee):
- """ Check if the day is a public holiday """
- # public holidays start the day before in database (ex: date_from : 7mai 22:00 and date_to : 8mai 23:59 for 8mai public holiday) )
- public_holidays = self.env['resource.calendar.leaves'].search([
- ('date_from', '<=', date-timedelta(days=1)),
- ('date_to', '>=', date),
- ('resource_id', '=', False) # resource_id is null for public holiday
- ])
+ """
+ Check if the day is a public holiday.
+ """
+ # public holidays start the day before in database
+ # (ex: date_from : 7mai 22:00 and date_to : 8mai 23:59 for 8mai public holiday))
+ public_holidays = self.env["resource.calendar.leaves"].search(
+ [
+ ("date_from", "<=", date - timedelta(days=1)),
+ ("date_to", ">=", date),
+ ("resource_id", "=", False), # resource_id is null for public holiday
+ ]
+ )
if public_holidays:
return True
return False
def compute_leave_duration_by_day(self, leave):
- """ Compute the leave duration by day based on the leave type """
+ """
+ Compute the leave duration by day based on the leave type.
+ """
leave_duration_by_day = 0.0
# Full day case
if leave.request_unit_half:
@@ -55,46 +67,64 @@ class TimeOffDay(models.Model):
def cron_create_timeoff_days(self):
# Browse all validated leaves
- leaves = self.env['hr.leave'].search([
- ('state', '=', 'validate'),
- ('request_date_from', '!=', False),
- ('request_date_to', '!=', False),
- ('employee_id', '!=', False),
- ])
+ leaves = self.env["hr.leave"].search(
+ [
+ ("state", "=", "validate"),
+ ("request_date_from", "!=", False),
+ ("request_date_to", "!=", False),
+ ("employee_id", "!=", False),
+ ]
+ )
for leave in leaves:
current_date = leave.request_date_from
employee = leave.employee_id
while current_date <= leave.request_date_to:
- if self.employee_is_scheduled_to_work_this_day(current_date, employee) and not self.is_a_public_holiday(current_date, employee):
- # The employee is scheluded to work this day according his calendar and it's not a public holiday,
+ if self.employee_is_scheduled_to_work_this_day(
+ current_date, employee
+ ) and not self.is_a_public_holiday(current_date, employee):
+ # The employee is scheluded to work this day according his calendar
+ # and it's not a public holiday,
# so create a timeoff day record if it does not already exist
- if not self.search([
- ('date', '=', current_date),
- ('employee_id', '=', employee.id),
- ('hr_leave_id', '=', leave.id),
- ], limit=1):
- self.create({
- 'date': current_date,
- 'employee_id': employee.id,
- 'hr_leave_id': leave.id,
- 'leave_duration_by_day': self.compute_leave_duration_by_day(leave),
- })
+ if not self.search(
+ [
+ ("date", "=", current_date),
+ ("employee_id", "=", employee.id),
+ ("hr_leave_id", "=", leave.id),
+ ],
+ limit=1,
+ ):
+ self.create(
+ {
+ "date": current_date,
+ "employee_id": employee.id,
+ "hr_leave_id": leave.id,
+ "leave_duration_by_day": self.compute_leave_duration_by_day( # noqa: E501
+ leave
+ ),
+ }
+ )
current_date += timedelta(days=1)
def cron_delete_timeoff_days(self):
# Browse all unvalidated leaves
- leaves = self.env['hr.leave'].search([
- ('state', '!=', 'validate'),
- ('request_date_from', '!=', False),
- ('request_date_to', '!=', False),
- ('employee_id', '!=', False),
- ])
+ leaves = self.env["hr.leave"].search(
+ [
+ ("state", "!=", "validate"),
+ ("request_date_from", "!=", False),
+ ("request_date_to", "!=", False),
+ ("employee_id", "!=", False),
+ ]
+ )
# Delete timeoff days for leaves that are no longer validated
for leave in leaves:
- self.search([
- ('hr_leave_id', '=', leave.id),
- ]).unlink()
+ self.search(
+ [
+ ("hr_leave_id", "=", leave.id),
+ ]
+ ).unlink()
# Delete timeoff days that are not linked to any leave
- self.search([
- ('hr_leave_id', '=', False),
- ]).unlink()
+ self.search(
+ [
+ ("hr_leave_id", "=", False),
+ ]
+ ).unlink()
diff --git a/hr_holidays_timeoff_analysis/tests/__init__.py b/hr_holidays_timeoff_analysis/tests/__init__.py
index 79046e1..56a62c1 100644
--- a/hr_holidays_timeoff_analysis/tests/__init__.py
+++ b/hr_holidays_timeoff_analysis/tests/__init__.py
@@ -1,3 +1 @@
-# -*- coding: utf-8 -*-
-
from . import test_hr_leave_timeoff_day
diff --git a/hr_holidays_timeoff_analysis/tests/test_hr_leave_timeoff_day.py b/hr_holidays_timeoff_analysis/tests/test_hr_leave_timeoff_day.py
index c004ac8..d679e56 100644
--- a/hr_holidays_timeoff_analysis/tests/test_hr_leave_timeoff_day.py
+++ b/hr_holidays_timeoff_analysis/tests/test_hr_leave_timeoff_day.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from odoo.fields import Date
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
@@ -96,21 +95,123 @@ class TestHrLeaveTimeoffDay(TransactionCase):
)
def test_leave_duration_by_day_hour_compare_to_employee_calendar(self):
- employee_calendar = self.env['resource.calendar'].create({
- 'name': 'Employee Calendar',
- 'attendance_ids': [
- (0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (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 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'}),
- (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 Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- (0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- ],
- })
+ employee_calendar = self.env["resource.calendar"].create(
+ {
+ "name": "Employee Calendar",
+ "attendance_ids": [
+ (
+ 0,
+ 0,
+ {
+ "name": "Monday Morning",
+ "dayofweek": "0",
+ "hour_from": 8,
+ "hour_to": 12,
+ "day_period": "morning",
+ },
+ ),
+ (
+ 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 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",
+ },
+ ),
+ (
+ 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 Afternoon",
+ "dayofweek": "3",
+ "hour_from": 13,
+ "hour_to": 17,
+ "day_period": "afternoon",
+ },
+ ),
+ (
+ 0,
+ 0,
+ {
+ "name": "Friday Morning",
+ "dayofweek": "4",
+ "hour_from": 8,
+ "hour_to": 12,
+ "day_period": "morning",
+ },
+ ),
+ (
+ 0,
+ 0,
+ {
+ "name": "Friday Afternoon",
+ "dayofweek": "4",
+ "hour_from": 13,
+ "hour_to": 17,
+ "day_period": "afternoon",
+ },
+ ),
+ ],
+ }
+ )
self.employee.resource_calendar_id = employee_calendar
leave = self.env["hr.leave"].create(
@@ -119,7 +220,7 @@ class TestHrLeaveTimeoffDay(TransactionCase):
"request_date_from": Date.to_date("2025-07-03"),
"request_date_to": Date.to_date("2025-07-03"),
"request_unit_hours": True,
- "request_hour_from":"8",
+ "request_hour_from": "8",
"request_hour_to": "12",
"holiday_status_id": self.time_off_hour_type.id,
}
@@ -129,8 +230,6 @@ class TestHrLeaveTimeoffDay(TransactionCase):
leave._compute_number_of_days_display()
leave.state = "validate" # Simulate the leave being validated
-
-
self.env["hr.leave.timeoff.day"].cron_manage_timeoff_days()
timeoff_days = self.env["hr.leave.timeoff.day"].search(
[
@@ -150,19 +249,101 @@ class TestHrLeaveTimeoffDay(TransactionCase):
)
def test_leave_duration_by_day_compare_to_time_part_employee_calendar(self):
- employee_calendar = self.env['resource.calendar'].create({
- 'name': 'Employee Calendar',
- 'attendance_ids': [
- (0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (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 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'}),
- (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 Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- ],
- })
+ employee_calendar = self.env["resource.calendar"].create(
+ {
+ "name": "Employee Calendar",
+ "attendance_ids": [
+ (
+ 0,
+ 0,
+ {
+ "name": "Monday Morning",
+ "dayofweek": "0",
+ "hour_from": 8,
+ "hour_to": 12,
+ "day_period": "morning",
+ },
+ ),
+ (
+ 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 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",
+ },
+ ),
+ (
+ 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 Afternoon",
+ "dayofweek": "3",
+ "hour_from": 13,
+ "hour_to": 17,
+ "day_period": "afternoon",
+ },
+ ),
+ ],
+ }
+ )
self.employee.resource_calendar_id = employee_calendar
leave = self.env["hr.leave"].create(
@@ -194,7 +375,8 @@ class TestHrLeaveTimeoffDay(TransactionCase):
)
def test_public_holidays_between_a_leave(self):
- # Leaves the code below commented because default database already has a public holiday on 8th May 2025
+ # Leaves the code below commented because
+ # the default database already has a public holiday on 8th May 2025
# self.env["resource.calendar.leaves"].create(
# {
# "name": "8 mai 2025",
@@ -206,7 +388,9 @@ class TestHrLeaveTimeoffDay(TransactionCase):
{
"employee_id": self.employee.id,
"request_date_from": Date.to_date("2025-05-05"),
- "request_date_to": Date.to_date("2025-05-11"), #a public holiday is in between (8 may)
+ "request_date_to": Date.to_date(
+ "2025-05-11"
+ ), # a public holiday is in between (8 may)
"holiday_status_id": self.time_off_type.id,
}
)
@@ -267,7 +451,7 @@ class TestHrLeaveTimeoffDay(TransactionCase):
self.assertEqual(
len(timeoff_days), 0, "There should be no timeoff days for this leave"
)
-
+
def test_deleted_leave(self):
leave = self.env["hr.leave"].create(
{
@@ -305,4 +489,3 @@ class TestHrLeaveTimeoffDay(TransactionCase):
self.assertEqual(
len(timeoff_days), 0, "There should be no timeoff days for this leave"
)
-
\ No newline at end of file
diff --git a/hr_holidays_timeoff_analysis/views/hr_leave_timeoff_day_views.xml b/hr_holidays_timeoff_analysis/views/hr_leave_timeoff_day_views.xml
index a1e129b..d092ca4 100644
--- a/hr_holidays_timeoff_analysis/views/hr_leave_timeoff_day_views.xml
+++ b/hr_holidays_timeoff_analysis/views/hr_leave_timeoff_day_views.xml
@@ -1,14 +1,14 @@
-
+
hr.leave.timeoff.day
-
-
-
-
+
+
+
+
@@ -17,9 +17,9 @@
hr.leave.timeoff.day
-
-
-
+
+
+
@@ -30,10 +30,14 @@
-
+
-
+
@@ -42,7 +46,7 @@
Timeoff Days
hr.leave.timeoff.day
tree,form,pivot
-
+
{'search_default_filter_date': True}
@@ -52,6 +56,7 @@
parent="hr_holidays.menu_hr_holidays_configuration"
action="hr_leave_timeoff_day_action"
groups="hr_holidays.group_hr_holidays_manager"
- sequence="6"/>
+ sequence="6"
+ />
-
\ No newline at end of file
+