[MIGR] hr_recruitment: migrate to the new API.

This commit should not contain heavy code cleaning or refactoring.
The code has been translated and maybe slightly improved or
simplified.
This commit is contained in:
Joren Van Onder
2015-07-24 15:50:51 +02:00
committed by Thibault Delavallée
parent 2ed1cf80e1
commit a805e091c6
9 changed files with 523 additions and 602 deletions
+1 -1
View File
@@ -357,7 +357,7 @@
</div>
<group name="employee_data">
<group>
<field name="department_id" groups="hr.group_multi_departments" class="oe_inline"/>
<field name="department_id" class="oe_inline"/>
<field name="no_of_recruitment" class="oe_inline"/>
</group>
</group>
@@ -2,9 +2,16 @@
from openerp import api, fields, models
class hr_department(models.Model):
class HrDepartment(models.Model):
_inherit = 'hr.department'
new_applicant_count = fields.Integer(
compute='_compute_new_applicant_count', string='New Applicant')
new_hired_employee = fields.Integer(
compute='_compute_recruitment_stats', string='New Hired Employee')
expected_employee = fields.Integer(
compute='_compute_recruitment_stats', string='Expected Employee')
@api.multi
def _compute_new_applicant_count(self):
applicant_data = self.env['hr.applicant'].read_group(
@@ -24,10 +31,3 @@ class hr_department(models.Model):
for department in self:
department.new_hired_employee = new_emp.get(department.id, 0)
department.expected_employee = expected_emp.get(department.id, 0)
new_applicant_count = fields.Integer(
compute='_compute_new_applicant_count', string='New Applicant')
new_hired_employee = fields.Integer(
compute='_compute_recruitment_stats', string='New Hired Employee')
expected_employee = fields.Integer(
compute='_compute_recruitment_stats', string='Expected Employee')
+15 -18
View File
@@ -1,26 +1,23 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import fields, osv
from openerp import api, fields, models
class hr_employee(osv.Model):
class hr_employee(models.Model):
_inherit = "hr.employee"
def _newly_hired_employee(self, cr, uid, ids, field_name, arg, context=None):
data = self.pool['hr.applicant'].read_group(cr, uid,
[('emp_id', 'in', ids), ('job_id.state', '=', 'recruit')],
['emp_id'], ['emp_id'], context=context)
result = dict.fromkeys(ids, False)
for d in data:
if d['emp_id_count'] >= 1:
result[d['emp_id'][0]] = True
return result
newly_hired_employee = fields.Boolean('Newly hired employee', compute='_compute_newly_hired_employee',
search='_search_newly_hired_employee')
def _search_newly_hired_employee(self, cr, uid, obj, name, args, context=None):
applicant_ids = self.pool['hr.applicant'].search_read(cr, uid, [('job_id.state', '=', 'recruit')], ['emp_id'], context=context)
hired_emp_ids = [applicant['emp_id'][0] for applicant in applicant_ids if applicant['emp_id']]
return [('id', 'in', hired_emp_ids)]
@api.multi
def _compute_newly_hired_employee(self):
read_group_result = self.env['hr.applicant'].read_group(
[('emp_id', 'in', self.ids), ('job_id.state', '=', 'recruit')],
['emp_id'], ['emp_id'])
result = dict((data['emp_id'], data['emp_id_count'] > 0) for data in read_group_result)
for record in self:
record.newly_hired_employee = result.get(record.id, False)
_columns = {
'newly_hired_employee': fields.function(_newly_hired_employee, fnct_search=_search_newly_hired_employee, type='boolean', string='Newly hired employees')
}
def _search_newly_hired_employee(self, operator, value):
applicants = self.env['hr.applicant'].search([('job_id.state', '=', 'recruit')])
return [('id', 'in', applicants.ids)]
+89 -84
View File
@@ -1,102 +1,107 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import fields, osv
from openerp import api, fields, models
class hr_job(osv.osv):
class Job(models.Model):
_inherit = "hr.job"
_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)
@api.model
def _default_address_id(self):
return self.env.user.company_id.partner_id
def _get_attached_docs(self, cr, uid, ids, field_name, arg, context=None):
res = {}
attachment_obj = self.pool.get('ir.attachment')
for job_id in ids:
applicant_ids = self.pool.get('hr.applicant').search(cr, uid, [('job_id', '=', job_id)], context=context)
res[job_id] = attachment_obj.search(
cr, uid, [
'|',
'&', ('res_model', '=', 'hr.job'), ('res_id', '=', job_id),
'&', ('res_model', '=', 'hr.applicant'), ('res_id', 'in', applicant_ids)
], context=context)
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="Applications")
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", track_visibility='onchange')
stage_ids = fields.Many2many(
'hr.recruitment.stage', 'job_stage_rel', 'job_id', 'stage_id',
'Job Stages')
document_ids = fields.One2many('ir.attachment', compute='_compute_document_ids', string="Applications")
documents_count = fields.Integer(compute='_compute_document_ids', string="Documents")
survey_id = fields.Many2one(
'survey.survey', "Interview Form",
help="Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job")
alias_id = fields.Many2one(
'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")
def _compute_document_ids(self):
applicants = self.mapped('application_ids')
app_to_job = dict((applicant.id, applicant.job_id.id) for applicant in applicants)
attachments = self.env['ir.attachment'].search([
'|',
'&', ('res_model', '=', 'hr.job'), ('res_id', 'in', self.ids),
'&', ('res_model', '=', 'hr.applicant'), ('res_id', 'in', self.mapped('application_ids').ids)])
result = dict.fromkeys(self.ids, self.env['ir.attachment'])
for attachment in attachments:
if attachment.res_model == 'hr.applicant':
result[app_to_job[attachment.res_id]] |= attachment
else:
result[attachment.res_id] |= attachment
for job in self:
job.document_ids = result[job.id]
job.documents_count = len(job.document_ids)
@api.multi
def _compute_application_count(self):
read_group_result = self.env['hr.applicant'].read_group([('job_id', '=', self.id)], ['job_id'], ['job_id'])
result = dict((data['job_id'], data['job_id_count']) for data in read_group_result)
for job in self:
job.application_count = result.get(job.id, 0)
@api.model
def create(self, vals):
job = super(Job, self.with_context(alias_model_name='hr.applicant',
mail_create_nolog=True,
alias_parent_model_name=self._name)).create(vals)
job.alias_id.write({'alias_parent_thread_id': job.id, "alias_defaults": {'job_id': job.id}})
return job
@api.multi
def unlink(self):
# Cascade-delete mail aliases as well, as they should not exist without the job position.
aliases = self.mapped('alias_id')
res = super(Job, self).unlink()
aliases.unlink()
return res
def _count_all(self, cr, uid, ids, field_name, arg, context=None):
Applicant = self.pool['hr.applicant']
return {
job_id: {
'application_count': Applicant.search_count(cr,uid, [('job_id', '=', job_id)], context=context),
'documents_count': len(self._get_attached_docs(cr, uid, [job_id], field_name, arg, context=context)[job_id])
}
for job_id in ids
}
_columns = {
'survey_id': fields.many2one('survey.survey', 'Interview Form', help="Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job"),
'alias_id': fields.many2one('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."),
'address_id': fields.many2one('res.partner', 'Job Location', help="Address where employees are working"),
'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'),
'color': fields.integer('Color Index'),
}
def _address_get(self, cr, uid, context=None):
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.company_id.partner_id.id
_defaults = {
'address_id': _address_get,
}
def _auto_init(self, cr, context=None):
"""Installation hook to create aliases for all jobs and avoid constraint errors."""
return self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(hr_job, self)._auto_init,
'hr.applicant', self._columns['alias_id'], 'name', alias_prefix='job+', alias_defaults={'job_id': 'id'}, context=context)
return self.pool.get('mail.alias').migrate_to_alias(
cr, self._name, self._table, super(Job, self)._auto_init,
'hr.applicant', self._columns['alias_id'], 'name',
alias_prefix='job+', alias_defaults={'job_id': 'id'}, context=context)
def create(self, cr, uid, vals, context=None):
create_context = dict(context,
alias_model_name='hr.applicant',
mail_create_nolog=True,
alias_parent_model_name=self._name)
job_id = super(hr_job, self).create(cr, uid, vals, context=create_context)
job = self.browse(cr, uid, job_id, context=context)
self.pool.get('mail.alias').write(cr, uid, [job.alias_id.id], {'alias_parent_thread_id': job_id, "alias_defaults": {'job_id': job_id}}, context)
return job_id
@api.multi
def _track_subtype(self, init_values):
if 'state' in init_values and self.state == 'open':
return 'hr_recruitment.mt_job_new'
return super(Job, self)._track_subtype(init_values)
def unlink(self, cr, uid, ids, context=None):
# Cascade-delete mail aliases as well, as they should not exist without the job position.
mail_alias = self.pool.get('mail.alias')
alias_ids = [job.alias_id.id for job in self.browse(cr, uid, ids, context=context) if job.alias_id]
res = super(hr_job, self).unlink(cr, uid, ids, context=context)
mail_alias.unlink(cr, uid, alias_ids, context=context)
return res
@api.multi
def action_print_survey(self):
return self.survey_id.action_print_survey()
def action_print_survey(self, cr, uid, ids, context=None):
job = self.browse(cr, uid, ids, context=context)[0]
survey_id = job.survey_id.id
return self.pool.get('survey.survey').action_print_survey(cr, uid, [survey_id], context=context)
def action_get_attachment_tree_view(self, cr, uid, ids, context=None):
#open attachments of job and related applicantions.
model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'action_attachment')
action = self.pool.get(model).read(cr, uid, action_id, context=context)
applicant_ids = self.pool.get('hr.applicant').search(cr, uid, [('job_id', 'in', ids)], context=context)
action['context'] = {'default_res_model': self._name, 'default_res_id': ids[0]}
action['domain'] = str(['|', '&', ('res_model', '=', 'hr.job'), ('res_id', 'in', ids), '&', ('res_model', '=', 'hr.applicant'), ('res_id', 'in', applicant_ids)])
@api.multi
def action_get_attachment_tree_view(self):
action = self.env.ref('base.action_attachment').read()[0]
action['context'] = {
'default_res_model': self._name,
'default_res_id': self.ids[0]
}
action['domain'] = '%s' % ['|', '&', ('res_model', '=', 'hr.job'), ('res_id', 'in', self.ids), '&', ('res_model', '=', 'hr.applicant'), ('res_id', 'in', self.mapped('application_ids'))]
return action
def action_set_no_of_recruitment(self, cr, uid, id, value, context=None):
return self.write(cr, uid, [id], {'no_of_recruitment': value}, context=context)
@api.multi
def action_set_no_of_recruitment(self, value):
return self.write({'no_of_recruitment': value})
+353 -411
View File
@@ -4,14 +4,10 @@
import werkzeug
from datetime import datetime
from openerp import api
from openerp import tools
from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp import api, fields, models, tools
from openerp.tools.translate import _
from openerp.exceptions import UserError
AVAILABLE_PRIORITIES = [
('0', 'Normal'),
('1', 'Good'),
@@ -20,390 +16,438 @@ AVAILABLE_PRIORITIES = [
]
class hr_recruitment_source(osv.osv):
""" Sources of HR Recruitment """
class RecruitmentSource(models.Model):
_name = "hr.recruitment.source"
_description = "Source of Applicants"
_inherits = {"utm.source": "source_id"}
def _get_url(self, cr, uid, ids, field_name, arg, context=None):
result = {}
imd = self.pool['ir.model.data']
for source in self.browse(cr, uid, ids, context=context):
if not source.alias_id.id:
result[source.id] = ''
else:
result[source.id] = werkzeug.url_encode({
'utm_campaign': imd.xmlid_to_object(cr, SUPERUSER_ID, 'hr_recruitment.utm_campaign_job').name,
'utm_medium': imd.xmlid_to_object(cr, SUPERUSER_ID, 'utm.utm_medium_website').name,
'utm_source': source.source_id.name
})
return result
source_id = fields.Many2one('utm.source', "Source", ondelete='cascade', required=True)
url = fields.Char(string="Url parameters", compute='_compute_url')
email = fields.Char(related='alias_id.alias_name', string="Email", readonly=True)
job_id = fields.Many2one('hr.job', "Job ID")
alias_id = fields.Many2one('mail.alias', "Alias ID")
def create_alias(self, cr, uid, ids, context=None):
imd = self.pool['ir.model.data']
for source in self.browse(cr, uid, ids, context=context):
alias_context = dict(context or {}, alias_model_name='hr.applicant', alias_parent_model_name='hr.job')
@api.depends('source_id', 'source_id.name')
@api.one
def _compute_url(self):
if not self.alias_id.id:
self.url = ""
else:
self.url = werkzeug.url_encode({
'utm_campaign': self.env.ref('hr_recruitment.utm_campaign_job').name,
'utm_medium': self.env.ref('utm.utm_medium_website').name,
'utm_source': self.source_id.name
})
@api.multi
def create_alias(self):
campaign = self.env.ref('hr_recruitment.utm_campaign_job')
medium = self.env.ref('utm.utm_medium_email')
for source in self:
vals = {
'alias_parent_thread_id': source.job_id.id,
'alias_name': "%s+%s" % (source.job_id.alias_name or source.job_id.name, source.name),
'alias_defaults': {
'job_id': source.job_id.id,
'campaign_id': imd.xmlid_to_res_id(cr, SUPERUSER_ID, 'hr_recruitment.utm_campaign_job'),
'medium_id': imd.xmlid_to_res_id(cr, SUPERUSER_ID, 'utm.utm_medium_email'),
'campaign_id': campaign.id,
'medium_id': medium.id,
'source_id': source.source_id.id,
},
}
new_alias_id = self.pool['mail.alias'].create(cr, uid, vals, alias_context)
source.write({'alias_id': new_alias_id, 'name': source.source_id.name})
return
source.alias_id = self.with_context(alias_model_name='hr.applicant', alias_parent_model_name='hr.job').env['mail.alias'].create(vals)
source.name = source.source_id.name
def _get_source(self, cr, uid, ids, context=None):
if ids:
return self.pool['hr.recruitment.source'].search(cr, uid, [('source_id', 'in', ids)], context=context)
return []
_columns = {
'source_id': fields.many2one('utm.source', 'Source', ondelete='cascade', required=True),
'url': fields.function(_get_url, string='Url Parameters', type='char', store={
'hr.recruitment.source': (lambda self, cr, uid, ids, ctx: ids, ['source_id'], 20),
'utm.source': (_get_source, ['name'], 20),
}),
'email': fields.related('alias_id', 'alias_name', string='Email', readonly=True, type='char'),
'job_id': fields.many2one('hr.job', 'Job ID'),
'alias_id': fields.many2one('mail.alias', 'Alias ID'),
}
class hr_recruitment_stage(osv.osv):
""" Stage of HR Recruitment """
class RecruitmentStage(models.Model):
_name = "hr.recruitment.stage"
_description = "Stage of Recruitment"
_order = 'sequence'
_columns = {
'name': fields.char('Stage Name', required=True, translate=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
'job_ids': fields.many2many('hr.job', 'job_stage_rel', 'stage_id', 'job_id', 'Job Stages'),
'requirements': fields.text('Requirements', help="Enter here the internal requirements for this stage (ex: Offer sent to applicant). It will appear as a tooltip over the stage's name."),
'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."),
'fold': fields.boolean('Folded in Kanban View',
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,
}
name = fields.Char("Stage name", required=True, translate=True)
sequence = fields.Integer(
"Sequence", default=1,
help="Gives the sequence order when displaying a list of stages.")
job_ids = fields.Many2many(
'hr.job', 'job_stage_rel', 'stage_id', 'job_id',
string='Job Stages',
default=lambda self: [(4, self._context['default_job_id'])] if self._context.get('default_job_id') else None)
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.")
fold = fields.Boolean(
"Folded in Kanban View",
help="This stage is folded in the kanban view when there are no records in that stage to display.")
class hr_recruitment_degree(osv.osv):
""" Degree of HR Recruitment """
class RecruitmentDegree(models.Model):
_name = "hr.recruitment.degree"
_description = "Degree of Recruitment"
_columns = {
'name': fields.char('Degree', required=True, translate=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of degrees."),
}
_defaults = {
'sequence': 1,
}
_sql_constraints = [
('name_uniq', 'unique (name)', 'The name of the Degree of Recruitment must be unique!')
]
name = fields.Char("Degree", required=True, translate=True)
sequence = fields.Integer("Sequence", default=1, help="Gives the sequence order when displaying a list of degrees.")
class hr_applicant(osv.Model):
class Applicant(models.Model):
_name = "hr.applicant"
_description = "Applicant"
_order = "priority desc, id desc"
_inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin']
_mail_mass_mailing = _('Applicants')
def _get_default_stage_id(self, cr, uid, context=None):
job_id = context and context.get('default_job_id')
return self.stage_find(cr, uid, [], job_id, [('fold', '=', False)], context=context)
def _default_stage_id(self):
if self._context.get('default_job_id'):
return self.env['hr.recruitment.stage'].search([
('job_ids', '=', self._context['default_job_id']),
('fold', '=', False)
], order='sequence asc', limit=1).ids[0]
return False
def _get_default_company_id(self, cr, uid, department_id=None, context=None):
def _default_company_id(self):
company_id = False
if department_id:
department = self.pool['hr.department'].browse(cr, uid, department_id, context=context)
company_id = department.company_id.id if department and department.company_id else False
if self._context.get('default_department_id'):
department = self.env['hr.department'].browse(self._context['default_department_id'])
company_id = department.company_id.id
if not company_id:
company_id = self.pool['res.company']._company_default_get(cr, uid, 'hr.applicant', context=context)
company_id = self.env['res.company']._company_default_get('hr.applicant')
return company_id
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('hr.recruitment.stage')
order = stage_obj._order
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="These people will receive email.")
email_cc = fields.Text("Watchers Emails", size=252,
help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma")
probability = fields.Float("Probability")
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="[('job_ids', '=', job_id)]", copy=False, select=1,
default=_default_stage_id)
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")
company_id = fields.Many2one('res.company', "Company", default=_default_company_id)
user_id = fields.Many2one('res.users', "Responsible", track_visibility="onchange", default=lambda self: self.env.uid)
date_closed = fields.Datetime("Closed", readonly=True, select=True)
date_open = fields.Datetime("Assigned", readonly=True, select=True)
date_last_stage_update = fields.Datetime("Last Stage Update", select=True, default=fields.Datetime.now)
date_action = fields.Date("Next Action Date")
title_action = fields.Char("Next Action", size=64)
priority = fields.Selection(AVAILABLE_PRIORITIES, "Appreciation", default='0')
job_id = fields.Many2one('hr.job', "Applied Job")
salary_proposed_extra = fields.Char("Proposed Salary Extra", help="Salary Proposed by the Organisation, extra advantages")
salary_expected_extra = fields.Char("Expected Salary Extra", help="Salary Expected by Applicant, extra advantages")
salary_proposed = fields.Float("Proposed Salary", help="Salary Proposed by the Organisation")
salary_expected = fields.Float("Expected Salary", help="Salary Expected by Applicant")
availability = fields.Date("Availability", help="The date at which the applicant will be available to start working")
partner_name = fields.Char("Applicant's Name")
partner_phone = fields.Char("Phone", size=32)
partner_mobile = fields.Char("Mobile", size=32)
type_id = fields.Many2one('hr.recruitment.degree', "Degree")
department_id = fields.Many2one('hr.department', "Department")
survey = fields.Many2one('survey.survey', related='job_id.survey_id', string="Survey") # TDE FIXME: rename to survey_id
response_id = fields.Many2one('survey.user_input', "Response", ondelete="set null", oldname="response")
reference = fields.Char("Referred By")
day_open = fields.Float(compute='_compute_day', string="Days to Open")
day_close = fields.Float(compute='_compute_day', string="Days to Close")
color = fields.Integer("Color Index", default=0)
emp_id = fields.Many2one('hr.employee', string="Employee", track_visibility="onchange", help="Employee linked to the applicant.")
user_email = fields.Char(related='user_id.email', type="char", string="User Email", readonly=True)
attachment_number = fields.Integer(compute='_get_attachment_number', string="Number of Attachments")
employee_name = fields.Char(related='emp_id.name', string="Employee Name")
@api.depends('date_open', 'date_closed')
@api.one
def _compute_day(self):
if self.date_open:
date_create = datetime.strptime(self.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
date_open = datetime.strptime(self.date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT)
self.day_open = (date_open - date_create).total_seconds() / (24.0 * 3600)
if self.date_closed:
date_create = datetime.strptime(self.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
date_closed = datetime.strptime(self.date_closed, tools.DEFAULT_SERVER_DATETIME_FORMAT)
self.day_close = (date_closed - date_create).total_seconds() / (24.0 * 3600)
@api.multi
def _get_attachment_number(self):
read_group_res = self.env['ir.attachment'].read_group(
[('res_model', '=', 'hr.applicant'), ('res_id', 'in', self.ids)],
['res_id'], ['res_id'])
attach_data = dict((res['res_id'], res['res_id_count']) for res in read_group_res)
for record in self:
record.attachment_number = attach_data.get(record.id, 0)
@api.model
def _read_group_stage_ids(self, ids, domain, read_group_order=None, access_rights_uid=None):
access_rights_uid = access_rights_uid or self.env.uid
Stage = self.env['hr.recruitment.stage']
order = Stage._order
# 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 job_id from the context and write the domain: ids + contextual columns (job or default)
job_id = context and context.get('default_job_id')
job_id = self._context.get('default_job_id')
department_id = self._context.get('default_department_id')
search_domain = []
if job_id:
search_domain += ['|', ('job_ids', '=', job_id)]
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)
search_domain = [('job_ids', '=', job_id)]
if department_id:
if search_domain:
search_domain = ['|', ('job_ids.department_id', '=', department_id)] + search_domain
else:
search_domain = [('job_ids.department_id', '=', department_id)]
if self.ids:
if search_domain:
search_domain = ['|', ('id', 'in', self.ids)] + search_domain
else:
search_domain = [('id', 'in', self.ids)]
stage_ids = Stage._search(search_domain, order=order, access_rights_uid=access_rights_uid)
stages = Stage.sudo(access_rights_uid).browse(stage_ids)
result = stages.name_get()
# restore order of the search
result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
fold = {}
for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
for stage in stages:
fold[stage.id] = stage.fold or False
return result, fold
def _compute_day(self, cr, uid, ids, fields, args, context=None):
res = dict((res_id, {}) for res_id in ids)
for issue in self.browse(cr, uid, ids, context=context):
values = {
'day_open': 0.0,
'day_close': 0.0,
}
if issue.date_open:
date_create = datetime.strptime(issue.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
date_open = datetime.strptime(issue.date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT)
values['day_open'] = (date_open - date_create).total_seconds() / (24.0 * 3600)
if issue.date_closed:
date_create = datetime.strptime(issue.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
date_closed = datetime.strptime(issue.date_closed, tools.DEFAULT_SERVER_DATETIME_FORMAT)
values['day_close'] = (date_closed - date_create).total_seconds() / (24.0 * 3600)
# filter only required values
for field in fields:
res[issue.id][field] = values[field]
return res
def _get_attachment_number(self, cr, uid, ids, fields, args, context=None):
res = dict.fromkeys(ids, 0)
for app_id in ids:
res[app_id] = self.pool['ir.attachment'].search_count(cr, uid, [('res_model', '=', 'hr.applicant'), ('res_id', '=', app_id)], context=context)
return res
_columns = {
'name': fields.char('Subject / Application Name', required=True),
'active': fields.boolean('Active', 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="These people will receive email."),
'email_cc': fields.text('Watchers Emails', size=252, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
'probability': fields.float('Probability'),
'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="[('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'),
'company_id': fields.many2one('res.company', 'Company'),
'user_id': fields.many2one('res.users', 'Responsible', track_visibility='onchange'),
'date_closed': fields.datetime('Closed', readonly=True, select=True),
'date_open': fields.datetime('Assigned', readonly=True, select=True),
'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
'date_action': fields.date('Next Action Date'),
'title_action': fields.char('Next Action', size=64),
'priority': fields.selection(AVAILABLE_PRIORITIES, 'Appreciation'),
'job_id': fields.many2one('hr.job', 'Applied Job'),
'salary_proposed_extra': fields.char('Proposed Salary Extra', help="Salary Proposed by the Organisation, extra advantages"),
'salary_expected_extra': fields.char('Expected Salary Extra', help="Salary Expected by Applicant, extra advantages"),
'salary_proposed': fields.float('Proposed Salary', help="Salary Proposed by the Organisation"),
'salary_expected': fields.float('Expected Salary', help="Salary Expected by Applicant"),
'availability': fields.date('Availability', help="The date in which the applicant will be available to start working. \n\nAdditional information like 'Not before the first 15 days of March' or 'Depends on the notice negociation' can be written in the application summary field."),
'partner_name': fields.char("Applicant's Name"),
'partner_phone': fields.char('Phone', size=32),
'partner_mobile': fields.char('Mobile', size=32),
'type_id': fields.many2one('hr.recruitment.degree', 'Degree'),
'department_id': fields.many2one('hr.department', 'Department'),
'survey': fields.related('job_id', 'survey_id', type='many2one', relation='survey.survey', string='Survey'),
'response_id': fields.many2one('survey.user_input', "Response", ondelete='set null', oldname="response"),
'reference': fields.char('Referred By'),
'day_open': fields.function(_compute_day, string='Days to Open',
multi='day_open', type="float",
store={'hr.applicant': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
'day_close': fields.function(_compute_day, string='Days to Close',
multi='day_close', type="float",
store={'hr.applicant': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
'color': fields.integer('Color Index'),
'emp_id': fields.many2one('hr.employee', string='Employee', track_visibility='onchange', help='Employee linked to the applicant.'),
'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
'attachment_number': fields.function(_get_attachment_number, string='Number of Attachments', type="integer"),
'employee_name': fields.related('emp_id', 'name', type='char', string='Employee Name'),
}
_defaults = {
'active': lambda *a: 1,
'user_id': lambda s, cr, uid, c: uid,
'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,
}
_group_by_full = {
'stage_id': _read_group_stage_ids
}
def action_get_created_employee(self, cr, uid, ids, context=None):
if isinstance(ids, (list, tuple)):
ids = ids and ids[0] or False
applicant = self.browse(cr, uid, ids, context=context)
action = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'hr', 'open_view_employee_list', context=context)
action['res_id'] = applicant and applicant.emp_id.id or False
return action
@api.onchange('job_id')
def onchange_job_id(self):
vals = self._onchange_job_id_internal(self.job_id.id)
self.department_id = vals['value']['department_id']
self.user_id = vals['value']['user_id']
self.stage_id = vals['value']['stage_id']
def onchange_job(self, cr, uid, ids, job_id=False, stage_id=False, context=None):
def _onchange_job_id_internal(self, job_id):
department_id = False
user_id = False
stage_id = self.stage_id.id
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
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}}
job = self.env['hr.job'].browse(job_id)
department_id = job.department_id.id
user_id = job.user_id.id
if not self.stage_id:
stage_id = self.env['hr.recruitment.stage'].search([
('job_ids', '=', job.id),
('fold', '=', False)
], order='sequence asc', limit=1).ids[0]
def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
data = {'partner_phone': False,
'partner_mobile': False,
'email_from': False}
if partner_id:
addr = self.pool.get('res.partner').browse(cr, uid, partner_id, context)
data.update({'partner_phone': addr.phone,
'partner_mobile': addr.mobile,
'email_from': addr.email})
return {'value': data}
return {'value': {
'department_id': department_id,
'user_id': user_id,
'stage_id': stage_id
}}
def onchange_stage_id(self, cr, uid, ids, stage_id, context=None):
@api.onchange('partner_id')
def onchange_partner_id(self):
self.partner_phone = self.partner_id.phone
self.partner_mobile = self.partner_id.mobile
self.email_from = self.partner_id.email
@api.onchange('stage_id')
def onchange_stage_id(self):
vals = self._onchange_stage_id_internal(self.stage_id.id)
if vals['value'].get('date_closed'):
self.date_closed = vals['value']['date_closed']
def _onchange_stage_id_internal(self, stage_id):
if not stage_id:
return {'value': {}}
stage = self.pool['hr.recruitment.stage'].browse(cr, uid, stage_id, context=context)
stage = self.env['hr.recruitment.stage'].browse(stage_id)
if stage.fold:
return {'value': {'date_closed': fields.datetime.now()}}
return {'value': {'date_closed': False}}
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:
- job_id: if set, stages must belong to this section or
be a default case
"""
search_domain = []
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 += [('job_ids', 'in', list(job_ids))]
# 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, limit=1, context=context)
if stage_ids:
return stage_ids[0]
return False
@api.model
def create(self, vals):
if vals.get('department_id') and not self._context.get('default_department_id'):
self = self.with_context(default_department_id=vals.get('department_id'))
if vals.get('job_id') or self._context.get('default_job_id'):
job_id = vals.get('job_id') or self._context.get('default_job_id')
for key, value in self._onchange_job_id_internal(job_id)['value'].iteritems():
if key not in vals:
vals[key] = value
if vals.get('user_id'):
vals['date_open'] = fields.Datetime.now()
if 'stage_id' in vals:
vals.update(self._onchange_stage_id_internal(vals.get('stage_id'))['value'])
applicant = super(Applicant, self.with_context(mail_create_nolog=True)).create(vals)
if applicant.job_id:
name = applicant.partner_name if applicant.partner_name else applicant.name
applicant.job_id.message_post(
body=_('New application from %s') % name,
subtype="hr_recruitment.mt_job_applicant_new")
return applicant
def action_makeMeeting(self, cr, uid, ids, context=None):
@api.multi
def write(self, vals):
# user_id change: update date_open
if vals.get('user_id'):
vals['date_open'] = fields.Datetime.now()
# stage_id: track last stage before update
if 'stage_id' in vals:
vals['date_last_stage_update'] = fields.Datetime.now()
vals.update(self._onchange_stage_id_internal(vals.get('stage_id'))['value'])
for applicant in self:
vals['last_stage_id'] = applicant.stage_id.id
res = super(Applicant, self).write(vals)
else:
res = super(Applicant, self).write(vals)
# post processing: if job changed, post a message on the job
if vals.get('job_id'):
for applicant in self:
name = applicant.partner_name if applicant.partner_name else applicant.name
self.env['hr.job'].browse([vals['job_id']]).message_post(
body=_('New application from %s') % name,
subtype="hr_recruitment.mt_job_applicant_new")
# post processing: if stage changed, post a message in the chatter
if vals.get('stage_id'):
if self.stage_id.template_id:
# TDENOTE: probably factorize me in a message_post_with_template generic method FIXME
composer = self.env['mail.compose.message'].with_context(active_ids=self.ids).create(
{
'model': self._name,
'composition_mode': 'mass_mail',
'template_id': self.stage_id.template_id.id,
'notify': True,
})
values = composer.onchange_template_id(
self.stage_id.template_id.id, 'mass_mail', self._name, False)['value']
if values.get('attachment_ids'):
values['attachment_ids'] = [(6, 0, values['attachment_ids'])]
composer.write(values)
composer.send_mail()
return res
@api.model
def _broadcast_welcome(self):
""" Broadcast the welcome message to all users in the employee company. """
IrModelData = self.env['ir.model.data']
channel_all_employees = IrModelData.xmlid_to_object('mail.channel_all_employees')
template_new_employee = IrModelData.xmlid_to_object('hr_recruitment.hr_welcome_new_employee')
if template_new_employee:
MailTemplate = self.env['mail.template']
body_html = MailTemplate.render_template(template_new_employee.body_html, 'hr.employee', self.id)
subject = MailTemplate.render_template(template_new_employee.subject, 'hr.employee', self.id)
channel_all_employees.message_post(
body=body_html, subject=subject,
subtype='mail.mt_comment')
return True
@api.model
def get_empty_list_help(self, help):
return super(Applicant, self.with_context(empty_list_help_model='hr.job',
empty_list_help_id=self.env.context.get('default_job_id'),
empty_list_help_document_name=_("job applicants"))).get_empty_list_help(help)
@api.multi
def action_get_created_employee(self):
self.ensure_one()
action = self.env['ir.actions.act_window'].for_xml_id('hr', 'open_view_employee_list')
action['res_id'] = self.mapped('emp_id').ids[0]
return action
@api.multi
def action_makeMeeting(self):
""" This opens Meeting's calendar view to schedule meeting on current applicant
@return: Dictionary value for created Meeting view
"""
applicant = self.browse(cr, uid, ids[0], context)
applicant_ids = []
if applicant.partner_id:
applicant_ids.append(applicant.partner_id.id)
if applicant.department_id and applicant.department_id.manager_id and applicant.department_id.manager_id.user_id and applicant.department_id.manager_id.user_id.partner_id:
applicant_ids.append(applicant.department_id.manager_id.user_id.partner_id.id)
category = self.pool.get('ir.model.data').get_object(cr, uid, 'hr_recruitment', 'categ_meet_interview', context)
res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'calendar', 'action_calendar_event', context)
self.ensure_one()
partners = self.partner_id
if self.department_id and self.department_id.manager_id and self.department_id.manager_id.user_id:
partners |= self.department_id.manager_id.user_id.partner_id
category = self.env.ref('hr_recruitment.categ_meet_interview')
res = self.env['ir.actions.act_window'].for_xml_id('calendar', 'action_calendar_event')
res['context'] = {
'default_partner_ids': applicant_ids,
'default_user_id': uid,
'default_name': applicant.name,
'default_partner_ids': partners.ids,
'default_user_id': self.env.uid,
'default_name': self.name,
'default_categ_ids': category and [category.id] or False,
}
return res
def action_start_survey(self, cr, uid, ids, context=None):
context = dict(context or {})
applicant = self.browse(cr, uid, ids, context=context)[0]
survey_obj = self.pool.get('survey.survey')
response_obj = self.pool.get('survey.user_input')
@api.multi
def action_start_survey(self):
self.ensure_one()
# create a response and link it to this applicant
if not applicant.response_id:
response_id = response_obj.create(cr, uid, {'survey_id': applicant.survey.id, 'partner_id': applicant.partner_id.id}, context=context)
self.write(cr, uid, ids[0], {'response_id': response_id}, context=context)
if not self.response_id:
response = self.env['survey.user_input'].create({'survey_id': self.survey.id, 'partner_id': self.partner_id.id})
self.response_id = response.id
else:
response_id = applicant.response_id.id
response = self.response_id
# grab the token of the response and start surveying
response = response_obj.browse(cr, uid, response_id, context=context)
context.update({'survey_token': response.token})
return survey_obj.action_start_survey(cr, uid, [applicant.survey.id], context=context)
return self.survey.with_context(survey_token=response.token).action_start_survey()
def action_print_survey(self, cr, uid, ids, context=None):
@api.multi
def action_print_survey(self):
""" If response is available then print this response otherwise print survey form (print template of the survey) """
context = dict(context or {})
applicant = self.browse(cr, uid, ids, context=context)[0]
survey_obj = self.pool.get('survey.survey')
response_obj = self.pool.get('survey.user_input')
if not applicant.response_id:
return survey_obj.action_print_survey(cr, uid, [applicant.survey.id], context=context)
self.ensure_one()
if not self.response_id:
return self.survey.action_print_survey()
else:
response = response_obj.browse(cr, uid, applicant.response_id.id, context=context)
context.update({'survey_token': response.token})
return survey_obj.action_print_survey(cr, uid, [applicant.survey.id], context=context)
response = self.response_id
return self.survey.with_context(survey_token=response.token).action_print_survey()
def action_get_attachment_tree_view(self, cr, uid, ids, context=None):
model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'action_attachment')
action = self.pool.get(model).read(cr, uid, action_id, context=context)
action['context'] = {'default_res_model': self._name, 'default_res_id': ids[0]}
action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', ids)])
@api.multi
def action_get_attachment_tree_view(self):
attachment_action = self.env.ref('base.action_attachment')
action = attachment_action.read()[0]
action['context'] = {'default_res_model': self._name, 'default_res_id': self.ids[0]}
action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', self.ids)])
return action
def _track_subtype(self, cr, uid, ids, init_values, context=None):
record = self.browse(cr, uid, ids[0], context=context)
@api.multi
def _track_subtype(self, init_values):
record = self[0]
if 'emp_id' in init_values and record.emp_id:
return 'hr_recruitment.mt_applicant_hired'
elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1:
return 'hr_recruitment.mt_applicant_new'
elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence > 1:
return 'hr_recruitment.mt_applicant_stage_changed'
return super(hr_applicant, self)._track_subtype(cr, uid, ids, init_values, context=context)
return super(Applicant, self)._track_subtype(init_values)
@api.cr_uid_context
def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
@api.model
def message_get_reply_to(self, ids, default=None):
""" Override to get the reply_to of the parent project. """
applicants = self.browse(cr, SUPERUSER_ID, ids, context=context)
job_ids = set([applicant.job_id.id for applicant in applicants if applicant.job_id])
aliases = self.pool['hr.job'].message_get_reply_to(cr, uid, list(job_ids), default=default, context=context)
applicants = self.sudo().browse(ids)
aliases = self.env['hr.job'].message_get_reply_to(applicants.mapped('job_id').ids, default=default)
return dict((applicant.id, aliases.get(applicant.job_id and applicant.job_id.id or 0, False)) for applicant in applicants)
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
recipients = super(hr_applicant, self).message_get_suggested_recipients(cr, uid, ids, context=context)
for applicant in self.browse(cr, uid, ids, context=context):
@api.multi
def message_get_suggested_recipients(self):
recipients = super(Applicant, self).message_get_suggested_recipients()
for applicant in self:
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'))
return recipients
def message_new(self, cr, uid, msg, custom_values=None, context=None):
@api.model
def message_new(self, msg, custom_values=None):
""" Overrides mail_thread message_new that is called by the mailgateway
through message_process.
This override updates the document according to the email.
"""
if custom_values is None:
custom_values = {}
val = msg.get('from').split('<')[0]
defaults = {
'name': msg.get('subject') or _("No Subject"),
'name': msg.get('subject') or _("No Subject"),
'partner_name': val,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
@@ -412,148 +456,46 @@ class hr_applicant(osv.Model):
}
if msg.get('priority'):
defaults['priority'] = msg.get('priority')
defaults.update(custom_values)
return super(hr_applicant, self).message_new(cr, uid, msg, custom_values=defaults, context=context)
if custom_values:
defaults.update(custom_values)
return super(Applicant, self).message_new(msg, custom_values=defaults)
def create(self, cr, uid, vals, context=None):
context = dict(context or {})
context['mail_create_nolog'] = True
if vals.get('department_id') and not context.get('default_department_id'):
context['default_department_id'] = vals.get('department_id')
if vals.get('job_id') or context.get('default_job_id'):
job_id = vals.get('job_id') or context.get('default_job_id')
vals.update(self.onchange_job(cr, uid, [], job_id, context=context)['value'])
if vals.get('user_id'):
vals['date_open'] = fields.datetime.now()
if 'stage_id' in vals:
vals.update(self.onchange_stage_id(cr, uid, None, vals.get('stage_id'), context=context)['value'])
obj_id = super(hr_applicant, self).create(cr, uid, vals, context=context)
applicant = self.browse(cr, uid, obj_id, context=context)
if applicant.job_id:
name = applicant.partner_name if applicant.partner_name else applicant.name
self.pool['hr.job'].message_post(
cr, uid, [applicant.job_id.id],
body=_('New application from %s') % name,
subtype="hr_recruitment.mt_job_applicant_new", context=context)
return obj_id
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
res = True
# user_id change: update date_open
if vals.get('user_id'):
vals['date_open'] = fields.datetime.now()
# stage_id: track last stage before update
if 'stage_id' in vals:
vals['date_last_stage_update'] = fields.datetime.now()
vals.update(self.onchange_stage_id(cr, uid, ids, vals.get('stage_id'), context=context)['value'])
for applicant in self.browse(cr, uid, ids, context=None):
vals['last_stage_id'] = applicant.stage_id.id
res = super(hr_applicant, self).write(cr, uid, [applicant.id], vals, context=context)
else:
res = super(hr_applicant, self).write(cr, uid, ids, vals, context=context)
# post processing: if job changed, post a message on the job
if vals.get('job_id'):
for applicant in self.browse(cr, uid, ids, context=None):
name = applicant.partner_name if applicant.partner_name else applicant.name
self.pool['hr.job'].message_post(
cr, uid, [vals['job_id']],
body=_('New application from %s') % name,
subtype="hr_recruitment.mt_job_applicant_new", context=context)
# post processing: if stage changed, post a message in the chatter
if vals.get('stage_id'):
stage = self.pool['hr.recruitment.stage'].browse(cr, uid, vals['stage_id'], context=context)
if stage.template_id:
# TDENOTE: probably factorize me in a message_post_with_template generic method FIXME
compose_ctx = dict(context,
active_ids=ids)
compose_id = self.pool['mail.compose.message'].create(
cr, uid, {
'model': self._name,
'composition_mode': 'mass_mail',
'template_id': stage.template_id.id,
'notify': True,
}, context=compose_ctx)
values = self.pool['mail.compose.message'].onchange_template_id(
cr, uid, [compose_id], stage.template_id.id, 'mass_mail', self._name, False, context=compose_ctx)['value']
if values.get('attachment_ids'):
values['attachment_ids'] = [(6, 0, values['attachment_ids'])]
self.pool['mail.compose.message'].write(
cr, uid, [compose_id],
values,
context=compose_ctx)
self.pool['mail.compose.message'].send_mail(cr, uid, [compose_id], context=compose_ctx)
return res
def _broadcast_welcome(self, cr, uid, employee_id, context=None):
""" Broadcast the welcome message to all users in the employee company. """
IrModelData = self.pool['ir.model.data']
group_all_employees = IrModelData.xmlid_to_object(cr, uid, 'mail.channel_all_employees', context=context)
template_new_employee = IrModelData.xmlid_to_object(cr, uid, 'hr_recruitment.hr_welcome_new_employee', context=context)
if template_new_employee:
MailTemplate = self.pool['mail.template']
body_html = MailTemplate.render_template(cr, uid, template_new_employee.body_html, 'hr.employee', employee_id, context=context)
subject = MailTemplate.render_template(cr, uid, template_new_employee.subject, 'hr.employee', employee_id, context=context)
self.pool['mail.channel'].message_post(cr, uid, [group_all_employees.id],
body=body_html, subject=subject,
subtype='mail.mt_comment', context=context)
return True
def create_employee_from_applicant(self, cr, uid, ids, context=None):
@api.multi
def create_employee_from_applicant(self):
""" Create an hr.employee from the hr.applicants """
if context is None:
context = {}
hr_employee = self.pool.get('hr.employee')
model_data = self.pool.get('ir.model.data')
act_window = self.pool.get('ir.actions.act_window')
emp_id = False
for applicant in self.browse(cr, uid, ids, context=context):
employee = False
for applicant in self:
address_id = contact_name = False
if applicant.partner_id:
address_id = self.pool.get('res.partner').address_get(cr, uid, [applicant.partner_id.id], ['contact'])['contact']
contact_name = self.pool.get('res.partner').name_get(cr, uid, [applicant.partner_id.id])[0][1]
address_id = applicant.partner_id.address_get(['contact'])['contact']
contact_name = applicant.partner_id.name_get()[0][1]
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})
emp_id = hr_employee.create(cr, uid, {'name': applicant.partner_name or contact_name,
'job_id': applicant.job_id.id,
'address_home_id': address_id,
'department_id': applicant.department_id.id or False,
'address_id': applicant.company_id and applicant.company_id.partner_id and applicant.company_id.partner_id.id or False,
'work_email': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.email or False,
'work_phone': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.phone or False,
}, context=context)
self.write(cr, uid, [applicant.id], {'emp_id': emp_id}, context=context)
self.pool['hr.job'].message_post(
cr, uid, [applicant.job_id.id],
employee = self.env['hr.employee'].create({'name': applicant.partner_name or contact_name,
'job_id': applicant.job_id.id,
'address_home_id': address_id,
'department_id': applicant.department_id.id or False,
'address_id': applicant.company_id and applicant.company_id.partner_id and applicant.company_id.partner_id.id or False,
'work_email': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.email or False,
'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", context=context)
self._broadcast_welcome(cr, uid, emp_id, context=context)
subtype="hr_recruitment.mt_job_applicant_hired")
employee._broadcast_welcome()
else:
raise UserError(_('You must define an Applied Job and a Contact Name for this applicant.'))
action_model, action_id = model_data.get_object_reference(cr, uid, 'hr', 'open_view_employee_list')
dict_act_window = act_window.read(cr, uid, [action_id], [])[0]
if emp_id:
dict_act_window['res_id'] = emp_id
employee_action = self.env.ref('hr.open_view_employee_list')
dict_act_window = employee_action.read([])[0]
if employee:
dict_act_window['res_id'] = employee.id
dict_act_window['view_mode'] = 'form,tree'
return dict_act_window
def get_empty_list_help(self, cr, uid, help, context=None):
context = dict(context or {})
context['empty_list_help_model'] = 'hr.job'
context['empty_list_help_id'] = context.get('default_job_id', None)
context['empty_list_help_document_name'] = _("job applicants")
return super(hr_applicant, self).get_empty_list_help(cr, uid, help, context=context)
class applicant_category(osv.osv):
""" Category of applicant """
class applicant_category(models.Model):
_name = "hr.applicant.category"
_description = "Category of applicant"
_columns = {
'name': fields.char('Name', required=True, translate=True),
}
name = fields.Char("Name", required=True, translate=True)
@@ -1,54 +1,44 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp import api, fields, models
class hr_applicant_settings(osv.TransientModel):
class RecruitmentSettings(models.TransientModel):
_name = 'hr.recruitment.config.settings'
_inherit = ['res.config.settings', 'fetchmail.config.settings']
_columns = {
'module_document': fields.boolean('Allow the automatic indexation of resumes',
help='Manage your CV\'s and motivation letter related to all applicants.\n'
'-This installs the module document_ftp. This will install the knowledge management module in order to allow you to search using specific keywords through the content of all documents (PDF, .DOCx...)'),
'alias_prefix': fields.char('Default Alias Name for Jobs'),
'alias_domain': fields.char('Alias Domain'),
}
module_document = fields.Boolean(string='Allow the automatic indexation of resumes',
help='Manage your CV\'s and motivation letter related to all applicants.\n'
'-This installs the module document_ftp. This will install the knowledge management module in order to allow you to search using specific keywords through the content of all documents (PDF, .DOCx...)')
alias_prefix = fields.Char(string='Default Alias Name for Jobs')
alias_domain = fields.Char(string='Alias Domain',
default=lambda self: self.env["ir.config_parameter"].get_param("mail.catchall.domain"))
_defaults = {
'alias_domain': lambda self, cr, uid, context: self.pool["ir.config_parameter"].get_param(cr, uid, "mail.catchall.domain", context=context),
}
@api.model
def _find_default_job_alias_id(self):
jobs_alias = self.env.ref('hr_recruitment.mail_alias_jobs')
if not jobs_alias:
aliases = self.env['mail.alias'].search([
('alias_model_id.model', '=', 'hr.applicant'),
('alias_force_thread_id', '=', False),
('alias_parent_model_id.model', '=', 'hr.job'),
('alias_parent_thread_id', '=', False),
('alias_defaults', '=', '{}')], limit=1)
jobs_alias = aliases and aliases[0] or False
return jobs_alias
def _find_default_job_alias_id(self, cr, uid, context=None):
alias_id = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'hr_recruitment.mail_alias_jobs')
if not alias_id:
alias_ids = self.pool['mail.alias'].search(
cr, uid, [
('alias_model_id.model', '=', 'hr.applicant'),
('alias_force_thread_id', '=', False),
('alias_parent_model_id.model', '=', 'hr.job'),
('alias_parent_thread_id', '=', False),
('alias_defaults', '=', '{}')
], context=context)
alias_id = alias_ids and alias_ids[0] or False
return alias_id
@api.multi
def get_default_alias_prefix(self):
alias = self._find_default_job_alias_id()
return {'alias_prefix': alias and alias.alias_name or False}
def get_default_alias_prefix(self, cr, uid, ids, context=None):
alias_name = False
alias_id = self._find_default_job_alias_id(cr, uid, context=context)
if alias_id:
alias_name = self.pool['mail.alias'].browse(cr, uid, alias_id, context=context).alias_name
return {'alias_prefix': alias_name}
def set_default_alias_prefix(self, cr, uid, ids, context=None):
mail_alias = self.pool.get('mail.alias')
for record in self.browse(cr, uid, ids, context=context):
alias_id = self._find_default_job_alias_id(cr, uid, context=context)
if not alias_id:
create_ctx = dict(context, alias_model_name='hr.applicant', alias_parent_model_name='hr.job')
alias_id = self.pool['mail.alias'].create(cr, uid, {'alias_name': record.alias_prefix}, context=create_ctx)
@api.multi
def set_default_alias_prefix(self):
for record in self:
alias = self._find_default_job_alias_id()
if not alias:
alias = self.env['mail.alias'].with_context(alias_model_name='hr.applicant', alias_parent_model_name='hr.job').create({'alias_name': record.alias_prefix})
else:
mail_alias.write(cr, uid, alias_id, {'alias_name': record.alias_prefix}, context=context)
alias.write({'alias_name': record.alias_prefix})
return True
@@ -1,40 +1,35 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import tools
from openerp.osv import fields, osv
from openerp import fields, models, tools
from ..models import hr_recruitment
from openerp.addons.decimal_precision import decimal_precision as dp
class hr_recruitment_report(osv.Model):
class hr_recruitment_report(models.Model):
_name = "hr.recruitment.report"
_description = "Recruitments Statistics"
_auto = False
_rec_name = 'date_create'
_order = 'date_create desc'
_columns = {
'user_id': fields.many2one('res.users', 'User', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'date_create': fields.datetime('Create Date', readonly=True),
'date_last_stage_update': fields.datetime('Last Stage Update', readonly=True),
'date_closed': fields.date('Closed', readonly=True),
'job_id': fields.many2one('hr.job', 'Applied Job',readonly=True),
'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage'),
'type_id': fields.many2one('hr.recruitment.degree', 'Degree'),
'department_id': fields.many2one('hr.department','Department',readonly=True),
'priority': fields.selection(hr_recruitment.AVAILABLE_PRIORITIES, 'Appreciation'),
'salary_prop' : fields.float("Salary Proposed", digits=0),
'salary_prop_avg' : fields.float("Avg. Proposed Salary", group_operator="avg", digits=0),
'salary_exp' : fields.float("Salary Expected", digits=0),
'salary_exp_avg' : fields.float("Avg. Expected Salary", group_operator="avg", digits=0),
'partner_id': fields.many2one('res.partner', 'Partner',readonly=True),
'delay_close': fields.float('Avg. Delay to Close', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to close the project issue"),
'last_stage_id': fields.many2one ('hr.recruitment.stage', 'Last Stage'),
}
user_id = fields.Many2one('res.users', 'User', readonly=True)
company_id = fields.Many2one('res.company', 'Company', readonly=True)
date_create = fields.Datetime('Create Date', readonly=True)
date_last_stage_update = fields.Datetime('Last Stage Update', readonly=True)
date_closed = fields.Date('Closed', readonly=True)
job_id = fields.Many2one('hr.job', 'Applied Job', readonly=True)
stage_id = fields.Many2one('hr.recruitment.stage', 'Stage')
type_id = fields.Many2one('hr.recruitment.degree', 'Degree')
department_id = fields.Many2one('hr.department', 'Department', readonly=True)
priority = fields.Selection(hr_recruitment.AVAILABLE_PRIORITIES, 'Appreciation')
salary_prop = fields.Float("Salary Proposed", digits=0)
salary_prop_avg = fields.Float("Avg. Proposed Salary", group_operator="avg", digits=0)
salary_exp = fields.Float("Salary Expected", digits=0)
salary_exp_avg = fields.Float("Avg. Expected Salary", group_operator="avg", digits=0)
partner_id = fields.Many2one('res.partner', 'Partner', readonly=True)
delay_close = fields.Float('Avg. Delay to Close', digits=(16, 2), readonly=True, group_operator="avg", help="Number of Days to close the project issue")
last_stage_id = fields.Many2one('hr.recruitment.stage', 'Last Stage')
def init(self, cr):
tools.drop_view_if_exists(cr, 'hr_recruitment_report')
cr.execute("""
@@ -22,8 +22,9 @@
-
!python {model: mail.thread}: |
request_file = open(openerp.modules.module.get_module_resource('hr_recruitment','test', 'resume.eml'),'rb')
job_id = self.pool["ir.model.data"].xmlid_to_res_id(cr, uid, "hr.job_developer")
request_message = request_file.read()
self.message_process(cr, uid, 'hr.applicant', request_message)
self.message_process(cr, uid, 'hr.applicant', request_message, custom_values={"job_id": job_id})
-
After getting the mail, I check the details of the new applicant.
-
@@ -52,11 +53,3 @@
!python {model: hr.applicant}: |
context.update({'active_model': 'hr.applicant', 'active_ids': [ref("hr_recruitment.hr_case_programmer")], 'active_id': ref("hr_recruitment.hr_case_programmer")})
self.action_print_survey(cr, uid, [ref('hr_case_programmer')])
-
Now hired employee want to be a partner of company.
-
!record {model: hr.recruitment.partner.create, id: hr_recruitment_partner_id1 }:
-
!python {model: hr.recruitment.partner.create}: |
context.update({'active_model': 'hr.applicant', 'active_ids': [ref("hr_recruitment.hr_case_programmer")], 'active_id': ref("hr_recruitment.hr_case_programmer")})
self.make_order(cr, uid, [ref("hr_recruitment_partner_id1")], context=context)
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Stage -->
<record id="hr_job_stage_act" model="ir.actions.act_window">
<field name="name">Recruitment / Applicants Stages</field>
@@ -118,8 +118,7 @@
</div>
<group>
<group>
<field name="partner_id"
on_change="onchange_partner_id(partner_id)"/>
<field name="partner_id"/>
<field name="email_from" widget="email"/>
<field name="partner_phone"/>
<field name="partner_mobile"/>
@@ -141,7 +140,7 @@
<group string="Job">
<field name="survey" invisible="1"/>
<field name="response_id" invisible="1"/>
<field name="job_id" on_change="onchange_job(job_id,stage_id)"/>
<field name="job_id"/>
<field name="department_id"/>
<field name="company_id" groups="base.group_multi_company" options='{"no_open":True}' />
</group>
@@ -663,7 +662,7 @@
<field name="res_model">hr.applicant</field>
<field name="view_mode">kanban,tree,form,graph,calendar</field>
<field name="context">{
'search_default_department_id': [active_id],
'search_default_department_id': active_id,
'default_department_id': active_id}
</field>
<field name="domain">[('stage_id.sequence','&lt;=','1')]</field>