Moved the attendance reports and wizard from the "hr" module to "hr_attendence".

Pulled back the weekly attendance report from the 4.2 branch
Removed theorical workhours and holiday workhours from the reports to make sure they can be printed even if only "hr" and "hr_attendance" are installed

bzr revid: ls@numerigraphe.fr-20090202182543-km4x5yp27d74ksk8
This commit is contained in:
Lionel Sausin
2009-02-02 19:25:43 +01:00
parent 51823787ce
commit 5780d1032b
15 changed files with 109 additions and 503 deletions
-1
View File
@@ -43,7 +43,6 @@
'security/hr_security.xml',
'security/ir.model.access.csv',
'hr_view.xml',
'hr_wizard.xml',
'hr_department_view.xml',
'process/hr_process.xml'
],
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- <wizard id="print_week" keyword="client_print_multi" model="hr.employee" name="hr.print_week" string="Print Timesheet by week"/>
<wizard id="print_month" keyword="client_print_multi" model="hr.employee" name="hr.print_month" string="Print Timesheet by month"/> -->
</data>
</openerp>
-26
View File
@@ -1,26 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import bymonth
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-163
View File
@@ -1,163 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from mx import DateTime
from mx.DateTime import now
import time
import netsvc
import pooler
from report.interface import report_rml
from report.interface import toxml
one_day = DateTime.RelativeDateTime(days=1)
month2name = [0,'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
def hour2str(h):
hours = int(h)
minutes = int(round((h - hours) * 60, 0))
return '%02dh%02d' % (hours, minutes)
class report_custom(report_rml):
def create_xml(self, cr, uid, ids, datas, context):
service = netsvc.LocalService('object_proxy')
month = DateTime.DateTime(datas['form']['year'], datas['form']['month'], 1)
user_xml = ['<month>%s</month>' % month2name[month.month], '<year>%s</year>' % month.year]
# Public holidays
jf_sql = """select hol.date_from, hol.date_to from hr_holidays as hol, hr_holidays_status as stat
where hol.holiday_status = stat.id and stat.name = 'Public holidays' """
cr.execute(jf_sql)
jfs = []
jfs = [(DateTime.strptime(l['date_from'], '%Y-%m-%d %H:%M:%S'), DateTime.strptime(l['date_to'], '%Y-%m-%d %H:%M:%S')) for l in cr.dictfetchall()]
for employee_id in ids:
emp = service.execute(cr.dbname, uid, 'hr.employee', 'read', [employee_id])[0]
stop, days_xml = False, []
user_repr = '''
<user>
<name>%s</name>
<regime>%s</regime>
<holiday>%s</holiday>
%%s
</user>
''' % (toxml(emp['name']),emp['regime'],emp['holiday_max'])
today, tomor = month, month + one_day
while today.month == month.month:
#### Work hour calculation
sql = '''
select action, att.name
from hr_employee as emp inner join hr_attendance as att
on emp.id = att.employee_id
where att.name between '%s' and '%s' and emp.id = %s
order by att.name
'''
cr.execute(sql, (today, tomor, employee_id))
attendences = cr.dictfetchall()
wh = 0
if attendences and attendences[0]['action'] == 'sign_out':
attendences.insert(0, {'name': today.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'})
if attendences and attendences[-1]['action'] == 'sign_in':
attendences.append({'name' : tomor.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_out'})
for att in attendences:
dt = DateTime.strptime(att['name'], '%Y-%m-%d %H:%M:%S')
if att['action'] == 'sign_out':
wh += (dt - ldt).hours
ldt = dt
#### Theoretical workhour calculation
sql = '''
select t.hour_from, t.hour_to
from hr_timesheet as t
inner join (hr_timesheet_group as g inner join hr_timesheet_employee_rel as rel
on rel.tgroup_id = g.id and rel.emp_id = %s)
on t.tgroup_id = g.id
where dayofweek = '%s'
and date_from = (select max(date_from)
from hr_timesheet inner join (hr_timesheet_employee_rel
inner join hr_timesheet_group
on hr_timesheet_group.id = hr_timesheet_employee_rel.tgroup_id
and hr_timesheet_employee_rel.emp_id = %s)
on hr_timesheet.tgroup_id = hr_timesheet_group.id
where dayofweek = '%s' and date_from <= '%s')
order by date_from desc
'''
isPH = False
for jf_start, jf_end in jfs:
if jf_start <= today <= jf_end:
isPH = True
break
if isPH:
twh = 0
else:
cr.execute(sql, (emp['id'], today.day_of_week, emp['id'], today.day_of_week, today))
ths = cr.dictfetchall()
twh = reduce(lambda x,y:x+(DateTime.strptime(y['hour_to'], '%H:%M:%S') - DateTime.strptime(y['hour_from'], '%H:%M:%S')).hours,ths, 0)
#### Holiday calculation
hh = 0
sql = '''
select hol.date_from, hol.date_to, stat.name as status
from hr_employee as emp
inner join (hr_holidays as hol left join hr_holidays_status as stat
on hol.holiday_status = stat.id)
on emp.id = hol.employee_id
where ((hol.date_from <= '%s' and hol.date_to >= '%s')
or (hol.date_from < '%s' and hol.date_to >= '%s')
or (hol.date_from > '%s' and hol.date_to < '%s'))
and emp.id = %s
order by hol.date_from
'''
cr.execute(sql, (today, today, tomor, tomor, today, tomor, employee_id))
holidays = cr.dictfetchall()
for hol in holidays:
df = DateTime.strptime(hol['date_from'], '%Y-%m-%d %H:%M:%S')
dt = DateTime.strptime(hol['date_to'], '%Y-%m-%d %H:%M:%S')
if (df.year, df.month, df.day) <= (today.year, today.month, today.day) <= (dt.year, dt.month, dt.day):
if (df.year, df.month, df.day) == (dt.year, dt.month, dt.day):
hh += (dt - df).hours
else:
hh = twh
# Week xml representation
twh, wh, hh = map(hour2str, (twh, wh, hh))
today_xml = '<day num="%s"><th>%s</th><wh>%s</wh><hh>%s</hh></day>' % ((today - month).days+1, twh, wh, hh)
days_xml.append(today_xml)
today, tomor = tomor, tomor + one_day
user_xml.append(user_repr % '\n'.join(days_xml))
xml = '''<?xml version="1.0" encoding="UTF-8" ?>
<report>
%s
</report>
''' % '\n'.join(user_xml)
return xml
report_custom('report.hr.timesheet.bymonth', 'hr.employee', '', 'addons/hr/report/bymonth.xsl')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<employees>
<employee>
<name type="field" name="name" />
<address type="field" name="address_id.name" />
<attendances>
<attendance type="zoom" name="attendances">
<date type="field" name="name" />
<action type="field" name="action" />
</attendance>
</attendances>
</employee>
</employees>
-67
View File
@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:import href="../../custom/corporate_defaults.xsl"/>
<xsl:import href="../../base/report/rml_template.xsl"/>
<xsl:variable name="page_format">a4_normal</xsl:variable>
<xsl:template name="stylesheet">
<blockTableStyle id="products">
<blockFont name="Helvetica-BoldOblique" size="12"
start="0,0" stop="-1,0"/>
<blockBackground colorName="yellow" start="0,0" stop="-1,0"/>
<blockValign value="TOP"/>
</blockTableStyle>
</xsl:template>
<xsl:template name="story">
<xsl:apply-templates select="employees"/>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="rml"/>
</xsl:template>
<xsl:template match="employees">
<xsl:apply-templates select="employee"/>
</xsl:template>
<xsl:template match="employee">
<setNextTemplate name="other_pages"/>
<para>
<b t="1">Name</b>:
<i><xsl:value-of select="name"/></i>
</para>
<para>
<b t="1">Address</b>:
<i><xsl:value-of select="address"/></i>
</para>
<spacer length="1cm" width="2mm"/>
<blockTable colWidths="3cm,2.5cm,2.5cm" style="products">
<tr>
<td>Date</td>
<td>Action</td>
<td>Hour</td>
</tr>
<xsl:apply-templates name="attendances"/>
</blockTable>
<setNextTemplate name="first_page"/>
<pageBreak/>
</xsl:template>
<xsl:template match="attendance">
<tr>
<td>
<xsl:value-of select="substring(date,1,10)"/>
</td>
<td>
<xsl:value-of select="action"/>
</td>
<td>
<xsl:value-of select="substring(date,12,5)"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
-30
View File
@@ -1,30 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
#from print_byweek import wiz_byweek
#from print_bymonth import wiz_bymonth
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-182
View File
@@ -1,182 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import wizard
import netsvc
import time
from tools.translate import _
si_so_form ='''<?xml version="1.0"?>
<form string="Sign in / Sign out">
<separator string="You are now ready to sign in or out of the attendance follow up" colspan="4" />
<field name="name" readonly="True" />
<field name="state" readonly="True" />
</form>'''
si_so_fields = {
'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True},
'state' : {'string' : "Current state", 'type' : 'char', 'required' : True, 'readonly': True},
}
si_form = '''<?xml version="1.0" ?>
<form string="Sign in status">
<separator string="This is the status of your sign in request. Check it out maybe you were already signed in." colspan="4" />
<field name="success" readonly="True" />
</form>'''
si_fields = {
'success' : {'string' : "Sign in's status", 'type' : 'char', 'required' : True, 'readonly' : True},
}
so_form = '''<?xml version="1.0" ?>
<form string="Sign in status">
<separator string="This is the status of your sign out request. Check it out maybe you were already signed out." colspan="4" />
<field name="success" readonly="True" />
</for>'''
so_fields = {
'success' : {'string' : "Sign out's status", 'type' : 'char', 'required' : True, 'readonly' : True},
}
def _get_empid(self, cr, uid, data, context):
service = netsvc.LocalService('object_proxy')
emp_id = service.execute(cr.dbname, uid, 'hr.employee', 'search', [('user_id', '=', uid)])
if emp_id:
employee = service.execute(cr.dbname, uid, 'hr.employee', 'read', emp_id)[0]
return {'name': employee['name'], 'state': employee['state'], 'emp_id': emp_id[0]}
return {}
def _sign_in(self, cr, uid, data, context):
service = netsvc.LocalService('object_proxy')
emp_id = data['form']['emp_id']
if 'last_time' in data['form'] :
if data['form']['last_time'] > time.strftime('%Y-%m-%d'):
raise wizard.except_wizard(_('UserError'), _('The sign-out date must be in the past'))
return {'success': False}
service.execute(cr.dbname, uid, 'hr.attendance', 'create', {
'name': data['form']['last_time'],
'action': 'sign_out',
'employee_id': emp_id
})
try:
success = service.execute(cr.dbname, uid, 'hr.employee', 'sign_in', [emp_id])
except:
raise wizard.except_wizard(_('UserError'), _('A sign-in must be right after a sign-out !'))
return {'success': success}
def _sign_out(self, cr, uid, data, context):
service = netsvc.LocalService('object_proxy')
emp_id = data['form']['emp_id']
if 'last_time' in data['form'] :
if data['form']['last_time'] > time.strftime('%Y-%m-%d'):
raise wizard.except_wizard(_('UserError'), _('The Sign-in date must be in the past'))
return {'success': False}
service.execute(cr.dbname, uid, 'hr.attendance', 'create', {'name':data['form']['last_time'], 'action':'sign_in', 'employee_id':emp_id})
try:
success = service.execute(cr.dbname, uid, 'hr.employee', 'sign_out', [emp_id])
except:
raise wizard.except_wizard(_('UserError'), _('A sign-out must be right after a sign-in !'))
return {'success' : success}
so_ask_form ='''<?xml version="1.0"?>
<form string="Sign in / Sign out">
<separator string="You did not signed out the last time. Please enter the date and time you signed out." colspan="4" />
<field name="name" readonly="True" />
<field name="last_time" />
</form>'''
so_ask_fields = {
'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True},
'last_time' : {'string' : "Your last sign out", 'type' : 'datetime', 'required' : True},
}
def _si_check(self, cr, uid, data, context):
states = {True : 'si', False: 'si_ask_so'}
service = netsvc.LocalService('object_proxy')
emp_id = data['form']['emp_id']
att_id = service.execute(cr.dbname, uid, 'hr.attendance', 'search', [('employee_id', '=', emp_id)], limit=1, order='name desc')
last_att = service.execute(cr.dbname, uid, 'hr.attendance', 'read', att_id)
if last_att:
last_att = last_att[0]
cond = not last_att or last_att['action'] == 'sign_out'
return states[cond]
si_ask_form ='''<?xml version="1.0"?>
<form string="Sign in / Sign out">
<separator string="You did not signed in the last time. Please enter the date and time you signed in." colspan="4" />
<field name="name" readonly="True" />
<field name="last_time" />
</form>'''
si_ask_fields = {
'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True},
'last_time' : {'string' : "Your last sign in", 'type' : 'datetime', 'required' : True},
}
def _so_check(self, cr, uid, data, context):
states = {True : 'so', False: 'so_ask_si'}
service = netsvc.LocalService('object_proxy')
emp_id = data['form']['emp_id']
att_id = service.execute(cr.dbname, uid, 'hr.attendance', 'search', [('employee_id', '=', emp_id)], limit=1, order='name desc')
last_att = service.execute(cr.dbname, uid, 'hr.attendance', 'read', att_id)
if last_att:
last_att = last_att[0]
cond = last_att and last_att['action'] == 'sign_in'
return states[cond]
class wiz_si_so(wizard.interface):
states = {
'init' : {
'actions' : [_get_empid],
'result' : {'type' : 'form', 'arch' : si_so_form, 'fields' : si_so_fields, 'state' : [('end', 'Cancel'),('si_test', 'Sign in'),('so_test', 'Sign out')] }
},
'si_test' : {
'actions' : [],
'result' : {'type' : 'choice', 'next_state': _si_check}
},
'si_ask_so' : {
'actions' : [],
'result' : {'type' : 'form', 'arch' : so_ask_form, 'fields' : so_ask_fields, 'state' : [('end', 'Cancel'),('si', 'Sign in') ] }
},
'si' : {
'actions' : [_sign_in],
'result' : {'type' : 'state', 'state':'end'}
},
'so_test' : {
'actions' : [],
'result' : {'type' : 'choice', 'next_state': _so_check }
},
'so_ask_si' : {
'actions' : [],
'result' : {'type' : 'form', 'arch' : si_ask_form, 'fields' : si_ask_fields, 'state' : [('end', 'Cancel'),('so', 'Sign out')] }
},
'so' : {
'actions' : [_sign_out],
'result' : {'type' : 'state', 'state':'end'}
},
}
wiz_si_so('hr.si_so')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -5,6 +5,9 @@
<wizard id="si_so" model="hr.employee" name="hr.si_so" string="Sign in / Sign out"/>
<wizard id="wizard_attendance_error" keyword="client_print_multi" model="hr.employee" name="hr.timesheet.attendance.report" string="Print Attendance Error Report"/>
<wizard id="print_week" keyword="client_print_multi" model="hr.employee" name="hr.print_week" string="Print Timesheet by week"/>
<wizard id="print_month" keyword="client_print_multi" model="hr.employee" name="hr.print_month" string="Print Timesheet by month"/>
<menuitem action="si_so" id="menu_si_so" parent="menu_hr_attendance" type="wizard"
groups="group_hr_attendance"/>
+2
View File
@@ -20,5 +20,7 @@
#
##############################################################################
import attendance_errors
import bymonth
import timesheet
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+102
View File
@@ -0,0 +1,102 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from mx import DateTime
from mx.DateTime import now
import time
import netsvc
import pooler
from report.interface import report_rml
from report.interface import toxml
one_day = DateTime.RelativeDateTime(days=1)
month2name = [0,'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
def hour2str(h):
hours = int(h)
minutes = int(round((h - hours) * 60, 0))
return '%02dh%02d' % (hours, minutes)
class report_custom(report_rml):
def create_xml(self, cr, uid, ids, datas, context):
service = netsvc.LocalService('object_proxy')
month = DateTime.DateTime(datas['form']['year'], datas['form']['month'], 1)
user_xml = ['<month>%s</month>' % month2name[month.month], '<year>%s</year>' % month.year]
for employee_id in ids:
emp = service.execute(cr.dbname, uid, 'hr.employee', 'read', [employee_id])[0]
stop, days_xml = False, []
user_repr = '''
<user>
<name>%s</name>
%%s
</user>
''' % (toxml(emp['name']))
today, tomor = month, month + one_day
while today.month == month.month:
#### Work hour calculation
sql = '''
select action, att.name
from hr_employee as emp inner join hr_attendance as att
on emp.id = att.employee_id
where att.name between %s and %s and emp.id = %s
order by att.name
'''
cr.execute(sql, (today, tomor, employee_id))
attendences = cr.dictfetchall()
wh = 0
# Fake sign ins/outs at week ends, to take attendances across week ends into account
if attendences and attendences[0]['action'] == 'sign_out':
attendences.insert(0, {'name': today.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'})
if attendences and attendences[-1]['action'] == 'sign_in':
attendences.append({'name' : tomor.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_out'})
# sum up the attendances' durations
for att in attendences:
dt = DateTime.strptime(att['name'], '%Y-%m-%d %H:%M:%S')
if att['action'] == 'sign_out':
wh += (dt - ldt).hours
ldt = dt
# Week xml representation
wh = hour2str(wh)
today_xml = '<day num="%s"><wh>%s</wh></day>' % ((today - month).days+1, wh)
days_xml.append(today_xml)
today, tomor = tomor, tomor + one_day
user_xml.append(user_repr % '\n'.join(days_xml))
xml = '''<?xml version="1.0" encoding="UTF-8" ?>
<report>
%s
</report>
''' % '\n'.join(user_xml)
return xml
report_custom('report.hr.timesheet.bymonth', 'hr.employee', '', 'addons/hr_attendance/report/bymonth.xsl')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -85,16 +85,8 @@
<tr>
<td>
<para style="name"><xsl:value-of select="name" /></para>
<para style="normal"><xsl:text>By week: </xsl:text><xsl:value-of select="format-number(regime, '#.#')" /><xsl:text> h</xsl:text></para>
<para style="normal"><xsl:text>Holidays: </xsl:text><xsl:value-of select="holiday" /></para>
</td>
</tr>
<tr>
<td>Theoretical</td>
<xsl:for-each select="day">
<td><xsl:value-of select="th" /></td>
</xsl:for-each>
</tr>
<tr>
<td>Worked</td>
<xsl:for-each select="day">
+2
View File
@@ -21,5 +21,7 @@
##############################################################################
import sign_in_out
import print_attendance_error
from print_byweek import wiz_byweek
from print_bymonth import wiz_bymonth
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: