1 # -*- coding: utf-8 -*-
2 ##################################################################################
4 # Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
5 # and 2004-2009 Tiny SPRL (<http://tiny.be>).
7 # $Id: hr.py 4656 2006-11-24 09:58:42Z Cyp $
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.
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.
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/>.
22 ##############################################################################
24 from mx import DateTime
29 from osv import fields, osv
30 from tools.translate import _
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={}):
37 for record in self.browse(cr, uid, ids, context):
39 max_leaves = leaves_taken = 0
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]
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
52 def _user_left_days(self, cr, uid, ids, name, args, context={}):
55 if context and context.has_key('employee_id'):
56 if not context['employee_id']:
58 employee_id = context['employee_id']
60 employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)])
62 employee_id = employee_ids[0]
65 res = self.get_days(cr, uid, ids, employee_id, return_false, context=context)
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'),
80 'color_name': lambda *args: 'red',
81 'active' : lambda *a: True,
85 class hr_holidays_per_user(osv.osv):
86 _name = "hr.holidays.per.user"
87 _description = "Holidays Per User"
90 def _get_remaining_leaves(self, cr, uid, ids, field_name, arg=None, context={}):
91 obj_holiday = self.pool.get('hr.holidays')
93 for holiday_user in self.browse(cr, uid, ids):
95 ids_request = obj_holiday.search(cr, uid, [('employee_id', '=', holiday_user.employee_id.id),('state', '=', 'validate'),('holiday_status', '=', holiday_user.holiday_status.id)])
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
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')
116 'active' : lambda *a: True,
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)
125 hr_holidays_per_user()
127 class hr_holidays(osv.osv):
128 _name = "hr.holidays"
129 _description = "Holidays"
130 _order = "type desc, date_from asc"
132 def _employee_get(obj,cr,uid,context={}):
133 ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
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',),
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,
165 _order = 'date_from desc'
167 def create(self, cr, uid, vals, context={}):
168 print 'create', vals, 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)
176 #~ def _check_date(self, cr, uid, 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:
184 #_constraints = [(_check_date, 'Start date should not be greater than end date! ', ['number_of_days'])]
186 def onchange_date_from(self, cr, uid, ids, date_to, date_from):
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)
193 'number_of_days_temp': round(diff_day)+1
197 'number_of_days_temp': 0,
201 def onchange_date_to(self, cr, uid, ids, date_from, date_to):
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)
208 'number_of_days_temp': round(diff_day)+1
212 'number_of_days_temp': 0
216 def onchange_sec_id(self, cr, uid, ids, status, context={}):
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:
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."
225 return {'warning': warning}
228 def set_to_draft(self, cr, uid, ids, *args):
229 self.write(cr, uid, ids, {
234 wf_service = netsvc.LocalService("workflow")
235 for holiday_id in ids:
236 wf_service.trg_create(uid, 'hr.holidays', holiday_id, cr)
239 def holidays_validate(self, cr, uid, ids, *args):
240 self.check_holidays(cr,uid,ids)
244 ids2 = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
246 vals['manager_id'] = ids2[0]
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)
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)
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
266 self.write(cr, uid, [record.id], {
268 'number_of_days': nb,
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
279 self.pool.get('resource.calendar.leaves').create(cr,uid,vals)
283 def holidays_refuse(self, cr, uid, ids, *args):
287 ids2 = self.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
289 vals['manager_id'] = ids2[0]
290 self.write(cr, uid, ids, vals)
293 def holidays_cancel(self, cr, uid, ids, *args):
294 for record in self.browse(cr, uid, ids):
295 if record.state=='validate':
297 self.pool.get('crm.case').unlink(cr,uid,[record.case_id.id])
298 if record.linked_request_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, {
309 def holidays_draft(self, cr, uid, ids, *args):
310 self.write(cr, uid, ids, {
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']
325 if leaves_rest < -(leave_asked):
326 raise osv.except_osv(_('Warning!'),_('You Cannot Validate leaves while available leaves are less than asked leaves.'))
330 'name' : record.name,
331 'holiday_status_id' : record.holiday_status_id.id,
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,
339 'allocation_type': record.allocation_type,
340 'parent_id': record.id,
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)
349 if record.holiday_status_id.section_id and record.date_from and record.date_to and record.employee_id:
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})