[IMP] hr_recruitment(_skills/_survey): grant interviewer more rights
Purpose: In order to increase visibility of the interviewer group, it is moved from hidden group, to recruitment group. Moreover, we grant the interviewer additional rights in order to manage the application process properly and be able to execute following actions • Send an interview • Check interview results • Add/delete/modify skills • Create a meeting In order to allow above mentioned actions on skills to the interviewer, we grant them all the rights to the hr_applicant_skill. In order to let the interviewer send the survey we grant them access to the private addresses and now recruitment-interviewer implies survey-officer group as well. Additionally, for the security purposes, we make a change, so that the survey officer is not able to read recruitment interview results unless they are also an interviewer for a specific applicant. task - 3179332 closes odoo/odoo#118897 Related: odoo/enterprise#40696 Related: odoo/upgrade#4624 Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
@@ -280,7 +280,7 @@ class Applicant(models.Model):
|
||||
applicant.date_closed = False
|
||||
|
||||
def _check_interviewer_access(self):
|
||||
if self.user_has_groups('hr_recruitment.group_hr_recruitment_interviewer'):
|
||||
if self.user_has_groups('hr_recruitment.group_hr_recruitment_interviewer') and not self.user_has_groups('hr_recruitment.group_hr_recruitment_user'):
|
||||
raise AccessError(_('You are not allowed to perform this action.'))
|
||||
|
||||
@api.model_create_multi
|
||||
@@ -367,19 +367,20 @@ class Applicant(models.Model):
|
||||
|
||||
def _notify_get_recipients(self, message, msg_vals, **kwargs):
|
||||
"""
|
||||
Do not notify members of the Recruitment Interviewer group, as this
|
||||
Do not notify members of the Recruitment Interviewer group that are not part of
|
||||
Recruitment User group as well, as this
|
||||
might leak some data they shouldn't have access to.
|
||||
"""
|
||||
recipients = super()._notify_get_recipients(message, msg_vals, **kwargs)
|
||||
interviewer_group = self.env.ref('hr_recruitment.group_hr_recruitment_interviewer').id
|
||||
return [recipient for recipient in recipients if interviewer_group not in recipient['groups']]
|
||||
user_group = self.env.ref('hr_recruitment.group_hr_recruitment_user').id
|
||||
return [recipient for recipient in recipients if not (interviewer_group in recipient['groups'] and user_group not in recipient['groups'])]
|
||||
|
||||
def action_makeMeeting(self):
|
||||
""" This opens Meeting's calendar view to schedule meeting on current applicant
|
||||
@return: Dictionary value for created Meeting view
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
if not self.partner_id:
|
||||
if not self.partner_name:
|
||||
raise UserError(_('You must define a Contact Name for this applicant.'))
|
||||
@@ -392,11 +393,20 @@ class Applicant(models.Model):
|
||||
'mobile': self.partner_mobile
|
||||
})
|
||||
|
||||
partners = self.partner_id | self.user_id.partner_id | self.department_id.manager_id.user_id.partner_id
|
||||
partners = self.partner_id | self.department_id.manager_id.user_id.partner_id
|
||||
if self.user_has_groups('hr_recruitment.group_hr_recruitment_interviewer') and not self.user_has_groups('hr_recruitment.group_hr_recruitment_user'):
|
||||
partners |= self.env.user.partner_id
|
||||
else:
|
||||
partners |= self.user_id.partner_id
|
||||
|
||||
category = self.env.ref('hr_recruitment.categ_meet_interview')
|
||||
res = self.env['ir.actions.act_window']._for_xml_id('calendar.action_calendar_event')
|
||||
# As we are redirected from the hr.applicant, calendar checks rules on "hr.applicant",
|
||||
# in order to decide whether to allow creation of a meeting.
|
||||
# As interviewer does not have create right on the hr.applicant, in order to allow them
|
||||
# to create a meeting for an applicant, we pass 'create': True to the context.
|
||||
res['context'] = {
|
||||
'create': True,
|
||||
'default_applicant_id': self.id,
|
||||
'default_partner_ids': partners.ids,
|
||||
'default_user_id': self.env.uid,
|
||||
|
||||
@@ -9,6 +9,8 @@ class IrUiMenu(models.Model):
|
||||
|
||||
def _load_menus_blacklist(self):
|
||||
res = super()._load_menus_blacklist()
|
||||
if self.env.user.has_group('hr_recruitment.group_hr_recruitment_interviewer'):
|
||||
if self.env.user.has_group('hr_recruitment.group_hr_recruitment_interviewer') and not self.env.user.has_group('hr_recruitment.group_hr_recruitment_user'):
|
||||
res.append(self.env.ref('hr_recruitment.menu_hr_job_position').id)
|
||||
elif self.env.user.has_group('hr_recruitment.group_hr_recruitment_user'):
|
||||
res.append(self.env.ref('hr_recruitment.menu_hr_job_position_interviewer').id)
|
||||
return res
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<odoo noupdate="1">
|
||||
|
||||
<record model="ir.module.category" id="base.module_category_human_resources_recruitment">
|
||||
<field name="description">The user interacting with the application as interviewer don't need any specific access. They'll have access thanks to their interviewer assignation.</field>
|
||||
<field name="description">Interviewer right will give access to all job position/applications where the employee is defined. It will allow to refuse, plan meetings. Chatter content will not be available.</field>
|
||||
<field name="sequence">11</field>
|
||||
</record>
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
</record>
|
||||
|
||||
<record id="group_hr_recruitment_interviewer" model="res.groups">
|
||||
<field name="name">Recruitment Interviewer</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
<field name="name">Interviewer</field>
|
||||
<field name="category_id" ref="base.module_category_human_resources_recruitment"/>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_hr_recruitment_user" model="res.groups">
|
||||
<field name="name">Officer: Manage all applicants</field>
|
||||
<field name="category_id" ref="base.module_category_human_resources_recruitment"/>
|
||||
<field name="implied_ids" eval="[(4, ref('hr.group_hr_user'))]"/>
|
||||
<field name="implied_ids" eval="[(4, ref('hr.group_hr_user')), (4, ref('group_hr_recruitment_interviewer'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_hr_recruitment_manager" model="res.groups">
|
||||
@@ -59,6 +59,20 @@
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_interviewer'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_applicant_user_rule" model="ir.rule">
|
||||
<field name="name">User: All Applicants</field>
|
||||
<field name="model_id" ref="model_hr_applicant"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_job_user_rule" model="ir.rule">
|
||||
<field name="name">User: All Applicants</field>
|
||||
<field name="model_id" ref="model_hr_job"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="mail_message_interviewer_rule" model="ir.rule">
|
||||
<field name="name">Interviewer: No Applicant Chatter</field>
|
||||
<field name="model_id" ref="mail.model_mail_message"/>
|
||||
@@ -74,4 +88,11 @@
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_interviewer'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="mail_message_user_rule" model="ir.rule">
|
||||
<field name="name">User: All Chatter</field>
|
||||
<field name="model_id" ref="mail.model_mail_message"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_user'))]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -67,11 +67,6 @@ class TestRecruitmentInterviewer(MailCommon):
|
||||
applicant.interviewer_ids = False
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should be removed from interviewer")
|
||||
|
||||
# A Manager should not be added to the Interviewer group
|
||||
self.assertFalse(interviewer_group.id in self.manager_user.groups_id.ids, "Manager User should not be interviewer")
|
||||
applicant.interviewer_ids = self.manager_user.ids
|
||||
self.assertFalse(interviewer_group.id in self.manager_user.groups_id.ids, "Manager User should not be added in Interviewer group")
|
||||
|
||||
def test_interviewer_access_rights(self):
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'name': 'toto',
|
||||
|
||||
@@ -11,4 +11,12 @@
|
||||
]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_interviewer'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_applicant_skill_officer_rule" model="ir.rule">
|
||||
<field name="name">Applicant Skill: Officer</field>
|
||||
<field name="model_id" ref="model_hr_applicant_skill"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_user'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
hr_recruitment_skills.access_hr_applicant_skill,access_hr_applicant_skill,hr_recruitment_skills.model_hr_applicant_skill,hr_recruitment.group_hr_recruitment_user,1,1,1,1
|
||||
hr_recruitment_skills.access_hr_applicant_skill_interviewer,access_hr_applicant_skill_interviewer,hr_recruitment_skills.model_hr_applicant_skill,hr_recruitment.group_hr_recruitment_interviewer,1,0,0,0
|
||||
hr_recruitment_skills.access_hr_applicant_skill_interviewer,access_hr_applicant_skill_interviewer,hr_recruitment_skills.model_hr_applicant_skill,hr_recruitment.group_hr_recruitment_interviewer,1,1,1,1
|
||||
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<field name="id" invisible="1"/>
|
||||
<field name="is_interviewer" invisible="1"/>
|
||||
<field mode="tree" nolabel="1" name="applicant_skill_ids" widget="skills_one2many"
|
||||
context="{'default_applicant_id': id}" attrs="{'readonly': [('is_interviewer', '=', True)]}">
|
||||
context="{'default_applicant_id': id}">
|
||||
<tree>
|
||||
<field name="skill_type_id" invisible="1"/>
|
||||
<field name="skill_id"/>
|
||||
|
||||
@@ -18,12 +18,18 @@ class Applicant(models.Model):
|
||||
.filtered(lambda i: i.survey_id == self.survey_id)\
|
||||
.sorted(lambda i: i.create_date, reverse=True)
|
||||
if not sorted_interviews:
|
||||
return self.survey_id.action_print_survey()
|
||||
action = self.survey_id.action_print_survey()
|
||||
action['target'] = 'new'
|
||||
return action
|
||||
|
||||
answered_interviews = sorted_interviews.filtered(lambda i: i.state == 'done')
|
||||
if answered_interviews:
|
||||
return self.survey_id.action_print_survey(answer=answered_interviews[0])
|
||||
return self.survey_id.action_print_survey(answer=sorted_interviews[0])
|
||||
action = self.survey_id.action_print_survey(answer=answered_interviews[0])
|
||||
action['target'] = 'new'
|
||||
return action
|
||||
action = self.survey_id.action_print_survey(answer=sorted_interviews[0])
|
||||
action['target'] = 'new'
|
||||
return action
|
||||
|
||||
def action_send_survey(self):
|
||||
self.ensure_one()
|
||||
@@ -32,7 +38,7 @@ class Applicant(models.Model):
|
||||
if not self.partner_id:
|
||||
if not self.partner_name:
|
||||
raise UserError(_('Please provide an applicant name.'))
|
||||
self.partner_id = self.env['res.partner'].create({
|
||||
self.partner_id = self.env['res.partner'].sudo().create({
|
||||
'is_company': False,
|
||||
'type': 'private',
|
||||
'name': self.partner_name,
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="hr_recruitment.group_hr_recruitment_user" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('survey.group_survey_user'))]"/>
|
||||
<record id="hr_recruitment.group_hr_recruitment_interviewer" model="res.groups">
|
||||
<field name="implied_ids" eval="[Command.link(ref('survey.group_survey_user')), Command.link(ref('base.group_private_addresses'))]"/>
|
||||
<field name="comment">It will also allow to send surveys and see the resume.</field>
|
||||
</record>
|
||||
<record id="survey.survey_user_input_rule_survey_user_read" model="ir.rule">
|
||||
<field name="name">Survey user input: officer: read all non private survey answers</field>
|
||||
<field name="domain_force">[('applicant_id', '=', False)]</field>
|
||||
</record>
|
||||
<record id="survey_user_input_rule_recruitment_interviewer" model="ir.rule">
|
||||
<field name="name">Survey user input: recruitment interview: read survey answers for which they are set as interviewer</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[
|
||||
('applicant_id', '!=', False),
|
||||
'|',
|
||||
('applicant_id.interviewer_ids', 'in', user.id),
|
||||
('applicant_id.job_id.interviewer_ids', 'in', user.id),
|
||||
]</field>
|
||||
<field name="groups" eval="[(4, ref('hr_recruitment.group_hr_recruitment_interviewer'))]"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
icon="fa-pencil-square-o"
|
||||
type="object"
|
||||
help="See interview report"
|
||||
attrs="{'invisible':['|',('survey_id','=',False),('response_ids','=',False)]}">
|
||||
attrs="{'invisible':['|',('survey_id','=',False),('response_ids','=',[])]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">Consult</span>
|
||||
<span class="o_stat_text">Interview</span>
|
||||
|
||||
@@ -31,8 +31,8 @@ class SurveyInvite(models.TransientModel):
|
||||
survey = self.survey_id.with_context(clean_context(self.env.context))
|
||||
|
||||
if not self.applicant_id.response_ids.filtered(lambda res: res.survey_id.id == self.survey_id.id):
|
||||
self.applicant_id.write({
|
||||
'response_ids': (self.applicant_id.response_ids | survey._create_answer(partner=self.applicant_id.partner_id,
|
||||
self.applicant_id.sudo().write({
|
||||
'response_ids': (self.applicant_id.response_ids | survey.sudo()._create_answer(partner=self.applicant_id.partner_id,
|
||||
**self._get_answers_values())).ids
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user