d4fe632591746835a1c1306fe01835c9a11362a1
[odoo/odoo.git] / addons / hr_recruitment / hr_recruitment.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from base_status.base_stage import base_stage
23 import time
24 from datetime import datetime, timedelta
25
26 from osv import fields, osv
27 from crm import crm
28 import tools
29 import collections
30 import binascii
31 import tools
32 from tools.translate import _
33 from crm import wizard
34
35 wizard.mail_compose_message.SUPPORTED_MODELS.append('hr.applicant')
36
37 AVAILABLE_STATES = [
38     ('draft', 'New'),
39     ('cancel', 'Refused'),
40     ('open', 'In Progress'),
41     ('pending', 'Pending'),
42     ('done', 'Hired')
43 ]
44
45 AVAILABLE_PRIORITIES = [
46     ('', ''),
47     ('5', 'Not Good'),
48     ('4', 'On Average'),
49     ('3', 'Good'),
50     ('2', 'Very Good'),
51     ('1', 'Excellent')
52 ]
53
54 class hr_recruitment_source(osv.osv):
55     """ Sources of HR Recruitment """
56     _name = "hr.recruitment.source"
57     _description = "Source of Applicants"
58     _columns = {
59         'name': fields.char('Source Name', size=64, required=True, translate=True),
60     }
61
62 class hr_recruitment_stage(osv.osv):
63     """ Stage of HR Recruitment """
64     _name = "hr.recruitment.stage"
65     _description = "Stage of Recruitment"
66     _order = 'sequence'
67     _columns = {
68         'name': fields.char('Name', size=64, required=True, translate=True),
69         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
70         '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 tempy this field."),
71         'state': fields.selection(AVAILABLE_STATES, 'State', required=True, help="The related state for the stage. The state of your document will automatically change regarding the selected stage. Example, a stage is related to the state 'Close', when your document reach this stage, it will be automatically closed."),
72         'fold': fields.boolean('Hide in views if empty', help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
73         'requirements': fields.text('Requirements'),
74     }
75     _defaults = {
76         'sequence': 1,
77         'state': 'draft',
78         'fold': False,
79     }
80
81 class hr_recruitment_degree(osv.osv):
82     """ Degree of HR Recruitment """
83     _name = "hr.recruitment.degree"
84     _description = "Degree of Recruitment"
85     _columns = {
86         'name': fields.char('Name', size=64, required=True, translate=True),
87         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of degrees."),
88     }
89     _defaults = {
90         'sequence': 1,
91     }
92     _sql_constraints = [
93         ('name_uniq', 'unique (name)', 'The name of the Degree of Recruitment must be unique!')
94     ]
95
96 class hr_applicant(base_stage, osv.Model):
97     _name = "hr.applicant"
98     _description = "Applicant"
99     _order = "id desc"
100     _inherit = ['ir.needaction_mixin', 'mail.thread']
101
102     def _get_default_department_id(self, cr, uid, context=None):
103         """ Gives default department by checking if present in the context """
104         return (self._resolve_department_id_from_context(cr, uid, context=context) or False)
105
106     def _get_default_stage_id(self, cr, uid, context=None):
107         """ Gives default stage_id """
108         department_id = self._get_default_department_id(cr, uid, context=context)
109         return self.stage_find(cr, uid, [], department_id, [('state', '=', 'draft')], context=context)
110
111     def _resolve_department_id_from_context(self, cr, uid, context=None):
112         """ Returns ID of department based on the value of 'default_department_id'
113             context key, or None if it cannot be resolved to a single
114             department.
115         """
116         if context is None:
117             context = {}
118         if type(context.get('default_department_id')) in (int, long):
119             return context.get('default_department_id')
120         if isinstance(context.get('default_department_id'), basestring):
121             department_name = context['default_department_id']
122             department_ids = self.pool.get('hr.department').name_search(cr, uid, name=department_name, context=context)
123             if len(department_ids) == 1:
124                 return int(department_ids[0][0])
125         return None
126
127     def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
128         access_rights_uid = access_rights_uid or uid
129         stage_obj = self.pool.get('hr.recruitment.stage')
130         order = stage_obj._order
131         # lame hack to allow reverting search, should just work in the trivial case
132         if read_group_order == 'stage_id desc':
133             order = "%s desc" % order
134         # retrieve section_id from the context and write the domain
135         # - ('id', 'in', 'ids'): add columns that should be present
136         # - OR ('department_id', '=', False), ('fold', '=', False): add default columns that are not folded
137         # - OR ('department_id', 'in', department_id), ('fold', '=', False) if department_id: add department columns that are not folded
138         department_id = self._resolve_department_id_from_context(cr, uid, context=context)
139         search_domain = []
140         if department_id:
141             search_domain += ['|', '&', ('department_id', '=', department_id), ('fold', '=', False)]
142         search_domain += ['|', ('id', 'in', ids), '&', ('department_id', '=', False), ('fold', '=', False)]
143         stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
144         result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
145         # restore order of the search
146         result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
147         return result
148
149     def _compute_day(self, cr, uid, ids, fields, args, context=None):
150         """
151         @param cr: the current row, from the database cursor,
152         @param uid: the current user’s ID for security checks,
153         @param ids: List of Openday’s IDs
154         @return: difference between current date and log date
155         @param context: A standard dictionary for contextual values
156         """
157         res = {}
158         for issue in self.browse(cr, uid, ids, context=context):
159             for field in fields:
160                 res[issue.id] = {}
161                 duration = 0
162                 ans = False
163                 hours = 0
164
165                 if field in ['day_open']:
166                     if issue.date_open:
167                         date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
168                         date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S")
169                         ans = date_open - date_create
170                         date_until = issue.date_open
171
172                 elif field in ['day_close']:
173                     if issue.date_closed:
174                         date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
175                         date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S")
176                         date_until = issue.date_closed
177                         ans = date_close - date_create
178                 if ans:
179                     duration = float(ans.days)
180                     res[issue.id][field] = abs(float(duration))
181         return res
182
183     _columns = {
184         'name': fields.char('Name', size=128, required=True),
185         'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
186         'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."),
187         'description': fields.text('Description'),
188         'email_from': fields.char('Email', size=128, help="These people will receive email."),
189         '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"),
190         'probability': fields.float('Probability'),
191         'partner_id': fields.many2one('res.partner', 'Partner'),
192         'create_date': fields.datetime('Creation Date', readonly=True, select=True),
193         'write_date': fields.datetime('Update Date', readonly=True),
194         'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage',
195                         domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
196         'state': fields.related('stage_id', 'state', type="selection", store=True,
197                 selection=AVAILABLE_STATES, string="State", readonly=True,
198                 help='The state is set to \'Draft\', when a case is created.\
199                       If the case is in progress the state is set to \'Open\'.\
200                       When the case is over, the state is set to \'Done\'.\
201                       If the case needs to be reviewed then the state is \
202                       set to \'Pending\'.'),
203         'company_id': fields.many2one('res.company', 'Company'),
204         'user_id': fields.many2one('res.users', 'Responsible'),
205         # Applicant Columns
206         'date_closed': fields.datetime('Closed', readonly=True, select=True),
207         'date_open': fields.datetime('Opened', readonly=True, select=True),
208         'date': fields.datetime('Date'),
209         'date_action': fields.date('Next Action Date'),
210         'title_action': fields.char('Next Action', size=64),
211         'priority': fields.selection(AVAILABLE_PRIORITIES, 'Appreciation'),
212         'job_id': fields.many2one('hr.job', 'Applied Job'),
213         'salary_proposed_extra': fields.char('Proposed Salary Extra', size=100, help="Salary Proposed by the Organisation, extra advantages"),
214         'salary_expected_extra': fields.char('Expected Salary Extra', size=100, help="Salary Expected by Applicant, extra advantages"),
215         'salary_proposed': fields.float('Proposed Salary', help="Salary Proposed by the Organisation"),
216         'salary_expected': fields.float('Expected Salary', help="Salary Expected by Applicant"),
217         'availability': fields.integer('Availability (Days)'),
218         'partner_name': fields.char("Applicant's Name", size=64),
219         'partner_phone': fields.char('Phone', size=32),
220         'partner_mobile': fields.char('Mobile', size=32),
221         'type_id': fields.many2one('hr.recruitment.degree', 'Degree'),
222         'department_id': fields.many2one('hr.department', 'Department'),
223         'survey': fields.related('job_id', 'survey_id', type='many2one', relation='survey', string='Survey'),
224         'response': fields.integer("Response"),
225         'reference': fields.char('Refered By', size=128),
226         'source_id': fields.many2one('hr.recruitment.source', 'Source'),
227         'day_open': fields.function(_compute_day, string='Days to Open', \
228                                 multi='day_open', type="float", store=True),
229         'day_close': fields.function(_compute_day, string='Days to Close', \
230                                 multi='day_close', type="float", store=True),
231         'color': fields.integer('Color Index'),
232         'emp_id': fields.many2one('hr.employee', 'employee'),
233         'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
234     }
235
236     _defaults = {
237         'active': lambda *a: 1,
238         'user_id':  lambda s, cr, uid, c: uid,
239         'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
240         'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
241         'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
242         'priority': lambda *a: '',
243         'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
244         'color': 0,
245     }
246
247     _group_by_full = {
248         'stage_id': _read_group_stage_ids
249     }
250
251     def onchange_job(self,cr, uid, ids, job, context=None):
252         result = {}
253
254         if job:
255             job_obj = self.pool.get('hr.job')
256             result['department_id'] = job_obj.browse(cr, uid, job, context=context).department_id.id
257             return {'value': result}
258         return {'value': {'department_id': False}}
259
260     def onchange_department_id(self, cr, uid, ids, department_id=False, context=None):
261         if not department_id:
262             return {'value': {'stage_id': False}}
263         obj_recru_stage = self.pool.get('hr.recruitment.stage')
264         stage_ids = obj_recru_stage.search(cr, uid, ['|',('department_id','=',department_id),('department_id','=',False)], context=context)
265         stage_id = stage_ids and stage_ids[0] or False
266         return {'value': {'stage_id': stage_id}}
267
268     def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
269         """ Override of the base.stage method
270             Parameter of the stage search taken from the lead:
271             - department_id: if set, stages must belong to this section or
272               be a default case
273         """
274         if isinstance(cases, (int, long)):
275             cases = self.browse(cr, uid, cases, context=context)
276         domain = list(domain)
277         if section_id:
278                 domain += ['|', ('department_id', '=', section_id), ('department_id', '=', False)]
279         for case in cases:
280             case_section_id = case.department_id.id if case.department_id else None
281             if case_section_id:
282                 domain += ['|', ('department_id', '=', case_section_id), ('department_id', '=', False)]
283         stage_ids = self.pool.get('hr.recruitment.stage').search(cr, uid, domain, order=order, context=context)
284         if stage_ids:
285             return stage_ids[0]
286         return False
287
288     def stage_previous(self, cr, uid, ids, context=None):
289         """ This function computes previous stage for case from its current stage
290             using available stage for that case type
291         """
292         stage_obj = self.pool.get('hr.recruitment.stage')
293         for case in self.browse(cr, uid, ids, context=context):
294             department = (case.department_id.id or False)
295             st = case.stage_id.id  or False
296             stage_ids = stage_obj.search(cr, uid, ['|',('department_id','=',department),('department_id','=',False)], context=context)
297             if st and stage_ids.index(st):
298                 self.write(cr, uid, [case.id], {'stage_id': stage_ids[stage_ids.index(st)-1]}, context=context)
299             else:
300                 self.write(cr, uid, [case.id], {'stage_id': False}, context=context)
301         return True
302
303     def stage_next(self, cr, uid, ids, context=None):
304         """ This function computes next stage for case from its current stage
305              using available stage for that case type
306         """
307         stage_obj = self.pool.get('hr.recruitment.stage')
308         for case in self.browse(cr, uid, ids, context=context):
309             department = (case.department_id.id or False)
310             st = case.stage_id.id  or False
311             stage_ids = stage_obj.search(cr, uid, ['|',('department_id','=',department),('department_id','=',False)], context=context)
312             val = False
313             if st and len(stage_ids) != stage_ids.index(st)+1:
314                 val = stage_ids[stage_ids.index(st)+1]
315             elif (not st) and stage_ids:
316                 val = stage_ids[0]
317             else:
318                 val = False
319             self.write(cr, uid, [case.id], {'stage_id': val}, context=context)
320         return True
321
322     def action_makeMeeting(self, cr, uid, ids, context=None):
323         """
324         This opens Meeting's calendar view to schedule meeting on current Opportunity
325         @param self: The object pointer
326         @param cr: the current row, from the database cursor,
327         @param uid: the current user’s ID for security checks,
328         @param ids: List of Opportunity to Meeting IDs
329         @param context: A standard dictionary for contextual values
330
331         @return: Dictionary value for created Meeting view
332         """
333         data_obj = self.pool.get('ir.model.data')
334         if context is None:
335             context = {}
336         value = {}
337         for opp in self.browse(cr, uid, ids, context=context):
338             # Get meeting views
339             result = data_obj._get_id(cr, uid, 'crm', 'view_crm_case_meetings_filter')
340             res = data_obj.read(cr, uid, result, ['res_id'], context=context)
341             id1 = data_obj._get_id(cr, uid, 'crm', 'crm_case_calendar_view_meet')
342             id2 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view_meet')
343             id3 = data_obj._get_id(cr, uid, 'crm', 'crm_case_tree_view_meet')
344             if id1:
345                 id1 = data_obj.browse(cr, uid, id1, context=context).res_id
346             if id2:
347                 id2 = data_obj.browse(cr, uid, id2, context=context).res_id
348             if id3:
349                 id3 = data_obj.browse(cr, uid, id3, context=context).res_id
350
351             context = {
352                 'default_partner_id': opp.partner_id and opp.partner_id.id or False,
353                 'default_email_from': opp.email_from,
354                 'default_state': 'open',
355                 'default_name': opp.name
356             }
357             value = {
358                 'name': ('Meetings'),
359                 'domain': "[('user_id','=',%s)]" % (uid),
360                 'context': context,
361                 'view_type': 'form',
362                 'view_mode': 'calendar,form,tree',
363                 'res_model': 'crm.meeting',
364                 'view_id': False,
365                 'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
366                 'type': 'ir.actions.act_window',
367                 'search_view_id': res['res_id'],
368                 'nodestroy': True
369             }
370         return value
371
372     def action_print_survey(self, cr, uid, ids, context=None):
373         """
374         If response is available then print this response otherwise print survey form(print template of the survey).
375
376         @param self: The object pointer
377         @param cr: the current row, from the database cursor,
378         @param uid: the current user’s ID for security checks,
379         @param ids: List of Survey IDs
380         @param context: A standard dictionary for contextual values
381         @return: Dictionary value for print survey form.
382         """
383         if context is None:
384             context = {}
385         record = self.browse(cr, uid, ids, context=context)
386         record = record and record[0]
387         context.update({'survey_id': record.survey.id, 'response_id': [record.response], 'response_no': 0, })
388         value = self.pool.get("survey").action_print_survey(cr, uid, ids, context=context)
389         return value
390
391     def message_new(self, cr, uid, msg, custom_values=None, context=None):
392         """Automatically called when new email message arrives"""
393         res_id = super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
394         subject = msg.get('subject') or _("No Subject")
395         body = msg.get('body_text')
396         msg_from = msg.get('from')
397         priority = msg.get('priority')
398         vals = {
399             'name': subject,
400             'email_from': msg_from,
401             'email_cc': msg.get('cc'),
402             'description': body,
403             'user_id': False,
404         }
405         if priority:
406             vals['priority'] = priority
407         vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False)))
408         self.write(cr, uid, [res_id], vals, context)
409         return res_id
410
411     def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
412         if isinstance(ids, (str, int, long)):
413             ids = [ids]
414         if vals is None:
415             vals = {}
416         msg_from = msg['from']
417         vals.update({
418             'description': msg['body_text']
419         })
420         if msg.get('priority', False):
421             vals['priority'] = msg.get('priority')
422
423         maps = {
424             'cost':'planned_cost',
425             'revenue': 'planned_revenue',
426             'probability':'probability'
427         }
428         vls = { }
429         for line in msg['body_text'].split('\n'):
430             line = line.strip()
431             res = tools.misc.command_re.match(line)
432             if res and maps.get(res.group(1).lower(), False):
433                 key = maps.get(res.group(1).lower())
434                 vls[key] = res.group(2).lower()
435
436         vals.update(vls)
437         res = self.write(cr, uid, ids, vals, context=context)
438         self.message_append_dict(cr, uid, ids, msg, context=context)
439         return res
440
441     def create(self, cr, uid, vals, context=None):
442         obj_id = super(hr_applicant, self).create(cr, uid, vals, context=context)
443         self.create_send_note(cr, uid, [obj_id], context=context)
444         return obj_id
445
446     def case_open(self, cr, uid, ids, context=None):
447         """
448             open Request of the applicant for the hr_recruitment
449         """
450         res = super(hr_applicant, self).case_open(cr, uid, ids, context)
451         date = self.read(cr, uid, ids, ['date_open'])[0]
452         if not date['date_open']:
453             self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'),})
454         return res
455
456     def case_close(self, cr, uid, ids, context=None):
457         res = super(hr_applicant, self).case_close(cr, uid, ids, context)
458         return res
459
460     def case_close_with_emp(self, cr, uid, ids, context=None):
461         if context is None:
462             context = {}
463         hr_employee = self.pool.get('hr.employee')
464         model_data = self.pool.get('ir.model.data')
465         act_window = self.pool.get('ir.actions.act_window')
466         emp_id = False
467         for applicant in self.browse(cr, uid, ids, context=context):
468             address_id = False
469             if applicant.partner_id:
470                 address_id = applicant.partner_id.address_get(['contact'])['contact']
471             if applicant.job_id:
472                 applicant.job_id.write({'no_of_recruitment': applicant.job_id.no_of_recruitment - 1})
473                 emp_id = hr_employee.create(cr,uid,{'name': applicant.partner_name or applicant.name,
474                                                      'job_id': applicant.job_id.id,
475                                                      'address_home_id': address_id,
476                                                      'department_id': applicant.department_id.id
477                                                      })
478                 self.write(cr, uid, [applicant.id], {'emp_id': emp_id}, context=context)
479                 self.case_close(cr, uid, [applicant.id], context)
480             else:
481                 raise osv.except_osv(_('Warning!'),_('You must define Applied Job for this applicant.'))
482
483         action_model, action_id = model_data.get_object_reference(cr, uid, 'hr', 'open_view_employee_list')
484         dict_act_window = act_window.read(cr, uid, action_id, [])
485         if emp_id:
486             dict_act_window['res_id'] = emp_id
487         dict_act_window['view_mode'] = 'form,tree'
488         return dict_act_window
489
490     def case_cancel(self, cr, uid, ids, context=None):
491         """Overrides cancel for crm_case for setting probability
492         """
493         res = super(hr_applicant, self).case_cancel(cr, uid, ids, context)
494         self.write(cr, uid, ids, {'probability' : 0.0})
495         return res
496
497     def case_pending(self, cr, uid, ids, context=None):
498         """Marks case as pending"""
499         res = super(hr_applicant, self).case_pending(cr, uid, ids, context)
500         self.write(cr, uid, ids, {'probability' : 0.0})
501         return res
502
503     def case_reset(self, cr, uid, ids, context=None):
504         """Resets case as draft
505         """
506         res = super(hr_applicant, self).case_reset(cr, uid, ids, context)
507         self.write(cr, uid, ids, {'date_open': False, 'date_closed': False})
508         return res
509
510     def set_priority(self, cr, uid, ids, priority, *args):
511         """Set applicant priority
512         """
513         return self.write(cr, uid, ids, {'priority' : priority})
514
515     def set_high_priority(self, cr, uid, ids, *args):
516         """Set applicant priority to high
517         """
518         return self.set_priority(cr, uid, ids, '1')
519
520     def set_normal_priority(self, cr, uid, ids, *args):
521         """Set applicant priority to normal
522         """
523         return self.set_priority(cr, uid, ids, '3')
524
525     def write(self, cr, uid, ids, vals, context=None):
526         if 'stage_id' in vals and vals['stage_id']:
527             stage = self.pool.get('hr.recruitment.stage').browse(cr, uid, vals['stage_id'], context=context)
528             self.message_append_note(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % stage.name, context=context)
529         return super(hr_applicant,self).write(cr, uid, ids, vals, context=context)
530
531     # -------------------------------------------------------
532     # OpenChatter methods and notifications
533     # -------------------------------------------------------
534     
535     def message_get_subscribers(self, cr, uid, ids, context=None):
536         sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
537         for obj in self.browse(cr, uid, ids, context=context):
538             if obj.user_id:
539                 sub_ids.append(obj.user_id.id)
540         return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
541
542     def get_needaction_user_ids(self, cr, uid, ids, context=None):
543         result = dict.fromkeys(ids, [])
544         for obj in self.browse(cr, uid, ids, context=context):
545             if obj.state == 'draft' and obj.user_id:
546                 result[obj.id] = [obj.user_id.id]
547         return result
548     
549     def case_get_note_msg_prefix(self, cr, uid, id, context=None):
550                 return 'Applicant'
551
552     def case_open_send_note(self, cr, uid, ids, context=None):
553         message = _("Applicant has been set <b>in progress</b>.")
554         return self.message_append_note(cr, uid, ids, body=message, context=context)
555
556     def case_close_send_note(self, cr, uid, ids, context=None):
557         if context is None:
558             context = {}
559         for applicant in self.browse(cr, uid, ids, context=context):
560             if applicant.emp_id:
561                 message = _("Applicant has been <b>hired</b> and created as an employee.")
562                 self.message_append_note(cr, uid, [applicant.id], body=message, context=context)
563             else:
564                 message = _("Applicant has been <b>hired</b>.")
565                 self.message_append_note(cr, uid, [applicant.id], body=message, context=context)
566         return True
567
568     def case_cancel_send_note(self, cr, uid, ids, context=None):
569         msg = 'Applicant <b>refused</b>.'
570         return self.message_append_note(cr, uid, ids, body=msg, context=context)
571
572     def case_reset_send_note(self,  cr, uid, ids, context=None):
573         message =_("Applicant has been set as <b>new</b>.")
574         return self.message_append_note(cr, uid, ids, body=message, context=context)
575
576     def create_send_note(self, cr, uid, ids, context=None):
577         message = _("Applicant has been <b>created</b>.")
578         return self.message_append_note(cr, uid, ids, body=message, context=context)
579
580
581 class hr_job(osv.osv):
582     _inherit = "hr.job"
583     _name = "hr.job"
584     _columns = {
585         'survey_id': fields.many2one('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"),
586     }
587
588
589 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: