216 lines
10 KiB
Python
216 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from freezegun import freeze_time
|
|
|
|
from odoo import fields, SUPERUSER_ID
|
|
from odoo.exceptions import UserError
|
|
from datetime import date, datetime
|
|
|
|
from odoo.addons.mail.tests.common import MailCase, mail_new_test_user
|
|
|
|
from odoo.addons.hr_timesheet.tests.test_timesheet import TestCommonTimesheet
|
|
|
|
|
|
class TestTimesheetGridHolidays(TestCommonTimesheet, MailCase):
|
|
|
|
@freeze_time('2018-2-6')
|
|
def test_timer_methods_handle_project_access_restrictions(self):
|
|
"""Ensure timer visibility computation does not raise AccessError for restricted users.
|
|
1. Create a private project with a task and a linked timesheet.
|
|
2. Use a Timesheet Manager user without project access.
|
|
3. Call _should_not_display_timer() and action_timer_start() as that user.
|
|
4. Verify that no AccessError is raised due to the use of sudo() within these methods.
|
|
"""
|
|
self.timesheet.date = fields.Date.today()
|
|
self.timesheet_manager_no_project_user.action_create_employee()
|
|
employee = self.timesheet_manager_no_project_user.employee_id
|
|
timesheet = self.timesheet.with_user(self.timesheet_manager_no_project_user)
|
|
timesheet.employee_id = employee
|
|
self.assertFalse(
|
|
timesheet._should_not_display_timer(),
|
|
"_should_not_display_timer() should be True"
|
|
)
|
|
timesheet.action_timer_start()
|
|
self.assertTrue(
|
|
timesheet.is_timer_running,
|
|
"Timer should be running after action_timer_start()"
|
|
)
|
|
|
|
def test_overtime_calcution_timesheet_holiday_flow(self):
|
|
""" Employee's leave is not calculated as overtime hours when employee is on time off."""
|
|
self.empl_employee.write({
|
|
'create_date': date(2021, 1, 1),
|
|
'employee_type': 'freelance', # Avoid searching the contract if hr_contract module is installed before this module.
|
|
})
|
|
start_date = '2021-10-04'
|
|
end_date = '2021-10-09'
|
|
result = self.empl_employee.get_timesheet_and_working_hours_for_employees(start_date, end_date)
|
|
self.assertEqual(result[self.empl_employee.id]['units_to_work'], 40, "Employee weekly working hours should be 40.")
|
|
self.assertEqual(result[self.empl_employee.id]['worked_hours'], 0.0, "Employee's working hours should be None.")
|
|
|
|
# all company have those internal project/task (created by default)
|
|
internal_project = self.env.company.internal_project_id
|
|
internal_task_leaves = self.env.company.leave_timesheet_task_id
|
|
hr_leave_type = self.env['hr.leave.type'].create({
|
|
'name': 'Leave Type with timesheet generation',
|
|
'requires_allocation': False,
|
|
})
|
|
HrLeave = self.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True)
|
|
# employee creates a leave request
|
|
holiday = HrLeave.with_user(self.user_employee).create({
|
|
'name': 'Leave 1',
|
|
'employee_id': self.empl_employee.id,
|
|
'holiday_status_id': hr_leave_type.id,
|
|
'request_date_from': '2021-10-05',
|
|
'request_date_to': '2021-10-05',
|
|
})
|
|
holiday.with_user(SUPERUSER_ID).action_approve()
|
|
result = self.empl_employee.get_timesheet_and_working_hours_for_employees(start_date, end_date)
|
|
self.assertTrue(len(holiday.timesheet_ids) > 0, 'Timesheet entry should be created in Internal project for time off.')
|
|
# working hours for employee after leave creations
|
|
self.assertEqual(result[self.empl_employee.id]['units_to_work'], 32, "Employee's weekly units of work after the leave creation should be 32.")
|
|
self.assertEqual(result[self.empl_employee.id]['worked_hours'], 0.0, "Employee's working hours shouldn't be altered after the leave creation.")
|
|
|
|
# Timesheet created for same project
|
|
timesheet1 = self.env['account.analytic.line'].with_user(self.user_employee).create({
|
|
'name': "my timesheet 1",
|
|
'project_id': internal_project.id,
|
|
'date': '2021-10-04',
|
|
'unit_amount': 8.0,
|
|
})
|
|
timesheet1.with_user(self.user_manager).action_validate_timesheet()
|
|
result = self.empl_employee.get_timesheet_and_working_hours_for_employees(start_date, end_date)
|
|
# working hours for employee after Timesheet creations
|
|
self.assertEqual(result[self.empl_employee.id]['units_to_work'], 32, "Employee's one week units of work after the Timesheet creation should be 32.")
|
|
|
|
@freeze_time('2018-02-06')
|
|
def test_grid_update_holiday(self):
|
|
Requests = self.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True)
|
|
hr_leave_type_with_ts = self.env['hr.leave.type'].create({
|
|
'name': 'Leave Type with timesheet generation',
|
|
'requires_allocation': False,
|
|
})
|
|
# employee creates a leave request
|
|
holiday = Requests.with_user(self.user_employee).create({
|
|
'name': 'Leave 1',
|
|
'employee_id': self.empl_employee.id,
|
|
'holiday_status_id': hr_leave_type_with_ts.id,
|
|
'request_date_from': '2018-02-05',
|
|
'request_date_to': '2018-02-05',
|
|
'request_hour_from': 8,
|
|
'request_hour_to': 9,
|
|
'request_unit_hours': True,
|
|
})
|
|
# validate leave request and create timesheet
|
|
holiday.with_user(SUPERUSER_ID).action_approve()
|
|
self.assertEqual(len(holiday.timesheet_ids), 1)
|
|
|
|
# create timesheet via grid_update_cell
|
|
today_date = fields.Date.today()
|
|
column_date = today_date
|
|
timesheet = holiday.timesheet_ids
|
|
domain = [ # domain given by the grid cell edited
|
|
('date', '=', today_date),
|
|
('employee_id', '=', self.empl_employee.id),
|
|
('project_id', '=', timesheet.project_id.id),
|
|
('task_id', '=', timesheet.task_id.id),
|
|
]
|
|
self.env['account.analytic.line'] \
|
|
.with_user(SUPERUSER_ID) \
|
|
.with_context( # when the user will edit a grid cell, the context of the cell will be given
|
|
default_date=column_date,
|
|
default_project_id=timesheet.project_id.id,
|
|
default_task_id=timesheet.task_id.id,
|
|
default_employee_id=self.empl_employee.id,
|
|
) \
|
|
.grid_update_cell(domain, 'unit_amount', 3.0)
|
|
|
|
timesheets = self.env['account.analytic.line'].search([
|
|
('date', '=', today_date),
|
|
('employee_id', '=', self.empl_employee.id),
|
|
('id', 'not in', holiday.timesheet_ids.ids),
|
|
])
|
|
self.assertEqual(len(timesheets), 1, "The new timesheet should have been created")
|
|
self.assertFalse(timesheets.holiday_id, "The new timesheet should not be linked to a leave request")
|
|
self.assertEqual(timesheets.unit_amount, 3.0, "The new timesheet should have the correct amount of time")
|
|
self.assertEqual(timesheets.project_id, holiday.timesheet_ids.project_id, "The new timesheet should have the same project as the timesheet generated by the leave request")
|
|
self.assertEqual(timesheets.task_id, holiday.timesheet_ids.task_id, "The new timesheet should have the same task as the timesheet generated by the leave request")
|
|
|
|
def test_start_timer_in_timeoff_task(self):
|
|
common_vals = {
|
|
'project_id': self.env.company.internal_project_id.id,
|
|
'task_id': self.env.company.leave_timesheet_task_id.id,
|
|
}
|
|
Timesheet = self.env['account.analytic.line'].with_user(self.user_manager)
|
|
timesheet = Timesheet.sudo().create({
|
|
'name': "my timesheet 1",
|
|
**common_vals,
|
|
})
|
|
with self.assertRaises(UserError, msg="a user cannot start timer in timesheet in time off task"):
|
|
timesheet.action_timer_start()
|
|
|
|
with self.assertRaises(UserError, msg="the user cannot create a timesheet and start a timer in time off task"):
|
|
timesheet.action_start_new_timesheet_timer(common_vals)
|
|
|
|
def test_employee_timesheet_reminder_skips_holidays_and_leaves(self):
|
|
"""
|
|
Reminder mail should be sent to employees, skipping holidays and approved leaves.
|
|
steps:
|
|
- Create User & Employee
|
|
- Setup Leave Type
|
|
- Create & Validate Leave
|
|
- Create Public Holiday
|
|
- Create Timesheet Entry
|
|
- Set Next Reminder Date
|
|
- Run Cron & Assert Emails
|
|
"""
|
|
user_hruser = mail_new_test_user(self.env, login='armande1', groups='base.group_user,hr_holidays.group_hr_holidays_user')
|
|
user_hruser.action_create_employee()
|
|
|
|
Requests = self.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True)
|
|
hr_leave_type = self.env['hr.leave.type'].create({
|
|
'name': 'Leave Type with timesheet generation',
|
|
'requires_allocation': False,
|
|
})
|
|
|
|
time_off = Requests.with_user(user_hruser).create({
|
|
'name': 'Test Time Off',
|
|
'employee_id': user_hruser.employee_id.id,
|
|
'holiday_status_id': hr_leave_type.id,
|
|
'request_date_from': '2022-01-31',
|
|
'request_date_to': '2022-01-31',
|
|
})
|
|
time_off.with_user(SUPERUSER_ID).action_approve()
|
|
|
|
self.env['resource.calendar.leaves'].create({
|
|
'name': 'New Public Holiday',
|
|
'calendar_id': user_hruser.employee_id.resource_calendar_id.id,
|
|
'date_from': '2022-02-01 00:00:00',
|
|
'date_to': '2022-02-01 23:59:00',
|
|
})
|
|
|
|
timesheet_date = datetime(2022, 2, 4, 8, 8, 15)
|
|
timesheet_vals = {
|
|
'name': "My Timesheet",
|
|
'project_id': self.project_customer.id,
|
|
'task_id': self.task2.id,
|
|
'date': '2022-02-03',
|
|
'unit_amount': 8.0,
|
|
}
|
|
self.env['account.analytic.line'].with_user(self.user_employee).create(timesheet_vals)
|
|
|
|
self.user_employee.company_id.timesheet_mail_employee_nextdate = timesheet_date
|
|
|
|
with freeze_time(timesheet_date), self.mock_mail_gateway():
|
|
self.env['res.company']._cron_timesheet_reminder_employee()
|
|
|
|
self.assertEqual(
|
|
len(self._new_mails.filtered(lambda x: x.res_id == self.user_employee.employee_id.id)),
|
|
1, "An email should be sent to 'User Empl Officer'"
|
|
)
|
|
self.assertEqual(
|
|
len(self._new_mails.filtered(lambda x: x.res_id == user_hruser.employee_id.id)),
|
|
0, "No email should be sent to 'HR User Empl Officer'"
|
|
)
|