Merge lp:~zaber/openobject-addons/timesheet-tz into lp:openobject-addons/6.1

Proposed by Don Kirkby
Status: Needs review
Proposed branch: lp:~zaber/openobject-addons/timesheet-tz
Merge into: lp:openobject-addons/6.1
Diff against target: 275 lines (+69/-37)
4 files modified
hr_attendance/hr_attendance.py (+7/-1)
hr_attendance/wizard/hr_attendance_sign_in_out.py (+2/-2)
hr_timesheet/wizard/hr_timesheet_sign_in_out.py (+3/-3)
hr_timesheet_sheet/hr_timesheet_sheet.py (+57/-31)
To merge this branch: bzr merge lp:~zaber/openobject-addons/timesheet-tz
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+141836@code.launchpad.net

Description of the change

This fixes bug lp:943091 in 6.1. There were several timezone problems, so I tried to fix each one in a separate commit.
There could still be problems if two users from different timezones edit the same timesheet, but the current 6.1 timesheet modules are unusable because of timezone problems, so I think this is a big improvement.

To post a comment you must log in.
7122. By Don Kirkby

[FIX] Use time zone for signing in and out of timesheets as part of bug lp:943091. Change hr_attendance.day field to use local date instead of UTC date.

7123. By Don Kirkby

[FIX] Use local time in total attendance calculation on timesheet as part of bug lp:943091.

7124. By Don Kirkby

[FIX] Store hours on hr_attendance because doing the timezone calculation in SQL was messy. Fixes timesheet by day view.

Unmerged revisions

7125. By Don Kirkby

[FIX] Use timezone in Sign in / sign out by project feature as part of bug lp:943091.

7124. By Don Kirkby

[FIX] Store hours on hr_attendance because doing the timezone calculation in SQL was messy. Fixes timesheet by day view.

7123. By Don Kirkby

[FIX] Use local time in total attendance calculation on timesheet as part of bug lp:943091.

7122. By Don Kirkby

[FIX] Use time zone for signing in and out of timesheets as part of bug lp:943091. Change hr_attendance.day field to use local date instead of UTC date.

7121. By Don Kirkby

