From b658171ea2ec1a55ec2d79489f91964ab0b413c3 Mon Sep 17 00:00:00 2001 From: Kevin Baptiste Date: Thu, 15 Sep 2022 14:02:54 +0000 Subject: [PATCH] [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 --- addons/hr_recruitment/models/hr_job.py | 8 +++---- .../hr_recruitment/models/hr_recruitment.py | 14 ++++++------ addons/hr_recruitment/models/res_users.py | 4 ++-- .../security/hr_recruitment_security.xml | 2 +- .../tests/test_recruitment_interviewer.py | 22 +++++++++---------- .../views/hr_recruitment_views.xml | 4 ++-- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/addons/hr_recruitment/models/hr_job.py b/addons/hr_recruitment/models/hr_job.py index 46b62bdc447..7564ba14e48 100644 --- a/addons/hr_recruitment/models/hr_job.py +++ b/addons/hr_recruitment/models/hr_job.py @@ -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]))] diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index 670711178c4..cef4f905f3c 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -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): diff --git a/addons/hr_recruitment/models/res_users.py b/addons/hr_recruitment/models/res_users.py index 92e5601f937..996530feae4 100644 --- a/addons/hr_recruitment/models/res_users.py +++ b/addons/hr_recruitment/models/res_users.py @@ -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)) diff --git a/addons/hr_recruitment/security/hr_recruitment_security.xml b/addons/hr_recruitment/security/hr_recruitment_security.xml index ac63198974b..112cb764449 100644 --- a/addons/hr_recruitment/security/hr_recruitment_security.xml +++ b/addons/hr_recruitment/security/hr_recruitment_security.xml @@ -52,7 +52,7 @@ [ '|', ('job_id.interviewer_ids', 'in', user.id), - ('interviewer_id', '=', user.id), + ('interviewer_ids', 'in', user.id), ] diff --git a/addons/hr_recruitment/tests/test_recruitment_interviewer.py b/addons/hr_recruitment/tests/test_recruitment_interviewer.py index ef451dabd6e..0e13a596e61 100644 --- a/addons/hr_recruitment/tests/test_recruitment_interviewer.py +++ b/addons/hr_recruitment/tests/test_recruitment_interviewer.py @@ -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]) diff --git a/addons/hr_recruitment/views/hr_recruitment_views.xml b/addons/hr_recruitment/views/hr_recruitment_views.xml index 1ade582c330..1df73dc31e3 100644 --- a/addons/hr_recruitment/views/hr_recruitment_views.xml +++ b/addons/hr_recruitment/views/hr_recruitment_views.xml @@ -40,7 +40,7 @@ - + @@ -145,7 +145,7 @@ - +