From b8a8ad27de40e856d713b0d57c1f8f591e1e4523 Mon Sep 17 00:00:00 2001 From: Kevin Baptiste Date: Thu, 9 Apr 2020 14:55:52 +0000 Subject: [PATCH] [FIX] hr_holidays: "Everyone" view on calendar_event Purpose ======= Avoid to show to much information to leave responsibles. Specification ============= - Dynamically add/remove the leave_manager_ids on the group group_hr_holidays_responsible, on employee creation/modification - Retrict the available employees when creating a leave. For manager/users : Everyone. For responsibles: The employee for who the user is responsible. Otherwise: Only me. - Don't obfuscate the leave name on employees for who I'm the responsible. - Add a new SQL view for "Everyone" report. To select only the information we want to share instead of showing the hr.leave model directly. closes odoo/odoo#47659 closes odoo/odoo#49765 Taskid: 2206862 Related: odoo/enterprise#9239 Related: odoo/enterprise#10021 X-original-commit: 781147dcaf1552b8f2ab687ed73034b8fd1f7352 Signed-off-by: Yannick Tivisse (yti) --- addons/hr_holidays/__manifest__.py | 1 + addons/hr_holidays/models/hr_employee.py | 29 ++++++++++ addons/hr_holidays/models/hr_leave.py | 3 +- addons/hr_holidays/models/res_users.py | 16 ++++++ addons/hr_holidays/report/__init__.py | 1 + .../report/hr_leave_report_calendar.py | 42 ++++++++++++++ .../report/hr_leave_report_calendar.xml | 39 +++++++++++++ .../security/hr_holidays_security.xml | 55 ++++++++++++++++++- .../hr_holidays/security/ir.model.access.csv | 1 + .../hr_holidays/tests/test_access_rights.py | 19 +++---- .../hr_holidays/views/hr_holidays_views.xml | 1 + .../views/hr_leave_allocation_views.xml | 6 +- addons/hr_holidays/views/hr_leave_views.xml | 17 +----- 13 files changed, 197 insertions(+), 33 deletions(-) create mode 100644 addons/hr_holidays/report/hr_leave_report_calendar.py create mode 100644 addons/hr_holidays/report/hr_leave_report_calendar.xml diff --git a/addons/hr_holidays/__manifest__.py b/addons/hr_holidays/__manifest__.py index dcac711d856..167ccba5d6c 100644 --- a/addons/hr_holidays/__manifest__.py +++ b/addons/hr_holidays/__manifest__.py @@ -45,6 +45,7 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p 'report/hr_holidays_templates.xml', 'report/hr_holidays_reports.xml', 'report/hr_leave_reports.xml', + 'report/hr_leave_report_calendar.xml', 'views/hr_views.xml', 'views/hr_leave_template.xml', diff --git a/addons/hr_holidays/models/hr_employee.py b/addons/hr_holidays/models/hr_employee.py index 4a2343fda12..22e9804aa67 100644 --- a/addons/hr_holidays/models/hr_employee.py +++ b/addons/hr_holidays/models/hr_employee.py @@ -148,8 +148,37 @@ class HrEmployeeBase(models.AbstractModel): ]) return [('id', 'in', holidays.mapped('employee_id').ids)] + @api.model + def create(self, values): + if 'parent_id' in values: + manager = self.env['hr.employee'].browse(values['parent_id']).user_id + values['leave_manager_id'] = values.get('leave_manager_id', manager.id) + if values.get('leave_manager_id', False): + approver_group = self.env.ref('hr_holidays.group_hr_holidays_responsible', raise_if_not_found=False) + if approver_group: + approver_group.sudo().write({'users': [(4, values['leave_manager_id'])]}) + return super(HrEmployeeBase, self).create(values) + def write(self, values): + if 'parent_id' in values: + manager = self.env['hr.employee'].browse(values['parent_id']).user_id + if manager: + to_change = self.filtered(lambda e: e.leave_manager_id == e.parent_id.user_id or not e.leave_manager_id) + to_change.write({'leave_manager_id': values.get('leave_manager_id', manager.id)}) + + old_managers = self.env['res.users'] + if 'leave_manager_id' in values: + old_managers = self.mapped('leave_manager_id') + if values['leave_manager_id']: + old_managers -= self.env['res.users'].browse(values['leave_manager_id']) + approver_group = self.env.ref('hr_holidays.group_hr_holidays_responsible', raise_if_not_found=False) + if approver_group: + approver_group.sudo().write({'users': [(4, values['leave_manager_id'])]}) + res = super(HrEmployeeBase, self).write(values) + # remove users from the Responsible group if they are no longer leave managers + old_managers._clean_leave_responsible_users() + if 'parent_id' in values or 'department_id' in values: today_date = fields.Datetime.now() hr_vals = {} diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 39ab4eee64b..31d27441f34 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -128,6 +128,7 @@ class HolidaysRequest(models.Model): domain=[('valid', '=', True)]) validation_type = fields.Selection(string='Validation Type', related='holiday_status_id.leave_validation_type', readonly=False) # HR data + employee_id = fields.Many2one( 'hr.employee', string='Employee', index=True, readonly=True, ondelete="restrict", states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, tracking=True) @@ -829,7 +830,7 @@ class HolidaysRequest(models.Model): holidays._create_resource_leave() for holiday in holidays.filtered(lambda l: l.holiday_status_id.create_calendar_meeting): meeting_values = holiday._prepare_holidays_meeting_values() - meeting = self.env['calendar.event'].with_context(no_mail_to_attendees=True).create(meeting_values) + meeting = self.env['calendar.event'].with_context(no_mail_to_attendees=True, active_model=self._name).create(meeting_values) holiday.write({'meeting_id': meeting.id}) def _prepare_holidays_meeting_values(self): diff --git a/addons/hr_holidays/models/res_users.py b/addons/hr_holidays/models/res_users.py index c78f415f923..f18c40fff10 100644 --- a/addons/hr_holidays/models/res_users.py +++ b/addons/hr_holidays/models/res_users.py @@ -57,3 +57,19 @@ class User(models.Model): AND res_users.active = 't' AND date_from <= %%s AND date_to >= %%s''' % field, (now, now)) return [r[0] for r in self.env.cr.fetchall()] + + def _clean_leave_responsible_users(self): + # self = old bunch of leave responsibles + # This method compares the current leave managers + # and remove the access rights to those who don't + # need them anymore + approver_group = self.env.ref('hr_holidays.group_hr_holidays_responsible', raise_if_not_found=False) + if not self or not approver_group: + return + res = self.env['hr.employee'].read_group( + [('leave_manager_id', 'in', self.ids)], + ['leave_manager_id'], + ['leave_manager_id']) + responsibles_to_remove_ids = set(self.ids) - {x['leave_manager_id'][0] for x in res} + approver_group.sudo().write({ + 'users': [(3, manager_id) for manager_id in responsibles_to_remove_ids]}) diff --git a/addons/hr_holidays/report/__init__.py b/addons/hr_holidays/report/__init__.py index 4dcb9db9075..c59b1e68b62 100644 --- a/addons/hr_holidays/report/__init__.py +++ b/addons/hr_holidays/report/__init__.py @@ -3,3 +3,4 @@ from . import holidays_summary_report from . import hr_leave_report +from . import hr_leave_report_calendar diff --git a/addons/hr_holidays/report/hr_leave_report_calendar.py b/addons/hr_holidays/report/hr_leave_report_calendar.py new file mode 100644 index 00000000000..49418c9a63b --- /dev/null +++ b/addons/hr_holidays/report/hr_leave_report_calendar.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, tools + +from odoo.addons.base.models.res_partner import _tz_get + + +class LeaveReportCalendar(models.Model): + _name = "hr.leave.report.calendar" + _description = 'Time Off Calendar' + _auto = False + _order = "start_datetime DESC, employee_id" + + name = fields.Char(string='Name', readonly=True) + start_datetime = fields.Datetime(string='From', readonly=True) + stop_datetime = fields.Datetime(string='To', readonly=True) + tz = fields.Selection(_tz_get, string="Timezone", readonly=True) + duration = fields.Float(string='Duration', readonly=True) + employee_id = fields.Many2one('hr.employee', readonly=True) + company_id = fields.Many2one('res.company', readonly=True) + + def init(self): + tools.drop_view_if_exists(self._cr, 'hr_leave_report_calendar') + + self._cr.execute("""CREATE OR REPLACE VIEW hr_leave_report_calendar AS + (SELECT + row_number() OVER() AS id, + ce.name AS name, + ce.start AS start_datetime, + ce.stop AS stop_datetime, + ce.duration AS duration, + hl.employee_id AS employee_id, + em.company_id AS company_id + FROM hr_leave hl + LEFT JOIN calendar_event ce + ON ce.id = hl.meeting_id + LEFT JOIN hr_employee em + ON em.id = hl.employee_id + WHERE + hl.state = 'validate'); + """) diff --git a/addons/hr_holidays/report/hr_leave_report_calendar.xml b/addons/hr_holidays/report/hr_leave_report_calendar.xml new file mode 100644 index 00000000000..459fe8810ec --- /dev/null +++ b/addons/hr_holidays/report/hr_leave_report_calendar.xml @@ -0,0 +1,39 @@ + + + + All Time Off + hr.leave.report.calendar + calendar + + + + + + + + + hr.leave.report.calendar.view + hr.leave.report.calendar + + + + + + + + + hr.leave.report.calendar.view.form + hr.leave.report.calendar + +
+ + + + + + +
+
+
+
diff --git a/addons/hr_holidays/security/hr_holidays_security.xml b/addons/hr_holidays/security/hr_holidays_security.xml index 9b2c42d4d5a..bb94ef5856d 100644 --- a/addons/hr_holidays/security/hr_holidays_security.xml +++ b/addons/hr_holidays/security/hr_holidays_security.xml @@ -5,10 +5,16 @@ 10 + + Responsible + + + + All Approver - + @@ -27,7 +33,7 @@ Time Off base.group_user read - [(1, '=', 1)] + [('employee_id.user_id', '=', user.id)] @@ -62,6 +68,44 @@ + + Time Off Responsible read + + [ + ('employee_id.leave_manager_id', '=', user.id), + ] + + + + + + + + Time Off Responsible create/write + + [ + ('holiday_type', '=', 'employee'), + '|', + '&', + ('employee_id.user_id', '=', user.id), + ('state', '!=', 'validate'), + ('employee_id.leave_manager_id', '=', user.id), + ] + + + + + + + Time Off All Approver read + + [(1, '=', 1)] + + + + + + Time Off All Approver create/write @@ -193,5 +237,12 @@ ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + Time Off Report Calendar: multi company global rule + + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + diff --git a/addons/hr_holidays/security/ir.model.access.csv b/addons/hr_holidays/security/ir.model.access.csv index 5c966878c8a..83de3ad7c7a 100644 --- a/addons/hr_holidays/security/ir.model.access.csv +++ b/addons/hr_holidays/security/ir.model.access.csv @@ -15,3 +15,4 @@ access_calendar_event_type_manager,calendar.event.type.manager,calendar.model_ca access_calendar_attendee_hr_user,calendar.attendee.hr.user,calendar.model_calendar_attendee,hr_holidays.group_hr_holidays_user,1,1,1,1 access_mail_activity_type_holidays_manager,mail.activity.type.holidays.manager,mail.model_mail_activity_type,hr_holidays.group_hr_holidays_manager,1,1,1,1 access_hr_holidays_summary_employee,access.hr.holidays.summary.employee,model_hr_holidays_summary_employee,hr_holidays.group_hr_holidays_user,1,1,1,0 +access_hr_leave_report_calendar,access_hr_leave_report_calendar,model_hr_leave_report_calendar,base.group_user,1,0,0,0 diff --git a/addons/hr_holidays/tests/test_access_rights.py b/addons/hr_holidays/tests/test_access_rights.py index 0f77cf57caf..c82be054881 100644 --- a/addons/hr_holidays/tests/test_access_rights.py +++ b/addons/hr_holidays/tests/test_access_rights.py @@ -528,7 +528,7 @@ class TestAccessRightsRead(TestHrHolidaysAccessRightsCommon): @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_leave_read_by_user_other(self): - """ Users should be able to read other people requests except name field """ + """ Users should not be able to read other people requests """ other_leave = self.env['hr.leave'].with_user(self.user_hruser).create({ 'name': 'Test', 'holiday_status_id': self.leave_type.id, @@ -538,15 +538,12 @@ class TestAccessRightsRead(TestHrHolidaysAccessRightsCommon): 'date_to': datetime.now() + relativedelta(days=1), 'number_of_days': 1, }) - res = other_leave.with_user(self.user_employee_id).read(['number_of_days', 'state', 'name']) - self.assertEqual( - res[0]['name'], '*****', - 'Private information should have been stripped, received %s instead' % res[0]['name'] - ) + with self.assertRaises(AccessError), self.cr.savepoint(): + res = other_leave.with_user(self.user_employee_id).read(['number_of_days', 'state', 'name']) @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_leave_read_by_user_other_browse(self): - """ Users should be able to browse other people requests except name field """ + """ Users should not be able to browse other people requests """ other_leave = self.env['hr.leave'].with_user(self.user_hruser).create({ 'name': 'Test', 'holiday_status_id': self.leave_type.id, @@ -556,11 +553,9 @@ class TestAccessRightsRead(TestHrHolidaysAccessRightsCommon): 'date_to': datetime.now() + relativedelta(days=1), 'number_of_days': 1, }) - other_leave.invalidate_cache(['name']) - self.assertEqual( - other_leave.with_user(self.user_employee_id).name, '*****', - 'Private information should have been stripped, received %s instead' % other_leave.with_user(self.user_employee_id).name - ) + with self.assertRaises(AccessError), self.cr.savepoint(): + other_leave.invalidate_cache(['name']) + name = other_leave.with_user(self.user_employee_id).name @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_leave_read_by_user_own(self): diff --git a/addons/hr_holidays/views/hr_holidays_views.xml b/addons/hr_holidays/views/hr_holidays_views.xml index 832de6634fc..5357839bc06 100644 --- a/addons/hr_holidays/views/hr_holidays_views.xml +++ b/addons/hr_holidays/views/hr_holidays_views.xml @@ -50,6 +50,7 @@ id="hr_holidays_menu_manager_approve" name="To Approve" parent="menu_hr_holidays_approvals" + groups="hr_holidays.group_hr_holidays_responsible,hr_holidays.group_hr_holidays_user" sequence="1"/> + groups="hr_holidays.group_hr_holidays_responsible"/>