[IMP] hr_recruitment: allow multiple interviewers on applicant
Allow multiple interviewers on hr.applicant instead of a Many2One. odoo/upgrade#3904 task-2939388 closes odoo/odoo#100338 Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
@@ -82,16 +82,16 @@ class Job(models.Model):
|
||||
job.activities_overdue = job_activities[job.id].get('overdue', 0)
|
||||
job.activities_today = job_activities[job.id].get('today', 0)
|
||||
|
||||
@api.depends('application_ids.interviewer_id')
|
||||
@api.depends('application_ids.interviewer_ids')
|
||||
def _compute_extended_interviewer_ids(self):
|
||||
# Use SUPERUSER_ID as the search_read is protected in hr_referral
|
||||
results_raw = self.env['hr.applicant'].with_user(SUPERUSER_ID).search_read([
|
||||
('job_id', 'in', self.ids),
|
||||
('interviewer_id', '!=', False)
|
||||
], ['interviewer_id', 'job_id'])
|
||||
('interviewer_ids', '!=', False)
|
||||
], ['interviewer_ids', 'job_id'])
|
||||
interviewers_by_job = defaultdict(set)
|
||||
for result_raw in results_raw:
|
||||
interviewers_by_job[result_raw['job_id'][0]].add(result_raw['interviewer_id'][0])
|
||||
interviewers_by_job[result_raw['job_id'][0]] |= set(result_raw['interviewer_ids'])
|
||||
for job in self:
|
||||
job.extended_interviewer_ids = [(6, 0, list(interviewers_by_job[job.id]))]
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ class Applicant(models.Model):
|
||||
campaign_id = fields.Many2one(ondelete='set null')
|
||||
medium_id = fields.Many2one(ondelete='set null')
|
||||
source_id = fields.Many2one(ondelete='set null')
|
||||
interviewer_id = fields.Many2one(
|
||||
'res.users', string='Interviewer', index=True, tracking=True,
|
||||
interviewer_ids = fields.Many2many('res.users', 'hr_applicant_res_users_interviewers_rel',
|
||||
string='Interviewers', index=True, tracking=True,
|
||||
domain="[('share', '=', False), ('company_ids', 'in', company_id)]")
|
||||
linkedin_profile = fields.Char('LinkedIn Profile')
|
||||
application_status = fields.Selection([
|
||||
@@ -391,7 +391,7 @@ class Applicant(models.Model):
|
||||
if vals.get('email_from'):
|
||||
vals['email_from'] = vals['email_from'].strip()
|
||||
applicants = super().create(vals_list)
|
||||
applicants.sudo().interviewer_id._create_recruitment_interviewers()
|
||||
applicants.sudo().interviewer_ids._create_recruitment_interviewers()
|
||||
# Record creation through calendar, creates the calendar event directly, it will also create the activity.
|
||||
if 'default_activity_date_deadline' in self.env.context:
|
||||
deadline = fields.Datetime.to_datetime(self.env.context.get('default_activity_date_deadline'))
|
||||
@@ -415,7 +415,7 @@ class Applicant(models.Model):
|
||||
vals['date_open'] = fields.Datetime.now()
|
||||
if vals.get('email_from'):
|
||||
vals['email_from'] = vals['email_from'].strip()
|
||||
old_interviewers = self.interviewer_id
|
||||
old_interviewers = self.interviewer_ids
|
||||
# stage_id: track last stage before update
|
||||
if 'stage_id' in vals:
|
||||
vals['date_last_stage_update'] = fields.Datetime.now()
|
||||
@@ -426,10 +426,10 @@ class Applicant(models.Model):
|
||||
res = super(Applicant, self).write(vals)
|
||||
else:
|
||||
res = super(Applicant, self).write(vals)
|
||||
if 'interviewer_id' in vals:
|
||||
interviewers_to_clean = old_interviewers - self.interviewer_id
|
||||
if 'interviewer_ids' in vals:
|
||||
interviewers_to_clean = old_interviewers - self.interviewer_ids
|
||||
interviewers_to_clean._remove_recruitment_interviewers()
|
||||
self.sudo().interviewer_id._create_recruitment_interviewers()
|
||||
self.sudo().interviewer_ids._create_recruitment_interviewers()
|
||||
return res
|
||||
|
||||
def get_empty_list_help(self, help):
|
||||
|
||||
@@ -26,8 +26,8 @@ class ResUsers(models.Model):
|
||||
job_interviewers = self.env['hr.job']._read_group([('interviewer_ids', 'in', self.ids)], ['interviewer_ids'], ['interviewer_ids'])
|
||||
user_ids = {j['interviewer_ids'][0] for j in job_interviewers}
|
||||
|
||||
application_interviewers = self.env['hr.applicant']._read_group([('interviewer_id', 'in', self.ids)], ['interviewer_id'], ['interviewer_id'])
|
||||
user_ids |= {a['interviewer_id'][0] for a in application_interviewers}
|
||||
application_interviewers = self.env['hr.applicant']._read_group([('interviewer_ids', 'in', self.ids)], ['interviewer_ids'], ['interviewer_ids'])
|
||||
user_ids |= {a['interviewer_ids'][0] for a in application_interviewers}
|
||||
|
||||
# Remove users that are no longer interviewers on at least a job or an application
|
||||
users_to_remove = set(self.ids) - (user_ids | set(recruitment_group.users.ids))
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<field name="domain_force">[
|
||||
'|',
|
||||
('job_id.interviewer_ids', 'in', user.id),
|
||||
('interviewer_id', '=', user.id),
|
||||
('interviewer_ids', 'in', user.id),
|
||||
]</field>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
|
||||
@@ -46,30 +46,30 @@ class TestRecruitmentInterviewer(MailCommon):
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.id,
|
||||
'interviewer_id': self.simple_user.id,
|
||||
'interviewer_ids': self.simple_user.ids,
|
||||
})
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should be added as interviewer")
|
||||
|
||||
applicant.interviewer_id = False
|
||||
applicant.interviewer_ids = False
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should be removed from interviewer")
|
||||
|
||||
self.job.interviewer_ids = self.simple_user.ids
|
||||
applicant.interviewer_id = self.simple_user.id
|
||||
applicant.interviewer_ids = self.simple_user.ids
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should be added as interviewer")
|
||||
|
||||
applicant.interviewer_id = False
|
||||
applicant.interviewer_ids = False
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should stay interviewer")
|
||||
|
||||
self.job.write({'interviewer_ids': [(5, 0, 0)]})
|
||||
applicant.interviewer_id = self.simple_user.id
|
||||
applicant.interviewer_ids = self.simple_user.ids
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should stay interviewer")
|
||||
|
||||
applicant.interviewer_id = False
|
||||
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_id = self.manager_user.id
|
||||
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):
|
||||
@@ -85,7 +85,7 @@ class TestRecruitmentInterviewer(MailCommon):
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.id,
|
||||
'interviewer_id': self.interviewer_user.id,
|
||||
'interviewer_ids': self.interviewer_user.ids,
|
||||
})
|
||||
applicant.with_user(self.interviewer_user).read()
|
||||
|
||||
@@ -98,8 +98,8 @@ class TestRecruitmentInterviewer(MailCommon):
|
||||
applicant.with_user(self.interviewer_user).read()
|
||||
|
||||
# An interviewer can change the interviewers
|
||||
applicant.with_user(self.interviewer_user).interviewer_id = self.simple_user.id
|
||||
self.assertEqual(self.simple_user, applicant.interviewer_id)
|
||||
applicant.with_user(self.interviewer_user).interviewer_ids = self.simple_user.ids
|
||||
self.assertEqual(self.simple_user, applicant.interviewer_ids)
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
applicant.with_user(self.interviewer_user).create_employee_from_applicant()
|
||||
@@ -111,7 +111,7 @@ class TestRecruitmentInterviewer(MailCommon):
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.id,
|
||||
'interviewer_id': self.interviewer_user.id,
|
||||
'interviewer_ids': self.interviewer_user.ids,
|
||||
})
|
||||
|
||||
applicant.message_subscribe(partner_ids=[self.interviewer_user.partner_id.id])
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<field name="partner_mobile" widget="phone" readonly="1" optional="show" class="text-end"/>
|
||||
<field name="categ_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
|
||||
<field name="user_id" widget="many2one_avatar_user" optional="show"/>
|
||||
<field name="interviewer_id" widget="many2one_avatar_user" optional="hide"/>
|
||||
<field name="interviewer_ids" widget="many2many_avatar_user" optional="hide"/>
|
||||
<field name="create_date" readonly="1" widget="date" optional="show"/>
|
||||
<field name="partner_phone" widget="phone" readonly="1" optional="hide"/>
|
||||
<field name="medium_id" optional="hide"/>
|
||||
@@ -145,7 +145,7 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="categ_ids" placeholder="Tags" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}"/>
|
||||
<field name="interviewer_id" options="{'no_create': True, 'no_create_edit': True}" widget="many2one_avatar_user" />
|
||||
<field name="interviewer_ids" options="{'no_create': True, 'no_create_edit': True}" widget="many2many_avatar_user" />
|
||||
<field name="user_id" widget="many2one_avatar_user"/>
|
||||
<field name="date_closed" attrs="{'invisible': [('date_closed', '=', False)]}" />
|
||||
<field name="priority" widget="priority"/>
|
||||
|
||||
Reference in New Issue
Block a user