mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-26 00:52:04 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -2,6 +2,8 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_recruitment_process
|
||||
from . import test_recruitment_talent_pools
|
||||
from . import test_recruitment
|
||||
from . import test_utm
|
||||
from . import test_recruitment_interviewer
|
||||
from . import test_recruitment_allowed_user_ids
|
||||
|
|
|
|||
|
|
@ -1,71 +1,494 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import tagged, TransactionCase
|
||||
import base64
|
||||
|
||||
from odoo import Command
|
||||
from odoo.fields import Domain
|
||||
from odoo.tests import tagged, TransactionCase, Form
|
||||
|
||||
|
||||
@tagged('recruitment')
|
||||
class TestRecruitment(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.company = cls.env['res.company'].create({
|
||||
'name': 'Company Test',
|
||||
'country_id': cls.env.ref('base.us').id,
|
||||
})
|
||||
cls.env.user.company_id = cls.company
|
||||
cls.env.user.company_ids = [Command.set(cls.company.ids)]
|
||||
|
||||
cls.TEXT = base64.b64encode(bytes("hr_recruitment", 'utf-8'))
|
||||
cls.Attachment = cls.env['ir.attachment']
|
||||
|
||||
def test_infer_applicant_lang_from_context(self):
|
||||
# Prerequisites
|
||||
self.env['res.lang']._activate_lang('pl_PL')
|
||||
self.env['res.lang']._activate_lang('en_US')
|
||||
self.env['ir.default'].set('res.partner', 'lang', 'en_US')
|
||||
|
||||
# Creating an applicant will create a partner (email_from inverse)
|
||||
applicant = self.env['hr.applicant'].sudo().with_context(lang='pl_PL').create({
|
||||
'partner_name': 'Test Applicant',
|
||||
'email_from': "test_aplicant@example.com"
|
||||
})
|
||||
self.assertEqual(applicant.partner_id.lang, 'pl_PL', 'Context langague not used for partner creation')
|
||||
|
||||
def test_duplicate_email(self):
|
||||
# Tests that duplicate email match ignores case
|
||||
# And that no match is found when there is none
|
||||
# Tests that duplicate email matching is case insesitive
|
||||
dup1, dup2, no_dup = self.env['hr.applicant'].create([
|
||||
{
|
||||
'name': 'Application 1',
|
||||
'partner_name': 'Application 1',
|
||||
'email_from': 'laurie.poiret@aol.ru',
|
||||
},
|
||||
{
|
||||
'name': 'Application 2',
|
||||
'partner_name': 'Application 2',
|
||||
'email_from': 'laurie.POIRET@aol.ru',
|
||||
},
|
||||
{
|
||||
'name': 'Application 3',
|
||||
'partner_name': 'Application 3',
|
||||
'email_from': 'laure.poiret@aol.ru',
|
||||
},
|
||||
])
|
||||
self.assertEqual(dup1.application_count, 1)
|
||||
self.assertEqual(dup2.application_count, 1)
|
||||
self.assertEqual(no_dup.application_count, 0)
|
||||
self.assertEqual(dup1.application_count, 2)
|
||||
self.assertEqual(dup2.application_count, 2)
|
||||
self.assertEqual(no_dup.application_count, 1)
|
||||
|
||||
def test_application_count(self):
|
||||
""" Test that we find same applicants based on simmilar mail,
|
||||
phone or mobile phone.
|
||||
"""
|
||||
A, B, C, D, E, F = self.env['hr.applicant'].create([
|
||||
def test_similar_applicants_count(self):
|
||||
"""Test that we find same applicant based on simmilar mail or phone."""
|
||||
A, B, C, D, E, F, _ = self.env['hr.applicant'].create([
|
||||
{
|
||||
'name': 'Application A',
|
||||
'active': False, # Refused/archived application should still count
|
||||
'partner_name': 'Application A',
|
||||
'email_from': 'abc@odoo.com',
|
||||
'partner_phone': '123',
|
||||
'partner_mobile': '14-15-16',
|
||||
},
|
||||
{
|
||||
'name': 'Application B',
|
||||
'partner_name': 'Application B',
|
||||
'partner_phone': '456',
|
||||
'partner_mobile': '11-12-13',
|
||||
},
|
||||
{
|
||||
'name': 'Application C',
|
||||
'partner_name': 'Application C',
|
||||
'email_from': 'def@odoo.com',
|
||||
'partner_phone': '123',
|
||||
'partner_mobile': '14-15-16',
|
||||
},
|
||||
{
|
||||
'name': 'Application D',
|
||||
'email_from': 'def@odoo.com',
|
||||
'partner_name': 'Application D',
|
||||
'email_from': 'abc@odoo.com',
|
||||
'partner_phone': '456',
|
||||
'partner_mobile': '14-15-16',
|
||||
},
|
||||
{
|
||||
'name': 'Application E',
|
||||
'partner_name': 'Application E',
|
||||
'partner_phone': '',
|
||||
},
|
||||
{
|
||||
'name': 'Application F',
|
||||
'partner_phone': '11-12-13', # In case phone is configured in a wrong field
|
||||
}
|
||||
'partner_name': 'Application F',
|
||||
'email_from': 'ghi@odoo.com',
|
||||
'partner_phone': '789',
|
||||
},
|
||||
{
|
||||
'partner_name': 'Application G',
|
||||
},
|
||||
])
|
||||
self.assertEqual(A.application_count, 2) # C, D
|
||||
self.assertEqual(B.application_count, 2) # D, F
|
||||
self.assertEqual(C.application_count, 2) # A, D
|
||||
self.assertEqual(D.application_count, 3) # A, B, C
|
||||
self.assertEqual(E.application_count, 0)
|
||||
self.assertEqual(F.application_count, 1) # B
|
||||
self.assertEqual(A.application_count, 3) # A, C, D
|
||||
self.assertEqual(B.application_count, 2) # B, D
|
||||
self.assertEqual(C.application_count, 2) # C, A
|
||||
self.assertEqual(D.application_count, 3) # D, A, B
|
||||
self.assertEqual(E.application_count, 0) # Should not match with E and G as there is no data to use for matching.
|
||||
self.assertEqual(F.application_count, 1) # F
|
||||
|
||||
def test_talent_pool_count(self):
|
||||
tp_A, tp_B = self.env["hr.talent.pool"].create([{"name": "Cool Pool"}, {"name": "Other Pool"}])
|
||||
t_A, t_B = self.env["hr.applicant"].create(
|
||||
[
|
||||
{
|
||||
"partner_name": "Talent A",
|
||||
"email_from": "abc@example.com",
|
||||
"partner_phone": "1234",
|
||||
"linkedin_profile": "linkedin/talent",
|
||||
"talent_pool_ids": [tp_A.id, tp_B.id],
|
||||
},
|
||||
{
|
||||
"partner_name": "Talent B",
|
||||
"email_from": "talent_b@example.com",
|
||||
"partner_phone": "9999",
|
||||
"talent_pool_ids": [tp_B.id],
|
||||
},
|
||||
]
|
||||
)
|
||||
# The only way to create a talent is through the wizards. Talents that are
|
||||
# created through the wizard also assign their own ID as pool_applicant_id
|
||||
t_A.pool_applicant_id = t_A.id
|
||||
t_B.pool_applicant_id = t_B.id
|
||||
|
||||
A, B, C, D, E, F, G = self.env["hr.applicant"].create(
|
||||
[
|
||||
{"partner_name": "A", "pool_applicant_id": t_A.id},
|
||||
{
|
||||
"partner_name": "B",
|
||||
"email_from": "def@example.com",
|
||||
"partner_phone": "6789",
|
||||
"linkedin_profile": "linkedin/b",
|
||||
"pool_applicant_id": t_A.id,
|
||||
},
|
||||
{
|
||||
"partner_name": "C",
|
||||
"email_from": "def@example.com",
|
||||
},
|
||||
{
|
||||
"partner_name": "D",
|
||||
"partner_phone": "6789",
|
||||
},
|
||||
{
|
||||
"partner_name": "E",
|
||||
"linkedin_profile": "linkedin/b",
|
||||
},
|
||||
{
|
||||
"partner_name": "F",
|
||||
"email_from": "not_linked@example.com",
|
||||
"partner_phone": "00000",
|
||||
"linkedin_profile": "linkedin/not_linked",
|
||||
},
|
||||
{"partner_name": "G", "pool_applicant_id": t_B.id},
|
||||
]
|
||||
)
|
||||
self.assertEqual(t_A.talent_pool_count, 2)
|
||||
self.assertEqual(t_B.talent_pool_count, 1)
|
||||
self.assertEqual(A.talent_pool_count, 2)
|
||||
self.assertEqual(B.talent_pool_count, 2)
|
||||
self.assertEqual(C.talent_pool_count, 2)
|
||||
self.assertEqual(D.talent_pool_count, 2)
|
||||
self.assertEqual(E.talent_pool_count, 2)
|
||||
self.assertEqual(F.talent_pool_count, 0)
|
||||
self.assertEqual(G.talent_pool_count, 1)
|
||||
|
||||
def test_compute_and_search_is_applicant_in_pool(self):
|
||||
"""
|
||||
Test that the _compute_is_applicant_in_pool and _search_is_applicant_in_pool
|
||||
methods return correct information.
|
||||
An application is considered to be in a pool if it is either directly linked
|
||||
to a pool (through pool_applicant_id or talents_pool_ids) or shares a phone number,
|
||||
email or linkedin with another directly linked application.
|
||||
"""
|
||||
talent_pool = self.env["hr.talent.pool"].create({"name": "Cool Pool"})
|
||||
job = self.env["hr.job"].create(
|
||||
{
|
||||
"name": "Cool Job",
|
||||
}
|
||||
)
|
||||
A, B, C, D, E, F, G, H = self.env["hr.applicant"].create(
|
||||
[
|
||||
{
|
||||
"partner_name": "Talent A",
|
||||
"email_from": "mainTalentEmail@example.com",
|
||||
"talent_pool_ids": talent_pool.ids,
|
||||
},
|
||||
{
|
||||
"partner_name": "Applicant 1 B",
|
||||
"email_from": "otherTalentEmail@example.com",
|
||||
"partner_phone": "1234",
|
||||
"linkedin_profile": "linkedin.com/in/applicant",
|
||||
"job_id": job.id,
|
||||
},
|
||||
{
|
||||
"partner_name": "Applicant 1 C",
|
||||
"email_from": "otherTalentEmail@example.com",
|
||||
"job_id": job.id,
|
||||
},
|
||||
{
|
||||
"partner_name": "Applicant 1 D",
|
||||
"partner_phone": "1234",
|
||||
"job_id": job.id,
|
||||
},
|
||||
{
|
||||
"partner_name": "Applicant 1 E",
|
||||
"linkedin_profile": "linkedin.com/in/applicant",
|
||||
"job_id": job.id,
|
||||
},
|
||||
{
|
||||
"partner_name": "A different applicant F",
|
||||
"email_from": "differentEmail@example.com",
|
||||
"partner_phone": "9876",
|
||||
"linkedin_profile": "linkedin.com/in/NotAnApplicant",
|
||||
"job_id": job.id,
|
||||
},
|
||||
{
|
||||
"partner_name": "Talent With No information G",
|
||||
"talent_pool_ids": talent_pool.ids,
|
||||
},
|
||||
{
|
||||
"partner_name": "Applicant With No information H",
|
||||
},
|
||||
]
|
||||
)
|
||||
B.pool_applicant_id = A.id
|
||||
H.pool_applicant_id = G.id
|
||||
|
||||
# Testing the compute
|
||||
|
||||
# A is directly linked to Cool Pool through talent_pool_ids
|
||||
self.assertTrue(A.is_applicant_in_pool)
|
||||
# B is directly linked to Cool Pool through pool_applicant_id
|
||||
self.assertTrue(B.is_applicant_in_pool)
|
||||
# C is indirectly linked through email to B who is directly linked
|
||||
self.assertTrue(C.is_applicant_in_pool)
|
||||
# D is indirectly linked through phone to B who is directly linked
|
||||
self.assertTrue(D.is_applicant_in_pool)
|
||||
# E is indirectly linked through linkedin to B who is directly linked
|
||||
self.assertTrue(E.is_applicant_in_pool)
|
||||
# F is not linked to a Pool
|
||||
self.assertFalse(F.is_applicant_in_pool)
|
||||
# G is directly linked to Cool Pool through talent_pool_ids
|
||||
self.assertTrue(G.is_applicant_in_pool)
|
||||
# H is directly linked to Cool Pool through pool_applicant_id
|
||||
self.assertTrue(H.is_applicant_in_pool)
|
||||
|
||||
# Testing the search
|
||||
# Note: For some reason testing the search does not work if the compute
|
||||
# is not tested first which is why these two tests are in one test.
|
||||
applicant = self.env["hr.applicant"]
|
||||
in_pool_domain = applicant._search_is_applicant_in_pool("in", [True])
|
||||
in_pool_applicants = applicant.search(Domain.AND([in_pool_domain, [("company_id", "=", self.env.company.id)]]))
|
||||
out_of_pool_applicants = applicant.search(Domain.AND([~Domain(in_pool_domain), [("company_id", "=", self.env.company.id)]]))
|
||||
self.assertCountEqual(in_pool_applicants, A | B | C | D | E | G | H)
|
||||
self.assertCountEqual(out_of_pool_applicants, F)
|
||||
|
||||
def test_application_no_partner_duplicate(self):
|
||||
""" Test that when applying, the existing partner
|
||||
doesn't get duplicated.
|
||||
"""
|
||||
applicant_data = {
|
||||
'partner_name': 'Test',
|
||||
'email_from': 'test@thisisatest.com',
|
||||
}
|
||||
# First application, a partner should be created
|
||||
self.env['hr.applicant'].create(applicant_data)
|
||||
partner_count = self.env['res.partner'].search_count([('email', '=', 'test@thisisatest.com')])
|
||||
self.assertEqual(partner_count, 1)
|
||||
# Second application, no partner should be created
|
||||
self.env['hr.applicant'].create(applicant_data)
|
||||
partner_count = self.env['res.partner'].search_count([('email', '=', 'test@thisisatest.com')])
|
||||
self.assertEqual(partner_count, 1)
|
||||
|
||||
def test_target_on_application_hiring(self):
|
||||
"""
|
||||
Test that the target is updated when hiring an applicant
|
||||
"""
|
||||
job = self.env['hr.job'].create({
|
||||
'name': 'Test Job',
|
||||
'no_of_recruitment': 1,
|
||||
})
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'partner_name': 'Test Applicant',
|
||||
'job_id': job.id,
|
||||
})
|
||||
stage_new = self.env['hr.recruitment.stage'].create({
|
||||
'name': 'New',
|
||||
'sequence': 0,
|
||||
'hired_stage': False,
|
||||
})
|
||||
stage_hired = self.env['hr.recruitment.stage'].create({
|
||||
'name': 'Hired',
|
||||
'sequence': 1,
|
||||
'hired_stage': True,
|
||||
})
|
||||
self.assertEqual(job.no_of_recruitment, 1)
|
||||
applicant.stage_id = stage_hired
|
||||
self.assertEqual(job.no_of_recruitment, 0)
|
||||
|
||||
applicant.stage_id = stage_new
|
||||
self.assertEqual(job.no_of_recruitment, 1)
|
||||
|
||||
def test_open_refuse_applicant_wizard_without_partner_name(self):
|
||||
"""Test opening the refuse wizard when the applicant has no partner_name."""
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'partner_phone': '123',
|
||||
})
|
||||
wizard = Form(self.env['applicant.get.refuse.reason'].with_context(
|
||||
default_applicant_ids=[applicant.id], active_test=False))
|
||||
|
||||
wizard_applicant = wizard.applicant_ids[0]
|
||||
self.assertFalse(wizard_applicant.partner_name)
|
||||
|
||||
def test_applicant_refuse_reason(self):
|
||||
|
||||
refuse_reason = self.env['hr.applicant.refuse.reason'].create([{'name': 'Fired'}])
|
||||
|
||||
app_1, app_2 = self.env['hr.applicant'].create([
|
||||
{
|
||||
'partner_name': 'Laurie Poiret',
|
||||
'email_from': 'laurie.poiret@aol.ru',
|
||||
},
|
||||
{
|
||||
'partner_name': 'Mitchell Admin',
|
||||
'email_from': 'mitchell_admin@example.com',
|
||||
},
|
||||
])
|
||||
|
||||
applicant_get_refuse_reason = self.env['applicant.get.refuse.reason'].create([{
|
||||
'refuse_reason_id': refuse_reason.id,
|
||||
'applicant_ids': [app_1.id],
|
||||
'duplicates': True
|
||||
}])
|
||||
applicant_get_refuse_reason.action_refuse_reason_apply()
|
||||
self.assertFalse(self.env['hr.applicant'].search([('email_from', 'ilike', 'laurie.poiret@aol.ru')]))
|
||||
self.assertEqual(
|
||||
self.env['hr.applicant'].search([('email_from', 'ilike', 'mitchell_admin@example.com')]),
|
||||
app_2
|
||||
)
|
||||
|
||||
def test_applicant_refuse_mail_from_template(self):
|
||||
mail_template = self.env['mail.template'].create({
|
||||
'name': 'Test template',
|
||||
'model_id': self.env['ir.model']._get('hr.applicant').id,
|
||||
'email_from': 'test@test.test',
|
||||
})
|
||||
refuse_reason = self.env['hr.applicant.refuse.reason'].create({
|
||||
'name': 'Not good',
|
||||
})
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'partner_name': 'Laurie Poiret',
|
||||
'email_from': 'laurie.poiret@aol.ru',
|
||||
})
|
||||
applicant_get_refuse_reason = self.env['applicant.get.refuse.reason'].create([{
|
||||
'refuse_reason_id': refuse_reason.id,
|
||||
'applicant_ids': applicant.ids,
|
||||
'duplicates': True,
|
||||
}])
|
||||
mail_values = applicant_get_refuse_reason._prepare_mail_values(applicant)
|
||||
self.assertEqual(mail_values['email_from'], self.env.user.email_formatted)
|
||||
|
||||
refuse_reason_template = self.env['hr.applicant.refuse.reason'].create({
|
||||
'name': 'Fired',
|
||||
'template_id': mail_template.id,
|
||||
})
|
||||
applicant_get_refuse_reason.refuse_reason_id = refuse_reason_template
|
||||
mail_values = applicant_get_refuse_reason._prepare_mail_values(applicant)
|
||||
self.assertEqual(mail_values['email_from'], 'test@test.test')
|
||||
|
||||
def test_copy_attachments_while_creating_employee(self):
|
||||
"""
|
||||
Test that attachments are copied when creating an employee from an applicant
|
||||
"""
|
||||
applicant_1 = self.env['hr.applicant'].create({
|
||||
'partner_name': 'Applicant 1',
|
||||
'email_from': 'test_applicant@example.com'
|
||||
})
|
||||
applicant_attachment = self.Attachment.create({
|
||||
'datas': self.TEXT,
|
||||
'name': 'textFile.txt',
|
||||
'mimetype': 'text/plain',
|
||||
'res_model': applicant_1._name,
|
||||
'res_id': applicant_1.id
|
||||
})
|
||||
|
||||
employee_applicant = applicant_1.create_employee_from_applicant()
|
||||
self.assertTrue(employee_applicant['res_id'])
|
||||
attachment_employee_applicant = self.Attachment.search([
|
||||
('res_model', '=', employee_applicant['res_model']),
|
||||
('res_id', '=', employee_applicant['res_id']),
|
||||
])
|
||||
self.assertEqual(applicant_attachment['datas'], attachment_employee_applicant['datas'])
|
||||
|
||||
def test_other_applications_count(self):
|
||||
"""
|
||||
Test that the application_count field does not change
|
||||
when archiving or refusing a linked application.
|
||||
"""
|
||||
|
||||
A1, A2, A3 = self.env["hr.applicant"].create(
|
||||
[
|
||||
{"partner_name": "test", "email_from": "test@example.com"},
|
||||
{"partner_name": "test", "email_from": "test@example.com"},
|
||||
{"partner_name": "test", "email_from": "test@example.com"},
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(A1.application_count, 3)
|
||||
|
||||
# Archive A2
|
||||
A2.action_archive()
|
||||
self.assertEqual(
|
||||
A1.application_count,
|
||||
3,
|
||||
"Application_count should not change when archiving a linked application",
|
||||
)
|
||||
# Refuse A3
|
||||
refuse_reason = self.env["hr.applicant.refuse.reason"].create([{"name": "Fired"}])
|
||||
applicant_get_refuse_reason = self.env["applicant.get.refuse.reason"].create(
|
||||
[
|
||||
{
|
||||
"refuse_reason_id": refuse_reason.id,
|
||||
"applicant_ids": [A3.id],
|
||||
}
|
||||
]
|
||||
)
|
||||
applicant_get_refuse_reason.action_refuse_reason_apply()
|
||||
self.assertEqual(
|
||||
A1.application_count,
|
||||
3,
|
||||
"The other_applications_count should not change when refusing an application",
|
||||
)
|
||||
|
||||
def test_open_other_applications_count(self):
|
||||
"""
|
||||
The smart button labeled 'Other Applications N' (where N represents the number of
|
||||
other job applications linked to the same applicant) should, when clicked, open a list view
|
||||
displaying all related applications.
|
||||
|
||||
This list should include both the N other applications and the current one,
|
||||
resulting in a total of N + 1 records.
|
||||
"""
|
||||
|
||||
A1, _, _ = self.env["hr.applicant"].create(
|
||||
[
|
||||
{"partner_name": "test", "email_from": "test@example.com"},
|
||||
{"partner_name": "test", "email_from": "test@example.com"},
|
||||
{"partner_name": "test", "email_from": "test@example.com"},
|
||||
]
|
||||
)
|
||||
|
||||
res = A1.action_open_applications()
|
||||
self.assertEqual(len(res['domain'][0][2]), 3, "The list view should display 3 applications")
|
||||
|
||||
def test_applicant_modify_email_number(self):
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'partner_name': 'Mary Applicant',
|
||||
'email_from': 'applicant@example.com',
|
||||
'partner_phone': '123456789',
|
||||
})
|
||||
self.assertEqual(applicant.partner_id.email, 'applicant@example.com', "Email should have been set on the partner.")
|
||||
self.assertEqual(applicant.partner_id.phone, '123456789', "Phone should have been set on the partner.")
|
||||
|
||||
applicant.email_from = 'applicant_diff@example.com'
|
||||
self.assertEqual(applicant.partner_id.email, 'applicant_diff@example.com', "Email should have been updated on the partner.")
|
||||
applicant.partner_phone = '987654321'
|
||||
self.assertEqual(applicant.partner_id.phone, '987654321', "Phone should have been updated on the partner.")
|
||||
|
||||
def test_send_mail_when_refuse_applicant(self):
|
||||
mail_template = self.env['mail.template'].create({
|
||||
'name': 'Test template',
|
||||
'model_id': self.env['ir.model']._get('hr.applicant').id,
|
||||
'subject': 'Application refused: {{ object.partner_name }}',
|
||||
})
|
||||
|
||||
refuse_reason = self.env['hr.applicant.refuse.reason'].create([{
|
||||
'name': 'Not good',
|
||||
'template_id': mail_template.id,
|
||||
}])
|
||||
|
||||
app_1 = self.env['hr.applicant'].create({
|
||||
'partner_name': 'Mario',
|
||||
'email_from': 'super@mario.bros',
|
||||
})
|
||||
|
||||
applicant_get_refuse_reason = self.env['applicant.get.refuse.reason'].create({
|
||||
'refuse_reason_id': refuse_reason.id,
|
||||
'send_mail': True,
|
||||
'applicant_ids': [(6, 0, [app_1.id])],
|
||||
})
|
||||
applicant_get_refuse_reason._prepare_send_refusal_mails()
|
||||
mail = self.env['mail.mail'].search([('subject', '=', 'Application refused: Mario')], limit=1)
|
||||
self.assertEqual(mail.partner_ids, app_1.partner_id)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
from odoo.tests import tagged, TransactionCase
|
||||
|
||||
|
||||
@tagged('recruitment_allowed_user_ids')
|
||||
class TestRecruitmentAllowedUserIds(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.env = self.env(context=dict(self.env.context, tracking_disable=True))
|
||||
|
||||
self.company_a = self.env['res.company'].create({'name': 'Company A'})
|
||||
self.company_b = self.env['res.company'].create({'name': 'Company BBS'})
|
||||
|
||||
# Internal user in company A
|
||||
self.user_a = self.env['res.users'].create({
|
||||
'name': 'User A',
|
||||
'login': 'usera@test.com',
|
||||
'email': 'usera@test.com',
|
||||
'share': False,
|
||||
'company_ids': [self.company_a.id],
|
||||
'company_id': self.company_a.id,
|
||||
})
|
||||
|
||||
# Internal user in company B
|
||||
self.user_b = self.env['res.users'].create({
|
||||
'name': 'User B',
|
||||
'login': 'userb@test.com',
|
||||
'email': 'userb@test.com',
|
||||
'share': False,
|
||||
'company_ids': [self.company_b.id],
|
||||
'company_id': self.company_b.id,
|
||||
})
|
||||
|
||||
def test_recruiter_user_id_with_company(self):
|
||||
# Test job with company A - should allow user_a but not user_b
|
||||
job_a = self.env['hr.job'].create({
|
||||
'name': 'Job Position Company A',
|
||||
'company_id': self.company_a.id,
|
||||
})
|
||||
|
||||
# user_a can be set as a recruiter
|
||||
job_a.user_id = self.user_a
|
||||
self.assertEqual(job_a.user_id, self.user_a)
|
||||
|
||||
# Validate that the domain defined on hr.job.user_id contains user_a, but excludes user_b
|
||||
domain = [('share', '=', False), ('company_ids', '=?', self.company_a.id)]
|
||||
allowed_users = self.env['res.users'].search(domain)
|
||||
self.assertIn(self.user_a, allowed_users)
|
||||
self.assertNotIn(self.user_b, allowed_users)
|
||||
|
||||
def test_recruiter_user_id_without_company(self):
|
||||
# Test job without company - should allow both users
|
||||
job = self.env['hr.job'].create({
|
||||
'name': 'Job Position',
|
||||
'company_id': False,
|
||||
})
|
||||
|
||||
# When company_id is False, users from *any* company are ok
|
||||
domain = [('share', '=', False), ('company_ids', '=?', False)]
|
||||
allowed_users = self.env['res.users'].search(domain)
|
||||
self.assertIn(self.user_a, allowed_users)
|
||||
self.assertIn(self.user_b, allowed_users)
|
||||
|
||||
# Both users should be settable as recruiter
|
||||
job.user_id = self.user_a
|
||||
self.assertEqual(job.user_id, self.user_a)
|
||||
|
||||
job.user_id = self.user_b
|
||||
self.assertEqual(job.user_id, self.user_b)
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo import fields
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.tests.common import new_test_user
|
||||
|
||||
from odoo.addons.mail.tests.common import MailCommon
|
||||
from odoo.addons.mail.tests.common import MailCase
|
||||
from odoo.tests import tagged
|
||||
|
||||
class TestRecruitmentInterviewer(MailCommon):
|
||||
|
||||
@tagged('recruitment_interviewer')
|
||||
class TestRecruitmentInterviewer(MailCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
|
@ -34,55 +38,48 @@ class TestRecruitmentInterviewer(MailCommon):
|
|||
"""
|
||||
interviewer_group = self.env.ref('hr_recruitment.group_hr_recruitment_interviewer')
|
||||
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should not be interviewer")
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should not be interviewer")
|
||||
|
||||
self.job.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")
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should be added as interviewer")
|
||||
|
||||
self.job.write({'interviewer_ids': [(5, 0, 0)]})
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should be removed from interviewer")
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should be removed from interviewer")
|
||||
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.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")
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should be added as interviewer")
|
||||
|
||||
applicant.interviewer_ids = False
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should be removed from interviewer")
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should be removed from interviewer")
|
||||
|
||||
self.job.interviewer_ids = self.simple_user.ids
|
||||
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")
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should be added as interviewer")
|
||||
|
||||
applicant.interviewer_ids = False
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should stay interviewer")
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should stay interviewer")
|
||||
|
||||
self.job.write({'interviewer_ids': [(5, 0, 0)]})
|
||||
applicant.interviewer_ids = self.simple_user.ids
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.groups_id.ids, "Simple User should stay interviewer")
|
||||
self.assertTrue(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should stay interviewer")
|
||||
|
||||
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")
|
||||
self.assertFalse(interviewer_group.id in self.simple_user.all_group_ids.ids, "Simple User should be removed from interviewer")
|
||||
|
||||
def test_interviewer_access_rights(self):
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.id,
|
||||
})
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
applicant.with_user(self.interviewer_user).read()
|
||||
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.id,
|
||||
'interviewer_ids': self.interviewer_user.ids,
|
||||
|
|
@ -91,7 +88,6 @@ class TestRecruitmentInterviewer(MailCommon):
|
|||
|
||||
self.job.interviewer_ids = self.interviewer_user.ids
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'job_id': self.job.id,
|
||||
})
|
||||
|
|
@ -101,32 +97,60 @@ class TestRecruitmentInterviewer(MailCommon):
|
|||
applicant.with_user(self.interviewer_user).interviewer_ids = self.simple_user.ids
|
||||
self.assertEqual(self.simple_user, applicant.interviewer_ids)
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
with self.assertRaises(UserError):
|
||||
applicant.with_user(self.interviewer_user).create_employee_from_applicant()
|
||||
|
||||
def test_interviewer_chatter(self):
|
||||
self.manager_user.notification_type = 'email'
|
||||
self.interviewer_user.notification_type = 'email'
|
||||
def test_update_interviewer_for_multiple_applicants(self):
|
||||
"""
|
||||
Test that assigning interviewer to multiple applicants.
|
||||
"""
|
||||
interviewer_user_1 = new_test_user(self.env, 'sma',
|
||||
groups='base.group_user,hr_recruitment.group_hr_recruitment_interviewer',
|
||||
name='Recruitment Interviewer1', email='sma@example.com')
|
||||
|
||||
interviewer_user_2 = new_test_user(self.env, 'jab',
|
||||
groups='base.group_user,hr_recruitment.group_hr_recruitment_interviewer',
|
||||
name='Recruitment Interviewer2', email='jab@example.com')
|
||||
|
||||
interviewer_user_3 = new_test_user(self.env, 'aad',
|
||||
groups='base.group_user,hr_recruitment.group_hr_recruitment_interviewer',
|
||||
name='Recruitment Interviewer3', email='aad@example.com')
|
||||
|
||||
applicant = self.env['hr.applicant'].create({
|
||||
'name': 'toto',
|
||||
'partner_name': 'toto',
|
||||
'partner_name': 'Applicant',
|
||||
'job_id': self.job.id,
|
||||
'interviewer_ids': self.interviewer_user.ids,
|
||||
'interviewer_ids': [(6, 0, [interviewer_user_1.id])]
|
||||
})
|
||||
applicants = applicant + applicant.copy({'interviewer_ids': [(6, 0, [interviewer_user_2.id])]})
|
||||
|
||||
applicant.message_subscribe(partner_ids=[self.interviewer_user.partner_id.id])
|
||||
# update interviewer to multiple applicants.
|
||||
applicants.write({'interviewer_ids': [(4, interviewer_user_3.id)]})
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
message = applicant.message_post(body='A super secret message', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
# Ensure all interviewers are assigned
|
||||
self.assertCountEqual(
|
||||
applicants.interviewer_ids.ids, [interviewer_user_1.id, interviewer_user_2.id, interviewer_user_3.id]
|
||||
)
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
message.with_user(self.interviewer_user).read()
|
||||
# Checked that notification message is created
|
||||
message = self.env['mail.message'].search([('res_id', '=', applicant.id)], limit=1)
|
||||
self.assertEqual(message.subject, f"You have been assigned as an interviewer for {applicant.display_name}")
|
||||
|
||||
try:
|
||||
self._find_mail_mail_wpartners(self.interviewer_user.partner_id, None)
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('No mail.mail should be sent to members of Interviewer group')
|
||||
|
||||
self.assertSentEmail(self.env.user.partner_id, [self.manager_user.partner_id])
|
||||
def test_update_recruiter_for_ongoing_application(self):
|
||||
Application = self.env['hr.applicant']
|
||||
new_manager_user = new_test_user(self.env, 'thala',
|
||||
groups='base.group_user,hr_recruitment.group_hr_recruitment_manager',
|
||||
name='New Recruitment Manager', email='thala@example.com')
|
||||
ongoing_application = Application.create({
|
||||
'job_id': self.job.id,
|
||||
'user_id': self.manager_user.id,
|
||||
'application_status': 'ongoing',
|
||||
})
|
||||
hired_application = Application.create({
|
||||
'job_id': self.job.id,
|
||||
'user_id': self.manager_user.id,
|
||||
'date_closed': fields.Datetime.now(),
|
||||
'application_status': 'hired',
|
||||
})
|
||||
self.job.write({'user_id': new_manager_user.id})
|
||||
self.assertEqual(ongoing_application.user_id, new_manager_user)
|
||||
self.assertEqual(hired_application.user_id, self.manager_user)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo.addons.hr.tests.common import TestHrCommon
|
||||
from odoo.modules.module import get_module_resource
|
||||
from odoo.tools.misc import file_open
|
||||
|
||||
|
||||
class TestRecruitmentProcess(TestHrCommon):
|
||||
|
|
@ -11,35 +10,35 @@ class TestRecruitmentProcess(TestHrCommon):
|
|||
def test_00_recruitment_process(self):
|
||||
""" Test recruitment process """
|
||||
|
||||
self.dep_rd = self.env['hr.department'].create({
|
||||
dep_rd = self.env['hr.department'].create({
|
||||
'name': 'Research & Development',
|
||||
})
|
||||
self.job_developer = self.env['hr.job'].create({
|
||||
job_developer = self.env['hr.job'].create({
|
||||
'name': 'Experienced Developer',
|
||||
'department_id': self.dep_rd.id,
|
||||
'department_id': dep_rd.id,
|
||||
'no_of_recruitment': 5,
|
||||
})
|
||||
self.employee_niv = self.env['hr.employee'].create({
|
||||
employee_niv = self.env['hr.employee'].create({
|
||||
'name': 'Sharlene Rhodes',
|
||||
})
|
||||
self.job_developer = self.job_developer.with_user(self.res_users_hr_officer.id)
|
||||
self.employee_niv = self.employee_niv.with_user(self.res_users_hr_officer.id)
|
||||
job_developer = job_developer.with_user(self.res_users_hr_officer.id)
|
||||
employee_niv = employee_niv.with_user(self.res_users_hr_officer.id)
|
||||
|
||||
# Create a new HR Recruitment Officer
|
||||
self.res_users_hr_recruitment_officer = self.env['res.users'].create({
|
||||
res_users_hr_recruitment_officer = self.env['res.users'].create({
|
||||
'company_id': self.env.ref('base.main_company').id,
|
||||
'name': 'HR Recruitment Officer',
|
||||
'login': "hrro",
|
||||
'email': "hrofcr@yourcompany.com",
|
||||
'groups_id': [(6, 0, [self.env.ref('hr_recruitment.group_hr_recruitment_user').id])]
|
||||
'group_ids': [(6, 0, [self.env.ref('hr_recruitment.group_hr_recruitment_user').id])]
|
||||
})
|
||||
|
||||
# An applicant is interested in the job position. So he sends a resume by email.
|
||||
# In Order to test process of Recruitment so giving HR officer's rights
|
||||
with open(get_module_resource('hr_recruitment', 'tests', 'resume.eml'), 'rb') as request_file:
|
||||
with file_open('hr_recruitment/tests/resume.eml', 'rb') as request_file:
|
||||
request_message = request_file.read()
|
||||
self.env['mail.thread'].with_user(self.res_users_hr_recruitment_officer).message_process(
|
||||
'hr.applicant', request_message, custom_values={"job_id": self.job_developer.id})
|
||||
self.env['mail.thread'].with_user(res_users_hr_recruitment_officer).message_process(
|
||||
'hr.applicant', request_message, custom_values={"job_id": job_developer.id})
|
||||
|
||||
# After getting the mail, I check the details of the new applicant.
|
||||
applicant = self.env['hr.applicant'].search([('email_from', 'ilike', 'Richard_Anderson@yahoo.com')], limit=1)
|
||||
|
|
@ -48,15 +47,15 @@ class TestRecruitmentProcess(TestHrCommon):
|
|||
('name', '=', 'resume.pdf'),
|
||||
('res_model', '=', self.env['hr.applicant']._name),
|
||||
('res_id', '=', applicant.id)])
|
||||
self.assertEqual(applicant.name, 'Application for the post of Jr.application Programmer.', 'Applicant name does not match.')
|
||||
self.assertEqual(applicant.stage_id, self.env.ref('hr_recruitment.stage_job1'),
|
||||
"Stage should be 'Initial qualification' and is '%s'." % (applicant.stage_id.name))
|
||||
self.assertEqual(applicant.partner_name, 'Mr. Richard Anderson', 'Applicant name does not match.')
|
||||
self.assertEqual(applicant.stage_id, self.env.ref('hr_recruitment.stage_job0'),
|
||||
"Stage should be 'New' and is '%s'." % (applicant.stage_id.name))
|
||||
self.assertTrue(resume_ids, 'Resume is not attached.')
|
||||
# I assign the Job position to the applicant
|
||||
applicant.write({'job_id': self.job_developer.id})
|
||||
applicant.write({'job_id': job_developer.id})
|
||||
# I schedule meeting with applicant for interview.
|
||||
applicant_meeting = applicant.action_makeMeeting()
|
||||
self.assertEqual(applicant_meeting['context']['default_name'], 'Application for the post of Jr.application Programmer.',
|
||||
applicant_meeting = applicant.action_create_meeting()
|
||||
self.assertEqual(applicant_meeting['context']['default_name'], 'Mr. Richard Anderson',
|
||||
'Applicant name does not match.')
|
||||
|
||||
def test_01_hr_application_notification(self):
|
||||
|
|
@ -71,7 +70,7 @@ class TestRecruitmentProcess(TestHrCommon):
|
|||
"name": "user_1",
|
||||
"login": "user_1",
|
||||
"email": "user_1@example.com",
|
||||
"groups_id": [
|
||||
"group_ids": [
|
||||
(4, self.env.ref("hr.group_hr_manager").id),
|
||||
(4, self.env.ref("hr_recruitment.group_hr_recruitment_manager").id),
|
||||
],
|
||||
|
|
@ -88,7 +87,7 @@ class TestRecruitmentProcess(TestHrCommon):
|
|||
}
|
||||
)
|
||||
application = self.env["hr.applicant"].create(
|
||||
{"name": "Test Job Application for Notification", "job_id": job.id}
|
||||
{"partner_name": "Test Job Application for Notification", "job_id": job.id}
|
||||
)
|
||||
new_application_message = application.message_ids.filtered(
|
||||
lambda m: m.subtype_id == new_application_mt
|
||||
|
|
@ -96,3 +95,95 @@ class TestRecruitmentProcess(TestHrCommon):
|
|||
self.assertTrue(
|
||||
user.partner_id in new_application_message.notified_partner_ids
|
||||
)
|
||||
|
||||
def test_job_platforms(self):
|
||||
self.env['hr.job.platform'].create({
|
||||
'name': 'YourJobPlatform',
|
||||
'email': 'yourjobplatform@platform.com',
|
||||
'regex': '^New application:.*from (.*)'
|
||||
})
|
||||
# Regex applied on Subject
|
||||
applicant = self.env['hr.applicant'].message_new({
|
||||
'message_id': 'message_id_for_rec',
|
||||
'email_from': '"Job Platform Application" <yourjobplatform@platform.com>',
|
||||
'from': '"Job Platform Application" <yourjobplatform@platform.com>',
|
||||
'subject': 'New application: ERP Implementation Consultant from John Doe',
|
||||
'body': 'I want to apply to your company',
|
||||
})
|
||||
|
||||
# Regex applied on Body
|
||||
applicant2 = self.env['hr.applicant'].message_new({
|
||||
'message_id': 'message_id_for_rec',
|
||||
'email_from': '"Job Platform Application" <yourjobplatform@platform.com>',
|
||||
'from': '"Job Platform Application" <yourjobplatform@platform.com>',
|
||||
'subject': 'Very badly formatted subject :D',
|
||||
'body': 'New application: ERP Implementation Consultant from John Doe',
|
||||
})
|
||||
|
||||
self.assertEqual(applicant.partner_name, 'John Doe')
|
||||
self.assertFalse(applicant.email_from)
|
||||
|
||||
self.assertEqual(applicant2.partner_name, 'John Doe')
|
||||
self.assertFalse(applicant2.email_from)
|
||||
|
||||
def test_email_application_multi_company(self):
|
||||
""" Make sure that receiving emails for jobs in companies different from self.env.company work. """
|
||||
other_company = self.env['res.company'].create({'name': 'Other Company'})
|
||||
job_developer = self.env['hr.job'].create({
|
||||
'name': 'Experienced Developer (Other Company)',
|
||||
'company_id': other_company.id,
|
||||
})
|
||||
|
||||
with file_open('hr_recruitment/tests/resume.eml', 'rb') as request_file:
|
||||
request_message = request_file.read()
|
||||
self.env['mail.thread'].message_process('hr.applicant', request_message, custom_values={"job_id": job_developer.id})
|
||||
|
||||
# Make sure the applicant are created in the right company
|
||||
applicant = self.env['hr.applicant'].search([('email_from', 'ilike', 'Richard_Anderson@yahoo.com')], limit=1)
|
||||
self.assertEqual(applicant.company_id, other_company, 'Applicant should be created in the right company')
|
||||
|
||||
def test_email_application_department_with_no_company(self):
|
||||
"""
|
||||
Test that applicants created from incoming emails are assigned the job's company when the job's department
|
||||
has no company set.
|
||||
"""
|
||||
mystery_company = self.env["res.company"].create({"name": "Mystery Company"})
|
||||
_mail_alias_domain = self.env["mail.alias.domain"].create(
|
||||
{
|
||||
"company_ids": [(4, mystery_company.id)],
|
||||
"name": "test.example.com",
|
||||
}
|
||||
)
|
||||
alias = self.env["mail.alias"].create(
|
||||
{
|
||||
"alias_domain_id": mystery_company.alias_domain_id.id,
|
||||
"alias_name": "mystery_job",
|
||||
"alias_model_id": self.env["ir.model"]._get_id("hr.job"),
|
||||
}
|
||||
)
|
||||
|
||||
mystery_department = self.env["hr.department"].create({"name": "Mystery Department", "company_id": False})
|
||||
mystery_job = self.env["hr.job"].create(
|
||||
{
|
||||
"name": "Mystery Job",
|
||||
"company_id": mystery_company.id,
|
||||
"department_id": mystery_department.id,
|
||||
"alias_id": alias.id,
|
||||
}
|
||||
)
|
||||
|
||||
email = f"""MIME-Version: 1.0
|
||||
Message-ID: <verymysteriousapplication>
|
||||
From: Applicant <test@example.com>
|
||||
To: {mystery_job.alias_id.display_name}
|
||||
Subject: Job Application
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
I want to work for you!"""
|
||||
|
||||
applicant_from_email = self.env["mail.thread"].message_process("hr.applicant", email)
|
||||
applicant = self.env["hr.applicant"].browse(applicant_from_email)
|
||||
self.assertEqual(
|
||||
applicant.department_id, mystery_department, "Applicant should be assigned to the right department"
|
||||
)
|
||||
self.assertEqual(applicant.company_id, mystery_company, "Applicant should be created in the right company")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,339 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.fields import Domain
|
||||
from odoo.tests import Form, tagged, TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
@tagged("recruitment")
|
||||
class TestRecruitmentTalentPool(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.t_talent_pool_1, cls.t_talent_pool_2 = cls.env["hr.talent.pool"].create(
|
||||
[{"name": "Test Talent Pool 1"}, {"name": "Test Talent Pool 2"}]
|
||||
)
|
||||
|
||||
cls.t_applicant_1, cls.t_applicant_2 = cls.env["hr.applicant"].create(
|
||||
[{"partner_name": "Test Applicant 1"}, {"partner_name": "Test Applicant 2"}]
|
||||
)
|
||||
|
||||
cls.t_job_1, cls.t_job_2, cls.t_job_3 = cls.env["hr.job"].create(
|
||||
[
|
||||
{"name": "Job 1"},
|
||||
{"name": "Job 2"},
|
||||
{"name": "Job 3"},
|
||||
]
|
||||
)
|
||||
cls.mail_template = cls.env['mail.template'].create({
|
||||
'name': 'Test stage template',
|
||||
'model_id': cls.env['ir.model']._get_id('hr.applicant'),
|
||||
'subject': 'Job application test',
|
||||
})
|
||||
|
||||
def test_add_applicant_to_one_talent_pool(self):
|
||||
"""
|
||||
Test that a applicant is duplicated and linked to a pool when creating a talent.
|
||||
"""
|
||||
talent_pool_applicant = self.t_talent_pool_1.talent_ids
|
||||
self.assertFalse(talent_pool_applicant, "There should not be any applicants in the talent pool")
|
||||
|
||||
wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
wizard.talent_pool_ids = self.t_talent_pool_1
|
||||
wizard.applicant_ids = self.t_applicant_1
|
||||
talent_pool_applicant = wizard.save()._add_applicants_to_pool()
|
||||
|
||||
self.assertTrue(
|
||||
talent_pool_applicant, "An applicant('talent') should be created when adding an applicant to a pool"
|
||||
)
|
||||
self.assertNotEqual(
|
||||
self.t_applicant_1, talent_pool_applicant, "The 'talent' and the applicant should be two different records"
|
||||
)
|
||||
self.assertEqual(
|
||||
talent_pool_applicant.talent_pool_ids,
|
||||
self.t_talent_pool_1,
|
||||
"The talent should be linked to the talent pool",
|
||||
)
|
||||
|
||||
def test_create_talent_in_pool(self):
|
||||
talent = self.env["hr.applicant"].with_context(default_talent_pool_ids=self.t_talent_pool_1.ids).create({
|
||||
'partner_name': 'Talent in a pool',
|
||||
})
|
||||
|
||||
self.assertEqual(talent.talent_pool_ids, self.t_talent_pool_1)
|
||||
self.assertEqual(talent.pool_applicant_id, talent)
|
||||
|
||||
job_wizard = Form(self.env["job.add.applicants"].with_context({"default_applicant_ids": talent.ids}))
|
||||
job_wizard.job_ids = self.t_job_1
|
||||
job_1_applicant = job_wizard.save()._add_applicants_to_job()
|
||||
|
||||
self.assertEqual(job_1_applicant.pool_applicant_id, talent)
|
||||
|
||||
def test_add_applicant_to_multiple_talent_pools(self):
|
||||
"""
|
||||
Test that a applicant is only duplicated once and linked to multiple pools when creating a talent.
|
||||
"""
|
||||
wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
wizard.talent_pool_ids.add(self.t_talent_pool_1)
|
||||
wizard.talent_pool_ids.add(self.t_talent_pool_2)
|
||||
wizard.applicant_ids = self.t_applicant_1
|
||||
talent_pool_applicant = wizard.save()._add_applicants_to_pool()
|
||||
|
||||
self.assertTrue(
|
||||
talent_pool_applicant, "An applicant('talent') should be created when adding an applicant to a pool"
|
||||
)
|
||||
self.assertEqual(
|
||||
len(talent_pool_applicant), 1, "Exactly one 'talent' should be created when adding an applicant to a pool"
|
||||
)
|
||||
self.assertEqual(
|
||||
talent_pool_applicant.talent_pool_ids,
|
||||
self.t_talent_pool_1 | self.t_talent_pool_2,
|
||||
"The 'talent' should belong to both talent pools",
|
||||
)
|
||||
|
||||
def test_add_multiple_applicants_to_multiple_talent_pools(self):
|
||||
"""
|
||||
Test that multiple applicants are only duplicated once and linked to multiple pools when creating talents.
|
||||
"""
|
||||
talent_pool_applicants = self.t_talent_pool_1.talent_ids | self.t_talent_pool_2.talent_ids
|
||||
self.assertFalse(talent_pool_applicants, "There should not be any applicants in the talent pools")
|
||||
|
||||
with Form(self.env["talent.pool.add.applicants"]) as wizard:
|
||||
wizard.talent_pool_ids.add(self.t_talent_pool_1)
|
||||
wizard.talent_pool_ids.add(self.t_talent_pool_2)
|
||||
wizard.applicant_ids.add(self.t_applicant_1)
|
||||
wizard.applicant_ids.add(self.t_applicant_2)
|
||||
|
||||
talent_pool_applicants = wizard.record._add_applicants_to_pool()
|
||||
|
||||
self.assertTrue(
|
||||
talent_pool_applicants, "An applicant('talent') should be created when adding an applicant to a pool"
|
||||
)
|
||||
self.assertEqual(
|
||||
len(talent_pool_applicants),
|
||||
2,
|
||||
"Exactly two 'talents' should be created when adding two applicants to pools",
|
||||
)
|
||||
for applicant in talent_pool_applicants:
|
||||
self.assertEqual(
|
||||
applicant.talent_pool_ids,
|
||||
self.t_talent_pool_1 | self.t_talent_pool_2,
|
||||
f"Talent {applicant.partner_name} should belong to two talent pools",
|
||||
)
|
||||
|
||||
def test_add_applicant_is_only_duplicated_once(self):
|
||||
"""
|
||||
Test that a talent is not duplicated when added to two different pools in two different steps.
|
||||
"""
|
||||
with Form(self.env["talent.pool.add.applicants"]) as wizard:
|
||||
wizard.talent_pool_ids = self.t_talent_pool_1
|
||||
wizard.applicant_ids = self.t_applicant_1
|
||||
tp_applicant_1 = wizard.record._add_applicants_to_pool()
|
||||
|
||||
self.assertTrue(tp_applicant_1, "An applicant('talent') should be created when adding an applicant to a pool")
|
||||
self.assertEqual(
|
||||
len(tp_applicant_1), 1, "Exactly one 'talent' should be created when adding an applicant to a pool"
|
||||
)
|
||||
|
||||
# Try adding the same applicant to a different pool
|
||||
# This is impossible through the UI as there is a domain on the
|
||||
# `applicant_ids` field.
|
||||
wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
wizard.talent_pool_ids = self.t_talent_pool_2
|
||||
wizard.applicant_ids = self.t_applicant_1
|
||||
tp_applicant_2 = wizard.save()._add_applicants_to_pool()
|
||||
self.assertFalse(tp_applicant_2, "A second talent for the same applicant should not have been created")
|
||||
|
||||
wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
wizard.talent_pool_ids = self.t_talent_pool_2
|
||||
wizard.applicant_ids = tp_applicant_1
|
||||
tp_applicant_2 = wizard.save()._add_applicants_to_pool()
|
||||
|
||||
self.assertEqual(tp_applicant_1, tp_applicant_2, "tp_applicant_1 and tp_applicant_2 should be the same record")
|
||||
self.assertEqual(
|
||||
tp_applicant_1.talent_pool_ids,
|
||||
self.t_talent_pool_1 | self.t_talent_pool_2,
|
||||
f"tp_applicant_1 should be linked to {self.t_talent_pool_1.name} and {self.t_talent_pool_2.name}",
|
||||
)
|
||||
|
||||
def test_tags_are_added_to_talent(self):
|
||||
"""
|
||||
Test that a tag is added to the talent but not the applicant when creating talents.
|
||||
"""
|
||||
tag = self.env["hr.applicant.category"].create({"name": "Test Tag"})
|
||||
|
||||
talent_pool_applicant = (
|
||||
self.env["talent.pool.add.applicants"]
|
||||
.create(
|
||||
{
|
||||
"applicant_ids": self.t_applicant_1,
|
||||
"talent_pool_ids": self.t_talent_pool_1,
|
||||
"categ_ids": tag,
|
||||
}
|
||||
)
|
||||
._add_applicants_to_pool()
|
||||
)
|
||||
self.assertTrue(talent_pool_applicant, "A 'talent' should have been created")
|
||||
self.assertFalse(self.t_applicant_1.categ_ids, "The original applicant should not have any linked tags")
|
||||
self.assertEqual(
|
||||
talent_pool_applicant.categ_ids, tag, "The 'talent' should have the tag 'Test Tag' linked to it"
|
||||
)
|
||||
|
||||
def test_add_talent_to_one_job(self):
|
||||
"""
|
||||
Test that a talent is duplicated when added to a job
|
||||
"""
|
||||
pool_wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
pool_wizard.talent_pool_ids = self.t_talent_pool_1
|
||||
pool_wizard.applicant_ids = self.t_applicant_1
|
||||
talent_pool_applicant = pool_wizard.save()._add_applicants_to_pool()
|
||||
|
||||
recuritment_stage = self.env["hr.recruitment.stage"].create({
|
||||
"name": "Recruitment Stage",
|
||||
"job_ids": self.t_job_2.ids,
|
||||
"template_id": self.mail_template.id,
|
||||
"sequence": 0,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
len(talent_pool_applicant), 1, "Exactly one 'talent' should be created when adding an applicant to a pool"
|
||||
)
|
||||
|
||||
job_wizard = Form(
|
||||
self.env["job.add.applicants"].with_context({"default_applicant_ids": talent_pool_applicant.ids})
|
||||
)
|
||||
job_wizard.job_ids = self.t_job_2
|
||||
job_2_applicant = job_wizard.save()._add_applicants_to_job()
|
||||
self.flush_tracking()
|
||||
|
||||
all_applications = self.env["hr.applicant"].search(Domain("partner_name", "=", "Test Applicant 1"))
|
||||
self.assertEqual(
|
||||
len(all_applications),
|
||||
3,
|
||||
"""There should be three applications with the name 'Test Applicant 1' - The original, the talent and the one created through the job.add.applicants wizard""",
|
||||
)
|
||||
self.assertEqual(
|
||||
job_2_applicant,
|
||||
self.t_job_2.application_ids,
|
||||
"Job_2_applicant, created through the wizard, should be linked to Job 2",
|
||||
)
|
||||
self.assertNotEqual(
|
||||
job_2_applicant, talent_pool_applicant, "Job_2_applicant and the talent should not be the same record"
|
||||
)
|
||||
|
||||
# Make sure that the stage was populated correctly during creation not in compute,
|
||||
# If it was passed in creation the record will have the mail linked to the stage
|
||||
self.assertEqual(job_2_applicant.stage_id, recuritment_stage)
|
||||
self.assertEqual(job_2_applicant.message_ids[0].subject, self.mail_template.subject)
|
||||
|
||||
def test_add_talent_to_multiple_jobs(self):
|
||||
"""
|
||||
Test that a talent is duplicated multiple times when added to multiple jobs.
|
||||
"""
|
||||
pool_wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
pool_wizard.talent_pool_ids = self.t_talent_pool_1
|
||||
pool_wizard.applicant_ids = self.t_applicant_1
|
||||
pool_wizard.save().action_add_applicants_to_pool()
|
||||
|
||||
talent_pool_applicant = self.t_talent_pool_1.talent_ids
|
||||
self.assertEqual(
|
||||
len(talent_pool_applicant), 1, "Exactly one 'talent' should be created when adding an applicant to a pool"
|
||||
)
|
||||
|
||||
job_wizard = Form(
|
||||
self.env["job.add.applicants"].with_context({"default_applicant_ids": talent_pool_applicant.ids})
|
||||
)
|
||||
job_wizard.job_ids.add(self.t_job_2)
|
||||
job_wizard.job_ids.add(self.t_job_3)
|
||||
new_job_applicants = job_wizard.save()._add_applicants_to_job()
|
||||
|
||||
all_applications = self.env["hr.applicant"].search(Domain("partner_name", "=", "Test Applicant 1"))
|
||||
self.assertEqual(
|
||||
len(all_applications),
|
||||
4,
|
||||
"""There should be four applications with the name 'Test Applicant' - The original, the talent and one each for job_2 and job_3, created through the wizard""",
|
||||
)
|
||||
self.assertEqual(
|
||||
new_job_applicants.job_id,
|
||||
self.t_job_2 | self.t_job_3,
|
||||
"new_job_applicants, created through the wizard, should be linked to Job 2 and Job 3",
|
||||
)
|
||||
|
||||
def test_add_multiple_talents_to_multiple_jobs(self):
|
||||
"""
|
||||
Test that multiple talents are duplicated multiple times when added to multiple jobs.
|
||||
"""
|
||||
pool_wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
pool_wizard.talent_pool_ids = self.t_talent_pool_1
|
||||
pool_wizard.applicant_ids.add(self.t_applicant_1)
|
||||
pool_wizard.applicant_ids.add(self.t_applicant_2)
|
||||
talent_pool_applicants = pool_wizard.save()._add_applicants_to_pool()
|
||||
self.assertEqual(
|
||||
len(talent_pool_applicants), 2, "Exactly two 'talents' should be created when adding an applicant to a pool"
|
||||
)
|
||||
|
||||
job_wizard = Form(
|
||||
self.env["job.add.applicants"].with_context({"default_applicant_ids": talent_pool_applicants.ids})
|
||||
)
|
||||
job_wizard.job_ids.add(self.t_job_2)
|
||||
job_wizard.job_ids.add(self.t_job_3)
|
||||
new_job_applicants = job_wizard.save()._add_applicants_to_job()
|
||||
|
||||
all_a_1_applications = self.env["hr.applicant"].search(Domain("partner_name", "=", "Test Applicant 1"))
|
||||
all_a_2_applications = self.env["hr.applicant"].search(Domain("partner_name", "=", "Test Applicant 2"))
|
||||
self.assertEqual(
|
||||
len(all_a_1_applications),
|
||||
4,
|
||||
"""There should be four applications with the name 'Test Applicant 1' - The original, the talent and one each for job_2 and job_3, created through the wizard""",
|
||||
)
|
||||
self.assertEqual(
|
||||
len(all_a_2_applications),
|
||||
4,
|
||||
"""There should be four applications with the name 'Test Applicant 2' - The original, the talent and one each for job_2 and job_3, created through the wizard""",
|
||||
)
|
||||
new_job_applicants = new_job_applicants.mapped(lambda a: {"name": a.partner_name, "job": a.job_id})
|
||||
expected = [
|
||||
{"name": "Test Applicant 1", "job": self.t_job_2},
|
||||
{"name": "Test Applicant 1", "job": self.t_job_3},
|
||||
{"name": "Test Applicant 2", "job": self.t_job_2},
|
||||
{"name": "Test Applicant 2", "job": self.t_job_3},
|
||||
]
|
||||
self.assertEqual(
|
||||
new_job_applicants,
|
||||
expected,
|
||||
"new_job_applicants, created through the wizard, should be linked to Job 2 and Job 3",
|
||||
)
|
||||
|
||||
def test_update_applicant_after_adding_to_pool(self):
|
||||
"""
|
||||
Test that an applicant's fields (e.g., email) can still be updated after adding them to a talent pool.
|
||||
"""
|
||||
self.env["talent.pool.add.applicants"].create({
|
||||
"applicant_ids": self.t_applicant_1.ids,
|
||||
"talent_pool_ids": self.t_talent_pool_1,
|
||||
}).action_add_applicants_to_pool()
|
||||
|
||||
new_email = "updated@gmail.com"
|
||||
self.t_applicant_1.write({"email_from": new_email})
|
||||
self.assertEqual(
|
||||
self.t_applicant_1.email_from,
|
||||
new_email,
|
||||
"The email_from field should be updated successfully",
|
||||
)
|
||||
|
||||
def test_talent_must_have_at_least_one_pool(self):
|
||||
"""
|
||||
Ensure that removing all the talent pool from a talent created raises a ValidationError.
|
||||
"""
|
||||
wizard = Form(self.env["talent.pool.add.applicants"])
|
||||
wizard.talent_pool_ids.add(self.t_talent_pool_1)
|
||||
wizard.applicant_ids.add(self.t_applicant_1)
|
||||
talent = wizard.save()._add_applicants_to_pool()
|
||||
with self.assertRaises(ValidationError):
|
||||
talent.write({"talent_pool_ids": [(5, 0, 0)]})
|
||||
|
||||
def flush_tracking(self):
|
||||
""" Force the creation of tracking values. """
|
||||
self.env.flush_all()
|
||||
self.cr.flush()
|
||||
|
|
@ -2,24 +2,20 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.utm.tests.common import TestUTMCommon
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.tests.common import new_test_user, tagged, users
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import tagged, users
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'utm_consistency')
|
||||
class TestUTMConsistencyHrRecruitment(TestUTMCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.hr_recruitment_source = cls.env['hr.recruitment.source'].create({
|
||||
'name': 'Recruitment Source'
|
||||
})
|
||||
|
||||
@users('__system__')
|
||||
def test_utm_consistency(self):
|
||||
hr_recruitment_source = self.env['hr.recruitment.source'].create({
|
||||
'name': 'Recruitment Source'
|
||||
})
|
||||
# the source is automatically created when creating a recruitment source
|
||||
utm_source = self.hr_recruitment_source.source_id
|
||||
utm_source = hr_recruitment_source.source_id
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
# can't unlink the source as it's used by a mailing.mailing as its source
|
||||
|
|
@ -30,24 +26,3 @@ class TestUTMConsistencyHrRecruitment(TestUTMCommon):
|
|||
# the creation of the alias of the recruitment source
|
||||
with self.assertRaises(UserError):
|
||||
self.env.ref('hr_recruitment.utm_campaign_job').unlink()
|
||||
|
||||
def test_create_alias(self):
|
||||
"""This ensures that users who are not recruitment officers are not allowed to
|
||||
create a mail alias for the recruiting source while who are recruitment officers are
|
||||
"""
|
||||
simple_user = new_test_user(self.env, 'smp',
|
||||
groups='base.group_user', name='Simple User', email='smp@example.com')
|
||||
interviewer_user = new_test_user(self.env, 'itw',
|
||||
groups='base.group_user,hr_recruitment.group_hr_recruitment_interviewer',
|
||||
name='Recruitment Interviewer', email='itw@example.com')
|
||||
recruitment_officer_user = new_test_user(self.env, 'rec_off',
|
||||
groups='base.group_user,hr_recruitment.group_hr_recruitment_user',
|
||||
name='Recruitment Officer', email='rec_off@example.com')
|
||||
with self.assertRaises(AccessError):
|
||||
self.hr_recruitment_source.with_user(simple_user).create_alias()
|
||||
with self.assertRaises(AccessError):
|
||||
self.hr_recruitment_source.with_user(interviewer_user).create_alias()
|
||||
try:
|
||||
self.hr_recruitment_source.with_user(recruitment_officer_user).create_alias()
|
||||
except AccessError:
|
||||
self.fail("Recruitment Officer should be able to create mail alias for hr.recruitment.source.")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue