19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:00 +01:00
parent a1137a1456
commit e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions

View file

@ -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)