[IMP] hr_recruitment : add favorite job & new application count, modify views
- hr.job receives the favorite functionnality. - Now, a newly created hr.job is favorite by default. - hr.job receives a new field new_application_count that computes the number of applications which are new in the flow for that job (1st step of the recruitment flow). - hr.job kanban boxes now display the field described just above. - hr.job kanban boxes are now clickable and thus open related applications in a kanban view. - Views are modified according to the images given in the related task (see Task ID below). These changes are details such as modifying strings that are displayed or their position in the view, or again icons that are displayed. -> Note : Check related task to see decisions that have been made concerning these images. - Search on Email on hr.applicant has been separated so that it is clearer. - Rename old view crm_case_form_view_job into hr_applicant_view_form Task ID : 1896305
This commit is contained in:
committed by
Yannick Tivisse
parent
8c7970bf8f
commit
fcba642d6f
@@ -506,7 +506,7 @@
|
||||
<h1><field name="name" placeholder="e.g. Sales Manager"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Recruitment" name="recruitment">
|
||||
<group name="recruitment">
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
<field name="department_id"/>
|
||||
<field name="no_of_recruitment"/>
|
||||
|
||||
@@ -6,21 +6,29 @@ from odoo import api, fields, models, _
|
||||
class Job(models.Model):
|
||||
_name = "hr.job"
|
||||
_inherit = ["mail.alias.mixin", "hr.job"]
|
||||
_order = "state desc, name asc"
|
||||
|
||||
@api.model
|
||||
def _default_address_id(self):
|
||||
return self.env.user.company_id.partner_id
|
||||
|
||||
def _get_default_favorite_user_ids(self):
|
||||
return [(6, 0, [self.env.uid])]
|
||||
|
||||
address_id = fields.Many2one(
|
||||
'res.partner', "Job Location", default=_default_address_id,
|
||||
help="Address where employees are working")
|
||||
application_ids = fields.One2many('hr.applicant', 'job_id', "Applications")
|
||||
application_count = fields.Integer(compute='_compute_application_count', string="Application Count")
|
||||
new_application_count = fields.Integer(
|
||||
compute='_compute_new_application_count', string="New Application",
|
||||
help="Number of applications that are new in the flow (typically at first step of the flow)")
|
||||
manager_id = fields.Many2one(
|
||||
'hr.employee', related='department_id.manager_id', string="Department Manager",
|
||||
readonly=True, store=True)
|
||||
user_id = fields.Many2one('res.users', "Recruitment Responsible", tracking=True)
|
||||
hr_responsible_id = fields.Many2one('res.users', "HR Responsible", tracking=True,
|
||||
user_id = fields.Many2one('res.users', "Responsible", tracking=True)
|
||||
hr_responsible_id = fields.Many2one(
|
||||
'res.users', "HR Responsible", tracking=True,
|
||||
help="Person responsible of validating the employee's contracts.")
|
||||
document_ids = fields.One2many('ir.attachment', compute='_compute_document_ids', string="Documents")
|
||||
documents_count = fields.Integer(compute='_compute_document_ids', string="Document Count")
|
||||
@@ -28,6 +36,22 @@ class Job(models.Model):
|
||||
'mail.alias', "Alias", ondelete="restrict", required=True,
|
||||
help="Email alias for this job position. New emails will automatically create new applicants for this job position.")
|
||||
color = fields.Integer("Color Index")
|
||||
is_favorite = fields.Boolean(compute='_compute_is_favorite', inverse='_inverse_is_favorite')
|
||||
favorite_user_ids = fields.Many2many('res.users', 'job_favorite_user_rel', 'job_id', 'user_id', default=_get_default_favorite_user_ids)
|
||||
|
||||
def _compute_is_favorite(self):
|
||||
for job in self:
|
||||
job.is_favorite = self.env.user in job.favorite_user_ids
|
||||
|
||||
def _inverse_is_favorite(self):
|
||||
unfavorited_jobs = favorited_jobs = self.env['hr.job']
|
||||
for job in self:
|
||||
if self.env.user in job.favorite_user_ids:
|
||||
unfavorited_jobs |= job
|
||||
else:
|
||||
favorited_jobs |= job
|
||||
favorited_jobs.write({'favorite_user_ids': [(4, self.env.uid)]})
|
||||
unfavorited_jobs.write({'favorite_user_ids': [(3, self.env.uid)]})
|
||||
|
||||
def _compute_document_ids(self):
|
||||
applicants = self.mapped('application_ids').filtered(lambda self: not self.emp_id)
|
||||
@@ -54,6 +78,22 @@ class Job(models.Model):
|
||||
for job in self:
|
||||
job.application_count = result.get(job.id, 0)
|
||||
|
||||
def _get_first_stage(self):
|
||||
self.ensure_one()
|
||||
return self.env['hr.recruitment.stage'].search([
|
||||
'|',
|
||||
('job_id', '=', False),
|
||||
('job_id', '=', 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}
|
||||
mapped_data = dict.fromkeys(self.ids, 0)
|
||||
for applicant in self.mapped('application_ids'):
|
||||
if applicant.stage_id.id == first_stages.get(applicant.job_id.id):
|
||||
mapped_data[applicant.job_id.id] += 1
|
||||
for job in self:
|
||||
job.new_application_count = mapped_data.get(job.id)
|
||||
|
||||
def get_alias_model_name(self, vals):
|
||||
return 'hr.applicant'
|
||||
|
||||
@@ -68,6 +108,7 @@ class Job(models.Model):
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
vals['favorite_user_ids'] = vals.get('favorite_user_ids', []) + [(4, self.env.uid)]
|
||||
return super(Job, self.with_context(mail_create_nolog=True)).create(vals)
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -96,14 +96,12 @@ class Applicant(models.Model):
|
||||
|
||||
def _default_stage_id(self):
|
||||
if self._context.get('default_job_id'):
|
||||
ids = self.env['hr.recruitment.stage'].search([
|
||||
return self.env['hr.recruitment.stage'].search([
|
||||
'|',
|
||||
('job_id', '=', False),
|
||||
('job_id', '=', self._context['default_job_id']),
|
||||
('fold', '=', False)
|
||||
], order='sequence asc', limit=1).ids
|
||||
if ids:
|
||||
return ids[0]
|
||||
], order='sequence asc', limit=1).id
|
||||
return False
|
||||
|
||||
def _default_company_id(self):
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
odoo.define('job.update_kanban', function (require) {
|
||||
'use strict';
|
||||
var KanbanRecord = require('web.KanbanRecord');
|
||||
|
||||
KanbanRecord.include({
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_openRecord: function () {
|
||||
if (this.modelName === 'hr.job' && this.$(".o_hr_job_boxes a").length) {
|
||||
this.$(".o_hr_job_boxes a").first().click();
|
||||
} else {
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -16,19 +16,22 @@
|
||||
<field name="name"/>
|
||||
<field name="alias_name"/>
|
||||
<field name="alias_domain"/>
|
||||
<field name="is_favorite"/>
|
||||
<field name="department_id"/>
|
||||
<field name="no_of_recruitment"/>
|
||||
<field name="color"/>
|
||||
<field name="new_application_count"/>
|
||||
<field name="no_of_hired_employee"/>
|
||||
<field name="manager_id"/>
|
||||
<field name="state"/>
|
||||
<field name="user_id"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="#{kanban_color(record.color.raw_value)}">
|
||||
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click">
|
||||
<div t-attf-class="o_kanban_card_header">
|
||||
<div class="o_kanban_card_header_title">
|
||||
<div class="o_primary"><t t-esc="record.name.value"/></div>
|
||||
<div class="o_primary col-12"><t t-esc="record.name.value"/></div>
|
||||
<field name="is_favorite" widget="boolean_favorite" nolabel="1"/>
|
||||
<div t-if="record.alias_name.value and record.alias_domain.value and record.state.raw_value == 'recruit'" class="o_secondary o_job_alias">
|
||||
<small> <i class="fa fa-envelope-o" role="img" aria-label="Alias" title="Alias"></i> <field name="alias_id"/> </small>
|
||||
</div>
|
||||
@@ -39,24 +42,17 @@
|
||||
</div>
|
||||
<div class="container o_kanban_card_content">
|
||||
<t t-if="record.state.raw_value == 'recruit'">
|
||||
<div class="row">
|
||||
<div name="application_button" class="col-6 o_kanban_primary_left">
|
||||
<button class="btn btn-primary" name="%(action_hr_job_applications)d" type="action">Application(s)</button>
|
||||
</div>
|
||||
<div>
|
||||
<field name="new_application_count"/> New Applications <br/>
|
||||
<field name="no_of_hired_employee"/> Hired Employees
|
||||
</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-book' role="img" aria-label="Documents"/>
|
||||
<span title='Documents'><i class='fa fa-paperclip' role="img" aria-label="Documents"/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="o_recruitment_kanban_box o_kanban_primary_bottom bottom_block">
|
||||
<span>
|
||||
<field name="no_of_hired_employee"/>
|
||||
Hired Employees
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="record.state.raw_value == 'open'">
|
||||
@@ -66,18 +62,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div><div class="container o_kanban_card_manage_pane dropdown-menu" role="menu">
|
||||
<div t-if="widget.editable" class="o_kanban_card_manage_settings row">
|
||||
<div class="col-8" role="menuitem" aria-haspopup="true">
|
||||
<ul class="oe_kanban_colorpicker" data-field="color" role="menu"/>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a role="menuitem" t-if="record.state.raw_value == 'recruit'" name="set_open" type="object">Recruitment Done</a>
|
||||
<a role="menuitem" t-if="record.state.raw_value == 'open'" name="set_recruit" type="object">Start recruitment</a>
|
||||
<br role="separator"/>
|
||||
<a role="menuitem" name="edit_job" type="edit">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_kanban_card_manage_pane dropdown-menu" role="menu">
|
||||
<div class="o_kanban_card_manage_section">
|
||||
<div role="menuitem"><a t-if="record.state.raw_value == 'recruit'" name="set_open" type="object">Recruitment Done</a></div>
|
||||
<div role="menuitem"><a t-if="record.state.raw_value == 'open'" name="set_recruit" type="object">Start recruitment</a></div>
|
||||
<div role="menuitem"><a t-if="widget.editable" name="edit_job" type="edit">Edit</a></div>
|
||||
</div>
|
||||
<div role="menuitem" aria-haspopup="true">
|
||||
<ul class="oe_kanban_colorpicker" data-field="color" role="menu"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_hr_job_boxes">
|
||||
<a class="o_hr_job_box" name="%(action_hr_job_applications)d" type="action"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<template id="assets_backend" name="hr_recruitment assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/hr_recruitment/static/src/scss/hr_job.scss"/>
|
||||
<script type="text/javascript" src="/hr_recruitment/static/src/js/recruitment.js"></script>
|
||||
<script type="text/javascript" src="/hr_recruitment/static/src/js/tour.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="crm_case_form_view_job">
|
||||
<record model="ir.ui.view" id="hr_applicant_view_form">
|
||||
<field name="name">Jobs - Recruitment Form</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="arch" type="xml">
|
||||
@@ -202,7 +202,8 @@
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Applicants">
|
||||
<field name="partner_name" filter_domain="['|','|',('name','ilike',self),('partner_name','ilike',self),('email_from','ilike',self)]" string="Subject / Applicant"/>
|
||||
<field name="partner_name" filter_domain="['|', ('name', 'ilike', self), ('partner_name', 'ilike', self)]" string="Subject / Applicant"/>
|
||||
<field name="email_from" filter_domain="[('email_from', 'ilike', self)]" string="Email"/>
|
||||
<filter string="My Applications" name="my_applications" domain="[('user_id', '=', uid)]"/>
|
||||
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
@@ -296,13 +297,13 @@
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_applicant_kanban oe_semantic_html_override">
|
||||
<div class="o_dropdown_kanban dropdown">
|
||||
|
||||
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
|
||||
<span class="fa fa-ellipsis-v"/>
|
||||
</a>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<t t-if="widget.deletable"><a role="menuitem" type="delete" class="dropdown-item">Delete</a></t>
|
||||
<a role="menuitem" name="action_makeMeeting" type="object" class="dropdown-item">Schedule Interview</a>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<ul class="oe_kanban_colorpicker" data-field="color"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -335,7 +336,7 @@
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<a name="action_get_attachment_tree_view" type="object">
|
||||
<span title='Documents'><i class='fa fa-book' role="img" aria-label="Documents"/>
|
||||
<span title='Documents'><i class='fa fa-paperclip' role="img" aria-label="Documents"/>
|
||||
<t t-esc="record.attachment_number.raw_value"/>
|
||||
</span>
|
||||
</a>
|
||||
@@ -403,6 +404,9 @@
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
</field>
|
||||
<xpath expr="//filter[@name='in_position']" position="before">
|
||||
<filter string="My Favorites" name="my_favorite_jobs" domain="[('favorite_user_ids', 'in', uid)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -466,7 +470,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='department_id']" position="after">
|
||||
<field name="address_id" context="{'show_address': 1}" domain= "[('is_company', '=', True )]" options="{'always_reload': True}"/>
|
||||
<label for="alias_name" string="Email alias" attrs="{'invisible': [('alias_domain', '=', False)]}" help="Define a specific contact address for this job position. If you keep it empty, the default email address will be used which is in human resources settings"/>
|
||||
<label for="alias_name" string="Email Alias" attrs="{'invisible': [('alias_domain', '=', False)]}" help="Define a specific contact address for this job position. If you keep it empty, the default email address will be used which is in human resources settings"/>
|
||||
<div name="alias_def" attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
<field name="alias_id" class="oe_read_only" string="Email Alias" required="0"/>
|
||||
<div class="oe_edit_only" name="edit_alias">
|
||||
@@ -475,11 +479,6 @@
|
||||
</div>
|
||||
<field name="user_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//group[@name='recruitment']" position="after">
|
||||
<group string="Offer" name="offer">
|
||||
<field name="hr_responsible_id"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<div name="button_box" position="inside">
|
||||
<button class="oe_stat_button"
|
||||
icon="fa-file-o"
|
||||
@@ -495,7 +494,7 @@
|
||||
<field name="no_of_employee" widget="statinfo" string="Employees"/>
|
||||
</button>
|
||||
<button class="oe_stat_button"
|
||||
icon="fa-book"
|
||||
icon="fa-paperclip"
|
||||
name="action_get_attachment_tree_view"
|
||||
type="object">
|
||||
<field name="documents_count" widget="statinfo" string="Documents"/>
|
||||
@@ -570,7 +569,7 @@
|
||||
<record model="ir.actions.act_window.view" id="action_hr_sec_form_view_act_job">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="crm_case_form_view_job"/>
|
||||
<field name="view_id" ref="hr_applicant_view_form"/>
|
||||
<field name="act_window_id" ref="crm_case_categ0_act_job"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_case_form_view_job_inherit" model="ir.ui.view">
|
||||
<record id="hr_applicant_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.applicant.form.inherit</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="inherit_id" ref="hr_recruitment.crm_case_form_view_job"/>
|
||||
<field name="inherit_id" ref="hr_recruitment.hr_applicant_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_makeMeeting']" position="after">
|
||||
<button name="action_start_survey"
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<field name="survey_id"/>
|
||||
</xpath>
|
||||
<xpath expr='//a[@name="edit_job"]' position="after">
|
||||
<br/>
|
||||
<a t-if="record.survey_id.raw_value" name="action_print_survey" type="object" title="Display Interview Form">Interview Form</a>
|
||||
<span t-if="!record.survey_id.raw_value">No Interview Form</span>
|
||||
</xpath>
|
||||
|
||||
@@ -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}">Website Editor</a>
|
||||
<a t-attf-href="#{record.website_url.raw_value}">Job Description</a>
|
||||
</span>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user