Compare commits
1 Commits
16.0
...
dfd5f26e95
| Author | SHA1 | Date | |
|---|---|---|---|
| dfd5f26e95 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hr_employee_stats_sheet",
|
"name": "hr_employee_stats_sheet",
|
||||||
"version": "16.0.3.1.3",
|
"version": "16.0.3.0.0",
|
||||||
"description": "Add global sheet for employee stats",
|
"description": "Add global sheet for employee stats",
|
||||||
"summary": "Add global sheet for employee stats",
|
"summary": "Add global sheet for employee stats",
|
||||||
"author": "Nicolas JEUDY",
|
"author": "Nicolas JEUDY",
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"resource",
|
"resource",
|
||||||
"hr_employee_calendar_planning",
|
"hr_employee_calendar_planning",
|
||||||
"hr_timesheet_sheet_usability_misc",
|
"hr_timesheet_sheet_usability_misc",
|
||||||
|
"hr_timesheet_sheet_usability_akretion",
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
"security/ir.model.access.csv",
|
"security/ir.model.access.csv",
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class HrEmployee(models.Model):
|
|||||||
# if hr_employee_calendar found, use its calendar_id
|
# if hr_employee_calendar found, use its calendar_id
|
||||||
elif calendars and calendars[0].calendar_id:
|
elif calendars and calendars[0].calendar_id:
|
||||||
return calendars[0].calendar_id
|
return calendars[0].calendar_id
|
||||||
|
# if no hr_employee_calendar found, use employee resource_calendar_id
|
||||||
|
elif self.resource_calendar_id:
|
||||||
|
return self.resource_calendar_id
|
||||||
# if resource calendar not found, use the ressource calendar of the company linked to the employee
|
# if resource calendar not found, use the ressource calendar of the company linked to the employee
|
||||||
elif self.company_id.resource_calendar_id:
|
elif self.company_id.resource_calendar_id:
|
||||||
return self.company_id.resource_calendar_id
|
return self.company_id.resource_calendar_id
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import logging
|
import logging
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from pytz import utc
|
from pytz import utc
|
||||||
from odoo.exceptions import UserError
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HrEmployeeStats(models.Model):
|
class HrEmployeeStats(models.Model):
|
||||||
@@ -18,10 +19,11 @@ class HrEmployeeStats(models.Model):
|
|||||||
is_public_holiday = fields.Boolean("Public Holiday", compute="_compute_dayofweek")
|
is_public_holiday = fields.Boolean("Public Holiday", compute="_compute_dayofweek")
|
||||||
employee_id = fields.Many2one("hr.employee", "Employee", required=True)
|
employee_id = fields.Many2one("hr.employee", "Employee", required=True)
|
||||||
department_id = fields.Many2one("hr.department", "Department")
|
department_id = fields.Many2one("hr.department", "Department")
|
||||||
timesheet_line_ids = fields.Many2many(
|
timesheet_line_ids = fields.One2many(
|
||||||
"account.analytic.line",
|
"account.analytic.line",
|
||||||
|
"employee_id",
|
||||||
|
"Timesheet lines",
|
||||||
compute="_compute_timesheet_line_ids",
|
compute="_compute_timesheet_line_ids",
|
||||||
string="Timesheet lines",
|
|
||||||
)
|
)
|
||||||
date = fields.Date("Date", required=True)
|
date = fields.Date("Date", required=True)
|
||||||
company_id = fields.Many2one(
|
company_id = fields.Many2one(
|
||||||
@@ -113,23 +115,23 @@ class HrEmployeeStats(models.Model):
|
|||||||
recovery = self.env["hr.leave"]
|
recovery = self.env["hr.leave"]
|
||||||
total_recovery_hours = 0
|
total_recovery_hours = 0
|
||||||
if self.date and self.employee_id and self._get_holiday_status_id():
|
if self.date and self.employee_id and self._get_holiday_status_id():
|
||||||
recovery_ids = recovery.search(
|
recovery_id = recovery.search(
|
||||||
[
|
[
|
||||||
("employee_id", "=", self.employee_id.id),
|
("employee_id", "=", self.employee_id.id),
|
||||||
("holiday_status_id", "=", self._get_holiday_status_id()),
|
("holiday_status_id", "=", self._get_holiday_status_id()),
|
||||||
("request_date_from", "<=", self.date),
|
("request_date_from", "<=", self.date),
|
||||||
("request_date_to", ">=", self.date),
|
("request_date_to", ">=", self.date),
|
||||||
],
|
],
|
||||||
|
limit=1
|
||||||
)
|
)
|
||||||
if recovery_ids:
|
if recovery_id:
|
||||||
for recovery_id in recovery_ids:
|
|
||||||
if recovery_id.request_unit_hours:
|
if recovery_id.request_unit_hours:
|
||||||
recovery_hours = recovery_id.number_of_hours_display
|
recovery_hours = recovery_id.number_of_hours_display
|
||||||
total_recovery_hours += min(recovery_hours,self._get_total_planned_hours())
|
total_recovery_hours = min(recovery_hours,self._get_total_planned_hours())
|
||||||
elif recovery_id.request_unit_half:
|
elif recovery_id.request_unit_half:
|
||||||
total_recovery_hours += self._get_total_planned_hours() / 2
|
total_recovery_hours = self._get_total_planned_hours() / 2
|
||||||
else :
|
else :
|
||||||
total_recovery_hours += self._get_total_planned_hours()
|
total_recovery_hours = self._get_total_planned_hours()
|
||||||
return total_recovery_hours
|
return total_recovery_hours
|
||||||
|
|
||||||
def _get_total_leave_hours(self):
|
def _get_total_leave_hours(self):
|
||||||
@@ -137,23 +139,23 @@ class HrEmployeeStats(models.Model):
|
|||||||
leave = self.env["hr.leave"]
|
leave = self.env["hr.leave"]
|
||||||
total_leave_hours = 0
|
total_leave_hours = 0
|
||||||
if self.date and self.employee_id:
|
if self.date and self.employee_id:
|
||||||
leave_ids = leave.search(
|
leave_id = leave.search(
|
||||||
[
|
[
|
||||||
("employee_id", "=", self.employee_id.id),
|
("employee_id", "=", self.employee_id.id),
|
||||||
("holiday_status_id", "!=", self._get_holiday_status_id()),
|
("holiday_status_id", "!=", self._get_holiday_status_id()),
|
||||||
("request_date_from", "<=", self.date),
|
("request_date_from", "<=", self.date),
|
||||||
("request_date_to", ">=", self.date),
|
("request_date_to", ">=", self.date),
|
||||||
],
|
],
|
||||||
|
limit=1
|
||||||
)
|
)
|
||||||
if leave_ids:
|
if leave_id:
|
||||||
for leave_id in leave_ids:
|
|
||||||
if leave_id.request_unit_hours:
|
if leave_id.request_unit_hours:
|
||||||
leave_hours = leave_id.number_of_hours_display
|
leave_hours = leave_id.number_of_hours_display
|
||||||
total_leave_hours += min(leave_hours,self._get_total_planned_hours())
|
total_leave_hours = min(leave_hours,self._get_total_planned_hours())
|
||||||
elif leave_id.request_unit_half:
|
elif leave_id.request_unit_half:
|
||||||
total_leave_hours += self._get_total_planned_hours() / 2
|
total_leave_hours = self._get_total_planned_hours() / 2
|
||||||
else :
|
else :
|
||||||
total_leave_hours += self._get_total_planned_hours()
|
total_leave_hours = self._get_total_planned_hours()
|
||||||
return total_leave_hours
|
return total_leave_hours
|
||||||
|
|
||||||
@api.depends("employee_id", "date")
|
@api.depends("employee_id", "date")
|
||||||
@@ -191,10 +193,6 @@ class HrEmployeeStats(models.Model):
|
|||||||
)
|
)
|
||||||
if not public_holidays:
|
if not public_holidays:
|
||||||
return False
|
return False
|
||||||
if len(public_holidays) > 1:
|
|
||||||
raise UserError(
|
|
||||||
_("Several holidays have been found ont he date '%s'. Please correct the anomaly before continuing.") % self.date
|
|
||||||
)
|
|
||||||
ph = public_holidays[0]
|
ph = public_holidays[0]
|
||||||
# Convert public holiday to the employee timezone
|
# Convert public holiday to the employee timezone
|
||||||
ph_datetime_from_tz = self._convert_to_employee_tz(ph.date_from)
|
ph_datetime_from_tz = self._convert_to_employee_tz(ph.date_from)
|
||||||
|
|||||||
@@ -100,6 +100,56 @@ class HrTimesheetSheet(models.Model):
|
|||||||
sheet.recovery_allocation_ids.write({"state": "refuse"})
|
sheet.recovery_allocation_ids.write({"state": "refuse"})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# def _get_calendar_in_progress_during_timesheet_time_period(self):
|
||||||
|
# """
|
||||||
|
# get the ressource calendar which was used during the timesheet sheet time period
|
||||||
|
# """
|
||||||
|
# #find calendar(s) running over the duration of the timesheet
|
||||||
|
# calendars = self.env["hr.employee.calendar"].search(
|
||||||
|
# [
|
||||||
|
# ("employee_id", "=", self.employee_id.id),
|
||||||
|
|
||||||
|
# ("date_start", "<=", self.date_end),
|
||||||
|
# "|",
|
||||||
|
# ("date_end", "=", False), # pas de date de fin OU
|
||||||
|
# ("date_end", ">=", self.date_start), # date de fin après le début
|
||||||
|
# ],
|
||||||
|
# )
|
||||||
|
# if len(calendars) > 1:
|
||||||
|
# raise UserError(
|
||||||
|
# _("There is a calendar starting during the timesheet sheet time period for the employee %s"
|
||||||
|
# "Please create a new timesheet sheet starting from the new calendar start date")
|
||||||
|
# % self.employee_id.display_name
|
||||||
|
# )
|
||||||
|
# # get the ressource calendar id according to the work contract
|
||||||
|
# elif calendars and calendars[0].calendar_id:
|
||||||
|
# return calendars[0].calendar_id
|
||||||
|
# #get the ressource calendar linked to the employee
|
||||||
|
# elif self.env.company.resource_calendar_id:
|
||||||
|
# return self.env.company.resource_calendar_id
|
||||||
|
|
||||||
|
def _get_working_hours_per_week(self):
|
||||||
|
"""
|
||||||
|
Get the weekly working hours for the employee, which is defined by:
|
||||||
|
- the employee's work contract,
|
||||||
|
- or their resource calendar,
|
||||||
|
- or the company's resource calendar,
|
||||||
|
- or the default value of 40 hours per week.
|
||||||
|
:return: limit recovery hours
|
||||||
|
"""
|
||||||
|
# get ressource calendar id used during the timesheet sheet time period
|
||||||
|
ressource_calendar_id = self.employee_id._get_calendar_in_progress_during_a_time_period(self.date_start,self.date_end)
|
||||||
|
if ressource_calendar_id:
|
||||||
|
resource_calendar_attendance_ids = self.env[
|
||||||
|
"resource.calendar.attendance"
|
||||||
|
].search([("calendar_id", "=", ressource_calendar_id.id)])
|
||||||
|
# calculate working hours per week according to the employee's resource calendar
|
||||||
|
weekly_working_hours = 0
|
||||||
|
for day in resource_calendar_attendance_ids:
|
||||||
|
weekly_working_hours += day.hour_to - day.hour_from
|
||||||
|
return weekly_working_hours
|
||||||
|
return HOURS_PER_DAY * 5
|
||||||
|
|
||||||
def _get_working_hours_per_day(self):
|
def _get_working_hours_per_day(self):
|
||||||
"""
|
"""
|
||||||
Get the hours per day for the employee according to:
|
Get the hours per day for the employee according to:
|
||||||
@@ -115,6 +165,12 @@ class HrTimesheetSheet(models.Model):
|
|||||||
return ressource_calendar_id.hours_per_day
|
return ressource_calendar_id.hours_per_day
|
||||||
return HOURS_PER_DAY
|
return HOURS_PER_DAY
|
||||||
|
|
||||||
|
def _get_max_allowed_recovery_hours(self):
|
||||||
|
"""
|
||||||
|
Get the maximum number of hours beyond which new recovery allowances cannot be created
|
||||||
|
"""
|
||||||
|
return self._get_working_hours_per_week()
|
||||||
|
|
||||||
def action_generate_recovery_allocation(self):
|
def action_generate_recovery_allocation(self):
|
||||||
# check if the user has the right to review the timesheet sheet
|
# check if the user has the right to review the timesheet sheet
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -147,6 +203,25 @@ class HrTimesheetSheet(models.Model):
|
|||||||
# get recovery hours from total gap hours of the timesheet sheet
|
# get recovery hours from total gap hours of the timesheet sheet
|
||||||
recovery_hours = self._get_timesheet_sheet_recovery_hours()
|
recovery_hours = self._get_timesheet_sheet_recovery_hours()
|
||||||
|
|
||||||
|
# get recovery hours cap
|
||||||
|
max_allowed_recovery_hours = self._get_max_allowed_recovery_hours()
|
||||||
|
if max_allowed_recovery_hours:
|
||||||
|
# find recovery remaining leaves for the employee
|
||||||
|
recovery_type_id = self.env.company.recovery_type_id
|
||||||
|
# get virtual remaining leaves for the employee and the recovery leaves type
|
||||||
|
data_days = recovery_type_id.get_employees_days([employee_id.id])[employee_id.id]
|
||||||
|
total_recovery_type_leaves = data_days.get(recovery_type_id.id,{})
|
||||||
|
total_virtual_remaining_recovery_type_leaves = total_recovery_type_leaves.get('virtual_remaining_leaves', 0)
|
||||||
|
# add the recovery hours to the total remaining leaves recovery type, and check if the limit of recovery hours is exceeded
|
||||||
|
exceeded_hours = total_virtual_remaining_recovery_type_leaves + recovery_hours - max_allowed_recovery_hours
|
||||||
|
# if limit recovery hours is exceeded, don't create a new allocation
|
||||||
|
if exceeded_hours > 0:
|
||||||
|
raise UserError(
|
||||||
|
_(
|
||||||
|
"The number of recovery hours exceeds the authorized limit (%s h) by %s hours"
|
||||||
|
)
|
||||||
|
% (max_allowed_recovery_hours, exceeded_hours)
|
||||||
|
)
|
||||||
# convert recovery hours into days
|
# convert recovery hours into days
|
||||||
recovery_days = recovery_hours / self._get_working_hours_per_day()
|
recovery_days = recovery_hours / self._get_working_hours_per_day()
|
||||||
|
|
||||||
@@ -159,7 +234,6 @@ class HrTimesheetSheet(models.Model):
|
|||||||
"number_of_days": recovery_days,
|
"number_of_days": recovery_days,
|
||||||
"timesheet_sheet_id": self.id,
|
"timesheet_sheet_id": self.id,
|
||||||
"allocation_type": 'accrual',
|
"allocation_type": 'accrual',
|
||||||
"date_from": self.date_start,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -79,12 +79,6 @@ class TestHrEmployeeStatsRecovery(TransactionCase):
|
|||||||
timesheet_sheet.action_generate_recovery_allocation()
|
timesheet_sheet.action_generate_recovery_allocation()
|
||||||
|
|
||||||
def test_no_recovery_hours(self):
|
def test_no_recovery_hours(self):
|
||||||
self.env['hr.employee.calendar'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2025-01-01"),
|
|
||||||
'date_end': None,
|
|
||||||
'calendar_id': self.base_calendar.id,
|
|
||||||
})
|
|
||||||
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # monday of last week
|
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # monday of last week
|
||||||
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
||||||
for stat in self._create_stats(start_date, 5, 7): #créer 5 stats de 7h chacune
|
for stat in self._create_stats(start_date, 5, 7): #créer 5 stats de 7h chacune
|
||||||
@@ -99,12 +93,6 @@ class TestHrEmployeeStatsRecovery(TransactionCase):
|
|||||||
self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 0, "timesheet_sheet_recovery_hours should be 0",)
|
self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 0, "timesheet_sheet_recovery_hours should be 0",)
|
||||||
|
|
||||||
def test_positive_recovery_hours(self):
|
def test_positive_recovery_hours(self):
|
||||||
self.env['hr.employee.calendar'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2025-01-01"),
|
|
||||||
'date_end': None,
|
|
||||||
'calendar_id': self.base_calendar.id,
|
|
||||||
})
|
|
||||||
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # lundi de la semaine dernière
|
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # lundi de la semaine dernière
|
||||||
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
||||||
for stat in self._create_stats(start_date, 5, 8): #créer 5 stats de 8h chacune
|
for stat in self._create_stats(start_date, 5, 8): #créer 5 stats de 8h chacune
|
||||||
@@ -124,12 +112,6 @@ class TestHrEmployeeStatsRecovery(TransactionCase):
|
|||||||
self.assertEqual(recovery_allocation.number_of_days,0.8928571428571429, "The recovery allocation should be for 6.25h/7h = 0.8928571428571429 day")
|
self.assertEqual(recovery_allocation.number_of_days,0.8928571428571429, "The recovery allocation should be for 6.25h/7h = 0.8928571428571429 day")
|
||||||
|
|
||||||
def test_negative_recovery_hours(self):
|
def test_negative_recovery_hours(self):
|
||||||
self.env['hr.employee.calendar'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2025-01-01"),
|
|
||||||
'date_end': None,
|
|
||||||
'calendar_id': self.base_calendar.id,
|
|
||||||
})
|
|
||||||
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # lundi de la semaine dernière
|
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # lundi de la semaine dernière
|
||||||
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
||||||
for stat in self._create_stats(start_date, 5, 6): #créer 5 stats de 6h chacune
|
for stat in self._create_stats(start_date, 5, 6): #créer 5 stats de 6h chacune
|
||||||
@@ -162,12 +144,8 @@ class TestHrEmployeeStatsRecovery(TransactionCase):
|
|||||||
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
self.env['hr.employee.calendar'].create({
|
self.employee.resource_calendar_id = part_time_calendar.id
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2025-01-01"),
|
|
||||||
'date_end': None,
|
|
||||||
'calendar_id': part_time_calendar.id,
|
|
||||||
})
|
|
||||||
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # lundi de la semaine dernière
|
start_date = date.today() - timedelta(days=date.today().weekday() + 7) # lundi de la semaine dernière
|
||||||
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
timesheet_sheet = self._create_timesheet_sheet(start_date)
|
||||||
for stat in self._create_stats(start_date, 4, 8): #créer 4 stats de 8h chacune
|
for stat in self._create_stats(start_date, 4, 8): #créer 4 stats de 8h chacune
|
||||||
@@ -277,89 +255,8 @@ class TestHrEmployeeStatsRecovery(TransactionCase):
|
|||||||
self.assertEqual(timesheet_sheet.timesheet_sheet_gap_hours, 7, "timesheet_sheet_gap_hours should be 7",)
|
self.assertEqual(timesheet_sheet.timesheet_sheet_gap_hours, 7, "timesheet_sheet_gap_hours should be 7",)
|
||||||
self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 8.75, "timesheet_sheet_recovery_hours should be 8,75",)
|
self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 8.75, "timesheet_sheet_recovery_hours should be 8,75",)
|
||||||
|
|
||||||
def test_recovery_allocation_doesnt_change_with_of_calendar(self):
|
|
||||||
employee_full_time_calendar = self.base_calendar # full time calendar (from monday to friday)
|
|
||||||
employee_full_time_calendar.hours_per_day = 7
|
|
||||||
employee_part_time_calendar = self.env['resource.calendar'].create({
|
|
||||||
'name': 'Part Time Calendar',
|
|
||||||
'attendance_ids': [
|
|
||||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
|
||||||
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 19, 'day_period': 'afternoon'}),
|
|
||||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
|
||||||
(0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 19, 'day_period': 'afternoon'}),
|
|
||||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
|
||||||
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 19, 'day_period': 'afternoon'}),
|
|
||||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
|
||||||
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 19, 'day_period': 'afternoon'}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
employee_part_time_calendar.hours_per_day = 9
|
|
||||||
|
|
||||||
#create two hr.employee.calendar to change calendar during the timesheet period
|
|
||||||
self.env['hr.employee.calendar'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2023-07-01"),
|
|
||||||
'date_end': Date.to_date("2025-07-31"),
|
|
||||||
'calendar_id': employee_full_time_calendar.id,
|
|
||||||
})
|
|
||||||
self.env['hr.employee.calendar'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2025-08-01"),
|
|
||||||
'date_end': None,
|
|
||||||
'calendar_id': employee_part_time_calendar.id,
|
|
||||||
})
|
|
||||||
self.employee.resource_calendar_id = employee_part_time_calendar.id
|
|
||||||
|
|
||||||
#create stats during period of full time calendar for the employee
|
|
||||||
timesheet_sheet_1 = self.env['hr_timesheet.sheet'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': "2025-07-07",
|
|
||||||
'date_end': "2025-07-13",
|
|
||||||
})
|
|
||||||
stats = self._create_stats(Date.to_date("2025-07-07"), 5, 8)
|
|
||||||
for stat in stats:
|
|
||||||
stat._compute_dayofweek()
|
|
||||||
stat._compute_hours()
|
|
||||||
print("stat :", stat.date, stat.total_hours, stat.total_planned_hours, stat.gap_hours)
|
|
||||||
timesheet_sheet_1.action_generate_recovery_allocation()
|
|
||||||
self.assertEqual(timesheet_sheet_1.timesheet_sheet_gap_hours, 5, "timesheet_sheet_gap_hours should be 0",)
|
|
||||||
self.assertEqual(timesheet_sheet_1.timesheet_sheet_recovery_hours, 6.25, "timesheet_sheet_recovery_hours should be 6,25",)
|
|
||||||
allocation_1 = self.env["hr.leave.allocation"].search([("timesheet_sheet_id","=",timesheet_sheet_1.id)])
|
|
||||||
self.assertEqual(len(allocation_1), 1, "There should be one recovery")
|
|
||||||
self.assertEqual(allocation_1.number_of_days,0.8928571428571429, "The recovery allocation should be for 0.8928571428571429 day")
|
|
||||||
|
|
||||||
#create stats during period of part time calendar for the employee
|
|
||||||
# generation 4 stats of 10h each, the employee is supposed to work 9h per day during 4 days
|
|
||||||
stats_2 = self._create_stats(Date.to_date("2025-09-08"), 4, 10)
|
|
||||||
for stat in stats_2:
|
|
||||||
stat._compute_dayofweek()
|
|
||||||
stat._compute_hours()
|
|
||||||
timesheet_sheet_2 = self.env['hr_timesheet.sheet'].create({
|
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': "2025-09-08",
|
|
||||||
'date_end': "2025-09-14",
|
|
||||||
})
|
|
||||||
timesheet_sheet_2.action_generate_recovery_allocation()
|
|
||||||
self.assertEqual(timesheet_sheet_2.timesheet_sheet_gap_hours, 4, "timesheet_sheet_gap_hours should be 4",)
|
|
||||||
self.assertEqual(timesheet_sheet_2.timesheet_sheet_recovery_hours, 5, "timesheet_sheet_recovery_hours should be 5",)
|
|
||||||
allocation_2 = self.env["hr.leave.allocation"].search([("timesheet_sheet_id","=",timesheet_sheet_2.id)])
|
|
||||||
self.assertEqual(len(allocation_2), 1, "There should be one recovery")
|
|
||||||
self.assertEqual(allocation_2.number_of_days,0.5555555555555556, "The recovery allocation should be for 0,555555556 (5/9) day")
|
|
||||||
#check that allocation_1 hasn't changed
|
|
||||||
self.assertEqual(allocation_1.number_of_days,0.8928571428571429, "The recovery allocation should be for 0,892857143 day")
|
|
||||||
|
|
||||||
def test_public_holiday(self):
|
def test_public_holiday(self):
|
||||||
self.env['hr.employee.calendar'].create({
|
# create a public holiday
|
||||||
'employee_id': self.employee.id,
|
|
||||||
'date_start': Date.to_date("2025-01-01"),
|
|
||||||
'date_end': None,
|
|
||||||
'calendar_id': self.base_calendar.id,
|
|
||||||
})
|
|
||||||
# create a public holiday :
|
|
||||||
# When you create holidays graphically with a TZ,
|
|
||||||
# they are saved in the database after conversion to UTC.
|
|
||||||
# This is why, for a holiday starting on May 1, 2025, at 00:00:00 UTC+2,
|
|
||||||
# it will be saved in the database as April 30, 2025, at 22:00:00.
|
|
||||||
self.env["resource.calendar.leaves"].create(
|
self.env["resource.calendar.leaves"].create(
|
||||||
{
|
{
|
||||||
"name": "1 mai 2025",
|
"name": "1 mai 2025",
|
||||||
@@ -378,8 +275,6 @@ class TestHrEmployeeStatsRecovery(TransactionCase):
|
|||||||
'date_start': "2025-04-28",
|
'date_start': "2025-04-28",
|
||||||
'date_end': "2025-05-04",
|
'date_end': "2025-05-04",
|
||||||
})
|
})
|
||||||
# the employee has worked 7h on first may (public holiday) instead of 0h
|
|
||||||
# so the gap hours should be 7h and recovery hours 8,75h with coef 25%
|
|
||||||
self.assertEqual(timesheet_sheet.timesheet_sheet_gap_hours, 7, "timesheet_sheet_gap_hours should be 7",)
|
self.assertEqual(timesheet_sheet.timesheet_sheet_gap_hours, 7, "timesheet_sheet_gap_hours should be 7",)
|
||||||
self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 8.75, "timesheet_sheet_recovery_hours should be 8,75",)
|
self.assertEqual(timesheet_sheet.timesheet_sheet_recovery_hours, 8.75, "timesheet_sheet_recovery_hours should be 8,75",)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user