53cbb004872682e26f8be415b1013f20974675a5
[odoo/odoo.git] / addons / hr_attendance / report / timesheet.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22
23 from datetime import datetime
24 from dateutil.relativedelta import relativedelta
25
26 import pooler
27 from report.interface import report_rml
28 from report.interface import toxml
29 import tools
30
31 one_week = relativedelta(days=7)
32 num2day = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
33
34 def to_hour(h):
35     return int(h), int(round((h - int(h)) * 60, 0))
36
37 class report_custom(report_rml):
38
39     def create_xml(self, cr, uid, ids, datas, context=None):
40         obj_emp = pooler.get_pool(cr.dbname).get('hr.employee')
41
42         start_date = datetime.strptime(datas['form']['init_date'], '%Y-%m-%d')
43         end_date = datetime.strptime(datas['form']['end_date'], '%Y-%m-%d')
44         first_monday = start_date - relativedelta(days=start_date.date().weekday())
45         last_monday = end_date + relativedelta(days=7 - end_date.date().weekday())
46
47         if last_monday < first_monday:
48             first_monday, last_monday = last_monday, first_monday
49
50         user_xml = []
51
52         for employee_id in ids:
53             emp = obj_emp.read(cr, uid, [employee_id], ['id', 'name'])[0]
54             monday, n_monday = first_monday, first_monday + one_week
55             stop, week_xml = False, []
56             user_repr = '''
57             <user>
58               <name>%s</name>
59               %%s
60             </user>
61             ''' % tools.ustr(toxml(emp['name']))
62             while monday != last_monday:
63                 #### Work hour calculation
64                 sql = '''
65                 select action, att.name
66                 from hr_employee as emp inner join hr_attendance as att
67                      on emp.id = att.employee_id
68                 where att.name between %s and %s and emp.id = %s
69                 order by att.name
70                 '''
71                 for idx in range(7):
72                     cr.execute(sql, (monday.strftime('%Y-%m-%d %H:%M:%S'), (monday + relativedelta(days=idx+1)).strftime('%Y-%m-%d %H:%M:%S'), employee_id))
73                     attendances = cr.dictfetchall()
74                     week_wh = {}
75                     # Fake sign ins/outs at week ends, to take attendances across week ends into account
76                     # XXX this is wrong for the first sign-in ever and the last sign out to this date
77                     if attendances and attendances[0]['action'] == 'sign_out':
78                         attendances.insert(0, {'name': monday.strftime('%Y-%m-%d %H:%M:%S'), 'action': 'sign_in'})
79                     if attendances and attendances[-1]['action'] == 'sign_in':
80                         attendances.append({'name': n_monday.strftime('%Y-%m-%d %H:%M:%S'), 'action': 'sign_out'})
81                     # sum up the attendances' durations
82                     ldt = None
83                     for att in attendances:
84                         dt = datetime.strptime(att['name'], '%Y-%m-%d %H:%M:%S')
85                         if ldt and att['action'] == 'sign_out':
86                             week_wh[ldt.date().weekday()] = week_wh.get(ldt.date().weekday(), 0) + (float((dt - ldt).seconds)/3600)
87                         else:
88                             ldt = dt
89
90                 # Week xml representation
91                 week_repr = ['<week>', '<weekstart>%s</weekstart>' % monday.strftime('%Y-%m-%d'), '<weekend>%s</weekend>' % n_monday.strftime('%Y-%m-%d')]
92                 for idx in range(7):
93                     week_repr.append('<%s>' % num2day[idx])
94                     if idx in week_wh:
95                         week_repr.append('<workhours>%sh%02d</workhours>' % to_hour(week_wh[idx]))
96                     week_repr.append('</%s>' % num2day[idx])
97                 week_repr.append('<total>')
98                 week_repr.append('<worked>%sh%02d</worked>' % to_hour(reduce(lambda x,y:x+y, week_wh.values(), 0)))
99                 week_repr.append('</total>')
100                 week_repr.append('</week>')
101                 if len(week_repr) > 21: # 21 = minimal length of week_repr
102                     week_xml.append('\n'.join(week_repr))
103
104                 monday, n_monday = n_monday, n_monday + one_week
105             user_xml.append(user_repr % '\n'.join(week_xml))
106
107         xml = '''<?xml version="1.0" encoding="UTF-8" ?>
108         <report>
109         %s
110         </report>
111         ''' % '\n'.join(user_xml)
112         return self.post_process_xml_data(cr, uid, xml, context)
113
114 report_custom('report.hr.attendance.allweeks', 'hr.employee', '', 'addons/hr_attendance/report/timesheet.xsl')
115 # vim:noexpandtab:tw=0