2010-01-12 10:25:29 +00:00
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# 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.
#
# 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.
#
# 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/>.
#
##############################################################################
2010-08-18 07:11:15 +00:00
2010-08-12 07:31:45 +00:00
from datetime import datetime , timedelta
2010-01-12 10:25:29 +00:00
import time
import math
2010-09-24 09:26:14 +00:00
from faces import *
from new import classobj
2010-01-12 10:25:29 +00:00
from osv import fields , osv
from tools . translate import _
2010-09-24 09:26:14 +00:00
import tools
2010-01-12 10:25:29 +00:00
class resource_calendar ( osv . osv ) :
_name = " resource.calendar "
_description = " Resource Calendar "
_columns = {
' name ' : fields . char ( " Name " , size = 64 , required = True ) ,
2010-05-14 09:15:02 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = False ) ,
2010-07-19 09:46:40 +00:00
' attendance_ids ' : fields . one2many ( ' resource.calendar.attendance ' , ' calendar_id ' , ' Working Time ' ) ,
2010-01-12 10:25:29 +00:00
' manager ' : fields . many2one ( ' res.users ' , ' Workgroup manager ' ) ,
}
_defaults = {
2010-06-01 12:06:40 +00:00
' company_id ' : lambda self , cr , uid , context : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' resource.calendar ' , context = context )
2010-01-12 10:25:29 +00:00
}
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
def _get_leaves ( self , cr , uid , id , resource ) :
2010-02-25 10:11:23 +00:00
resource_cal_leaves = self . pool . get ( ' resource.calendar.leaves ' )
2010-02-19 10:26:24 +00:00
dt_leave = [ ]
2010-08-18 07:11:15 +00:00
2010-03-02 13:27:02 +00:00
resource_leave_ids = resource_cal_leaves . search ( cr , uid , [ ( ' calendar_id ' , ' = ' , id ) , ' | ' , ( ' resource_id ' , ' = ' , False ) , ( ' resource_id ' , ' = ' , resource ) ] )
res_leaves = resource_cal_leaves . read ( cr , uid , resource_leave_ids , [ ' date_from ' , ' date_to ' ] )
2010-08-18 07:11:15 +00:00
2010-02-19 10:26:24 +00:00
for leave in res_leaves :
2010-08-12 07:31:45 +00:00
dtf = datetime . strptime ( leave [ ' date_from ' ] , ' % Y- % m- %d % H: % M: % S ' )
dtt = datetime . strptime ( leave [ ' date_to ' ] , ' % Y- % m- %d % H: % M: % S ' )
2010-02-19 10:26:24 +00:00
no = dtt - dtf
2010-08-12 07:31:45 +00:00
[ dt_leave . append ( ( dtf + timedelta ( days = x ) ) . strftime ( ' % Y- % m- %d ' ) ) for x in range ( int ( no . days + 1 ) ) ]
2010-02-19 10:26:24 +00:00
dt_leave . sort ( )
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
return dt_leave
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
def interval_min_get ( self , cr , uid , id , dt_from , hours , resource = False ) :
if not id :
2010-08-12 07:31:45 +00:00
td = int ( hours ) * 3
return [ ( dt_from - timedelta ( hours = td ) , dt_from ) ]
2010-06-04 10:49:47 +00:00
dt_leave = self . _get_leaves ( cr , uid , id , resource )
dt_leave . reverse ( )
2010-01-12 10:25:29 +00:00
todo = hours
cycle = 0
result = [ ]
maxrecur = 100
current_hour = dt_from . hour
while ( todo > 0 ) and maxrecur :
2010-08-12 07:31:45 +00:00
cr . execute ( " select hour_from,hour_to from resource_calendar_attendance where dayofweek= ' %s ' and calendar_id= %s order by hour_from desc " , ( dt_from . weekday ( ) , id ) )
2010-01-12 10:25:29 +00:00
for ( hour_from , hour_to ) in cr . fetchall ( ) :
2010-02-19 10:26:24 +00:00
leave_flag = False
2010-02-18 10:07:31 +00:00
if ( hour_from < current_hour ) and ( todo > 0 ) :
m = min ( hour_to , current_hour )
if ( m - hour_from ) > todo :
hour_from = m - todo
2010-02-19 10:26:24 +00:00
dt_check = dt_from . strftime ( ' % Y- % m- %d ' )
for leave in dt_leave :
if dt_check == leave :
2010-08-12 07:31:45 +00:00
dt_check = datetime . strptime ( dt_check , ' % Y- % m- %d ' ) + timedelta ( days = 1 )
2010-02-19 10:26:24 +00:00
leave_flag = True
if leave_flag :
break
else :
2010-08-12 07:31:45 +00:00
d1 = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( hour_from ) ) , int ( ( hour_from % 1 ) * 60 ) )
d2 = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( m ) ) , int ( ( m % 1 ) * 60 ) )
2010-02-19 10:26:24 +00:00
result . append ( ( d1 , d2 ) )
current_hour = hour_from
todo - = ( m - hour_from )
2010-08-12 07:31:45 +00:00
dt_from - = timedelta ( days = 1 )
2010-02-18 10:07:31 +00:00
current_hour = 24
2010-01-12 10:25:29 +00:00
maxrecur - = 1
2010-02-18 10:07:31 +00:00
result . reverse ( )
2010-01-12 10:25:29 +00:00
return result
2010-02-25 10:11:23 +00:00
def interval_get ( self , cr , uid , id , dt_from , hours , resource = False , byday = True ) :
2010-01-12 10:25:29 +00:00
if not id :
2010-08-12 07:31:45 +00:00
td = int ( hours ) * 3
return [ ( dt_from , dt_from + timedelta ( hours = td ) ) ]
2010-06-04 10:49:47 +00:00
dt_leave = self . _get_leaves ( cr , uid , id , resource )
2010-01-12 10:25:29 +00:00
todo = hours
cycle = 0
result = [ ]
maxrecur = 100
current_hour = dt_from . hour
while ( todo > 0 ) and maxrecur :
2010-08-12 07:31:45 +00:00
cr . execute ( " select hour_from,hour_to from resource_calendar_attendance where dayofweek= ' %s ' and calendar_id= %s order by hour_from " , ( dt_from . weekday ( ) , id ) )
2010-01-12 10:25:29 +00:00
for ( hour_from , hour_to ) in cr . fetchall ( ) :
2010-06-04 10:49:47 +00:00
leave_flag = False
if ( hour_to > current_hour ) and ( todo > 0 ) :
m = max ( hour_from , current_hour )
if ( hour_to - m ) > todo :
hour_to = m + todo
dt_check = dt_from . strftime ( ' % Y- % m- %d ' )
for leave in dt_leave :
if dt_check == leave :
2010-08-12 07:31:45 +00:00
dt_check = datetime . strptime ( dt_check , ' % Y- % m- %d ' ) + timedelta ( days = 1 )
2010-06-04 10:49:47 +00:00
leave_flag = True
if leave_flag :
break
else :
2010-08-12 07:31:45 +00:00
d1 = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( m ) ) , int ( ( m % 1 ) * 60 ) )
d2 = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( hour_to ) ) , int ( ( hour_to % 1 ) * 60 ) )
2010-06-04 10:49:47 +00:00
result . append ( ( d1 , d2 ) )
current_hour = hour_to
todo - = ( hour_to - m )
2010-08-12 07:31:45 +00:00
dt_from + = timedelta ( days = 1 )
2010-01-12 10:25:29 +00:00
current_hour = 0
maxrecur - = 1
return result
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
def interval_hours_get ( self , cr , uid , id , dt_from , dt_to , resource = False ) :
result = [ ]
if not id :
return 0.0
dt_leave = self . _get_leaves ( cr , uid , id , resource )
hours = 0.0
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
current_hour = dt_from . hour
2010-01-12 10:25:29 +00:00
2010-06-04 10:49:47 +00:00
while ( dt_from < = dt_to ) :
2010-08-12 07:31:45 +00:00
cr . execute ( " select hour_from,hour_to from resource_calendar_attendance where dayofweek= ' %s ' and calendar_id= %s order by hour_from " , ( dt_from . weekday ( ) , id ) )
2010-06-04 10:49:47 +00:00
der = cr . fetchall ( )
for ( hour_from , hour_to ) in der :
if hours != 0.0 : #For first time of the loop only,hours will be 0
current_hour = hour_from
leave_flag = False
if ( hour_to > = current_hour ) :
dt_check = dt_from . strftime ( ' % Y- % m- %d ' )
for leave in dt_leave :
if dt_check == leave :
2010-08-12 07:31:45 +00:00
dt_check = datetime . strptime ( dt_check , " % Y- % m- %d " ) + timedelta ( days = 1 )
2010-06-04 10:49:47 +00:00
leave_flag = True
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
if leave_flag :
break
else :
d1 = dt_from
2010-08-12 07:31:45 +00:00
d2 = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( hour_to ) ) , int ( ( hour_to % 1 ) * 60 ) )
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
if hours != 0.0 : #For first time of the loop only,hours will be 0
2010-08-12 07:31:45 +00:00
d1 = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( current_hour ) ) , int ( ( current_hour % 1 ) * 60 ) )
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
if dt_from . day == dt_to . day :
if hour_from < = dt_to . hour < = hour_to :
d2 = dt_to
dt_from = d2
hours + = ( d2 - d1 ) . seconds
2010-08-12 07:31:45 +00:00
dt_from = datetime ( dt_from . year , dt_from . month , dt_from . day , int ( math . floor ( current_hour ) ) , int ( ( current_hour % 1 ) * 60 ) ) + timedelta ( days = 1 )
2010-06-04 10:49:47 +00:00
current_hour = 0.0
2010-08-18 07:11:15 +00:00
2010-06-04 10:49:47 +00:00
return ( hours / 3600 )
2010-08-18 07:11:15 +00:00
2010-01-12 10:25:29 +00:00
resource_calendar ( )
2010-07-19 09:46:40 +00:00
class resource_calendar_attendance ( osv . osv ) :
_name = " resource.calendar.attendance "
2010-01-12 10:25:29 +00:00
_description = " Work Detail "
_columns = {
' name ' : fields . char ( " Name " , size = 64 , required = True ) ,
' dayofweek ' : fields . selection ( [ ( ' 0 ' , ' Monday ' ) , ( ' 1 ' , ' Tuesday ' ) , ( ' 2 ' , ' Wednesday ' ) , ( ' 3 ' , ' Thursday ' ) , ( ' 4 ' , ' Friday ' ) , ( ' 5 ' , ' Saturday ' ) , ( ' 6 ' , ' Sunday ' ) ] , ' Day of week ' ) ,
' date_from ' : fields . date ( ' Starting date ' ) ,
' hour_from ' : fields . float ( ' Work from ' , size = 8 , required = True ) ,
' hour_to ' : fields . float ( " Work to " , size = 8 , required = True ) ,
' calendar_id ' : fields . many2one ( " resource.calendar " , " Resource ' s Calendar " , required = True ) ,
}
_order = ' dayofweek, hour_from '
2010-07-19 09:46:40 +00:00
resource_calendar_attendance ( )
2010-01-12 10:25:29 +00:00
2010-09-24 09:26:14 +00:00
def convert_timeformat ( time_string ) :
split_list = str ( time_string ) . split ( ' . ' )
hour_part = split_list [ 0 ]
mins_part = split_list [ 1 ]
round_mins = int ( round ( float ( mins_part ) * 60 , - 2 ) )
converted_string = hour_part + ' : ' + str ( round_mins ) [ 0 : 2 ]
return converted_string
2010-01-12 10:25:29 +00:00
class resource_resource ( osv . osv ) :
_name = " resource.resource "
_description = " Resource Detail "
_columns = {
2010-05-14 09:50:39 +00:00
' name ' : fields . char ( " Name " , size = 64 , required = True ) ,
2010-01-12 10:25:29 +00:00
' code ' : fields . char ( ' Code ' , size = 16 ) ,
' active ' : fields . boolean ( ' Active ' , help = " If the active field is set to true, it will allow you to hide the resource record without removing it. " ) ,
2010-05-14 09:50:39 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2010-01-12 10:25:29 +00:00
' resource_type ' : fields . selection ( [ ( ' user ' , ' Human ' ) , ( ' material ' , ' Material ' ) ] , ' Resource Type ' , required = True ) ,
2010-03-17 07:15:53 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' User ' , help = ' Related user name for the resource to manage its access. ' ) ,
' time_efficiency ' : fields . float ( ' Efficiency factor ' , size = 8 , required = True , help = " This field depict the efficiency of the resource to complete tasks. e.g resource put alone on a phase of 5 days with 5 tasks assigned to him, will show a load of 100 % f or this phase by default, but if we put a efficency of 200 % , then his load will only be 50 % . " ) ,
' calendar_id ' : fields . many2one ( " resource.calendar " , " Working time " , help = " Define the schedule of resource " ) ,
2010-01-12 10:25:29 +00:00
}
_defaults = {
2010-05-14 09:15:02 +00:00
' resource_type ' : ' user ' ,
' time_efficiency ' : 1 ,
' active ' : True ,
2010-05-17 10:20:35 +00:00
' company_id ' : lambda self , cr , uid , context : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' resource.resource ' , context = context )
2010-01-12 10:25:29 +00:00
}
2010-02-25 10:11:23 +00:00
2010-09-24 09:26:14 +00:00
2010-09-18 05:52:11 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
if default is None :
default = { }
if not default . get ( ' name ' , False ) :
default [ ' name ' ] = self . browse ( cr , uid , id , context = context ) . name + _ ( ' (copy) ' )
return super ( resource_resource , self ) . copy ( cr , uid , id , default , context )
2010-09-24 09:26:14 +00:00
def generate_resources ( self , cr , uid , user_ids , calendar_id , context = None ) :
"""
Return a list of Resource Class objects for the resources allocated to the phase .
"""
if context is None :
context = { }
resource_objs = [ ]
user_pool = self . pool . get ( ' res.users ' )
for user in user_pool . browse ( cr , uid , user_ids , context = context ) :
resource_ids = self . search ( cr , uid , [ ( ' user_id ' , ' = ' , user . id ) ] , context = context )
#assert len(resource_ids) < 1, "User should not has more than one resources"
leaves = [ ]
resource_eff = 1.0
if resource_ids :
resource_id = resource_ids [ 0 ]
resource = self . browse ( cr , uid , resource_id , context = context )
resource_eff = resource . time_efficiency
resource_cal = resource . calendar_id . id
if resource_cal :
leaves = self . compute_vacation ( cr , uid , calendar_id , resource . id , resource_cal , context = context )
resource_objs . append ( classobj ( str ( user . name ) , ( Resource , ) , {
' __doc__ ' : user . name ,
' __name__ ' : user . name ,
' vacation ' : tuple ( leaves ) ,
' efficiency ' : resource_eff ,
} ) )
return resource_objs
def compute_vacation ( self , cr , uid , calendar_id , resource_id = False , resource_calendar = False , context = None ) :
"""
Compute the vacation from the working calendar of the resource .
@param calendar_id : working calendar of the project
@param resource_id : resource working on phase / task
@param resource_calendar : working calendar of the resource
"""
if context is None :
context = { }
resource_calendar_leaves_pool = self . pool . get ( ' resource.calendar.leaves ' )
leave_list = [ ]
if resource_id :
leave_ids = resource_calendar_leaves_pool . search ( cr , uid , [ ' | ' , ( ' calendar_id ' , ' = ' , calendar_id ) ,
( ' calendar_id ' , ' = ' , resource_calendar ) ,
( ' resource_id ' , ' = ' , resource_id )
] , context = context )
else :
leave_ids = resource_calendar_leaves_pool . search ( cr , uid , [ ( ' calendar_id ' , ' = ' , calendar_id ) ,
( ' resource_id ' , ' = ' , False )
] , context = context )
leaves = resource_calendar_leaves_pool . read ( cr , uid , leave_ids , [ ' date_from ' , ' date_to ' ] , context = context )
for i in range ( len ( leaves ) ) :
dt_start = datetime . datetime . strptime ( leaves [ i ] [ ' date_from ' ] , ' % Y- % m- %d % H: % M: % S ' )
dt_end = datetime . datetime . strptime ( leaves [ i ] [ ' date_to ' ] , ' % Y- % m- %d % H: % M: % S ' )
no = dt_end - dt_start
[ leave_list . append ( ( dt_start + datetime . timedelta ( days = x ) ) . strftime ( ' % Y- % m- %d ' ) ) for x in range ( int ( no . days + 1 ) ) ]
leave_list . sort ( )
return leave_list
def compute_working_calendar ( cr , uid , calendar_id , context = None ) :
"""
Change the format of working calendar from ' Openerp ' format to bring it into ' Faces ' format .
@param calendar_id : working calendar of the project
"""
if context is None :
context = { }
resource_attendance_pool = self . pool . get ( ' resource.calendar.attendance ' )
time_range = " 8:00-8:00 "
non_working = " "
week_days = { " 0 " : " mon " , " 1 " : " tue " , " 2 " : " wed " , " 3 " : " thu " , " 4 " : " fri " , " 5 " : " sat " , " 6 " : " sun " }
wk_days = { }
wk_time = { }
wktime_list = [ ]
wktime_cal = [ ]
week_ids = resource_attendance_pool . search ( cr , uid , [ ( ' calendar_id ' , ' = ' , calendar_id ) ] , context = context )
weeks = resource_attendance_pool . read ( cr , uid , week_ids , [ ' dayofweek ' , ' hour_from ' , ' hour_to ' ] , context = context )
# Convert time formats into appropriate format required
# and create a list like [('mon', '8:00-12:00'), ('mon', '13:00-18:00')]
for week in weeks :
res_str = " "
if week_days . has_key ( week [ ' dayofweek ' ] ) :
day = week_days [ week [ ' dayofweek ' ] ]
wk_days [ week [ ' dayofweek ' ] ] = week_days [ week [ ' dayofweek ' ] ]
hour_from_str = convert_timeformat ( cr , uid , week [ ' hour_from ' ] )
hour_to_str = convert_timeformat ( cr , uid , week [ ' hour_to ' ] )
res_str = hour_from_str + ' - ' + hour_to_str
wktime_list . append ( ( day , res_str ) )
# Convert into format like [('mon', '8:00-12:00', '13:00-18:00')]
for item in wktime_list :
if wk_time . has_key ( item [ 0 ] ) :
wk_time [ item [ 0 ] ] . append ( item [ 1 ] )
else :
wk_time [ item [ 0 ] ] = [ item [ 0 ] ]
wk_time [ item [ 0 ] ] . append ( item [ 1 ] )
for k , v in wk_time . items ( ) :
wktime_cal . append ( tuple ( v ) )
# Add for the non-working days like: [('sat, sun', '8:00-8:00')]
for k , v in wk_days . items ( ) :
if week_days . has_key ( k ) :
week_days . pop ( k )
for v in week_days . itervalues ( ) :
non_working + = v + ' , '
if non_working :
wktime_cal . append ( ( non_working [ : - 1 ] , time_range ) )
return wktime_cal
2010-01-12 10:25:29 +00:00
resource_resource ( )
class resource_calendar_leaves ( osv . osv ) :
_name = " resource.calendar.leaves "
_description = " Leave Detail "
_columns = {
' name ' : fields . char ( " Name " , size = 64 ) ,
2010-01-13 10:47:23 +00:00
' company_id ' : fields . related ( ' calendar_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = " Company " , readonly = True ) ,
2010-01-12 10:25:29 +00:00
' calendar_id ' : fields . many2one ( " resource.calendar " , " Working time " ) ,
' date_from ' : fields . datetime ( ' Start Date ' , required = True ) ,
' date_to ' : fields . datetime ( ' End Date ' , required = True ) ,
' resource_id ' : fields . many2one ( " resource.resource " , " Resource " , help = " If empty, this is a generic holiday for the company. If a resource is set, the holiday/leave is only for this resource " ) ,
}
2010-08-18 07:11:15 +00:00
2010-03-17 07:15:53 +00:00
def check_dates ( self , cr , uid , ids , context = { } ) :
2010-03-02 13:27:02 +00:00
leave = self . read ( cr , uid , ids [ 0 ] , [ ' date_from ' , ' date_to ' ] )
2010-01-18 13:44:53 +00:00
if leave [ ' date_from ' ] and leave [ ' date_to ' ] :
if leave [ ' date_from ' ] > leave [ ' date_to ' ] :
return False
return True
_constraints = [
( check_dates , ' Error! leave start-date must be lower then leave end-date. ' , [ ' date_from ' , ' date_to ' ] )
]
2010-02-25 10:11:23 +00:00
2010-08-18 07:11:15 +00:00
def onchange_resource ( self , cr , uid , ids , resource , context = None ) :
2010-02-25 10:11:23 +00:00
result = { }
if resource :
resource_pool = self . pool . get ( ' resource.resource ' )
2010-03-02 13:27:02 +00:00
result [ ' calendar_id ' ] = resource_pool . browse ( cr , uid , resource ) . calendar_id . id
return { ' value ' : result }
return { ' value ' : { ' calendar_id ' : [ ] } }
2010-02-25 10:11:23 +00:00
2010-01-12 10:25:29 +00:00
resource_calendar_leaves ( )
2010-08-18 07:11:15 +00:00
2010-09-24 09:26:14 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: