[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:
Kevin Baptiste
2019-07-17 07:02:01 +00:00
committed by Yannick Tivisse
parent bd3a553ad9
commit 81a2ad4e05
10 changed files with 69 additions and 70 deletions
+2 -2
View File
@@ -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}
+51 -25
View File
@@ -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]
+9 -10
View File
@@ -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>