2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2008-08-24 14:45:43 +00:00
##################################################################################
#
2010-02-17 08:49:38 +00:00
# Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
2010-01-12 09:18:39 +00:00
# and 2004-2010 Tiny SPRL (<http://tiny.be>).
2008-08-24 14:45:43 +00:00
#
# $Id: hr.py 4656 2006-11-24 09:58:42Z Cyp $
#
2009-10-14 11:15:34 +00:00
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2008-08-24 14:45:43 +00:00
#
2009-10-14 11:15:34 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
2008-08-24 14:45:43 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2008-08-24 14:45:43 +00:00
#
##############################################################################
2010-07-03 10:21:52 +00:00
2012-12-18 16:42:17 +00:00
import datetime
2013-07-24 11:21:30 +00:00
import math
2012-12-18 16:42:17 +00:00
import time
2013-07-24 11:21:30 +00:00
from operator import attrgetter
2010-05-04 11:19:18 +00:00
2014-01-14 11:45:59 +00:00
from openerp . exceptions import Warning
2012-12-06 14:56:32 +00:00
from openerp import tools
from openerp . osv import fields , osv
from openerp . tools . translate import _
2008-08-24 14:45:43 +00:00
2010-09-22 08:23:53 +00:00
2008-08-30 11:45:40 +00:00
class hr_holidays_status ( osv . osv ) :
_name = " hr.holidays.status "
2010-05-19 18:32:32 +00:00
_description = " Leave Type "
2010-05-04 11:19:18 +00:00
2013-07-24 11:21:30 +00:00
def get_days ( self , cr , uid , ids , employee_id , context = None ) :
result = dict ( ( id , dict ( max_leaves = 0 , leaves_taken = 0 , remaining_leaves = 0 ,
virtual_remaining_leaves = 0 ) ) for id in ids )
holiday_ids = self . pool [ ' hr.holidays ' ] . search ( cr , uid , [ ( ' employee_id ' , ' = ' , employee_id ) ,
( ' state ' , ' in ' , [ ' confirm ' , ' validate1 ' , ' validate ' ] ) ,
( ' holiday_status_id ' , ' in ' , ids )
] , context = context )
for holiday in self . pool [ ' hr.holidays ' ] . browse ( cr , uid , holiday_ids , context = context ) :
status_dict = result [ holiday . holiday_status_id . id ]
if holiday . type == ' add ' :
2015-01-08 11:58:14 +00:00
status_dict [ ' virtual_remaining_leaves ' ] + = holiday . number_of_days_temp
2013-07-24 11:21:30 +00:00
if holiday . state == ' validate ' :
2015-01-08 11:58:14 +00:00
status_dict [ ' max_leaves ' ] + = holiday . number_of_days_temp
status_dict [ ' remaining_leaves ' ] + = holiday . number_of_days_temp
2013-07-24 11:21:30 +00:00
elif holiday . type == ' remove ' : # number of days is negative
2015-01-08 11:58:14 +00:00
status_dict [ ' virtual_remaining_leaves ' ] - = holiday . number_of_days_temp
2013-07-24 11:21:30 +00:00
if holiday . state == ' validate ' :
2015-01-08 11:58:14 +00:00
status_dict [ ' leaves_taken ' ] + = holiday . number_of_days_temp
status_dict [ ' remaining_leaves ' ] - = holiday . number_of_days_temp
2013-07-24 11:21:30 +00:00
return result
2009-03-13 06:25:53 +00:00
2010-05-26 10:19:25 +00:00
def _user_left_days ( self , cr , uid , ids , name , args , context = None ) :
2010-04-16 13:05:03 +00:00
employee_id = False
2013-07-24 11:21:30 +00:00
if context and ' employee_id ' in context :
2009-09-24 10:46:21 +00:00
employee_id = context [ ' employee_id ' ]
else :
2013-07-24 11:21:30 +00:00
employee_ids = self . pool . get ( ' hr.employee ' ) . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] , context = context )
2009-09-24 10:46:21 +00:00
if employee_ids :
employee_id = employee_ids [ 0 ]
2010-03-17 17:09:26 +00:00
if employee_id :
2013-07-24 11:21:30 +00:00
res = self . get_days ( cr , uid , ids , employee_id , context = context )
2010-05-14 14:46:47 +00:00
else :
2014-09-18 08:18:05 +00:00
res = dict ( ( res_id , { ' leaves_taken ' : 0 , ' remaining_leaves ' : 0 , ' max_leaves ' : 0 } ) for res_id in ids )
2009-09-24 10:46:21 +00:00
return res
2009-03-13 06:25:53 +00:00
_columns = {
2010-10-12 21:59:40 +00:00
' name ' : fields . char ( ' Leave Type ' , size = 64 , required = True , translate = True ) ,
2014-01-15 09:38:05 +00:00
' categ_id ' : fields . many2one ( ' calendar.event.type ' , ' Meeting Type ' ,
2014-07-09 11:39:38 +00:00
help = ' Once a leave is validated, Odoo will create a corresponding meeting of this type in the calendar. ' ) ,
2012-07-13 10:17:51 +00:00
' 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. ' ) ,
2012-12-14 10:37:01 +00:00
' 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 will not take them into account for the " Remaining Legal Leaves " defined on the employee form. ' ) ,
2010-05-04 11:19:18 +00:00
' 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. " ) ,
2011-07-01 23:41:24 +00:00
' 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 ' ) ,
' 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 ' ) ,
' remaining_leaves ' : fields . function ( _user_left_days , string = ' Remaining Leaves ' , help = ' Maximum Leaves Allowed - Leaves Already Taken ' , multi = ' user_left_days ' ) ,
2013-07-24 11:21:30 +00:00
' virtual_remaining_leaves ' : fields . function ( _user_left_days , string = ' Virtual Remaining Leaves ' , help = ' Maximum Leaves Allowed - Leaves Already Taken - Leaves Waiting Approval ' , multi = ' user_left_days ' ) ,
2012-06-13 14:26:05 +00:00
' double_validation ' : fields . boolean ( ' Apply Double Validation ' , help = " When selected, the Allocation/Leave Requests for this type require a second validation to be approved. " ) ,
2009-03-13 06:25:53 +00:00
}
_defaults = {
2010-05-04 11:19:18 +00:00
' color_name ' : ' red ' ,
' active ' : True ,
2010-08-26 04:52:04 +00:00
}
2012-11-07 11:28:07 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2014-12-19 09:53:25 +00:00
if context is None :
context = { }
2014-05-08 12:49:46 +00:00
if not context . get ( ' employee_id ' , False ) :
2014-05-14 08:04:16 +00:00
# leave counts is based on employee_id, would be inaccurate if not based on correct employee
2014-05-08 12:49:46 +00:00
return super ( hr_holidays_status , self ) . name_get ( cr , uid , ids , context = context )
2012-11-10 15:12:29 +00:00
res = [ ]
2012-11-10 14:05:19 +00:00
for record in self . browse ( cr , uid , ids , context = context ) :
name = record . name
if not record . limit :
2014-06-12 15:50:19 +00:00
name = name + ( ' ( %g / %g ) ' % ( record . leaves_taken or 0.0 , record . max_leaves or 0.0 ) )
2012-11-10 14:05:19 +00:00
res . append ( ( record . id , name ) )
2012-11-07 11:28:07 +00:00
return res
2009-03-13 06:25:53 +00:00
2008-08-24 14:45:43 +00:00
class hr_holidays ( osv . osv ) :
_name = " hr.holidays "
2010-05-19 18:32:32 +00:00
_description = " Leave "
2009-09-24 10:46:21 +00:00
_order = " type desc, date_from asc "
2012-12-18 16:42:17 +00:00
_inherit = [ ' mail.thread ' , ' ir.needaction_mixin ' ]
_track = {
' state ' : {
2013-06-27 14:46:47 +00:00
' hr_holidays.mt_holidays_approved ' : lambda self , cr , uid , obj , ctx = None : obj . state == ' validate ' ,
' hr_holidays.mt_holidays_refused ' : lambda self , cr , uid , obj , ctx = None : obj . state == ' refuse ' ,
' hr_holidays.mt_holidays_confirmed ' : lambda self , cr , uid , obj , ctx = None : obj . state == ' confirm ' ,
2012-12-18 16:42:17 +00:00
} ,
}
2009-09-24 10:46:21 +00:00
2013-02-11 10:57:01 +00:00
def _employee_get ( self , cr , uid , context = None ) :
emp_id = context . get ( ' default_employee_id ' , False )
if emp_id :
return emp_id
2011-06-20 06:04:34 +00:00
ids = self . pool . get ( ' hr.employee ' ) . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] , context = context )
2010-05-10 04:56:49 +00:00
if ids :
return ids [ 0 ]
return False
2008-11-20 17:21:19 +00:00
2010-12-29 12:41:23 +00:00
def _compute_number_of_days ( self , cr , uid , ids , name , args , context = None ) :
result = { }
for hol in self . browse ( cr , uid , ids , context = context ) :
if hol . type == ' remove ' :
result [ hol . id ] = - hol . number_of_days_temp
else :
result [ hol . id ] = hol . number_of_days_temp
return result
2013-08-09 14:47:52 +00:00
def _get_can_reset ( self , cr , uid , ids , name , arg , context = None ) :
""" User can reset a leave request if it is its own leave request or if
he is an Hr Manager . """
user = self . pool [ ' res.users ' ] . browse ( cr , uid , uid , context = context )
group_hr_manager_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' base ' , ' group_hr_manager ' ) [ 1 ]
if group_hr_manager_id in [ g . id for g in user . groups_id ] :
return dict . fromkeys ( ids , True )
result = dict . fromkeys ( ids , False )
for holiday in self . browse ( cr , uid , ids , context = context ) :
if holiday . employee_id and holiday . employee_id . user_id and holiday . employee_id . user_id . id == uid :
result [ holiday . id ] = True
return result
2014-07-06 14:44:26 +00:00
def _check_date ( self , cr , uid , ids , context = None ) :
for holiday in self . browse ( cr , uid , ids , context = context ) :
domain = [
( ' date_from ' , ' <= ' , holiday . date_to ) ,
( ' date_to ' , ' >= ' , holiday . date_from ) ,
( ' employee_id ' , ' = ' , holiday . employee_id . id ) ,
( ' id ' , ' != ' , holiday . id ) ,
( ' state ' , ' not in ' , [ ' cancel ' , ' refuse ' ] ) ,
]
nholidays = self . search_count ( cr , uid , domain , context = context )
if nholidays :
2012-11-06 13:55:15 +00:00
return False
return True
2013-10-15 16:54:16 +00:00
_check_holidays = lambda self , cr , uid , ids , context = None : self . check_holidays ( cr , uid , ids , context = context )
2008-08-24 14:45:43 +00:00
_columns = {
2012-08-01 12:40:44 +00:00
' name ' : fields . char ( ' Description ' , size = 64 ) ,
2012-05-23 06:53:42 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' To Submit ' ) , ( ' cancel ' , ' Cancelled ' ) , ( ' confirm ' , ' To Approve ' ) , ( ' refuse ' , ' Refused ' ) , ( ' validate1 ' , ' Second Approval ' ) , ( ' validate ' , ' Approved ' ) ] ,
2014-07-06 14:44:26 +00:00
' Status ' , readonly = True , track_visibility = ' onchange ' , copy = False ,
2012-12-18 16:42:17 +00:00
help = ' The status is set to \' To Submit \' , when a holiday request is created. \
2012-10-12 11:42:58 +00:00
\nThe status is \' To Approve \' , when holiday request is confirmed by user. \
\nThe status is \' Refused \' , when holiday request is refused by manager. \
\nThe status is \' Approved \' , when holiday request is approved by manager. ' ) ,
2010-12-29 12:41:23 +00:00
' user_id ' : fields . related ( ' employee_id ' , ' user_id ' , type = ' many2one ' , relation = ' res.users ' , string = ' User ' , store = True ) ,
2014-07-06 14:44:26 +00:00
' date_from ' : fields . datetime ( ' Start Date ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } , select = True , copy = False ) ,
' date_to ' : fields . datetime ( ' End Date ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } , copy = False ) ,
2012-02-22 13:06:38 +00:00
' holiday_status_id ' : fields . many2one ( " hr.holidays.status " , " Leave Type " , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2012-11-20 11:33:08 +00:00
' employee_id ' : fields . many2one ( ' hr.employee ' , " Employee " , select = True , invisible = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2014-07-06 14:44:26 +00:00
' manager_id ' : fields . many2one ( ' hr.employee ' , ' First Approval ' , invisible = False , readonly = True , copy = False ,
help = ' This area is automatically filled by the user who validate the leave ' ) ,
2012-02-22 13:06:38 +00:00
' notes ' : fields . text ( ' Reasons ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2014-07-06 14:44:26 +00:00
' number_of_days_temp ' : fields . float ( ' Allocation ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } , copy = False ) ,
2011-07-01 23:41:24 +00:00
' number_of_days ' : fields . function ( _compute_number_of_days , string = ' Number of Days ' , store = True ) ,
2014-01-15 09:38:05 +00:00
' meeting_id ' : fields . many2one ( ' calendar.event ' , ' Meeting ' ) ,
2012-02-22 13:06:38 +00:00
' 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. \n Choose ' Allocation Request ' if you want to increase the number of leaves available for someone " , select = True ) ,
2009-09-24 10:46:21 +00:00
' parent_id ' : fields . many2one ( ' hr.holidays ' , ' Parent ' ) ,
' linked_request_ids ' : fields . one2many ( ' hr.holidays ' , ' parent_id ' , ' Linked Requests ' , ) ,
2010-04-07 10:41:20 +00:00
' department_id ' : fields . related ( ' employee_id ' , ' department_id ' , string = ' Department ' , type = ' many2one ' , relation = ' hr.department ' , readonly = True , store = True ) ,
2012-11-19 09:20:53 +00:00
' category_id ' : fields . many2one ( ' hr.employee.category ' , " Employee Tag " , help = ' Category of Employee ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
' 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 ) ,
2014-07-06 14:44:26 +00:00
' manager_id2 ' : fields . many2one ( ' hr.employee ' , ' Second Approval ' , readonly = True , copy = False ,
help = ' This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation) ' ) ,
2011-06-01 05:19:41 +00:00
' double_validation ' : fields . related ( ' holiday_status_id ' , ' double_validation ' , type = ' boolean ' , relation = ' hr.holidays.status ' , string = ' Apply Double Validation ' ) ,
2013-08-09 14:47:52 +00:00
' can_reset ' : fields . function (
_get_can_reset ,
type = ' boolean ' ) ,
2010-08-26 04:52:04 +00:00
}
2008-08-24 14:45:43 +00:00
_defaults = {
2010-10-27 12:49:59 +00:00
' employee_id ' : _employee_get ,
2013-08-05 11:30:23 +00:00
' state ' : ' confirm ' ,
2010-05-04 11:19:18 +00:00
' type ' : ' remove ' ,
2008-11-20 17:21:19 +00:00
' user_id ' : lambda obj , cr , uid , context : uid ,
2010-05-05 12:18:58 +00:00
' holiday_type ' : ' employee '
2010-08-26 04:52:04 +00:00
}
2012-11-06 13:55:15 +00:00
_constraints = [
2012-11-08 09:24:51 +00:00
( _check_date , ' You can not have 2 leaves that overlaps on same day! ' , [ ' date_from ' , ' date_to ' ] ) ,
2013-10-15 16:54:16 +00:00
( _check_holidays , ' The number of remaining leaves is not sufficient for this leave type ' , [ ' state ' , ' number_of_days_temp ' ] )
2012-11-06 13:55:15 +00:00
]
2010-12-29 12:41:23 +00:00
_sql_constraints = [
2013-04-08 15:06:35 +00:00
( ' type_value ' , " CHECK( (holiday_type= ' employee ' AND employee_id IS NOT NULL) or (holiday_type= ' category ' AND category_id IS NOT NULL)) " ,
2013-04-15 15:06:55 +00:00
" The employee or employee category of this request is missing. Please make sure that your user login is linked to an employee. " ) ,
2012-10-31 18:00:53 +00:00
( ' date_check2 ' , " CHECK ( (type= ' add ' ) OR (date_from <= date_to)) " , " The start date must be anterior to the end date. " ) ,
( ' date_check ' , " CHECK ( number_of_days_temp >= 0 ) " , " The number of days must be greater than 0. " ) ,
2010-12-29 12:41:23 +00:00
]
2012-10-31 17:32:55 +00:00
2011-11-14 12:22:44 +00:00
def _create_resource_leave ( self , cr , uid , leaves , context = None ) :
2010-05-26 10:19:25 +00:00
''' This method will create entry in resource calendar leave object at the time of holidays validated '''
2010-05-26 09:54:56 +00:00
obj_res_leave = self . pool . get ( ' resource.calendar.leaves ' )
2011-11-14 12:22:44 +00:00
for leave in leaves :
vals = {
' name ' : leave . name ,
' date_from ' : leave . date_from ,
' holiday_id ' : leave . id ,
' date_to ' : leave . date_to ,
' resource_id ' : leave . employee_id . resource_id . id ,
' calendar_id ' : leave . employee_id . resource_id . calendar_id . id
}
obj_res_leave . create ( cr , uid , vals , context = context )
return True
2010-05-26 09:54:56 +00:00
2011-11-14 12:22:44 +00:00
def _remove_resource_leave ( self , cr , uid , ids , context = None ) :
2010-05-26 10:19:25 +00:00
''' This method will create entry in resource calendar leave object at the time of holidays cancel/removed '''
2010-05-26 09:54:56 +00:00
obj_res_leave = self . pool . get ( ' resource.calendar.leaves ' )
2010-07-27 07:11:45 +00:00
leave_ids = obj_res_leave . search ( cr , uid , [ ( ' holiday_id ' , ' in ' , ids ) ] , context = context )
2011-11-14 12:22:44 +00:00
return obj_res_leave . unlink ( cr , uid , leave_ids , context = context )
2010-05-26 09:54:56 +00:00
2013-02-24 13:21:11 +00:00
def onchange_type ( self , cr , uid , ids , holiday_type , employee_id = False , context = None ) :
2013-02-11 10:57:01 +00:00
result = { }
if holiday_type == ' employee ' and not employee_id :
2010-05-10 04:56:49 +00:00
ids_employee = self . pool . get ( ' hr.employee ' ) . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] )
if ids_employee :
result [ ' value ' ] = {
' employee_id ' : ids_employee [ 0 ]
2010-12-29 12:41:23 +00:00
}
2013-12-11 18:00:25 +00:00
elif holiday_type != ' employee ' :
result [ ' value ' ] = {
' employee_id ' : False
}
2010-05-10 04:56:49 +00:00
return result
2012-10-29 07:14:09 +00:00
def onchange_employee ( self , cr , uid , ids , employee_id ) :
result = { ' value ' : { ' department_id ' : False } }
if employee_id :
employee = self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , employee_id )
result [ ' value ' ] = { ' department_id ' : employee . department_id . id }
return result
2010-12-29 12:41:23 +00:00
# TODO: can be improved using resource calendar method
2010-05-18 06:26:16 +00:00
def _get_number_of_days ( self , date_from , date_to ) :
2010-05-14 15:18:57 +00:00
""" Returns a float equals to the timedelta between two dates given as string. """
DATETIME_FORMAT = " % Y- % m- %d % H: % M: % S "
from_dt = datetime . datetime . strptime ( date_from , DATETIME_FORMAT )
to_dt = datetime . datetime . strptime ( date_to , DATETIME_FORMAT )
timedelta = to_dt - from_dt
2010-05-18 06:26:16 +00:00
diff_day = timedelta . days + float ( timedelta . seconds ) / 86400
2010-05-14 15:18:57 +00:00
return diff_day
2008-12-31 09:54:58 +00:00
2010-05-26 09:54:56 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-12-29 12:41:23 +00:00
for rec in self . browse ( cr , uid , ids , context = context ) :
2012-07-30 10:31:39 +00:00
if rec . state not in [ ' draft ' , ' cancel ' , ' confirm ' ] :
2012-10-31 18:00:53 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot delete a leave which is in %s state. ' ) % ( rec . state ) )
2010-01-05 08:07:22 +00:00
return super ( hr_holidays , self ) . unlink ( cr , uid , ids , context )
2008-12-31 09:54:58 +00:00
2012-10-31 18:00:53 +00:00
def onchange_date_from ( self , cr , uid , ids , date_to , date_from ) :
2012-11-05 13:32:29 +00:00
"""
If there are no date set for date_to , automatically set one 8 hours later than
the date_from .
Also update the number_of_days .
"""
# date_to has to be greater than date_from
if ( date_from and date_to ) and ( date_from > date_to ) :
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' The start date must be anterior to the end date. ' ) )
result = { ' value ' : { } }
2012-10-31 18:00:53 +00:00
# No date_to set so far: automatically compute one 8 hours later
if date_from and not date_to :
2012-11-05 17:03:18 +00:00
date_to_with_delta = datetime . datetime . strptime ( date_from , tools . DEFAULT_SERVER_DATETIME_FORMAT ) + datetime . timedelta ( hours = 8 )
2012-11-05 13:32:29 +00:00
result [ ' value ' ] [ ' date_to ' ] = str ( date_to_with_delta )
# Compute and update the number of days
if ( date_to and date_from ) and ( date_from < = date_to ) :
diff_day = self . _get_number_of_days ( date_from , date_to )
result [ ' value ' ] [ ' number_of_days_temp ' ] = round ( math . floor ( diff_day ) ) + 1
else :
result [ ' value ' ] [ ' number_of_days_temp ' ] = 0
2012-10-31 18:00:53 +00:00
return result
2012-10-29 08:34:14 +00:00
def onchange_date_to ( self , cr , uid , ids , date_to , date_from ) :
2012-11-05 13:32:29 +00:00
"""
Update the number_of_days .
"""
# date_to has to be greater than date_from
if ( date_from and date_to ) and ( date_from > date_to ) :
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' The start date must be anterior to the end date. ' ) )
result = { ' value ' : { } }
# Compute and update the number of days
if ( date_to and date_from ) and ( date_from < = date_to ) :
2010-05-14 15:18:57 +00:00
diff_day = self . _get_number_of_days ( date_from , date_to )
2012-11-05 13:32:29 +00:00
result [ ' value ' ] [ ' number_of_days_temp ' ] = round ( math . floor ( diff_day ) ) + 1
2012-10-31 18:00:53 +00:00
else :
2012-11-05 13:32:29 +00:00
result [ ' value ' ] [ ' number_of_days_temp ' ] = 0
2012-10-31 18:00:53 +00:00
2008-11-20 17:21:19 +00:00
return result
2013-01-04 14:50:30 +00:00
def create ( self , cr , uid , values , context = None ) :
""" Override to avoid automatic logging of creation """
if context is None :
context = { }
2015-11-18 14:57:57 +00:00
context = dict ( context , mail_create_nolog = True , mail_create_nosubscribe = True )
2014-08-25 12:51:03 +00:00
if values . get ( ' state ' ) and values [ ' state ' ] not in [ ' draft ' , ' confirm ' , ' cancel ' ] and not self . pool [ ' res.users ' ] . has_group ( cr , uid , ' base.group_hr_user ' ) :
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot set a leave request as \' %s \' . Contact a human resource manager. ' ) % values . get ( ' state ' ) )
2013-01-04 14:50:30 +00:00
return super ( hr_holidays , self ) . create ( cr , uid , values , context = context )
2012-11-06 15:26:44 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2014-08-25 12:51:03 +00:00
if vals . get ( ' state ' ) and vals [ ' state ' ] not in [ ' draft ' , ' confirm ' , ' cancel ' ] and not self . pool [ ' res.users ' ] . has_group ( cr , uid , ' base.group_hr_user ' ) :
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot set a leave request as \' %s \' . Contact a human resource manager. ' ) % vals . get ( ' state ' ) )
2012-11-08 09:24:51 +00:00
return super ( hr_holidays , self ) . write ( cr , uid , ids , vals , context = context )
2008-11-20 17:21:19 +00:00
2013-08-05 11:30:23 +00:00
def holidays_reset ( self , cr , uid , ids , context = None ) :
2008-08-24 14:45:43 +00:00
self . write ( cr , uid , ids , {
2010-07-03 10:21:52 +00:00
' state ' : ' draft ' ,
2009-09-24 10:46:21 +00:00
' manager_id ' : False ,
2011-03-22 10:04:47 +00:00
' manager_id2 ' : False ,
2008-08-24 14:45:43 +00:00
} )
2011-12-30 12:28:11 +00:00
to_unlink = [ ]
for record in self . browse ( cr , uid , ids , context = context ) :
for record2 in record . linked_request_ids :
2013-08-05 11:30:23 +00:00
self . holidays_reset ( cr , uid , [ record2 . id ] , context = context )
2011-12-30 12:28:11 +00:00
to_unlink . append ( record2 . id )
if to_unlink :
self . unlink ( cr , uid , to_unlink , context = context )
2008-08-24 14:45:43 +00:00
return True
2012-08-14 12:20:50 +00:00
def holidays_first_validate ( self , cr , uid , ids , context = None ) :
2010-12-29 12:41:23 +00:00
obj_emp = self . pool . get ( ' hr.employee ' )
2010-12-09 05:50:40 +00:00
ids2 = obj_emp . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] )
2010-12-29 12:41:23 +00:00
manager = ids2 and ids2 [ 0 ] or False
2012-08-14 12:20:50 +00:00
self . holidays_first_validate_notificate ( cr , uid , ids , context = context )
2010-12-29 12:41:23 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' validate1 ' , ' manager_id ' : manager } )
2010-05-14 09:13:31 +00:00
2012-08-14 12:20:50 +00:00
def holidays_validate ( self , cr , uid , ids , context = None ) :
2010-12-29 12:41:23 +00:00
obj_emp = self . pool . get ( ' hr.employee ' )
2010-07-03 10:21:52 +00:00
ids2 = obj_emp . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] )
2010-12-29 12:41:23 +00:00
manager = ids2 and ids2 [ 0 ] or False
2011-03-22 10:04:47 +00:00
self . write ( cr , uid , ids , { ' state ' : ' validate ' } )
2010-12-29 12:41:23 +00:00
data_holiday = self . browse ( cr , uid , ids )
2010-05-10 10:58:32 +00:00
for record in data_holiday :
2012-08-14 12:20:50 +00:00
if record . double_validation :
self . write ( cr , uid , [ record . id ] , { ' manager_id2 ' : manager } )
else :
self . write ( cr , uid , [ record . id ] , { ' manager_id ' : manager } )
2010-07-03 10:21:52 +00:00
if record . holiday_type == ' employee ' and record . type == ' remove ' :
2014-01-15 09:38:05 +00:00
meeting_obj = self . pool . get ( ' calendar.event ' )
2012-07-05 08:06:59 +00:00
meeting_vals = {
2012-08-14 05:44:25 +00:00
' name ' : record . name or _ ( ' Leave Request ' ) ,
2012-07-14 16:04:03 +00:00
' categ_ids ' : record . holiday_status_id . categ_id and [ ( 6 , 0 , [ record . holiday_status_id . categ_id . id ] ) ] or [ ] ,
2010-12-29 12:41:23 +00:00
' duration ' : record . number_of_days_temp * 8 ,
2011-12-05 15:55:10 +00:00
' description ' : record . notes ,
2010-12-29 12:41:23 +00:00
' user_id ' : record . user_id . id ,
2014-05-05 15:42:46 +00:00
' start ' : record . date_from ,
' stop ' : record . date_to ,
2014-04-30 09:36:13 +00:00
' allday ' : False ,
2014-02-25 21:31:21 +00:00
' state ' : ' open ' , # to block that meeting date in the calendar
' class ' : ' confidential '
2013-12-20 14:18:55 +00:00
}
#Add the partner_id (if exist) as an attendee
if record . user_id and record . user_id . partner_id :
meeting_vals [ ' partner_ids ' ] = [ ( 4 , record . user_id . partner_id . id ) ]
2014-10-03 18:43:48 +00:00
ctx_no_email = dict ( context or { } , no_email = True )
meeting_id = meeting_obj . create ( cr , uid , meeting_vals , context = ctx_no_email )
2011-11-14 12:22:44 +00:00
self . _create_resource_leave ( cr , uid , [ record ] , context = context )
2012-07-05 08:06:59 +00:00
self . write ( cr , uid , ids , { ' meeting_id ' : meeting_id } )
2010-12-20 09:45:13 +00:00
elif record . holiday_type == ' category ' :
2010-12-29 12:41:23 +00:00
emp_ids = obj_emp . search ( cr , uid , [ ( ' category_ids ' , ' child_of ' , [ record . category_id . id ] ) ] )
2010-12-20 09:45:13 +00:00
leave_ids = [ ]
2015-11-18 14:57:57 +00:00
batch_context = dict ( context , mail_notify_force_send = False )
for emp in obj_emp . browse ( cr , uid , emp_ids , context = context ) :
2010-05-18 10:40:30 +00:00
vals = {
2010-12-29 12:41:23 +00:00
' name ' : record . name ,
' type ' : record . type ,
' holiday_type ' : ' employee ' ,
' holiday_status_id ' : record . holiday_status_id . id ,
' date_from ' : record . date_from ,
' date_to ' : record . date_to ,
' notes ' : record . notes ,
' number_of_days_temp ' : record . number_of_days_temp ,
' parent_id ' : record . id ,
' employee_id ' : emp . id
2010-12-20 09:45:13 +00:00
}
2015-11-18 14:57:57 +00:00
leave_ids . append ( self . create ( cr , uid , vals , context = batch_context ) )
2010-12-29 12:41:23 +00:00
for leave_id in leave_ids :
2013-01-28 14:16:23 +00:00
# TODO is it necessary to interleave the calls?
2014-07-06 14:44:26 +00:00
for sig in ( ' confirm ' , ' validate ' , ' second_validate ' ) :
self . signal_workflow ( cr , uid , [ leave_id ] , sig )
2008-08-24 14:45:43 +00:00
return True
2011-07-05 07:11:51 +00:00
def holidays_confirm ( self , cr , uid , ids , context = None ) :
2012-08-28 13:28:20 +00:00
for record in self . browse ( cr , uid , ids , context = context ) :
if record . employee_id and record . employee_id . parent_id and record . employee_id . parent_id . user_id :
2012-09-25 17:33:20 +00:00
self . message_subscribe_users ( cr , uid , [ record . id ] , user_ids = [ record . employee_id . parent_id . user_id . id ] , context = context )
2012-12-18 16:57:08 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' confirm ' } )
2008-08-24 14:45:43 +00:00
2012-08-14 12:20:50 +00:00
def holidays_refuse ( self , cr , uid , ids , context = None ) :
2010-12-09 05:50:40 +00:00
obj_emp = self . pool . get ( ' hr.employee ' )
2010-12-29 12:41:23 +00:00
ids2 = obj_emp . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] )
manager = ids2 and ids2 [ 0 ] or False
2012-08-14 12:20:50 +00:00
for holiday in self . browse ( cr , uid , ids , context = context ) :
if holiday . state == ' validate1 ' :
self . write ( cr , uid , [ holiday . id ] , { ' state ' : ' refuse ' , ' manager_id ' : manager } )
else :
self . write ( cr , uid , [ holiday . id ] , { ' state ' : ' refuse ' , ' manager_id2 ' : manager } )
2011-07-05 07:11:51 +00:00
self . holidays_cancel ( cr , uid , ids , context = context )
2008-08-24 14:45:43 +00:00
return True
2011-07-05 07:11:51 +00:00
def holidays_cancel ( self , cr , uid , ids , context = None ) :
2010-12-29 12:41:23 +00:00
for record in self . browse ( cr , uid , ids ) :
# Delete the meeting
2012-07-05 08:06:59 +00:00
if record . meeting_id :
2014-07-06 14:44:26 +00:00
record . meeting_id . unlink ( )
2008-08-24 14:45:43 +00:00
2010-12-29 12:41:23 +00:00
# If a category that created several holidays, cancel all related
2014-07-06 14:44:26 +00:00
self . signal_workflow ( cr , uid , map ( attrgetter ( ' id ' ) , record . linked_request_ids or [ ] ) , ' refuse ' )
2010-12-29 12:41:23 +00:00
2011-11-14 12:22:44 +00:00
self . _remove_resource_leave ( cr , uid , ids , context = context )
2010-12-29 12:41:23 +00:00
return True
2008-08-24 14:45:43 +00:00
2011-06-28 11:46:33 +00:00
def check_holidays ( self , cr , uid , ids , context = None ) :
2013-07-24 12:27:54 +00:00
for record in self . browse ( cr , uid , ids , context = context ) :
if record . holiday_type != ' employee ' or record . type != ' remove ' or not record . employee_id or record . holiday_status_id . limit :
continue
leave_days = self . pool . get ( ' hr.holidays.status ' ) . get_days ( cr , uid , [ record . holiday_status_id . id ] , record . employee_id . id , context = context ) [ record . holiday_status_id . id ]
2013-10-15 16:54:16 +00:00
if leave_days [ ' remaining_leaves ' ] < 0 or leave_days [ ' virtual_remaining_leaves ' ] < 0 :
# Raising a warning gives a more user-friendly feedback than the default constraint error
2014-01-14 11:45:59 +00:00
raise Warning ( _ ( ' The number of remaining leaves is not sufficient for this leave type. \n '
' Please verify also the leaves waiting for validation. ' ) )
2008-08-24 14:45:43 +00:00
return True
2012-08-15 10:29:19 +00:00
2012-02-20 17:23:23 +00:00
# -----------------------------
# OpenChatter and notifications
# -----------------------------
2012-08-15 10:29:19 +00:00
2012-12-20 11:02:42 +00:00
def _needaction_domain_get ( self , cr , uid , context = None ) :
2012-08-15 10:29:19 +00:00
emp_obj = self . pool . get ( ' hr.employee ' )
2012-09-19 13:12:40 +00:00
empids = emp_obj . search ( cr , uid , [ ( ' parent_id.user_id ' , ' = ' , uid ) ] , context = context )
dom = [ ' & ' , ( ' state ' , ' = ' , ' confirm ' ) , ( ' employee_id ' , ' in ' , empids ) ]
2012-08-15 10:29:19 +00:00
# if this user is a hr.manager, he should do second validations
if self . pool . get ( ' res.users ' ) . has_group ( cr , uid , ' base.group_hr_manager ' ) :
2012-09-19 13:12:40 +00:00
dom = [ ' | ' ] + dom + [ ( ' state ' , ' = ' , ' validate1 ' ) ]
2012-08-15 10:29:19 +00:00
return dom
2012-06-05 12:22:21 +00:00
2012-08-14 12:20:50 +00:00
def holidays_first_validate_notificate ( self , cr , uid , ids , context = None ) :
2012-08-31 14:42:44 +00:00
for obj in self . browse ( cr , uid , ids , context = context ) :
2012-09-03 09:18:18 +00:00
self . message_post ( cr , uid , [ obj . id ] ,
2013-01-04 14:50:30 +00:00
_ ( " Request approved, waiting second validation. " ) , context = context )
2012-09-03 09:55:26 +00:00
2010-05-11 12:28:51 +00:00
class resource_calendar_leaves ( osv . osv ) :
_inherit = " resource.calendar.leaves "
_description = " Leave Detail "
_columns = {
2012-06-26 06:20:15 +00:00
' holiday_id ' : fields . many2one ( " hr.holidays " , " Leave Request " ) ,
2010-08-26 04:52:04 +00:00
}
2010-05-11 12:28:51 +00:00
2010-05-26 10:19:25 +00:00
2011-05-12 11:26:29 +00:00
class hr_employee ( osv . osv ) :
2012-01-03 13:52:24 +00:00
_inherit = " hr.employee "
2011-05-12 11:26:29 +00:00
2012-01-03 13:52:24 +00:00
def create ( self , cr , uid , vals , context = None ) :
# don't pass the value of remaining leave if it's 0 at the creation time, otherwise it will trigger the inverse
# function _set_remaining_days and the system may not be configured for. Note that we don't have this problem on
# the write because the clients only send the fields that have been modified.
if ' remaining_leaves ' in vals and not vals [ ' remaining_leaves ' ] :
del ( vals [ ' remaining_leaves ' ] )
return super ( hr_employee , self ) . create ( cr , uid , vals , context = context )
def _set_remaining_days ( self , cr , uid , empl_id , name , value , arg , context = None ) :
2011-05-12 12:39:20 +00:00
employee = self . browse ( cr , uid , empl_id , context = context )
diff = value - employee . remaining_leaves
2011-05-12 11:26:29 +00:00
type_obj = self . pool . get ( ' hr.holidays.status ' )
holiday_obj = self . pool . get ( ' hr.holidays ' )
2011-05-12 12:39:20 +00:00
# Find for holidays status
status_ids = type_obj . search ( cr , uid , [ ( ' limit ' , ' = ' , False ) ] , context = context )
2011-05-27 12:26:56 +00:00
if len ( status_ids ) != 1 :
2012-08-07 11:34:14 +00:00
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. \n You 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 ) ) )
2011-05-12 12:39:20 +00:00
status_id = status_ids and status_ids [ 0 ] or False
2011-05-27 13:09:04 +00:00
if not status_id :
return False
if diff > 0 :
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 )
elif diff < 0 :
2014-07-29 09:42:10 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot reduce validated allocation requests ' ) )
2011-05-27 13:09:04 +00:00
else :
2011-05-12 12:39:20 +00:00
return False
2014-07-06 14:44:26 +00:00
for sig in ( ' confirm ' , ' validate ' , ' second_validate ' ) :
holiday_obj . signal_workflow ( cr , uid , [ leave_id ] , sig )
2011-05-12 12:39:20 +00:00
return True
2012-01-03 13:52:24 +00:00
def _get_remaining_days ( self , cr , uid , ids , name , args , context = None ) :
2011-10-01 22:59:58 +00:00
cr . execute ( """ SELECT
sum ( h . number_of_days ) as days ,
2012-10-31 17:32:55 +00:00
h . employee_id
2011-10-01 22:59:58 +00:00
from
hr_holidays h
2012-10-31 17:32:55 +00:00
join hr_holidays_status s on ( s . id = h . holiday_status_id )
2011-10-01 22:59:58 +00:00
where
h . state = ' validate ' and
s . limit = False and
2014-07-04 14:45:41 +00:00
h . employee_id in % s
group by h . employee_id """ , (tuple(ids),))
2011-05-12 12:39:20 +00:00
res = cr . dictfetchall ( )
remaining = { }
for r in res :
remaining [ r [ ' employee_id ' ] ] = r [ ' days ' ]
for employee_id in ids :
if not remaining . get ( employee_id ) :
remaining [ employee_id ] = 0.0
return remaining
2012-10-31 17:32:55 +00:00
def _get_leave_status ( self , cr , uid , ids , name , args , context = None ) :
2012-05-17 10:59:02 +00:00
holidays_obj = self . pool . get ( ' hr.holidays ' )
2012-10-31 17:32:55 +00:00
holidays_id = holidays_obj . search ( cr , uid ,
2012-05-22 14:37:59 +00:00
[ ( ' employee_id ' , ' in ' , ids ) , ( ' date_from ' , ' <= ' , time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ) ,
( ' date_to ' , ' >= ' , time . strftime ( ' % Y- % m- %d 23:59:59 ' ) ) , ( ' type ' , ' = ' , ' remove ' ) , ( ' state ' , ' not in ' , ( ' cancel ' , ' refuse ' ) ) ] ,
2011-12-22 14:24:02 +00:00
context = context )
result = { }
for id in ids :
result [ id ] = {
' current_leave_state ' : False ,
2011-12-23 09:02:10 +00:00
' current_leave_id ' : False ,
2012-05-22 14:37:59 +00:00
' leave_date_from ' : False ,
' leave_date_to ' : False ,
2011-12-22 14:24:02 +00:00
}
for holiday in self . pool . get ( ' hr.holidays ' ) . browse ( cr , uid , holidays_id , context = context ) :
2012-05-22 14:37:59 +00:00
result [ holiday . employee_id . id ] [ ' leave_date_from ' ] = holiday . date_from
result [ holiday . employee_id . id ] [ ' leave_date_to ' ] = holiday . date_to
2011-12-22 14:24:02 +00:00
result [ holiday . employee_id . id ] [ ' current_leave_state ' ] = holiday . state
result [ holiday . employee_id . id ] [ ' current_leave_id ' ] = holiday . holiday_status_id . id
return result
2014-04-15 10:54:19 +00:00
def _leaves_count ( self , cr , uid , ids , field_name , arg , context = None ) :
2014-05-07 08:35:56 +00:00
Holidays = self . pool [ ' hr.holidays ' ]
return {
2015-05-27 08:19:25 +00:00
employee_id : Holidays . search_count ( cr , uid , [ ( ' employee_id ' , ' = ' , employee_id ) , ( ' type ' , ' = ' , ' remove ' ) ] , context = context )
2014-05-07 08:35:56 +00:00
for employee_id in ids
}
2014-04-15 10:54:19 +00:00
2012-01-03 13:52:24 +00:00
_columns = {
2012-11-19 09:20:53 +00:00
' 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. ' ) ,
2011-12-22 14:24:02 +00:00
' current_leave_state ' : fields . function ( _get_leave_status , multi = " leave_status " , string = " Current Leave Status " , type = " selection " ,
selection = [ ( ' draft ' , ' New ' ) , ( ' confirm ' , ' Waiting Approval ' ) , ( ' refuse ' , ' Refused ' ) ,
( ' validate1 ' , ' Waiting Second Approval ' ) , ( ' validate ' , ' Approved ' ) , ( ' cancel ' , ' Cancelled ' ) ] ) ,
2011-12-22 22:10:31 +00:00
' current_leave_id ' : fields . function ( _get_leave_status , multi = " leave_status " , string = " Current Leave Type " , type = ' many2one ' , relation = ' hr.holidays.status ' ) ,
2012-05-22 14:37:59 +00:00
' leave_date_from ' : fields . function ( _get_leave_status , multi = ' leave_status ' , type = ' date ' , string = ' From Date ' ) ,
2012-10-31 17:32:55 +00:00
' leave_date_to ' : fields . function ( _get_leave_status , multi = ' leave_status ' , type = ' date ' , string = ' To Date ' ) ,
2014-04-15 10:54:19 +00:00
' leaves_count ' : fields . function ( _leaves_count , type = ' integer ' , string = ' Leaves ' ) ,
2011-05-12 11:26:29 +00:00
}
2011-10-01 22:59:58 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: