[IMP] hr_recruitment: stages are now job-related, not department-related

anymore. Also added default stages.
This commit is contained in:
Mitesh Savani
2015-04-27 10:20:38 +02:00
committed by Thibault Delavallée
parent 817fc0a8d7
commit 1701e7764f
6 changed files with 82 additions and 82 deletions
+48 -70
View File
@@ -36,6 +36,7 @@ AVAILABLE_PRIORITIES = [
('3', 'Excellent')
]
class hr_recruitment_source(osv.osv):
""" Sources of HR Recruitment """
_name = "hr.recruitment.source"
@@ -95,10 +96,14 @@ class hr_recruitment_stage(osv.osv):
_name = "hr.recruitment.stage"
_description = "Stage of Recruitment"
_order = 'sequence'
_columns = {
'name': fields.char('Name', required=True, translate=True),
'name': fields.char('Stage Name', required=True, translate=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
'department_id':fields.many2one('hr.department', 'Specific to a Department', help="Stages of the recruitment process may be different per department. If this stage is common to all departments, keep this field empty."),
'case_default': fields.boolean(
'Default for New Recruitment',
help="If you check this field, this stage will be proposed by default on each new Recruitment. It will not assign this stage to existing Recruitments."),
'job_ids': fields.many2many('hr.job', 'job_stage_rel', 'stage_id', 'job_id', 'Job Stages'),
'requirements': fields.text('Requirements'),
'template_id': fields.many2one('mail.template', 'Use template', help="If set, a message is posted on the applicant using the template when the applicant is set to the stage."),
'legend_priority': fields.text(
@@ -108,10 +113,13 @@ class hr_recruitment_stage(osv.osv):
help='This stage is folded in the kanban view when'
'there are no records in that stage to display.'),
}
_defaults = {
'sequence': 1,
'job_ids': lambda self, cr, uid, context=None: [context['default_job_id']] if context and context.get('default_job_id') else None,
}
class hr_recruitment_degree(osv.osv):
""" Degree of HR Recruitment """
_name = "hr.recruitment.degree"
@@ -127,6 +135,7 @@ class hr_recruitment_degree(osv.osv):
('name_uniq', 'unique (name)', 'The name of the Degree of Recruitment must be unique!')
]
class hr_applicant(osv.Model):
_name = "hr.applicant"
_description = "Applicant"
@@ -134,30 +143,9 @@ class hr_applicant(osv.Model):
_inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin']
_mail_mass_mailing = _('Applicants')
def _get_default_department_id(self, cr, uid, context=None):
""" Gives default department by checking if present in the context """
return (self._resolve_department_id_from_context(cr, uid, context=context) or False)
def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """
department_id = self._get_default_department_id(cr, uid, context=context)
return self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context)
def _resolve_department_id_from_context(self, cr, uid, context=None):
""" Returns ID of department based on the value of 'default_department_id'
context key, or None if it cannot be resolved to a single
department.
"""
if context is None:
context = {}
if type(context.get('default_department_id')) in (int, long):
return context.get('default_department_id')
if isinstance(context.get('default_department_id'), basestring):
department_name = context['default_department_id']
department_ids = self.pool.get('hr.department').name_search(cr, uid, name=department_name, context=context)
if len(department_ids) == 1:
return int(department_ids[0][0])
return None
job_id = context and context.get('default_job_id')
return self.stage_find(cr, uid, [], job_id, [('fold', '=', False)], context=context)
def _get_default_company_id(self, cr, uid, department_id=None, context=None):
company_id = False
@@ -175,15 +163,14 @@ class hr_applicant(osv.Model):
# lame hack to allow reverting search, should just work in the trivial case
if read_group_order == 'stage_id desc':
order = "%s desc" % order
# retrieve section_id from the context and write the domain
# - ('id', 'in', 'ids'): add columns that should be present
# - OR ('department_id', '=', False), ('fold', '=', False): add default columns that are not folded
# - OR ('department_id', 'in', department_id), ('fold', '=', False) if department_id: add department columns that are not folded
department_id = self._resolve_department_id_from_context(cr, uid, context=context)
# retrieve job_id from the context and write the domain: ids + contextual columns (job or default)
job_id = context and context.get('default_job_id')
search_domain = []
if department_id:
search_domain += ['|', ('department_id', '=', department_id)]
search_domain += ['|', ('id', 'in', ids), ('department_id', '=', False)]
if job_id:
search_domain += ['|', ('job_ids', '=', job_id)]
else:
search_domain += ['|', ('case_default', '=', True)]
search_domain += [('id', 'in', ids)]
stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
# restore order of the search
@@ -233,8 +220,9 @@ class hr_applicant(osv.Model):
'partner_id': fields.many2one('res.partner', 'Contact'),
'create_date': fields.datetime('Creation Date', readonly=True, select=True),
'write_date': fields.datetime('Update Date', readonly=True),
'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', track_visibility='onchange',
domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
'stage_id': fields.many2one(
'hr.recruitment.stage', 'Stage', track_visibility='onchange',
domain="['|', ('case_default', '=', True), ('job_ids', '=', job_id)]", copy=False, select=1),
'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'),
@@ -276,9 +264,8 @@ class hr_applicant(osv.Model):
_defaults = {
'active': lambda *a: 1,
'user_id': lambda s, cr, uid, c: uid,
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
'company_id': lambda s, cr, uid, c: s._get_default_company_id(cr, uid, s._get_default_department_id(cr, uid, c), c),
'stage_id': _get_default_stage_id,
'company_id': lambda s, cr, uid, c: s._get_default_company_id(cr, uid, c and c.get('default_department_id'), c),
'color': 0,
'priority': '0',
'date_last_stage_update': fields.datetime.now,
@@ -296,23 +283,16 @@ class hr_applicant(osv.Model):
action['res_id'] = applicant and applicant.emp_id.id or False
return action
def onchange_job(self, cr, uid, ids, job_id=False, context=None):
def onchange_job(self, cr, uid, ids, job_id=False, stage_id=False, context=None):
department_id = False
user_id = False
if job_id:
job_record = self.pool.get('hr.job').browse(cr, uid, job_id, context=context)
department_id = job_record and job_record.department_id and job_record.department_id.id or False
user_id = job_record and job_record.user_id and job_record.user_id.id or False
return {'value': {'department_id': department_id, 'user_id': user_id}}
def onchange_department_id(self, cr, uid, ids, department_id=False, stage_id=False, context=None):
values = {}
if not stage_id:
values['stage_id'] = self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context)
if department_id:
department = self.pool['hr.department'].browse(cr, uid, department_id, context=context)
values['company_id'] = department.company_id.id
return {'value': values}
if not stage_id and job_id:
stage_id = self.stage_find(cr, uid, [], job_id, [('fold', '=', False)], context=context)
return {'value': {'department_id': department_id, 'user_id': user_id, 'stage_id': stage_id}}
def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
data = {'partner_phone': False,
@@ -333,30 +313,26 @@ class hr_applicant(osv.Model):
return {'value': {'date_closed': fields.datetime.now()}}
return {'value': {'date_closed': False}}
def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
def stage_find(self, cr, uid, applicants, job_id, domain=[], order='sequence', context=None):
""" Override of the base.stage method
Parameter of the stage search taken from the lead:
- department_id: if set, stages must belong to this section or
- job_id: if set, stages must belong to this section or
be a default case
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
# collect all section_ids
department_ids = []
if section_id:
department_ids.append(section_id)
for case in cases:
if case.department_id:
department_ids.append(case.department_id.id)
# OR all section_ids and OR with case_default
search_domain = []
if department_ids:
search_domain += ['|', ('department_id', 'in', department_ids)]
search_domain.append(('department_id', '=', False))
if isinstance(applicants, (int, long)):
applicants = self.browse(cr, uid, applicants, context=context)
job_ids = set([applicant.job_id.id for applicant in applicants if applicant.job_id])
if job_id:
job_ids.add(job_id)
# OR all job_id and OR with applicant_default
if job_ids:
search_domain = ['|', ('case_default', '=', True), ('job_ids', 'in', list(job_ids))]
else:
search_domain = [('case_default', '=', True)]
# AND with the domain in parameter
search_domain += list(domain)
# perform search, return the first found
stage_ids = self.pool.get('hr.recruitment.stage').search(cr, uid, search_domain, order=order, context=context)
stage_ids = self.pool.get('hr.recruitment.stage').search(cr, uid, search_domain, order=order, limit=1, context=context)
if stage_ids:
return stage_ids[0]
return False
@@ -605,15 +581,12 @@ class hr_job(osv.osv):
_name = "hr.job"
_inherits = {'mail.alias': 'alias_id'}
def _track_subtype(self, cr, uid, ids, init_values, context=None):
record = self.browse(cr, uid, ids[0], context=context)
if 'state' in init_values and record.state == 'open':
return 'hr_recruitment.mt_job_new'
return super(hr_job, self)._track_subtype(cr, uid, ids, init_values, context=context)
def _get_attached_docs(self, cr, uid, ids, field_name, arg, context=None):
res = {}
attachment_obj = self.pool.get('ir.attachment')
@@ -646,6 +619,7 @@ class hr_job(osv.osv):
'application_ids': fields.one2many('hr.applicant', 'job_id', 'Applications'),
'application_count': fields.function(_count_all, type='integer', string='Applications', multi=True),
'manager_id': fields.related('department_id', 'manager_id', type='many2one', string='Department Manager', relation='hr.employee', readonly=True, store=True),
'stage_ids': fields.many2many('hr.recruitment.stage', 'job_stage_rel', 'job_id', 'stage_id', 'Job Stages'),
'document_ids': fields.function(_get_attached_docs, type='one2many', relation='ir.attachment', string='Applications'),
'documents_count': fields.function(_count_all, type='integer', string='Documents', multi=True),
'user_id': fields.many2one('res.users', 'Recruitment Responsible', track_visibility='onchange'),
@@ -656,8 +630,12 @@ class hr_job(osv.osv):
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.company_id.partner_id.id
def _get_type_common(self, cr, uid, context=None):
return self.pool['hr.recruitment.stage'].search(cr, uid, [('case_default', '=', True)], context=context)
_defaults = {
'address_id': _address_get
'address_id': _address_get,
'stage_ids': _get_type_common,
}
def _auto_init(self, cr, context=None):
@@ -93,30 +93,36 @@
<record model="hr.recruitment.stage" id="stage_job1">
<field name="name">Initial Qualification</field>
<field name="sequence">1</field>
<field name="case_default" eval="True"/>
</record>
<record model="hr.recruitment.stage" id="stage_job2">
<field name="name">First Interview</field>
<field name="template_id" ref="applicant_interest"/>
<field name="sequence">2</field>
<field name="case_default" eval="True"/>
</record>
<record model="hr.recruitment.stage" id="stage_job3">
<field name="name">Second Interview</field>
<field name="sequence">3</field>
<field name="case_default" eval="True"/>
</record>
<record model="hr.recruitment.stage" id="stage_job4">
<field name="name">Contract Proposed</field>
<field name="name">Contract Proposal</field>
<field name="sequence">4</field>
<field name="case_default" eval="True"/>
</record>
<record model="hr.recruitment.stage" id="stage_job5">
<field name="name">Contract Signed</field>
<field name="sequence">5</field>
<field name="fold" eval="True"/>
<field name="case_default" eval="True"/>
</record>
<record model="hr.recruitment.stage" id="stage_job6">
<field name="name">Refused</field>
<field name="sequence">6</field>
<field name="template_id" ref="applicant_refuse"/>
<field name="fold" eval="True"/>
<field name="case_default" eval="True"/>
</record>
<record id="mail_alias_jobs" model="mail.alias">
@@ -127,34 +127,41 @@
<field name="state">recruit</field>
<field name="no_of_recruitment">4</field>
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
<record id="hr.job_ceo" model="hr.job">
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
<record id="hr.job_cto" model="hr.job">
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
<record id="hr.job_consultant" model="hr.job">
<field name="state">recruit</field>
<field name="no_of_recruitment">1</field>
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
<record id="hr.job_hrm" model="hr.job">
<field name="no_of_recruitment">1</field>
<field name="state">recruit</field>
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
<record id="hr.job_marketing" model="hr.job">
<field name="state">recruit</field>
<field name="no_of_recruitment">3</field>
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
<record id="hr.job_trainee" model="hr.job">
<field name="state">recruit</field>
<field name="no_of_recruitment">6</field>
<field name="survey_id" ref="recruitment_form"/>
<field name="stage_ids" eval="[(6, 0, [ref('stage_job1'), ref('stage_job2'), ref('stage_job3'), ref('stage_job4'), ref('stage_job5'), ref('stage_job6')])]"/>
</record>
-->
<record id="message_application_demo" model="mail.message">
<field name="model">hr.applicant</field>
<field name="res_id" ref="hr_case_advertisement"/>
+17 -9
View File
@@ -60,7 +60,7 @@
<field name="arch" type="xml">
<form string="Jobs - Recruitment Form">
<header>
<field name="stage_id" widget="statusbar" clickable="True"/>
<field name="stage_id" widget="statusbar" clickable="True" options="{'fold_field': 'fold'}"/>
</header>
<sheet>
<div class="oe_right oe_button_box">
@@ -135,8 +135,8 @@
<group string="Job">
<field name="survey" invisible="1"/>
<field name="response_id" invisible="1"/>
<field name="job_id" on_change="onchange_job(job_id)"/>
<field name="department_id" on_change="onchange_department_id(department_id, stage_id)" groups="hr.group_multi_departments"/>
<field name="job_id" on_change="onchange_job(job_id,stage_id)"/>
<field name="department_id"/>
<field name="company_id" />
<label for="availability"/>
<div>
@@ -391,6 +391,13 @@
<field name="address_id" context="{'show_address': 1}" domain= "[('is_company', '=', True )]"/>
</div>
</group>
<group name="employee_data" position="after">
<notebook>
<page string="Recruitment Stage" name="recruitment_stage">
<field name="stage_ids" widget="many2many"></field>
</page>
</notebook>
</group>
<xpath expr="//field[@name='department_id']" position="after">
<label for="alias_name" string="Specific Email Address" 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)]}">
@@ -494,7 +501,9 @@
<div class="col-md-6 oe_applications">
<div class="oe_applications oe_job_font">
<a name="%(action_hr_job_applications)d" type="action">
<span><t t-esc="record.application_ids.raw_value.length"/> Applications</span>
<span t-if="record.application_ids.raw_value.length == 0"><t t-esc="record.application_ids.raw_value.length"/> Application</span>
<span t-if="record.application_ids.raw_value.length == 1"><t t-esc="record.application_ids.raw_value.length"/> Application</span>
<span t-if="record.application_ids.raw_value.length gt 1"><t t-esc="record.application_ids.raw_value.length"/> Applications</span>
</a>
<br/>
<a t-if="record.document_ids.raw_value.length gt 0" name="action_get_attachment_tree_view" type="object">
@@ -563,9 +572,8 @@
<field name="model">hr.recruitment.stage</field>
<field name="arch" type="xml">
<tree string="Stages">
<field name="sequence" invisible="1"/>
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="department_id" groups="hr.group_multi_departments"/>
<field name="fold"/>
</tree>
</field>
@@ -580,12 +588,12 @@
<group string="Stage Definition">
<group>
<field name="name"/>
<field name="department_id" groups="hr.group_multi_departments"/>
<field name="sequence"/>
<field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
<field name="case_default"/>
<field name="fold"/>
</group>
<group>
<field name="fold"/>
<field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
<field name="legend_priority"/>
</group>
</group>
@@ -111,7 +111,7 @@ class website_hr_recruitment(http.Controller):
}
for f in ['email_from', 'partner_name', 'description']:
value[f] = post.get(f)
for f in ['department_id', 'job_id']:
for f in ['department_id', 'job_id', 'stage_id']:
value[f] = int(post.get(f) or 0)
# Retro-compatibility for saas-3. "phone" field should be replace by "partner_phone" in the template in trunk.
value['partner_phone'] = post.pop('phone', False)
@@ -155,6 +155,7 @@
<form class="form-horizontal mt32" action="/jobs/thankyou" method="post" enctype="multipart/form-data">
<input type="hidden" t-att-value="job and job.department_id.id or False" name="department_id"/>
<input type="hidden" t-att-value="job and job.id or False" name="job_id"/>
<input type="hidden" t-att-value="job and job.stage_ids and job.stage_ids[0].id or False" name="stage_id"/>
<div t-attf-class="form-group #{error.get('partner_name') and 'has-error' or ''}">
<label class="col-md-3 col-sm-4 control-label" for="partner_name">Your Name</label>
<div class="col-md-7 col-sm-8">