[REF] removed explicit model instanciations.
[odoo/odoo.git] / addons / hr_holidays / hr_holidays.py
1 # -*- coding: utf-8 -*-
2 ##################################################################################
3 #
4 # Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
5 # and 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 # $Id: hr.py 4656 2006-11-24 09:58:42Z Cyp $
8 #
9 #     This program is free software: you can redistribute it and/or modify
10 #     it under the terms of the GNU Affero General Public License as
11 #     published by the Free Software Foundation, either version 3 of the
12 #     License, or (at your option) any later version.
13 #
14 #     This program is distributed in the hope that it will be useful,
15 #     but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 #     GNU Affero General Public License for more details.
18 #
19 #     You should have received a copy of the GNU Affero General Public License
20 #     along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22 ##############################################################################
23
24 import datetime
25 import time
26 from itertools import groupby
27 from operator import attrgetter, itemgetter
28
29 import math
30 from openerp import tools
31 from openerp.osv import fields, osv
32 from openerp.tools.translate import _
33
34
35 class hr_holidays_status(osv.osv):
36     _name = "hr.holidays.status"
37     _description = "Leave Type"
38
39     def get_days(self, cr, uid, ids, employee_id, return_false, context=None):
40         cr.execute("""SELECT id, type, number_of_days, holiday_status_id FROM hr_holidays WHERE employee_id = %s AND state='validate' AND holiday_status_id in %s""",
41             [employee_id, tuple(ids)])
42         result = sorted(cr.dictfetchall(), key=lambda x: x['holiday_status_id'])
43         grouped_lines = dict((k, [v for v in itr]) for k, itr in groupby(result, itemgetter('holiday_status_id')))
44         res = {}
45         for record in self.browse(cr, uid, ids, context=context):
46             res[record.id] = {}
47             max_leaves = leaves_taken = 0
48             if not return_false:
49                 if record.id in grouped_lines:
50                     leaves_taken = -sum([item['number_of_days'] for item in grouped_lines[record.id] if item['type'] == 'remove'])
51                     max_leaves = sum([item['number_of_days'] for item in grouped_lines[record.id] if item['type'] == 'add'])
52             res[record.id]['max_leaves'] = max_leaves
53             res[record.id]['leaves_taken'] = leaves_taken
54             res[record.id]['remaining_leaves'] = max_leaves - leaves_taken
55         return res
56
57     def _user_left_days(self, cr, uid, ids, name, args, context=None):
58         return_false = False
59         employee_id = False
60         res = {}
61         if context and context.has_key('employee_id'):
62             if not context['employee_id']:
63                 return_false = True
64             employee_id = context['employee_id']
65         else:
66             employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)], context=context)
67             if employee_ids:
68                 employee_id = employee_ids[0]
69             else:
70                 return_false = True
71         if employee_id:
72             res = self.get_days(cr, uid, ids, employee_id, return_false, context=context)
73         else:
74             res = dict.fromkeys(ids, {'leaves_taken': 0, 'remaining_leaves': 0, 'max_leaves': 0})
75         return res
76
77     _columns = {
78         'name': fields.char('Leave Type', size=64, required=True, translate=True),
79         'categ_id': fields.many2one('crm.meeting.type', 'Meeting Type',
80             help='Once a leave is validated, OpenERP will create a corresponding meeting of this type in the calendar.'),
81         'color_name': fields.selection([('red', 'Red'),('blue','Blue'), ('lightgreen', 'Light Green'), ('lightblue','Light Blue'), ('lightyellow', 'Light Yellow'), ('magenta', 'Magenta'),('lightcyan', 'Light Cyan'),('black', 'Black'),('lightpink', 'Light Pink'),('brown', 'Brown'),('violet', 'Violet'),('lightcoral', 'Light Coral'),('lightsalmon', 'Light Salmon'),('lavender', 'Lavender'),('wheat', 'Wheat'),('ivory', 'Ivory')],'Color in Report', required=True, help='This color will be used in the leaves summary located in Reporting\Leaves by Department.'),
82         'limit': fields.boolean('Allow to Override Limit', help='If you select this check box, the system allows the employees to take more leaves than the available ones for this type and take them into account for the "Remaining Legal Leaves" defined on the employee form.'),
83         'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the leave type without removing it."),
84         'max_leaves': fields.function(_user_left_days, string='Maximum Allowed', help='This value is given by the sum of all holidays requests with a positive value.', multi='user_left_days'),
85         'leaves_taken': fields.function(_user_left_days, string='Leaves Already Taken', help='This value is given by the sum of all holidays requests with a negative value.', multi='user_left_days'),
86         'remaining_leaves': fields.function(_user_left_days, string='Remaining Leaves', help='Maximum Leaves Allowed - Leaves Already Taken', multi='user_left_days'),
87         'double_validation': fields.boolean('Apply Double Validation', help="When selected, the Allocation/Leave Requests for this type require a second validation to be approved."),
88     }
89     _defaults = {
90         'color_name': 'red',
91         'active': True,
92     }
93
94     def name_get(self, cr, uid, ids, context=None):
95         if not ids:
96             return []
97         res = []
98         for record in self.browse(cr, uid, ids, context=context):
99             name = record.name
100             if not record.limit:
101                 name = name + ('  (%d/%d)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
102             res.append((record.id, name))
103         return res
104
105
106 class hr_holidays(osv.osv):
107     _name = "hr.holidays"
108     _description = "Leave"
109     _order = "type desc, date_from asc"
110     _inherit = ['mail.thread', 'ir.needaction_mixin']
111     _track = {
112         'state': {
113             'hr_holidays.mt_holidays_approved': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'validate',
114             'hr_holidays.mt_holidays_refused': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'refuse',
115             'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm',
116         },
117     }
118
119     def _employee_get(self, cr, uid, context=None):        
120         emp_id = context.get('default_employee_id', False)
121         if emp_id:
122             return emp_id
123         ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)
124         if ids:
125             return ids[0]
126         return False
127
128     def _compute_number_of_days(self, cr, uid, ids, name, args, context=None):
129         result = {}
130         for hol in self.browse(cr, uid, ids, context=context):
131             if hol.type=='remove':
132                 result[hol.id] = -hol.number_of_days_temp
133             else:
134                 result[hol.id] = hol.number_of_days_temp
135         return result
136
137     def _check_date(self, cr, uid, ids):
138         for holiday in self.browse(cr, uid, ids):
139             holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from), ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id)])
140             if holiday_ids:
141                 return False
142         return True
143
144     _columns = {
145         'name': fields.char('Description', size=64),
146         'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')],
147             'Status', readonly=True, track_visibility='onchange',
148             help='The status is set to \'To Submit\', when a holiday request is created.\
149             \nThe status is \'To Approve\', when holiday request is confirmed by user.\
150             \nThe status is \'Refused\', when holiday request is refused by manager.\
151             \nThe status is \'Approved\', when holiday request is approved by manager.'),
152         'user_id':fields.related('employee_id', 'user_id', type='many2one', relation='res.users', string='User', store=True),
153         'date_from': fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True),
154         'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
155         'holiday_status_id': fields.many2one("hr.holidays.status", "Leave Type", required=True,readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
156         'employee_id': fields.many2one('hr.employee', "Employee", select=True, invisible=False, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
157         'manager_id': fields.many2one('hr.employee', 'First Approval', invisible=False, readonly=True, help='This area is automatically filled by the user who validate the leave'),
158         'notes': fields.text('Reasons',readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
159         'number_of_days_temp': fields.float('Allocation', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
160         'number_of_days': fields.function(_compute_number_of_days, string='Number of Days', store=True),
161         'meeting_id': fields.many2one('crm.meeting', 'Meeting'),
162         'type': fields.selection([('remove','Leave Request'),('add','Allocation Request')], 'Request Type', required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help="Choose 'Leave Request' if someone wants to take an off-day. \nChoose 'Allocation Request' if you want to increase the number of leaves available for someone", select=True),
163         'parent_id': fields.many2one('hr.holidays', 'Parent'),
164         'linked_request_ids': fields.one2many('hr.holidays', 'parent_id', 'Linked Requests',),
165         'department_id':fields.related('employee_id', 'department_id', string='Department', type='many2one', relation='hr.department', readonly=True, store=True),
166         'category_id': fields.many2one('hr.employee.category', "Employee Tag", help='Category of Employee', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
167         'holiday_type': fields.selection([('employee','By Employee'),('category','By Employee Tag')], 'Allocation Mode', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help='By Employee: Allocation/Request for individual Employee, By Employee Tag: Allocation/Request for group of employees in category', required=True),
168         'manager_id2': fields.many2one('hr.employee', 'Second Approval', readonly=True, help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)'),
169         'double_validation': fields.related('holiday_status_id', 'double_validation', type='boolean', relation='hr.holidays.status', string='Apply Double Validation'),
170     }
171     _defaults = {
172         'employee_id': _employee_get,
173         'state': 'draft',
174         'type': 'remove',
175         'user_id': lambda obj, cr, uid, context: uid,
176         'holiday_type': 'employee'
177     }
178     _constraints = [
179         (_check_date, 'You can not have 2 leaves that overlaps on same day!', ['date_from','date_to']),
180     ] 
181     
182     _sql_constraints = [
183         ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "The employee or employee category of this request is missing."),
184         ('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be anterior to the end date."),
185         ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
186     ]
187     
188     def copy(self, cr, uid, id, default=None, context=None):
189         if default is None:
190             default = {}
191         if context is None:
192             context = {}
193         default = default.copy()
194         default['date_from'] = False
195         default['date_to'] = False
196         return super(hr_holidays, self).copy(cr, uid, id, default, context=context)
197
198     def _create_resource_leave(self, cr, uid, leaves, context=None):
199         '''This method will create entry in resource calendar leave object at the time of holidays validated '''
200         obj_res_leave = self.pool.get('resource.calendar.leaves')
201         for leave in leaves:
202             vals = {
203                 'name': leave.name,
204                 'date_from': leave.date_from,
205                 'holiday_id': leave.id,
206                 'date_to': leave.date_to,
207                 'resource_id': leave.employee_id.resource_id.id,
208                 'calendar_id': leave.employee_id.resource_id.calendar_id.id
209             }
210             obj_res_leave.create(cr, uid, vals, context=context)
211         return True
212
213     def _remove_resource_leave(self, cr, uid, ids, context=None):
214         '''This method will create entry in resource calendar leave object at the time of holidays cancel/removed'''
215         obj_res_leave = self.pool.get('resource.calendar.leaves')
216         leave_ids = obj_res_leave.search(cr, uid, [('holiday_id', 'in', ids)], context=context)
217         return obj_res_leave.unlink(cr, uid, leave_ids, context=context)
218
219     def onchange_type(self, cr, uid, ids, holiday_type, employee_id=False, context=None):
220         result = {}
221         if holiday_type == 'employee' and not employee_id:
222             ids_employee = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
223             if ids_employee:
224                 result['value'] = {
225                     'employee_id': ids_employee[0]
226                 }
227         return result
228
229     def onchange_employee(self, cr, uid, ids, employee_id):
230         result = {'value': {'department_id': False}}
231         if employee_id:
232             employee = self.pool.get('hr.employee').browse(cr, uid, employee_id)
233             result['value'] = {'department_id': employee.department_id.id}
234         return result
235
236     # TODO: can be improved using resource calendar method
237     def _get_number_of_days(self, date_from, date_to):
238         """Returns a float equals to the timedelta between two dates given as string."""
239
240         DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
241         from_dt = datetime.datetime.strptime(date_from, DATETIME_FORMAT)
242         to_dt = datetime.datetime.strptime(date_to, DATETIME_FORMAT)
243         timedelta = to_dt - from_dt
244         diff_day = timedelta.days + float(timedelta.seconds) / 86400
245         return diff_day
246
247     def unlink(self, cr, uid, ids, context=None):
248         for rec in self.browse(cr, uid, ids, context=context):
249             if rec.state not in ['draft', 'cancel', 'confirm']:
250                 raise osv.except_osv(_('Warning!'),_('You cannot delete a leave which is in %s state.')%(rec.state))
251         return super(hr_holidays, self).unlink(cr, uid, ids, context)
252
253     def onchange_date_from(self, cr, uid, ids, date_to, date_from):
254         """
255         If there are no date set for date_to, automatically set one 8 hours later than
256         the date_from.
257         Also update the number_of_days.
258         """
259         # date_to has to be greater than date_from
260         if (date_from and date_to) and (date_from > date_to):
261             raise osv.except_osv(_('Warning!'),_('The start date must be anterior to the end date.'))
262
263         result = {'value': {}}
264
265         # No date_to set so far: automatically compute one 8 hours later
266         if date_from and not date_to:
267             date_to_with_delta = datetime.datetime.strptime(date_from, tools.DEFAULT_SERVER_DATETIME_FORMAT) + datetime.timedelta(hours=8)
268             result['value']['date_to'] = str(date_to_with_delta)
269
270         # Compute and update the number of days
271         if (date_to and date_from) and (date_from <= date_to):
272             diff_day = self._get_number_of_days(date_from, date_to)
273             result['value']['number_of_days_temp'] = round(math.floor(diff_day))+1
274         else:
275             result['value']['number_of_days_temp'] = 0
276
277         return result
278
279     def onchange_date_to(self, cr, uid, ids, date_to, date_from):
280         """
281         Update the number_of_days.
282         """
283
284         # date_to has to be greater than date_from
285         if (date_from and date_to) and (date_from > date_to):
286             raise osv.except_osv(_('Warning!'),_('The start date must be anterior to the end date.'))
287
288         result = {'value': {}}
289
290         # Compute and update the number of days
291         if (date_to and date_from) and (date_from <= date_to):
292             diff_day = self._get_number_of_days(date_from, date_to)
293             result['value']['number_of_days_temp'] = round(math.floor(diff_day))+1
294         else:
295             result['value']['number_of_days_temp'] = 0
296
297         return result
298
299     def create(self, cr, uid, values, context=None):
300         """ Override to avoid automatic logging of creation """
301         if context is None:
302             context = {}
303         context = dict(context, mail_create_nolog=True)
304         return super(hr_holidays, self).create(cr, uid, values, context=context)
305
306     def write(self, cr, uid, ids, vals, context=None):
307         check_fnct = self.pool.get('hr.holidays.status').check_access_rights
308         for  holiday in self.browse(cr, uid, ids, context=context):
309             if holiday.state in ('validate','validate1') and not check_fnct(cr, uid, 'write', raise_exception=False):
310                 raise osv.except_osv(_('Warning!'),_('You cannot modify a leave request that has been approved. Contact a human resource manager.'))
311         return super(hr_holidays, self).write(cr, uid, ids, vals, context=context)
312
313     def set_to_draft(self, cr, uid, ids, context=None):
314         self.write(cr, uid, ids, {
315             'state': 'draft',
316             'manager_id': False,
317             'manager_id2': False,
318         })
319         self.delete_workflow(cr, uid, ids)
320         self.create_workflow(cr, uid, ids)
321         to_unlink = []
322         for record in self.browse(cr, uid, ids, context=context):
323             for record2 in record.linked_request_ids:
324                 self.set_to_draft(cr, uid, [record2.id], context=context)
325                 to_unlink.append(record2.id)
326         if to_unlink:
327             self.unlink(cr, uid, to_unlink, context=context)
328         return True
329
330     def holidays_first_validate(self, cr, uid, ids, context=None):
331         self.check_holidays(cr, uid, ids, context=context)
332         obj_emp = self.pool.get('hr.employee')
333         ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
334         manager = ids2 and ids2[0] or False
335         self.holidays_first_validate_notificate(cr, uid, ids, context=context)
336         return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager})
337
338     def holidays_validate(self, cr, uid, ids, context=None):
339         self.check_holidays(cr, uid, ids, context=context)
340         obj_emp = self.pool.get('hr.employee')
341         ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
342         manager = ids2 and ids2[0] or False
343         self.write(cr, uid, ids, {'state':'validate'})
344         data_holiday = self.browse(cr, uid, ids)
345         for record in data_holiday:
346             if record.double_validation:
347                 self.write(cr, uid, [record.id], {'manager_id2': manager})
348             else:
349                 self.write(cr, uid, [record.id], {'manager_id': manager})
350             if record.holiday_type == 'employee' and record.type == 'remove':
351                 meeting_obj = self.pool.get('crm.meeting')
352                 meeting_vals = {
353                     'name': record.name or _('Leave Request'),
354                     'categ_ids': record.holiday_status_id.categ_id and [(6,0,[record.holiday_status_id.categ_id.id])] or [],
355                     'duration': record.number_of_days_temp * 8,
356                     'description': record.notes,
357                     'user_id': record.user_id.id,
358                     'date': record.date_from,
359                     'end_date': record.date_to,
360                     'date_deadline': record.date_to,
361                     'state': 'open',            # to block that meeting date in the calendar
362                 }
363                 meeting_id = meeting_obj.create(cr, uid, meeting_vals)
364                 self._create_resource_leave(cr, uid, [record], context=context)
365                 self.write(cr, uid, ids, {'meeting_id': meeting_id})
366             elif record.holiday_type == 'category':
367                 emp_ids = obj_emp.search(cr, uid, [('category_ids', 'child_of', [record.category_id.id])])
368                 leave_ids = []
369                 for emp in obj_emp.browse(cr, uid, emp_ids):
370                     vals = {
371                         'name': record.name,
372                         'type': record.type,
373                         'holiday_type': 'employee',
374                         'holiday_status_id': record.holiday_status_id.id,
375                         'date_from': record.date_from,
376                         'date_to': record.date_to,
377                         'notes': record.notes,
378                         'number_of_days_temp': record.number_of_days_temp,
379                         'parent_id': record.id,
380                         'employee_id': emp.id
381                     }
382                     leave_ids.append(self.create(cr, uid, vals, context=None))
383                 for leave_id in leave_ids:
384                     # TODO is it necessary to interleave the calls?
385                     self.signal_confirm(cr, uid, [leave_id])
386                     self.signal_validate(cr, uid, [leave_id])
387                     self.signal_second_validate(cr, uid, [leave_id])
388         return True
389
390     def holidays_confirm(self, cr, uid, ids, context=None):
391         self.check_holidays(cr, uid, ids, context=context)
392         for record in self.browse(cr, uid, ids, context=context):
393             if record.employee_id and record.employee_id.parent_id and record.employee_id.parent_id.user_id:
394                 self.message_subscribe_users(cr, uid, [record.id], user_ids=[record.employee_id.parent_id.user_id.id], context=context)
395         return self.write(cr, uid, ids, {'state': 'confirm'})
396
397     def holidays_refuse(self, cr, uid, ids, context=None):
398         obj_emp = self.pool.get('hr.employee')
399         ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
400         manager = ids2 and ids2[0] or False
401         for holiday in self.browse(cr, uid, ids, context=context):
402             if holiday.state == 'validate1':
403                 self.write(cr, uid, [holiday.id], {'state': 'refuse', 'manager_id': manager})
404             else:
405                 self.write(cr, uid, [holiday.id], {'state': 'refuse', 'manager_id2': manager})
406         self.holidays_cancel(cr, uid, ids, context=context)
407         return True
408
409     def holidays_cancel(self, cr, uid, ids, context=None):
410         meeting_obj = self.pool.get('crm.meeting')
411         for record in self.browse(cr, uid, ids):
412             # Delete the meeting
413             if record.meeting_id:
414                 meeting_obj.unlink(cr, uid, [record.meeting_id.id])
415
416             # If a category that created several holidays, cancel all related
417             self.signal_refuse(cr, uid, map(attrgetter('id'), record.linked_request_ids or []))
418
419         self._remove_resource_leave(cr, uid, ids, context=context)
420         return True
421
422     def check_holidays(self, cr, uid, ids, context=None):
423         holi_status_obj = self.pool.get('hr.holidays.status')
424         for record in self.browse(cr, uid, ids):
425             if record.holiday_type == 'employee' and record.type == 'remove':
426                 if record.employee_id and not record.holiday_status_id.limit:
427                     leaves_rest = holi_status_obj.get_days( cr, uid, [record.holiday_status_id.id], record.employee_id.id, False)[record.holiday_status_id.id]['remaining_leaves']
428                     if leaves_rest < record.number_of_days_temp:
429                         raise osv.except_osv(_('Warning!'), _('There are not enough %s allocated for employee %s; please create an allocation request for this leave type.') % (record.holiday_status_id.name, record.employee_id.name))
430         return True
431
432     # -----------------------------
433     # OpenChatter and notifications
434     # -----------------------------
435
436     def _needaction_domain_get(self, cr, uid, context=None):
437         emp_obj = self.pool.get('hr.employee')
438         empids = emp_obj.search(cr, uid, [('parent_id.user_id', '=', uid)], context=context)
439         dom = ['&', ('state', '=', 'confirm'), ('employee_id', 'in', empids)]
440         # if this user is a hr.manager, he should do second validations
441         if self.pool.get('res.users').has_group(cr, uid, 'base.group_hr_manager'):
442             dom = ['|'] + dom + [('state', '=', 'validate1')]
443         return dom
444
445     def holidays_first_validate_notificate(self, cr, uid, ids, context=None):
446         for obj in self.browse(cr, uid, ids, context=context):
447             self.message_post(cr, uid, [obj.id],
448                 _("Request approved, waiting second validation."), context=context)
449
450 class resource_calendar_leaves(osv.osv):
451     _inherit = "resource.calendar.leaves"
452     _description = "Leave Detail"
453     _columns = {
454         'holiday_id': fields.many2one("hr.holidays", "Leave Request"),
455     }
456
457
458
459 class hr_employee(osv.osv):
460     _inherit="hr.employee"
461
462     def create(self, cr, uid, vals, context=None):
463         # don't pass the value of remaining leave if it's 0 at the creation time, otherwise it will trigger the inverse
464         # function _set_remaining_days and the system may not be configured for. Note that we don't have this problem on
465         # the write because the clients only send the fields that have been modified.
466         if 'remaining_leaves' in vals and not vals['remaining_leaves']:
467             del(vals['remaining_leaves'])
468         return super(hr_employee, self).create(cr, uid, vals, context=context)
469
470     def _set_remaining_days(self, cr, uid, empl_id, name, value, arg, context=None):
471         employee = self.browse(cr, uid, empl_id, context=context)
472         diff = value - employee.remaining_leaves
473         type_obj = self.pool.get('hr.holidays.status')
474         holiday_obj = self.pool.get('hr.holidays')
475         # Find for holidays status
476         status_ids = type_obj.search(cr, uid, [('limit', '=', False)], context=context)
477         if len(status_ids) != 1 :
478             raise osv.except_osv(_('Warning!'),_("The feature behind the field 'Remaining Legal Leaves' can only be used when there is only one leave type with the option 'Allow to Override Limit' unchecked. (%s Found). Otherwise, the update is ambiguous as we cannot decide on which leave type the update has to be done. \nYou may prefer to use the classic menus 'Leave Requests' and 'Allocation Requests' located in 'Human Resources \ Leaves' to manage the leave days of the employees if the configuration does not allow to use this field.") % (len(status_ids)))
479         status_id = status_ids and status_ids[0] or False
480         if not status_id:
481             return False
482         if diff > 0:
483             leave_id = holiday_obj.create(cr, uid, {'name': _('Allocation for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'add', 'holiday_type': 'employee', 'number_of_days_temp': diff}, context=context)
484         elif diff < 0:
485             leave_id = holiday_obj.create(cr, uid, {'name': _('Leave Request for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'remove', 'holiday_type': 'employee', 'number_of_days_temp': abs(diff)}, context=context)
486         else:
487             return False
488         holiday_obj.signal_confirm(cr, uid, [leave_id])
489         holiday_obj.signal_validate(cr, uid, [leave_id])
490         holiday_obj.signal_second_validate(cr, uid, [leave_id])
491         return True
492
493     def _get_remaining_days(self, cr, uid, ids, name, args, context=None):
494         cr.execute("""SELECT
495                 sum(h.number_of_days) as days,
496                 h.employee_id
497             from
498                 hr_holidays h
499                 join hr_holidays_status s on (s.id=h.holiday_status_id)
500             where
501                 h.state='validate' and
502                 s.limit=False and
503                 h.employee_id in (%s)
504             group by h.employee_id"""% (','.join(map(str,ids)),) )
505         res = cr.dictfetchall()
506         remaining = {}
507         for r in res:
508             remaining[r['employee_id']] = r['days']
509         for employee_id in ids:
510             if not remaining.get(employee_id):
511                 remaining[employee_id] = 0.0
512         return remaining
513
514     def _get_leave_status(self, cr, uid, ids, name, args, context=None):
515         holidays_obj = self.pool.get('hr.holidays')
516         holidays_id = holidays_obj.search(cr, uid,
517            [('employee_id', 'in', ids), ('date_from','<=',time.strftime('%Y-%m-%d %H:%M:%S')),
518            ('date_to','>=',time.strftime('%Y-%m-%d 23:59:59')),('type','=','remove'),('state','not in',('cancel','refuse'))],
519            context=context)
520         result = {}
521         for id in ids:
522             result[id] = {
523                 'current_leave_state': False,
524                 'current_leave_id': False,
525                 'leave_date_from':False,
526                 'leave_date_to':False,
527             }
528         for holiday in self.pool.get('hr.holidays').browse(cr, uid, holidays_id, context=context):
529             result[holiday.employee_id.id]['leave_date_from'] = holiday.date_from
530             result[holiday.employee_id.id]['leave_date_to'] = holiday.date_to
531             result[holiday.employee_id.id]['current_leave_state'] = holiday.state
532             result[holiday.employee_id.id]['current_leave_id'] = holiday.holiday_status_id.id
533         return result
534
535     _columns = {
536         'remaining_leaves': fields.function(_get_remaining_days, string='Remaining Legal Leaves', fnct_inv=_set_remaining_days, type="float", help='Total number of legal leaves allocated to this employee, change this value to create allocation/leave request. Total based on all the leave types without overriding limit.'),
537         'current_leave_state': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Status", type="selection",
538             selection=[('draft', 'New'), ('confirm', 'Waiting Approval'), ('refuse', 'Refused'),
539             ('validate1', 'Waiting Second Approval'), ('validate', 'Approved'), ('cancel', 'Cancelled')]),
540         'current_leave_id': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Type",type='many2one', relation='hr.holidays.status'),
541         'leave_date_from': fields.function(_get_leave_status, multi='leave_status', type='date', string='From Date'),
542         'leave_date_to': fields.function(_get_leave_status, multi='leave_status', type='date', string='To Date'),
543     }
544
545
546 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: