[CLEAN] crm_claim, hr_recruitment: very small code cleaning
[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         # get department_id from context
135         department_id = self._resolve_department_id_from_context(cr, uid, context=context)
136         search_domain = []
137         if department_id:
138             search_domain += ['|', '&', ('department_id', '=', department_id), ('fold', '=', False)]
139         search_domain += ['|', ('id', 'in', ids), '&', ('department_id', '=', False), ('fold', '=', False)]
140         stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
141         result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
142         # restore order of the search
143         result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
144         return result
145
146     def _compute_day(self, cr, uid, ids, fields, args, context=None):
147         """
148         @param cr: the current row, from the database cursor,
149         @param uid: the current user’s ID for security checks,
150         @param ids: List of Openday’s IDs
151         @return: difference between current date and log date
152         @param context: A standard dictionary for contextual values
153         """
154         res = {}
155         for issue in self.browse(cr, uid, ids, context=context):
156             for field in fields:
157                 res[issue.id] = {}
158                 duration = 0
159                 ans = False
160                 hours = 0
161
162                 if field in ['day_open']:
163                     if issue.date_open:
164                         date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
165                         date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S")
166                         ans = date_open - date_create
167                         date_until = issue.date_open
168
169                 elif field in ['day_close']:
170                     if issue.date_closed:
171                         date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
172                         date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S")
173                         date_until = issue.date_closed
174                         ans = date_close - date_create
175                 if ans:
176                     duration = float(ans.days)
177                     res[issue.id][field] = abs(float(duration))
178         return res
179
180     _columns = {
181         'name': fields.char('Name', size=128, required=True),
182         'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
183         'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."),
184         'description': fields.text('Description'),
185         'email_from': fields.char('Email', size=128, help="These people will receive email."),
186         '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"),
187         'probability': fields.float('Probability'),
188         'partner_id': fields.many2one('res.partner', 'Partner'),
189         'create_date': fields.datetime('Creation Date', readonly=True, select=True),
190         'write_date': fields.datetime('Update Date', readonly=True),
191         'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage',
192                         domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
193         'state': fields.related('stage_id', 'state', type="selection", store=True,
194                 selection=AVAILABLE_STATES, string="State", readonly=True,
195                 help='The state is set to \'Draft\', when a case is created.\
196                       If the case is in progress the state is set to \'Open\'.\
197                       When the case is over, the state is set to \'Done\'.\
198                       If the case needs to be reviewed then the state is \
199                       set to \'Pending\'.'),
200         'company_id': fields.many2one('res.company', 'Company'),
201         'user_id': fields.many2one('res.users', 'Responsible'),
202         # Applicant Columns
203         'date_closed': fields.datetime('Closed', readonly=True, select=True),
204         'date_open': fields.datetime('Opened', readonly=True, select=True),
205         'date': fields.datetime('Date'),
206         'date_action': fields.date('Next Action Date'),
207         'title_action': fields.char('Next Action', size=64),
208         'priority': fields.selection(AVAILABLE_PRIORITIES, 'Appreciation'),
209         'job_id': fields.many2one('hr.job', 'Applied Job'),
210         'salary_proposed_extra': fields.char('Proposed Salary Extra', size=100, help="Salary Proposed by the Organisation, extra advantages"),
211         'salary_expected_extra': fields.char('Expected Salary Extra', size=100, help="Salary Expected by Applicant, extra advantages"),
212         'salary_proposed': fields.float('Proposed Salary', help="Salary Proposed by the Organisation"),
213         'salary_expected': fields.float('Expected Salary', help="Salary Expected by Applicant"),
214         'availability': fields.integer('Availability (Days)'),
215         'partner_name': fields.char("Applicant's Name", size=64),
216         'partner_phone': fields.char('Phone', size=32),
217         'partner_mobile': fields.char('Mobile', size=32),
218         'type_id': fields.many2one('hr.recruitment.degree', 'Degree'),
219         'department_id': fields.many2one('hr.department', 'Department'),
220         'survey': fields.related('job_id', 'survey_id', type='many2one', relation='survey', string='Survey'),
221         'response': fields.integer("Response"),
222         'reference': fields.char('Refered By', size=128),
223         'source_id': fields.many2one('hr.recruitment.source', 'Source'),
224         'day_open': fields.function(_compute_day, string='Days to Open', \
225                                 multi='day_open', type="float", store=True),
226         'day_close': fields.function(_compute_day, string='Days to Close', \
227                                 multi='day_close', type="float", store=True),
228         'color': fields.integer('Color Index'),
229         'emp_id': fields.many2one('hr.employee', 'employee'),
230         'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
231     }
232
233     _defaults = {
234         'active': lambda *a: 1,
235         'user_id':  lambda s, cr, uid, c: uid,
236         'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
237         'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
238         'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
239         'priority': lambda *a: '',
240         'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
241         'color': 0,
242     }
243
244     _group_by_full = {
245         'stage_id': _read_group_stage_ids
246     }
247
248     def onchange_job(self,cr, uid, ids, job, context=None):
249         result = {}
250
251         if job:
252             job_obj = self.pool.get('hr.job')
253             result['department_id'] = job_obj.browse(cr, uid, job, context=context).department_id.id
254             return {'value': result}
255         return {'value': {'department_id': False}}
256
257     def onchange_department_id(self, cr, uid, ids, department_id=False, context=None):
258         if not department_id:
259             return {'value': {'stage_id': False}}
260         obj_recru_stage = self.pool.get('hr.recruitment.stage')
261         stage_ids = obj_recru_stage.search(cr, uid, ['|',('department_id','=',department_id),('department_id','=',False)], context=context)
262         stage_id = stage_ids and stage_ids[0] or False
263         return {'value': {'stage_id': stage_id}}
264
265     def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
266         """ Override of the base.stage method
267             Parameter of the stage search taken from the lead:
268             - department_id: if set, stages must belong to this section or
269               be a default case
270         """
271         if isinstance(cases, (int, long)):
272             cases = self.browse(cr, uid, cases, context=context)
273         domain = list(domain)
274         if section_id:
275                 domain += ['|', ('department_id', '=', section_id), ('department_id', '=', False)]
276         for case in cases:
277             case_section_id = case.department_id.id if case.department_id else None
278             if case_section_id:
279                 domain += ['|', ('department_id', '=', case_section_id), ('department_id', '=', False)]
280         stage_ids = self.pool.get('hr.recruitment.stage').search(cr, uid, domain, order=order, context=context)
281         if stage_ids:
282             return stage_ids[0]
283         return False
284
285     def stage_previous(self, cr, uid, ids, context=None):
286         """ This function computes previous stage for case from its current stage
287             using available stage for that case type
288         """
289         stage_obj = self.pool.get('hr.recruitment.stage')
290         for case in self.browse(cr, uid, ids, context=context):
291             department = (case.department_id.id or False)
292             st = case.stage_id.id  or False
293             stage_ids = stage_obj.search(cr, uid, ['|',('department_id','=',department),('department_id','=',False)], context=context)
294             if st and stage_ids.index(st):
295                 self.write(cr, uid, [case.id], {'stage_id': stage_ids[stage_ids.index(st)-1]}, context=context)
296             else:
297                 self.write(cr, uid, [case.id], {'stage_id': False}, context=context)
298         return True
299
300     def stage_next(self, cr, uid, ids, context=None):
301         """ This function computes next stage for case from its current stage
302              using available stage for that case type
303         """
304         stage_obj = self.pool.get('hr.recruitment.stage')
305         for case in self.browse(cr, uid, ids, context=context):
306             department = (case.department_id.id or False)
307             st = case.stage_id.id  or False
308             stage_ids = stage_obj.search(cr, uid, ['|',('department_id','=',department),('department_id','=',False)], context=context)
309             val = False
310             if st and len(stage_ids) != stage_ids.index(st)+1:
311                 val = stage_ids[stage_ids.index(st)+1]
312             elif (not st) and stage_ids:
313                 val = stage_ids[0]
314             else:
315                 val = False
316             self.write(cr, uid, [case.id], {'stage_id': val}, context=context)
317         return True
318
319     def action_makeMeeting(self, cr, uid, ids, context=None):
320         """
321         This opens Meeting's calendar view to schedule meeting on current Opportunity
322         @param self: The object pointer
323         @param cr: the current row, from the database cursor,
324         @param uid: the current user’s ID for security checks,
325         @param ids: List of Opportunity to Meeting IDs
326         @param context: A standard dictionary for contextual values
327
328         @return: Dictionary value for created Meeting view
329         """
330         data_obj = self.pool.get('ir.model.data')
331         if context is None:
332             context = {}
333         value = {}
334         for opp in self.browse(cr, uid, ids, context=context):
335             # Get meeting views
336             result = data_obj._get_id(cr, uid, 'crm', 'view_crm_case_meetings_filter')
337             res = data_obj.read(cr, uid, result, ['res_id'], context=context)
338             id1 = data_obj._get_id(cr, uid, 'crm', 'crm_case_calendar_view_meet')
339             id2 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view_meet')
340             id3 = data_obj._get_id(cr, uid, 'crm', 'crm_case_tree_view_meet')
341             if id1:
342                 id1 = data_obj.browse(cr, uid, id1, context=context).res_id
343             if id2:
344                 id2 = data_obj.browse(cr, uid, id2, context=context).res_id
345             if id3:
346                 id3 = data_obj.browse(cr, uid, id3, context=context).res_id
347
348             context = {
349                 'default_partner_id': opp.partner_id and opp.partner_id.id or False,
350                 'default_email_from': opp.email_from,
351                 'default_state': 'open',
352                 'default_name': opp.name
353             }
354             value = {
355                 'name': ('Meetings'),
356                 'domain': "[('user_id','=',%s)]" % (uid),
357                 'context': context,
358                 'view_type': 'form',
359                 'view_mode': 'calendar,form,tree',
360                 'res_model': 'crm.meeting',
361                 'view_id': False,
362                 'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
363                 'type': 'ir.actions.act_window',
364                 'search_view_id': res['res_id'],
365                 'nodestroy': True
366             }
367         return value
368
369     def action_print_survey(self, cr, uid, ids, context=None):
370         """
371         If response is available then print this response otherwise print survey form(print template of the survey).
372
373         @param self: The object pointer
374         @param cr: the current row, from the database cursor,
375         @param uid: the current user’s ID for security checks,
376         @param ids: List of Survey IDs
377         @param context: A standard dictionary for contextual values
378         @return: Dictionary value for print survey form.
379         """
380         if context is None:
381             context = {}
382         record = self.browse(cr, uid, ids, context=context)
383         record = record and record[0]
384         context.update({'survey_id': record.survey.id, 'response_id': [record.response], 'response_no': 0, })
385         value = self.pool.get("survey").action_print_survey(cr, uid, ids, context=context)
386         return value
387
388     def message_new(self, cr, uid, msg, custom_values=None, context=None):
389         """Automatically called when new email message arrives"""
390         res_id = super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
391         subject = msg.get('subject') or _("No Subject")
392         body = msg.get('body_text')
393         msg_from = msg.get('from')
394         priority = msg.get('priority')
395         vals = {
396             'name': subject,
397             'email_from': msg_from,
398             'email_cc': msg.get('cc'),
399             'description': body,
400             'user_id': False,
401         }
402         if priority:
403             vals['priority'] = priority
404         vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False)))
405         self.write(cr, uid, [res_id], vals, context)
406         return res_id
407
408     def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
409         if isinstance(ids, (str, int, long)):
410             ids = [ids]
411         if vals is None:
412             vals = {}
413         msg_from = msg['from']
414         vals.update({
415             'description': msg['body_text']
416         })
417         if msg.get('priority', False):
418             vals['priority'] = msg.get('priority')
419
420         maps = {
421             'cost':'planned_cost',
422             'revenue': 'planned_revenue',
423             'probability':'probability'
424         }
425         vls = { }
426         for line in msg['body_text'].split('\n'):
427             line = line.strip()
428             res = tools.misc.command_re.match(line)
429             if res and maps.get(res.group(1).lower(), False):
430                 key = maps.get(res.group(1).lower())
431                 vls[key] = res.group(2).lower()
432
433         vals.update(vls)
434         res = self.write(cr, uid, ids, vals, context=context)
435         self.message_append_dict(cr, uid, ids, msg, context=context)
436         return res
437
438     def create(self, cr, uid, vals, context=None):
439         obj_id = super(hr_applicant, self).create(cr, uid, vals, context=context)
440         self.create_send_note(cr, uid, [obj_id], context=context)
441         return obj_id
442
443     def case_open(self, cr, uid, ids, context=None):
444         """
445             open Request of the applicant for the hr_recruitment
446         """
447         res = super(hr_applicant, self).case_open(cr, uid, ids, context)
448         date = self.read(cr, uid, ids, ['date_open'])[0]
449         if not date['date_open']:
450             self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'),})
451         return res
452
453     def case_close(self, cr, uid, ids, context=None):
454         res = super(hr_applicant, self).case_close(cr, uid, ids, context)
455         return res
456
457     def case_close_with_emp(self, cr, uid, ids, context=None):
458         if context is None:
459             context = {}
460         hr_employee = self.pool.get('hr.employee')
461         model_data = self.pool.get('ir.model.data')
462         act_window = self.pool.get('ir.actions.act_window')
463         emp_id = False
464         for applicant in self.browse(cr, uid, ids, context=context):
465             address_id = False
466             if applicant.partner_id:
467                 address_id = applicant.partner_id.address_get(['contact'])['contact']
468             if applicant.job_id:
469                 applicant.job_id.write({'no_of_recruitment': applicant.job_id.no_of_recruitment - 1})
470                 emp_id = hr_employee.create(cr,uid,{'name': applicant.partner_name or applicant.name,
471                                                      'job_id': applicant.job_id.id,
472                                                      'address_home_id': address_id,
473                                                      'department_id': applicant.department_id.id
474                                                      })
475                 self.write(cr, uid, [applicant.id], {'emp_id': emp_id}, context=context)
476                 self.case_close(cr, uid, [applicant.id], context)
477             else:
478                 raise osv.except_osv(_('Warning!'),_('You must define Applied Job for this applicant.'))
479
480         action_model, action_id = model_data.get_object_reference(cr, uid, 'hr', 'open_view_employee_list')
481         dict_act_window = act_window.read(cr, uid, action_id, [])
482         if emp_id:
483             dict_act_window['res_id'] = emp_id
484         dict_act_window['view_mode'] = 'form,tree'
485         return dict_act_window
486
487     def case_cancel(self, cr, uid, ids, context=None):
488         """Overrides cancel for crm_case for setting probability
489         """
490         res = super(hr_applicant, self).case_cancel(cr, uid, ids, context)
491         self.write(cr, uid, ids, {'probability' : 0.0})
492         return res
493
494     def case_pending(self, cr, uid, ids, context=None):
495         """Marks case as pending"""
496         res = super(hr_applicant, self).case_pending(cr, uid, ids, context)
497         self.write(cr, uid, ids, {'probability' : 0.0})
498         return res
499
500     def case_reset(self, cr, uid, ids, context=None):
501         """Resets case as draft
502         """
503         res = super(hr_applicant, self).case_reset(cr, uid, ids, context)
504         self.write(cr, uid, ids, {'date_open': False, 'date_closed': False})
505         return res
506
507     def set_priority(self, cr, uid, ids, priority, *args):
508         """Set applicant priority
509         """
510         return self.write(cr, uid, ids, {'priority' : priority})
511
512     def set_high_priority(self, cr, uid, ids, *args):
513         """Set applicant priority to high
514         """
515         return self.set_priority(cr, uid, ids, '1')
516
517     def set_normal_priority(self, cr, uid, ids, *args):
518         """Set applicant priority to normal
519         """
520         return self.set_priority(cr, uid, ids, '3')
521
522     def write(self, cr, uid, ids, vals, context=None):
523         if 'stage_id' in vals and vals['stage_id']:
524             stage = self.pool.get('hr.recruitment.stage').browse(cr, uid, vals['stage_id'], context=context)
525             self.message_append_note(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % stage.name, context=context)
526         return super(hr_applicant,self).write(cr, uid, ids, vals, context=context)
527
528     # -------------------------------------------------------
529     # OpenChatter methods and notifications
530     # -------------------------------------------------------
531     
532     def message_get_subscribers(self, cr, uid, ids, context=None):
533         sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
534         for obj in self.browse(cr, uid, ids, context=context):
535             if obj.user_id:
536                 sub_ids.append(obj.user_id.id)
537         return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
538
539     def get_needaction_user_ids(self, cr, uid, ids, context=None):
540         result = dict.fromkeys(ids, [])
541         for obj in self.browse(cr, uid, ids, context=context):
542             if obj.state == 'draft' and obj.user_id:
543                 result[obj.id] = [obj.user_id.id]
544         return result
545     
546     def case_get_note_msg_prefix(self, cr, uid, id, context=None):
547                 return 'Applicant'
548
549     def case_open_send_note(self, cr, uid, ids, context=None):
550         message = _("Applicant has been set <b>in progress</b>.")
551         return self.message_append_note(cr, uid, ids, body=message, context=context)
552
553     def case_close_send_note(self, cr, uid, ids, context=None):
554         if context is None:
555             context = {}
556         for applicant in self.browse(cr, uid, ids, context=context):
557             if applicant.emp_id:
558                 message = _("Applicant has been <b>hired</b> and created as an employee.")
559                 self.message_append_note(cr, uid, [applicant.id], body=message, context=context)
560             else:
561                 message = _("Applicant has been <b>hired</b>.")
562                 self.message_append_note(cr, uid, [applicant.id], body=message, context=context)
563         return True
564
565     def case_cancel_send_note(self, cr, uid, ids, context=None):
566         msg = 'Applicant <b>refused</b>.'
567         return self.message_append_note(cr, uid, ids, body=msg, context=context)
568
569     def case_reset_send_note(self,  cr, uid, ids, context=None):
570         message =_("Applicant has been set as <b>new</b>.")
571         return self.message_append_note(cr, uid, ids, body=message, context=context)
572
573     def create_send_note(self, cr, uid, ids, context=None):
574         message = _("Applicant has been <b>created</b>.")
575         return self.message_append_note(cr, uid, ids, body=message, context=context)
576
577
578 class hr_job(osv.osv):
579     _inherit = "hr.job"
580     _name = "hr.job"
581     _columns = {
582         '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"),
583     }
584
585
586 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: