forked from Mapan/odoo17e
979 lines
52 KiB
Python
979 lines
52 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import hashlib
|
|
|
|
from collections import defaultdict, OrderedDict
|
|
from odoo import fields, http, models, _, Command, SUPERUSER_ID
|
|
|
|
from odoo.addons.sign.controllers.main import Sign
|
|
from odoo.exceptions import UserError
|
|
from odoo.http import request
|
|
from odoo.tools import consteq
|
|
from odoo.tools.image import image_data_uri
|
|
from werkzeug.exceptions import NotFound
|
|
from werkzeug.wsgi import get_current_url
|
|
from urllib.parse import urlparse, parse_qs
|
|
from datetime import datetime
|
|
|
|
|
|
class SignContract(Sign):
|
|
|
|
@http.route()
|
|
def sign(self, sign_request_id, token, sms_token=False, signature=None, **kwargs):
|
|
result = super().sign(sign_request_id, token, sms_token=sms_token, signature=signature, **kwargs)
|
|
if result.get('success'):
|
|
request_item = request.env['sign.request.item'].sudo().search([('access_token', '=', token)])
|
|
contract = request.env['hr.contract'].sudo().with_context(active_test=False).search([
|
|
('sign_request_ids', 'in', request_item.sign_request_id.ids)])
|
|
offer = request.env['hr.contract.salary.offer'].sudo().search([
|
|
('sign_request_ids', 'in', request_item.sign_request_id.ids)])
|
|
if offer.state in ['expired', 'refused']:
|
|
raise UserError(_('This offer is outdated, please request an updated link...'))
|
|
request_template_id = request_item.sign_request_id.template_id.id
|
|
# Only if the signed document is the document to sign from the salary package
|
|
contract_documents = [
|
|
contract.sign_template_id.id,
|
|
contract.contract_update_template_id.id,
|
|
]
|
|
if contract and request_template_id in contract_documents:
|
|
self._update_contract_on_signature(request_item, contract, offer)
|
|
if request_item.sign_request_id.nb_closed == 1:
|
|
return dict(result, **{'url': '/salary_package/thank_you/' + str(contract.id)})
|
|
return result
|
|
|
|
def _update_contract_on_signature(self, request_item, contract, offer):
|
|
# Only the applicant/employee has signed
|
|
if request_item.sign_request_id.nb_closed == 1:
|
|
contract.active = True
|
|
contract.hash_token = False
|
|
if contract.applicant_id:
|
|
contract.applicant_id.emp_id = contract.employee_id
|
|
self._create_activity_benefit(contract, 'running')
|
|
contract.wage_on_signature = contract.wage_with_holidays
|
|
offer.state = "half_signed"
|
|
|
|
# Both applicant/employee and HR responsible have signed
|
|
if request_item.sign_request_id.nb_closed == 2:
|
|
if contract.employee_id:
|
|
contract.employee_id.active = True
|
|
if contract.applicant_id:
|
|
contract.applicant_id._move_to_hired_stage()
|
|
if contract.employee_id.work_contact_id:
|
|
contract.employee_id.work_contact_id.active = True
|
|
self._create_activity_benefit(contract, 'countersigned')
|
|
self._send_benefit_sign_request(contract)
|
|
offer.state = "full_signed"
|
|
|
|
def _create_activity_benefit(self, contract, contract_state):
|
|
benefits = request.env['hr.contract.salary.benefit'].sudo().search([
|
|
('structure_type_id', '=', contract.structure_type_id.id),
|
|
('activity_type_id', '!=', False),
|
|
('activity_creation', '=', contract_state)])
|
|
for benefit in benefits:
|
|
field = benefit.res_field_id.name
|
|
value = contract[field]
|
|
if (benefit.activity_creation_type == "onchange" and contract[field] != contract.origin_contract_id[field]) or \
|
|
benefit.activity_creation_type == "always" and value:
|
|
contract.activity_schedule(
|
|
activity_type_id=benefit.activity_type_id.id,
|
|
note="%s: %s" % (benefit.name or benefit.res_field_id.name, value),
|
|
user_id=benefit.activity_responsible_id.id)
|
|
|
|
def _send_benefit_sign_request(self, contract):
|
|
benefits = request.env['hr.contract.salary.benefit'].sudo().search([
|
|
('structure_type_id', '=', contract.structure_type_id.id),
|
|
('sign_template_id', '!=', False)])
|
|
|
|
# ask the contract responsible to create sign requests
|
|
SignRequestSudo = request.env['sign.request'].with_user(contract.hr_responsible_id).sudo()
|
|
|
|
sent_templates = request.env['sign.template']
|
|
for benefit in benefits:
|
|
field = benefit.res_field_id.name
|
|
value = contract[field]
|
|
sign_template = benefit.sign_template_id
|
|
if sign_template in sent_templates:
|
|
continue
|
|
if (benefit.activity_creation_type == "onchange" and contract[field] != contract.origin_contract_id[field]) or \
|
|
benefit.activity_creation_type == "always" and value:
|
|
|
|
sent_templates |= sign_template
|
|
|
|
sign_request_sudo = SignRequestSudo.create({
|
|
'template_id': sign_template.id,
|
|
'request_item_ids': [
|
|
Command.create({'role_id': request.env.ref('sign.sign_item_role_employee').id,
|
|
'partner_id': contract.employee_id.work_contact_id.id}),
|
|
Command.create({'role_id': request.env.ref('hr_contract_sign.sign_item_role_job_responsible').id,
|
|
'partner_id': contract.hr_responsible_id.partner_id.id}),
|
|
],
|
|
'reference': _('Signature Request - %s', benefit.name or contract.name),
|
|
'subject': _('Signature Request - %s', benefit.name or contract.name),
|
|
})
|
|
sign_request_sudo.message_subcribe(partner_ids=benefit.sign_copy_partner_id.ids)
|
|
sign_request_sudo.toggle_favorited()
|
|
|
|
contract.sign_request_ids += sign_request_sudo
|
|
|
|
class HrContractSalary(http.Controller):
|
|
|
|
def _check_access_rights(self, contract_id):
|
|
contract_sudo = request.env['hr.contract'].sudo().browse(contract_id)
|
|
if not contract_sudo.employee_id or contract_sudo.employee_id.user_id == request.env.user:
|
|
return contract_sudo
|
|
contract = request.env['hr.contract'].with_context(allowed_company_ids=request.env.user.company_ids.ids).browse(contract_id)
|
|
contract.check_access_rights('read')
|
|
contract.check_access_rule('read')
|
|
return contract_sudo
|
|
|
|
def _get_default_template_values(self, contract, offer):
|
|
values = self._get_salary_package_values(contract, offer)
|
|
values.update({
|
|
'redirect_to_job': False,
|
|
# YTI PROBABLY TO REMOVE
|
|
'applicant_id': offer.applicant_id.id,
|
|
'employee_contract_id': offer.employee_contract_id.id,
|
|
'employee_job_id': offer.employee_job_id.id,
|
|
'department_id': offer.department_id.id,
|
|
'job_title': offer.job_title,
|
|
'whitelist': False,
|
|
'part_time': False,
|
|
'final_yearly_costs': offer.final_yearly_costs,
|
|
})
|
|
return values
|
|
|
|
@http.route(['/salary_package/simulation/contract/<int:contract_id>'], type='http', auth="public", website=True, sitemap=False)
|
|
def salary_package_deprecated(self, contract_id=None, **kw):
|
|
return request.render('http_routing.http_error', {
|
|
'status_code': _('Oops'),
|
|
'status_message': _('This offer is outdated, please request an updated link...')})
|
|
|
|
@http.route(['/salary_package/simulation/offer/<int:offer_id>'], type='http', auth="public", website=True, sitemap=False)
|
|
def salary_package(self, offer_id=None, **kw):
|
|
response = False
|
|
|
|
debug = request.session.debug
|
|
for bundle_name in ["web.assets_frontend", "web.assets_frontend_lazy"]:
|
|
request.env["ir.qweb"]._get_asset_nodes(bundle_name, debug=debug, js=True, css=True)
|
|
request.env.cr.commit()
|
|
|
|
request.env.cr.execute('SAVEPOINT salary')
|
|
|
|
offer = request.env['hr.contract.salary.offer'].sudo().browse(offer_id)
|
|
contract = offer.contract_template_id
|
|
if not offer.exists() or offer.state in ['expired', 'refused']:
|
|
return request.render('http_routing.http_error', {
|
|
'status_code': _('Oops'),
|
|
'status_message': _('This offer has been updated, please request an updated link..')})
|
|
|
|
if not request.env.user.has_group('hr_contract.group_hr_contract_manager'):
|
|
if offer.applicant_id:
|
|
if not kw.get('token') or \
|
|
not offer.access_token or \
|
|
not consteq(offer.access_token, kw.get('token')) or \
|
|
offer.offer_end_date < fields.Date.today():
|
|
return request.render('http_routing.http_error', {
|
|
'status_code': _('Oops'),
|
|
'status_message': _('This link is invalid. Please contact the HR Responsible to get a new one...')})
|
|
if contract.employee_id and not contract.employee_id.user_id and not offer.applicant_id:
|
|
return request.render('http_routing.http_error', {
|
|
'status_code': _('Oops'),
|
|
'status_message': _('The employee is not linked to an existing user, please contact the administrator..')})
|
|
if contract.employee_id and contract.employee_id.user_id != request.env.user:
|
|
raise NotFound()
|
|
if contract.employee_id and offer.offer_end_date < fields.Date.today():
|
|
return request.render('http_routing.http_error', {
|
|
'status_code': _('Oops'),
|
|
'status_message': _('This link is invalid. Please contact the HR Responsible to get a new one...')})
|
|
|
|
employee_contract = False
|
|
if offer.employee_contract_id:
|
|
employee_contract = offer.employee_contract_id
|
|
# do not recreate a new employee if the salary configurator is launched with a new
|
|
# type of contract (in the event that the employee changes jobs) since the contract
|
|
# is a template without an employee
|
|
if not contract.employee_id and employee_contract.employee_id:
|
|
contract.employee_id = employee_contract.employee_id
|
|
if not request.env.user.has_group('hr_contract.group_hr_contract_manager') and employee_contract.employee_id \
|
|
and employee_contract.employee_id.user_id != request.env.user:
|
|
raise NotFound()
|
|
|
|
if not contract.employee_id or not employee_contract:
|
|
contract_country = contract.company_id.country_id
|
|
# Pre-filling
|
|
temporary_name = False
|
|
temporary_mobile = False
|
|
private_email = False
|
|
# Pre-filling name / phone / mail if coming from an applicant
|
|
if offer.applicant_id:
|
|
temporary_name = offer.applicant_id.partner_name
|
|
temporary_mobile = offer.applicant_id.partner_phone
|
|
private_email = offer.applicant_id.email_from
|
|
contract.employee_id = request.env['hr.employee'].with_context(
|
|
tracking_disable=True,
|
|
salary_simulation=True,
|
|
).with_user(SUPERUSER_ID).sudo().create({
|
|
'name': temporary_name,
|
|
'private_phone': temporary_mobile,
|
|
'private_email': private_email,
|
|
'active': False,
|
|
'country_id': contract_country.id,
|
|
'private_country_id': contract_country.id,
|
|
'certificate': False, # To force encoding it
|
|
'company_id': contract.company_id.id,
|
|
'resource_calendar_id': contract.resource_calendar_id.id,
|
|
})
|
|
|
|
if offer.applicant_id:
|
|
contract = contract.with_context(is_applicant=True)
|
|
|
|
values = self._get_default_template_values(contract, offer)
|
|
for field_name, value in kw.items():
|
|
if field_name == 'job_id':
|
|
values['redirect_to_job'] = value
|
|
if field_name == 'allow':
|
|
values['whitelist'] = value
|
|
if field_name == 'part':
|
|
values['part_time'] = True
|
|
new_gross = contract.sudo()._get_gross_from_employer_costs(values['final_yearly_costs'])
|
|
contract.write({
|
|
'wage': new_gross,
|
|
'final_yearly_costs': values['final_yearly_costs'],
|
|
})
|
|
values.update({
|
|
'need_personal_information': not values['redirect_to_job'],
|
|
'submit': not values['redirect_to_job'],
|
|
'default_mobile': request.env['ir.default'].sudo()._get('hr.contract', 'mobile'),
|
|
'original_link': get_current_url(request.httprequest.environ),
|
|
'token': kw.get('token'),
|
|
'offer_id': offer.id,
|
|
'master_department_id': request.env['hr.department'].sudo().browse(int(values['department_id'])).master_department_id.id if values['department_id'] else False
|
|
})
|
|
|
|
response = request.render("hr_contract_salary.salary_package", values)
|
|
response.flatten()
|
|
request.env.flush_all()
|
|
request.env.cr.precommit.clear()
|
|
request.env.cr.execute('ROLLBACK TO SAVEPOINT salary')
|
|
return response
|
|
|
|
@http.route(['/salary_package/thank_you/<int:contract_id>'], type='http', auth="public", website=True, sitemap=False)
|
|
def salary_package_thank_you(self, contract_id=None, **kw):
|
|
contract = request.env['hr.contract'].sudo().browse(contract_id)
|
|
return request.render("hr_contract_salary.salary_package_thank_you", {
|
|
'responsible_name': contract.hr_responsible_id.partner_id.name or contract.job_id.user_id.partner_id.name,
|
|
'responsible_email': contract.hr_responsible_id.work_email or contract.job_id.user_id.partner_id.email,
|
|
'responsible_phone': contract.hr_responsible_id.work_phone or contract.job_id.user_id.partner_id.phone,
|
|
})
|
|
|
|
def _get_personal_infos_countries(self, contract, personal_info):
|
|
return request.env['res.country'].search([])
|
|
|
|
def _get_personal_infos_states(self, contract, personal_info):
|
|
return request.env['res.country.state'].search([])
|
|
|
|
def _get_personal_infos_langs(self, contract, personal_info):
|
|
return request.env['res.lang'].search([])
|
|
|
|
def _get_personal_infos(self, contract):
|
|
initial_values = {}
|
|
dropdown_options = {}
|
|
targets = {
|
|
'employee': contract.employee_id,
|
|
'bank_account': contract.employee_id.bank_account_id,
|
|
}
|
|
|
|
# PERSONAL INFOS
|
|
personal_infos = request.env['hr.contract.salary.personal.info'].sudo().search([
|
|
'|',
|
|
('structure_type_id', '=', False),
|
|
('structure_type_id', '=', contract.structure_type_id.id)]).sorted(lambda info: (info.info_type_id.sequence, info.sequence))
|
|
mapped_personal_infos = [
|
|
defaultdict(lambda: request.env['hr.contract.salary.personal.info']), # Main Panel
|
|
request.env['hr.contract.salary.personal.info'], # Side Panel
|
|
]
|
|
for personal_info in personal_infos:
|
|
if personal_info.position == 'left':
|
|
mapped_personal_infos[0][personal_info.info_type_id.name] |= personal_info
|
|
else:
|
|
mapped_personal_infos[1] |= personal_info
|
|
|
|
target = targets[personal_info.applies_on]
|
|
|
|
if personal_info.display_type == 'document':
|
|
if personal_info.field in target and target[personal_info.field]:
|
|
if target[personal_info.field][:7] == b'JVBERi0':
|
|
content = "data:application/pdf;base64,%s" % (target[personal_info.field].decode())
|
|
else:
|
|
content = image_data_uri(target[personal_info.field])
|
|
else:
|
|
content = False
|
|
initial_values[personal_info.field] = content
|
|
initial_values[personal_info.field + '_filename'] = contract[personal_info.field + '_filename'] or personal_info.field
|
|
else:
|
|
initial_values[personal_info.field] = target[personal_info.field] if personal_info.field in target else ''
|
|
|
|
if personal_info.display_type == 'dropdown':
|
|
# Set record id instead of browse record as value
|
|
if isinstance(initial_values[personal_info.field], models.BaseModel):
|
|
initial_values[personal_info.field] = initial_values[personal_info.field].id
|
|
|
|
if personal_info.dropdown_selection == 'specific':
|
|
values = [(value.value, value.name) for value in personal_info.value_ids]
|
|
elif personal_info.dropdown_selection == 'country':
|
|
values = [(country.id, country.name) for country in self._get_personal_infos_countries(contract, personal_info)]
|
|
elif personal_info.dropdown_selection == 'state':
|
|
values = [(state.id, state.name, state.country_id.id) for state in self._get_personal_infos_states(contract, personal_info)]
|
|
elif personal_info.dropdown_selection == 'lang':
|
|
values = [(lang.code, lang.name) for lang in self._get_personal_infos_langs(contract, personal_info)]
|
|
dropdown_options[personal_info.field] = values
|
|
return mapped_personal_infos, dropdown_options, initial_values
|
|
|
|
def _get_benefits(self, contract, offer):
|
|
return request.env['hr.contract.salary.benefit'].sudo().search([
|
|
('structure_type_id', '=', contract.structure_type_id.id)])
|
|
|
|
def _get_benefits_values(self, contract, offer):
|
|
initial_values = {}
|
|
dropdown_options = {}
|
|
dropdown_group_options = {}
|
|
|
|
# benefits
|
|
benefits = self._get_benefits(contract, offer)
|
|
mapped_benefits = defaultdict(lambda: request.env['hr.contract.salary.benefit'])
|
|
for benefit in benefits:
|
|
mapped_benefits[benefit.benefit_type_id] |= benefit
|
|
field = benefit.field
|
|
initial_values[field] = contract[field]
|
|
|
|
if benefit.folded:
|
|
fold_field = 'fold_%s' % (benefit.field)
|
|
benefit_fold_field = benefit.fold_field or benefit.field
|
|
initial_values[fold_field] = contract[benefit_fold_field] if benefit_fold_field and benefit_fold_field in contract else 0
|
|
|
|
if benefit.display_type == 'manual':
|
|
manual_field = '%s_manual' % (benefit.field)
|
|
field = benefit.manual_field or benefit.field
|
|
initial_values[manual_field] = contract[field] if field and field in contract else 0
|
|
if benefit.display_type == 'text':
|
|
text_field = '%s_text' % (benefit.field)
|
|
field = benefit.manual_field or benefit.field
|
|
initial_values[text_field] = contract[field] if field and field in contract else ''
|
|
elif benefit.display_type == 'dropdown' or benefit.display_type == 'dropdown-group':
|
|
initial_values['select_%s' % field] = contract[field]
|
|
|
|
dropdown_benefits = benefits.filtered(lambda a: a.display_type == 'dropdown')
|
|
for dropdown_benefit in dropdown_benefits:
|
|
dropdown_options[dropdown_benefit.field] = \
|
|
[(value.value, value.value) for value in dropdown_benefit.value_ids.filtered(lambda v: v.display_type == 'line')]
|
|
dropdown_group_benefits = benefits.filtered(lambda a: a.display_type == 'dropdown-group')
|
|
for dropdown_group_benefit in dropdown_group_benefits:
|
|
values = OrderedDict()
|
|
values[""] = []
|
|
current_section = ""
|
|
for value in dropdown_group_benefit.value_ids:
|
|
if value.display_type == 'section':
|
|
current_section = value.name
|
|
values[current_section] = []
|
|
else:
|
|
values[current_section].append((value.value, value.value))
|
|
dropdown_group_options[dropdown_group_benefit.field] = values
|
|
benefit_types = sorted(benefits.mapped('benefit_type_id'), key=lambda x: x.sequence)
|
|
mapped_dependent_benefits = defaultdict(lambda: '')
|
|
mapped_mandatory_benefits = defaultdict(lambda: '')
|
|
# When the dependent benefit is disabled, on hover over we display the information
|
|
# regarding which (mandatory) benefits need to be selected, in order to be able to select
|
|
# the (dependent) benefit in question. For this purpose, here we build the string for each dependent benefit.
|
|
# The string starts with the display name of the dependent benefit and is followed by the display names
|
|
# of the mandatory benefits, separated by semicolon.
|
|
mapped_mandatory_benefits_names = defaultdict(lambda: '')
|
|
for dependent_benefit in benefits:
|
|
if not dependent_benefit.field:
|
|
continue
|
|
mapped_mandatory_benefits_names[dependent_benefit] = (dependent_benefit.fold_label or dependent_benefit.name) + ';'
|
|
if dependent_benefit.folded:
|
|
dependent_name = 'fold_%s' % (dependent_benefit.field)
|
|
else:
|
|
dependent_name = dependent_benefit.field + '_' + dependent_benefit.display_type
|
|
dependent_benefit_str = dependent_name + ' '
|
|
for mandatory_benefit in dependent_benefit.benefit_ids:
|
|
mapped_dependent_benefits[mandatory_benefit] += dependent_benefit_str
|
|
if mandatory_benefit.folded:
|
|
mandatory_name = 'fold_%s' % (mandatory_benefit.field)
|
|
else:
|
|
mandatory_name = mandatory_benefit.field + '_' + mandatory_benefit.display_type
|
|
mapped_mandatory_benefits[dependent_benefit] += mandatory_name + ' '
|
|
mapped_mandatory_benefits_names[dependent_benefit] += (mandatory_benefit.fold_label or mandatory_benefit.name) + ';'
|
|
return mapped_benefits, mapped_dependent_benefits, mapped_mandatory_benefits, mapped_mandatory_benefits_names, benefit_types, dropdown_options, dropdown_group_options, initial_values
|
|
|
|
def _get_salary_package_values(self, contract, offer):
|
|
mapped_personal_infos, dropdown_options_1, initial_values_1 = self._get_personal_infos(contract)
|
|
mapped_benefits, mapped_dependent_benefits, mandatory_benefits, mandatory_benefits_names, benefit_types, dropdown_options_2, dropdown_group_options, initial_values_2 = self._get_benefits_values(contract, offer)
|
|
all_initial_values = {**initial_values_1, **initial_values_2}
|
|
all_initial_values = {key: round(value, 2) if isinstance(value, float) else value for key, value in all_initial_values.items()}
|
|
all_dropdown_options = {**dropdown_options_1, **dropdown_options_2}
|
|
return {
|
|
'contract': contract,
|
|
'states': request.env['res.country.state'].search([]),
|
|
'countries': request.env['res.country'].search([]),
|
|
'benefits': mapped_benefits,
|
|
'dependent_benefits': mapped_dependent_benefits,
|
|
'mandatory_benefits': mandatory_benefits,
|
|
'mandatory_benefits_names': mandatory_benefits_names,
|
|
'benefit_types': benefit_types,
|
|
'mapped_personal_infos': mapped_personal_infos,
|
|
'dropdown_options': all_dropdown_options,
|
|
'dropdown_group_options': dropdown_group_options,
|
|
'initial_values': all_initial_values,
|
|
}
|
|
|
|
def _get_new_contract_values(self, contract, employee, benefits, offer):
|
|
contract_benefits = self._get_benefits(contract, offer)
|
|
contract_vals = {
|
|
'active': False,
|
|
'name': contract.name if contract.state == 'draft' else "Package Simulation",
|
|
'job_id': offer.employee_job_id.id or contract.job_id.id or employee.job_id.id,
|
|
'department_id': offer.department_id.id or contract.department_id.id or employee.department_id.id,
|
|
'company_id': contract.company_id.id,
|
|
'currency_id': contract.company_id.currency_id.id,
|
|
'employee_id': employee.id,
|
|
'structure_type_id': contract.structure_type_id.id,
|
|
'wage': benefits['wage'],
|
|
'final_yearly_costs': benefits['final_yearly_costs'],
|
|
'resource_calendar_id': contract.resource_calendar_id.id,
|
|
'default_contract_id': contract.default_contract_id.id,
|
|
'hr_responsible_id': contract.hr_responsible_id.id,
|
|
'sign_template_id': contract.sign_template_id.id,
|
|
'contract_update_template_id': contract.contract_update_template_id.id,
|
|
'date_start': offer.contract_start_date or fields.Date.today().replace(day=1),
|
|
'contract_type_id': contract.contract_type_id.id,
|
|
}
|
|
if 'work_entry_source' in contract:
|
|
contract_vals['work_entry_source'] = contract.work_entry_source
|
|
|
|
for benefit in contract_benefits:
|
|
if not benefit.res_field_id or benefit.field not in contract:
|
|
continue
|
|
if hasattr(contract, '_get_benefit_values_%s' % (benefit.field)):
|
|
contract_vals.update(getattr(contract, '_get_benefit_values_%s' % (benefit.field))(contract, benefits))
|
|
continue
|
|
if benefit.folded:
|
|
contract_vals[benefit.fold_field or benefit.field] = benefits['fold_%s' % (benefit.field)]
|
|
if benefit.display_type == 'dropdown':
|
|
contract_vals[benefit.field] = benefits[benefit.field]
|
|
if benefit.display_type in ['manual', 'text']:
|
|
contract_vals[benefit.manual_field or benefit.field] = benefits['%s_%s' % (benefit.field, 'manual' if benefit.display_type == 'manual' else 'text')]
|
|
else:
|
|
contract_vals[benefit.field] = benefits[benefit.field]
|
|
return contract_vals
|
|
|
|
def _update_personal_info(self, employee, contract, personal_infos_values, no_name_write=False):
|
|
def resolve_value(field_name, values):
|
|
targets = {
|
|
'employee': request.env['hr.employee'],
|
|
'bank_account': request.env['res.partner.bank'],
|
|
}
|
|
field_value = values[field_name]
|
|
|
|
target = targets[personal_info.applies_on]
|
|
if field_name in target and isinstance(target[field_name], models.BaseModel):
|
|
field_value = int(field_value) if field_value else False
|
|
return field_value
|
|
|
|
def _is_valid_date(date):
|
|
return fields.Date.from_string(date) < fields.Date.from_string('1900-01-01')
|
|
|
|
personal_infos = request.env['hr.contract.salary.personal.info'].sudo().search([
|
|
'|', ('structure_type_id', '=', False), ('structure_type_id', '=', contract.structure_type_id.id)])
|
|
|
|
employee_infos = personal_infos_values['employee']
|
|
bank_account_infos = personal_infos_values['bank_account']
|
|
|
|
for key in ['employee_job_id', 'department_id']:
|
|
try:
|
|
employee_infos[key] = int(employee_infos[key])
|
|
except ValueError:
|
|
employee_infos[key] = None
|
|
|
|
job = request.env['hr.job'].sudo().browse(employee_infos['employee_job_id'])
|
|
if not employee_infos['job_title']:
|
|
employee_infos['job_title'] = job.name
|
|
|
|
if employee.department_id.parent_path:
|
|
employee_department = employee.department_id.id if str(employee_infos['department_id']) in employee.department_id.parent_path.split('/') else employee_infos['department_id']
|
|
else:
|
|
employee_department = employee_infos['department_id']
|
|
|
|
employee_vals = {'job_title': employee_infos['job_title'],
|
|
'job_id': employee_infos['employee_job_id'],
|
|
'department_id': employee_department}
|
|
work_contact_vals = {}
|
|
bank_account_vals = {}
|
|
attachment_create_vals = []
|
|
|
|
if employee_infos.get('birthday') and _is_valid_date(employee_infos['birthday']):
|
|
employee_infos['birthday'] = ''
|
|
|
|
for personal_info in personal_infos:
|
|
field_name = personal_info.field
|
|
|
|
if personal_info.display_type == 'document' and not employee_infos.get(field_name):
|
|
continue
|
|
|
|
if field_name in employee_infos and personal_info.applies_on == 'employee':
|
|
employee_vals[field_name] = resolve_value(field_name, employee_infos)
|
|
elif field_name in bank_account_infos and personal_info.applies_on == 'bank_account':
|
|
bank_account_vals[field_name] = resolve_value(field_name, bank_account_infos)
|
|
|
|
work_contact_vals['name'] = employee_vals['name']
|
|
work_contact_vals['email'] = employee_vals['private_email']
|
|
|
|
# Update personal info on the private address
|
|
if employee.work_contact_id:
|
|
if no_name_write or employee.user_id.name:
|
|
del work_contact_vals['name']
|
|
partner = employee.work_contact_id
|
|
# We shouldn't modify the partner email like this
|
|
if employee.work_contact_id.email:
|
|
work_contact_vals.pop('email', None)
|
|
partner.write(work_contact_vals)
|
|
else:
|
|
work_contact_vals['active'] = False
|
|
partner = request.env['res.partner'].sudo().with_context(lang=None, tracking_disable=True).create(work_contact_vals)
|
|
|
|
# Update personal info on the employee
|
|
if bank_account_vals:
|
|
bank_account_vals['partner_id'] = partner.id
|
|
existing_bank_account = request.env['res.partner.bank'].sudo().search([
|
|
('partner_id', '=', partner.id),
|
|
('acc_number', '=', bank_account_vals['acc_number'])], limit=1)
|
|
if existing_bank_account:
|
|
bank_account = existing_bank_account
|
|
else:
|
|
bank_account = request.env['res.partner.bank'].sudo().create(bank_account_vals)
|
|
|
|
employee_vals['bank_account_id'] = bank_account.id
|
|
|
|
employee_vals['work_contact_id'] = partner.id
|
|
|
|
if job.address_id:
|
|
employee_vals['address_id'] = job.address_id.id
|
|
|
|
if not no_name_write:
|
|
employee_vals['name'] = employee_infos['name']
|
|
employee.write(employee_vals)
|
|
if attachment_create_vals:
|
|
request.env['ir.attachment'].sudo().create(attachment_create_vals)
|
|
|
|
def create_new_contract(self, contract, offer_id, benefits, no_write=False, **kw):
|
|
# Generate a new contract with the current modifications
|
|
contract_diff = []
|
|
contract_values = benefits['contract']
|
|
personal_infos = {
|
|
'employee': benefits['employee'],
|
|
'address': benefits['address'],
|
|
'bank_account': benefits['bank_account'],
|
|
}
|
|
offer = request.env['hr.contract.salary.offer'].sudo().browse(offer_id).exists()
|
|
applicant = offer.applicant_id
|
|
employee = kw.get('employee') or contract.employee_id or applicant.emp_id
|
|
if not employee and applicant:
|
|
existing_contract = request.env['hr.contract'].sudo().with_context(active_test=False).search([
|
|
('applicant_id', '=', applicant.id), ('employee_id', '!=', False)], limit=1)
|
|
employee = existing_contract.employee_id
|
|
if not employee:
|
|
employee = request.env['hr.employee'].sudo().with_context(
|
|
tracking_disable=True,
|
|
salary_simulation=not no_write,
|
|
).create({
|
|
'name': 'Simulation Employee',
|
|
'active': False,
|
|
'company_id': contract.company_id.id,
|
|
'lang': contract.company_id.partner_id.lang,
|
|
'resource_calendar_id': contract.resource_calendar_id.id,
|
|
})
|
|
|
|
# get differences for personnal information
|
|
if no_write:
|
|
employee_fields = request.env['hr.employee']._fields
|
|
for section in personal_infos:
|
|
for field in personal_infos[section]:
|
|
if field in employee_fields:
|
|
current_value = employee[field]
|
|
new_value = personal_infos[section][field]
|
|
|
|
if isinstance(current_value, type(new_value)) and current_value == new_value:
|
|
continue
|
|
|
|
elif employee_fields[field].relational:
|
|
current_value = str(current_value.name)
|
|
if new_value:
|
|
new_record = request.env[employee_fields[field].comodel_name].sudo().browse(int(new_value))
|
|
new_value = new_record['name'] if new_record else ''
|
|
|
|
elif employee_fields[field].type in ['integer', 'float']:
|
|
current_value = str(current_value)
|
|
if not new_value:
|
|
new_value = '0'
|
|
|
|
elif employee_fields[field].type == 'date':
|
|
current_value = current_value.strftime('%Y-%m-%d') if current_value else ''
|
|
|
|
elif employee_fields[field].type == 'boolean':
|
|
current_value = str(current_value)
|
|
new_value = str(new_value)
|
|
|
|
elif employee_fields[field].type == 'binary':
|
|
continue
|
|
|
|
if current_value != new_value:
|
|
employee_field_name = employee_fields[field].string or field
|
|
contract_diff.append((employee_field_name, current_value, new_value))
|
|
|
|
self._update_personal_info(employee, contract, personal_infos, no_name_write=bool(kw.get('employee')))
|
|
new_contract = request.env['hr.contract'].with_context(
|
|
tracking_disable=True
|
|
).sudo().create(self._get_new_contract_values(contract, employee, contract_values, offer))
|
|
|
|
# get differences for contract information
|
|
if no_write:
|
|
contract_fields = request.env['hr.contract']._fields
|
|
for field in contract_fields:
|
|
if field in contract_values and contract[field] != new_contract[field]\
|
|
and (contract[field] or new_contract[field]):
|
|
current_value = contract[field]
|
|
new_value = new_contract[field]
|
|
contract_field_name = contract_fields[field].string or field
|
|
contract_diff.append((contract_field_name, current_value, new_value))
|
|
|
|
if 'original_link' in kw:
|
|
start_date = parse_qs(urlparse(kw['original_link']).query).get('contract_start_date', False)
|
|
if start_date:
|
|
new_contract.date_start = datetime.strptime(start_date[0], '%Y-%m-%d').date()
|
|
|
|
new_contract.wage_with_holidays = contract_values['wage']
|
|
new_contract.final_yearly_costs = float(contract_values['final_yearly_costs'] or 0.0)
|
|
new_contract._inverse_wage_with_holidays()
|
|
|
|
return new_contract, contract_diff
|
|
|
|
@http.route('/salary_package/update_salary', type="json", auth="public")
|
|
def update_salary(self, contract_id=None, offer_id=None, benefits=None, **kw):
|
|
result = {}
|
|
contract = self._check_access_rights(contract_id)
|
|
|
|
new_contract = self.create_new_contract(contract, offer_id, benefits, no_write=True)[0]
|
|
final_yearly_costs = float(benefits['contract']['final_yearly_costs'] or 0.0)
|
|
new_gross = new_contract._get_gross_from_employer_costs(final_yearly_costs)
|
|
new_contract.write({
|
|
'wage': new_gross,
|
|
'final_yearly_costs': final_yearly_costs,
|
|
})
|
|
|
|
result['new_gross'] = round(new_gross, 2)
|
|
new_contract = new_contract.with_context(
|
|
origin_contract_id=contract.id,
|
|
simulation_working_schedule=kw.get('simulation_working_schedule', '100'))
|
|
result.update(self._get_compute_results(new_contract))
|
|
|
|
request.env.cr.rollback()
|
|
return result
|
|
|
|
def _get_compute_results(self, new_contract):
|
|
new_contract.wage_on_signature = new_contract.wage_with_holidays
|
|
|
|
result = {}
|
|
result['wage_with_holidays'] = round(new_contract.wage_with_holidays, 2)
|
|
# Allowed company ids might not be filled or request.env.user.company_ids might be wrong
|
|
# since we are in route context, force the company to make sure we load everything
|
|
resume_lines = request.env['hr.contract.salary.resume'].sudo().with_company(new_contract.company_id).search([
|
|
'|',
|
|
('structure_type_id', '=', False),
|
|
('structure_type_id', '=', new_contract.structure_type_id.id),
|
|
('value_type', 'in', ['fixed', 'contract', 'monthly_total', 'sum'])])
|
|
|
|
result['resume_categories'] = [c.name for c in sorted(resume_lines.mapped('category_id'), key=lambda x: x.sequence)]
|
|
result['resume_lines_mapped'] = defaultdict(lambda: {})
|
|
|
|
monthly_total = 0
|
|
monthly_total_lines = resume_lines.filtered(lambda l: l.value_type == 'monthly_total')
|
|
|
|
uoms = {'days': _('Days'), 'percent': '%', 'currency': new_contract.company_id.currency_id.symbol, 'position': new_contract.company_id.currency_id.position}
|
|
|
|
resume_explanation = False
|
|
for resume_line in resume_lines - monthly_total_lines:
|
|
value = 0
|
|
uom = uoms[resume_line.uom]
|
|
resume_explanation = False
|
|
if resume_line.value_type == 'fixed':
|
|
value = resume_line.fixed_value
|
|
if resume_line.value_type == 'contract':
|
|
value = new_contract[resume_line.code] if resume_line.code in new_contract else 0
|
|
if resume_line.value_type == 'sum':
|
|
resume_explanation = _('Equals to the sum of the following values:\n\n%s',
|
|
'\n+ '.join(resume_line.benefit_ids.res_field_id.sudo().mapped('field_description')))
|
|
for benefit in resume_line.benefit_ids:
|
|
if not benefit.fold_field or (benefit.fold_field and new_contract[benefit.fold_field]):
|
|
field = benefit.field
|
|
value += new_contract[field]
|
|
if resume_line.impacts_monthly_total:
|
|
monthly_total += value / 12.0 if resume_line.category_id.periodicity == 'yearly' else value
|
|
try:
|
|
value = round(float(value), 2)
|
|
except:
|
|
pass
|
|
result['resume_lines_mapped'][resume_line.category_id.name][resume_line.code] = (resume_line.name, value, uom, resume_explanation, new_contract.company_id.currency_id.position, resume_line.uom)
|
|
for resume_line in monthly_total_lines:
|
|
result['resume_lines_mapped'][resume_line.category_id.name][resume_line.code] = (resume_line.name, round(float(monthly_total), 2), uoms['currency'], uoms['position'], resume_explanation, resume_line.uom)
|
|
return result
|
|
|
|
@http.route(['/salary_package/onchange_benefit'], type='json', auth='public')
|
|
def onchange_benefit(self, benefit_field, new_value, contract_id, benefits):
|
|
# Return a dictionary describing the new benefit configuration:
|
|
# - new_value: The benefit new_value (same by default)
|
|
# - description: The dynamic description corresponding to the benefit new value
|
|
# - extra_value: A list of tuple (input name, input value) change another input due
|
|
# to the benefit new_value
|
|
# Override this controllers to add customize
|
|
# the returned value for a specific benefit
|
|
contract = self._check_access_rights(contract_id)
|
|
benefit = request.env['hr.contract.salary.benefit'].sudo().search([
|
|
('structure_type_id', '=', contract.structure_type_id.id),
|
|
('res_field_id.name', '=', benefit_field)], limit=1)
|
|
if hasattr(contract, '_get_description_%s' % benefit_field):
|
|
description = getattr(contract, '_get_description_%s' % benefit_field)(new_value)
|
|
else:
|
|
description = benefit.description
|
|
return {'new_value': new_value, 'description': description, 'extra_values': False}
|
|
|
|
@http.route(['/salary_package/onchange_personal_info'], type='json', auth='public')
|
|
def onchange_personal_info(self, field, value):
|
|
# sudo as public users can't access ir.model.fields
|
|
info = request.env['hr.contract.salary.personal.info'].sudo().search([('field', '=', field)])
|
|
if not info.child_ids:
|
|
return {}
|
|
if info.value_ids:
|
|
value = info.value_ids.filtered(lambda v: v.value == value)
|
|
return {'hide_children': value.hide_children, 'field': field}
|
|
return {'hide_children': not bool(value), 'field': field}
|
|
|
|
def _get_email_info(self, contract, **kw):
|
|
field_names = {
|
|
model: {
|
|
field.name: field.field_description for field in request.env['ir.model.fields'].sudo().search([('model', '=', model)])
|
|
} for model in ['hr.employee', 'hr.contract', 'res.partner', 'res.partner.bank']}
|
|
result = {
|
|
_('Salary Package Summary'): {
|
|
'General Information': [
|
|
(_('Employee Name'), contract.employee_id.name),
|
|
(_('Job Position'), contract.job_id.name),
|
|
(_('Job Title'), contract.employee_id.job_title),
|
|
(_('Contract Type'), contract.contract_type_id.name),
|
|
(_('Original Link'), kw.get('original_link'))
|
|
],
|
|
}
|
|
}
|
|
# Contract Information
|
|
contract_benefits = request.env['hr.contract.salary.benefit'].sudo().search([('structure_type_id', '=', contract.structure_type_id.id)])
|
|
contract_info = {benefit_type.name: [] for benefit_type in sorted(contract_benefits.mapped('benefit_type_id'), key=lambda x: x.sequence)}
|
|
for benefit in contract_benefits:
|
|
if benefit.folded and benefit.fold_field:
|
|
value = _('Yes') if contract[benefit.fold_field] else _('No')
|
|
contract_info[benefit.benefit_type_id.name].append((field_names['hr.contract'][benefit.fold_field], value))
|
|
field_name = benefit.field
|
|
if not field_name or field_name not in contract:
|
|
continue
|
|
field_value = contract[field_name]
|
|
if isinstance(field_value, models.BaseModel):
|
|
field_value = field_value.name
|
|
elif isinstance(field_value, float):
|
|
field_value = round(field_value, 2)
|
|
contract_info[benefit.benefit_type_id.name].append((field_names['hr.contract'][field_name], field_value))
|
|
# Add wage information
|
|
contract_info[_('Wage')] = [
|
|
(_('Monthly Gross Salary'), contract.wage_with_holidays),
|
|
(_('Annual Employer Cost'), contract.final_yearly_costs),
|
|
]
|
|
result[_('Contract Information:')] = contract_info
|
|
# Personal Information
|
|
infos = request.env['hr.contract.salary.personal.info'].sudo().search([('display_type', '!=', 'document'), '|', ('structure_type_id', '=', False), ('structure_type_id', '=', contract.structure_type_id.id)])
|
|
personal_infos = {personal_info_type.name: [] for personal_info_type in sorted(infos.mapped('info_type_id'), key=lambda x: x.sequence)}
|
|
for info in infos:
|
|
if info.applies_on == 'employee':
|
|
field_label = field_names['hr.employee'][info.field]
|
|
field_value = contract.employee_id[info.field]
|
|
if info.applies_on == 'bank_account':
|
|
field_label = field_names['res.partner.bank'][info.field]
|
|
field_value = contract.employee_id.bank_account_id[info.field]
|
|
if isinstance(field_value, models.BaseModel):
|
|
field_value = field_value.name
|
|
elif isinstance(field_value, float):
|
|
field_value = round(field_value, 2)
|
|
personal_infos[info.info_type_id.name].append((field_label, field_value))
|
|
result[_('Personal Information')] = personal_infos
|
|
return {'mapped_data': result}
|
|
|
|
def _send_mail_message(self, offer, template, kw, values, new_contract_id=None):
|
|
model = 'hr.contract' if new_contract_id else 'hr.contract.salary.offer'
|
|
res_id = new_contract_id or offer.id
|
|
request.env[model].sudo().browse(res_id).message_post_with_source(
|
|
template,
|
|
render_values=values,
|
|
subtype_xmlid='mail.mt_comment',
|
|
)
|
|
|
|
def send_email(self, offer, contract, **kw):
|
|
self._send_mail_message(
|
|
offer,
|
|
'hr_contract_salary.hr_contract_salary_email_template',
|
|
kw,
|
|
self._get_email_info(contract, **kw))
|
|
return contract.id
|
|
|
|
def send_diff_email(self, offer, differences, new_contract_id, **kw):
|
|
self._send_mail_message(
|
|
offer,
|
|
'hr_contract_salary.hr_contract_salary_diff_email_template',
|
|
kw,
|
|
{'differences': differences},
|
|
new_contract_id)
|
|
|
|
@http.route(['/salary_package/submit'], type='json', auth='public')
|
|
def submit(self, contract_id=None, offer_id=None, benefits=None, **kw):
|
|
offer = request.env['hr.contract.salary.offer'].sudo().browse(offer_id).exists()
|
|
if not offer.applicant_id and not offer.employee_contract_id:
|
|
raise UserError(_('This link is invalid. Please contact the HR Responsible to get a new one...'))
|
|
|
|
contract = self._check_access_rights(contract_id)
|
|
if offer.employee_contract_id:
|
|
contract = offer.employee_contract_id
|
|
if contract.employee_id.user_id == request.env.user:
|
|
kw['employee'] = contract.employee_id
|
|
kw['package_submit'] = True
|
|
new_contract = self.create_new_contract(contract, offer_id, benefits, no_write=True, **kw)
|
|
|
|
if isinstance(new_contract, dict) and new_contract.get('error'):
|
|
return new_contract
|
|
|
|
new_contract, contract_diff = new_contract
|
|
|
|
# write on new contract differences with current one
|
|
current_contract = request.env['hr.contract'].sudo().search([
|
|
('active', '=', True),
|
|
('employee_id', '=', new_contract.employee_id.id),
|
|
('state', '=', 'open'),
|
|
])
|
|
if current_contract:
|
|
self.send_diff_email(offer, contract_diff, new_contract.id, **kw)
|
|
|
|
self.send_email(offer, new_contract, **kw)
|
|
|
|
applicant = offer.applicant_id
|
|
if applicant and offer.access_token:
|
|
hash_token_access = hashlib.sha1(kw.get('token').encode("utf-8")).hexdigest()
|
|
existing_contract = request.env['hr.contract'].sudo().search([
|
|
('applicant_id', '=', applicant.id), ('hash_token', '=', hash_token_access), ('active', '=', False)])
|
|
existing_contract.sign_request_ids.write({'state': 'canceled', 'active': False})
|
|
existing_contract.unlink()
|
|
new_contract.hash_token = hash_token_access
|
|
elif not applicant and contract.employee_id.user_id and contract.employee_id.user_id == request.env.user and kw.get('original_link', False):
|
|
hash_token_access = hashlib.sha1(kw.get('original_link').encode("utf-8")).hexdigest()
|
|
existing_contract = request.env['hr.contract'].sudo().search([
|
|
('employee_id', 'in', request.env.user.employee_ids.ids), ('hash_token', '=', hash_token_access), ('active', '=', False)])
|
|
existing_contract.sign_request_ids.write({'state': 'canceled', 'active': False})
|
|
existing_contract.unlink()
|
|
new_contract.hash_token = hash_token_access
|
|
|
|
if new_contract.id != contract.id:
|
|
new_contract.write({
|
|
'state': 'draft',
|
|
'name': 'New contract - ' + new_contract.employee_id.name,
|
|
'origin_contract_id': contract_id,
|
|
})
|
|
sign_template = new_contract.contract_update_template_id if offer.employee_contract_id else new_contract.sign_template_id
|
|
if not sign_template:
|
|
return {'error': 1, 'error_msg': _('No signature template defined on the contract. Please contact the HR responsible.')}
|
|
if not new_contract.hr_responsible_id:
|
|
return {'error': 1, 'error_msg': _('No HR responsible defined on the job position. Please contact an administrator.')}
|
|
|
|
# ask the contract responsible to create a sign request
|
|
SignRequestSudo = request.env['sign.request'].with_user(new_contract.hr_responsible_id).sudo()
|
|
|
|
sign_request_sudo = SignRequestSudo.create({
|
|
'template_id': sign_template.id,
|
|
'request_item_ids': [
|
|
Command.create({
|
|
'role_id': request.env.ref('sign.sign_item_role_employee').id,
|
|
'partner_id': new_contract.employee_id.work_contact_id.id,
|
|
'mail_sent_order': 1
|
|
}),
|
|
Command.create({
|
|
'role_id': request.env.ref('hr_contract_sign.sign_item_role_job_responsible').id,
|
|
'partner_id': new_contract.hr_responsible_id.partner_id.id,
|
|
'mail_sent_order': 2
|
|
}),
|
|
],
|
|
'reference': _('Signature Request - %s', new_contract.name),
|
|
'subject': _('Signature Request - %s', new_contract.name),
|
|
})
|
|
sign_request_sudo.toggle_favorited()
|
|
|
|
# Prefill the sign boxes
|
|
sign_items = request.env['sign.item'].sudo().search([
|
|
('template_id', '=', sign_template.id),
|
|
('name', '!=', '')
|
|
])
|
|
sign_values_by_role = defaultdict(lambda: defaultdict(lambda: request.env['sign.item']))
|
|
for item in sign_items:
|
|
try:
|
|
new_value = None
|
|
if item.name == 'car' and new_contract.transport_mode_car:
|
|
if not new_contract.new_car and new_contract.car_id:
|
|
new_value = new_contract.car_id.model_id.name
|
|
elif new_contract.new_car and new_contract.new_car_model_id:
|
|
new_value = new_contract.new_car_model_id.name
|
|
# YTI FIXME: Clean that brol
|
|
elif item.name == 'l10n_be_group_insurance_rate':
|
|
new_value = 1 if item.name in new_contract and new_contract[item.name] else 0
|
|
elif item.name == "ip_wage_rate":
|
|
new_value = new_value if new_contract.ip else 0
|
|
else:
|
|
new_values = new_contract.mapped(item.name)
|
|
if not new_values or isinstance(new_values, models.BaseModel):
|
|
raise Exception
|
|
new_value = new_values[0]
|
|
if isinstance(new_value, float):
|
|
new_value = round(new_value, 2)
|
|
if item.type_id.item_type == "checkbox":
|
|
new_value = 'on' if new_value else 'off'
|
|
if new_value is not None:
|
|
sign_values_by_role[item.responsible_id][str(item.id)] = new_value
|
|
except Exception:
|
|
pass
|
|
for sign_request_item in sign_request_sudo.request_item_ids:
|
|
if sign_request_item.role_id in sign_values_by_role:
|
|
sign_request_item._fill(sign_values_by_role[sign_request_item.role_id])
|
|
|
|
access_token = request.env['sign.request.item'].sudo().search([
|
|
('sign_request_id', '=', sign_request_sudo.id),
|
|
('role_id', '=', request.env.ref('sign.sign_item_role_employee').id)
|
|
]).access_token
|
|
|
|
new_contract.sign_request_ids += sign_request_sudo
|
|
offer.sign_request_ids += sign_request_sudo
|
|
|
|
if new_contract:
|
|
if offer.applicant_id:
|
|
new_contract.sudo().applicant_id = offer.applicant_id
|
|
if offer.employee_contract_id:
|
|
new_contract.sudo().origin_contract_id = offer.employee_contract_id
|
|
|
|
return {
|
|
'job_id': new_contract.job_id.id,
|
|
'request_id': sign_request_sudo.id,
|
|
'token': access_token,
|
|
'error': 0,
|
|
'new_contract_id': new_contract.id
|
|
}
|