452 lines
20 KiB
Python
452 lines
20 KiB
Python
from freezegun import freeze_time
|
|
import base64
|
|
import json
|
|
from odoo import fields
|
|
from odoo.addons.mail.tests.common import mail_new_test_user
|
|
from odoo.tools import file_open
|
|
from odoo.tests import Form, HttpCase, tagged
|
|
|
|
|
|
@tagged('-at_install', 'post_install', 'salary')
|
|
class TestSalaryConfiguratorForApplicant(HttpCase):
|
|
@classmethod
|
|
@freeze_time('2022-01-01 09:00:00')
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
demo = mail_new_test_user(
|
|
cls.env,
|
|
email='[email protected]',
|
|
groups='hr.group_hr_user,sign.group_sign_user',
|
|
login='be_demo',
|
|
name="Laurie Poiret",
|
|
)
|
|
with file_open('hr_contract_salary/static/src/demo/employee_contract.pdf', "rb") as f:
|
|
cls.pdf_content = f.read()
|
|
|
|
attachment = cls.env['ir.attachment'].create({
|
|
'type': 'binary',
|
|
'raw': cls.pdf_content,
|
|
'name': 'test_employee_contract.pdf',
|
|
})
|
|
|
|
cls.template = cls.env['sign.template'].create({})
|
|
|
|
cls.document_id = cls.env['sign.document'].create({
|
|
'attachment_id': attachment.id,
|
|
'template_id': cls.template.id,
|
|
})
|
|
|
|
cls.env['sign.item'].create([
|
|
{
|
|
'type_id': cls.env.ref('sign.sign_item_type_text').id,
|
|
'name': 'employee_id.name',
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 1,
|
|
'posX': 0.273,
|
|
'posY': 0.158,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.150,
|
|
'height': 0.015,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_date').id,
|
|
'name': False,
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 1,
|
|
'posX': 0.707,
|
|
'posY': 0.158,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.150,
|
|
'height': 0.015,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_text').id,
|
|
'name': 'private_city',
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 1,
|
|
'posX': 0.506,
|
|
'posY': 0.184,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.150,
|
|
'height': 0.015,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_text').id,
|
|
'name': 'private_country_id.name',
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 1,
|
|
'posX': 0.663,
|
|
'posY': 0.184,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.150,
|
|
'height': 0.015,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_text').id,
|
|
'name': 'private_street',
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 1,
|
|
'posX': 0.349,
|
|
'posY': 0.184,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.150,
|
|
'height': 0.015,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_signature').id,
|
|
'name': False,
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_job_responsible').id,
|
|
'page': 2,
|
|
'posX': 0.333,
|
|
'posY': 0.575,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.200,
|
|
'height': 0.050,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_signature').id,
|
|
'name': False,
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 2,
|
|
'posX': 0.333,
|
|
'posY': 0.665,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.200,
|
|
'height': 0.050,
|
|
}, {
|
|
'type_id': cls.env.ref('sign.sign_item_type_date').id,
|
|
'name': False,
|
|
'required': True,
|
|
'responsible_id': cls.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
'page': 2,
|
|
'posX': 0.665,
|
|
'posY': 0.694,
|
|
'document_id': cls.document_id.id,
|
|
'width': 0.150,
|
|
'height': 0.015,
|
|
}
|
|
])
|
|
|
|
cls.company_id = cls.env['res.company'].create({
|
|
'name': 'My Belgian Company - TEST',
|
|
'country_id': cls.env.ref('base.be').id,
|
|
})
|
|
partner_id = cls.env['res.partner'].create({
|
|
'name': 'Laurie Poiret',
|
|
'street': '58 rue des Wallons',
|
|
'city': 'Louvain-la-Neuve',
|
|
'zip': '1348',
|
|
'country_id': cls.env.ref("base.be").id,
|
|
'phone': '+0032476543210',
|
|
'email': '[email protected]',
|
|
'company_id': cls.company_id.id,
|
|
})
|
|
|
|
with file_open('sign/static/demo/signature.png', "rb") as f:
|
|
img_content = base64.b64encode(f.read())
|
|
|
|
cls.env.ref('base.user_admin').write({
|
|
'company_ids': [(4, cls.company_id.id)],
|
|
'company_id': cls.company_id.id,
|
|
'name': 'Mitchell Admin',
|
|
'sign_signature': img_content,
|
|
})
|
|
cls.env.ref('base.user_admin').partner_id.write({
|
|
'email': '[email protected]',
|
|
'name': 'Mitchell Admin',
|
|
'street': '215 Vine St',
|
|
'city': 'Scranton',
|
|
'zip': '18503',
|
|
'country_id': cls.env.ref('base.us').id,
|
|
'state_id': cls.env.ref('base.state_us_39').id,
|
|
'phone': '+1 555-555-5555',
|
|
'tz': 'Europe/Brussels',
|
|
'company_id': cls.company_id.id,
|
|
})
|
|
demo.write({
|
|
'partner_id': partner_id,
|
|
'company_id': cls.company_id.id,
|
|
'company_ids': [(4, cls.company_id.id)]
|
|
})
|
|
cls.env.ref('base.main_partner').email = "[email protected]"
|
|
|
|
cls.new_dev_contract = cls.env['hr.version'].create({
|
|
'name': 'New Developer Template Contract',
|
|
'wage': 3000,
|
|
'structure_type_id': cls.env.ref('hr.structure_type_employee').id,
|
|
'sign_template_id': cls.template.id,
|
|
'contract_update_template_id': cls.template.id,
|
|
'hr_responsible_id': cls.env.ref('base.user_admin').id,
|
|
})
|
|
|
|
cls.senior_dev_contract = cls.env['hr.version'].create({
|
|
'name': 'Senior Developer Template Contract',
|
|
'wage': 6000,
|
|
'structure_type_id': cls.env.ref('hr.structure_type_employee').id,
|
|
'sign_template_id': cls.template.id,
|
|
'contract_update_template_id': cls.template.id,
|
|
'hr_responsible_id': cls.env.ref('base.user_admin').id,
|
|
'company_id': cls.company_id.id,
|
|
})
|
|
|
|
cls.senior_dev_job = cls.env['hr.job'].create({
|
|
'name': 'Senior Developer BE',
|
|
'company_id': cls.company_id.id,
|
|
'contract_template_id': cls.senior_dev_contract.id,
|
|
})
|
|
|
|
def test_applicant_salary_configurator_flow(self):
|
|
with freeze_time("2022-01-01 12:00:00"):
|
|
self.start_tour("/", 'hr_contract_salary_applicant_flow_tour', login='admin', timeout=350)
|
|
employee = self.env['hr.employee'].search([('name', 'ilike', 'Mitchell Admin 3'), ('active', '=', False)])
|
|
active_versions = self.env['hr.version'].search([('employee_id', '=', employee.id), ('active', '=', True)])
|
|
archived_versions = self.env['hr.version'].search([('employee_id', '=', employee.id), ('active', '=', False)])
|
|
self.assertEqual(len(active_versions), 1, "Exactly one active version should have been created.")
|
|
self.assertEqual(len(archived_versions), 1, "Exactly one archived version should have been created.")
|
|
self.assertEqual(active_versions.wage, 0, "The active version should be a dummy version")
|
|
self.assertEqual(archived_versions.wage, 6000.0, "The archived version should be the one from the offer")
|
|
|
|
self.assertTrue(employee, 'An employee has been created')
|
|
self.assertFalse(employee.active, 'Employee is not active')
|
|
|
|
with freeze_time("2022-01-01 13:00:00"):
|
|
self.start_tour("/", 'hr_contract_salary_applicant_flow_tour_counter_sign', login='admin', timeout=350)
|
|
employee = self.env['hr.employee'].search([('name', 'ilike', 'Mitchell Admin 3')])
|
|
active_versions = self.env['hr.version'].search([('employee_id', '=', employee.id), ('active', '=', True)])
|
|
archived_versions = self.env['hr.version'].search([('employee_id', '=', employee.id), ('active', '=', False)])
|
|
self.assertEqual(active_versions.wage, 6000.0, "The active version should be the one from the offer")
|
|
self.assertEqual(archived_versions.wage, 0, "The archived version should be a dummy version")
|
|
|
|
self.assertTrue(employee.active, 'Employee is active')
|
|
|
|
def test_applicant_offer_form(self):
|
|
applicant = self.env['hr.applicant'].create({
|
|
'partner_name': 'Test app',
|
|
'job_id': self.senior_dev_job.id,
|
|
})
|
|
applicant.action_generate_offer()
|
|
offer = self.env['hr.contract.salary.offer'].search([('applicant_id', '=', applicant.id)])
|
|
data = {
|
|
"params": {
|
|
"offer_id": offer.id,
|
|
"benefits": {
|
|
'version': {
|
|
'wage': 1000,
|
|
'final_yearly_costs': 12000,
|
|
'holidays': 10,
|
|
},
|
|
'version_personal': {
|
|
'private_city': "Louvain-La-Neuve",
|
|
'private_country_id': self.env.ref("base.be").id,
|
|
'private_street': "58 rue des Wallons",
|
|
},
|
|
'employee': {
|
|
'name': 'New Employee',
|
|
'private_email': '[email protected]',
|
|
'employee_job_id': self.senior_dev_job.id,
|
|
'department_id': None,
|
|
'job_title': self.senior_dev_job.name,
|
|
},
|
|
'address': {},
|
|
'bank_account': {},
|
|
},
|
|
"token": offer.access_token,
|
|
}
|
|
}
|
|
res = self.url_open("/salary_package/submit", json=data)
|
|
content = json.loads(res.content)
|
|
new_version = self.env['hr.version'].browse(content['result']['new_version_id'])
|
|
new_version.action_generate_offer()
|
|
new_offer = self.env['hr.contract.salary.offer'].search([('employee_version_id', '=', new_version.id)])
|
|
with Form(new_offer) as offer_form:
|
|
self.assertEqual(offer_form.contract_template_id, new_version)
|
|
self.assertEqual(offer_form.sign_template_id, offer_form.contract_template_id.sign_template_id)
|
|
self.assertEqual(offer_form.final_yearly_costs, 12000)
|
|
self.assertEqual(offer_form.employee_job_id, self.senior_dev_job)
|
|
self.assertTrue(offer_form.job_title, self.senior_dev_job.name)
|
|
self.assertTrue(offer_form.contract_start_date, fields.Date.today())
|
|
self.assertTrue(offer_form.employee_id, new_version.employee_id)
|
|
|
|
def test_deleting_partial_signed_offer_should_not_delete_employee(self):
|
|
self.start_tour("/", 'hr_contract_salary_applicant_flow_tour', login='admin', timeout=350)
|
|
self.start_tour("/", 'hr_contract_salary_applicant_flow_tour_counter_sign', login='admin', timeout=350)
|
|
self.start_tour("/", 'hr_contract_salary_applicant_flow_tour', login='admin', timeout=350)
|
|
applicant = self.env['hr.applicant'].search([('partner_name', 'ilike', 'Mitchell Admin 3')])
|
|
offers = self.env['hr.contract.salary.offer'].search([('applicant_id', 'in', applicant.ids), ('state', '=', 'half_signed')])
|
|
offers.unlink()
|
|
employee = self.env['hr.employee'].search([('name', 'ilike', 'Mitchell Admin 3'), ('active', '=', True)])
|
|
self.assertTrue(employee, "The employee should not be deleted")
|
|
|
|
def _create_sign_values(self, sign_item_ids, role_id):
|
|
return {
|
|
str(sign_id): 'a'
|
|
for sign_id in sign_item_ids.filtered(
|
|
lambda r: not r.responsible_id or r.responsible_id.id == role_id
|
|
).mapped('id')
|
|
}
|
|
|
|
def test_employee_work_email_should_not_be_updated(self):
|
|
applicant = self.env['hr.applicant'].create({
|
|
'partner_id': self.env['res.partner'].create({
|
|
'name': 'test',
|
|
'email': '[email protected]'
|
|
}).id,
|
|
})
|
|
employee = self.env['hr.employee'].create({
|
|
'name': 'test',
|
|
'user_id': self.env['res.users'].create({
|
|
'name': 'foo',
|
|
'login': 'foo',
|
|
'email': '[email protected]',
|
|
'password': 'foopassword',
|
|
}).id,
|
|
})
|
|
offer = self.env['hr.contract.salary.offer'].create({
|
|
'contract_template_id': self.senior_dev_job.contract_template_id.id,
|
|
'employee_version_id': employee.version_id.id,
|
|
'employee_id': employee.id,
|
|
'applicant_id': applicant.id,
|
|
})
|
|
data = {
|
|
'params': {
|
|
'offer_id': offer.id,
|
|
'benefits': {
|
|
'version': {
|
|
'wage': 1000,
|
|
'final_yearly_costs': 12000,
|
|
'holidays': 10,
|
|
},
|
|
'version_personal': {
|
|
'private_city': 'Louvain-La-Neuve',
|
|
'private_country_id': self.env.ref('base.be').id,
|
|
'private_street': '58 rue des Wallons',
|
|
},
|
|
'employee': {
|
|
'name': 'New Employee',
|
|
'private_email': '[email protected]',
|
|
'employee_job_id': self.senior_dev_job.id,
|
|
'department_id': None,
|
|
'job_title': self.senior_dev_job.name,
|
|
},
|
|
'address': {},
|
|
'bank_account': {},
|
|
},
|
|
'token': offer.access_token,
|
|
}
|
|
}
|
|
self.authenticate('foo', 'foopassword')
|
|
res = self.url_open('/salary_package/submit', json=data)
|
|
content = json.loads(res.content)
|
|
self.assertIn('result', content)
|
|
self.assertEqual(employee.work_email, '[email protected]')
|
|
|
|
sign_request = self.env['sign.request'].browse(content['result']['request_id'])
|
|
sign_request.template_id.sign_item_ids.write({'required': False})
|
|
sign_data = {
|
|
'signature': self._create_sign_values(
|
|
sign_request.template_id.sign_item_ids,
|
|
self.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
)
|
|
}
|
|
|
|
sign_res = self.url_open(
|
|
'/sign/sign/%d/%s' % (sign_request.id, content['result']['token']),
|
|
data=json.dumps(sign_data),
|
|
headers={'Content-Type': 'application/json'},
|
|
)
|
|
self.assertIn('result', json.loads(sign_res.content))
|
|
self.assertEqual(employee.work_email, '[email protected]')
|
|
|
|
def test_open_contract_on_offer_signed_multiple_times(self):
|
|
applicant = self.env['hr.applicant'].create({
|
|
'partner_name': 'Test app',
|
|
'job_id': self.senior_dev_job.id,
|
|
})
|
|
applicant.action_generate_offer()
|
|
offer = self.env['hr.contract.salary.offer'].search([('applicant_id', '=', applicant.id)])
|
|
data = {
|
|
"params": {
|
|
"offer_id": offer.id,
|
|
"benefits": {
|
|
'version': {
|
|
'wage': 1000,
|
|
'final_yearly_costs': 12000,
|
|
'holidays': 10,
|
|
},
|
|
'version_personal': {
|
|
'private_city': "Louvain-La-Neuve",
|
|
'private_country_id': self.env.ref("base.be").id,
|
|
'private_street': "58 rue des Wallons",
|
|
},
|
|
'employee': {
|
|
'name': 'New Employee',
|
|
'private_email': '[email protected]',
|
|
'employee_job_id': self.senior_dev_job.id,
|
|
'department_id': None,
|
|
'job_title': self.senior_dev_job.name,
|
|
},
|
|
'address': {},
|
|
'bank_account': {},
|
|
},
|
|
"token": offer.access_token,
|
|
},
|
|
}
|
|
|
|
# applicant signs for the first time
|
|
res_1 = self.url_open("/salary_package/submit", json=data)
|
|
content_1 = json.loads(res_1.content)
|
|
sign_request_1 = self.env['sign.request'].browse(content_1['result']['request_id'])
|
|
sign_request_1.template_id.sign_item_ids.write({'required': False})
|
|
sign_data_applicant_1 = {
|
|
'signature': self._create_sign_values(
|
|
sign_request_1.template_id.sign_item_ids,
|
|
self.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
),
|
|
}
|
|
self.url_open(
|
|
'/sign/sign/%d/%s' % (sign_request_1.id, content_1['result']['token']),
|
|
data=json.dumps(sign_data_applicant_1),
|
|
headers={'Content-Type': 'application/json'},
|
|
)
|
|
|
|
# applicant signs for the second time
|
|
res_2 = self.url_open("/salary_package/submit", json=data)
|
|
content_2 = json.loads(res_2.content)
|
|
sign_request_2 = self.env['sign.request'].browse(content_2['result']['request_id'])
|
|
sign_request_2.template_id.sign_item_ids.write({'required': False})
|
|
sign_data_applicant_2 = {
|
|
'signature': self._create_sign_values(
|
|
sign_request_1.template_id.sign_item_ids,
|
|
self.env.ref('hr_sign.sign_item_role_employee_signatory').id,
|
|
),
|
|
}
|
|
self.url_open(
|
|
'/sign/sign/%d/%s' % (sign_request_2.id, content_2['result']['token']),
|
|
data=json.dumps(sign_data_applicant_2),
|
|
headers={'Content-Type': 'application/json'},
|
|
)
|
|
|
|
# hr signs counter signs the second sign request
|
|
sign_data_hr = {
|
|
'signature': self._create_sign_values(
|
|
sign_request_2.template_id.sign_item_ids,
|
|
self.env.ref('hr_sign.sign_item_role_job_responsible').id,
|
|
),
|
|
}
|
|
self.url_open(
|
|
'/sign/sign/%d/%s' % (sign_request_2.id, content_2['result']['token']),
|
|
data=json.dumps(sign_data_hr),
|
|
headers={'Content-Type': 'application/json'},
|
|
)
|
|
|
|
# check that the smart button redirects to the correct version
|
|
action = offer.action_view_version()
|
|
redirected_version_id = action['context']['version_id']
|
|
self.assertEqual(
|
|
redirected_version_id,
|
|
content_2['result']['new_version_id'],
|
|
"action_view_version() should redirect to the fully signed contract and not to the partially signed archived one",
|
|
)
|