From 13549b56f5cea4cabd6335c90604dd6ca64a81c2 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Thu, 25 Jan 2024 12:45:14 +0000 Subject: [PATCH] [FIX] project_timesheet_holidays: Fix company right issues Before this commit, when the company of the employees updated is not in the allowed_company_ids of the current user (could happen in the execution of a cron), an error is triggered saying we are not allowed to create a timesheet for an employee inactive. The reason is because the employee is not fetched in the check made in the timesheet creation because of the company of that employee is not in the companies contained in the environment. This commit makes sure the companies in the environment are the ones set on the employees created/updated, to be sure those employees are fetched in the check made in timesheet creation. closes odoo/odoo#151656 X-original-commit: ed6b02b6323691f79aa4ff466541eeb9768de1c9 Signed-off-by: Xavier Bol (xbo) --- addons/hr_timesheet/tests/test_timesheet.py | 17 ++- .../models/hr_employee.py | 13 +- .../tests/__init__.py | 1 + .../tests/test_employee.py | 114 ++++++++++++++++++ 4 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 addons/project_timesheet_holidays/tests/test_employee.py diff --git a/addons/hr_timesheet/tests/test_timesheet.py b/addons/hr_timesheet/tests/test_timesheet.py index a571b33d4ab..7aa62b88893 100644 --- a/addons/hr_timesheet/tests/test_timesheet.py +++ b/addons/hr_timesheet/tests/test_timesheet.py @@ -460,9 +460,10 @@ class TestTimesheet(TestCommonTimesheet): def test_create_timesheet_with_multi_company(self): """ Always set the current company in the timesheet, not the employee company """ company_4 = self.env['res.company'].create({'name': 'Company 4'}) - empl_employee = self.env['hr.employee'].with_company(company_4).create({ - 'name': 'Employee 3', - }) + empl_employee, archived_employee = self.env['hr.employee'].with_company(company_4).create([ + {'name': 'Employee 3'}, + {'name': 'Employee 4', 'active': False}, + ]) Timesheet = self.env['account.analytic.line'].with_context(allowed_company_ids=[company_4.id, self.env.company.id]) @@ -473,9 +474,17 @@ class TestTimesheet(TestCommonTimesheet): 'unit_amount': 4, 'employee_id': empl_employee.id, }) - self.assertEqual(timesheet.company_id.id, self.env.company.id) + with self.assertRaises(UserError, msg="The employee must be active to encode a timesheet"): + Timesheet.create({ + 'project_id': self.project_customer.id, + 'task_id': self.task1.id, + 'name': 'my first timesheet', + 'unit_amount': 4, + 'employee_id': archived_employee.id, + }) + def test_subtask_log_timesheet(self): """ Test parent task takes into account the timesheets of its sub-tasks. Test Case: diff --git a/addons/project_timesheet_holidays/models/hr_employee.py b/addons/project_timesheet_holidays/models/hr_employee.py index 4c9216f37b5..a6066720910 100644 --- a/addons/project_timesheet_holidays/models/hr_employee.py +++ b/addons/project_timesheet_holidays/models/hr_employee.py @@ -16,24 +16,25 @@ class Employee(models.Model): # We need to create timesheet entries for the global time off that are already created # and are planned for after this employee creation date - - self._create_future_public_holidays_timesheets(employees) + self.with_context(allowed_company_ids=employees.company_id.ids) \ + ._create_future_public_holidays_timesheets(employees) return employees def write(self, vals): result = super(Employee, self).write(vals) + self_company = self.with_context(allowed_company_ids=self.company_id.ids) if 'active' in vals: if vals.get('active'): # Create future holiday timesheets - inactive_emp = self.filtered(lambda e: not e.active) + inactive_emp = self_company.filtered(lambda e: not e.active) inactive_emp._create_future_public_holidays_timesheets(self) else: # Delete future holiday timesheets - self._delete_future_public_holidays_timesheets() + self_company._delete_future_public_holidays_timesheets() elif 'resource_calendar_id' in vals: # Update future holiday timesheets - self._delete_future_public_holidays_timesheets() - self._create_future_public_holidays_timesheets(self) + self_company._delete_future_public_holidays_timesheets() + self_company._create_future_public_holidays_timesheets(self) return result def _delete_future_public_holidays_timesheets(self): diff --git a/addons/project_timesheet_holidays/tests/__init__.py b/addons/project_timesheet_holidays/tests/__init__.py index bd77f885744..48b4b6c6eca 100644 --- a/addons/project_timesheet_holidays/tests/__init__.py +++ b/addons/project_timesheet_holidays/tests/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import test_employee from . import test_timesheet_holidays from . import test_timesheet_global_time_off diff --git a/addons/project_timesheet_holidays/tests/test_employee.py b/addons/project_timesheet_holidays/tests/test_employee.py new file mode 100644 index 00000000000..a131a5551ef --- /dev/null +++ b/addons/project_timesheet_holidays/tests/test_employee.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from freezegun import freeze_time + +from odoo.tests import TransactionCase, tagged + + +@tagged('post_install', '-at_install') +class TestEmployee(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company = cls.env['res.company'].create({ + 'name': 'Test Company', + }) + cls.global_leave = cls.env['resource.calendar.leaves'].create({ + 'name': 'Test Global Leave', + 'date_from': '2020-01-01 00:00:00', + 'date_to': '2020-01-01 23:59:59', + 'calendar_id': cls.company.resource_calendar_id.id, + 'company_id': cls.company.id, + }) + + @freeze_time('2020-01-01') + def test_create_employee(self): + """ Test the timesheets representing the time off of this new employee + is correctly generated once the employee is created + + Test Case: + ========= + 1) Create a new employee + 2) Check the timesheets representing the time off of this new employee + is correctly generated + """ + employee = self.env['hr.employee'].create({ + 'name': 'Test Employee', + 'company_id': self.company.id, + 'resource_calendar_id': self.company.resource_calendar_id.id, + }) + timesheet = self.env['account.analytic.line'].search([ + ('employee_id', '=', employee.id), + ('global_leave_id', '=', self.global_leave.id), + ]) + self.assertEqual(len(timesheet), 1, 'A timesheet should have been created for the global leave of the employee') + self.assertEqual(str(timesheet.date), '2020-01-01', 'The timesheet should be created for the correct date') + self.assertEqual(timesheet.unit_amount, 8, 'The timesheet should be created for the correct duration') + + # simulate the company of the employee updated is not in the allowed_company_ids of the current user + employee2 = self.env['hr.employee'].with_company(self.env.company).create({ + 'name': 'Test Employee', + 'company_id': self.company.id, + 'resource_calendar_id': self.company.resource_calendar_id.id, + }) + timesheet = self.env['account.analytic.line'].search([ + ('employee_id', '=', employee2.id), + ('global_leave_id', '=', self.global_leave.id), + ]) + self.assertEqual(len(timesheet), 1, 'A timesheet should have been created for the global leave of the employee') + self.assertEqual(str(timesheet.date), '2020-01-01', 'The timesheet should be created for the correct date') + self.assertEqual(timesheet.unit_amount, 8, 'The timesheet should be created for the correct duration') + + @freeze_time('2020-01-01') + def test_write_employee(self): + """ Test the timesheets representing the time off of this employee + is correctly generated once the employee is updated + + Test Case: + ========= + 1) Create a new employee + 2) Check the timesheets representing the time off of this new employee + is correctly generated + 3) Update the employee + 4) Check the timesheets representing the time off of this employee + is correctly updated + """ + employee = self.env['hr.employee'].create({ + 'name': 'Test Employee', + 'company_id': self.company.id, + }) + employee.write({'resource_calendar_id': self.company.resource_calendar_id.id}) + timesheet = self.env['account.analytic.line'].search([ + ('employee_id', '=', employee.id), + ('global_leave_id', '=', self.global_leave.id), + ]) + self.assertEqual(len(timesheet), 1, 'A timesheet should have been created for the global leave of the employee') + self.assertEqual(str(timesheet.date), '2020-01-01', 'The timesheet should be created for the correct date') + self.assertEqual(timesheet.unit_amount, 8, 'The timesheet should be created for the correct duration') + + employee.write({'active': False}) + timesheet = self.env['account.analytic.line'].search([ + ('employee_id', '=', employee.id), + ('global_leave_id', '=', self.global_leave.id), + ]) + self.assertFalse(timesheet, 'The timesheet should have been deleted when the employee was archived') + + employee.write({'active': True}) + timesheet = self.env['account.analytic.line'].search([ + ('employee_id', '=', employee.id), + ('global_leave_id', '=', self.global_leave.id), + ]) + self.assertEqual(len(timesheet), 1, 'A timesheet should have been created for the global leave of the employee') + self.assertEqual(str(timesheet.date), '2020-01-01', 'The timesheet should be created for the correct date') + self.assertEqual(timesheet.unit_amount, 8, 'The timesheet should be created for the correct duration') + + # simulate the company of the employee updated is not in the allowed_company_ids of the current user + employee.with_company(self.env.company).write({'resource_calendar_id': self.company.resource_calendar_id.id}) + timesheet = self.env['account.analytic.line'].search([ + ('employee_id', '=', employee.id), + ('global_leave_id', '=', self.global_leave.id), + ]) + self.assertEqual(len(timesheet), 1, 'A timesheet should have been created for the global leave of the employee') + self.assertEqual(str(timesheet.date), '2020-01-01', 'The timesheet should be created for the correct date') + self.assertEqual(timesheet.unit_amount, 8, 'The timesheet should be created for the correct duration')