diff options
| author | pinky <> | 2006-12-07 13:41:40 +0000 |
|---|---|---|
| committer | pinky <> | 2006-12-07 13:41:40 +0000 |
| commit | a83dce15e0f33145b2eea7e878ae7eacf3aba90a (patch) | |
| tree | a3d1399cbfebf7d90a8894d97c9b7e0a9d881960 /addons/hr/report | |
| parent | 004a0b996ff8f269451e07346f71a129a1f3fbaf (diff) | |
New trunk
bzr revid: pinky-dedd7f8a42bd4557112a0513082691b8590ad6cc
Diffstat (limited to 'addons/hr/report')
| -rw-r--r-- | addons/hr/report/__init__.py | 32 | ||||
| -rw-r--r-- | addons/hr/report/attendance_errors.py | 83 | ||||
| -rw-r--r-- | addons/hr/report/attendance_errors.rml | 150 | ||||
| -rw-r--r-- | addons/hr/report/bymonth.py | 166 | ||||
| -rw-r--r-- | addons/hr/report/bymonth.xsl | 105 | ||||
| -rw-r--r-- | addons/hr/report/siso.xml | 13 | ||||
| -rw-r--r-- | addons/hr/report/siso.xsl | 67 | ||||
| -rw-r--r-- | addons/hr/report/timesheet.py | 187 | ||||
| -rw-r--r-- | addons/hr/report/timesheet.xsl | 241 |
9 files changed, 1044 insertions, 0 deletions
diff --git a/addons/hr/report/__init__.py b/addons/hr/report/__init__.py new file mode 100644 index 00000000000..d4489be4209 --- /dev/null +++ b/addons/hr/report/__init__.py @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) +# +# $Id$ +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contact a Free Software +# Service Company +# +# 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 2 +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import timesheet +import bymonth +import attendance_errors diff --git a/addons/hr/report/attendance_errors.py b/addons/hr/report/attendance_errors.py new file mode 100644 index 00000000000..ed23387250d --- /dev/null +++ b/addons/hr/report/attendance_errors.py @@ -0,0 +1,83 @@ +############################################################################## +# +# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# Fabien Pinckaers <fp@tiny.Be> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# 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 2 +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + + +import time +from report import report_sxw + +class attendance_print(report_sxw.rml_parse): + def __init__(self, cr, uid, name, context): + super(attendance_print, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'lst': self._lst, + 'total': self._lst_total, + }) + + def _sign(self, dt): + if abs(dt.days)>1: + format = '%d day'+(((abs(dt.days)>=2) and 's') or '')+' %H:%M:%S' + else: + format = '%H:%M:%S' + if dt.seconds<0: + return dt.strftime('- '+format) + else: + return dt.strftime(format) + + def _lst(self, employee_id, dt_from, dt_to, max, *args): + self.cr.execute('select name as date, create_date, action, create_date-name as delay from hr_attendance where employee_id=%d and name<=%s and name>=%s and action in (%s,%s) order by name', (employee_id, dt_to, dt_from, 'sign_in', 'sign_out')) + res = self.cr.dictfetchall() + for r in res: + if r['action']=='sign_out': + r['delay'] = - r['delay'] + temp = r['delay'].seconds + r['delay'] = self._sign(r['delay']) + if abs(temp) < max*60: + r['delay2'] = r['delay'] + else: + r['delay2'] = '/' + return res + + def _lst_total(self, employee_id, dt_from, dt_to, max, *args): + self.cr.execute('select name as date, create_date, action, create_date-name as delay from hr_attendance where employee_id=%d and name<=%s and name>=%s and action in (%s,%s)', (employee_id, dt_to, dt_from, 'sign_in', 'sign_out')) + res = self.cr.dictfetchall() + if not res: + return ('/','/') + total = 0 + total2 = 0 + for r in res: + if r['action']=='sign_out': + r['delay'] = - r['delay'] + total += r['delay'] + if abs(r['delay'].seconds) < max*60: + total2 += r['delay'] + + return (self._sign(total),total2 and self._sign(total2)) + +report_sxw.report_sxw('report.hr.timesheet.attendance.error', 'hr.employee', 'addons/hr/report/attendance_errors.rml',parser=attendance_print) + diff --git a/addons/hr/report/attendance_errors.rml b/addons/hr/report/attendance_errors.rml new file mode 100644 index 00000000000..c81da90c57b --- /dev/null +++ b/addons/hr/report/attendance_errors.rml @@ -0,0 +1,150 @@ +<?xml version="1.0"?> +<document filename="test.pdf"> + <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20"> + <pageTemplate id="first"> + <frame id="first" x1="57.0" y1="57.0" width="481" height="728"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau1"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <blockBackground colorName="#c0c0c0" start="0,0" stop="0,0"/> + <blockBackground colorName="#c0c0c0" start="1,0" stop="1,0"/> + <blockBackground colorName="#c0c0c0" start="2,0" stop="2,0"/> + </blockTableStyle> + <blockTableStyle id="Tableau2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="GRID" colorName="black"/> + <blockBackground colorName="#ff9966" start="0,0" stop="0,0"/> + </blockTableStyle> + <blockTableStyle id="Tableau3"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="GRID" colorName="black"/> + <blockBackground colorName="#ffcc99" start="0,0" stop="0,0"/> + <blockBackground colorName="#ffcc99" start="1,0" stop="1,0"/> + <blockBackground colorName="#ffcc99" start="2,0" stop="2,0"/> + <blockBackground colorName="#ffcc99" start="3,0" stop="3,0"/> + <blockBackground colorName="#ffcc99" start="4,0" stop="4,0"/> + </blockTableStyle> + <blockTableStyle id="Tableau4"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="GRID" colorName="black"/> + <blockBackground colorName="#e6e6ff" start="1,0" stop="1,0"/> + <blockBackground colorName="#e6e6ff" start="2,0" stop="2,0"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="P1" fontName="Times-Roman" fontSize="22.0" leading="27" alignment="CENTER"/> + <paraStyle name="P2" fontName="Times-Roman" alignment="RIGHT"/> + <paraStyle name="P3" fontName="Times-Roman" fontSize="14.0" leading="17" alignment="CENTER"/> + <paraStyle name="Standard" fontName="Times-Roman"/> + <paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Table Contents" fontName="Times-Roman"/> + <paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Times-Roman"/> + </stylesheet> + <story> + <blockTable colWidths="122.0,269.0,91.0" style="Tableau1"> + <tr> + <td> + <para style="Table Contents">Report</para> + <para style="Table Contents">From: [[ data['form']['init_date'] ]]</para> + <para style="Table Contents">To: [[ data['form']['end_date'] ]]</para> + </td> + <td> + <para style="P1">Attendance Errors</para> + </td> + <td> + <para style="P2">Date Printed:</para> + <para style="P2">[[ time.strftime('%Y/%m/%d') ]]</para> + </td> + </tr> + </blockTable> + <para style="Standard"> + <font color="white"> </font> + </para> + <section> + <para style="Standard">[[ repeatIn(objects, 'employee') ]]</para> + <blockTable colWidths="482.0" repeatRows="1" style="Tableau2"> + <tr> + <td> + <para style="P3">[[ employee.name ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="67.0,106.0,134.0,85.0,90.0" style="Tableau3"> + <tr> + <td> + <para style="Table Contents">Operation</para> + </td> + <td> + <para style="Table Contents">Date Signed</para> + </td> + <td> + <para style="Table Contents">Date Recorded</para> + </td> + <td> + <para style="Table Contents">Delay</para> + </td> + <td> + <para style="Table Contents">Avg Delay</para> + </td> + </tr> + <tr> + <td> + <para style="Table Contents"><font face="Times-Roman">[[repeatIn(lst(employee.id,data['form']['init_date'], data['form']['end_date'], data['form']['max_delay']), 'att') ]]</font> [[att['action'] ]]</para> + </td> + <td> + <para style="Table Contents">[[ att['date'] ]]</para> + </td> + <td> + <para style="Table Contents">[[ att['create_date'] ]]</para> + </td> + <td> + <para style="Table Contents">[[ att['delay'] ]]</para> + </td> + <td> + <para style="Table Contents">[[ att['delay2'] ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="307.0,85.0,90.0" style="Tableau4"> + <tr> + <td> + <para style="Table Contents">Total period:</para> + </td> + <td> + <para style="Table Contents"> + <font face="Times-Roman">[[total(employee.id,data['form']['init_date'], data['form']['end_date'], data['form']['max_delay'])[0] ]]</font> + </para> + </td> + <td> + <para style="Table Contents"> + <font face="Times-Roman">[[total(employee.id,data['form']['init_date'], data['form']['end_date'], data['form']['max_delay'])[1] ]]</font> + </para> + </td> + </tr> + </blockTable> + <para style="Standard"> + <font color="white"> </font> + </para> + </section> + <para style="Standard"> + <font color="white"> </font> + </para> + <para style="Standard">(*) A positive delay means that the employee worked less than recorded.</para> + <para style="Standard">(*) A negative delay means that the employee worked more than encoded.</para> + </story> +</document> + diff --git a/addons/hr/report/bymonth.py b/addons/hr/report/bymonth.py new file mode 100644 index 00000000000..06dc2dab54f --- /dev/null +++ b/addons/hr/report/bymonth.py @@ -0,0 +1,166 @@ +############################################################################## +# +# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# 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 2 +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +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:noexpandtab:tw=0 diff --git a/addons/hr/report/bymonth.xsl b/addons/hr/report/bymonth.xsl new file mode 100644 index 00000000000..0b79152bf97 --- /dev/null +++ b/addons/hr/report/bymonth.xsl @@ -0,0 +1,105 @@ +<?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:template match="/"> + <xsl:call-template name="rml" /> + </xsl:template> + + + <xsl:template name="rml"> + <document filename="example.pdf"> + <template pageSize="29.7cm,21cm" leftMargin="2.0cm" rightMargin="2.0cm" topMargin="2.0cm" bottomMargin="2.0cm" title="Timesheets" author="Generated by Tiny ERP, Fabien Pinckaers" allowSplitting="20"> + <pageTemplate id="first"> + <pageGraphics> + <drawRightString x="19.0cm" y="26.0cm"><xsl:value-of select="date"/></drawRightString> + </pageGraphics> + <frame id="col1" x1="2.0cm" y1="2.5cm" width="22.7cm" height="18cm"/> + </pageTemplate> + </template> + + <stylesheet> + <paraStyle name="name" fontName="Helvetica" textColor="green" fontSize="7"/> + <paraStyle name="normal" fontName="Helvetica" fontSize="6"/> + <blockTableStyle id="week"> + <blockFont name="Helvetica-BoldOblique" size="8" start="0,0" stop="-1,0"/> + <blockFont name="Helvetica" size="5" start="0,1" stop="-1,-1"/> + <blockBackground colorName="grey" start="0,0" stop="-1,0"/> + <lineStyle kind="LINEABOVE" colorName="grey" start="0,0" stop="-1,0" /> + <lineStyle kind="LINEBEFORE" colorName="grey" start="0,0" stop="-1,-1"/> + <lineStyle kind="LINEAFTER" colorName="grey" start="-1,0" stop="-1,-1"/> + <blockValign value="TOP"/> + </blockTableStyle> + </stylesheet> + + <story> + <xsl:call-template name="story"/> + </story> + </document> + </xsl:template> + + <xsl:template name="story"> + <blockTable + colWidths="2cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm,0.79cm" style="week"> + <tr> + <td><xsl:value-of select="/report/month" /><xsl:text> </xsl:text><xsl:value-of select="/report/year" /></td> + <td>1</td> + <td>2</td> + <td>3</td> + <td>4</td> + <td>5</td> + <td>6</td> + <td>7</td> + <td>8</td> + <td>9</td> + <td>10</td> + <td>11</td> + <td>12</td> + <td>13</td> + <td>14</td> + <td>15</td> + <td>16</td> + <td>17</td> + <td>18</td> + <td>19</td> + <td>20</td> + <td>21</td> + <td>22</td> + <td>23</td> + <td>24</td> + <td>25</td> + <td>26</td> + <td>27</td> + <td>28</td> + <td>29</td> + <td>30</td> + <td>31</td> + </tr> + <xsl:apply-templates select="report/user"/> + </blockTable> + </xsl:template> + + <xsl:template match="user"> + <tr></tr> + <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"> + <td><xsl:value-of select="wh" /></td> + </xsl:for-each> + </tr> + </xsl:template> +</xsl:stylesheet> diff --git a/addons/hr/report/siso.xml b/addons/hr/report/siso.xml new file mode 100644 index 00000000000..d640cd98b51 --- /dev/null +++ b/addons/hr/report/siso.xml @@ -0,0 +1,13 @@ +<?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> diff --git a/addons/hr/report/siso.xsl b/addons/hr/report/siso.xsl new file mode 100644 index 00000000000..96cfc70c133 --- /dev/null +++ b/addons/hr/report/siso.xsl @@ -0,0 +1,67 @@ +<?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> diff --git a/addons/hr/report/timesheet.py b/addons/hr/report/timesheet.py new file mode 100644 index 00000000000..f5561e1de50 --- /dev/null +++ b/addons/hr/report/timesheet.py @@ -0,0 +1,187 @@ +############################################################################## +# +# Copyright (c) 2005 TINY SPRL. (http://tiny.be) All Rights Reserved. +# Fabien Pinckaers <fp@tiny.Be> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# 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 2 +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +from mx import DateTime +from mx.DateTime import now + +import netsvc +import pooler + +from report.interface import report_rml +from report.interface import toxml + +one_week = DateTime.RelativeDateTime(days=7) +num2day = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + +def to_hour(h): + return int(h), int(round((h - int(h)) * 60, 0)) + +class report_custom(report_rml): + def create_xml(self, cr, uid, ids, datas, context): + service = netsvc.LocalService('object_proxy') + + start_date = DateTime.strptime(datas['form']['init_date'], '%Y-%m-%d') + end_date = DateTime.strptime(datas['form']['end_date'], '%Y-%m-%d') + first_monday = start_date - DateTime.RelativeDateTime(days=start_date.day_of_week) + last_monday = end_date + DateTime.RelativeDateTime(days=7 - end_date.day_of_week) + + if last_monday < first_monday: + first_monday, last_monday = last_monday, first_monday + + user_xml = [] + + 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], ['id', 'name'])[0] + monday, n_monday = first_monday, first_monday + one_week + stop, week_xml = False, [] + user_repr = ''' + <user> + <name>%s</name> + %%s + </user> + ''' % toxml(emp['name']) + while monday != last_monday: + #### 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 + ''' + for idx in range(7): + cr.execute(sql, (monday, monday + DateTime.RelativeDateTime(days=idx+1), employee_id)) + attendences = cr.dictfetchall() + week_wh = {} + if attendences and attendences[0]['action'] == 'sign_out': + attendences.insert(0, {'name': monday.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'}) + if attendences and attendences[-1]['action'] == 'sign_in': + attendences.append({'name' : n_monday.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': + week_wh[ldt.day_of_week] = week_wh.get(ldt.day_of_week, 0) + (dt - ldt).hours + ldt = dt + + #### Theoretical workhour calculation + week_twh = {} + 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 + ''' + for idx in range(7): + day = monday + DateTime.RelativeDateTime(days=idx+1) + # Is this a public holiday ? + isPH = False + for jf_start, jf_end in jfs: + if jf_start <= day < jf_end: + isPH = True + break + if isPH: + week_twh[idx] = 0 + else: + cr.execute(sql, (emp['id'], day.day_of_week, emp['id'], day.day_of_week, day)) + dhs = cr.dictfetchall() + week_twh[idx] = reduce(lambda x,y:x+(DateTime.strptime(y['hour_to'], '%H:%M:%S') - DateTime.strptime(y['hour_from'], '%H:%M:%S')).hours,dhs, 0) + + #### Holiday calculation + 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 stat.name != 'Public holidays') and emp.id = %s + order by hol.date_from + ''' + cr.execute(sql, (monday, monday, n_monday, n_monday, monday, n_monday, employee_id)) + holidays = cr.dictfetchall() + week_hol = {} + 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') + for idx in range(7): + day = monday + DateTime.RelativeDateTime(days=idx+1) + if (df.year, df.month, df.day) <= (day.year, day.month, day.day) <= (dt.year, dt.month, dt.day): + if (df.year, df.month, df.day) == (dt.year, dt.month, dt.day): + week_hol[idx] = {'status' : hol['status'], 'hours' : (dt - df).hours} + else: + week_hol[idx] = {'status' : hol['status'], 'hours' : week_twh[idx]} + + # Week xml representation + week_repr = ['<week>', '<weekstart>%s</weekstart>' % monday.strftime('%Y-%m-%d'), '<weekend>%s</weekend>' % n_monday.strftime('%Y-%m-%d')] + for idx in range(7): + week_repr.append('<%s>' % num2day[idx]) + week_repr.append('<theoretical>%sh%02d</theoretical>' % to_hour(week_twh[idx])) + if idx in week_wh: + week_repr.append('<workhours>%sh%02d</workhours>' % to_hour(week_wh[idx])) + if idx in week_hol and week_hol[idx]['hours']: + week_repr.append('<holidayhours type="%(status)s">%(hours)s</holidayhours>' % week_hol[idx]) + week_repr.append('</%s>' % num2day[idx]) + week_repr.append('<total>') + week_repr.append('<theoretical>%sh%02d</theoretical>' % to_hour(reduce(lambda x,y:x+y, week_twh.values(), 0))) + week_repr.append('<worked>%sh%02d</worked>' % to_hour(reduce(lambda x,y:x+y, week_wh.values(), 0))) + week_repr.append('<holiday>%sh%02d</holiday>' % to_hour(reduce(lambda x,y:x+y, [day['hours'] for day in week_hol.values()], 0))) + week_repr.append('</total>') + week_repr.append('</week>') + if len(week_repr) > 30: # 30 = minimal length of week_repr + week_xml.append('\n'.join(week_repr)) + + monday, n_monday = n_monday, n_monday + one_week + user_xml.append(user_repr % '\n'.join(week_xml)) + + xml = '''<?xml version="1.0" encoding="UTF-8" ?> + <report> + %s + </report> + ''' % '\n'.join(user_xml) + return self.post_process_xml_data(cr, uid, xml, context) + +report_custom('report.hr.timesheet.allweeks', 'hr.employee', '', 'addons/hr/report/timesheet.xsl') +# vim:noexpandtab:tw=0 diff --git a/addons/hr/report/timesheet.xsl b/addons/hr/report/timesheet.xsl new file mode 100644 index 00000000000..d318f2bf614 --- /dev/null +++ b/addons/hr/report/timesheet.xsl @@ -0,0 +1,241 @@ +<?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="week"> + <blockFont name="Helvetica-BoldOblique" size="12" start="0,0" stop="-1,0"/> + <blockBackground colorName="grey" start="0,0" stop="-1,0"/> + <blockTextColor colorName="red" start="-1,0" stop="-1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="grey" start="-1,0" stop="-1,-1"/> + <blockValign value="TOP"/> + </blockTableStyle> + </xsl:template> + + <xsl:template match="/"> + <xsl:call-template name="rml" /> + </xsl:template> + + <xsl:template name="story"> + <xsl:apply-templates select="report/user"/> + </xsl:template> + + <xsl:template match="user"> + <para> + <b>Name:</b> + <i><xsl:value-of select="name" /></i> + </para> + <blockTable colWidths="4cm,1.5cm,1.5cm,1.5cm,1.5cm,1.5cm,1.5cm,1.5cm,1.5cm" style="week"> + <tr> + <td></td> + <td>Mon</td> + <td>Tue</td> + <td>Wed</td> + <td>Thu</td> + <td>Fri</td> + <td>Sat</td> + <td>Sun</td> + <td>Tot</td> + </tr> + <xsl:for-each select="week"> + <tr></tr> + <tr> + <td>Week :</td> + <td></td> + <td>from <xsl:value-of select="weekstart" /> to <xsl:value-of select="weekend" /></td> + </tr> + <tr> + <td>Theoretical workhours</td> + <td> + <xsl:choose> + <xsl:when test="Monday/theoretical"> + <xsl:value-of select="Monday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Tuesday/theoretical"> + <xsl:value-of select="Tuesday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Wednesday/theoretical"> + <xsl:value-of select="Wednesday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Thursday/theoretical"> + <xsl:value-of select="Thursday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Friday/theoretical"> + <xsl:value-of select="Friday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Saturday/theoretical"> + <xsl:value-of select="Saturday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Sunday/theoretical"> + <xsl:value-of select="Sunday/theoretical" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:value-of select="total/theoretical" /> + </td> + </tr> + <tr> + <td>Workhours</td> + <td> + <xsl:choose> + <xsl:when test="Monday/workhours"> + <xsl:value-of select="Monday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Tuesday/workhours"> + <xsl:value-of select="Tuesday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Wednesday/workhours"> + <xsl:value-of select="Wednesday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Thursday/workhours"> + <xsl:value-of select="Thursday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Friday/workhours"> + <xsl:value-of select="Friday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Saturday/workhours"> + <xsl:value-of select="Saturday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Sunday/workhours"> + <xsl:value-of select="Sunday/workhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:value-of select="total/worked" /> + </td> + </tr> + <tr> + <td>Holiday hours</td> + <td> + <xsl:choose> + <xsl:when test="Monday/holidayhours"> + <xsl:value-of select="Monday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Tuesday/holidayhours"> + <xsl:value-of select="Tuesday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Wednesday/holidayhours"> + <xsl:value-of select="Wednesday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Thursday/holidayhours"> + <xsl:value-of select="Thursday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Friday/holidayhours"> + <xsl:value-of select="Friday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Saturday/holidayhours"> + <xsl:value-of select="Saturday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:choose> + <xsl:when test="Sunday/holidayhours"> + <xsl:value-of select="Sunday/holidayhours" /> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:value-of select="total/holiday" /> + </td> + </tr> + </xsl:for-each> + </blockTable> + </xsl:template> +</xsl:stylesheet> |
