2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2007-03-05 18:22:32 +00:00
##############################################################################
2009-12-24 07:05:23 +00:00
#
2009-10-14 11:15:34 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2007-03-05 18:22:32 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 11:15:34 +00:00
# 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.
2007-03-05 18:22:32 +00:00
#
2008-11-03 19:18:56 +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
2009-10-14 11:15:34 +00:00
# GNU Affero General Public License for more details.
2007-03-05 18:22:32 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2009-12-24 07:05:23 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2007-03-05 18:22:32 +00:00
#
##############################################################################
import time
2013-09-09 10:36:54 +00:00
from datetime import datetime
2010-10-15 14:01:53 +00:00
from dateutil . relativedelta import relativedelta
2013-07-23 18:43:41 +00:00
from pytz import timezone
import pytz
2010-11-02 11:11:12 +00:00
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
from openerp import netsvc
2013-09-09 10:37:24 +00:00
from openerp . tools import DEFAULT_SERVER_DATE_FORMAT , DEFAULT_SERVER_DATETIME_FORMAT
2007-03-05 18:22:32 +00:00
class hr_timesheet_sheet ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " hr_timesheet_sheet.sheet "
2012-09-24 10:38:12 +00:00
_inherit = " mail.thread "
2008-07-22 15:11:28 +00:00
_table = ' hr_timesheet_sheet_sheet '
_order = " id desc "
2010-09-01 12:04:41 +00:00
_description = " Timesheet "
2008-07-22 15:11:28 +00:00
2010-05-19 13:07:03 +00:00
def _total ( self , cr , uid , ids , name , args , context = None ) :
2012-01-06 09:25:20 +00:00
""" Compute the attendances, analytic lines timesheets and differences between them
for all the days of a timesheet and the current day
2011-11-14 12:20:42 +00:00
"""
2009-09-24 10:46:21 +00:00
res = { }
2012-10-26 07:57:15 +00:00
for sheet in self . browse ( cr , uid , ids , context = context or { } ) :
res . setdefault ( sheet . id , {
' total_attendance ' : 0.0 ,
' total_timesheet ' : 0.0 ,
' total_difference ' : 0.0 ,
} )
for period in sheet . period_ids :
res [ sheet . id ] [ ' total_attendance ' ] + = period . total_attendance
res [ sheet . id ] [ ' total_timesheet ' ] + = period . total_timesheet
res [ sheet . id ] [ ' total_difference ' ] + = period . total_attendance - period . total_timesheet
2009-09-24 10:46:21 +00:00
return res
2008-07-22 15:11:28 +00:00
2010-08-11 15:58:57 +00:00
def check_employee_attendance_state ( self , cr , uid , sheet_id , context = None ) :
ids_signin = self . pool . get ( ' hr.attendance ' ) . search ( cr , uid , [ ( ' sheet_id ' , ' = ' , sheet_id ) , ( ' action ' , ' = ' , ' sign_in ' ) ] )
ids_signout = self . pool . get ( ' hr.attendance ' ) . search ( cr , uid , [ ( ' sheet_id ' , ' = ' , sheet_id ) , ( ' action ' , ' = ' , ' sign_out ' ) ] )
if len ( ids_signin ) != len ( ids_signout ) :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( ( ' Warning! ' ) , _ ( ' The timesheet cannot be validated as it does not contain an equal number of sign ins and sign outs. ' ) )
2010-08-11 15:58:57 +00:00
return True
2008-07-22 15:11:28 +00:00
2009-01-15 16:57:02 +00:00
def copy ( self , cr , uid , ids , * args , * * argv ) :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot duplicate a timesheet. ' ) )
2009-01-15 16:57:02 +00:00
2014-04-23 11:25:54 +00:00
def create ( self , cr , uid , vals , context = None ) :
2010-10-06 16:39:01 +00:00
if ' employee_id ' in vals :
2014-04-23 11:25:54 +00:00
if not self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , vals [ ' employee_id ' ] , context = context ) . user_id :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' In order to create a timesheet for this employee, you must assign it to a user. ' ) )
2014-04-23 11:25:54 +00:00
if not self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , vals [ ' employee_id ' ] , context = context ) . product_id :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' In order to create a timesheet for this employee, you must link the employee to a product, like \' Consultant \' . ' ) )
2014-04-23 11:25:54 +00:00
if not self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , vals [ ' employee_id ' ] , context = context ) . journal_id :
2012-10-11 12:20:36 +00:00
raise osv . except_osv ( _ ( ' Configuration Error! ' ) , _ ( ' In order to create a timesheet for this employee, you must assign an analytic journal to the employee, like \' Timesheet Journal \' . ' ) )
2013-06-04 07:01:31 +00:00
if vals . get ( ' attendances_ids ' ) :
2014-04-23 11:25:54 +00:00
# If attendances, we sort them by date asc before writing them, to satisfy the alternance constraint
vals [ ' attendances_ids ' ] = self . sort_attendances ( cr , uid , vals [ ' attendances_ids ' ] , context = context )
return super ( hr_timesheet_sheet , self ) . create ( cr , uid , vals , context = context )
2010-10-06 16:39:01 +00:00
2014-04-23 11:25:54 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2010-10-06 16:39:01 +00:00
if ' employee_id ' in vals :
2013-06-10 09:29:48 +00:00
new_user_id = self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , vals [ ' employee_id ' ] , context = context ) . user_id . id or False
2010-10-06 16:39:01 +00:00
if not new_user_id :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' In order to create a timesheet for this employee, you must assign it to a user. ' ) )
2013-06-10 09:29:48 +00:00
if not self . _sheet_date ( cr , uid , ids , forced_user_id = new_user_id , context = context ) :
2012-10-29 15:08:09 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot have 2 timesheets that overlap! \n You should use the menu \' My Timesheet \' to avoid this problem. ' ) )
2013-06-10 09:29:48 +00:00
if not self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , vals [ ' employee_id ' ] , context = context ) . product_id :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' In order to create a timesheet for this employee, you must link the employee to a product. ' ) )
2013-06-10 09:29:48 +00:00
if not self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , vals [ ' employee_id ' ] , context = context ) . journal_id :
2012-10-22 07:02:15 +00:00
raise osv . except_osv ( _ ( ' Configuration Error! ' ) , _ ( ' In order to create a timesheet for this employee, you must assign an analytic journal to the employee, like \' Timesheet Journal \' . ' ) )
2014-04-23 11:25:54 +00:00
if vals . get ( ' attendances_ids ' ) :
# If attendances, we sort them by date asc before writing them, to satisfy the alternance constraint
# In addition to the date order, deleting attendances are done before inserting attendances
vals [ ' attendances_ids ' ] = self . sort_attendances ( cr , uid , vals [ ' attendances_ids ' ] , context = context )
res = super ( hr_timesheet_sheet , self ) . write ( cr , uid , ids , vals , context = context )
if vals . get ( ' attendances_ids ' ) :
for timesheet in self . browse ( cr , uid , ids ) :
if not self . pool [ ' hr.attendance ' ] . _altern_si_so ( cr , uid , [ att . id for att in timesheet . attendances_ids ] ) :
raise osv . except_osv ( _ ( ' Warning ! ' ) , _ ( ' Error ! Sign in (resp. Sign out) must follow Sign out (resp. Sign in) ' ) )
return res
def sort_attendances ( self , cr , uid , attendance_tuples , context = None ) :
date_attendances = [ ]
for att_tuple in attendance_tuples :
if att_tuple [ 0 ] in [ 0 , 1 , 4 ] :
if att_tuple [ 0 ] in [ 0 , 1 ] :
name = att_tuple [ 2 ] [ ' name ' ]
else :
name = self . pool [ ' hr.attendance ' ] . browse ( cr , uid , att_tuple [ 1 ] ) . name
date_attendances . append ( ( 1 , name , att_tuple ) )
elif att_tuple [ 0 ] in [ 2 , 3 ] :
date_attendances . append ( ( 0 , self . pool [ ' hr.attendance ' ] . browse ( cr , uid , att_tuple [ 1 ] ) . name , att_tuple ) )
else :
date_attendances . append ( ( 0 , False , att_tuple ) )
date_attendances . sort ( )
return [ att [ 2 ] for att in date_attendances ]
2010-10-06 16:39:01 +00:00
2010-05-19 13:07:03 +00:00
def button_confirm ( self , cr , uid , ids , context = None ) :
for sheet in self . browse ( cr , uid , ids , context = context ) :
2012-09-24 10:38:12 +00:00
if sheet . employee_id and sheet . employee_id . parent_id and sheet . employee_id . parent_id . user_id :
2012-09-25 17:33:20 +00:00
self . message_subscribe_users ( cr , uid , [ sheet . id ] , user_ids = [ sheet . employee_id . parent_id . user_id . id ] , context = context )
2011-11-14 12:20:42 +00:00
self . check_employee_attendance_state ( cr , uid , sheet . id , context = context )
2008-07-22 15:11:28 +00:00
di = sheet . user_id . company_id . timesheet_max_difference
if ( abs ( sheet . total_difference ) < di ) or not di :
wf_service = netsvc . LocalService ( " workflow " )
wf_service . trg_validate ( uid , ' hr_timesheet_sheet.sheet ' , sheet . id , ' confirm ' , cr )
else :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' Please verify that the total difference of the sheet is lower than %.2f . ' ) % ( di , ) )
2008-07-22 15:11:28 +00:00
return True
2012-05-11 07:33:20 +00:00
def attendance_action_change ( self , cr , uid , ids , context = None ) :
hr_employee = self . pool . get ( ' hr.employee ' )
employee_ids = [ ]
for sheet in self . browse ( cr , uid , ids , context = context ) :
if sheet . employee_id . id not in employee_ids : employee_ids . append ( sheet . employee_id . id )
return hr_employee . attendance_action_change ( cr , uid , employee_ids , context = context )
2008-07-22 15:11:28 +00:00
_columns = {
2011-09-10 12:43:55 +00:00
' name ' : fields . char ( ' Note ' , size = 64 , select = 1 ,
2009-01-20 18:51:08 +00:00
states = { ' confirm ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2010-10-06 16:39:01 +00:00
' employee_id ' : fields . many2one ( ' hr.employee ' , ' Employee ' , required = True ) ,
' user_id ' : fields . related ( ' employee_id ' , ' user_id ' , type = " many2one " , relation = " res.users " , store = True , string = " User " , required = False , readonly = True ) , #fields.many2one('res.users', 'User', required=True, select=1, states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}),
2008-07-22 15:11:28 +00:00
' date_from ' : fields . date ( ' Date from ' , required = True , select = 1 , readonly = True , states = { ' new ' : [ ( ' readonly ' , False ) ] } ) ,
' date_to ' : fields . date ( ' Date to ' , required = True , select = 1 , readonly = True , states = { ' new ' : [ ( ' readonly ' , False ) ] } ) ,
2012-10-01 15:46:05 +00:00
' timesheet_ids ' : fields . one2many ( ' hr.analytic.timesheet ' , ' sheet_id ' ,
' Timesheet lines ' ,
2008-07-22 15:11:28 +00:00
readonly = True , states = {
' draft ' : [ ( ' readonly ' , False ) ] ,
' new ' : [ ( ' readonly ' , False ) ] }
) ,
2012-10-01 15:46:05 +00:00
' attendances_ids ' : fields . one2many ( ' hr.attendance ' , ' sheet_id ' , ' Attendances ' ) ,
2009-12-24 07:05:23 +00:00
' state ' : fields . selection ( [
( ' new ' , ' New ' ) ,
2011-09-10 12:43:55 +00:00
( ' draft ' , ' Open ' ) ,
( ' confirm ' , ' Waiting Approval ' ) ,
2012-05-04 11:57:48 +00:00
( ' done ' , ' Approved ' ) ] , ' Status ' , select = True , required = True , readonly = True ,
2012-10-12 11:42:58 +00:00
help = ' * The \' Draft \' status is used when a user is encoding a new and unconfirmed timesheet. \
\n * The \' Confirmed \' status is used for to confirm the timesheet by user. \
\n * The \' Done \' status is used when users timesheet is accepted by his/her senior. ' ) ,
2011-10-28 11:57:17 +00:00
' state_attendance ' : fields . related ( ' employee_id ' , ' state ' , type = ' selection ' , selection = [ ( ' absent ' , ' Absent ' ) , ( ' present ' , ' Present ' ) ] , string = ' Current Status ' , readonly = True ) ,
2011-11-14 12:20:42 +00:00
' total_attendance ' : fields . function ( _total , method = True , string = ' Total Attendance ' , multi = " _total " ) ,
' total_timesheet ' : fields . function ( _total , method = True , string = ' Total Timesheet ' , multi = " _total " ) ,
' total_difference ' : fields . function ( _total , method = True , string = ' Difference ' , multi = " _total " ) ,
2008-07-22 15:11:28 +00:00
' period_ids ' : fields . one2many ( ' hr_timesheet_sheet.sheet.day ' , ' sheet_id ' , ' Period ' , readonly = True ) ,
' account_ids ' : fields . one2many ( ' hr_timesheet_sheet.sheet.account ' , ' sheet_id ' , ' Analytic accounts ' , readonly = True ) ,
2009-11-10 12:49:20 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2010-02-17 08:49:38 +00:00
' department_id ' : fields . many2one ( ' hr.department ' , ' Department ' ) ,
2008-07-22 15:11:28 +00:00
}
2012-03-05 18:40:03 +00:00
def _default_date_from ( self , cr , uid , context = None ) :
2010-05-19 13:07:03 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
2008-07-22 15:11:28 +00:00
r = user . company_id and user . company_id . timesheet_range or ' month '
if r == ' month ' :
return time . strftime ( ' % Y- % m-01 ' )
elif r == ' week ' :
2011-12-08 06:28:56 +00:00
return ( datetime . today ( ) + relativedelta ( weekday = 0 , days = - 6 ) ) . strftime ( ' % Y- % m- %d ' )
2008-07-22 15:11:28 +00:00
elif r == ' year ' :
return time . strftime ( ' % Y-01-01 ' )
return time . strftime ( ' % Y- % m- %d ' )
2012-03-05 18:40:03 +00:00
def _default_date_to ( self , cr , uid , context = None ) :
2010-05-19 13:07:03 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
2008-07-22 15:11:28 +00:00
r = user . company_id and user . company_id . timesheet_range or ' month '
if r == ' month ' :
2010-10-15 14:01:53 +00:00
return ( datetime . today ( ) + relativedelta ( months = + 1 , day = 1 , days = - 1 ) ) . strftime ( ' % Y- % m- %d ' )
2008-07-22 15:11:28 +00:00
elif r == ' week ' :
2010-10-15 14:01:53 +00:00
return ( datetime . today ( ) + relativedelta ( weekday = 6 ) ) . strftime ( ' % Y- % m- %d ' )
2008-07-22 15:11:28 +00:00
elif r == ' year ' :
return time . strftime ( ' % Y-12-31 ' )
return time . strftime ( ' % Y- % m- %d ' )
2011-06-15 10:04:11 +00:00
def _default_employee ( self , cr , uid , context = None ) :
2010-10-13 12:00:39 +00:00
emp_ids = self . pool . get ( ' hr.employee ' ) . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] , context = context )
return emp_ids and emp_ids [ 0 ] or False
2011-11-09 06:25:43 +00:00
2008-07-22 15:11:28 +00:00
_defaults = {
' date_from ' : _default_date_from ,
' date_to ' : _default_date_to ,
2010-05-19 13:07:03 +00:00
' state ' : ' new ' ,
2010-10-13 12:00:39 +00:00
' employee_id ' : _default_employee ,
2010-10-06 16:39:01 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' hr_timesheet_sheet.sheet ' , context = c )
2008-07-22 15:11:28 +00:00
}
2010-11-19 13:48:01 +00:00
def _sheet_date ( self , cr , uid , ids , forced_user_id = False , context = None ) :
for sheet in self . browse ( cr , uid , ids , context = context ) :
2010-10-06 16:39:01 +00:00
new_user_id = forced_user_id or sheet . user_id and sheet . user_id . id
if new_user_id :
cr . execute ( ' SELECT id \
2008-07-22 15:11:28 +00:00
FROM hr_timesheet_sheet_sheet \
2011-11-09 06:25:43 +00:00
WHERE ( date_from < = % s and % s < = date_to ) \
2008-12-10 14:29:55 +00:00
AND user_id = % s \
2010-10-06 16:39:01 +00:00
AND id < > % s ' ,(sheet.date_to, sheet.date_from, new_user_id, sheet.id))
if cr . fetchall ( ) :
return False
2008-07-22 15:11:28 +00:00
return True
2008-08-19 09:15:24 +00:00
2008-07-22 15:11:28 +00:00
_constraints = [
2012-10-29 15:08:09 +00:00
( _sheet_date , ' You cannot have 2 timesheets that overlap! \n Please use the menu \' My Current Timesheet \' to avoid this problem. ' , [ ' date_from ' , ' date_to ' ] ) ,
2008-07-22 15:11:28 +00:00
]
def action_set_to_draft ( self , cr , uid , ids , * args ) :
self . write ( cr , uid , ids , { ' state ' : ' draft ' } )
wf_service = netsvc . LocalService ( ' workflow ' )
for id in ids :
wf_service . trg_create ( uid , self . _name , id , cr )
return True
2010-07-27 07:11:45 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2012-08-21 09:07:50 +00:00
if not ids :
2008-07-22 15:11:28 +00:00
return [ ]
2012-09-06 12:54:07 +00:00
if isinstance ( ids , ( long , int ) ) :
2012-08-17 10:47:02 +00:00
ids = [ ids ]
2012-10-26 07:57:15 +00:00
return [ ( r [ ' id ' ] , _ ( ' Week ' ) + datetime . strptime ( r [ ' date_from ' ] , ' % Y- % m- %d ' ) . strftime ( ' % U ' ) ) \
for r in self . read ( cr , uid , ids , [ ' date_from ' ] ,
2010-07-27 07:11:45 +00:00
context = context , load = ' _classic_write ' ) ]
2007-07-09 09:08:41 +00:00
2009-01-20 20:32:58 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-07-27 07:11:45 +00:00
sheets = self . read ( cr , uid , ids , [ ' state ' , ' total_attendance ' ] , context = context )
2009-12-21 13:00:04 +00:00
for sheet in sheets :
if sheet [ ' state ' ] in ( ' confirm ' , ' done ' ) :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' You cannot delete a timesheet which is already confirmed. ' ) )
2009-12-21 13:00:04 +00:00
elif sheet [ ' total_attendance ' ] < > 0.00 :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' You cannot delete a timesheet which have attendance entries. ' ) )
2009-01-20 20:32:58 +00:00
return super ( hr_timesheet_sheet , self ) . unlink ( cr , uid , ids , context = context )
2011-06-16 13:03:31 +00:00
def onchange_employee_id ( self , cr , uid , ids , employee_id , context = None ) :
2011-06-17 12:59:52 +00:00
department_id = False
2013-03-14 15:06:14 +00:00
user_id = False
2011-06-15 10:04:11 +00:00
if employee_id :
2012-12-12 15:21:26 +00:00
empl_id = self . pool . get ( ' hr.employee ' ) . browse ( cr , uid , employee_id , context = context )
department_id = empl_id . department_id . id
user_id = empl_id . user_id . id
return { ' value ' : { ' department_id ' : department_id , ' user_id ' : user_id , } }
2011-06-15 10:04:11 +00:00
2012-11-12 09:54:24 +00:00
# ------------------------------------------------
# OpenChatter methods and notifications
# ------------------------------------------------
2012-12-20 10:00:05 +00:00
2012-12-20 11:02:42 +00:00
def _needaction_domain_get ( self , cr , uid , context = None ) :
2012-11-12 09:54:24 +00:00
emp_obj = self . pool . get ( ' hr.employee ' )
empids = emp_obj . search ( cr , uid , [ ( ' parent_id.user_id ' , ' = ' , uid ) ] , context = context )
2012-12-20 10:00:05 +00:00
if not empids :
return False
2012-11-12 09:54:24 +00:00
dom = [ ' & ' , ( ' state ' , ' = ' , ' confirm ' ) , ( ' employee_id ' , ' in ' , empids ) ]
return dom
2012-12-20 10:00:05 +00:00
2007-03-05 18:22:32 +00:00
2012-10-25 14:54:43 +00:00
class account_analytic_line ( osv . osv ) :
_inherit = " account.analytic.line "
2008-07-22 15:11:28 +00:00
2010-07-27 07:11:45 +00:00
def _get_default_date ( self , cr , uid , context = None ) :
if context is None :
context = { }
2012-10-25 14:54:43 +00:00
#get the default date (should be: today)
res = super ( account_analytic_line , self ) . _get_default_date ( cr , uid , context = context )
#if we got the dates from and to from the timesheet and if the default date is in between, we use the default
#but if the default isn't included in those dates, we use the date start of the timesheet as default
if context . get ( ' timesheet_date_from ' ) and context . get ( ' timesheet_date_to ' ) :
if context [ ' timesheet_date_from ' ] < = res < = context [ ' timesheet_date_to ' ] :
return res
return context . get ( ' timesheet_date_from ' )
#if we don't get the dates from the timesheet, we return the default value from super()
return res
class hr_timesheet_line ( osv . osv ) :
_inherit = " hr.analytic.timesheet "
2008-07-22 15:11:28 +00:00
2010-07-27 07:11:45 +00:00
def _sheet ( self , cursor , user , ids , name , args , context = None ) :
2008-07-22 15:11:28 +00:00
sheet_obj = self . pool . get ( ' hr_timesheet_sheet.sheet ' )
2011-11-14 12:20:42 +00:00
res = { } . fromkeys ( ids , False )
for ts_line in self . browse ( cursor , user , ids , context = context ) :
sheet_ids = sheet_obj . search ( cursor , user ,
2012-01-06 09:25:20 +00:00
[ ( ' date_to ' , ' >= ' , ts_line . date ) , ( ' date_from ' , ' <= ' , ts_line . date ) ,
( ' employee_id.user_id ' , ' = ' , ts_line . user_id . id ) ] ,
context = context )
2011-11-14 12:20:42 +00:00
if sheet_ids :
# [0] because only one sheet possible for an employee between 2 dates
res [ ts_line . id ] = sheet_obj . name_get ( cursor , user , sheet_ids , context = context ) [ 0 ]
2008-07-22 15:11:28 +00:00
return res
2011-11-14 12:20:42 +00:00
def _get_hr_timesheet_sheet ( self , cr , uid , ids , context = None ) :
ts_line_ids = [ ]
for ts in self . browse ( cr , uid , ids , context = context ) :
cr . execute ( """
SELECT l . id
FROM hr_analytic_timesheet l
INNER JOIN account_analytic_line al
ON ( l . line_id = al . id )
WHERE % ( date_to ) s > = al . date
AND % ( date_from ) s < = al . date
AND % ( user_id ) s = al . user_id
GROUP BY l . id """ , { ' date_from ' : ts.date_from,
' date_to ' : ts . date_to ,
' user_id ' : ts . employee_id . user_id . id , } )
ts_line_ids . extend ( [ row [ 0 ] for row in cr . fetchall ( ) ] )
return ts_line_ids
def _get_account_analytic_line ( self , cr , uid , ids , context = None ) :
ts_line_ids = self . pool . get ( ' hr.analytic.timesheet ' ) . search ( cr , uid , [ ( ' line_id ' , ' in ' , ids ) ] )
return ts_line_ids
2008-07-22 15:11:28 +00:00
_columns = {
2013-05-08 08:59:38 +00:00
' sheet_id ' : fields . function ( _sheet , string = ' Sheet ' , select = " 1 " ,
2012-10-02 12:11:03 +00:00
type = ' many2one ' , relation = ' hr_timesheet_sheet.sheet ' , ondelete = " cascade " ,
2011-11-14 12:20:42 +00:00
store = {
' hr_timesheet_sheet.sheet ' : ( _get_hr_timesheet_sheet , [ ' employee_id ' , ' date_from ' , ' date_to ' ] , 10 ) ,
' account.analytic.line ' : ( _get_account_analytic_line , [ ' user_id ' , ' date ' ] , 10 ) ,
2012-03-13 15:03:47 +00:00
' hr.analytic.timesheet ' : ( lambda self , cr , uid , ids , context = None : ids , None , 10 ) ,
2011-11-14 12:20:42 +00:00
} ,
) ,
2008-07-22 15:11:28 +00:00
}
2011-04-15 08:49:06 +00:00
def _check_sheet_state ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
2011-04-15 09:14:28 +00:00
for timesheet_line in self . browse ( cr , uid , ids , context = context ) :
if timesheet_line . sheet_id and timesheet_line . sheet_id . state not in ( ' draft ' , ' new ' ) :
2011-04-15 08:49:06 +00:00
return False
return True
_constraints = [
2012-07-25 12:13:54 +00:00
( _check_sheet_state , ' You cannot modify an entry in a Confirmed/Done timesheet ! ' , [ ' state ' ] ) ,
2011-04-15 08:49:06 +00:00
]
2008-07-22 15:11:28 +00:00
def unlink ( self , cr , uid , ids , * args , * * kwargs ) :
2011-07-01 09:45:10 +00:00
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2008-07-22 15:11:28 +00:00
self . _check ( cr , uid , ids )
return super ( hr_timesheet_line , self ) . unlink ( cr , uid , ids , * args , * * kwargs )
def _check ( self , cr , uid , ids ) :
for att in self . browse ( cr , uid , ids ) :
if att . sheet_id and att . sheet_id . state not in ( ' draft ' , ' new ' ) :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot modify an entry in a confirmed timesheet. ' ) )
2008-07-22 15:11:28 +00:00
return True
2007-08-30 14:22:22 +00:00
2012-10-04 13:48:58 +00:00
def multi_on_change_account_id ( self , cr , uid , ids , account_ids , context = None ) :
2012-10-21 19:59:25 +00:00
return dict ( [ ( el , self . on_change_account_id ( cr , uid , ids , el , context . get ( ' user_id ' , uid ) ) ) for el in account_ids ] )
2012-10-04 13:48:58 +00:00
2007-03-05 18:22:32 +00:00
hr_timesheet_line ( )
class hr_attendance ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = " hr.attendance "
2010-07-27 07:11:45 +00:00
def _get_default_date ( self , cr , uid , context = None ) :
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
if ' name ' in context :
return context [ ' name ' ] + time . strftime ( ' % H: % M: % S ' )
return time . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2011-11-14 12:20:42 +00:00
def _get_hr_timesheet_sheet ( self , cr , uid , ids , context = None ) :
attendance_ids = [ ]
for ts in self . browse ( cr , uid , ids , context = context ) :
cr . execute ( """
SELECT a . id
FROM hr_attendance a
INNER JOIN hr_employee e
INNER JOIN resource_resource r
ON ( e . resource_id = r . id )
ON ( a . employee_id = e . id )
WHERE % ( date_to ) s > = date_trunc ( ' day ' , a . name )
AND % ( date_from ) s < = a . name
AND % ( user_id ) s = r . user_id
GROUP BY a . id """ , { ' date_from ' : ts.date_from,
' date_to ' : ts . date_to ,
' user_id ' : ts . employee_id . user_id . id , } )
attendance_ids . extend ( [ row [ 0 ] for row in cr . fetchall ( ) ] )
return attendance_ids
2010-07-27 07:11:45 +00:00
def _sheet ( self , cursor , user , ids , name , args , context = None ) :
2008-07-22 15:11:28 +00:00
sheet_obj = self . pool . get ( ' hr_timesheet_sheet.sheet ' )
2011-11-14 12:20:42 +00:00
res = { } . fromkeys ( ids , False )
for attendance in self . browse ( cursor , user , ids , context = context ) :
2013-07-23 18:43:41 +00:00
# Simulate timesheet in employee timezone
2013-09-09 10:37:49 +00:00
att_tz = timezone ( attendance . employee_id . user_id . partner_id . tz or ' utc ' )
2013-07-23 18:43:41 +00:00
attendance_dt = datetime . strptime ( attendance . name , DEFAULT_SERVER_DATETIME_FORMAT )
att_tz_dt = pytz . utc . localize ( attendance_dt )
att_tz_dt = att_tz_dt . astimezone ( att_tz )
2013-09-09 10:37:24 +00:00
# We take only the date omiting the hours as we compare with timesheet
# date_from which is a date format thus using hours would lead to
# be out of scope of timesheet
att_tz_date_str = datetime . strftime ( att_tz_dt , DEFAULT_SERVER_DATE_FORMAT )
2011-11-14 12:20:42 +00:00
sheet_ids = sheet_obj . search ( cursor , user ,
2013-09-09 10:37:24 +00:00
[ ( ' date_from ' , ' <= ' , att_tz_date_str ) ,
( ' date_to ' , ' >= ' , att_tz_date_str ) ,
2012-01-06 09:25:20 +00:00
( ' employee_id ' , ' = ' , attendance . employee_id . id ) ] ,
context = context )
2011-11-14 12:20:42 +00:00
if sheet_ids :
# [0] because only one sheet possible for an employee between 2 dates
res [ attendance . id ] = sheet_obj . name_get ( cursor , user , sheet_ids , context = context ) [ 0 ]
2008-07-22 15:11:28 +00:00
return res
_columns = {
2011-07-01 23:41:24 +00:00
' sheet_id ' : fields . function ( _sheet , string = ' Sheet ' ,
2008-07-22 15:11:28 +00:00
type = ' many2one ' , relation = ' hr_timesheet_sheet.sheet ' ,
2011-11-14 12:20:42 +00:00
store = {
' hr_timesheet_sheet.sheet ' : ( _get_hr_timesheet_sheet , [ ' employee_id ' , ' date_from ' , ' date_to ' ] , 10 ) ,
2012-01-06 09:25:20 +00:00
' hr.attendance ' : ( lambda self , cr , uid , ids , context = None : ids , [ ' employee_id ' , ' name ' , ' day ' ] , 10 ) ,
2011-11-14 12:20:42 +00:00
} ,
)
2008-07-22 15:11:28 +00:00
}
_defaults = {
' name ' : _get_default_date ,
}
2010-07-21 12:39:48 +00:00
def create ( self , cr , uid , vals , context = None ) :
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
if ' sheet_id ' in context :
2010-07-27 07:11:45 +00:00
ts = self . pool . get ( ' hr_timesheet_sheet.sheet ' ) . browse ( cr , uid , context [ ' sheet_id ' ] , context = context )
2008-07-22 15:11:28 +00:00
if ts . state not in ( ' draft ' , ' new ' ) :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot modify an entry in a confirmed timesheet. ' ) )
2008-07-22 15:11:28 +00:00
res = super ( hr_attendance , self ) . create ( cr , uid , vals , context = context )
if ' sheet_id ' in context :
2009-03-31 11:32:53 +00:00
if context [ ' sheet_id ' ] != self . browse ( cr , uid , res , context = context ) . sheet_id . id :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' User Error! ' ) , _ ( ' You cannot enter an attendance ' \
' date outside the current timesheet dates. ' ) )
2008-07-22 15:11:28 +00:00
return res
def unlink ( self , cr , uid , ids , * args , * * kwargs ) :
2011-07-01 09:45:10 +00:00
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2008-07-22 15:11:28 +00:00
self . _check ( cr , uid , ids )
return super ( hr_attendance , self ) . unlink ( cr , uid , ids , * args , * * kwargs )
2010-07-27 07:11:45 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2010-07-21 12:39:48 +00:00
if context is None :
context = { }
2011-07-01 09:45:10 +00:00
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2008-07-22 15:11:28 +00:00
self . _check ( cr , uid , ids )
res = super ( hr_attendance , self ) . write ( cr , uid , ids , vals , context = context )
if ' sheet_id ' in context :
for attendance in self . browse ( cr , uid , ids , context = context ) :
if context [ ' sheet_id ' ] != attendance . sheet_id . id :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' User Error! ' ) , _ ( ' You cannot enter an attendance ' \
' date outside the current timesheet dates. ' ) )
2008-07-22 15:11:28 +00:00
return res
def _check ( self , cr , uid , ids ) :
for att in self . browse ( cr , uid , ids ) :
if att . sheet_id and att . sheet_id . state not in ( ' draft ' , ' new ' ) :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot modify an entry in a confirmed timesheet ' ) )
2008-07-22 15:11:28 +00:00
return True
2007-08-30 14:22:22 +00:00
2007-03-05 18:22:32 +00:00
hr_attendance ( )
class hr_timesheet_sheet_sheet_day ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " hr_timesheet_sheet.sheet.day "
2010-05-19 18:32:32 +00:00
_description = " Timesheets by Period "
2008-07-22 15:11:28 +00:00
_auto = False
_order = ' name '
_columns = {
' name ' : fields . date ( ' Date ' , readonly = True ) ,
' sheet_id ' : fields . many2one ( ' hr_timesheet_sheet.sheet ' , ' Sheet ' , readonly = True , select = " 1 " ) ,
2011-01-06 05:35:21 +00:00
' total_timesheet ' : fields . float ( ' Total Timesheet ' , readonly = True ) ,
2008-07-22 15:11:28 +00:00
' total_attendance ' : fields . float ( ' Attendance ' , readonly = True ) ,
' total_difference ' : fields . float ( ' Difference ' , readonly = True ) ,
}
def init ( self , cr ) :
cr . execute ( """ create or replace view hr_timesheet_sheet_sheet_day as
SELECT
id ,
name ,
sheet_id ,
total_timesheet ,
total_attendance ,
2009-09-22 05:19:11 +00:00
cast ( round ( cast ( total_attendance - total_timesheet as Numeric ) , 2 ) as Double Precision ) AS total_difference
2008-07-22 15:11:28 +00:00
FROM
( (
SELECT
MAX ( id ) as id ,
name ,
sheet_id ,
SUM ( total_timesheet ) as total_timesheet ,
CASE WHEN SUM ( total_attendance ) < 0
THEN ( SUM ( total_attendance ) +
CASE WHEN current_date < > name
THEN 1440
2013-05-22 12:53:50 +00:00
ELSE ( EXTRACT ( hour FROM current_time AT TIME ZONE ' UTC ' ) * 60 ) + EXTRACT ( minute FROM current_time AT TIME ZONE ' UTC ' )
2008-07-22 15:11:28 +00:00
END
)
ELSE SUM ( total_attendance )
END / 60 as total_attendance
FROM
( (
select
min ( hrt . id ) as id ,
l . date : : date as name ,
s . id as sheet_id ,
sum ( l . unit_amount ) as total_timesheet ,
0.0 as total_attendance
from
hr_analytic_timesheet hrt
2013-05-08 08:59:38 +00:00
JOIN account_analytic_line l ON l . id = hrt . line_id
LEFT JOIN hr_timesheet_sheet_sheet s ON s . id = hrt . sheet_id
2008-07-22 15:11:28 +00:00
group by l . date : : date , s . id
) union (
select
- min ( a . id ) as id ,
a . name : : date as name ,
s . id as sheet_id ,
0.0 as total_timesheet ,
2013-09-09 10:38:19 +00:00
SUM ( ( ( EXTRACT ( hour FROM a . name ) * 60 ) + EXTRACT ( minute FROM a . name ) ) * ( CASE WHEN a . action = ' sign_in ' THEN - 1 ELSE 1 END ) ) as total_attendance
2008-07-22 15:11:28 +00:00
from
hr_attendance a
2013-05-08 08:59:38 +00:00
LEFT JOIN hr_timesheet_sheet_sheet s
ON s . id = a . sheet_id
2008-07-22 15:11:28 +00:00
WHERE action in ( ' sign_in ' , ' sign_out ' )
group by a . name : : date , s . id
) ) AS foo
GROUP BY name , sheet_id
) ) AS bar """ )
2007-08-30 14:22:22 +00:00
2007-03-05 18:22:32 +00:00
hr_timesheet_sheet_sheet_day ( )
2007-03-06 09:55:17 +00:00
class hr_timesheet_sheet_sheet_account ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " hr_timesheet_sheet.sheet.account "
2010-05-19 18:32:32 +00:00
_description = " Timesheets by Period "
2008-07-22 15:11:28 +00:00
_auto = False
_order = ' name '
_columns = {
2011-09-10 12:43:55 +00:00
' name ' : fields . many2one ( ' account.analytic.account ' , ' Project / Analytic Account ' , readonly = True ) ,
2008-07-22 15:11:28 +00:00
' sheet_id ' : fields . many2one ( ' hr_timesheet_sheet.sheet ' , ' Sheet ' , readonly = True ) ,
' total ' : fields . float ( ' Total Time ' , digits = ( 16 , 2 ) , readonly = True ) ,
' invoice_rate ' : fields . many2one ( ' hr_timesheet_invoice.factor ' , ' Invoice rate ' , readonly = True ) ,
2008-12-11 11:50:52 +00:00
}
2008-07-22 15:11:28 +00:00
def init ( self , cr ) :
cr . execute ( """ create or replace view hr_timesheet_sheet_sheet_account as (
select
min ( hrt . id ) as id ,
l . account_id as name ,
s . id as sheet_id ,
sum ( l . unit_amount ) as total ,
l . to_invoice as invoice_rate
from
hr_analytic_timesheet hrt
left join ( account_analytic_line l
LEFT JOIN hr_timesheet_sheet_sheet s
ON ( s . date_to > = l . date
AND s . date_from < = l . date
AND s . user_id = l . user_id ) )
on ( l . id = hrt . line_id )
group by l . account_id , s . id , l . to_invoice
) """ )
2007-08-30 14:22:22 +00:00
2007-03-06 09:55:17 +00:00
hr_timesheet_sheet_sheet_account ( )
2007-08-30 14:22:22 +00:00
2008-09-07 23:24:39 +00:00
2007-03-06 09:55:17 +00:00
class res_company ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = ' res.company '
_columns = {
2008-09-07 23:24:39 +00:00
' timesheet_range ' : fields . selection (
2011-10-01 18:46:25 +00:00
[ ( ' day ' , ' Day ' ) , ( ' week ' , ' Week ' ) , ( ' month ' , ' Month ' ) ] , ' Timesheet range ' ,
help = " Periodicity on which you validate your timesheets. " ) ,
2010-07-13 13:38:02 +00:00
' timesheet_max_difference ' : fields . float ( ' Timesheet allowed difference(Hours) ' ,
help = " Allowed difference in hours between the sign in/out and the timesheet " \
2008-09-07 23:24:39 +00:00
" computation for one sheet. Set this to 0 if you do not want any control. " ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-09-16 05:59:56 +00:00
' timesheet_range ' : lambda * args : ' week ' ,
2008-07-22 15:11:28 +00:00
' timesheet_max_difference ' : lambda * args : 0.0
}
2007-08-30 14:22:22 +00:00
2007-03-06 09:55:17 +00:00
res_company ( )
2008-07-23 14:41:47 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: