[IMP] hr_recruitment: Improve general usability (back2basics)
TaskID: 2033500 closes odoo/odoo#34858 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
bd3a553ad9
commit
81a2ad4e05
@@ -82,8 +82,8 @@ class Job(models.Model):
|
||||
self.ensure_one()
|
||||
return self.env['hr.recruitment.stage'].search([
|
||||
'|',
|
||||
('job_id', '=', False),
|
||||
('job_id', '=', self.id)], order='sequence asc', limit=1)
|
||||
('job_ids', '=', False),
|
||||
('job_ids', '=', self.id)], order='sequence asc', limit=1)
|
||||
|
||||
def _compute_new_application_count(self):
|
||||
first_stages = {job.id: job._get_first_stage().id for job in self}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from odoo import api, fields, models, SUPERUSER_ID
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
|
||||
AVAILABLE_PRIORITIES = [
|
||||
('0', 'Normal'),
|
||||
@@ -47,19 +48,19 @@ class RecruitmentStage(models.Model):
|
||||
_description = "Recruitment Stages"
|
||||
_order = 'sequence'
|
||||
|
||||
name = fields.Char("Stage name", required=True, translate=True)
|
||||
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_id = fields.Many2one('hr.job', string='Job Specific',
|
||||
ondelete='cascade',
|
||||
help='Specific job that uses this stage. Other jobs will not use this stage.')
|
||||
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', "Automated Email",
|
||||
'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 Recruitment Pipe",
|
||||
"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)
|
||||
@@ -98,8 +99,8 @@ class Applicant(models.Model):
|
||||
if self._context.get('default_job_id'):
|
||||
return self.env['hr.recruitment.stage'].search([
|
||||
'|',
|
||||
('job_id', '=', False),
|
||||
('job_id', '=', self._context['default_job_id']),
|
||||
('job_ids', '=', False),
|
||||
('job_ids', '=', self._context['default_job_id']),
|
||||
('fold', '=', False)
|
||||
], order='sequence asc', limit=1).id
|
||||
return False
|
||||
@@ -113,15 +114,21 @@ class Applicant(models.Model):
|
||||
company_id = self.env.company
|
||||
return company_id
|
||||
|
||||
def _get_stage_id_domain(self):
|
||||
domain = [('job_ids', '=', False)]
|
||||
if self.job_id:
|
||||
domain = expression.OR([domain, [('job_ids', 'in', self.job_id)]])
|
||||
return domain
|
||||
|
||||
name = fields.Char("Subject / Application Name", required=True)
|
||||
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")
|
||||
probability = fields.Float("Probability")
|
||||
partner_id = fields.Many2one('res.partner', "Contact")
|
||||
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,
|
||||
domain="['|', ('job_id', '=', False), ('job_id', '=', job_id)]",
|
||||
domain=_get_stage_id_domain,
|
||||
copy=False, index=True,
|
||||
group_expand='_read_group_stage_ids',
|
||||
default=_default_stage_id)
|
||||
@@ -185,7 +192,7 @@ class Applicant(models.Model):
|
||||
('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)
|
||||
for applicant in self.filtered(lambda applicant: applicant.email_from):
|
||||
applicant.application_count = application_data_mapped.get(applicant.email_from, 0)
|
||||
applicant.application_count = application_data_mapped.get(applicant.email_from, 1) - 1
|
||||
|
||||
@api.multi
|
||||
def _get_attachment_number(self):
|
||||
@@ -200,9 +207,9 @@ class Applicant(models.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_id', '=', False)]
|
||||
search_domain = [('job_ids', '=', False)]
|
||||
if job_id:
|
||||
search_domain = ['|', ('job_id', '=', job_id)] + search_domain
|
||||
search_domain = ['|', ('job_ids', '=', job_id)] + search_domain
|
||||
if stages:
|
||||
search_domain = ['|', ('id', 'in', stages.ids)] + search_domain
|
||||
|
||||
@@ -227,8 +234,8 @@ class Applicant(models.Model):
|
||||
if not stage_id:
|
||||
stage_ids = self.env['hr.recruitment.stage'].search([
|
||||
'|',
|
||||
('job_id', '=', False),
|
||||
('job_id', '=', job.id),
|
||||
('job_ids', '=', False),
|
||||
('job_ids', '=', job.id),
|
||||
('fold', '=', False)
|
||||
], order='sequence asc', limit=1).ids
|
||||
stage_id = stage_ids[0] if stage_ids else False
|
||||
@@ -239,6 +246,21 @@ class Applicant(models.Model):
|
||||
'stage_id': stage_id
|
||||
}}
|
||||
|
||||
@api.onchange('email_from')
|
||||
def onchange_email_from(self):
|
||||
if self.partner_id and self.email_from and not self.partner_id.email:
|
||||
self.partner_id.email = self.email_from
|
||||
|
||||
@api.onchange('partner_phone')
|
||||
def onchange_partner_phone(self):
|
||||
if self.partner_id and self.partner_phone and not self.partner_id.phone:
|
||||
self.partner_id.phone = self.partner_phone
|
||||
|
||||
@api.onchange('partner_mobile')
|
||||
def onchange_partner_mobile(self):
|
||||
if self.partner_id and self.partner_mobile and not self.partner_id.mobile:
|
||||
self.partner_id.mobile = self.partner_mobile
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def onchange_partner_id(self):
|
||||
self.partner_phone = self.partner_id.phone
|
||||
@@ -385,7 +407,10 @@ class Applicant(models.Model):
|
||||
if applicant.partner_id:
|
||||
applicant._message_add_suggested_recipient(recipients, partner=applicant.partner_id, reason=_('Contact'))
|
||||
elif applicant.email_from:
|
||||
applicant._message_add_suggested_recipient(recipients, email=applicant.email_from, reason=_('Contact Email'))
|
||||
email_from = applicant.email_from
|
||||
if applicant.partner_name:
|
||||
email_from = '%s<%s>' % (applicant.partner_name, email_from)
|
||||
applicant._message_add_suggested_recipient(recipients, email=email_from, reason=_('Contact Email'))
|
||||
return recipients
|
||||
|
||||
@api.model
|
||||
@@ -434,7 +459,9 @@ class Applicant(models.Model):
|
||||
if applicant.partner_id:
|
||||
address_id = applicant.partner_id.address_get(['contact'])['contact']
|
||||
contact_name = applicant.partner_id.display_name
|
||||
else :
|
||||
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,
|
||||
'name': applicant.partner_name,
|
||||
@@ -443,11 +470,10 @@ class Applicant(models.Model):
|
||||
'mobile': applicant.partner_mobile
|
||||
})
|
||||
address_id = new_partner_id.address_get(['contact'])['contact']
|
||||
if applicant.job_id and (applicant.partner_name or contact_name):
|
||||
applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1})
|
||||
if applicant.partner_name or contact_name:
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': applicant.partner_name or contact_name,
|
||||
'job_id': applicant.job_id.id,
|
||||
'job_id': applicant.job_id.id or False,
|
||||
'address_home_id': address_id,
|
||||
'department_id': applicant.department_id.id or False,
|
||||
'address_id': applicant.company_id and applicant.company_id.partner_id
|
||||
@@ -457,11 +483,11 @@ class Applicant(models.Model):
|
||||
'work_phone': applicant.department_id and applicant.department_id.company_id
|
||||
and applicant.department_id.company_id.phone or False})
|
||||
applicant.write({'emp_id': employee.id})
|
||||
applicant.job_id.message_post(
|
||||
body=_('New Employee %s Hired') % applicant.partner_name if applicant.partner_name else applicant.name,
|
||||
subtype="hr_recruitment.mt_job_applicant_hired")
|
||||
else:
|
||||
raise UserError(_('You must define an Applied Job and a Contact Name for this applicant.'))
|
||||
if applicant.job_id:
|
||||
applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1})
|
||||
applicant.job_id.message_post(
|
||||
body=_('New Employee %s Hired') % applicant.partner_name if applicant.partner_name else applicant.name,
|
||||
subtype="hr_recruitment.mt_job_applicant_hired")
|
||||
|
||||
employee_action = self.env.ref('hr.open_view_employee_list')
|
||||
dict_act_window = employee_action.read([])[0]
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<field name="user_id"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click">
|
||||
<div t-attf-class="#{kanban_color(record.color.raw_value)}">
|
||||
<div t-attf-class="o_kanban_card_header">
|
||||
<div class="o_kanban_card_header_title">
|
||||
<div class="o_primary col-12"><t t-esc="record.name.value"/></div>
|
||||
@@ -41,17 +41,16 @@
|
||||
</div>
|
||||
<div class="container o_kanban_card_content">
|
||||
<t t-if="record.state.raw_value == 'recruit'">
|
||||
<div>
|
||||
<field name="new_application_count"/> New Applications <br/>
|
||||
<field name="no_of_hired_employee"/> Hired Employees
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-secondary" name="%(action_hr_job_applications)d" type="action">Applications</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<field name="new_application_count"/> New Applications <br/>
|
||||
<field name="no_of_hired_employee"/> Hired Employees
|
||||
</div>
|
||||
</div>
|
||||
<div name="kanban_boxes" class="row o_recruitment_kanban_boxes">
|
||||
<div class="o_recruitment_kanban_box o_kanban_primary_bottom bottom_block">
|
||||
<a name="action_get_attachment_tree_view" type="object">
|
||||
<span title='Documents'><i class='fa fa-paperclip' role="img" aria-label="Documents"/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="record.state.raw_value == 'open'">
|
||||
|
||||
@@ -69,9 +69,9 @@
|
||||
<form string="Jobs - Recruitment Form">
|
||||
<header>
|
||||
<button string="Create Employee" name="create_employee_from_applicant" type="object"
|
||||
class="oe_highlight" attrs="{'invisible': ['|',('emp_id', '!=', False),('active', '=', False)]}"/>
|
||||
class="oe_highlight" attrs="{'invisible': ['|',('emp_id', '!=', False),('active', '=', False)]}" confirm="Are you sure?"/>
|
||||
<button string="Refuse" name="archive_applicant" type="object" attrs="{'invisible': [('active', '=', False)]}"/>
|
||||
<button string="Reopen Application" name="reset_applicant" type="object" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<button string="Restore" name="reset_applicant" type="object" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<field name="stage_id" widget="statusbar" options="{'clickable': '1', 'fold_field': 'fold'}" attrs="{'invisible': [('active', '=', False),('emp_id', '=', False)]}"/>
|
||||
</header>
|
||||
<sheet>
|
||||
@@ -82,7 +82,7 @@
|
||||
icon="fa-file-o"
|
||||
type="object"
|
||||
attrs="{'invisible': [('application_count', '=', 0)]}">
|
||||
<field name="application_count" widget="statinfo" string="Applications"/>
|
||||
<field name="application_count" widget="statinfo" string="Other Applications"/>
|
||||
</button>
|
||||
<button name="action_makeMeeting" class="oe_stat_button" icon="fa-calendar" type="object"
|
||||
help="Schedule interview with this applicant">
|
||||
@@ -125,7 +125,7 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="partner_id" invisible="1" />
|
||||
<field name="email_from" widget="email"/>
|
||||
<field name="email_cc" groups="base.group_no_one"/>
|
||||
<field name="partner_phone"/>
|
||||
@@ -704,11 +704,11 @@ action = act
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="sequence" groups="base.group_no_one"/>
|
||||
<field name="job_id"/>
|
||||
<field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
|
||||
</group>
|
||||
<group name="stage_details">
|
||||
<field name="fold"/>
|
||||
<field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
|
||||
<field name="job_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="tooltips" string="Tooltips">
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
'email_from',
|
||||
'partner_name',
|
||||
'partner_phone',
|
||||
'residence_country',
|
||||
'job_id',
|
||||
'department_id',
|
||||
]"/>
|
||||
|
||||
@@ -29,8 +29,6 @@ class Applicant(models.Model):
|
||||
|
||||
_inherit = 'hr.applicant'
|
||||
|
||||
residence_country = fields.Char(string="Residence Country")
|
||||
|
||||
def website_form_input_filter(self, request, values):
|
||||
if 'partner_name' in values:
|
||||
values.setdefault('name', '%s\'s Application' % values['partner_name'])
|
||||
|
||||
@@ -18,10 +18,6 @@ odoo.define('website_hr_recruitment.tour', function(require) {
|
||||
content: "Complete phone number",
|
||||
trigger: "input[name=partner_phone]",
|
||||
run: "text 118.218"
|
||||
}, {
|
||||
content: "Complete Residence Country",
|
||||
trigger: "input[name=residence_country]",
|
||||
run: "text Belgium"
|
||||
}, {
|
||||
content: "Complete Subject",
|
||||
trigger: "textarea[name=description]",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<field name="website_url" invisible="1"/>
|
||||
<div class="o_recruitment_kanban_box o_kanban_primary_bottom bottom_block" style="padding-left:8px; text-align:right">
|
||||
<span>
|
||||
<a t-attf-href="#{record.website_url.raw_value}">Job Description</a>
|
||||
<a t-attf-href="#{record.website_url.raw_value}">Website Description</a>
|
||||
</span>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
@@ -47,15 +47,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_applicant_form_inherit_website" model="ir.ui.view">
|
||||
<field name="name">hr.applicant.form</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="inherit_id" ref="hr_recruitment.hr_applicant_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='type_id']" position="after">
|
||||
<field name="residence_country"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -144,14 +144,6 @@
|
||||
<input type="text" class="form-control o_website_form_input" name="partner_phone" required=""/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row form-field o_website_form_required_custom">
|
||||
<div class="col-lg-3 col-md-4 text-right">
|
||||
<label class="col-form-label" for="residence_country">Your Residence Country</label>
|
||||
</div>
|
||||
<div class="col-lg-7 col-md-8">
|
||||
<input type="text" class="form-control o_website_form_input" name="residence_country" required=""/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row form-field">
|
||||
<div class="col-lg-3 col-md-4 text-right">
|
||||
<label class="col-form-label" for="description">Short Introduction</label>
|
||||
|
||||
Reference in New Issue
Block a user