summaryrefslogtreecommitdiff
path: root/addons/hr
diff options
context:
space:
mode:
authorpinky <>2006-12-07 13:41:40 +0000
committerpinky <>2006-12-07 13:41:40 +0000
commita83dce15e0f33145b2eea7e878ae7eacf3aba90a (patch)
treea3d1399cbfebf7d90a8894d97c9b7e0a9d881960 /addons/hr
parent004a0b996ff8f269451e07346f71a129a1f3fbaf (diff)
New trunk
bzr revid: pinky-dedd7f8a42bd4557112a0513082691b8590ad6cc
Diffstat (limited to 'addons/hr')
-rw-r--r--addons/hr/__init__.py3
-rw-r--r--addons/hr/__terp__.py14
-rw-r--r--addons/hr/hr.py250
-rw-r--r--addons/hr/hr_bel_holidays_2005.xml63
-rw-r--r--addons/hr/hr_demo.xml251
-rw-r--r--addons/hr/hr_report.xml5
-rw-r--r--addons/hr/hr_view.xml411
-rw-r--r--addons/hr/hr_wizard.xml12
-rw-r--r--addons/hr/report/__init__.py32
-rw-r--r--addons/hr/report/attendance_errors.py83
-rw-r--r--addons/hr/report/attendance_errors.rml150
-rw-r--r--addons/hr/report/bymonth.py166
-rw-r--r--addons/hr/report/bymonth.xsl105
-rw-r--r--addons/hr/report/siso.xml13
-rw-r--r--addons/hr/report/siso.xsl67
-rw-r--r--addons/hr/report/timesheet.py187
-rw-r--r--addons/hr/report/timesheet.xsl241
-rw-r--r--addons/hr/wizard/__init__.py32
-rw-r--r--addons/hr/wizard/print_attendance_error.py58
-rw-r--r--addons/hr/wizard/print_bymonth.py55
-rw-r--r--addons/hr/wizard/print_byweek.py56
-rw-r--r--addons/hr/wizard/sign_in_out.py179
22 files changed, 2433 insertions, 0 deletions
diff --git a/addons/hr/__init__.py b/addons/hr/__init__.py
new file mode 100644
index 00000000000..611fcf1be3b
--- /dev/null
+++ b/addons/hr/__init__.py
@@ -0,0 +1,3 @@
+import hr
+import report
+import wizard
diff --git a/addons/hr/__terp__.py b/addons/hr/__terp__.py
new file mode 100644
index 00000000000..327e7d0107c
--- /dev/null
+++ b/addons/hr/__terp__.py
@@ -0,0 +1,14 @@
+{
+ "name" : "Human Resources",
+ "version" : "0.1",
+ "author" : "Tiny",
+ "category" : "Generic Modules/Human Resources",
+ "website" : "http://tinyerp.com/module_hr.html",
+ "description": "Module for human ressource management",
+ "depends" : ["base"],
+ "init_xml" : [],
+ "demo_xml" : ["hr_demo.xml", "hr_bel_holidays_2005.xml"],
+ "update_xml" : ["hr_view.xml", "hr_report.xml", "hr_wizard.xml"],
+ "active": False,
+ "installable": True
+}
diff --git a/addons/hr/hr.py b/addons/hr/hr.py
new file mode 100644
index 00000000000..c13304ed2ea
--- /dev/null
+++ b/addons/hr/hr.py
@@ -0,0 +1,250 @@
+##############################################################################
+#
+# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# $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 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
+import time
+
+from osv import fields, osv
+
+class hr_timesheet_group(osv.osv):
+ _name = "hr.timesheet.group"
+ _description = "Timesheet"
+ _columns = {
+ 'name' : fields.char("Group name", size=64, required=True),
+ 'timesheet_id' : fields.one2many('hr.timesheet', 'tgroup_id', 'Timesheet'),
+ 'manager' : fields.many2one('res.users', 'Workgroup manager'),
+ }
+ #
+ # TODO: improve; very slow !
+ # bug if transition to another period
+ #
+ def interval_get(self, cr, uid, id, dt_from, hours, byday=True):
+ if not id:
+ return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=int(hours)*3))]
+ todo = hours
+ cycle = 0
+ result = []
+ while todo>0:
+ cr.execute('select hour_from,hour_to from hr_timesheet where dayofweek=%d and tgroup_id=%d order by hour_from', (dt_from.day_of_week,id))
+ for (hour_from,hour_to) in cr.fetchall():
+ h1,m1 = map(int,hour_from.split(':'))
+ h2,m2 = map(int,hour_to.split(':'))
+ d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h1,m1)
+ d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h2,m2)
+ if dt_from<d2:
+ date1 = max(dt_from,d1)
+ if date1+DateTime.RelativeDateTime(hours=todo)<=d2:
+ result.append((date1, date1+DateTime.RelativeDateTime(hours=todo)))
+ todo = 0
+ else:
+ todo -= (d2-date1).hours
+ result.append((date1, d2))
+ dt_from = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day)+DateTime.RelativeDateTime(days=1)
+ cycle+=1
+ if cycle>7 and todo==hours:
+ return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=hours*3))]
+ if byday:
+ i = 1
+ while i<len(result):
+ if (result[i][0]-result[i-1][1]).days<1:
+ result[i-1]=(result[i-1][0],result[i][1])
+ del result[i]
+ else:
+ i+=1
+ return result
+hr_timesheet_group()
+
+
+class hr_employee_category(osv.osv):
+ _name = "hr.employee.category"
+ _description = "Employee Category"
+ _columns = {
+ 'name' : fields.char("Category", size=64, required=True),
+ 'parent_id': fields.many2one('hr.employee.category', 'Parent category', select=True),
+ 'child_ids': fields.one2many('hr.employee.category', 'parent_id', 'Childs Categories')
+ }
+hr_employee_category()
+
+class hr_employee(osv.osv):
+ _name = "hr.employee"
+ _description = "Employee"
+ _columns = {
+ 'name' : fields.char("Employee", size=128, required=True),
+ 'active' : fields.boolean('Active'),
+ 'company_id': fields.many2one('res.company', 'Company'),
+ 'address_id': fields.many2one('res.partner.address', 'Contact address'),
+ 'state' : fields.selection([('absent', 'Absent'), ('present', 'Present')], 'Attendance', readonly=True),
+ 'started' : fields.date("Started on"),
+ 'notes': fields.text('Notes'),
+ 'attendances' : fields.one2many('hr.attendance', 'employee_id', "Employee's attendances"),
+ 'holidays' : fields.one2many('hr.holidays', 'employee_id', "Employee's holidays"),
+ 'workgroups' : fields.many2many('hr.timesheet.group', 'hr_timesheet_employee_rel', 'emp_id', 'tgroup_id', "Employee's work team"),
+ 'user_id' : fields.many2one('res.users', 'Tiny ERP User'),
+ 'category_id' : fields.many2one('hr.employee.category', 'Category'),
+ 'regime' : fields.float('Workhours by week'),
+ 'holiday_max' : fields.integer("Number of holidays"),
+ 'parent_id': fields.many2one('hr.employee', 'Boss', select=True),
+ 'child_ids': fields.one2many('hr.employee', 'parent_id','Subordinates'),
+ 'bank_account': fields.char("Bank Account", size=16),
+ }
+ _defaults = {
+ 'active' : lambda *a: True,
+ 'state' : lambda *a: 'absent',
+ }
+ def sign_change(self, cr, uid, ids, context={}, dt=False):
+ for emp in self.browse(cr, uid, ids):
+ if not self._action_check(cr, uid, emp.id, dt, context):
+ raise osv.except_osv('Warning', 'You tried to sign with a date anterior to another event !\nTry to contact the administrator to correct attendances.')
+ res = {'action':'action', 'employee_id':emp.id}
+ if dt:
+ res['name'] = dt
+ att_id = self.pool.get('hr.attendance').create(cr, uid, res)
+ return True
+
+ def sign_out(self, cr, uid, ids, context={}, dt=False, *args):
+ for emp in self.browse(cr, uid, ids):
+ if not self._action_check(cr, uid, emp.id, dt, context):
+ raise osv.except_osv('Warning', 'You tried to sign out with a date anterior to another event !\nTry to contact the administrator to correct attendances.')
+ res = {'action':'sign_out', 'employee_id':emp.id}
+ if dt:
+ res['name'] = dt
+ att_id = self.pool.get('hr.attendance').create(cr, uid, res)
+ self.write(cr, uid, [emp.id], {'state':'absent'})
+ return True
+
+ def _action_check(self, cr, uid, emp_id, dt=False,context={}):
+ cr.execute('select max(name) from hr_attendance where employee_id=%d', (emp_id,))
+ res = cr.fetchone()
+ return not (res and (res[0]>=(dt or time.strftime('%Y-%m-%d %H:%M:%S'))))
+
+ def sign_in(self, cr, uid, ids, context={}, dt=False, *args):
+ for emp in self.browse(cr, uid, ids):
+ if not self._action_check(cr, uid, emp.id, dt, context):
+ raise osv.except_osv('Warning', 'You tried to sign in with a date anterior to another event !\nTry to contact the administrator to correct attendances.')
+ res = {'action':'sign_in', 'employee_id':emp.id}
+ if dt:
+ res['name'] = dt
+ self.pool.get('hr.attendance').create(cr, uid, res)
+ self.write(cr, uid, [emp.id], {'state':'present'})
+ return True
+
+hr_employee()
+
+class hr_timesheet(osv.osv):
+ _name = "hr.timesheet"
+ _description = "Timesheet Line"
+ _columns = {
+ 'name' : fields.char("Name", size=64, required=True),
+ 'dayofweek': fields.selection([('0','Monday'),('1','Tuesday'),('2','Wednesday'),('3','Thursday'),('4','Friday'),('5','Saturday'),('6','Sunday')], 'Day of week'),
+ 'date_from' : fields.date('Starting date'),
+ 'hour_from' : fields.char('Work from', size=8),
+ 'hour_to' : fields.char("Work to", size=8),
+ 'tgroup_id' : fields.many2one("hr.timesheet.group", "Employee's timesheet group", select=True),
+ }
+ _order = 'dayofweek, hour_from'
+hr_timesheet()
+
+class hr_action_reason(osv.osv):
+ _name = "hr.action.reason"
+ _description = "Action reason"
+ _columns = {
+ 'name' : fields.char('Reason', size=64, required=True),
+ 'action_type' : fields.selection([('sign_in', 'Sign in'), ('sign_out', 'Sign out')], "Action's type"),
+ }
+ _defaults = {
+ 'action_type' : lambda *a: 'sign_in',
+ }
+hr_action_reason()
+
+def _employee_get(obj,cr,uid,context={}):
+ ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
+ if ids:
+ return ids[0]
+ return False
+
+class hr_attendance(osv.osv):
+ _name = "hr.attendance"
+ _description = "Attendance"
+ _columns = {
+ 'name' : fields.datetime('Date'),
+ 'action' : fields.selection([('sign_in', 'Sign In'), ('sign_out', 'Sign Out'),('action','Action')], 'Action'),
+ 'action_desc' : fields.many2one("hr.action.reason", "Action reason", domain="[('action_type', '=', action)]"),
+ 'employee_id' : fields.many2one('hr.employee', 'Employee',required=True, select=True),
+ }
+ _defaults = {
+ 'name' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'employee_id' : _employee_get,
+ }
+
+ def _altern_si_so(self, cr, uid, ids):
+ for id in ids:
+ sql = '''
+ select action, name
+ from hr_attendance as att
+ where employee_id = (select employee_id from hr_attendance where id=%s)
+ and action in ('sign_in','sign_out')
+ and name <= (select name from hr_attendance where id=%s)
+ order by name desc
+ limit 2
+ ''' % (id, id)
+ cr.execute(sql)
+ atts = cr.fetchall()
+ if not ((len(atts)==1 and atts[0][0] == 'sign_in') or (atts[0][0] != atts[1][0] and atts[0][1] != atts[1][1])):
+ return False
+ return True
+
+ _constraints = [(_altern_si_so, 'Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)', ['action'])]
+ _order = 'name desc'
+hr_attendance()
+
+class hr_holidays_status(osv.osv):
+ _name = "hr.holidays.status"
+ _description = "Holidays Status"
+ _columns = {
+ 'name' : fields.char('Holiday Status', size=64, required=True, translate=True),
+ }
+hr_holidays_status()
+
+class hr_holidays(osv.osv):
+ _name = "hr.holidays"
+ _description = "Holidays"
+ _columns = {
+ 'name' : fields.char('Description', required=True, size=64),
+ 'date_from' : fields.datetime('Vacation start day', required=True),
+ 'date_to' : fields.datetime('Vacation end day'),
+ 'holiday_status' : fields.many2one("hr.holidays.status", "Holiday's Status"),
+ 'employee_id' : fields.many2one('hr.employee', 'Employee', select=True),
+ }
+ _defaults = {
+ 'employee_id' : _employee_get
+ }
+ _order = 'date_from desc'
+hr_holidays()
+
+# vim:tw=0:noexpandtab
diff --git a/addons/hr/hr_bel_holidays_2005.xml b/addons/hr/hr_bel_holidays_2005.xml
new file mode 100644
index 00000000000..80cea7172a7
--- /dev/null
+++ b/addons/hr/hr_bel_holidays_2005.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<terp>
+ <data noupdate="1">
+ <record model="hr.holidays.status" id="jf">
+ <field name="name">Public holidays</field>
+ </record>
+
+ <record model="hr.holidays">
+ <field name="name">New Year</field>
+ <field name="date_from">2005-01-01 00:00</field>
+ <field name="date_to">2005-01-02 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Easter Monday</field>
+ <field name="date_from">2005-03-28 00:00</field>
+ <field name="date_to">2005-03-29 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Labor Day</field>
+ <field name="date_from">2005-05-01 00:00</field>
+ <field name="date_to">2005-05-02 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Ascension</field>
+ <field name="date_from">2005-05-05 00:00</field>
+ <field name="date_to">2005-05-06 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Ascension Day</field>
+ <field name="date_from">2005-05-16 00:00</field>
+ <field name="date_to">2005-05-17 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Pentecost Monday</field>
+ <field name="date_from">2005-05-16 00:00</field>
+ <field name="date_to">2005-05-17 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">National day</field>
+ <field name="date_from">2005-07-21 00:00</field>
+ <field name="date_to">2005-07-22 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">All Saints</field>
+ <field name="date_from">2005-11-01 00:00</field>
+ <field name="date_to">2005-11-02 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Christmas</field>
+ <field name="date_from">2005-12-25 00:00</field>
+ <field name="date_to">2005-12-26 00:00</field>
+ <field name="holiday_status" ref="jf" />
+ </record>
+ </data>
+</terp>
diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml
new file mode 100644
index 00000000000..f3b10528d39
--- /dev/null
+++ b/addons/hr/hr_demo.xml
@@ -0,0 +1,251 @@
+<?xml version="1.0"?>
+<terp>
+ <data noupdate="1">
+ <record model="res.groups">
+ <field name="name">HR</field>
+ </record>
+
+ <record model="hr.action.reason">
+ <field name="name">Start of shift</field>
+ <field name="action_type">sign_in</field>
+ </record>
+ <record model="hr.action.reason">
+ <field name="name">End of shift</field>
+ <field name="action_type">sign_out</field>
+ </record>
+ <record model="hr.action.reason">
+ <field name="name">Meal Break</field>
+ <field name="action_type">sign_in</field>
+ </record>
+ <record model="hr.action.reason">
+ <field name="name">Meal Break</field>
+ <field name="action_type">sign_out</field>
+ </record>
+ <record model="hr.action.reason">
+ <field name="name">Early exit (sick)</field>
+ <field name="action_type">sign_out</field>
+ </record>
+ <record model="hr.action.reason">
+ <field name="name">Early exit (work injury)</field>
+ <field name="action_type">sign_out</field>
+ </record>
+
+ <record model="hr.holidays.status">
+ <field name="name">Unpaid holidays</field>
+ </record>
+ <record model="hr.holidays.status" id="ca">
+ <field name="name">Annual holidays</field>
+ </record>
+ <record model="hr.holidays.status">
+ <field name="name">Sickness holidays</field>
+ </record>
+ <record model="hr.holidays.status" id="ma">
+ <field name="name">Maternity holidays</field>
+ </record>
+ <record model="hr.holidays.status">
+ <field name="name">Unauthorised holidays</field>
+ </record>
+
+
+ <!-- Example employee -->
+
+ <record model="hr.timesheet.group" id="timesheet_group1">
+ <field name="name">Default timesheet</field>
+ </record>
+
+ <record model="hr.employee" id="employee1">
+ <field name="name">Fabien Pinckaers</field>
+ <field name="regime">45</field>
+ <field name="user_id" search="[('login','=','admin')]" model="res.users"/>
+ <field name="holiday_max">25</field>
+ <fields name="workgroups" ref="timesheet_group1" />
+ </record>
+
+ <record model="hr.timesheet">
+ <field name="name">Monday morning</field>
+ <field name="dayofweek">0</field>
+ <field name="hour_from">08:00</field>
+ <field name="hour_to">12:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Monday evening</field>
+ <field name="dayofweek">0</field>
+ <field name="hour_from">13:00</field>
+ <field name="hour_to">18:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Tuesday morning</field>
+ <field name="dayofweek">1</field>
+ <field name="hour_from">08:00</field>
+ <field name="hour_to">12:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Tuesday evening</field>
+ <field name="dayofweek">1</field>
+ <field name="hour_from">13:00</field>
+ <field name="hour_to">18:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Wednesday morning</field>
+ <field name="dayofweek">2</field>
+ <field name="hour_from">08:00</field>
+ <field name="hour_to">12:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Wednesday evening</field>
+ <field name="dayofweek">2</field>
+ <field name="hour_from">13:00</field>
+ <field name="hour_to">18:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Thursday morning</field>
+ <field name="dayofweek">3</field>
+ <field name="hour_from">08:00</field>
+ <field name="hour_to">12:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Thursday evening</field>
+ <field name="hour_from">13:00</field>
+ <field name="hour_to">18:00</field>
+ <field name="dayofweek">3</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Friday morning</field>
+ <field name="dayofweek">4</field>
+ <field name="hour_from">08:00</field>
+ <field name="hour_to">12:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+ <record model="hr.timesheet">
+ <field name="name">Friday evening</field>
+ <field name="dayofweek">4</field>
+ <field name="hour_from">13:00</field>
+ <field name="hour_to">18:00</field>
+ <field name="tgroup_id" ref="timesheet_group1" />
+ </record>
+
+ <record model="hr.holidays">
+ <field name="name">Bali Trip</field>
+ <field name="date_from">2005-05-11 08:00</field>
+ <field name="date_to">2005-05-25 18:00</field>
+ <field name="holiday_status" ref="ca" />
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.holidays">
+ <field name="name">Paternity holiday</field>
+ <field name="date_from">2005-06-02 08:00</field>
+ <field name="date_to">2005-06-13 18:00</field>
+ <field name="holiday_status" ref="ma" />
+ <field name="employee_id" ref="employee1" />
+ </record>
+
+ <record model="hr.attendance">
+ <field name="name">2005-05-04 08:21</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-04 11:51</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-04 12:47</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-04 19:53</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-05 07:32</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-05 12:32</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-05 14:01</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-05 17:21</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-06 09:10</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-06 12:42</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-06 13:10</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-06 18:34</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-07 08:21</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-07 18:21</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-08 08:21</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-08 12:54</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-08 13:32</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-08 19:31</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-26 07:10</field>
+ <field name="action">sign_in</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ <record model="hr.attendance">
+ <field name="name">2005-05-26 12:34</field>
+ <field name="action">sign_out</field>
+ <field name="employee_id" ref="employee1" />
+ </record>
+ </data>
+</terp>
diff --git a/addons/hr/hr_report.xml b/addons/hr/hr_report.xml
new file mode 100644
index 00000000000..390f848d552
--- /dev/null
+++ b/addons/hr/hr_report.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<terp>
+ <data>
+ </data>
+</terp>
diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml
new file mode 100644
index 00000000000..9dadbef55f5
--- /dev/null
+++ b/addons/hr/hr_view.xml
@@ -0,0 +1,411 @@
+<?xml version="1.0" ?>
+<terp>
+ <data>
+ <menuitem name="Human Resources" id="menu_hr_root" icon="terp-hr"/>
+ <menuitem name="Human Resources/Configuration" groups="admin"/>
+
+ <!--
+ ==========
+ Employee
+ ==========
+ -->
+ <record model="ir.ui.view" id="view_employee_form">
+ <field name="name">hr.employee.form</field>
+ <field name="model">hr.employee</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Employee">
+ <notebook>
+ <page string="Information">
+ <separator string="General Information" colspan="4" />
+ <field name="name" select="1"/>
+ <field name="company_id"/>
+ <field name="user_id"/>
+ <field name="address_id"/>
+ <field name="notes" colspan="3"/>
+ <separator string="Working Data" colspan="4" />
+ <field name="regime" />
+ <field name="active" select="1" />
+ <field name="holiday_max" />
+ <field name="started" select="1" />
+ <field name="workgroups" colspan="4" />
+ <field name="state"/>
+ <field name="parent_id" select="1"/>
+ <field name="bank_account"/>
+ <group colspan="2" col="2">
+ <button string="Sign in !" name="sign_in" states="absent" type="object" />
+ <button string="Sign out !" name="sign_out" states="present" type="object" />
+ </group>
+ </page>
+ <page string="Holidays">
+ <field name="holidays" colspan="3"
+ widget="one2many_list"
+ nolabel="1" />
+ </page>
+ </notebook>
+ </form>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_employee_tree">
+ <field name="name">hr.employee.tree</field>
+ <field name="model">hr.employee</field>
+ <field name="type">tree</field>
+ <field name="field_parent">child_ids</field>
+ <field name="arch" type="xml">
+ <tree string="Employees">
+ <field name="name"/>
+ <field name="user_id"/>
+ <field name="address_id"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_view_employee_tree">
+ <field name="name">hr.employee</field>
+ <field name="res_model">hr.employee</field>
+ <field name="view_type">tree</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="view_employee_tree"/>
+ <field name="domain">[('parent_id','=',False)]</field>
+ </record>
+ <menuitem name="Human Resources/Hierarchical view of Employees" id="menu_open_view_employee_tree" action="open_view_employee_tree" groups="admin"/>
+
+ <record model="ir.ui.view" id="view_employee_list">
+ <field name="name">hr.employee.tree</field>
+ <field name="model">hr.employee</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Employees">
+ <field name="name"/>
+ <field name="user_id"/>
+ <field name="address_id"/>
+ <field name="state"/>
+ <field name="parent_id"/>
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_view_employee_list">
+ <field name="name">hr.employee</field>
+ <field name="res_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="view_employee_list"/>
+ </record>
+ <menuitem name="Human Resources/List of Employees" id="menu_open_view_employee_list" action="open_view_employee_list" groups="admin"/>
+
+ <!--
+ =======================
+ Employee architecture
+ =======================
+ -->
+ <record model="ir.ui.view" id="view_partner_tree2">
+ <field name="name">hr.employee.tree</field>
+ <field name="model">hr.employee</field>
+ <field name="type">tree</field>
+ <field name="field_parent">child_ids</field>
+ <field name="arch" type="xml">
+ <tree string="Employees">
+ <field name="name"/>
+ <field name="user_id"/>
+ <field name="address_id"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="action2">
+ <field name="name">Hierarchical view of this employee</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.employee</field>
+ <field name="domain">[('id','in',active_ids)]</field>
+ <field name="view_type">tree</field>
+ <field name="view_id" ref="view_partner_tree2"/>
+ </record>
+ <ir_set>
+ <field name="key" eval="'action'"/>
+ <field name="key2" eval="'client_action_multi'"/>
+ <field name="models" eval="['hr.employee']"/>
+ <field name="name">Employees Hierarchy</field>
+ <field name="value" eval="'ir.actions.act_window,'+str(action2)"/>
+ <field name="isobject" eval="True"/>
+ <field name="replace" eval="True"/>
+ </ir_set>
+
+
+ <!--
+ ============
+ Timesheets
+ ============
+ -->
+ <record model="ir.ui.view" id="create_group">
+ <field name="name">hr.timesheet.group.form</field>
+ <field name="model">hr.timesheet.group</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Timesheet group">
+ <field name="name" select="1"/>
+ <field name="manager" select="1"/>
+ <newline />
+ <separator string="Timesheets" colspan="4"/>
+ <newline />
+ <field name="timesheet_id" widget="one2many_list" nolabel="1" colspan="4"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="edit_workgroup">
+ <field name="name">hr.timesheet.group.form.open</field>
+ <field name="res_model">hr.timesheet.group</field>
+ <field name="view_type">form</field>
+ <field name="view_id" ref="create_group" />
+ </record>
+ <menuitem name="Human Resources/Configuration/Timesheet Groups" id="menu_edit_workgroup" action="edit_workgroup" groups="admin"/>
+
+ <record model="ir.ui.view" id="view_timesheet_tree">
+ <field name="name">hr.timesheet.tree</field>
+ <field name="model">hr.timesheet</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Timesheet">
+ <field name="name"/>
+ <field name="dayofweek"/>
+ <field name="hour_from"/>
+ <field name="hour_to"/>
+ </tree>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_timesheet_form">
+ <field name="name">hr.timesheet.form</field>
+ <field name="model">hr.timesheet</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Timesheet">
+ <field name="name" colspan="3" select="1"/>
+ <field name="date_from" />
+ <field name="dayofweek" select="1" />
+ <field name="hour_from"/>
+ <field name="hour_to"/>
+ </form>
+ </field>
+ </record>
+
+ <!-- Attendance -->
+ <record model="ir.ui.view" id="view_attendance_form">
+ <field name="name">hr.attendance.form</field>
+ <field name="model">hr.attendance</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Employee attendance">
+ <field name="employee_id" select="1"/>
+ <field name="name" colspan="3" select="1"/>
+ <field name="action" select="1"/>
+ <field name="action_desc" select="1"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_attendance_tree">
+ <field name="name">hr.attendance.tree</field>
+ <field name="model">hr.attendance</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Employee attendances">
+ <field name="name" />
+ <field name="action" />
+ <field name="action_desc" />
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_employee_category_form">
+ <field name="name">hr.employee.category.form</field>
+ <field name="model">hr.employee.category</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Employee Category">
+ <field name="name" select="1"/>
+ <field name="parent_id" select="1"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_employee_category_list">
+ <field name="name">hr.employee.category.list</field>
+ <field name="model">hr.employee.category</field>
+ <field name="type">tree</field>
+ <field name="priority" eval="8"/>
+ <field name="arch" type="xml">
+ <tree string="Employees Categories">
+ <field name="name" />
+ <field name="parent_id" />
+ </tree>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_employee_category_tree">
+ <field name="name">hr.employee.category.tree</field>
+ <field name="model">hr.employee.category</field>
+ <field name="type">tree</field>
+ <field name="field_parent">child_ids</field>
+ <field name="arch" type="xml">
+ <tree string="Employees Categories">
+ <field name="name" />
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_view_categ_form">
+ <field name="name">hr.employee.category.form.view</field>
+ <field name="res_model">hr.employee.category</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form,tree</field>
+ </record>
+ <menuitem name="Human Resources/Configuration/Categories of Employee" id="menu_view_employee_category_form" action="open_view_categ_form"/>
+
+ <record model="ir.actions.act_window" id="open_view_categ_tree">
+ <field name="name">hr.employee.category.form.view</field>
+ <field name="res_model">hr.employee.category</field>
+ <field name="view_type">tree</field>
+ <field name="view_id" ref="view_employee_category_tree"/>
+ <field name="domain">[('parent_id','=',False)]</field>
+ </record>
+ <menuitem name="Human Resources/Configuration/Categories of Employee/Categories Tree" id="menu_view_employee_category_tree" action="open_view_categ_tree"/>
+
+ <record model="ir.ui.view" id="view_attendance_who">
+ <field name="name">hr.attendance.tree</field>
+ <field name="model">hr.attendance</field>
+ <field name="type">tree</field>
+ <field name="priority" eval="3"/>
+ <field name="arch" type="xml">
+ <tree string="Employee attendance">
+ <field name="employee_id" />
+ <field name="name" />
+ <field name="action" />
+ <field name="action_desc" />
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_view_attendance">
+ <field name="name">hr.attendance.view</field>
+ <field name="res_model">hr.attendance</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form,tree</field>
+ </record>
+ <menuitem name="Human Resources/Attendances/Attendances" id="menu_open_view_attendance" action="open_view_attendance" groups="admin"/>
+
+
+ <!-- Holidays -->
+ <record model="ir.ui.view" id="edit_holiday">
+ <field name="name">hr.holidays.form</field>
+ <field name="model">hr.holidays</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Employee holidays">
+ <field name="name" colspan="3" select="1"/>
+ <field name="date_from" select="1"/>
+ <field name="date_to" select="1"/>
+ <newline />
+ <field name="holiday_status" select="1"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_holiday">
+ <field name="name">hr.holidays.tree</field>
+ <field name="model">hr.holidays</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Employee holidays">
+ <field name="name" />
+ <field name="date_from" />
+ <field name="date_to" />
+ <field name="holiday_status" />
+ </tree>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="ask_holiday">
+ <field name="name">hr.holidays.ask_form</field>
+ <field name="model">hr.holidays</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Vacations requests">
+ <field name="name" select="1"/>
+ <field name="holiday_status"/>
+ <field name="employee_id" colspan="3" select="1"/>
+ <newline />
+ <field name="date_from" select="1"/>
+ <field name="date_to" select="1"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_ask_holidays">
+ <field name="name">hr.holidays.ask_form</field>
+ <field name="res_model">hr.holidays</field>
+ <field name="view_type">form</field>
+ <field name="view_id" ref="ask_holiday" />
+ </record>
+ <menuitem name="Human Resources/Attendances/Vacation request"
+ id="menu_open_ask_holidays" action="open_ask_holidays" groups="admin"/>
+
+ <!-- Some Configuration (holiday type, attendance reasons) -->
+
+ <record model="ir.ui.view" id="edit_holiday_status">
+ <field name="name">hr.holidays.status.form</field>
+ <field name="model">hr.holidays.status</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Define holiday status">
+ <field name="name" colspan="3" select="1"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_holiday_status">
+ <field name="name">hr.holidays.status.tree</field>
+ <field name="model">hr.holidays.status</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Holiday status">
+ <field name="name"/>
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_view_holiday_status">
+ <field name="name">hr.holidays.status</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.holidays.status</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="view_holiday_status" />
+ </record>
+ <menuitem name="Human Resources/Configuration/Holiday Status"
+ id="menu_open_view_holiday_status" action="open_view_holiday_status"/>
+
+ <record model="ir.ui.view" id="edit_attendance_reason">
+ <field name="name">hr.action.reason.form</field>
+ <field name="model">hr.action.reason</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Define attendance reason">
+ <field name="name" colspan="3" select="1"/>
+ <field name="action_type" select="1"/>
+ </form>
+ </field>
+ </record>
+ <record model="ir.ui.view" id="view_attendance_reason">
+ <field name="name">hr.action.reason.tree</field>
+ <field name="model">hr.action.reason</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Attendance reasons">
+ <field name="name"/>
+ <field name="action_type" />
+ </tree>
+ </field>
+ </record>
+ <record model="ir.actions.act_window" id="open_view_attendance_reason">
+ <field name="name">hr.action.reason.tree</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.action.reason</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="view_attendance_reason" />
+ </record>
+ <menuitem name="Human Resources/Configuration/Attendance Reasons"
+ id="menu_open_view_attendance_reason" action="open_view_attendance_reason" groups="admin"/>
+
+ </data>
+</terp>
diff --git a/addons/hr/hr_wizard.xml b/addons/hr/hr_wizard.xml
new file mode 100644
index 00000000000..cf0daba16cc
--- /dev/null
+++ b/addons/hr/hr_wizard.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<terp>
+ <data>
+ <wizard string="Sign in / Sign out" model="hr.employee" name="hr.si_so" id="si_so"/>
+ <menuitem name="Human Resources/Attendances/Sign in\/out" action="si_so" type="wizard" groups="admin"/>
+
+ <wizard string="Print Timesheet by week" model="hr.employee" name="hr.print_week" keyword="client_print_multi" />
+ <wizard string="Print Timesheet by month" model="hr.employee" name="hr.print_month" keyword="client_print_multi" />
+
+ <wizard string="Print Attendance Error Report" model="hr.employee" name="hr.timesheet.attendance.report" keyword="client_print_multi" id="wizard_attendance_error" />
+ </data>
+</terp>
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>
diff --git a/addons/hr/wizard/__init__.py b/addons/hr/wizard/__init__.py
new file mode 100644
index 00000000000..a913b3411d3
--- /dev/null
+++ b/addons/hr/wizard/__init__.py
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# 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 print_byweek import wiz_byweek
+from print_bymonth import wiz_bymonth
+import print_attendance_error
+
+import sign_in_out
diff --git a/addons/hr/wizard/print_attendance_error.py b/addons/hr/wizard/print_attendance_error.py
new file mode 100644
index 00000000000..76bffd7fa98
--- /dev/null
+++ b/addons/hr/wizard/print_attendance_error.py
@@ -0,0 +1,58 @@
+##############################################################################
+#
+# Copyright (c) 2004-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.
+#
+##############################################################################
+
+import wizard
+import time
+
+_date_form = '''<?xml version="1.0"?>
+<form string="Select a time span">
+ <separator string="Analysis Information" colspan="4"/>
+ <field name="init_date"/>
+ <field name="end_date"/>
+ <field name="max_delay"/>
+ <label string="Bellow this delay, the error is considered as volunteer" colspan="2"/>
+</form>'''
+
+_date_fields = {
+ 'init_date': {'string':'Starting Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True},
+ 'end_date': {'string':'Ending Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True},
+ 'max_delay': {'string':'Max. Delay (Min)', 'type':'integer', 'default':lambda *a: 120, 'required':True},
+}
+
+class wiz_attendance(wizard.interface):
+ states = {
+ 'init': {
+ 'actions': [],
+ 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Attendance Report'),('end','Cancel') ]}
+ },
+ 'print': {
+ 'actions': [],
+ 'result': {'type': 'print', 'report': 'hr.timesheet.attendance.error', 'state':'end'}
+ }
+ }
+wiz_attendance('hr.timesheet.attendance.report')
+
diff --git a/addons/hr/wizard/print_bymonth.py b/addons/hr/wizard/print_bymonth.py
new file mode 100644
index 00000000000..68769882bd4
--- /dev/null
+++ b/addons/hr/wizard/print_bymonth.py
@@ -0,0 +1,55 @@
+##############################################################################
+#
+# Copyright (c) 2004-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.
+#
+##############################################################################
+
+import wizard
+import time
+
+_date_form = '''<?xml version="1.0"?>
+<form string="Select a month">
+ <separator string="Select a month" colspan="4"/>
+ <field name="month"/>
+ <field name="year"/>
+</form>'''
+
+_date_fields = {
+ 'month' : {'string' : 'Month', 'type' : 'selection', 'selection' : [(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], 'required':True },
+ 'year' : {'string' : 'Year', 'type' : 'integer', 'required':True, 'default' : lambda *a: time.gmtime()[0]},
+}
+
+class wiz_bymonth(wizard.interface):
+ states = {
+ 'init': {
+ 'actions': [],
+ 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Timesheet'),('end','Cancel')]}
+ },
+ 'print': {
+ 'actions': [],
+ 'result': {'type': 'print', 'report': 'hr.timesheet.bymonth', 'state': 'end'}
+ }
+ }
+wiz_bymonth('hr.print_month')
+
diff --git a/addons/hr/wizard/print_byweek.py b/addons/hr/wizard/print_byweek.py
new file mode 100644
index 00000000000..2d0796c863e
--- /dev/null
+++ b/addons/hr/wizard/print_byweek.py
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# Copyright (c) 2004-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.
+#
+##############################################################################
+
+import wizard
+import time
+
+_date_form = '''<?xml version="1.0"?>
+<form string="Select a time span">
+ <separator string="Select a starting and a end date" colspan="4"/>
+ <field name="init_date"/>
+ <newline/>
+ <field name="end_date"/>
+</form>'''
+
+_date_fields = {
+ 'init_date': {'string':'Starting Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True},
+ 'end_date': {'string':'Ending Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}
+}
+
+class wiz_byweek(wizard.interface):
+ states = {
+ 'init': {
+ 'actions': [],
+ 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Timesheet'),('end','Cancel') ]}
+ },
+ 'print': {
+ 'actions': [],
+ 'result': {'type': 'print', 'report': 'hr.timesheet.allweeks', 'state':'end'}
+ }
+ }
+wiz_byweek('hr.print_week')
+
diff --git a/addons/hr/wizard/sign_in_out.py b/addons/hr/wizard/sign_in_out.py
new file mode 100644
index 00000000000..6d5ffddcee7
--- /dev/null
+++ b/addons/hr/wizard/sign_in_out.py
@@ -0,0 +1,179 @@
+##############################################################################
+#
+# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# $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 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 wizard
+import netsvc
+
+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">
+ <seperator 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">
+ <seperator 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" />
+</form>'''
+
+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']:
+ 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']:
+ 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:noexpandtab:tw=0