[IMP] hr_recruitment: Add refuse reason and a wizard when archiving
Purpose ======= Like in crm, having a refusal reason to get some metrics on recruitment + recruitement dashboard id=2003841 closes odoo/odoo#38467 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
90fa3e3c60
commit
b2a2d0a730
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import wizard
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
'views/hr_job_views.xml',
|
||||
'views/mail_activity_views.xml',
|
||||
'views/digest_views.xml',
|
||||
'wizard/applicant_refuse_reason_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/hr_recruitment_demo.xml',
|
||||
|
||||
@@ -382,5 +382,16 @@
|
||||
<field name="fold" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- applicant refuse reason -->
|
||||
<record id="refuse_reason_1" model="hr.applicant.refuse.reason">
|
||||
<field name="name">Doesn't fit the job requirements</field>
|
||||
</record>
|
||||
<record id="refuse_reason_2" model="hr.applicant.refuse.reason">
|
||||
<field name="name">The applicant is not interested anymore</field>
|
||||
</record>
|
||||
<record id="refuse_reason_3" model="hr.applicant.refuse.reason">
|
||||
<field name="name">The applicant gets a better offer</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<field name="name">Sales Manager</field>
|
||||
<field name="job_id" ref="hr.job_marketing"/>
|
||||
<field name="department_id" ref="hr.dep_sales"/>
|
||||
<field name="medium_id" ref="utm.utm_medium_direct"/>
|
||||
<field name="type_id" ref="degree_graduate"/>
|
||||
<field name="categ_ids" eval="[(6,0,[ref('tag_applicant_sales')])]"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
@@ -68,6 +69,7 @@
|
||||
<field name="name">Developer PHP</field>
|
||||
<field name="job_id" ref="hr.job_developer"/>
|
||||
<field name="department_id" ref="hr.dep_rd"/>
|
||||
<field name="medium_id" ref="utm.utm_medium_email"/>
|
||||
<field name="type_id" ref="degree_graduate"/>
|
||||
<field name="categ_ids" eval="[(6,0,[ref('tag_applicant_it')])]"/>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
@@ -92,6 +94,7 @@
|
||||
<field name="name">Developer Python</field>
|
||||
<field name="job_id" ref="hr.job_developer"/>
|
||||
<field name="department_id" ref="hr.dep_rd"/>
|
||||
<field name="medium_id" ref="utm.utm_medium_email"/>
|
||||
<field name="type_id" ref="degree_graduate"/>
|
||||
<field name="categ_ids" eval="[(6,0,[ref('tag_applicant_it')])]"/>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
@@ -160,6 +163,7 @@
|
||||
</record>
|
||||
<record id="hr_case_yrsexperienceinphp0" model="hr.applicant">
|
||||
<field name="name">Marketing Job</field>
|
||||
<field eval="(datetime.now()+relativedelta(months=-2)).strftime('%Y-%m-03 01:00:00')" name="create_date"/>
|
||||
<field name="job_id" ref="hr.job_marketing"/>
|
||||
<field name="department_id" ref="hr.dep_sales"/>
|
||||
<field name="type_id" ref="degree_graduate"/>
|
||||
@@ -170,6 +174,7 @@
|
||||
</record>
|
||||
<record id="hr_case_marketingjob0" model="hr.applicant">
|
||||
<field name="name">More than 5 yrs Experience in PHP</field>
|
||||
<field eval="(datetime.now()+relativedelta(months=-1)).strftime('%Y-%m-08 01:00:00')" name="create_date"/>
|
||||
<field name="job_id" ref="hr.job_developer"/>
|
||||
<field name="department_id" ref="hr.dep_rd"/>
|
||||
<field name="type_id" ref="degree_licenced"/>
|
||||
|
||||
@@ -165,6 +165,7 @@ class Applicant(models.Model):
|
||||
legend_normal = fields.Char(related='stage_id.legend_normal', string='Kanban Ongoing', readonly=False)
|
||||
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):
|
||||
@@ -488,12 +489,39 @@ class Applicant(models.Model):
|
||||
return dict_act_window
|
||||
|
||||
def archive_applicant(self):
|
||||
self.write({'active': False})
|
||||
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_id = self._default_stage_id()
|
||||
self.write({'active': True, 'stage_id': default_stage_id})
|
||||
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):
|
||||
@@ -506,3 +534,11 @@ class ApplicantCategory(models.Model):
|
||||
_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)
|
||||
|
||||
@@ -3,6 +3,7 @@ access_hr_applicant_user,hr.applicant.user,model_hr_applicant,group_hr_recruitme
|
||||
access_hr_recruitment_stage_user,hr.recruitment.stage.user,model_hr_recruitment_stage,group_hr_recruitment_user,1,0,0,0
|
||||
access_hr_recruitment_stage_manager,hr.recruitment.stage.manager,model_hr_recruitment_stage,group_hr_recruitment_user,1,1,1,1
|
||||
access_hr_recruitment_degree,hr.recruitment.degree,model_hr_recruitment_degree,group_hr_recruitment_user,1,1,1,1
|
||||
access_hr_recruitment_refuse_reason,hr.applicant.refuse.reason,model_hr_applicant_refuse_reason,group_hr_recruitment_user,1,1,1,1
|
||||
access_res_partner_hr_user,res.partner.user,base.model_res_partner,group_hr_recruitment_user,1,1,1,1
|
||||
access_calendar_event_hruser,calendar.event.hruser,calendar.model_calendar_event,group_hr_recruitment_user,1,1,1,1
|
||||
access_hr_recruitment_source_hr_officer,hr.recruitment.source,model_hr_recruitment_source,group_hr_recruitment_user,1,1,1,1
|
||||
|
||||
|
@@ -71,8 +71,8 @@
|
||||
<header>
|
||||
<button string="Create Employee" name="create_employee_from_applicant" type="object"
|
||||
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="Restore" name="reset_applicant" type="object" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<button string="Refuse" name="toggle_active" type="object" attrs="{'invisible': [('active', '=', False)]}"/>
|
||||
<button string="Restore" name="toggle_active" 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>
|
||||
@@ -108,6 +108,7 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id" invisible="1" />
|
||||
<field name="refuse_reason_id" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<field name="email_from" widget="email"/>
|
||||
<field name="email_cc" groups="base.group_no_one"/>
|
||||
<field name="partner_phone" widget="phone"/>
|
||||
@@ -189,6 +190,7 @@
|
||||
<field name="user_id"/>
|
||||
<field name="stage_id" domain="[]"/>
|
||||
<field name="categ_ids"/>
|
||||
<field name="refuse_reason_id"/>
|
||||
<field name="attachment_ids" filter_domain="[('attachment_ids.index_content', 'ilike', self)]" string="Attachments"/>
|
||||
<filter string="My Applications" name="my_applications" domain="[('user_id', '=', uid)]"/>
|
||||
<filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
|
||||
@@ -213,6 +215,7 @@
|
||||
<filter string="Job" name="job" domain="[]" context="{'group_by': 'job_id'}"/>
|
||||
<filter string="Degree" name="degree" domain="[]" context="{'group_by': 'type_id'}"/>
|
||||
<filter string="Stage" name="stage" domain="[]" context="{'group_by': 'stage_id'}"/>
|
||||
<filter string="Refuse Reason" name="refuse_reason_id" domain="[]" context="{'group_by': 'refuse_reason_id'}"/>
|
||||
<filter string="Creation Date" name="creation_date" context="{'group_by': 'create_date'}"/>
|
||||
<filter string="Last Stage Update" name="last_stage_update" context="{'group_by': 'date_last_stage_update'}"/>
|
||||
</group>
|
||||
@@ -501,6 +504,46 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
hr.applicant.refuse.reason views
|
||||
-->
|
||||
<record id="hr_applicant_refuse_reason_view_form" model="ir.ui.view">
|
||||
<field name="name">Applicant refuse reason form</field>
|
||||
<field name="model">hr.applicant.refuse.reason</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Refuse Reason">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_title">
|
||||
<div class="oe_edit_only">
|
||||
<label for="name"/>
|
||||
</div>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
<field name="active" invisible="1"/>
|
||||
</div>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_applicant_refuse_reason_view_tree" model="ir.ui.view">
|
||||
<field name="name">Applicant refuse reason tree</field>
|
||||
<field name="model">hr.applicant.refuse.reason</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Refuse Reason" editable="bottom">
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_applicant_refuse_reason_action" model="ir.actions.act_window">
|
||||
<field name="name">Refuse Reasons</field>
|
||||
<field name="res_model">hr.applicant.refuse.reason</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
######################## JOB OPPORTUNITIES (menu) ###########################
|
||||
<record model="ir.actions.act_window" id="crm_case_categ0_act_job">
|
||||
<field name="name">Applications</field>
|
||||
@@ -570,6 +613,13 @@
|
||||
|
||||
<!-- ALL JOBS REQUESTS -->
|
||||
<menuitem parent="menu_hr_recruitment_configuration" id="menu_hr_job_position_config" action="action_hr_job_config" sequence="10"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_applicant_refuse_reason"
|
||||
action="hr_applicant_refuse_reason_action"
|
||||
parent="menu_hr_recruitment_configuration"
|
||||
sequence="10"/>
|
||||
|
||||
<menuitem
|
||||
name="Applications"
|
||||
parent="menu_hr_recruitment_root"
|
||||
@@ -933,6 +983,7 @@ action = act
|
||||
</record>
|
||||
<menuitem name="Reporting" id="report_hr_recruitment" parent="menu_hr_recruitment_root"
|
||||
sequence="99"/>
|
||||
|
||||
<menuitem name="Recruitment Analysis" id="hr_applicant_report_menu" parent="report_hr_recruitment"
|
||||
sequence="50" action="hr_applicant_action_analysis"/>
|
||||
<record id="action_hr_recruitment_report_filtered_department" model="ir.actions.act_window">
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import applicant_refuse_reason
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ApplicantGetRefuseReason(models.TransientModel):
|
||||
_name = 'applicant.get.refuse.reason'
|
||||
_description = 'Get Refuse Reason'
|
||||
|
||||
refuse_reason_id = fields.Many2one('hr.applicant.refuse.reason', 'Refuse Reason')
|
||||
applicant_ids = fields.Many2many('hr.applicant')
|
||||
|
||||
def action_refuse_reason_apply(self):
|
||||
return self.applicant_ids.write({'refuse_reason_id': self.refuse_reason_id.id})
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="applicant_get_refuse_reason_view_form" model="ir.ui.view">
|
||||
<field name="name">applicant.get.refuse.reason.form</field>
|
||||
<field name="model">applicant.get.refuse.reason</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Refuse Reason">
|
||||
<group class="oe_title">
|
||||
<field name="refuse_reason_id"/>
|
||||
<field name="applicant_ids" invisible="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_refuse_reason_apply" string="Submit" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="applicant_get_refuse_reason_action" model="ir.actions.act_window">
|
||||
<field name="name">Refuse Reason</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">applicant.get.refuse.reason</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="applicant_get_refuse_reason_view_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user