Problem ------- Recruitment pipe is design to work better if you archive the lost/refused candidate rather than moving them in a specific column this practice is reinforced by hr_referral. Therefore, the other application button should take into account archive applicant Solution -------- - count archived applicant - show archived applicant closes odoo/odoo#63982 X-original-commit: f242160eb06067ad56dec1c9acfbfe068ad1c6a1 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
552 lines
27 KiB
Python
552 lines
27 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from random import randint
|
|
|
|
from odoo import api, fields, models, tools, SUPERUSER_ID
|
|
from odoo.tools.translate import _
|
|
from odoo.exceptions import UserError
|
|
|
|
AVAILABLE_PRIORITIES = [
|
|
('0', 'Normal'),
|
|
('1', 'Good'),
|
|
('2', 'Very Good'),
|
|
('3', 'Excellent')
|
|
]
|
|
|
|
|
|
class RecruitmentSource(models.Model):
|
|
_name = "hr.recruitment.source"
|
|
_description = "Source of Applicants"
|
|
_inherits = {"utm.source": "source_id"}
|
|
|
|
source_id = fields.Many2one('utm.source', "Source", ondelete='cascade', required=True)
|
|
email = fields.Char(related='alias_id.display_name', string="Email", readonly=True)
|
|
job_id = fields.Many2one('hr.job', "Job", ondelete='cascade')
|
|
alias_id = fields.Many2one('mail.alias', "Alias ID")
|
|
|
|
def create_alias(self):
|
|
campaign = self.env.ref('hr_recruitment.utm_campaign_job')
|
|
medium = self.env.ref('utm.utm_medium_email')
|
|
for source in self:
|
|
vals = {
|
|
'alias_parent_thread_id': source.job_id.id,
|
|
'alias_model_id': self.env['ir.model']._get('hr.applicant').id,
|
|
'alias_parent_model_id': self.env['ir.model']._get('hr.job').id,
|
|
'alias_name': "%s+%s" % (source.job_id.alias_name or source.job_id.name, source.name),
|
|
'alias_defaults': {
|
|
'job_id': source.job_id.id,
|
|
'campaign_id': campaign.id,
|
|
'medium_id': medium.id,
|
|
'source_id': source.source_id.id,
|
|
},
|
|
}
|
|
source.alias_id = self.env['mail.alias'].create(vals)
|
|
source.name = source.source_id.name
|
|
|
|
class RecruitmentStage(models.Model):
|
|
_name = "hr.recruitment.stage"
|
|
_description = "Recruitment Stages"
|
|
_order = 'sequence'
|
|
|
|
name = fields.Char("Stage Name", required=True, translate=True)
|
|
sequence = fields.Integer(
|
|
"Sequence", default=10,
|
|
help="Gives the sequence order when displaying a list of stages.")
|
|
job_ids = fields.Many2many(
|
|
'hr.job', string='Job Specific',
|
|
help='Specific jobs that uses this stage. Other jobs will not use this stage.')
|
|
requirements = fields.Text("Requirements")
|
|
template_id = fields.Many2one(
|
|
'mail.template', "Email Template",
|
|
help="If set, a message is posted on the applicant using the template when the applicant is set to the stage.")
|
|
fold = fields.Boolean(
|
|
"Folded in Kanban",
|
|
help="This stage is folded in the kanban view when there are no records in that stage to display.")
|
|
legend_blocked = fields.Char(
|
|
'Red Kanban Label', default=lambda self: _('Blocked'), translate=True, required=True)
|
|
legend_done = fields.Char(
|
|
'Green Kanban Label', default=lambda self: _('Ready for Next Stage'), translate=True, required=True)
|
|
legend_normal = fields.Char(
|
|
'Grey Kanban Label', default=lambda self: _('In Progress'), translate=True, required=True)
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
if self._context and self._context.get('default_job_id') and not self._context.get('hr_recruitment_stage_mono', False):
|
|
context = dict(self._context)
|
|
context.pop('default_job_id')
|
|
self = self.with_context(context)
|
|
return super(RecruitmentStage, self).default_get(fields)
|
|
|
|
|
|
class RecruitmentDegree(models.Model):
|
|
_name = "hr.recruitment.degree"
|
|
_description = "Applicant Degree"
|
|
_sql_constraints = [
|
|
('name_uniq', 'unique (name)', 'The name of the Degree of Recruitment must be unique!')
|
|
]
|
|
|
|
name = fields.Char("Degree Name", required=True, translate=True)
|
|
sequence = fields.Integer("Sequence", default=1, help="Gives the sequence order when displaying a list of degrees.")
|
|
|
|
|
|
class Applicant(models.Model):
|
|
_name = "hr.applicant"
|
|
_description = "Applicant"
|
|
_order = "priority desc, id desc"
|
|
_inherit = ['mail.thread.cc', 'mail.activity.mixin', 'utm.mixin']
|
|
|
|
name = fields.Char("Subject / Application Name", required=True, help="Email subject for applications sent via email")
|
|
active = fields.Boolean("Active", default=True, help="If the active field is set to false, it will allow you to hide the case without removing it.")
|
|
description = fields.Text("Description")
|
|
email_from = fields.Char("Email", size=128, help="Applicant email", compute='_compute_partner_phone_email',
|
|
inverse='_inverse_partner_email', store=True)
|
|
probability = fields.Float("Probability")
|
|
partner_id = fields.Many2one('res.partner', "Contact", copy=False)
|
|
create_date = fields.Datetime("Creation Date", readonly=True, index=True)
|
|
stage_id = fields.Many2one('hr.recruitment.stage', 'Stage', ondelete='restrict', tracking=True,
|
|
compute='_compute_stage', store=True, readonly=False,
|
|
domain="['|', ('job_ids', '=', False), ('job_ids', '=', job_id)]",
|
|
copy=False, index=True,
|
|
group_expand='_read_group_stage_ids')
|
|
last_stage_id = fields.Many2one('hr.recruitment.stage', "Last Stage",
|
|
help="Stage of the applicant before being in the current stage. Used for lost cases analysis.")
|
|
categ_ids = fields.Many2many('hr.applicant.category', string="Tags")
|
|
company_id = fields.Many2one('res.company', "Company", compute='_compute_company', store=True, readonly=False, tracking=True)
|
|
user_id = fields.Many2one(
|
|
'res.users', "Recruiter", compute='_compute_user',
|
|
tracking=True, store=True, readonly=False)
|
|
date_closed = fields.Datetime("Closed", compute='_compute_date_closed', store=True, index=True)
|
|
date_open = fields.Datetime("Assigned", readonly=True, index=True)
|
|
date_last_stage_update = fields.Datetime("Last Stage Update", index=True, default=fields.Datetime.now)
|
|
priority = fields.Selection(AVAILABLE_PRIORITIES, "Appreciation", default='0')
|
|
job_id = fields.Many2one('hr.job', "Applied Job", domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True)
|
|
salary_proposed_extra = fields.Char("Proposed Salary Extra", help="Salary Proposed by the Organisation, extra advantages", tracking=True)
|
|
salary_expected_extra = fields.Char("Expected Salary Extra", help="Salary Expected by Applicant, extra advantages", tracking=True)
|
|
salary_proposed = fields.Float("Proposed Salary", group_operator="avg", help="Salary Proposed by the Organisation", tracking=True)
|
|
salary_expected = fields.Float("Expected Salary", group_operator="avg", help="Salary Expected by Applicant", tracking=True)
|
|
availability = fields.Date("Availability", help="The date at which the applicant will be available to start working", tracking=True)
|
|
partner_name = fields.Char("Applicant's Name")
|
|
partner_phone = fields.Char("Phone", size=32, compute='_compute_partner_phone_email',
|
|
inverse='_inverse_partner_phone', store=True)
|
|
partner_mobile = fields.Char("Mobile", size=32, compute='_compute_partner_phone_email',
|
|
inverse='_inverse_partner_mobile', store=True)
|
|
type_id = fields.Many2one('hr.recruitment.degree', "Degree")
|
|
department_id = fields.Many2one(
|
|
'hr.department', "Department", compute='_compute_department', store=True, readonly=False,
|
|
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True)
|
|
day_open = fields.Float(compute='_compute_day', string="Days to Open", compute_sudo=True)
|
|
day_close = fields.Float(compute='_compute_day', string="Days to Close", compute_sudo=True)
|
|
delay_close = fields.Float(compute="_compute_day", string='Delay to Close', readonly=True, group_operator="avg", help="Number of days to close", store=True)
|
|
color = fields.Integer("Color Index", default=0)
|
|
emp_id = fields.Many2one('hr.employee', string="Employee", help="Employee linked to the applicant.", copy=False)
|
|
user_email = fields.Char(related='user_id.email', string="User Email", readonly=True)
|
|
attachment_number = fields.Integer(compute='_get_attachment_number', string="Number of Attachments")
|
|
employee_name = fields.Char(related='emp_id.name', string="Employee Name", readonly=False, tracking=False)
|
|
attachment_ids = fields.One2many('ir.attachment', 'res_id', domain=[('res_model', '=', 'hr.applicant')], string='Attachments')
|
|
kanban_state = fields.Selection([
|
|
('normal', 'Grey'),
|
|
('done', 'Green'),
|
|
('blocked', 'Red')], string='Kanban State',
|
|
copy=False, default='normal', required=True)
|
|
legend_blocked = fields.Char(related='stage_id.legend_blocked', string='Kanban Blocked')
|
|
legend_done = fields.Char(related='stage_id.legend_done', string='Kanban Valid')
|
|
legend_normal = fields.Char(related='stage_id.legend_normal', string='Kanban Ongoing')
|
|
application_count = fields.Integer(compute='_compute_application_count', help='Applications with the same email')
|
|
meeting_count = fields.Integer(compute='_compute_meeting_count', help='Meeting Count')
|
|
refuse_reason_id = fields.Many2one('hr.applicant.refuse.reason', string='Refuse Reason', tracking=True)
|
|
|
|
@api.depends('date_open', 'date_closed')
|
|
def _compute_day(self):
|
|
for applicant in self:
|
|
if applicant.date_open:
|
|
date_create = applicant.create_date
|
|
date_open = applicant.date_open
|
|
applicant.day_open = (date_open - date_create).total_seconds() / (24.0 * 3600)
|
|
else:
|
|
applicant.day_open = False
|
|
if applicant.date_closed:
|
|
date_create = applicant.create_date
|
|
date_closed = applicant.date_closed
|
|
applicant.day_close = (date_closed - date_create).total_seconds() / (24.0 * 3600)
|
|
applicant.delay_close = applicant.day_close - applicant.day_open
|
|
else:
|
|
applicant.day_close = False
|
|
applicant.delay_close = False
|
|
|
|
@api.depends('email_from')
|
|
def _compute_application_count(self):
|
|
application_data = self.env['hr.applicant'].with_context(active_test=False).read_group([
|
|
('email_from', 'in', list(set(self.mapped('email_from'))))], ['email_from'], ['email_from'])
|
|
application_data_mapped = dict((data['email_from'], data['email_from_count']) for data in application_data)
|
|
applicants = self.filtered(lambda applicant: applicant.email_from)
|
|
for applicant in applicants:
|
|
applicant.application_count = application_data_mapped.get(applicant.email_from, 1) - 1
|
|
(self - applicants).application_count = False
|
|
|
|
def _compute_meeting_count(self):
|
|
if self.ids:
|
|
meeting_data = self.env['calendar.event'].sudo().read_group(
|
|
[('applicant_id', 'in', self.ids)],
|
|
['applicant_id'],
|
|
['applicant_id']
|
|
)
|
|
mapped_data = {m['applicant_id'][0]: m['applicant_id_count'] for m in meeting_data}
|
|
else:
|
|
mapped_data = dict()
|
|
for applicant in self:
|
|
applicant.meeting_count = mapped_data.get(applicant.id, 0)
|
|
|
|
def _get_attachment_number(self):
|
|
read_group_res = self.env['ir.attachment'].read_group(
|
|
[('res_model', '=', 'hr.applicant'), ('res_id', 'in', self.ids)],
|
|
['res_id'], ['res_id'])
|
|
attach_data = dict((res['res_id'], res['res_id_count']) for res in read_group_res)
|
|
for record in self:
|
|
record.attachment_number = attach_data.get(record.id, 0)
|
|
|
|
@api.model
|
|
def _read_group_stage_ids(self, stages, domain, order):
|
|
# retrieve job_id from the context and write the domain: ids + contextual columns (job or default)
|
|
job_id = self._context.get('default_job_id')
|
|
search_domain = [('job_ids', '=', False)]
|
|
if job_id:
|
|
search_domain = ['|', ('job_ids', '=', job_id)] + search_domain
|
|
if stages:
|
|
search_domain = ['|', ('id', 'in', stages.ids)] + search_domain
|
|
|
|
stage_ids = stages._search(search_domain, order=order, access_rights_uid=SUPERUSER_ID)
|
|
return stages.browse(stage_ids)
|
|
|
|
@api.depends('job_id', 'department_id')
|
|
def _compute_company(self):
|
|
for applicant in self:
|
|
company_id = False
|
|
if applicant.department_id:
|
|
company_id = applicant.department_id.company_id.id
|
|
if not company_id and applicant.job_id:
|
|
company_id = applicant.job_id.company_id.id
|
|
applicant.company_id = company_id or self.env.company.id
|
|
|
|
@api.depends('job_id')
|
|
def _compute_department(self):
|
|
for applicant in self:
|
|
applicant.department_id = applicant.job_id.department_id.id
|
|
|
|
@api.depends('job_id')
|
|
def _compute_stage(self):
|
|
for applicant in self:
|
|
if applicant.job_id:
|
|
if not applicant.stage_id:
|
|
stage_ids = self.env['hr.recruitment.stage'].search([
|
|
'|',
|
|
('job_ids', '=', False),
|
|
('job_ids', '=', applicant.job_id.id),
|
|
('fold', '=', False)
|
|
], order='sequence asc', limit=1).ids
|
|
applicant.stage_id = stage_ids[0] if stage_ids else False
|
|
else:
|
|
applicant.stage_id = False
|
|
|
|
@api.depends('job_id')
|
|
def _compute_user(self):
|
|
for applicant in self:
|
|
applicant.user_id = applicant.job_id.user_id.id or self.env.uid
|
|
|
|
@api.depends('partner_id')
|
|
def _compute_partner_phone_email(self):
|
|
for applicant in self:
|
|
applicant.partner_phone = applicant.partner_id.phone
|
|
applicant.partner_mobile = applicant.partner_id.mobile
|
|
applicant.email_from = applicant.partner_id.email
|
|
|
|
def _inverse_partner_email(self):
|
|
for applicant in self.filtered(lambda a: a.partner_id and a.email_from and not a.partner_id.email):
|
|
applicant.partner_id.email = applicant.email_from
|
|
|
|
def _inverse_partner_phone(self):
|
|
for applicant in self.filtered(lambda a: a.partner_id and a.partner_phone and not a.partner_id.phone):
|
|
applicant.partner_id.phone = applicant.partner_phone
|
|
|
|
def _inverse_partner_mobile(self):
|
|
for applicant in self.filtered(lambda a: a.partner_id and a.partner_mobile and not a.partner_id.mobile):
|
|
applicant.partner_id.mobile = applicant.partner_mobile
|
|
|
|
@api.depends('stage_id')
|
|
def _compute_date_closed(self):
|
|
for applicant in self:
|
|
if applicant.stage_id and applicant.stage_id.fold:
|
|
applicant.date_closed = fields.datetime.now()
|
|
else:
|
|
applicant.date_closed = False
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
if vals.get('department_id') and not self._context.get('default_department_id'):
|
|
self = self.with_context(default_department_id=vals.get('department_id'))
|
|
if vals.get('user_id'):
|
|
vals['date_open'] = fields.Datetime.now()
|
|
if vals.get('email_from'):
|
|
vals['email_from'] = vals['email_from'].strip()
|
|
return super(Applicant, self).create(vals)
|
|
|
|
def write(self, vals):
|
|
# user_id change: update date_open
|
|
if vals.get('user_id'):
|
|
vals['date_open'] = fields.Datetime.now()
|
|
if vals.get('email_from'):
|
|
vals['email_from'] = vals['email_from'].strip()
|
|
# stage_id: track last stage before update
|
|
if 'stage_id' in vals:
|
|
vals['date_last_stage_update'] = fields.Datetime.now()
|
|
if 'kanban_state' not in vals:
|
|
vals['kanban_state'] = 'normal'
|
|
for applicant in self:
|
|
vals['last_stage_id'] = applicant.stage_id.id
|
|
res = super(Applicant, self).write(vals)
|
|
else:
|
|
res = super(Applicant, self).write(vals)
|
|
return res
|
|
|
|
def get_empty_list_help(self, help):
|
|
if 'active_id' in self.env.context:
|
|
alias_id = self.env['hr.job'].browse(self.env.context['active_id']).alias_id
|
|
else:
|
|
alias_id = False
|
|
|
|
nocontent_values = {
|
|
'help_title': _('No application yet'),
|
|
'para_1': _('Let people apply by email to save time.') ,
|
|
'para_2': _('Attachments, like resumes, get indexed automatically.'),
|
|
}
|
|
nocontent_body = """
|
|
<p class="o_view_nocontent_empty_folder">%(help_title)s</p>
|
|
<p>%(para_1)s<br/>%(para_2)s</p>"""
|
|
|
|
if alias_id and alias_id.alias_domain and alias_id.alias_name:
|
|
email = alias_id.display_name
|
|
email_link = "<a href='mailto:%s'>%s</a>" % (email, email)
|
|
nocontent_values['email_link'] = email_link
|
|
nocontent_body += """<p class="o_copy_paste_email">%(email_link)s</p>"""
|
|
|
|
return nocontent_body % nocontent_values
|
|
|
|
def action_makeMeeting(self):
|
|
""" This opens Meeting's calendar view to schedule meeting on current applicant
|
|
@return: Dictionary value for created Meeting view
|
|
"""
|
|
self.ensure_one()
|
|
partners = self.partner_id | self.user_id.partner_id | self.department_id.manager_id.user_id.partner_id
|
|
|
|
category = self.env.ref('hr_recruitment.categ_meet_interview')
|
|
res = self.env['ir.actions.act_window']._for_xml_id('calendar.action_calendar_event')
|
|
res['context'] = {
|
|
'default_partner_ids': partners.ids,
|
|
'default_user_id': self.env.uid,
|
|
'default_name': self.name,
|
|
'default_categ_ids': category and [category.id] or False,
|
|
}
|
|
return res
|
|
|
|
def action_get_attachment_tree_view(self):
|
|
action = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment')
|
|
action['context'] = {'default_res_model': self._name, 'default_res_id': self.ids[0]}
|
|
action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', self.ids)])
|
|
action['search_view_id'] = (self.env.ref('hr_recruitment.ir_attachment_view_search_inherit_hr_recruitment').id, )
|
|
return action
|
|
|
|
def action_applications_email(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Job Applications'),
|
|
'res_model': self._name,
|
|
'view_mode': 'kanban,tree,form,pivot,graph,calendar,activity',
|
|
'domain': [('email_from', 'in', self.mapped('email_from'))],
|
|
'context': {
|
|
'active_test': False
|
|
},
|
|
}
|
|
|
|
def _track_template(self, changes):
|
|
res = super(Applicant, self)._track_template(changes)
|
|
applicant = self[0]
|
|
if 'stage_id' in changes and applicant.stage_id.template_id:
|
|
res['stage_id'] = (applicant.stage_id.template_id, {
|
|
'auto_delete_message': True,
|
|
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'),
|
|
'email_layout_xmlid': 'mail.mail_notification_light'
|
|
})
|
|
return res
|
|
|
|
def _creation_subtype(self):
|
|
return self.env.ref('hr_recruitment.mt_applicant_new')
|
|
|
|
def _track_subtype(self, init_values):
|
|
record = self[0]
|
|
if 'stage_id' in init_values and record.stage_id:
|
|
return self.env.ref('hr_recruitment.mt_applicant_stage_changed')
|
|
return super(Applicant, self)._track_subtype(init_values)
|
|
|
|
def _notify_get_reply_to(self, default=None, records=None, company=None, doc_names=None):
|
|
""" Override to set alias of applicants to their job definition if any. """
|
|
aliases = self.mapped('job_id')._notify_get_reply_to(default=default, records=None, company=company, doc_names=None)
|
|
res = {app.id: aliases.get(app.job_id.id) for app in self}
|
|
leftover = self.filtered(lambda rec: not rec.job_id)
|
|
if leftover:
|
|
res.update(super(Applicant, leftover)._notify_get_reply_to(default=default, records=None, company=company, doc_names=doc_names))
|
|
return res
|
|
|
|
def _message_get_suggested_recipients(self):
|
|
recipients = super(Applicant, self)._message_get_suggested_recipients()
|
|
for applicant in self:
|
|
if applicant.partner_id:
|
|
applicant._message_add_suggested_recipient(recipients, partner=applicant.partner_id, reason=_('Contact'))
|
|
elif applicant.email_from:
|
|
email_from = applicant.email_from
|
|
if applicant.partner_name:
|
|
email_from = tools.formataddr((applicant.partner_name, email_from))
|
|
applicant._message_add_suggested_recipient(recipients, email=email_from, reason=_('Contact Email'))
|
|
return recipients
|
|
|
|
@api.model
|
|
def message_new(self, msg, custom_values=None):
|
|
""" Overrides mail_thread message_new that is called by the mailgateway
|
|
through message_process.
|
|
This override updates the document according to the email.
|
|
"""
|
|
# remove default author when going through the mail gateway. Indeed we
|
|
# do not want to explicitly set user_id to False; however we do not
|
|
# want the gateway user to be responsible if no other responsible is
|
|
# found.
|
|
self = self.with_context(default_user_id=False)
|
|
val = msg.get('from').split('<')[0]
|
|
defaults = {
|
|
'name': msg.get('subject') or _("No Subject"),
|
|
'partner_name': val,
|
|
'email_from': msg.get('from'),
|
|
'partner_id': msg.get('author_id', False),
|
|
}
|
|
if msg.get('priority'):
|
|
defaults['priority'] = msg.get('priority')
|
|
if custom_values:
|
|
defaults.update(custom_values)
|
|
return super(Applicant, self).message_new(msg, custom_values=defaults)
|
|
|
|
def _message_post_after_hook(self, message, msg_vals):
|
|
if self.email_from and not self.partner_id:
|
|
# we consider that posting a message with a specified recipient (not a follower, a specific one)
|
|
# on a document without customer means that it was created through the chatter using
|
|
# suggested recipients. This heuristic allows to avoid ugly hacks in JS.
|
|
new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email_from)
|
|
if new_partner:
|
|
if new_partner.create_date.date() == fields.Date.today():
|
|
new_partner.write({
|
|
'type': 'private',
|
|
'phone': self.partner_phone,
|
|
'mobile': self.partner_mobile,
|
|
})
|
|
self.search([
|
|
('partner_id', '=', False),
|
|
('email_from', '=', new_partner.email),
|
|
('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id})
|
|
return super(Applicant, self)._message_post_after_hook(message, msg_vals)
|
|
|
|
def create_employee_from_applicant(self):
|
|
""" Create an hr.employee from the hr.applicants """
|
|
employee = False
|
|
for applicant in self:
|
|
contact_name = False
|
|
if applicant.partner_id:
|
|
address_id = applicant.partner_id.address_get(['contact'])['contact']
|
|
contact_name = applicant.partner_id.display_name
|
|
else:
|
|
if not applicant.partner_name:
|
|
raise UserError(_('You must define a Contact Name for this applicant.'))
|
|
new_partner_id = self.env['res.partner'].create({
|
|
'is_company': False,
|
|
'type': 'private',
|
|
'name': applicant.partner_name,
|
|
'email': applicant.email_from,
|
|
'phone': applicant.partner_phone,
|
|
'mobile': applicant.partner_mobile
|
|
})
|
|
applicant.partner_id = new_partner_id
|
|
address_id = new_partner_id.address_get(['contact'])['contact']
|
|
if applicant.partner_name or contact_name:
|
|
employee_data = {
|
|
'default_name': applicant.partner_name or contact_name,
|
|
'default_job_id': applicant.job_id.id,
|
|
'default_job_title': applicant.job_id.name,
|
|
'address_home_id': address_id,
|
|
'default_department_id': applicant.department_id.id or False,
|
|
'default_address_id': applicant.company_id and applicant.company_id.partner_id
|
|
and applicant.company_id.partner_id.id or False,
|
|
'default_work_email': applicant.department_id and applicant.department_id.company_id
|
|
and applicant.department_id.company_id.email or False,
|
|
'default_work_phone': applicant.department_id.company_id.phone,
|
|
'form_view_initial_mode': 'edit',
|
|
'default_applicant_id': applicant.ids,
|
|
}
|
|
|
|
dict_act_window = self.env['ir.actions.act_window']._for_xml_id('hr.open_view_employee_list')
|
|
dict_act_window['context'] = employee_data
|
|
return dict_act_window
|
|
|
|
def archive_applicant(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Refuse Reason'),
|
|
'res_model': 'applicant.get.refuse.reason',
|
|
'view_mode': 'form',
|
|
'target': 'new',
|
|
'context': {'default_applicant_ids': self.ids, 'active_test': False},
|
|
'views': [[False, 'form']]
|
|
}
|
|
|
|
def reset_applicant(self):
|
|
""" Reinsert the applicant into the recruitment pipe in the first stage"""
|
|
default_stage = dict()
|
|
for job_id in self.mapped('job_id'):
|
|
default_stage[job_id.id] = self.env['hr.recruitment.stage'].search(
|
|
['|',
|
|
('job_ids', '=', False),
|
|
('job_ids', '=', job_id.id),
|
|
('fold', '=', False)
|
|
], order='sequence asc', limit=1).id
|
|
for applicant in self:
|
|
applicant.write(
|
|
{'stage_id': default_stage[applicant.job_id.id], 'refuse_reason_id': False})
|
|
|
|
def toggle_active(self):
|
|
res = super(Applicant, self).toggle_active()
|
|
applicant_active = self.filtered(lambda applicant: applicant.active)
|
|
if applicant_active:
|
|
applicant_active.reset_applicant()
|
|
applicant_inactive = self.filtered(lambda applicant: not applicant.active)
|
|
if applicant_inactive:
|
|
return applicant_inactive.archive_applicant()
|
|
return res
|
|
|
|
|
|
class ApplicantCategory(models.Model):
|
|
_name = "hr.applicant.category"
|
|
_description = "Category of applicant"
|
|
|
|
def _get_default_color(self):
|
|
return randint(1, 11)
|
|
|
|
name = fields.Char("Tag Name", required=True)
|
|
color = fields.Integer(string='Color Index', default=_get_default_color)
|
|
|
|
_sql_constraints = [
|
|
('name_uniq', 'unique (name)', "Tag name already exists !"),
|
|
]
|
|
|
|
|
|
class ApplicantRefuseReason(models.Model):
|
|
_name = "hr.applicant.refuse.reason"
|
|
_description = 'Refuse Reason of Applicant'
|
|
|
|
name = fields.Char('Description', required=True, translate=True)
|
|
active = fields.Boolean('Active', default=True)
|