From 6135fa68eedbc1ebda0521a490a57fc6e8cd4f17 Mon Sep 17 00:00:00 2001 From: Quentin Mondot Date: Thu, 9 Oct 2025 11:20:12 +0200 Subject: [PATCH] [IMP] hr_holidays_timeoff_analysis : apply changes proposed by pre-commit --- .pre-commit-config.yaml | 7 +- hr_holidays_timeoff_analysis/.gitignore | 2 +- hr_holidays_timeoff_analysis/__init__.py | 3 - hr_holidays_timeoff_analysis/__manifest__.py | 8 +- .../data/hr_leave_timeoff_day_cron.xml | 26 +- .../models/__init__.py | 2 +- .../models/hr_leave_timeoff_day.py | 126 +++++---- .../tests/__init__.py | 2 - .../tests/test_hr_leave_timeoff_day.py | 255 +++++++++++++++--- .../views/hr_leave_timeoff_day_views.xml | 33 ++- 10 files changed, 337 insertions(+), 127 deletions(-) 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 +