[IMP]:removed duplicate fields from hr.employee module, put resource_id also while...
[odoo/odoo.git] / addons / hr_holidays / hr.py
1 # -*- coding: utf-8 -*-
2 ##################################################################################
3 #
4 # Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
5 # and 2004-2009 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 from mx import DateTime
25 import time
26 import pooler
27 import netsvc
28 import datetime
29 from osv import fields, osv
30 from tools.translate import _
31
32 class hr_holidays_status(osv.osv):
33     _name = "hr.holidays.status"
34     _description = "Leave Types"
35     def get_days(self, cr, uid, ids, employee_id, return_false, context={}):
36         res = {}
37         for record in self.browse(cr, uid, ids, context):
38             res[record.id] = {}
39             max_leaves = leaves_taken = 0
40             if not return_false:
41                 cr.execute("""SELECT type, sum(number_of_days) FROM hr_holidays WHERE employee_id = %s AND state='validate' AND holiday_status_id = %s GROUP BY type""", (str(employee_id), str(record.id)))
42                 for line in cr.fetchall():
43                     if line[0] =='remove':
44                         leaves_taken = -line[1]
45                     if line[0] =='add':
46                         max_leaves = line[1]
47             res[record.id]['max_leaves'] = max_leaves
48             res[record.id]['leaves_taken'] = leaves_taken
49             res[record.id]['remaining_leaves'] = max_leaves - leaves_taken
50         return res
51
52     def _user_left_days(self, cr, uid, ids, name, args, context={}):
53         res = {}
54         return_false = False
55         if context and context.has_key('employee_id'):
56             if not context['employee_id']:
57                 return_false = True
58             employee_id = context['employee_id']
59         else:
60             employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)])
61             if employee_ids:
62                 employee_id = employee_ids[0]
63             else:
64                 return_false = True
65         res = self.get_days(cr, uid, ids, employee_id, return_false, context=context)
66         return res
67
68     _columns = {
69         'name' : fields.char('Name', size=64, required=True, translate=True),
70         'section_id': fields.many2one('crm.case.section', 'CRM Section', help='If you link this type of leave with a section in the CRM, it will synchronize each leave asked with a case in this section, to display it in the company shared calendar for example.'),
71         'color_name' : fields.selection([('red', 'Red'), ('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 of the status', required=True, help='This color will be used in the leaves summary located in Reporting\Print Summary of Leaves'),
72         'limit' : fields.boolean('Allow to override Limit', help='If you thick this checkbox, the system will allow, for this section, the employees to take more leaves than the available ones.'),
73         'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the leave type without removing it."),
74         'max_leaves' : fields.function(_user_left_days, method=True, string='Maximum Leaves Allowed', help='This value is given by the sum of all holidays requests with a positive value.', multi='user_left_days'),
75         'leaves_taken' : fields.function(_user_left_days, method=True, string='Leaves Already Taken', help='This value is given by the sum of all holidays requests with a negative value.', multi='user_left_days'),
76         'remaining_leaves' : fields.function(_user_left_days, method=True, string='Remaining Leaves', multi='user_left_days'),
77
78     }
79     _defaults = {
80         'color_name': lambda *args: 'red',
81         'active' : lambda *a: True,
82     }
83 hr_holidays_status()
84
85 class hr_holidays_per_user(osv.osv):
86     _name = "hr.holidays.per.user"
87     _description = "Holidays Per User"
88     _rec_name = "user_id"
89
90     def _get_remaining_leaves(self, cr, uid, ids, field_name, arg=None, context={}):
91         obj_holiday = self.pool.get('hr.holidays')
92         result = {}
93         for holiday_user in self.browse(cr, uid, ids):
94             days = 0.0
95             ids_request = obj_holiday.search(cr, uid, [('employee_id', '=', holiday_user.employee_id.id),('state', '=', 'validate'),('holiday_status', '=', holiday_user.holiday_status.id)])
96             if ids_request:
97                 holidays = obj_holiday.browse(cr, uid, ids_request)
98                 for holiday in holidays:
99                     days += holiday.number_of_days
100             days = holiday_user.max_leaves - days
101             result[holiday_user.id] = days
102         return result
103
104     _columns = {
105         'employee_id': fields.many2one('hr.employee', "Employee's Name",required=True),
106         'user_id' : fields.many2one('res.users','User'),
107         'holiday_status' : fields.many2one("hr.holidays.status", "Holiday's Status", required=True),
108         'max_leaves' : fields.float('Maximum Leaves Allowed',required=True),
109         'leaves_taken' : fields.float('Leaves Already Taken',readonly=True),
110         'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the holidays per user without removing it."),
111         'notes' : fields.text('Notes'),
112         'remaining_leaves': fields.function(_get_remaining_leaves, method=True, string='Remaining Leaves', type='float'),
113         'holiday_ids': fields.one2many('hr.holidays', 'holiday_user_id', 'Holidays')
114     }
115     _defaults = {
116         'active' : lambda *a: True,
117     }
118
119     def create(self, cr, uid, vals, *args, **kwargs):
120         if vals['employee_id']:
121             obj_emp=self.pool.get('hr.employee').browse(cr,uid,vals['employee_id'])
122             vals.update({'user_id': obj_emp.user_id.id})
123         return super(osv.osv,self).create(cr, uid, vals, *args, **kwargs)
124
125 hr_holidays_per_user()
126
127 class hr_holidays(osv.osv):
128     _name = "hr.holidays"
129     _description = "Holidays"
130     _order = "type desc, date_from asc"
131
132     def _employee_get(obj,cr,uid,context={}):
133         ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
134         if ids:
135             return ids[0]
136         return False
137
138     _columns = {
139         'name' : fields.char('Description', required=True, readonly=True, size=64, states={'draft':[('readonly',False)]}),
140         'state': fields.selection([('draft', 'Draft'), ('confirm', 'Waiting Validation'), ('refuse', 'Refused'), ('validate', 'Validated'), ('cancel', 'Cancelled')], 'State', readonly=True, help='When the holiday request is created the state is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the state is \'Waiting Validation\'.\
141             If the admin accepts it, the state is \'Validated\'. If it is refused, the state is \'Refused\'.'),
142         'date_from' : fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)]}),
143         'user_id':fields.many2one('res.users', 'User', states={'draft':[('readonly',False)]}, select=True, readonly=True),
144         'date_to' : fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)]}),
145         'holiday_status_id' : fields.many2one("hr.holidays.status", "Leave Type", required=True,readonly=True, states={'draft':[('readonly',False)]}),
146         'employee_id' : fields.many2one('hr.employee', "Employee's Name", select=True, invisible=False, readonly=True, states={'draft':[('readonly',False)]}, help='Leave Manager can let this field empty if this leave request/allocation is for every employee'),
147         'manager_id' : fields.many2one('hr.employee', 'Leave Manager', invisible=False, readonly=True, help='This area is automaticly filled by the user who validate the leave'),
148         'notes' : fields.text('Notes',readonly=True, states={'draft':[('readonly',False)]}),
149         'number_of_days': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)]}),
150         'number_of_days_temp': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)]}),
151         'case_id': fields.many2one('crm.case', 'Case'),
152         'type': fields.selection([('remove','Leave Request'),('add','Allocation Request')], 'Request Type', required=True, readonly=True, states={'draft':[('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"),
153         'allocation_type': fields.selection([('employee','Employee Request'),('company','Company Allocation')], 'Allocation Type', required=True, readonly=True, states={'draft':[('readonly',False)]}, help='This field is only for informative purposes, to depict if the leave request/allocation comes from an employee or from the company'),
154         'parent_id': fields.many2one('hr.holidays', 'Parent'),
155         'linked_request_ids': fields.one2many('hr.holidays', 'parent_id', 'Linked Requests',),
156     }
157
158     _defaults = {
159         'employee_id' : _employee_get ,
160         'state' : lambda *a: 'draft',
161         'type': lambda *a: 'remove',
162         'allocation_type': lambda *a: 'employee',
163         'user_id': lambda obj, cr, uid, context: uid,
164     }
165     _order = 'date_from desc'
166
167     def create(self, cr, uid, vals, context={}):
168         print 'create', vals, context
169         if context:
170             if context.has_key('type'):
171                 vals['type'] = context['type']
172             if context.has_key('allocation_type'):
173                 vals['allocation_type'] = context['allocation_type']
174         return super(osv.osv,self).create(cr, uid, vals, context)
175
176     #~ def _check_date(self, cr, uid, ids):
177         #~ if ids:
178              #~ cr.execute('select number_of_days from hr_holidays where id in ('+','.join(map(str, ids))+')')
179              #~ res = cr.fetchall()
180              #~ if res and res[0][0] <= 0:
181                  #~ return False
182         #~ return True
183
184     #_constraints = [(_check_date, 'Start date should not be greater than end date! ', ['number_of_days'])]
185
186     def onchange_date_from(self, cr, uid, ids, date_to, date_from):
187         result = {}
188         if date_to and date_from:
189             from_dt = time.mktime(time.strptime(date_from,'%Y-%m-%d %H:%M:%S'))
190             to_dt = time.mktime(time.strptime(date_to,'%Y-%m-%d %H:%M:%S'))
191             diff_day = (to_dt-from_dt)/(3600*24)
192             result['value'] = {
193                 'number_of_days_temp': round(diff_day)+1
194             }
195             return result
196         result['value'] = {
197             'number_of_days_temp': 0,
198         }
199         return result
200
201     def onchange_date_to(self, cr, uid, ids, date_from, date_to):
202         result = {}
203         if date_from and date_to:
204             from_dt = time.mktime(time.strptime(date_from,'%Y-%m-%d %H:%M:%S'))
205             to_dt = time.mktime(time.strptime(date_to,'%Y-%m-%d %H:%M:%S'))
206             diff_day = (to_dt-from_dt)/(3600*24)
207             result['value'] = {
208                 'number_of_days_temp': round(diff_day)+1
209             }
210             return result
211         result['value'] = {
212             'number_of_days_temp': 0
213         }
214         return result
215
216     def onchange_sec_id(self, cr, uid, ids, status, context={}):
217         warning = {}
218         if status:
219             brows_obj = self.pool.get('hr.holidays.status').browse(cr, uid, [status])[0]
220             if brows_obj.section_id and not brows_obj.section_id.allow_unlink:
221                 warning = {
222                     'title': "Warning for ",
223                     'message': "You won\'t be able to cancel this leave request because the CRM Section of the leave type disallows."
224                         }
225         return {'warning': warning}
226
227
228     def set_to_draft(self, cr, uid, ids, *args):
229         self.write(cr, uid, ids, {
230             'state':'draft',
231             'manager_id': False,
232             'number_of_days': 0,
233         })
234         wf_service = netsvc.LocalService("workflow")
235         for holiday_id in ids:
236             wf_service.trg_create(uid, 'hr.holidays', holiday_id, cr)
237         return True
238
239     def holidays_validate(self, cr, uid, ids, *args):
240         self.check_holidays(cr,uid,ids)
241         vals = {
242             'state':'validate',
243         }
244         ids2 = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
245         if ids2:
246             vals['manager_id'] = ids2[0]
247         else:
248             raise osv.except_osv(_('Warning !'),_('Either there is no Employee defined, or no User attached with it.'))
249         self.write(cr, uid, ids, vals)
250         return True
251
252     def holidays_confirm(self, cr, uid, ids, *args):
253         for record in self.browse(cr, uid, ids):
254             leave_asked = record.number_of_days_temp
255             if record.type == 'remove':
256                 if record.employee_id and not record.holiday_status_id.limit:
257                     leaves_rest = self.pool.get('hr.holidays.status').get_days( cr, uid, [record.holiday_status_id.id], record.employee_id.id, False)[record.holiday_status_id.id]['remaining_leaves']
258                     if leaves_rest < leave_asked:
259                         raise osv.except_osv(_('Warning!'),_('You cannot validate leaves for %s while available leaves are less than asked leaves.' %(record.employee_id.name)))
260                 nb = -(record.number_of_days_temp)
261             else:
262                 nb = record.number_of_days_temp
263             if record.employee_id:
264                 user_id = record.employee_id.user_id and record.employee_id.user_id.id or uid
265
266             self.write(cr, uid, [record.id], {
267                 'state':'confirm',
268                 'number_of_days': nb,
269                 'user_id': user_id
270             })
271             vals= {
272                    'name':record.name,
273                    'date_from':record.date_from,
274                    'date_to':record.date_to,
275                    'calendar_id':record.employee_id.calendar_id.id,
276                    'company_id':record.employee_id.company_id.id,
277                    'resource_id':record.employee_id.resource_id.id
278                  }
279             self.pool.get('resource.calendar.leaves').create(cr,uid,vals)
280
281         return True
282
283     def holidays_refuse(self, cr, uid, ids, *args):
284         vals = {
285             'state':'refuse',
286         }
287         ids2 = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
288         if ids2:
289             vals['manager_id'] = ids2[0]
290         self.write(cr, uid, ids, vals)
291         return True
292
293     def holidays_cancel(self, cr, uid, ids, *args):
294         for record in self.browse(cr, uid, ids):
295             if record.state=='validate':
296                 if record.case_id:
297                     self.pool.get('crm.case').unlink(cr,uid,[record.case_id.id])
298                 if record.linked_request_ids:
299                     list_ids = []
300                     for id in record.linked_request_ids:
301                         list_ids.append(id.id)
302                     self.holidays_cancel(cr,uid,list_ids)
303                     self.unlink(cr,uid,list_ids)
304         self.write(cr, uid, ids, {
305             'state':'cancel'
306             })
307         return True
308
309     def holidays_draft(self, cr, uid, ids, *args):
310         self.write(cr, uid, ids, {
311             'state':'draft'
312         })
313         return True
314
315     def check_holidays(self,cr,uid,ids):
316         for record in self.browse(cr, uid, ids):
317             if not record.number_of_days:
318                     raise osv.except_osv(_('Warning!'),_('Wrong leave definition.'))
319             if record.employee_id:
320                 leave_asked = record.number_of_days
321                 if leave_asked < 0.00:
322                     if not record.holiday_status_id.limit:
323                         leaves_rest = self.pool.get('hr.holidays.status').get_days( cr, uid, [record.holiday_status_id.id], record.employee_id.id, False)[record.holiday_status_id.id]['remaining_leaves']
324
325                         if leaves_rest < -(leave_asked):
326                             raise osv.except_osv(_('Warning!'),_('You Cannot Validate leaves while available leaves are less than asked leaves.'))
327             else:
328                 holiday_ids = []
329                 vals = {
330                     'name' : record.name,
331                     'holiday_status_id' : record.holiday_status_id.id,
332                     'state': 'draft',
333                     'date_from' : record.date_from,
334                     'date_to' : record.date_to,
335                     'notes' : record.notes,
336                     'number_of_days': record.number_of_days,
337                     'number_of_days_temp': record.number_of_days_temp,
338                     'type': record.type,
339                     'allocation_type': record.allocation_type,
340                     'parent_id': record.id,
341                 }
342                 employee_ids = self.pool.get('hr.employee').search(cr, uid, [])
343                 for employee in employee_ids:
344                     vals['employee_id'] = employee
345                     holiday_ids.append(self.create(cr, uid, vals, context={}))
346                 self.holidays_confirm(cr, uid, holiday_ids)
347                 self.holidays_validate(cr, uid, holiday_ids)
348
349             if record.holiday_status_id.section_id and record.date_from and record.date_to and record.employee_id:
350                 vals={}
351                 vals['name']=record.name
352                 vals['section_id']=record.holiday_status_id.section_id.id
353                 epoch_c = time.mktime(time.strptime(record.date_to,'%Y-%m-%d %H:%M:%S'))
354                 epoch_d = time.mktime(time.strptime(record.date_from,'%Y-%m-%d %H:%M:%S'))
355                 diff_day = (epoch_c - epoch_d)/(3600*24)
356                 vals['duration'] = (diff_day) * 8
357                 vals['note'] = record.notes
358                 vals['user_id'] = record.user_id.id
359                 vals['date'] = record.date_from
360                 case_id = self.pool.get('crm.case').create(cr,uid,vals)
361                 self.write(cr, uid, ids, {'case_id':case_id})
362         return True
363 hr_holidays()
364