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