[FIX] fix today button to use time zone as part of bug lp:943091.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hr_attendance/hr_attendance.py'
2--- hr_attendance/hr_attendance.py 2012-01-31 13:36:57 +0000
3+++ hr_attendance/hr_attendance.py 2013-01-03 23:59:21 +0000
4@@ -20,6 +20,7 @@
5 ##############################################################################
6
7 import time
8+from datetime import datetime
9
10 from osv import fields, osv
11 from tools.translate import _
12@@ -48,7 +49,12 @@
13 def _day_compute(self, cr, uid, ids, fieldnames, args, context=None):
14 res = dict.fromkeys(ids, '')
15 for obj in self.browse(cr, uid, ids, context=context):
16- res[obj.id] = time.strftime('%Y-%m-%d', time.strptime(obj.name, '%Y-%m-%d %H:%M:%S'))
17+ timestamp = datetime.strptime(obj.name, '%Y-%m-%d %H:%M:%S')
18+ res[obj.id] = fields.date.context_today(self,
19+ cr,
20+ uid,
21+ context=context,
22+ timestamp=timestamp)
23 return res
24
25 _columns = {
26
27=== modified file 'hr_attendance/wizard/hr_attendance_sign_in_out.py'
28--- hr_attendance/wizard/hr_attendance_sign_in_out.py 2011-11-09 18:12:56 +0000
29+++ hr_attendance/wizard/hr_attendance_sign_in_out.py 2013-01-03 23:59:21 +0000
30@@ -162,7 +162,7 @@
31 self.pool.get('hr.attendance').create(cr, uid, {'name': data['last_time'], 'action': 'sign_out',
32 'employee_id': emp_id}, context=context)
33 try:
34- self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_in')
35+ self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_in', context=context)
36 except:
37 raise osv.except_osv(_('UserError'), _('A sign-in must be right after a sign-out !'))
38 return {'type': 'ir.actions.act_window_close'} # To do: Return Success message
39@@ -174,7 +174,7 @@
40 raise osv.except_osv(_('UserError'), _('The Sign-in date must be in the past'))
41 self.pool.get('hr.attendance').create(cr, uid, {'name':data['last_time'], 'action':'sign_in', 'employee_id':emp_id}, context=context)
42 try:
43- self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_out')
44+ self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_out', context=context)
45 except:
46 raise osv.except_osv(_('UserError'), _('A sign-out must be right after a sign-in !'))
47 return {'type': 'ir.actions.act_window_close'} # To do: Return Success message
48
49=== modified file 'hr_timesheet/wizard/hr_timesheet_sign_in_out.py'
50--- hr_timesheet/wizard/hr_timesheet_sign_in_out.py 2011-12-21 10:52:14 +0000
51+++ hr_timesheet/wizard/hr_timesheet_sign_in_out.py 2013-01-03 23:59:21 +0000
52@@ -91,7 +91,7 @@
53 emp_obj = self.pool.get('hr.employee')
54 for data in self.browse(cr, uid, ids, context=context):
55 emp_id = data.emp_id.id
56- emp_obj.attendance_action_change(cr, uid, [emp_id], type='sign_out', dt=data.date)
57+ emp_obj.attendance_action_change(cr, uid, [emp_id], type='sign_out', dt=data.date, context=context)
58 self._write(cr, uid, data, emp_id, context=context)
59 return {'type': 'ir.actions.act_window_close'}
60
61@@ -99,7 +99,7 @@
62 emp_obj = self.pool.get('hr.employee')
63 for data in self.browse(cr, uid, ids, context=context):
64 emp_id = data.emp_id.id
65- emp_obj.attendance_action_change(cr, uid, [emp_id], type='action', dt=data.date)
66+ emp_obj.attendance_action_change(cr, uid, [emp_id], type='action', dt=data.date, context=context)
67 self._write(cr, uid, data, emp_id, context=context)
68 return {'type': 'ir.actions.act_window_close'}
69
70@@ -156,7 +156,7 @@
71 emp_obj = self.pool.get('hr.employee')
72 for data in self.browse(cr, uid, ids, context=context):
73 emp_id = data.emp_id.id
74- emp_obj.attendance_action_change(cr, uid, [emp_id], type = 'sign_in' ,dt=data.date or False)
75+ emp_obj.attendance_action_change(cr, uid, [emp_id], type = 'sign_in' , context=context, dt=data.date or False)
76 return {'type': 'ir.actions.act_window_close'}
77
78 def default_get(self, cr, uid, fields_list, context=None):
79
80=== modified file 'hr_timesheet_sheet/hr_timesheet_sheet.py'
81--- hr_timesheet_sheet/hr_timesheet_sheet.py 2012-08-31 10:45:03 +0000
82+++ hr_timesheet_sheet/hr_timesheet_sheet.py 2013-01-03 23:59:21 +0000
83@@ -46,8 +46,8 @@
84 dom.insert(0 ,'|')
85 dom.append('&')
86 dom.append('&')
87- dom.append(('name', '>=', res6[id]))
88- dom.append(('name', '<=', res6[id]))
89+ dom.append(('day', '>=', res6[id]))
90+ dom.append(('day', '<=', res6[id]))
91 dom.append(('sheet_id', '=', id))
92
93 ids2 = obj.pool.get(self._obj).search(cr, user, dom, limit=self._limit)
94@@ -118,6 +118,10 @@
95 """
96 context = context or {}
97 attendance_obj = self.pool.get('hr.attendance')
98+ now = fields.datetime.context_timestamp(cr,
99+ uid,
100+ datetime.now(),
101+ context=context)
102 res = {}
103 for sheet_id in ids:
104 sheet = self.browse(cr, uid, sheet_id, context=context)
105@@ -129,25 +133,17 @@
106 total_attendance = {}
107 for attendance in [att for att in attendances
108 if att.action in ('sign_in', 'sign_out')]:
109- day = attendance.name[:10]
110+ day = attendance.day
111 if not total_attendance.get(day, False):
112 total_attendance[day] = timedelta(seconds=0)
113
114- attendance_in_time = datetime.strptime(attendance.name, '%Y-%m-%d %H:%M:%S')
115- attendance_interval = timedelta(hours=attendance_in_time.hour,
116- minutes=attendance_in_time.minute,
117- seconds=attendance_in_time.second)
118- if attendance.action == 'sign_in':
119- total_attendance[day] -= attendance_interval
120- else:
121- total_attendance[day] += attendance_interval
122+ total_attendance[day] += timedelta(hours=attendance.daily_hours)
123
124 # if the delta is negative, it means that a sign out is missing
125 # in a such case, we want to have the time to the end of the day
126 # for a past date, and the time to now for the current date
127 if total_attendance[day] < timedelta(0):
128 if day == date_current:
129- now = datetime.now()
130 total_attendance[day] += timedelta(hours=now.hour,
131 minutes=now.minute,
132 seconds=now.second)
133@@ -275,13 +271,15 @@
134 return True
135
136 def date_today(self, cr, uid, ids, context=None):
137+ today = fields.date.context_today(self, cr, uid, context=context)
138+
139 for sheet in self.browse(cr, uid, ids, context=context):
140- if datetime.today() <= datetime.strptime(sheet.date_from, '%Y-%m-%d'):
141- self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context)
142- elif datetime.now() >= datetime.strptime(sheet.date_to, '%Y-%m-%d'):
143- self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context)
144+ if today <= sheet.date_from:
145+ self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from}, context=context)
146+ elif today >= sheet.date_to:
147+ self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to}, context=context)
148 else:
149- self.write(cr, uid, [sheet.id], {'date_current': time.strftime('%Y-%m-%d')}, context=context)
150+ self.write(cr, uid, [sheet.id], {'date_current': today}, context=context)
151 return True
152
153 def date_previous(self, cr, uid, ids, context=None):
154@@ -314,7 +312,8 @@
155
156 def check_sign(self, cr, uid, ids, typ, context=None):
157 sheet = self.browse(cr, uid, ids, context=context)[0]
158- if not sheet.date_current == time.strftime('%Y-%m-%d'):
159+ today = fields.date.context_today(self, cr, uid, context=context)
160+ if not sheet.date_current == today:
161 raise osv.except_osv(_('Error !'), _('You cannot sign in/sign out from an other date than today'))
162 return True
163
164@@ -477,7 +476,7 @@
165 context = {}
166 if 'date' in context:
167 return context['date']
168- return time.strftime('%Y-%m-%d')
169+ return fields.date.context_today(self, cr, uid, context=context)
170
171 def _sheet(self, cursor, user, ids, name, args, context=None):
172 sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
173@@ -519,7 +518,7 @@
174 store={
175 'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10),
176 'account.analytic.line': (_get_account_analytic_line, ['user_id', 'date'], 10),
177- 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, ['line_id'], 10),
178+ 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
179 },
180 ),
181 }
182@@ -573,8 +572,8 @@
183 INNER JOIN resource_resource r
184 ON (e.resource_id = r.id)
185 ON (a.employee_id = e.id)
186- WHERE %(date_to)s >= date_trunc('day', a.name)
187- AND %(date_from)s <= a.name
188+ WHERE %(date_to)s >= a.day
189+ AND %(date_from)s <= a.day
190 AND %(user_id)s = r.user_id
191 GROUP BY a.id""", {'date_from': ts.date_from,
192 'date_to': ts.date_to,
193@@ -586,16 +585,38 @@
194 sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
195 res = {}.fromkeys(ids, False)
196 for attendance in self.browse(cursor, user, ids, context=context):
197- date_to = datetime.strftime(datetime.strptime(attendance.name[0:10], '%Y-%m-%d'), '%Y-%m-%d %H:%M:%S')
198+ timestamp = datetime.strptime(attendance.name, '%Y-%m-%d %H:%M:%S')
199+ day = fields.date.context_today(self,
200+ cursor,
201+ user,
202+ context=context,
203+ timestamp=timestamp)
204 sheet_ids = sheet_obj.search(cursor, user,
205- [('date_to', '>=', date_to), ('date_from', '<=', attendance.name),
206+ [('date_to', '>=', day), ('date_from', '<=', day),
207 ('employee_id', '=', attendance.employee_id.id)],
208 context=context)
209 if sheet_ids:
210 # [0] because only one sheet possible for an employee between 2 dates
211 res[attendance.id] = sheet_obj.name_get(cursor, user, sheet_ids, context=context)[0]
212 return res
213-
214+
215+ def _daily_hours(self, cr, uid, ids, name, args, context=None):
216+ res = {}.fromkeys(ids, False)
217+ for attendance in self.browse(cr, uid, ids, context=context):
218+ day = attendance.day
219+ attendance_in_time = fields.datetime.context_timestamp(
220+ cr,
221+ uid,
222+ datetime.strptime(attendance.name, '%Y-%m-%d %H:%M:%S'),
223+ context=context)
224+ hours = (attendance_in_time.hour +
225+ attendance_in_time.minute / 60.0 +
226+ attendance_in_time.second / 3600.0)
227+ if attendance.action == 'sign_in':
228+ hours *= -1
229+ res[attendance.id] = hours
230+ return res
231+
232 _columns = {
233 'sheet_id': fields.function(_sheet, string='Sheet',
234 type='many2one', relation='hr_timesheet_sheet.sheet',
235@@ -603,7 +624,12 @@
236 'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10),
237 'hr.attendance': (lambda self,cr,uid,ids,context=None: ids, ['employee_id', 'name', 'day'], 10),
238 },
239- )
240+ ),
241+ 'daily_hours': fields.function(_daily_hours,
242+ type='float',
243+ string='Daily Hours',
244+ digits=(16,2),
245+ store=True)
246 }
247 _defaults = {
248 'name': _get_default_date,
249@@ -709,10 +735,10 @@
250 ) union (
251 select
252 -min(a.id) as id,
253- a.name::date as name,
254+ a.day::date as name,
255 s.id as sheet_id,
256 0.0 as total_timesheet,
257- 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
258+ SUM(a.daily_hours)*60 as total_attendance
259 from
260 hr_attendance a
261 LEFT JOIN (hr_timesheet_sheet_sheet s
262@@ -721,10 +747,10 @@
263 ON (e.resource_id = r.id)
264 ON (s.user_id = r.user_id))
265 ON (a.employee_id = e.id
266- AND s.date_to >= date_trunc('day',a.name)
267- AND s.date_from <= a.name)
268+ AND s.date_to >= a.day::date
269+ AND s.date_from <= a.day::date)
270 WHERE action in ('sign_in', 'sign_out')
271- group by a.name::date, s.id
272+ group by a.day::date, s.id
273 )) AS foo
274 GROUP BY name, sheet_id
275 )) AS bar""")