Merge lp:~camptocamp/hr-timesheet/full-fill-timesheet-account-type-vre into lp:~hr-core-editors/hr-timesheet/6.1

Proposed by Vincent Renaville@camptocamp
Status: Superseded
Proposed branch: lp:~camptocamp/hr-timesheet/full-fill-timesheet-account-type-vre
Merge into: lp:~hr-core-editors/hr-timesheet/6.1
Diff against target: 7196 lines (+4884/-1282) (has conflicts)
65 files modified
hr_attendance_analysis/AUTHORS.txt (+1/-0)
hr_attendance_analysis/__init__.py (+26/-0)
hr_attendance_analysis/__openerp__.py (+51/-0)
hr_attendance_analysis/company_view.xml (+17/-0)
hr_attendance_analysis/hr_attendance.py (+419/-0)
hr_attendance_analysis/hr_attendance_view.xml (+80/-0)
hr_attendance_analysis/hr_contract.py (+56/-0)
hr_attendance_analysis/i18n/hr_attendance_analysis.pot (+564/-0)
hr_attendance_analysis/i18n/it.po (+611/-0)
hr_attendance_analysis/report/__init__.py (+21/-0)
hr_attendance_analysis/report/calendar_report.mako (+138/-0)
hr_attendance_analysis/report/calendar_report.py (+96/-0)
hr_attendance_analysis/reports.xml (+86/-0)
hr_attendance_analysis/resource.py (+102/-0)
hr_attendance_analysis/resource_view.xml (+52/-0)
hr_attendance_analysis/security/ir.model.access.csv (+3/-0)
hr_attendance_analysis/wizard/__init__.py (+23/-0)
hr_attendance_analysis/wizard/print_calendar_report.py (+366/-0)
hr_attendance_analysis/wizard/print_calendar_report.xml (+38/-0)
hr_timesheet_fulfill/__init__.py (+18/-25)
hr_timesheet_fulfill/__openerp__.py (+39/-50)
hr_timesheet_fulfill/wizard/__init__.py (+3/-0)
hr_timesheet_fulfill/wizard/timesheet_fulfill.py (+36/-44)
hr_timesheet_fulfill/wizard/timesheet_fulfill_view.xml (+55/-40)
hr_timesheet_holidays/__openerp__.py (+1/-1)
hr_timesheet_improvement/__init__.py (+22/-0)
hr_timesheet_improvement/__openerp__.py (+50/-0)
hr_timesheet_improvement/hr_attendance.py (+98/-0)
hr_timesheet_improvement/hr_timesheet.py (+51/-0)
hr_timesheet_improvement/hr_timesheet_view.xml (+17/-0)
hr_timesheet_print/__openerp__.py (+1/-1)
hr_timesheet_reminder/__init__.py (+15/-26)
hr_timesheet_reminder/__openerp__.py (+41/-43)
hr_timesheet_reminder/company.py (+51/-49)
hr_timesheet_reminder/hr_employee.py (+42/-53)
hr_timesheet_reminder/hr_employee_view.xml (+0/-1)
hr_timesheet_reminder/reminder.py (+100/-88)
hr_timesheet_reminder/report/__init__.py (+16/-25)
hr_timesheet_reminder/report/timesheet_status.py (+63/-65)
hr_timesheet_reminder/report/timesheet_status.rml (+157/-163)
hr_timesheet_reminder/timesheet_report.xml (+10/-10)
hr_timesheet_reminder/wizard/reminder_config.py (+37/-45)
hr_timesheet_reminder/wizard/reminder_config_view.xml (+38/-28)
hr_timesheet_reminder/wizard/reminder_status.py (+35/-44)
hr_timesheet_reminder/wizard/reminder_status_view.xml (+22/-21)
hr_timesheet_task/__init__.py (+15/-25)
hr_timesheet_task/__openerp__.py (+13/-9)
hr_timesheet_task/hr_analytic_timesheet_view.xml (+15/-28)
hr_timesheet_task/hr_timesheet_sheet_view.xml (+14/-12)
hr_timesheet_task/i18n/fr.po (+42/-0)
hr_timesheet_task/static/src/css/timesheet.css (+7/-0)
hr_timesheet_task/static/src/js/timesheet.js (+252/-0)
hr_timesheet_task/static/src/xml/timesheet.xml (+35/-0)
hr_timesheet_task/wizard/__init__.py (+0/-3)
hr_timesheet_task/wizard/associate_analytic_timesheet.py (+0/-48)
hr_timesheet_task/wizard/dissociate_analytic_timesheet.py (+0/-47)
hr_timesheet_task/wizard/hr_timesheet_invoice_create.py (+0/-46)
hr_timesheet_task/wizard/wizard_actions.xml (+0/-34)
timesheet_task/__init__.py (+15/-23)
timesheet_task/__openerp__.py (+25/-15)
timesheet_task/i18n/fr.po (+53/-0)
timesheet_task/project_task.py (+145/-104)
timesheet_task/project_task_view.xml (+203/-37)
timesheet_task/test/task_timesheet_indicators.yml (+282/-0)
timesheet_task/tmp_file_for_project_indicator.py (+0/-29)
Text conflict in hr_timesheet_reminder/company.py
To merge this branch: bzr merge lp:~camptocamp/hr-timesheet/full-fill-timesheet-account-type-vre
Reviewer Review Type Date Requested Status
Yannick Vaucher @ Camptocamp Needs Resubmitting
Joël Grand-Guillaume @ camptocamp code review, no tests Needs Fixing
Review via email: mp+194368@code.launchpad.net

This proposal has been superseded by a proposal from 2013-11-21.

Description of the change

change type from normal to contract to match project linked analytic account instead of standard analytic account

To post a comment you must log in.
Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

Hi,

Thanks for the contribs, I see a conflict here, can you check please ?

Regards,

review: Needs Fixing (code review, no tests)
Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

You might want to resubmit with only rev 58

rev 49 to 57 are certainly not meant to be in this MP

review: Needs Resubmitting
59. By Vincent Renaville@camptocamp

[FIX] change type test to be more generic

Unmerged revisions

59. By Vincent Renaville@camptocamp

[FIX] change type test to be more generic

58. By Vincent Renaville@camptocamp

[FIX] change type from normal to contract to match project linked analytic account instead of standard analytic account

57. By Launchpad Translations on behalf of hr-core-editors

Launchpad automatic translations update.

56. By Nicolas Bessi - Camptocamp

[IMP] in timesheet_task module:
    Fix propagation of indicator at project level when aa/timesheet line are altered
    Fix remaining_hours when a task is added on aa line without task

Add YAML tests
Improve tooltips of indicators

55. By Launchpad Translations on behalf of hr-core-editors

Launchpad automatic translations update.

54. By Yannick Vaucher @ Camptocamp

[FIX] hr-timesheet - add a dummy hr_analytic_timesheet_id field on hr.analytic.timesheet to be able to write name and project_id on project.task as project.task write method

53. By Lorenzo Battistini

[fix] if no next_attendance_ids found, avoid useless computations

52. By Lorenzo Battistini

[IMP] hr_attendance_analysis - summary

51. By Lorenzo Battistini

[FIX] consider attendances of type 'action' while searching for previous sign in

50. By Laetitia Gangloff (Acsone)

[FIX] fix the bug lp:1200183. allow duplicate contract. contract without trial period is not active contract

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'hr_attendance_analysis'
=== added file 'hr_attendance_analysis/AUTHORS.txt'
--- hr_attendance_analysis/AUTHORS.txt 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/AUTHORS.txt 2013-11-07 15:51:52 +0000
@@ -0,0 +1,1 @@
1Lorenzo Battistini <lorenzo.battistini@agilebg.com>
02
=== added file 'hr_attendance_analysis/__init__.py'
--- hr_attendance_analysis/__init__.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/__init__.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,26 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
5# Copyright (C) 2011-2013 Agile Business Group sagl
6# (<http://www.agilebg.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22import hr_attendance
23import hr_contract
24import report
25import wizard
26import resource
027
=== added file 'hr_attendance_analysis/__openerp__.py'
--- hr_attendance_analysis/__openerp__.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/__openerp__.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,51 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
5# Copyright (C) 2011-2013 Agile Business Group sagl
6# (<http://www.agilebg.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22{
23 'name': "HR - Attendance Analysis",
24 'version': '0.1',
25 'category': 'Generic Modules/Human Resources',
26 'summary': "Dynamic reports based on employee's attendances and contract's calendar",
27 'description': """
28Dynamic reports based on employee's attendances and contract's calendar.
29Among other things, it lets you see the amount of working hours outside and inside the contract's working schedule (overtime).
30It also provides a daily based report, showing the detailed and total hours compared to calendar hours.
31Several analysis settings can be configured, like:
32 - Tolerance for sign-in and sign-out
33 - Attendances and overtimes roundings
34 - Diffrent types of overtime, according to the overtime amount
35""",
36 'author': 'Agile Business Group',
37 'website': 'http://www.agilebg.com',
38 'license': 'AGPL-3',
39 "depends" : ['hr_attendance', 'hr_contract', 'hr_holidays', 'report_webkit'],
40 "data" : [
41 'company_view.xml',
42 'hr_attendance_view.xml',
43 'reports.xml',
44 'wizard/print_calendar_report.xml',
45 'resource_view.xml',
46 'security/ir.model.access.csv',
47 ],
48 "demo" : [],
49 "active": False,
50 "installable": True
51}
052
=== added file 'hr_attendance_analysis/company_view.xml'
--- hr_attendance_analysis/company_view.xml 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/company_view.xml 2013-11-07 15:51:52 +0000
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <record id="view_company_form" model="ir.ui.view">
5 <field name="inherit_id" ref="base.view_company_form"/>
6 <field name="name">view.company.form</field>
7 <field name="model">res.company</field>
8 <field name="arch" type="xml">
9 <page string="Configuration" position="inside">
10 <group name="attendance_analysis_grp" string="Attendance analysis">
11 <field name="working_time_precision" widget="float_time"/>
12 </group>
13 </page>
14 </field>
15 </record>
16 </data>
17</openerp>
018
=== added file 'hr_attendance_analysis/hr_attendance.py'
--- hr_attendance_analysis/hr_attendance.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/hr_attendance.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,419 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
5# Copyright (C) 2011-2013 Agile Business Group sagl
6# (<http://www.agilebg.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23from __future__ import division
24from openerp.osv import fields, orm
25from openerp.tools.translate import _
26import time
27from datetime import *
28import math
29
30import pytz
31
32class res_company(orm.Model):
33
34 _inherit = 'res.company'
35
36 _columns = {
37 'working_time_precision': fields.float('Working time precision', help='The precision used to analyse working times over working schedule (hh:mm)', required=True)
38 }
39
40 _defaults = {
41 'working_time_precision': 1.0 / 60 # hours
42 }
43
44
45class hr_attendance(orm.Model):
46
47 # ref: https://bugs.launchpad.net/openobject-client/+bug/887612
48 # test: 0.9853 - 0.0085
49 def float_time_convert(self, float_val):
50 hours = math.floor(abs(float_val))
51 mins = abs(float_val) - hours
52 mins = round(mins * 60)
53 if mins >= 60.0:
54 hours = hours + 1
55 mins = 0.0
56 float_time = '%02d:%02d' % (hours,mins)
57 return float_time
58
59 def float_to_datetime(self, float_val):
60 str_float = self.float_time_convert(float_val)
61 hours = int(str_float.split(':')[0])
62 minutes = int(str_float.split(':')[1])
63 days = 1
64 if hours / 24 > 0:
65 days += hours / 24
66 hours = hours % 24
67 return datetime(1900, 1, int(days), hours, minutes)
68
69 def float_to_timedelta(self, float_val):
70 str_time = self.float_time_convert(float_val)
71 return timedelta(0, int(str_time.split(':')[0]) * 60.0*60.0
72 + int(str_time.split(':')[1]) * 60.0)
73
74 def total_seconds(self, td):
75 return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
76
77 def time_difference(self, float_start_time, float_end_time):
78 if float_end_time < float_start_time:
79 raise orm.except_orm(_('Error'), _('End time %s < start time %s')
80 % (str(float_end_time),str(float_start_time)))
81 delta = self.float_to_datetime(float_end_time) - self.float_to_datetime(
82 float_start_time)
83 return self.total_seconds(delta) / 60.0 / 60.0
84
85 def time_sum(self, float_first_time, float_second_time):
86 str_first_time = self.float_time_convert(float_first_time)
87 first_timedelta = timedelta(0, int(str_first_time.split(':')[0]) * 60.0*60.0 +
88 int(str_first_time.split(':')[1]) * 60.0)
89 str_second_time = self.float_time_convert(float_second_time)
90 second_timedelta = timedelta(0, int(str_second_time.split(':')[0]) * 60.0*60.0 +
91 int(str_second_time.split(':')[1]) * 60.0)
92 return self.total_seconds(first_timedelta + second_timedelta) / 60.0 / 60.0
93
94 def _split_long_attendances(self, start_datetime, duration):
95 # start_datetime: datetime, duration: hours
96 # returns [(datetime, hours)]
97 res = []
98 if duration > 24:
99 res.append((start_datetime, 24))
100 res.extend(self._split_long_attendances(
101 start_datetime + timedelta(1), duration - 24))
102 else:
103 res.append((start_datetime, duration))
104 return res
105
106 def _split_no_recursive_attendance(self, start_datetime, duration, precision=0.25):
107 # start_datetime: datetime, duration: hours, precision: hours
108 # returns [(datetime, hours)]
109 res = []
110 while (duration > precision):
111 res.append((start_datetime, precision))
112 start_datetime += timedelta(days=0, seconds=0, microseconds=0, milliseconds=0,
113 minutes=0, hours=precision)
114 duration -= precision
115 if duration > precision / 2.0:
116 res.append((start_datetime, precision))
117 return res
118
119 def _split_attendance(self, start_datetime, duration, precision=0.25):
120 # start_datetime: datetime, duration: hours, precision: hours
121 # returns [(datetime, hours)]
122 res = []
123 if duration > precision:
124 res.append((start_datetime, precision))
125 res.extend(self._split_attendance(start_datetime + timedelta(0,0,0,0,0,precision), duration - precision, precision))
126 elif duration > precision / 2.0:
127 res.append((start_datetime, precision))
128 return res
129
130 def get_active_contracts(self, cr, uid, employee_id, date=datetime.now().strftime('%Y-%m-%d')):
131 contract_pool = self.pool.get('hr.contract')
132 active_contract_ids= contract_pool.search(cr, uid, [
133 '&',
134 ('employee_id', '=', employee_id),
135 '|',
136 '&',
137 ('date_start', '<=', date),
138 '|',
139 ('date_end', '>=', date),
140 ('date_end', '=', False),
141 '&',
142 '&',
143 ('trial_date_start', '!=', False),
144 ('trial_date_start', '<=', date),
145 '&',
146 ('trial_date_end', '!=', False),
147 ('trial_date_end', '>=', date),
148 ])
149 if len(active_contract_ids) > 1:
150 employee = self.pool.get('hr.employee').browse(cr,uid,employee_id)
151 raise orm.except_orm(_('Error'), _(
152 'Too many active contracts for employee %s'
153 ) % employee.name)
154 return active_contract_ids
155
156 def _ceil_rounding(self, rounding, datetime):
157 minutes = (datetime.minute / 60.0
158 + datetime.second / 60.0 / 60.0)
159 return math.ceil(minutes * rounding) / rounding
160
161 def _floor_rounding(self, rounding, datetime):
162 minutes = (datetime.minute / 60.0
163 + datetime.second / 60.0 / 60.0)
164 return math.floor(minutes * rounding) / rounding
165
166 def _get_attendance_duration(self, cr, uid, ids, field_name, arg, context=None):
167 res = {}
168 contract_pool = self.pool.get('hr.contract')
169 resource_pool = self.pool.get('resource.resource')
170 attendance_pool = self.pool.get('resource.calendar.attendance')
171 precision = self.pool.get('res.users').browse(cr, uid, uid).company_id.working_time_precision
172 # 2012.10.16 LF FIX : Get timezone from context
173 active_tz = pytz.timezone(context.get("tz","UTC") if context else "UTC")
174 str_now = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
175 for attendance_id in ids:
176 duration = 0.0
177 outside_calendar_duration = 0.0
178 inside_calendar_duration = 0.0
179 attendance = self.browse(cr, uid, attendance_id)
180 res[attendance.id] = {}
181 # 2012.10.16 LF FIX : Attendance in context timezone
182 attendance_start = datetime.strptime(
183 attendance.name, '%Y-%m-%d %H:%M:%S'
184 ).replace(tzinfo=pytz.utc).astimezone(active_tz)
185 next_attendance_date = str_now
186 next_attendance_ids = False
187 # should we compute for sign out too?
188 if attendance.action == 'sign_in':
189 next_attendance_ids = self.search(cr, uid, [
190 ('employee_id', '=', attendance.employee_id.id),
191 ('name', '>', attendance.name)], order='name')
192 if next_attendance_ids:
193 next_attendance = self.browse(cr, uid, next_attendance_ids[0])
194 if next_attendance.action == 'sign_in':
195 # 2012.10.16 LF FIX : Attendance in context timezone
196 raise orm.except_orm(_('Error'), _(
197 'Incongruent data: sign-in %s is followed by another sign-in'
198 ) % attendance_start)
199 next_attendance_date = next_attendance.name
200 # 2012.10.16 LF FIX : Attendance in context timezone
201 attendance_stop = datetime.strptime(
202 next_attendance_date, '%Y-%m-%d %H:%M:%S'
203 ).replace(tzinfo=pytz.utc).astimezone(active_tz)
204 duration_delta = attendance_stop - attendance_start
205 duration = self.total_seconds(duration_delta) / 60.0 / 60.0
206 duration = round(duration / precision) * precision
207 res[attendance.id]['duration'] = duration
208 res[attendance.id]['end_datetime'] = next_attendance_date
209 # If contract is not specified: working days = 24/7
210 res[attendance.id]['inside_calendar_duration'] = duration
211 res[attendance.id]['outside_calendar_duration'] = 0.0
212
213 active_contract_ids = self.get_active_contracts(
214 cr, uid, attendance.employee_id.id, date=str_now[:10])
215
216 if active_contract_ids and next_attendance_ids:
217 contract = contract_pool.browse(cr, uid, active_contract_ids[0])
218 if contract.working_hours:
219 # TODO applicare prima arrotondamento o tolleranza?
220 if contract.working_hours.attendance_rounding:
221 float_attendance_rounding = float(contract.working_hours.attendance_rounding)
222 rounded_start_hour = self._ceil_rounding(
223 float_attendance_rounding, attendance_start)
224 rounded_stop_hour = self._floor_rounding(
225 float_attendance_rounding, attendance_stop)
226
227 if abs(1- rounded_start_hour) < 0.01: # if shift == 1 hour
228 attendance_start = datetime(attendance_start.year, attendance_start.month,
229 attendance_start.day, attendance_start.hour + 1)
230 else:
231 attendance_start = datetime(attendance_start.year, attendance_start.month,
232 attendance_start.day, attendance_start.hour, int(round(rounded_start_hour * 60.0)))
233
234 attendance_stop = datetime(attendance_stop.year, attendance_stop.month,
235 attendance_stop.day, attendance_stop.hour,
236 int(round(rounded_stop_hour * 60.0)))
237
238 # again
239 duration_delta = attendance_stop - attendance_start
240 duration = self.total_seconds(duration_delta) / 60.0 / 60.0
241 duration = round(duration / precision) * precision
242 res[attendance.id]['duration'] = duration
243
244 res[attendance.id]['inside_calendar_duration'] = 0.0
245 res[attendance.id]['outside_calendar_duration'] = 0.0
246 calendar_id = contract.working_hours.id
247 intervals_within = 0
248
249 # split attendance in intervals = precision
250 # 2012.10.16 LF FIX : no recursion in split attendance
251 splitted_attendances = self._split_no_recursive_attendance(
252 attendance_start, duration, precision)
253 counter = 0
254 for atomic_attendance in splitted_attendances:
255 counter += 1
256 centered_attendance = atomic_attendance[0] + timedelta(
257 0,0,0,0,0, atomic_attendance[1] / 2.0)
258 centered_attendance_hour = (
259 centered_attendance.hour + centered_attendance.minute / 60.0
260 + centered_attendance.second / 60.0 / 60.0
261 )
262 # check if centered_attendance is within a working schedule
263 # 2012.10.16 LF FIX : weekday must be single character not int
264 weekday_char = str(unichr(centered_attendance.weekday() + 48))
265 matched_schedule_ids = attendance_pool.search(cr, uid, [
266 '&',
267 '|',
268 ('date_from', '=', False),
269 ('date_from','<=',centered_attendance.date()),
270 '|',
271 ('dayofweek', '=', False),
272 ('dayofweek','=',weekday_char),
273 ('calendar_id','=',calendar_id),
274 ('hour_to','>=',centered_attendance_hour),
275 ('hour_from','<=',centered_attendance_hour),
276 ])
277 if len(matched_schedule_ids) > 1:
278 raise orm.except_orm(_('Error'),
279 _('Wrongly configured working schedule with id %s') % str(calendar_id))
280 if matched_schedule_ids:
281 intervals_within += 1
282 # sign in tolerance
283 if intervals_within == 1:
284 calendar_attendance = attendance_pool.browse(cr, uid, matched_schedule_ids[0])
285 attendance_start_hour = (
286 attendance_start.hour + attendance_start.minute / 60.0
287 + attendance_start.second / 60.0 / 60.0
288 )
289 if attendance_start_hour >= (
290 calendar_attendance.hour_from and
291 (attendance_start_hour - (calendar_attendance.hour_from +
292 calendar_attendance.tolerance_to)) < 0.01
293 ): # handling float roundings (<=)
294 additional_intervals = round(
295 (attendance_start_hour - calendar_attendance.hour_from) / precision)
296 intervals_within += additional_intervals
297 res[attendance.id]['duration'] = self.time_sum(
298 res[attendance.id]['duration'], additional_intervals * precision)
299 # sign out tolerance
300 if len(splitted_attendances) == counter:
301 attendance_stop_hour = (
302 attendance_stop.hour + attendance_stop.minute / 60.0
303 + attendance_stop.second / 60.0 / 60.0
304 )
305 calendar_attendance = attendance_pool.browse(cr, uid, matched_schedule_ids[0])
306 if attendance_stop_hour <= (
307 calendar_attendance.hour_to and
308 (attendance_stop_hour - (calendar_attendance.hour_to -
309 calendar_attendance.tolerance_from)) > -0.01
310 ): # handling float roundings (>=)
311 additional_intervals = round(
312 (calendar_attendance.hour_to - attendance_stop_hour) / precision)
313 intervals_within += additional_intervals
314 res[attendance.id]['duration'] = self.time_sum(
315 res[attendance.id]['duration'], additional_intervals * precision)
316
317 res[attendance.id]['inside_calendar_duration'] = intervals_within * precision
318 # make difference using time in order to avoid rounding errors
319 # inside_calendar_duration can't be > duration
320 res[attendance.id]['outside_calendar_duration'] = self.time_difference(
321 res[attendance.id]['inside_calendar_duration'],
322 res[attendance.id]['duration'])
323
324 if contract.working_hours.overtime_rounding:
325 if res[attendance.id]['outside_calendar_duration']:
326 overtime = res[attendance.id]['outside_calendar_duration']
327 if contract.working_hours.overtime_rounding_tolerance:
328 overtime = self.time_sum(overtime,
329 contract.working_hours.overtime_rounding_tolerance)
330 float_overtime_rounding = float(contract.working_hours.overtime_rounding)
331 res[attendance.id]['outside_calendar_duration'] = math.floor(
332 overtime * float_overtime_rounding) / float_overtime_rounding
333
334 return res
335
336 def _get_by_contracts(self, cr, uid, ids, context=None):
337 attendance_ids = []
338 attendance_pool = self.pool.get('hr.attendance')
339 for contract in self.pool.get('hr.contract').browse(cr, uid, ids, context=context):
340 att_ids = attendance_pool.search(cr, uid, [('employee_id', '=', contract.employee_id.id)])
341 for att_id in att_ids:
342 if att_id not in attendance_ids:
343 attendance_ids.append(att_id)
344 return attendance_ids
345
346 def _get_by_calendars(self, cr, uid, ids, context=None):
347 attendance_ids = []
348 attendance_pool = self.pool.get('hr.attendance')
349 contract_pool = self.pool.get('hr.contract')
350 for calendar in self.pool.get('resource.calendar').browse(cr, uid, ids, context=context):
351 contract_ids = contract_pool.search(cr, uid, [('working_hours', '=', calendar.id)])
352 att_ids = attendance_pool._get_by_contracts(cr, uid, contract_ids, context=None)
353 for att_id in att_ids:
354 if att_id not in attendance_ids:
355 attendance_ids.append(att_id)
356 return attendance_ids
357
358 def _get_by_calendar_attendances(self, cr, uid, ids, context=None):
359 attendance_ids = []
360 attendance_pool = self.pool.get('hr.attendance')
361 for calendar_attendance in self.pool.get('resource.calendar.attendance').browse(cr, uid, ids, context=context):
362 att_ids = attendance_pool._get_by_calendars(cr, uid, [calendar_attendance.calendar_id.id], context=None)
363 for att_id in att_ids:
364 if att_id not in attendance_ids:
365 attendance_ids.append(att_id)
366 return attendance_ids
367
368 def _get_attendances(self, cr, uid, ids, context=None):
369 attendance_ids = []
370 for attendance in self.browse(cr, uid, ids, context=context):
371 if attendance.action == 'sign_in' and attendance.id not in attendance_ids:
372 attendance_ids.append(attendance.id)
373 elif attendance.action == 'sign_out':
374 previous_attendance_ids = self.search(cr, uid, [
375 ('employee_id', '=', attendance.employee_id.id),
376 ('name', '<', attendance.name),
377 ('action', '=', 'sign_in'),
378 ], order='name')
379 if previous_attendance_ids and previous_attendance_ids[len(previous_attendance_ids) - 1] not in attendance_ids:
380 attendance_ids.append(previous_attendance_ids[len(previous_attendance_ids) - 1])
381 return attendance_ids
382
383 _inherit = "hr.attendance"
384
385 _columns = {
386 'duration': fields.function(_get_attendance_duration, method=True, multi='duration', string="Attendance duration",
387 store={
388 'hr.attendance': (_get_attendances, ['name', 'action', 'employee_id'], 20),
389 'hr.contract': (_get_by_contracts, ['employee_id', 'date_start', 'date_end', 'trial_date_start', 'trial_date_end', 'working_hours'], 20),
390 'resource.calendar': (_get_by_calendars, ['attendance_ids'], 20),
391 'resource.calendar.attendance': (_get_by_calendar_attendances, ['dayofweek', 'date_from', 'hour_from', 'hour_to', 'calendar_id'], 20),
392 }
393 ),
394 'end_datetime': fields.function(_get_attendance_duration, method=True, multi='duration', type="datetime", string="End date time",
395 store={
396 'hr.attendance': (_get_attendances, ['name', 'action', 'employee_id'], 20),
397 'hr.contract': (_get_by_contracts, ['employee_id', 'date_start', 'date_end', 'trial_date_start', 'trial_date_end', 'working_hours'], 20),
398 'resource.calendar': (_get_by_calendars, ['attendance_ids'], 20),
399 'resource.calendar.attendance': (_get_by_calendar_attendances, ['dayofweek', 'date_from', 'hour_from', 'hour_to', 'calendar_id'], 20),
400 }),
401 'outside_calendar_duration': fields.function(_get_attendance_duration, method=True, multi='duration',
402 string="Overtime",
403 store={
404 'hr.attendance': (_get_attendances, ['name', 'action', 'employee_id'], 20),
405 'hr.contract': (_get_by_contracts, ['employee_id', 'date_start', 'date_end', 'trial_date_start', 'trial_date_end', 'working_hours'], 20),
406 'resource.calendar': (_get_by_calendars, ['attendance_ids'], 20),
407 'resource.calendar.attendance': (_get_by_calendar_attendances, ['dayofweek', 'date_from', 'hour_from', 'hour_to', 'calendar_id'], 20),
408 }),
409 'inside_calendar_duration': fields.function(_get_attendance_duration, method=True, multi='duration',
410 string="Duration within working schedule",
411 store={
412 'hr.attendance': (_get_attendances, ['name', 'action', 'employee_id'], 20),
413 'hr.contract': (_get_by_contracts, ['employee_id', 'date_start', 'date_end', 'trial_date_start', 'trial_date_end', 'working_hours'], 20),
414 'resource.calendar': (_get_by_calendars, ['attendance_ids'], 20),
415 'resource.calendar.attendance': (_get_by_calendar_attendances, ['dayofweek', 'date_from', 'hour_from', 'hour_to', 'calendar_id'], 20),
416 }),
417 }
418
419
0420
=== added file 'hr_attendance_analysis/hr_attendance_view.xml'
--- hr_attendance_analysis/hr_attendance_view.xml 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/hr_attendance_view.xml 2013-11-07 15:51:52 +0000
@@ -0,0 +1,80 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="view_attendance_form" model="ir.ui.view">
6 <field name="name">hr.attendance.form</field>
7 <field name="model">hr.attendance</field>
8 <field name="inherit_id" ref="hr_attendance.view_attendance_form"></field>
9 <field name="arch" type="xml">
10 <field name="action_desc" position="after">
11 <field name="duration" widget="float_time"/>
12 <field name="outside_calendar_duration" widget="float_time"/>
13 <field name="inside_calendar_duration" widget="float_time" />
14 </field>
15 </field>
16 </record>
17
18 <record id="view_attendance_analysis" model="ir.ui.view">
19 <field name="name">hr.attendance.analysis</field>
20 <field name="model">hr.attendance</field>
21 <field name="priority" eval="17"/>
22 <field name="arch" type="xml">
23 <tree string="Employee attendances analysis">
24 <field name="employee_id" />
25 <field name="name" string="Start date time"/>
26 <field name="end_datetime"/>
27 <field name="duration" sum="Total hours" widget="float_time"/>
28 <field name="outside_calendar_duration" sum="Overtime" widget="float_time"/>
29 <field name="inside_calendar_duration" sum="Within working schedule" widget="float_time" />
30 <field name="day" invisible="1"/>
31 </tree>
32 </field>
33 </record>
34
35 <record model="ir.ui.view" id="view_hr_attendance_filter">
36 <field name="name">view_hr_attendance_filter</field>
37 <field name="model">hr.attendance</field>
38 <field name="arch" type="xml">
39 <search string="Hr Attendance Search">
40 <filter icon="terp-go-today" string="Today" name="today" domain="[('name::date','=',current_date)]" />
41 <separator orientation="vertical"/>
42 <field name="employee_id"/>
43 <field name="name" string="Start date time"/>
44 <field name="end_datetime"/>
45 <newline/>
46 <group expand="0" string="Group By...">
47 <filter name="employee" string="Employee" icon="terp-personal" domain="[]" context="{'group_by':'employee_id'}"/>
48 <separator orientation="vertical"/>
49 <filter string="Day" icon="terp-go-today" domain="[]" context="{'group_by':'day'}"/>
50 </group>
51 </search>
52 </field>
53 </record>
54
55 <record model="ir.ui.view" id="view_hr_attendance_calendar">
56 <field name="name">view_hr_attendance.calendar</field>
57 <field name="model">hr.attendance</field>
58 <field name="arch" type="xml">
59 <calendar string="Calendar View" date_start="name" date_stop="end_datetime" color="employee_id">
60 <field name="duration" />
61 <field name="outside_calendar_duration" />
62 <field name="inside_calendar_duration" />
63 </calendar>
64 </field>
65 </record>
66
67 <record id="open_view_attendance" model="ir.actions.act_window">
68 <field name="name">Attendances analysis</field>
69 <field name="res_model">hr.attendance</field>
70 <field name="view_type">form</field>
71 <field name="view_mode">tree,form,calendar</field>
72 <field name="domain">[('action', '=', 'sign_in')]</field>
73 <field name="view_id" ref="view_attendance_analysis"/>
74 <field name="search_view_id" ref="view_hr_attendance_filter" />
75 </record>
76 <menuitem action="open_view_attendance" id="menu_open_view_attendance" parent="hr_attendance.menu_hr_attendance" groups="base.group_hr_manager"/>
77 <menuitem action="resource.action_resource_calendar_form" id="menu_view_resource_calendar" parent="hr_contract.next_id_56" sequence="1"/>
78
79 </data>
80</openerp>
081
=== added file 'hr_attendance_analysis/hr_contract.py'
--- hr_attendance_analysis/hr_contract.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/hr_contract.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,56 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Authors: Stéphane Bidoul & Laetitia Gangloff
5# Copyright (c) 2013 Acsone SA/NV (http://www.acsone.eu)
6# All Rights Reserved
7#
8# WARNING: This program as such is intended to be used by professional
9# programmers who take the whole responsibility of assessing all potential
10# consequences resulting from its eventual inadequacies and bugs.
11# End users who are looking for a ready-to-use solution with commercial
12# guarantees and support are strongly advised to contact a Free Software
13# Service Company.
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU Affero General Public License as
17# published by the Free Software Foundation, either version 3 of the
18# License, or (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU Affero General Public License for more details.
24#
25# You should have received a copy of the GNU Affero General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
27#
28##############################################################################
29import datetime
30
31from openerp.osv import fields, orm
32
33
34class hr_contract(orm.Model):
35 _inherit = 'hr.contract'
36
37 def copy(self, cr, uid, id, defaults, context=None):
38 """
39 When duplicate a contract set the start date to the last end date + 1 day.
40 If the last end date is False, do nothing
41 """
42 contract = self.browse(cr, uid, id, context=context)
43 end_date_contract_id = self.search(cr, uid, [('employee_id', '=', contract.employee_id.id), ], limit=1, order='date_end desc', context=context)
44 last_end_date = False
45 if end_date_contract_id:
46 contract = self.browse(cr, uid, end_date_contract_id, context=context)
47 last_end_date = contract[0].date_end
48
49 if last_end_date:
50 defaults['date_start'] = datetime.datetime.strftime(datetime.datetime.strptime(last_end_date, "%Y-%m-%d") + datetime.timedelta(days=1), "%Y-%m-%d")
51 defaults['date_end'] = False
52 defaults['trial_date_start'] = False
53 defaults['trial_date_end'] = False
54 return super(hr_contract, self).copy(cr, uid, id, defaults, context=context)
55
56# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
057
=== added directory 'hr_attendance_analysis/i18n'
=== added file 'hr_attendance_analysis/i18n/hr_attendance_analysis.pot'
--- hr_attendance_analysis/i18n/hr_attendance_analysis.pot 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/i18n/hr_attendance_analysis.pot 2013-11-07 15:51:52 +0000
@@ -0,0 +1,564 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * hr_attendance_analysis
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 6.0.3\n"
8"Report-Msgid-Bugs-To: support@openerp.com\n"
9"POT-Creation-Date: 2011-12-23 10:03+0000\n"
10"PO-Revision-Date: 2011-12-23 10:03+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: hr_attendance_analysis
19#: code:addons/hr_attendance_analysis/hr_attendance.py:123
20#, python-format
21msgid "Incongruent data"
22msgstr ""
23
24#. module: hr_attendance_analysis
25#: code:addons/hr_attendance_analysis/hr_attendance.py:87
26#: code:addons/hr_attendance_analysis/hr_attendance.py:123
27#: code:addons/hr_attendance_analysis/hr_attendance.py:189
28#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:58
29#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:129
30#, python-format
31msgid "Error"
32msgstr ""
33
34#. module: hr_attendance_analysis
35#: help:res.company,working_time_precision:0
36msgid "The precision used to analyse working times over working schedule"
37msgstr ""
38
39#. module: hr_attendance_analysis
40#: view:hr.attendance:0
41msgid "Group By..."
42msgstr ""
43
44#. module: hr_attendance_analysis
45#: code:addons/hr_attendance_analysis/report/calendar_report.py:47
46#, python-format
47msgid "Sunday"
48msgstr ""
49
50#. module: hr_attendance_analysis
51#: field:hr.attendance,end_datetime:0
52msgid "End date time"
53msgstr ""
54
55#. module: hr_attendance_analysis
56#: view:hr.attendance:0
57msgid "Today"
58msgstr ""
59
60#. module: hr_attendance_analysis
61#: view:hr.attendance:0
62msgid "Within working schedule"
63msgstr ""
64
65#. module: hr_attendance_analysis
66#: selection:resource.calendar,attendance_rounding:0
67#: selection:resource.calendar,leave_rounding:0
68#: selection:resource.calendar,overtime_rounding:0
69msgid "20"
70msgstr ""
71
72#. module: hr_attendance_analysis
73#: code:addons/hr_attendance_analysis/report/calendar_report.py:43
74#, python-format
75msgid "Friday"
76msgstr ""
77
78#. module: hr_attendance_analysis
79#: model:ir.model,name:hr_attendance_analysis.model_attendance_analysis_wizard_calendar_report
80msgid "attendance_analysis.wizard.calendar_report"
81msgstr ""
82
83#. module: hr_attendance_analysis
84#: selection:resource.calendar,attendance_rounding:0
85#: selection:resource.calendar,leave_rounding:0
86#: selection:resource.calendar,overtime_rounding:0
87msgid "8"
88msgstr ""
89
90#. module: hr_attendance_analysis
91#: view:hr.attendance:0
92msgid "Day"
93msgstr ""
94
95#. module: hr_attendance_analysis
96#: report:addons/hr_attendance_analysis/report/calendar_report.mako:36
97msgid "Leave"
98msgstr ""
99
100#. module: hr_attendance_analysis
101#: field:resource.calendar.overtime.type,limit:0
102msgid "Limit"
103msgstr ""
104
105#. module: hr_attendance_analysis
106#: selection:resource.calendar,attendance_rounding:0
107#: selection:resource.calendar,leave_rounding:0
108#: selection:resource.calendar,overtime_rounding:0
109msgid "15"
110msgstr ""
111
112#. module: hr_attendance_analysis
113#: help:resource.calendar,attendance_rounding:0
114msgid "For instance, using rounding = 15 minutes, every sign in will be rounded to the following quarter hour and every sign out to the previous quarter hour"
115msgstr ""
116
117#. module: hr_attendance_analysis
118#: report:addons/hr_attendance_analysis/report/calendar_report.mako:80
119msgid "Totals"
120msgstr ""
121
122#. module: hr_attendance_analysis
123#: help:resource.calendar.attendance,tolerance_to:0
124msgid "Sign in done in the interval \"Work from + Tolerance to\" will be considered done at \"Work from\""
125msgstr ""
126
127#. module: hr_attendance_analysis
128#: field:resource.calendar.attendance,tolerance_from:0
129msgid "Tolerance from"
130msgstr ""
131
132#. module: hr_attendance_analysis
133#: field:attendance_analysis.wizard.calendar_report,from_date:0
134msgid "From date"
135msgstr ""
136
137#. module: hr_attendance_analysis
138#: code:addons/hr_attendance_analysis/hr_attendance.py:87
139#, python-format
140msgid "Too many active contracts for employee %s"
141msgstr ""
142
143#. module: hr_attendance_analysis
144#: view:hr.attendance:0
145msgid "Calendar View"
146msgstr ""
147
148#. module: hr_attendance_analysis
149#: view:resource.calendar:0
150msgid "Roundings"
151msgstr ""
152
153#. module: hr_attendance_analysis
154#: report:addons/hr_attendance_analysis/report/calendar_report.mako:25
155msgid "Third Sign In"
156msgstr ""
157
158#. module: hr_attendance_analysis
159#: model:ir.model,name:hr_attendance_analysis.model_hr_attendance
160msgid "Attendance"
161msgstr ""
162
163#. module: hr_attendance_analysis
164#: constraint:res.company:0
165msgid "Error! You can not create recursive companies."
166msgstr ""
167
168#. module: hr_attendance_analysis
169#: report:addons/hr_attendance_analysis/report/calendar_report.mako:35
170msgid "Negative"
171msgstr ""
172
173#. module: hr_attendance_analysis
174#: view:hr.attendance:0
175msgid "Employee"
176msgstr ""
177
178#. module: hr_attendance_analysis
179#: view:resource.calendar:0
180msgid "Type"
181msgstr ""
182
183#. module: hr_attendance_analysis
184#: view:hr.attendance:0
185msgid "Hr Attendance Search"
186msgstr ""
187
188#. module: hr_attendance_analysis
189#: view:hr.attendance:0
190msgid "Start date time"
191msgstr ""
192
193#. module: hr_attendance_analysis
194#: help:resource.calendar,overtime_rounding:0
195msgid "Setting rounding = 30 minutes, an overtime of 29 minutes will be considered as 0 minutes, 31 minutes as 30 minutes, 61 minutes as 1 hour and so on"
196msgstr ""
197
198#. module: hr_attendance_analysis
199#: field:resource.calendar,attendance_rounding:0
200msgid "Attendance rounding"
201msgstr ""
202
203#. module: hr_attendance_analysis
204#: selection:resource.calendar,attendance_rounding:0
205#: selection:resource.calendar,leave_rounding:0
206#: selection:resource.calendar,overtime_rounding:0
207msgid "3"
208msgstr ""
209
210#. module: hr_attendance_analysis
211#: constraint:hr.attendance:0
212msgid "Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)"
213msgstr ""
214
215#. module: hr_attendance_analysis
216#: view:res.company:0
217msgid "Configuration"
218msgstr ""
219
220#. module: hr_attendance_analysis
221#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:58
222#, python-format
223msgid "From date must be < to date"
224msgstr ""
225
226#. module: hr_attendance_analysis
227#: view:hr.attendance:0
228msgid "Total hours"
229msgstr ""
230
231#. module: hr_attendance_analysis
232#: field:res.company,working_time_precision:0
233msgid "Working time precision"
234msgstr ""
235
236#. module: hr_attendance_analysis
237#: view:hr.attendance:0
238msgid "Employee attendances analysis"
239msgstr ""
240
241#. module: hr_attendance_analysis
242#: field:resource.calendar.attendance,tolerance_to:0
243msgid "Tolerance to"
244msgstr ""
245
246#. module: hr_attendance_analysis
247#: help:resource.calendar,leave_rounding:0
248msgid "On the contrary of overtime rounding, using rounding = 15 minutes, a leave of 1 minute will be considered as 15 minutes, 16 minutes as 30 minutes and so on"
249msgstr ""
250
251#. module: hr_attendance_analysis
252#: model:ir.actions.report.xml,name:hr_attendance_analysis.attendance_analysis_report_id
253msgid "Attendances Analysis"
254msgstr ""
255
256#. module: hr_attendance_analysis
257#: selection:resource.calendar,attendance_rounding:0
258#: selection:resource.calendar,leave_rounding:0
259#: selection:resource.calendar,overtime_rounding:0
260msgid "30"
261msgstr ""
262
263#. module: hr_attendance_analysis
264#: report:addons/hr_attendance_analysis/report/calendar_report.mako:110
265#: model:ir.model,name:hr_attendance_analysis.model_resource_calendar_overtime_type
266msgid "Overtime type"
267msgstr ""
268
269#. module: hr_attendance_analysis
270#: field:hr.attendance,inside_calendar_duration:0
271msgid "Duration within working schedule"
272msgstr ""
273
274#. module: hr_attendance_analysis
275#: field:resource.calendar,leave_rounding:0
276msgid "Leave rounding"
277msgstr ""
278
279#. module: hr_attendance_analysis
280#: field:resource.calendar.overtime.type,calendar_id:0
281msgid "Calendar"
282msgstr ""
283
284#. module: hr_attendance_analysis
285#: report:addons/hr_attendance_analysis/report/calendar_report.mako:32
286msgid "Due"
287msgstr ""
288
289#. module: hr_attendance_analysis
290#: view:attendance_analysis.wizard.calendar_report:0
291#: model:ir.actions.act_window,name:hr_attendance_analysis.action_wizard_calendar_report
292#: model:ir.ui.menu,name:hr_attendance_analysis.menu_action_wizard_calendar_report
293msgid "Attendances Analysis Calendar"
294msgstr ""
295
296#. module: hr_attendance_analysis
297#: selection:resource.calendar,attendance_rounding:0
298#: selection:resource.calendar,leave_rounding:0
299#: selection:resource.calendar,overtime_rounding:0
300msgid "60"
301msgstr ""
302
303#. module: hr_attendance_analysis
304#: view:resource.calendar:0
305#: field:resource.calendar,overtime_type_ids:0
306msgid "Overtime types"
307msgstr ""
308
309#. module: hr_attendance_analysis
310#: code:addons/hr_attendance_analysis/report/calendar_report.py:35
311#, python-format
312msgid "Monday"
313msgstr ""
314
315#. module: hr_attendance_analysis
316#: field:attendance_analysis.wizard.calendar_report,employee_ids:0
317msgid "unknown"
318msgstr ""
319
320#. module: hr_attendance_analysis
321#: report:addons/hr_attendance_analysis/report/calendar_report.mako:18
322msgid "First Sign Out"
323msgstr ""
324
325#. module: hr_attendance_analysis
326#: report:addons/hr_attendance_analysis/report/calendar_report.mako:15
327msgid "Day of week"
328msgstr ""
329
330#. module: hr_attendance_analysis
331#: field:resource.calendar,overtime_rounding_tolerance:0
332msgid "Overtime rounding tolerance"
333msgstr ""
334
335#. module: hr_attendance_analysis
336#: report:addons/hr_attendance_analysis/report/calendar_report.mako:29
337msgid "Fourth Sign In"
338msgstr ""
339
340#. module: hr_attendance_analysis
341#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:130
342#, python-format
343msgid "%s: 'Work to' is < 'Work from'"
344msgstr ""
345
346#. module: hr_attendance_analysis
347#: selection:resource.calendar,attendance_rounding:0
348#: selection:resource.calendar,leave_rounding:0
349#: selection:resource.calendar,overtime_rounding:0
350msgid "2"
351msgstr ""
352
353#. module: hr_attendance_analysis
354#: report:addons/hr_attendance_analysis/report/calendar_report.mako:10
355msgid "Employee: "
356msgstr ""
357
358#. module: hr_attendance_analysis
359#: selection:resource.calendar,attendance_rounding:0
360#: selection:resource.calendar,leave_rounding:0
361#: selection:resource.calendar,overtime_rounding:0
362msgid "6"
363msgstr ""
364
365#. module: hr_attendance_analysis
366#: report:addons/hr_attendance_analysis/report/calendar_report.mako:14
367msgid "Date"
368msgstr ""
369
370#. module: hr_attendance_analysis
371#: model:ir.module.module,shortdesc:hr_attendance_analysis.module_meta_information
372msgid "HR - Attendance Analysis"
373msgstr ""
374
375#. module: hr_attendance_analysis
376#: code:addons/hr_attendance_analysis/hr_attendance.py:190
377#, python-format
378msgid "Wrongly configured working schedule with id %s"
379msgstr ""
380
381#. module: hr_attendance_analysis
382#: model:ir.model,name:hr_attendance_analysis.model_res_company
383msgid "Companies"
384msgstr ""
385
386#. module: hr_attendance_analysis
387#: code:addons/hr_attendance_analysis/report/calendar_report.py:39
388#, python-format
389msgid "Wednesday"
390msgstr ""
391
392#. module: hr_attendance_analysis
393#: field:resource.calendar.overtime.type,name:0
394msgid "Type Description"
395msgstr ""
396
397#. module: hr_attendance_analysis
398#: report:addons/hr_attendance_analysis/report/calendar_report.mako:17
399msgid "First Sign In"
400msgstr ""
401
402#. module: hr_attendance_analysis
403#: report:addons/hr_attendance_analysis/report/calendar_report.mako:30
404msgid "Fourth Sign Out"
405msgstr ""
406
407#. module: hr_attendance_analysis
408#: model:ir.module.module,description:hr_attendance_analysis.module_meta_information
409msgid "\n"
410"Dynamic reports based on employee's attendances and contract's calendar.\n"
411"Among other things, it lets you see the amount of working hours outside and inside the contract's working schedule (overtime).\n"
412"It also provides a daily based report, showing the detailed and total hours compared to calendar hours.\n"
413""
414msgstr ""
415
416#. module: hr_attendance_analysis
417#: selection:resource.calendar,attendance_rounding:0
418#: selection:resource.calendar,leave_rounding:0
419#: selection:resource.calendar,overtime_rounding:0
420msgid "10"
421msgstr ""
422
423#. module: hr_attendance_analysis
424#: selection:resource.calendar,attendance_rounding:0
425#: selection:resource.calendar,leave_rounding:0
426#: selection:resource.calendar,overtime_rounding:0
427msgid "12"
428msgstr ""
429
430#. module: hr_attendance_analysis
431#: report:addons/hr_attendance_analysis/report/calendar_report.mako:22
432msgid "Second Sign Out"
433msgstr ""
434
435#. module: hr_attendance_analysis
436#: report:addons/hr_attendance_analysis/report/calendar_report.mako:34
437#: view:hr.attendance:0
438#: field:hr.attendance,outside_calendar_duration:0
439msgid "Overtime"
440msgstr ""
441
442#. module: hr_attendance_analysis
443#: model:ir.model,name:hr_attendance_analysis.model_resource_calendar_attendance
444msgid "Work Detail"
445msgstr ""
446
447#. module: hr_attendance_analysis
448#: help:resource.calendar.attendance,tolerance_from:0
449msgid "Sign out done in the interval \"Work to - Tolerance from\" will be considered done at \"Work to\""
450msgstr ""
451
452#. module: hr_attendance_analysis
453#: report:addons/hr_attendance_analysis/report/calendar_report.mako:21
454msgid "Second Sign In"
455msgstr ""
456
457#. module: hr_attendance_analysis
458#: view:attendance_analysis.wizard.calendar_report:0
459msgid "Cancel"
460msgstr ""
461
462#. module: hr_attendance_analysis
463#: code:addons/hr_attendance_analysis/report/calendar_report.py:37
464#, python-format
465msgid "Tuesday"
466msgstr ""
467
468#. module: hr_attendance_analysis
469#: model:ir.actions.act_window,name:hr_attendance_analysis.open_view_attendance
470#: model:ir.ui.menu,name:hr_attendance_analysis.menu_open_view_attendance
471msgid "Attendances analysis"
472msgstr ""
473
474#. module: hr_attendance_analysis
475#: code:addons/hr_attendance_analysis/report/calendar_report.py:41
476#, python-format
477msgid "Thursday"
478msgstr ""
479
480#. module: hr_attendance_analysis
481#: view:attendance_analysis.wizard.calendar_report:0
482msgid "Print"
483msgstr ""
484
485#. module: hr_attendance_analysis
486#: model:ir.model,name:hr_attendance_analysis.model_resource_calendar
487msgid "Resource Calendar"
488msgstr ""
489
490#. module: hr_attendance_analysis
491#: field:hr.attendance,duration:0
492msgid "Attendance duration"
493msgstr ""
494
495#. module: hr_attendance_analysis
496#: selection:resource.calendar,attendance_rounding:0
497#: selection:resource.calendar,leave_rounding:0
498#: selection:resource.calendar,overtime_rounding:0
499msgid "1"
500msgstr ""
501
502#. module: hr_attendance_analysis
503#: field:attendance_analysis.wizard.calendar_report,to_date:0
504msgid "To date"
505msgstr ""
506
507#. module: hr_attendance_analysis
508#: selection:resource.calendar,attendance_rounding:0
509#: selection:resource.calendar,leave_rounding:0
510#: selection:resource.calendar,overtime_rounding:0
511msgid "5"
512msgstr ""
513
514#. module: hr_attendance_analysis
515#: report:addons/hr_attendance_analysis/report/calendar_report.mako:33
516msgid "Working Hours"
517msgstr ""
518
519#. module: hr_attendance_analysis
520#: view:resource.calendar:0
521msgid "Types"
522msgstr ""
523
524#. module: hr_attendance_analysis
525#: field:resource.calendar,overtime_rounding:0
526msgid "Overtime rounding"
527msgstr ""
528
529#. module: hr_attendance_analysis
530#: view:attendance_analysis.wizard.calendar_report:0
531msgid "Employees"
532msgstr ""
533
534#. module: hr_attendance_analysis
535#: help:resource.calendar.overtime.type,limit:0
536msgid "Limit, in hours, of overtime that can be imputed to this type of overtime in a day. The surplus is imputed to the subsequent type"
537msgstr ""
538
539#. module: hr_attendance_analysis
540#: report:addons/hr_attendance_analysis/report/calendar_report.mako:26
541msgid "Third Sign Out"
542msgstr ""
543
544#. module: hr_attendance_analysis
545#: help:resource.calendar,overtime_rounding_tolerance:0
546msgid "Overtime can be rounded using a tolerance. Using tolerance = 3 minutes and rounding = 15 minutes, if employee does overtime of 12 minutes, it will be considered as 15 minutes."
547msgstr ""
548
549#. module: hr_attendance_analysis
550#: field:resource.calendar.overtime.type,sequence:0
551msgid "Sequence"
552msgstr ""
553
554#. module: hr_attendance_analysis
555#: report:addons/hr_attendance_analysis/report/calendar_report.mako:111
556msgid "Total"
557msgstr ""
558
559#. module: hr_attendance_analysis
560#: code:addons/hr_attendance_analysis/report/calendar_report.py:45
561#, python-format
562msgid "Saturday"
563msgstr ""
564
0565
=== added file 'hr_attendance_analysis/i18n/it.po'
--- hr_attendance_analysis/i18n/it.po 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/i18n/it.po 2013-11-07 15:51:52 +0000
@@ -0,0 +1,611 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * hr_attendance_analysis
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 6.0.3\n"
8"Report-Msgid-Bugs-To: support@openerp.com\n"
9"POT-Creation-Date: 2011-12-23 10:03+0000\n"
10"PO-Revision-Date: 2013-09-22 17:18+0000\n"
11"Last-Translator: Lorenzo Battistini - Agile BG "
12"<lorenzo.battistini@agilebg.com>\n"
13"Language-Team: \n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
17"X-Launchpad-Export-Date: 2013-10-16 05:12+0000\n"
18"X-Generator: Launchpad (build 16799)\n"
19
20#. module: hr_attendance_analysis
21#: code:addons/hr_attendance_analysis/hr_attendance.py:123
22#, python-format
23msgid "Incongruent data"
24msgstr ""
25
26#. module: hr_attendance_analysis
27#: code:addons/hr_attendance_analysis/hr_attendance.py:87
28#: code:addons/hr_attendance_analysis/hr_attendance.py:123
29#: code:addons/hr_attendance_analysis/hr_attendance.py:189
30#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:58
31#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:129
32#, python-format
33msgid "Error"
34msgstr "Errore"
35
36#. module: hr_attendance_analysis
37#: help:res.company,working_time_precision:0
38msgid "The precision used to analyse working times over working schedule"
39msgstr ""
40"La precisione utilizzata per analizzare le ore lavorative rispetto al "
41"calendario di lavoro"
42
43#. module: hr_attendance_analysis
44#: view:hr.attendance:0
45msgid "Group By..."
46msgstr "Raggruppa per..."
47
48#. module: hr_attendance_analysis
49#: code:addons/hr_attendance_analysis/report/calendar_report.py:47
50#, python-format
51msgid "Sunday"
52msgstr "Domenica"
53
54#. module: hr_attendance_analysis
55#: field:hr.attendance,end_datetime:0
56msgid "End date time"
57msgstr "Data e ora di fine"
58
59#. module: hr_attendance_analysis
60#: view:hr.attendance:0
61msgid "Today"
62msgstr "Oggi"
63
64#. module: hr_attendance_analysis
65#: view:hr.attendance:0
66msgid "Within working schedule"
67msgstr "All'interno del calendario di lavoro"
68
69#. module: hr_attendance_analysis
70#: selection:resource.calendar,attendance_rounding:0
71#: selection:resource.calendar,leave_rounding:0
72#: selection:resource.calendar,overtime_rounding:0
73msgid "20"
74msgstr "20"
75
76#. module: hr_attendance_analysis
77#: code:addons/hr_attendance_analysis/report/calendar_report.py:43
78#, python-format
79msgid "Friday"
80msgstr "Venerdì"
81
82#. module: hr_attendance_analysis
83#: model:ir.model,name:hr_attendance_analysis.model_attendance_analysis_wizard_calendar_report
84msgid "attendance_analysis.wizard.calendar_report"
85msgstr "attendance_analysis.wizard.calendar_report"
86
87#. module: hr_attendance_analysis
88#: selection:resource.calendar,attendance_rounding:0
89#: selection:resource.calendar,leave_rounding:0
90#: selection:resource.calendar,overtime_rounding:0
91msgid "8"
92msgstr "8"
93
94#. module: hr_attendance_analysis
95#: view:hr.attendance:0
96msgid "Day"
97msgstr "Giorno"
98
99#. module: hr_attendance_analysis
100#: report:addons/hr_attendance_analysis/report/calendar_report.mako:36
101msgid "Leave"
102msgstr "Assenza"
103
104#. module: hr_attendance_analysis
105#: field:resource.calendar.overtime.type,limit:0
106msgid "Limit"
107msgstr "Limite"
108
109#. module: hr_attendance_analysis
110#: selection:resource.calendar,attendance_rounding:0
111#: selection:resource.calendar,leave_rounding:0
112#: selection:resource.calendar,overtime_rounding:0
113msgid "15"
114msgstr "15"
115
116#. module: hr_attendance_analysis
117#: help:resource.calendar,attendance_rounding:0
118msgid ""
119"For instance, using rounding = 15 minutes, every sign in will be rounded to "
120"the following quarter hour and every sign out to the previous quarter hour"
121msgstr ""
122"Per esempio, utilizzando un arrotondamento = 15 minuti, ogni entrata verrà "
123"arrotondata al quarto d'ora successivo ed ogni uscita al quarto d'ora "
124"precedente"
125
126#. module: hr_attendance_analysis
127#: report:addons/hr_attendance_analysis/report/calendar_report.mako:80
128msgid "Totals"
129msgstr "Totali"
130
131#. module: hr_attendance_analysis
132#: help:resource.calendar.attendance,tolerance_to:0
133msgid ""
134"Sign in done in the interval \"Work from + Tolerance to\" will be considered "
135"done at \"Work from\""
136msgstr ""
137"Le entrate effettuate nell'intervallo \"Lavoro da + Tolleranza a\" saranno "
138"considerate come fatte in \"Lavoro da\""
139
140#. module: hr_attendance_analysis
141#: field:resource.calendar.attendance,tolerance_from:0
142msgid "Tolerance from"
143msgstr "Tolleranza da"
144
145#. module: hr_attendance_analysis
146#: field:attendance_analysis.wizard.calendar_report,from_date:0
147msgid "From date"
148msgstr "Dalla data"
149
150#. module: hr_attendance_analysis
151#: code:addons/hr_attendance_analysis/hr_attendance.py:87
152#, python-format
153msgid "Too many active contracts for employee %s"
154msgstr "Troppi contratti attivi per il dipendente %s"
155
156#. module: hr_attendance_analysis
157#: view:hr.attendance:0
158msgid "Calendar View"
159msgstr "Vista calendario"
160
161#. module: hr_attendance_analysis
162#: view:resource.calendar:0
163msgid "Roundings"
164msgstr "Arrotondamenti"
165
166#. module: hr_attendance_analysis
167#: report:addons/hr_attendance_analysis/report/calendar_report.mako:25
168msgid "Third Sign In"
169msgstr "Terza entrata"
170
171#. module: hr_attendance_analysis
172#: model:ir.model,name:hr_attendance_analysis.model_hr_attendance
173msgid "Attendance"
174msgstr "Presenze"
175
176#. module: hr_attendance_analysis
177#: constraint:res.company:0
178msgid "Error! You can not create recursive companies."
179msgstr "Errore! Non è possibile creare aziende ricorsive."
180
181#. module: hr_attendance_analysis
182#: report:addons/hr_attendance_analysis/report/calendar_report.mako:35
183msgid "Negative"
184msgstr "Negativo"
185
186#. module: hr_attendance_analysis
187#: view:hr.attendance:0
188msgid "Employee"
189msgstr "Dipendente"
190
191#. module: hr_attendance_analysis
192#: view:resource.calendar:0
193msgid "Type"
194msgstr "Tipo"
195
196#. module: hr_attendance_analysis
197#: view:hr.attendance:0
198msgid "Hr Attendance Search"
199msgstr "HR cerca presenza"
200
201#. module: hr_attendance_analysis
202#: view:hr.attendance:0
203msgid "Start date time"
204msgstr "Data e ora di inizio"
205
206#. module: hr_attendance_analysis
207#: help:resource.calendar,overtime_rounding:0
208msgid ""
209"Setting rounding = 30 minutes, an overtime of 29 minutes will be considered "
210"as 0 minutes, 31 minutes as 30 minutes, 61 minutes as 1 hour and so on"
211msgstr ""
212"Impostando un arrotondamento = 30 minuti, uno straordinario di 29 minuti "
213"sarà considerato come 0 minuti, 31 minuti come 30 minuti, 61 minuti come "
214"un'ora e così via"
215
216#. module: hr_attendance_analysis
217#: field:resource.calendar,attendance_rounding:0
218msgid "Attendance rounding"
219msgstr "Arrotondamento presenza"
220
221#. module: hr_attendance_analysis
222#: selection:resource.calendar,attendance_rounding:0
223#: selection:resource.calendar,leave_rounding:0
224#: selection:resource.calendar,overtime_rounding:0
225msgid "3"
226msgstr "3"
227
228#. module: hr_attendance_analysis
229#: constraint:hr.attendance:0
230msgid "Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)"
231msgstr ""
232"Errore: una operazione di Entrata (Uscita) deve essere seguito da una Uscita "
233"(Entrata)"
234
235#. module: hr_attendance_analysis
236#: view:res.company:0
237msgid "Configuration"
238msgstr "Configurazione"
239
240#. module: hr_attendance_analysis
241#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:58
242#, python-format
243msgid "From date must be < to date"
244msgstr "\"Dalla data\" deve essere < di \"alla data\""
245
246#. module: hr_attendance_analysis
247#: view:hr.attendance:0
248msgid "Total hours"
249msgstr "Ore totali"
250
251#. module: hr_attendance_analysis
252#: field:res.company,working_time_precision:0
253msgid "Working time precision"
254msgstr "Precisione orario lavorativo"
255
256#. module: hr_attendance_analysis
257#: view:hr.attendance:0
258msgid "Employee attendances analysis"
259msgstr "Analisi presenze dipendente"
260
261#. module: hr_attendance_analysis
262#: field:resource.calendar.attendance,tolerance_to:0
263msgid "Tolerance to"
264msgstr "Tolleranza a"
265
266#. module: hr_attendance_analysis
267#: help:resource.calendar,leave_rounding:0
268msgid ""
269"On the contrary of overtime rounding, using rounding = 15 minutes, a leave "
270"of 1 minute will be considered as 15 minutes, 16 minutes as 30 minutes and "
271"so on"
272msgstr ""
273"Al contrario dell'arrotondamento straordinari, utilizzando un arrotondamento "
274"= 15 minuti, un'assenza di 1 minuto verrà considerata come di 15 minuti, 16 "
275"minuti come 30 minuti e così via"
276
277#. module: hr_attendance_analysis
278#: model:ir.actions.report.xml,name:hr_attendance_analysis.attendance_analysis_report_id
279msgid "Attendances Analysis"
280msgstr "Analisi presenze"
281
282#. module: hr_attendance_analysis
283#: selection:resource.calendar,attendance_rounding:0
284#: selection:resource.calendar,leave_rounding:0
285#: selection:resource.calendar,overtime_rounding:0
286msgid "30"
287msgstr "30"
288
289#. module: hr_attendance_analysis
290#: report:addons/hr_attendance_analysis/report/calendar_report.mako:110
291#: model:ir.model,name:hr_attendance_analysis.model_resource_calendar_overtime_type
292msgid "Overtime type"
293msgstr "Tipo di straordinario"
294
295#. module: hr_attendance_analysis
296#: field:hr.attendance,inside_calendar_duration:0
297msgid "Duration within working schedule"
298msgstr "Durata all'interno del calendario lavorativo"
299
300#. module: hr_attendance_analysis
301#: field:resource.calendar,leave_rounding:0
302msgid "Leave rounding"
303msgstr "Arrotonamento assenza"
304
305#. module: hr_attendance_analysis
306#: field:resource.calendar.overtime.type,calendar_id:0
307msgid "Calendar"
308msgstr "Calendario"
309
310#. module: hr_attendance_analysis
311#: report:addons/hr_attendance_analysis/report/calendar_report.mako:32
312msgid "Due"
313msgstr "Dovuto"
314
315#. module: hr_attendance_analysis
316#: view:attendance_analysis.wizard.calendar_report:0
317#: model:ir.actions.act_window,name:hr_attendance_analysis.action_wizard_calendar_report
318#: model:ir.ui.menu,name:hr_attendance_analysis.menu_action_wizard_calendar_report
319msgid "Attendances Analysis Calendar"
320msgstr "Calendario di analisi presenze"
321
322#. module: hr_attendance_analysis
323#: selection:resource.calendar,attendance_rounding:0
324#: selection:resource.calendar,leave_rounding:0
325#: selection:resource.calendar,overtime_rounding:0
326msgid "60"
327msgstr "60"
328
329#. module: hr_attendance_analysis
330#: view:resource.calendar:0
331#: field:resource.calendar,overtime_type_ids:0
332msgid "Overtime types"
333msgstr "Tipi di straordinario"
334
335#. module: hr_attendance_analysis
336#: code:addons/hr_attendance_analysis/report/calendar_report.py:35
337#, python-format
338msgid "Monday"
339msgstr "Lunedì"
340
341#. module: hr_attendance_analysis
342#: field:attendance_analysis.wizard.calendar_report,employee_ids:0
343msgid "unknown"
344msgstr "sconosciuto"
345
346#. module: hr_attendance_analysis
347#: report:addons/hr_attendance_analysis/report/calendar_report.mako:18
348msgid "First Sign Out"
349msgstr "Prima uscita"
350
351#. module: hr_attendance_analysis
352#: report:addons/hr_attendance_analysis/report/calendar_report.mako:15
353msgid "Day of week"
354msgstr "Giorno della settimana"
355
356#. module: hr_attendance_analysis
357#: field:resource.calendar,overtime_rounding_tolerance:0
358msgid "Overtime rounding tolerance"
359msgstr "Tolleranza arrotondamento straordinari"
360
361#. module: hr_attendance_analysis
362#: report:addons/hr_attendance_analysis/report/calendar_report.mako:29
363msgid "Fourth Sign In"
364msgstr "Quarta entrata"
365
366#. module: hr_attendance_analysis
367#: code:addons/hr_attendance_analysis/wizard/print_calendar_report.py:130
368#, python-format
369msgid "%s: 'Work to' is < 'Work from'"
370msgstr "%s: 'Lavoro a' è < 'Lavoro da'"
371
372#. module: hr_attendance_analysis
373#: selection:resource.calendar,attendance_rounding:0
374#: selection:resource.calendar,leave_rounding:0
375#: selection:resource.calendar,overtime_rounding:0
376msgid "2"
377msgstr "2"
378
379#. module: hr_attendance_analysis
380#: report:addons/hr_attendance_analysis/report/calendar_report.mako:10
381msgid "Employee: "
382msgstr "Dipendente: "
383
384#. module: hr_attendance_analysis
385#: selection:resource.calendar,attendance_rounding:0
386#: selection:resource.calendar,leave_rounding:0
387#: selection:resource.calendar,overtime_rounding:0
388msgid "6"
389msgstr "6"
390
391#. module: hr_attendance_analysis
392#: report:addons/hr_attendance_analysis/report/calendar_report.mako:14
393msgid "Date"
394msgstr "Data"
395
396#. module: hr_attendance_analysis
397#: model:ir.module.module,shortdesc:hr_attendance_analysis.module_meta_information
398msgid "HR - Attendance Analysis"
399msgstr "HR - Analisi presenze"
400
401#. module: hr_attendance_analysis
402#: code:addons/hr_attendance_analysis/hr_attendance.py:190
403#, python-format
404msgid "Wrongly configured working schedule with id %s"
405msgstr "Calendario lavorativo con id %s configurato in modo scorretto"
406
407#. module: hr_attendance_analysis
408#: model:ir.model,name:hr_attendance_analysis.model_res_company
409msgid "Companies"
410msgstr "Aziende"
411
412#. module: hr_attendance_analysis
413#: code:addons/hr_attendance_analysis/report/calendar_report.py:39
414#, python-format
415msgid "Wednesday"
416msgstr "Mercoledì"
417
418#. module: hr_attendance_analysis
419#: field:resource.calendar.overtime.type,name:0
420msgid "Type Description"
421msgstr "Descrizione tipo"
422
423#. module: hr_attendance_analysis
424#: report:addons/hr_attendance_analysis/report/calendar_report.mako:17
425msgid "First Sign In"
426msgstr "Prima entrata"
427
428#. module: hr_attendance_analysis
429#: report:addons/hr_attendance_analysis/report/calendar_report.mako:30
430msgid "Fourth Sign Out"
431msgstr "Quarta uscita"
432
433#. module: hr_attendance_analysis
434#: model:ir.module.module,description:hr_attendance_analysis.module_meta_information
435msgid ""
436"\n"
437"Dynamic reports based on employee's attendances and contract's calendar.\n"
438"Among other things, it lets you see the amount of working hours outside and "
439"inside the contract's working schedule (overtime).\n"
440"It also provides a daily based report, showing the detailed and total hours "
441"compared to calendar hours.\n"
442msgstr ""
443"\n"
444"Dynamic reports based on employee's attendances and contract's calendar.\n"
445"Among other things, it lets you see the amount of working hours outside and "
446"inside the contract's working schedule (overtime).\n"
447"It also provides a daily based report, showing the detailed and total hours "
448"compared to calendar hours.\n"
449
450#. module: hr_attendance_analysis
451#: selection:resource.calendar,attendance_rounding:0
452#: selection:resource.calendar,leave_rounding:0
453#: selection:resource.calendar,overtime_rounding:0
454msgid "10"
455msgstr "10"
456
457#. module: hr_attendance_analysis
458#: selection:resource.calendar,attendance_rounding:0
459#: selection:resource.calendar,leave_rounding:0
460#: selection:resource.calendar,overtime_rounding:0
461msgid "12"
462msgstr "12"
463
464#. module: hr_attendance_analysis
465#: report:addons/hr_attendance_analysis/report/calendar_report.mako:22
466msgid "Second Sign Out"
467msgstr "Seconda uscita"
468
469#. module: hr_attendance_analysis
470#: report:addons/hr_attendance_analysis/report/calendar_report.mako:34
471#: view:hr.attendance:0
472#: field:hr.attendance,outside_calendar_duration:0
473msgid "Overtime"
474msgstr "Straordinario"
475
476#. module: hr_attendance_analysis
477#: model:ir.model,name:hr_attendance_analysis.model_resource_calendar_attendance
478msgid "Work Detail"
479msgstr "Dettagli del lavoro"
480
481#. module: hr_attendance_analysis
482#: help:resource.calendar.attendance,tolerance_from:0
483msgid ""
484"Sign out done in the interval \"Work to - Tolerance from\" will be "
485"considered done at \"Work to\""
486msgstr ""
487"Le uscite effettuate nell'intervallo \"Lavoro a - Tolleranza da\" saranno "
488"considerate come fatte in \"Lavoro a\""
489
490#. module: hr_attendance_analysis
491#: report:addons/hr_attendance_analysis/report/calendar_report.mako:21
492msgid "Second Sign In"
493msgstr "Seconda entrata"
494
495#. module: hr_attendance_analysis
496#: view:attendance_analysis.wizard.calendar_report:0
497msgid "Cancel"
498msgstr "Annulla"
499
500#. module: hr_attendance_analysis
501#: code:addons/hr_attendance_analysis/report/calendar_report.py:37
502#, python-format
503msgid "Tuesday"
504msgstr "Martedì"
505
506#. module: hr_attendance_analysis
507#: model:ir.actions.act_window,name:hr_attendance_analysis.open_view_attendance
508#: model:ir.ui.menu,name:hr_attendance_analysis.menu_open_view_attendance
509msgid "Attendances analysis"
510msgstr "Analisi presenze"
511
512#. module: hr_attendance_analysis
513#: code:addons/hr_attendance_analysis/report/calendar_report.py:41
514#, python-format
515msgid "Thursday"
516msgstr "Giovedì"
517
518#. module: hr_attendance_analysis
519#: view:attendance_analysis.wizard.calendar_report:0
520msgid "Print"
521msgstr "Stampa"
522
523#. module: hr_attendance_analysis
524#: model:ir.model,name:hr_attendance_analysis.model_resource_calendar
525msgid "Resource Calendar"
526msgstr "Calendario risorse"
527
528#. module: hr_attendance_analysis
529#: field:hr.attendance,duration:0
530msgid "Attendance duration"
531msgstr "Durata presenza"
532
533#. module: hr_attendance_analysis
534#: selection:resource.calendar,attendance_rounding:0
535#: selection:resource.calendar,leave_rounding:0
536#: selection:resource.calendar,overtime_rounding:0
537msgid "1"
538msgstr "1"
539
540#. module: hr_attendance_analysis
541#: field:attendance_analysis.wizard.calendar_report,to_date:0
542msgid "To date"
543msgstr "Alla data"
544
545#. module: hr_attendance_analysis
546#: selection:resource.calendar,attendance_rounding:0
547#: selection:resource.calendar,leave_rounding:0
548#: selection:resource.calendar,overtime_rounding:0
549msgid "5"
550msgstr "5"
551
552#. module: hr_attendance_analysis
553#: report:addons/hr_attendance_analysis/report/calendar_report.mako:33
554msgid "Working Hours"
555msgstr "Ore lavorative"
556
557#. module: hr_attendance_analysis
558#: view:resource.calendar:0
559msgid "Types"
560msgstr "Tipi"
561
562#. module: hr_attendance_analysis
563#: field:resource.calendar,overtime_rounding:0
564msgid "Overtime rounding"
565msgstr "Arrotondamento straordinario"
566
567#. module: hr_attendance_analysis
568#: view:attendance_analysis.wizard.calendar_report:0
569msgid "Employees"
570msgstr "Dipendenti"
571
572#. module: hr_attendance_analysis
573#: help:resource.calendar.overtime.type,limit:0
574msgid ""
575"Limit, in hours, of overtime that can be imputed to this type of overtime in "
576"a day. The surplus is imputed to the subsequent type"
577msgstr ""
578"Limite, in ore, dello straordinario che può essere attribuito a questo tipo "
579"di straordinario in un giorno. L'eccedenza è attribuita al tipo successivo."
580
581#. module: hr_attendance_analysis
582#: report:addons/hr_attendance_analysis/report/calendar_report.mako:26
583msgid "Third Sign Out"
584msgstr "Terza uscita"
585
586#. module: hr_attendance_analysis
587#: help:resource.calendar,overtime_rounding_tolerance:0
588msgid ""
589"Overtime can be rounded using a tolerance. Using tolerance = 3 minutes and "
590"rounding = 15 minutes, if employee does overtime of 12 minutes, it will be "
591"considered as 15 minutes."
592msgstr ""
593"Lo straordinario può essere arrotondato usando una tolleranza. Con una "
594"tolleranza = 3 minuti e arrotondamento = 15 minuti, se il dipendente fa uno "
595"straordinario di 12 minuti, sarà considerato come 15 minuti."
596
597#. module: hr_attendance_analysis
598#: field:resource.calendar.overtime.type,sequence:0
599msgid "Sequence"
600msgstr "Sequenza"
601
602#. module: hr_attendance_analysis
603#: report:addons/hr_attendance_analysis/report/calendar_report.mako:111
604msgid "Total"
605msgstr "Totale"
606
607#. module: hr_attendance_analysis
608#: code:addons/hr_attendance_analysis/report/calendar_report.py:45
609#, python-format
610msgid "Saturday"
611msgstr "Sabato"
0612
=== added directory 'hr_attendance_analysis/report'
=== added file 'hr_attendance_analysis/report/__init__.py'
--- hr_attendance_analysis/report/__init__.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/report/__init__.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,21 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
5# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as published
9# by the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21import calendar_report
022
=== added file 'hr_attendance_analysis/report/calendar_report.mako'
--- hr_attendance_analysis/report/calendar_report.mako 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/report/calendar_report.mako 2013-11-07 15:51:52 +0000
@@ -0,0 +1,138 @@
1<html>
2<head>
3 <style type="text/css">
4 ${css}
5 </style>
6</head>
7<body>
8 <% setLang(objects[0].company_id.partner_id.lang) %>
9 <% from datetime import datetime %>
10 %for employee in objects :
11 <h2>${_("Employee: ")} ${ employee.name or ''|entity }</h2>
12 <table style="width:100%" border="1">
13 <thead style="border-bottom: solid; background-color: WhiteSmoke">
14 <tr style="page-break-inside: avoid">
15 <th style="text-align:left">${_("Date")}</th>
16 <th style="text-align:left">${_("Day of week")}</th>
17 %if max_per_day() >= 1:
18 <th style="text-align:left">${_("First Sign In")}</th>
19 <th style="text-align:left">${_("First Sign Out")}</th>
20 %endif
21 %if max_per_day() >= 2:
22 <th style="text-align:left">${_("Second Sign In")}</th>
23 <th style="text-align:left">${_("Second Sign Out")}</th>
24 %endif
25 %if max_per_day() >= 3:
26 <th style="text-align:left">${_("Third Sign In")}</th>
27 <th style="text-align:left">${_("Third Sign Out")}</th>
28 %endif
29 %if max_per_day() >= 4:
30 <th style="text-align:left">${_("Fourth Sign In")}</th>
31 <th style="text-align:left">${_("Fourth Sign Out")}</th>
32 %endif
33 <th style="text-align:right">${_("Due")}</th>
34 <th style="text-align:right">${_("Working Hours")}</th>
35 <th style="text-align:right">${_("Overtime")}</th>
36 <th style="text-align:right">${_("Negative")}</th>
37 <th style="text-align:right">${_("Leave")}</th>
38 </tr>
39 </thead>
40 <% first_done = 0 %>
41 %for day in sorted(days_by_employee(employee.id).iterkeys()) :
42 %if datetime.strptime(day, '%Y-%m-%d').day == 1 or not first_done:
43 <tr style="page-break-inside: avoid" >
44 <td colspan="15">
45 <strong>${ month_name(day) | entity }</strong>
46 </td>
47 </tr>
48 <% first_done = 1 %>
49 %endif
50 <tr style="page-break-inside: avoid">
51 <td style="text-align:left">${ formatLang(day, date=True) | entity }</td>
52 <td style="text-align:left">${ day_of_week(day) | entity }</td>
53 %if max_per_day() >= 1:
54 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signin_1'] | entity }</td>
55 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signout_1'] | entity }</td>
56 %endif
57 %if max_per_day() >= 2:
58 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signin_2'] | entity }</td>
59 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signout_2'] | entity }</td>
60 %endif
61 %if max_per_day() >= 3:
62 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signin_3'] | entity }</td>
63 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signout_3'] | entity }</td>
64 %endif
65 %if max_per_day() >= 4:
66 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signin_4'] | entity }</td>
67 <td style="text-align:left">${ days_by_employee(employee.id)[day]['signout_4'] | entity }</td>
68 %endif
69 <td style="text-align:right">${ (days_by_employee(employee.id)[day]['due']) | entity }</td>
70 <td style="text-align:right">${ (days_by_employee(employee.id)[day]['attendances']) | entity }</td>
71 %if days_by_employee(employee.id)[day]['overtime'] != '00:00':
72 <td style="text-align:right; background-color:LightGreen">${ (days_by_employee(employee.id)[day]['overtime']) | entity }</td>
73 %else:
74 <td style="text-align:right">${ (days_by_employee(employee.id)[day]['overtime']) | entity }</td>
75 %endif
76 %if days_by_employee(employee.id)[day]['negative'] != '00:00':
77 <td style="text-align:right; background-color: Tomato">${ (days_by_employee(employee.id)[day]['negative']) | entity }</td>
78 %else:
79 <td style="text-align:right">${ (days_by_employee(employee.id)[day]['negative']) | entity }</td>
80 %endif
81 %if days_by_employee(employee.id)[day]['leaves'] != '00:00':
82 <td style="text-align:right; background-color: Silver">${ (days_by_employee(employee.id)[day]['leaves']) | entity }</td>
83 %else:
84 <td style="text-align:right">${ (days_by_employee(employee.id)[day]['leaves']) | entity }</td>
85 %endif
86 </tr>
87 %endfor
88 <tfoot style="font-weight:bold">
89 <tr style="page-break-inside: avoid">
90 <td style="text-align:left">${_("Totals")} </td>
91 <td></td>
92 %if max_per_day() >= 1:
93 <td></td>
94 <td></td>
95 %endif
96 %if max_per_day() >= 2:
97 <td></td>
98 <td></td>
99 %endif
100 %if max_per_day() >= 3:
101 <td></td>
102 <td></td>
103 %endif
104 %if max_per_day() >= 4:
105 <td></td>
106 <td></td>
107 %endif
108 <td style="border-top:1px solid #000; text-align:right">${ (totals_by_employee(employee.id)['total_due']) | entity }</td>
109 <td style="border-top:1px solid #000; text-align:right">${ (totals_by_employee(employee.id)['total_attendances']) | entity }</td>
110 <td style="border-top:1px solid #000; text-align:right">${ (totals_by_employee(employee.id)['total_overtime']) | entity }</td>
111 <td style="border-top:1px solid #000; text-align:right">${ (totals_by_employee(employee.id)['total_negative']) | entity }</td>
112 <td style="border-top:1px solid #000; text-align:right">${ (totals_by_employee(employee.id)['total_leaves']) | entity }</td>
113 </tr>
114 </tfoot>
115 </table>
116 <br/>
117 <table>
118 <thead>
119 <tr>
120 <th style="text-align:left">${_("Overtime type")}</th>
121 <th style="text-align:right">${_("Total")}</th>
122 </tr>
123 </thead>
124 %for type in totals_by_employee(employee.id)['total_types']:
125 <tr>
126 <td style="text-align:left">
127 ${type | entity}
128 </td>
129 <td style="text-align:right">
130 ${(totals_by_employee(employee.id)['total_types'][type]) | entity}
131 </td>
132 </tr>
133 %endfor
134 </table>
135 <p style="page-break-after:always; height: 1px"></p>
136 %endfor
137</body>
138</html>
0139
=== added file 'hr_attendance_analysis/report/calendar_report.py'
--- hr_attendance_analysis/report/calendar_report.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/report/calendar_report.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,96 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
5# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as published
9# by the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22import time
23from report import report_sxw
24from osv import osv
25from datetime import datetime
26from tools.translate import _
27
28class Parser(report_sxw.rml_parse):
29
30 def _get_day_of_week(self, day):
31 WEEKDAYS = {
32 0: _('Monday'),
33 1: _('Tuesday'),
34 2: _('Wednesday'),
35 3: _('Thursday'),
36 4: _('Friday'),
37 5: _('Saturday'),
38 6: _('Sunday'),
39 }
40 dayofweek=''
41 weekday = datetime.strptime(day,'%Y-%m-%d').weekday()
42 return WEEKDAYS[weekday]
43
44 def _get_month_name(self, day):
45 str_month=''
46 month = datetime.strptime(day,'%Y-%m-%d').month
47 if month == 1:
48 str_month = _('January')
49 elif month == 2:
50 str_month = _('February')
51 elif month == 3:
52 str_month = _('March')
53 elif month == 4:
54 str_month = _('April')
55 elif month == 5:
56 str_month = _('May')
57 elif month == 6:
58 str_month = _('June')
59 elif month == 7:
60 str_month = _('July')
61 elif month == 8:
62 str_month = _('August')
63 elif month == 9:
64 str_month = _('September')
65 elif month == 10:
66 str_month = _('October')
67 elif month == 11:
68 str_month = _('November')
69 elif month == 12:
70 str_month = _('December')
71 return str_month
72
73 def _get_days_by_employee(self, employee_id):
74 return self.localcontext['data']['form']['days_by_employee'][str(employee_id)]
75
76 def _get_totals_by_employee(self, employee_id):
77 return self.localcontext['data']['form']['totals_by_employee'][str(employee_id)]
78
79 def _get_max_per_day(self):
80 return self.localcontext['data']['form']['max_number_of_attendances_per_day']
81
82 def __init__(self, cr, uid, name, context):
83 super(Parser, self).__init__(cr, uid, name, context)
84 self.localcontext.update({
85 'time': time,
86 'days_by_employee': self._get_days_by_employee,
87 'totals_by_employee': self._get_totals_by_employee,
88 'day_of_week': self._get_day_of_week,
89 'max_per_day': self._get_max_per_day,
90 'month_name': self._get_month_name,
91 })
92
93report_sxw.report_sxw('report.attendance_analysis.calendar_report',
94 'attendance_analysis.calendar_report',
95 'attendance_analysis/report/calendar_report.mako',
96 parser=Parser)
097
=== added file 'hr_attendance_analysis/reports.xml'
--- hr_attendance_analysis/reports.xml 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/reports.xml 2013-11-07 15:51:52 +0000
@@ -0,0 +1,86 @@
1<?xml version="1.0"?>
2<openerp>
3 <data><record id="attendances_landscape_header" model="ir.header_webkit">
4 <field name="footer_html"><![CDATA[
5<html>
6 <head>
7 <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
8 <script>
9 function subst() {
10 var vars={};
11 var x=document.location.search.substring(1).split('&');
12 for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
13 var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
14 for(var i in x) {
15 var y = document.getElementsByClassName(x[i]);
16 for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
17 }
18 }
19 </script>
20 </head>
21 <% import datetime %>
22 <body style="border:0" onload="subst()">
23 <table style="border-top: 1px solid black; width: 1080px">
24 <tr style="border-collapse:collapse;">
25 <td style="text-align:left;font-size:10;width:350px;">${formatLang( str(datetime.datetime.today()), date_time=True)}</td>
26 <td style="text-align:center;font-size:10;width:350px;"></td>
27 <td style="text-align:right;font-size:10;width:350px;">Page&nbsp;<span class="page"/></td>
28 <td style="text-align:left;font-size:10;width:30px">&nbsp;of&nbsp;<span class="topage"/></td>
29 </tr>
30 </table>
31 </body>
32</html>]]></field>
33 <field name="orientation">Landscape</field>
34 <field name="format">A4</field>
35 <field name="html"><![CDATA[
36<html>
37 <head>
38 <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
39 <script>
40 function subst() {
41 var vars={};
42 var x=document.location.search.substring(1).split('&');
43 for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
44 var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
45 for(var i in x) {
46 var y = document.getElementsByClassName(x[i]);
47 for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
48 }
49 }
50 </script>
51 <style type="text/css">
52 ${css}
53 </style>
54 </head>
55 <body style="border:0; margin: 0;" onload="subst()">
56 <table class="header" style="border-bottom: 0px solid black; width: 100%">
57 <tr>
58 <td><h1>${_("Attendances Analysis")} - ${objects[0].company_id.partner_id.name | entity}</h1></td>
59 </tr>
60 </table> ${_debug or ''|n}
61 </body>
62</html>]]>
63 </field>
64 <field name="css"><![CDATA[
65
66body, table, td, span, div {
67 font-family: Helvetica, Arial;
68}
69
70]]>
71 </field>
72 <field eval="20" name="margin_top"/>
73 <field name="name">Attendances Landscape Header</field>
74 </record>
75 <record id="attendance_analysis_report_id" model="ir.actions.report.xml">
76 <field name="name">Attendances Analysis</field>
77 <field name="type">ir.actions.report.xml</field>
78 <field name="model">hr.employee</field>
79 <field name="report_name">attendance_analysis.calendar_report</field>
80 <field name="report_rml">hr_attendance_analysis/report/calendar_report.mako</field>
81 <field name="report_type">webkit</field>
82 <field name="webkit_header" ref="attendances_landscape_header"/>
83 </record>
84 </data>
85</openerp>
86
087
=== added file 'hr_attendance_analysis/resource.py'
--- hr_attendance_analysis/resource.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/resource.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,102 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
5# Copyright (C) 2011-2013 Agile Business Group sagl
6# (<http://www.agilebg.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23from openerp.osv import fields, orm
24
25class resource_calendar_attendance(orm.Model):
26 _inherit = "resource.calendar.attendance"
27 _columns = {
28 'tolerance_from': fields.float('Tolerance from', size=8,
29 help='Sign out done in the interval "Work to - Tolerance from" will be considered done at "Work to"'),
30 'tolerance_to': fields.float('Tolerance to', size=8,
31 help='Sign in done in the interval "Work from + Tolerance to" will be considered done at "Work from"'),
32 }
33
34
35class resource_calendar(orm.Model):
36 _inherit = "resource.calendar"
37 _columns = {
38 'attendance_rounding': fields.selection([
39 ('60', '1'),
40 ('30', '2'),
41 ('20', '3'),
42 ('12', '5'),
43 ('10', '6'),
44 ('7.5', '8'),
45 ('6', '10'),
46 ('5', '12'),
47 ('4', '15'),
48 ('3', '20'),
49 ('2', '30'),
50 ('1', '60'),
51 ],
52 'Attendance rounding', help='For instance, using rounding = 15 minutes, every sign in will be rounded to the following quarter hour and every sign out to the previous quarter hour'),
53 #'attendance_rounding': fields.float('Attendance rounding', size=8,
54 #help='For instance, using rounding = 15 minutes, every sign in will be rounded to the following quarter hour and every sign out to the previous quarter hour'),
55 'overtime_rounding': fields.selection([
56 ('60', '1'),
57 ('30', '2'),
58 ('20', '3'),
59 ('12', '5'),
60 ('10', '6'),
61 ('7.5', '8'),
62 ('6', '10'),
63 ('5', '12'),
64 ('4', '15'),
65 ('3', '20'),
66 ('2', '30'),
67 ('1', '60'),
68 ],
69 'Overtime rounding',
70 help='Setting rounding = 30 minutes, an overtime of 29 minutes will be considered as 0 minutes, 31 minutes as 30 minutes, 61 minutes as 1 hour and so on'),
71 'overtime_rounding_tolerance': fields.float('Overtime rounding tolerance', size=8,
72 help='Overtime can be rounded using a tolerance. Using tolerance = 3 minutes and rounding = 15 minutes, if employee does overtime of 12 minutes, it will be considered as 15 minutes.'),
73 'leave_rounding': fields.selection([
74 ('60', '1'),
75 ('30', '2'),
76 ('20', '3'),
77 ('12', '5'),
78 ('10', '6'),
79 ('7.5', '8'),
80 ('6', '10'),
81 ('5', '12'),
82 ('4', '15'),
83 ('3', '20'),
84 ('2', '30'),
85 ('1', '60'),
86 ],
87 'Leave rounding',
88 help='On the contrary of overtime rounding, using rounding = 15 minutes, a leave of 1 minute will be considered as 15 minutes, 16 minutes as 30 minutes and so on'),
89 'overtime_type_ids': fields.one2many('resource.calendar.overtime.type', 'calendar_id', 'Overtime types'),
90 }
91
92class resource_calendar_overtime_range(orm.Model):
93 _name = 'resource.calendar.overtime.type'
94 _description = 'Overtime type'
95 _order = 'sequence'
96 _columns = {
97 'sequence': fields.integer('Sequence', required=True),
98 'name': fields.char('Type Description', size=64, required=True),
99 'calendar_id': fields.many2one('resource.calendar', 'Calendar'),
100 'limit': fields.float('Limit', size=8,
101 help='Limit, in hours, of overtime that can be imputed to this type of overtime in a day. The surplus is imputed to the subsequent type')
102 }
0103
=== added file 'hr_attendance_analysis/resource_view.xml'
--- hr_attendance_analysis/resource_view.xml 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/resource_view.xml 2013-11-07 15:51:52 +0000
@@ -0,0 +1,52 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="view_resource_calendar_attendance_form" model="ir.ui.view">
6 <field name="name">resource.calendar.attendance.form</field>
7 <field name="model">resource.calendar.attendance</field>
8 <field name="inherit_id" ref="resource.view_resource_calendar_attendance_form"></field>
9 <field name="arch" type="xml">
10 <field name="hour_from" position="after">
11 <field name="tolerance_to" widget="float_time"/>
12 </field>
13 <field name="hour_to" position="after">
14 <field name="tolerance_from" widget="float_time"/>
15 </field>
16 </field>
17 </record>
18 <record id="resource_calendar_form" model="ir.ui.view">
19 <field name="name">resource.calendar.form</field>
20 <field name="model">resource.calendar</field>
21 <field name="inherit_id" ref="resource.resource_calendar_form"></field>
22 <field name="arch" type="xml">
23 <field name="attendance_ids" position="after">
24 <notebook colspan="4">
25 <page string="Roundings">
26 <group colspan="4">
27 <field name="attendance_rounding"/>
28 <field name="leave_rounding"/>
29 <field name="overtime_rounding"/>
30 <field name="overtime_rounding_tolerance" widget="float_time"/>
31 </group>
32 </page>
33 <page string="Overtime types">
34 <field name="overtime_type_ids" colspan="4" nolabel="1">
35 <tree string="Types" editable="bottom">
36 <field name="sequence"/>
37 <field name="name"/>
38 <field name="limit" widget="float_time"/>
39 </tree>
40 <form string="Type">
41 <field name="sequence"/>
42 <field name="name"/>
43 <field name="limit" widget="float_time"/>
44 </form>
45 </field>
46 </page>
47 </notebook>
48 </field>
49 </field>
50 </record>
51 </data>
52</openerp>
053
=== added directory 'hr_attendance_analysis/security'
=== added file 'hr_attendance_analysis/security/ir.model.access.csv'
--- hr_attendance_analysis/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/security/ir.model.access.csv 2013-11-07 15:51:52 +0000
@@ -0,0 +1,3 @@
1id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2access_resource_calendar_overtime_type,resource.calendar.overtime.type,model_resource_calendar_overtime_type,base.group_system,1,1,1,1
3access_hr_resource_calendar_overtime_type_user,hr.employee.resource.calendar.overtime.type.user,model_resource_calendar_overtime_type,base.group_hr_user,1,1,1,1
04
=== added directory 'hr_attendance_analysis/wizard'
=== added file 'hr_attendance_analysis/wizard/__init__.py'
--- hr_attendance_analysis/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/wizard/__init__.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,23 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
5# Copyright (C) 2011-2013 Agile Business Group sagl
6# (<http://www.agilebg.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23import print_calendar_report
024
=== added file 'hr_attendance_analysis/wizard/print_calendar_report.py'
--- hr_attendance_analysis/wizard/print_calendar_report.py 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/wizard/print_calendar_report.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,366 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
5# Copyright (C) 2011-2013 Agile Business Group sagl
6# (<http://www.agilebg.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23from openerp.osv import fields, orm
24from openerp.tools.translate import _
25from datetime import *
26import math
27import calendar
28
29class wizard_calendar_report(orm.TransientModel):
30
31 _columns = {
32 'month': fields.selection([
33 ('1', 'January'),
34 ('2', 'February'),
35 ('3', 'March'),
36 ('4', 'April'),
37 ('5', 'May'),
38 ('6', 'June'),
39 ('7', 'July'),
40 ('8', 'August'),
41 ('9', 'September'),
42 ('10', 'October'),
43 ('11', 'November'),
44 ('12', 'December'),
45 ], 'Month'),
46 'year': fields.integer('Year'),
47 'from_date': fields.date('From date', required=True),
48 'to_date': fields.date('To date', required=True),
49 'employee_ids': fields.many2many('hr.employee', 'calendar_report_employee_rel', 'employee_id', 'report_id',
50 required=True),
51 }
52
53 _defaults = {
54 'month': lambda * a: str(datetime.now().month),
55 'year': lambda * a: datetime.now().year,
56 'from_date': lambda * a: (datetime.now()-timedelta(30)).strftime('%Y-%m-%d'),
57 'to_date': lambda * a: datetime.now().strftime('%Y-%m-%d'),
58 'employee_ids': lambda s, cr, uid, c: s.pool.get('hr.employee').search(cr, uid, []),
59 }
60
61 _name = "attendance_analysis.wizard.calendar_report"
62
63 def on_change_month(self, cr, uid, id, str_month, year):
64 res = {}
65 if year and str_month:
66 month = int(str_month)
67 day = calendar.monthrange(year, month)[1]
68 to_date = date(year, month, day).strftime('%Y-%m-%d')
69 res= {'value':{'to_date': to_date, 'from_date': date(year, month, 1).strftime('%Y-%m-%d')}}
70 return res
71
72
73 def print_calendar(self, cr, uid, ids, context=None):
74 if context is None:
75 context = {}
76 attendance_pool = self.pool.get('hr.attendance')
77 contract_pool = self.pool.get('hr.contract')
78 holidays_pool = self.pool.get('hr.holidays')
79
80 days_by_employee = {}
81
82 form = self.read(cr, uid, ids)[0]
83 from_date = datetime.strptime(form['from_date'], '%Y-%m-%d')
84 to_date = datetime.strptime(form['to_date'], '%Y-%m-%d')
85 if from_date > to_date:
86 raise orm.except_orm(_('Error'), _('From date must be < to date'))
87 employee_ids=form['employee_ids']
88 delta = to_date - from_date
89 max_number_of_attendances_per_day = 0
90
91 for employee_id in employee_ids:
92 employee_id = str(employee_id)
93 days_by_employee[employee_id] = {}
94 day_count=0
95 while day_count <= delta.days:
96 current_date = from_date + timedelta(day_count)
97 current_total_attendances = 0.0
98 current_total_overtime = 0.0
99 current_total_leaves = 0.0
100 current_total_due = 24.0 # If contract is not specified: working days = 24/7
101 current_total_inside_calendar = 0.0
102 str_current_date = current_date.strftime('%Y-%m-%d')
103 days_by_employee[employee_id][str_current_date] = {
104 'signin_1': '',
105 'signout_1': '',
106 'signin_2': '',
107 'signout_2': '',
108 'signin_3': '',
109 'signout_3': '',
110 'signin_4': '',
111 'signout_4': '',
112 }
113 current_date_beginning = datetime.combine(current_date, time())
114 str_current_date_beginning = current_date_beginning.strftime(
115 '%Y-%m-%d %H:%M:%S')
116 current_date_end = datetime.combine(current_date, time())+ timedelta(1)
117 str_current_date_end = current_date_end.strftime('%Y-%m-%d %H:%M:%S')
118
119 attendance_ids = attendance_pool.search(cr, uid, [
120 ('employee_id','=',int(employee_id)),
121 ('name','>=',str_current_date_beginning),
122 ('name','<=',str_current_date_end),
123 ('action','=','sign_in'),
124 ])
125 # computing attendance totals
126 for attendance in attendance_pool.browse(cr, uid, attendance_ids):
127 current_total_attendances = attendance_pool.time_sum(
128 current_total_attendances,attendance.duration)
129 current_total_overtime = attendance_pool.time_sum(current_total_overtime,
130 attendance.outside_calendar_duration)
131 current_total_inside_calendar = attendance_pool.time_sum(
132 current_total_inside_calendar,
133 attendance.inside_calendar_duration)
134
135 #printing up to 4 attendances
136 if len(attendance_ids) < 5:
137 count = 1
138 for attendance in sorted(attendance_pool.browse(cr, uid, attendance_ids),
139 key=lambda x: x['name']):
140 days_by_employee[employee_id][str_current_date][
141 'signin_'+str(count)] = attendance.name[11:16]
142 days_by_employee[employee_id][str_current_date][
143 'signout_'+str(count)] = attendance.end_datetime[11:16]
144 count += 1
145 if len(attendance_ids) > max_number_of_attendances_per_day:
146 max_number_of_attendances_per_day = len(attendance_ids)
147
148 days_by_employee[employee_id][str_current_date][
149 'attendances'
150 ] = current_total_attendances
151 days_by_employee[employee_id][str_current_date][
152 'overtime'
153 ] = current_total_overtime
154
155 active_contract_ids = attendance_pool.get_active_contracts(
156 cr, uid, int(employee_id), date=str_current_date)
157 # computing due total
158 if active_contract_ids:
159 contract = contract_pool.browse(cr, uid, active_contract_ids[0])
160 if contract.working_hours and contract.working_hours.attendance_ids:
161 current_total_due = 0.0
162 for calendar_attendance in contract.working_hours.attendance_ids:
163 if ((
164 not calendar_attendance.dayofweek
165 or int(calendar_attendance.dayofweek) == current_date.weekday()
166 )
167 and (
168 not calendar_attendance.date_from or
169 datetime.strptime(calendar_attendance.date_from,'%Y-%m-%d')
170 <= current_date
171 )):
172 calendar_attendance_duration = attendance_pool.time_difference(
173 calendar_attendance.hour_from, calendar_attendance.hour_to)
174 if calendar_attendance_duration < 0:
175 raise orm.except_orm(_('Error'),
176 _("%s: 'Work to' is < 'Work from'")
177 % calendar_attendance.name)
178 current_total_due = attendance_pool.time_sum(current_total_due,
179 calendar_attendance_duration)
180
181 days_by_employee[employee_id][str_current_date]['due'] = current_total_due
182
183 # computing leaves
184 holidays_ids = holidays_pool.search(cr, uid, [
185 '&',
186 '&',
187 '|',
188 # leave begins today
189 '&',
190 ('date_from', '>=', str_current_date_beginning),
191 ('date_from', '<=', str_current_date_end),
192 '|',
193 # leave ends today
194 '&',
195 ('date_to', '<=', str_current_date_end),
196 ('date_to', '>=', str_current_date_beginning),
197 # leave is ongoing
198 '&',
199 ('date_from', '<', str_current_date_beginning),
200 ('date_to', '>', str_current_date_end),
201 ('state', '=', 'validate'),
202 ('employee_id', '=', int(employee_id)),
203 ])
204 for holiday in holidays_pool.browse(cr, uid, holidays_ids):
205 date_from = datetime.strptime(holiday.date_from, '%Y-%m-%d %H:%M:%S')
206 date_to = datetime.strptime(holiday.date_to, '%Y-%m-%d %H:%M:%S')
207 # if beginned before today
208 if date_from < current_date_beginning:
209 date_from = current_date_beginning
210 # if ends after today
211 if date_to > current_date_end:
212 date_to = current_date_end
213 current_total_leaves = attendance_pool.time_sum(
214 current_total_leaves,
215 (date_to - date_from).total_seconds() / 60.0 / 60.0)
216
217 days_by_employee[employee_id][str_current_date]['leaves'] = current_total_leaves
218 if current_total_leaves > days_by_employee[employee_id][
219 str_current_date]['due']:
220 days_by_employee[employee_id][str_current_date][
221 'leaves'
222 ] = days_by_employee[employee_id][str_current_date]['due']
223 due_minus_leaves = attendance_pool.time_difference(
224 current_total_leaves, current_total_due)
225 if due_minus_leaves < current_total_inside_calendar:
226 days_by_employee[employee_id][str_current_date]['negative'] = 0.0
227 else:
228 days_by_employee[employee_id][str_current_date][
229 'negative'
230 ] = attendance_pool.time_difference(
231 current_total_inside_calendar, due_minus_leaves)
232
233 if active_contract_ids:
234 contract = contract_pool.browse(cr, uid, active_contract_ids[0])
235 if contract.working_hours and contract.working_hours.leave_rounding:
236 float_rounding = float(contract.working_hours.leave_rounding)
237 days_by_employee[employee_id][str_current_date][
238 'negative'
239 ] = math.floor(
240 days_by_employee[employee_id][str_current_date]['negative'] *
241 float_rounding
242 ) / float_rounding
243
244 day_count += 1
245
246 totals_by_employee = {}
247 for employee_id in days_by_employee:
248 totals_by_employee[employee_id] = {
249 'total_attendances': 0.0,
250 'total_overtime': 0.0,
251 'total_negative': 0.0,
252 'total_leaves': 0.0,
253 'total_due': 0.0,
254 'total_types': {},
255 }
256
257 for str_date in days_by_employee[employee_id]:
258 totals_by_employee[employee_id]['total_attendances'] = attendance_pool.time_sum(
259 totals_by_employee[employee_id]['total_attendances'],
260 days_by_employee[employee_id][str_date]['attendances'])
261 totals_by_employee[employee_id]['total_overtime'] = attendance_pool.time_sum(
262 totals_by_employee[employee_id]['total_overtime'],
263 days_by_employee[employee_id][str_date]['overtime'])
264 totals_by_employee[employee_id]['total_negative'] = attendance_pool.time_sum(
265 totals_by_employee[employee_id]['total_negative'],
266 days_by_employee[employee_id][str_date]['negative'])
267 totals_by_employee[employee_id]['total_leaves'] = attendance_pool.time_sum(
268 totals_by_employee[employee_id]['total_leaves'],
269 days_by_employee[employee_id][str_date]['leaves'])
270 totals_by_employee[employee_id]['total_due'] = attendance_pool.time_sum(
271 totals_by_employee[employee_id]['total_due'],
272 days_by_employee[employee_id][str_date]['due'])
273
274 # computing overtime types
275 active_contract_ids = attendance_pool.get_active_contracts(
276 cr, uid, int(employee_id), date=str_date)
277 if active_contract_ids:
278 contract = contract_pool.browse(cr, uid, active_contract_ids[0])
279 if contract.working_hours and contract.working_hours.overtime_type_ids:
280 sorted_types = sorted(
281 contract.working_hours.overtime_type_ids,
282 key=lambda k: k.sequence)
283 current_overtime = days_by_employee[employee_id][
284 str_date]['overtime']
285 for overtime_type in sorted_types:
286 if not totals_by_employee[employee_id]['total_types'].get(
287 overtime_type.name, False):
288 totals_by_employee[employee_id]['total_types'][
289 overtime_type.name] = 0.0
290 if current_overtime:
291 if current_overtime <= overtime_type.limit or not overtime_type.limit:
292 totals_by_employee[employee_id]['total_types'][
293 overtime_type.name] = attendance_pool.time_sum(
294 totals_by_employee[employee_id]
295 ['total_types'][overtime_type.name],
296 current_overtime)
297 current_overtime = 0.0
298 else:
299 totals_by_employee[employee_id]['total_types'][
300 overtime_type.name] = attendance_pool.time_sum(
301 totals_by_employee[employee_id]['total_types']
302 [overtime_type.name], overtime_type.limit)
303 current_overtime = attendance_pool.time_difference(overtime_type.limit,
304 current_overtime)
305
306 days_by_employee[employee_id][str_date][
307 'attendances'
308 ] = attendance_pool.float_time_convert(
309 days_by_employee[employee_id][str_date]['attendances'])
310 days_by_employee[employee_id][str_date][
311 'overtime'
312 ] = attendance_pool.float_time_convert(
313 days_by_employee[employee_id][str_date]['overtime'])
314 days_by_employee[employee_id][str_date][
315 'negative'
316 ] = attendance_pool.float_time_convert(
317 days_by_employee[employee_id][str_date]['negative'])
318 days_by_employee[employee_id][str_date][
319 'leaves'
320 ] = attendance_pool.float_time_convert(
321 days_by_employee[employee_id][str_date]['leaves'])
322 days_by_employee[employee_id][str_date][
323 'due'
324 ] = attendance_pool.float_time_convert(
325 days_by_employee[employee_id][str_date]['due'])
326
327 totals_by_employee[employee_id][
328 'total_attendances'
329 ] = attendance_pool.float_time_convert(
330 totals_by_employee[employee_id]['total_attendances'])
331 totals_by_employee[employee_id][
332 'total_overtime'
333 ] = attendance_pool.float_time_convert(
334 totals_by_employee[employee_id]['total_overtime'])
335 totals_by_employee[employee_id][
336 'total_negative'
337 ] = attendance_pool.float_time_convert(
338 totals_by_employee[employee_id]['total_negative'])
339 totals_by_employee[employee_id][
340 'total_leaves'
341 ] = attendance_pool.float_time_convert(
342 totals_by_employee[employee_id]['total_leaves'])
343 totals_by_employee[employee_id][
344 'total_due'
345 ] = attendance_pool.float_time_convert(
346 totals_by_employee[employee_id]['total_due'])
347
348 for overtime_type in totals_by_employee[employee_id]['total_types']:
349 totals_by_employee[employee_id]['total_types'][
350 overtime_type
351 ] = attendance_pool.float_time_convert(
352 totals_by_employee[employee_id]['total_types'][overtime_type])
353
354 datas = {'ids': employee_ids}
355 datas['model'] = 'hr.employee'
356 datas['form'] = {}
357 datas['form']['days_by_employee'] = days_by_employee
358 datas['form']['totals_by_employee'] = totals_by_employee
359 datas['form']['max_number_of_attendances_per_day'] = max_number_of_attendances_per_day
360
361 return {
362 'type': 'ir.actions.report.xml',
363 'report_name': 'attendance_analysis.calendar_report',
364 'datas': datas,
365 }
366
0367
=== added file 'hr_attendance_analysis/wizard/print_calendar_report.xml'
--- hr_attendance_analysis/wizard/print_calendar_report.xml 1970-01-01 00:00:00 +0000
+++ hr_attendance_analysis/wizard/print_calendar_report.xml 2013-11-07 15:51:52 +0000
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="wizard_calendar_report" model="ir.ui.view">
6 <field name="name">Attendances Analysis Calendar</field>
7 <field name="model">attendance_analysis.wizard.calendar_report</field>
8 <field name="arch" type="xml">
9 <form string="Attendances Analysis Calendar">
10 <group colspan="4" height="400">
11 <field name="month" on_change="on_change_month(month, year)"/>
12 <field name="year" on_change="on_change_month(month, year)"/>
13 <field name="from_date"/>
14 <field name="to_date"/>
15 <separator colspan="4" string="Employees"/>
16 <field name="employee_ids" colspan="4" nolabel="1"/>
17 <separator colspan="4"/>
18 <button icon="gtk-cancel" special="cancel" string="Cancel" colspan="2"/>
19 <button icon="gtk-ok" name="print_calendar" string="Print" type="object" colspan="2"/>
20 </group>
21 </form>
22 </field>
23 </record>
24
25 <record id="action_wizard_calendar_report" model="ir.actions.act_window">
26 <field name="name">Attendances Analysis Calendar</field>
27 <field name="res_model">attendance_analysis.wizard.calendar_report</field>
28 <field name="view_type">form</field>
29 <field name="view_mode">form</field>
30 <field name="view_id" ref="wizard_calendar_report"/>
31 <field name="target">new</field>
32 </record>
33
34 <menuitem action="action_wizard_calendar_report"
35 id="menu_action_wizard_calendar_report"
36 parent="hr.menu_hr_reporting" />
37 </data>
38</openerp>
039
=== modified file 'hr_timesheet_fulfill/__init__.py'
--- hr_timesheet_fulfill/__init__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_fulfill/__init__.py 2013-11-07 15:51:52 +0000
@@ -1,32 +1,25 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Guewen Baconnier (Camptocamp)
5# All Right Reserved5# Author: Vincent Renaville
6#6# Copyright 2012 Camptocamp SA
7# Author : Guewen Baconnier (Camptocamp)7#
8#8# This program is free software: you can redistribute it and/or modify
9# WARNING: This program as such is intended to be used by professional9# it under the terms of the GNU Affero General Public License as
10# programmers who take the whole responsability of assessing all potential10# published by the Free Software Foundation, either version 3 of the
11# consequences resulting from its eventual inadequacies and bugs11# License, or (at your option) any later version.
12# End users who are looking for a ready-to-use solution with commercial12#
13# garantees and support are strongly adviced to contract a Free Software13# This program is distributed in the hope that it will be useful,
14# Service Company14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15#15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# This program is Free Software; you can redistribute it and/or16# GNU Affero General Public License for more details.
17# modify it under the terms of the GNU General Public License17#
18# as published by the Free Software Foundation; either version 218# You should have received a copy of the GNU Affero General Public License
19# of the License, or (at your option) any later version.19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21# This program is distributed in the hope that it will be useful,
22# but WITHOUT ANY WARRANTY; without even the implied warranty of
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24# GNU General Public License for more details.
25#
26# You should have received a copy of the GNU General Public License
27# along with this program; if not, write to the Free Software
28# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29#20#
30##############################################################################21##############################################################################
3122
32import wizard23import wizard
24
25# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
3326
=== modified file 'hr_timesheet_fulfill/__openerp__.py'
--- hr_timesheet_fulfill/__openerp__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_fulfill/__openerp__.py 2013-11-07 15:51:52 +0000
@@ -1,57 +1,46 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Guewen Baconnier (Camptocamp)
5# All Right Reserved5# Author: Vincent Renaville
6#6# Copyright 2012 Camptocamp SA
7# Author : Guewen Baconnier (Camptocamp)7#
8# Author : Vincent Renaville8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
3222
3323{'name' : 'Timesheet Fullfill Wizard',
34{24 'version' : '1.0',
35 "name" : "Timesheet fullfill wizard",25 'category' : 'Generic Modules/Human Resources',
36 "version" : "1.0",26 'description':
37 "author" : "Camptocamp",27 '''
38 "category" : "Generic Modules/Human Resources",28 Add a wizard into timesheet allowing people to complete a long period of time with the given values.
39 "description":29 This is mainly useful to handle a long period of time like holidays.
40"""30 Known limitation:
41Add a wizard into timesheet allowing people to complete a long period of time with the given values.31 - Will complete all day between dates
42This is mainly useful to handle a long period of time like holidays.32 ''',
43Known limitation:33 'author' : 'Camptocamp',
44 - Will complete all day between dates34 'website': 'http://camptocamp.com',
45""",35 'depends' : ['hr_timesheet_sheet',],
46 "website": "http://camptocamp.com",36 'data' : [
47 "depends" : [37 'wizard/timesheet_fulfill_view.xml',
48 "hr_timesheet_sheet",38 ],
49 ],39 'demo' : [],
50 "init_xml" : [],40 'test' : [],
51 "demo_xml" : [],41 'installable': True,
52 "update_xml" : [42 'auto_install' : False,
53 'wizard/timesheet_fulfill_view.xml',43 'application' : False,
54 ],
55 "active": False,
56 "installable": True
57}44}
45
46# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
5847
=== modified file 'hr_timesheet_fulfill/wizard/__init__.py'
--- hr_timesheet_fulfill/wizard/__init__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_fulfill/wizard/__init__.py 2013-11-07 15:51:52 +0000
@@ -1,1 +1,4 @@
1
1import timesheet_fulfill2import timesheet_fulfill
3
4# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2\ No newline at end of file5\ No newline at end of file
36
=== modified file 'hr_timesheet_fulfill/wizard/timesheet_fulfill.py'
--- hr_timesheet_fulfill/wizard/timesheet_fulfill.py 2011-09-01 13:44:19 +0000
+++ hr_timesheet_fulfill/wizard/timesheet_fulfill.py 2013-11-07 15:51:52 +0000
@@ -1,41 +1,29 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Guewen Baconnier (Camptocamp)
5# All Right Reserved5# Author: Vincent Renaville
6#6# Copyright 2012 Camptocamp SA
7# Author : Guewen Baconnier (Camptocamp)7#
8# Author : Vincent Renaville8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
3222
3323from openerp.osv import fields, osv, orm
34from osv import fields, osv
35from tools.translate import _24from tools.translate import _
36from datetime import datetime, timedelta25from datetime import datetime, timedelta
3726
38
39def get_number_days_between_dates(date_from, date_to):27def get_number_days_between_dates(date_from, date_to):
40 datetime_from = datetime.strptime(date_from, '%Y-%m-%d')28 datetime_from = datetime.strptime(date_from, '%Y-%m-%d')
41 datetime_to = datetime.strptime(date_to, '%Y-%m-%d')29 datetime_to = datetime.strptime(date_to, '%Y-%m-%d')
@@ -44,7 +32,7 @@
44 return difference.days + 132 return difference.days + 1
4533
4634
47class FulfillTimesheet(osv.osv_memory):35class HrTimesheetFulfill(orm.TransientModel):
48 _name = 'hr.timesheet.fulfill'36 _name = 'hr.timesheet.fulfill'
49 _description = "Wizard to fill-in timesheet for many days"37 _description = "Wizard to fill-in timesheet for many days"
5038
@@ -52,16 +40,19 @@
52 'date_from': fields.date('Date From', required=True),40 'date_from': fields.date('Date From', required=True),
53 'date_to': fields.date('Date To', required=True),41 'date_to': fields.date('Date To', required=True),
54 'description': fields.char('Description', size=100, required=True),42 'description': fields.char('Description', size=100, required=True),
55 'nb_hours': fields.float('Hours per day', digits=(2, 2), required=True),43 'nb_hours': fields.float('Hours per Day', digits=(2, 2), required=True),
56 'analytic_account_id': fields.many2one('account.analytic.account',44 'analytic_account_id': fields.many2one('account.analytic.account',
57 'Analytic Account', required=True,45 'Analytic Account', required=True,
58 domain="[('type', '=', 'normal'),"46 domain="[('type', '=', 'contract'),"
59 "('state', '!=', 'pending'),"47 "('state', '!=', 'pending'),"
60 "('state', '!=', 'close')]"),48 "('state', '!=', 'close')]"),
61 'task_id':fields.many2one('project.task','Task', required=False)49 'task_id':fields.many2one('project.task', 'Task', required=False)
62 }50 }
6351
64 def fulfill_timesheet(self, cr, uid, ids, context):52 def fulfill_timesheet(self, cr, uid, ids, context=None):
53 if context is None:
54 context = {}
55
65 employee_obj = self.pool.get('hr.employee')56 employee_obj = self.pool.get('hr.employee')
66 timesheet_obj = self.pool.get('hr_timesheet_sheet.sheet')57 timesheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
67 al_ts_obj = self.pool.get('hr.analytic.timesheet')58 al_ts_obj = self.pool.get('hr.analytic.timesheet')
@@ -117,23 +108,23 @@
117 'sheet_id': timesheet.id,108 'sheet_id': timesheet.id,
118 'journal_id': journal_id,109 'journal_id': journal_id,
119 }110 }
120
121 on_change_values = al_ts_obj.\111 on_change_values = al_ts_obj.\
122 on_change_unit_amount(cr, uid, False, product_id,112 on_change_unit_amount(cr, uid, False, product_id,
123 wizard.nb_hours, employee.company_id.id,113 wizard.nb_hours, employee.company_id.id,
124 task_id=wizard.task_id.id,114# task_id=wizard.task_id.id,
125 unit=unit_id, journal_id=journal_id,115 unit=unit_id, journal_id=journal_id,
126 context=context)116 context=context)
127 if on_change_values:117 if on_change_values:
128 res.update(on_change_values['value'])118 res.update(on_change_values['value'])
129 al_ts_obj.create(cr, uid, res, context)119 al_ts_obj.create(cr, uid, res, context)
130
131 # If there is no other attendances, create it120 # If there is no other attendances, create it
132 # create the attendances:121 # create the attendances:
133 existing_attendances = attendance_obj\122# print attendance_obj.read(cr,uid,['name'])
134 .search(cr, uid, [('name', '=', datetime_current),123 existing_attendances=0
135 ('employee_id', '=', employee_id)])124 att_id= attendance_obj.search(cr, uid, [('employee_id', '=', employee_id)])
136125 for record in attendance_obj.read(cr,uid,att_id,['name']):
126 if record['name'].startswith( datetime_current ):
127 existing_attendances=1
137 if not existing_attendances:128 if not existing_attendances:
138 att_date_start = datetime_current + " 00:00:00"129 att_date_start = datetime_current + " 00:00:00"
139 att_start = {130 att_start = {
@@ -154,6 +145,7 @@
154 }145 }
155 attendance_obj.create(cr, uid, att_start, context)146 attendance_obj.create(cr, uid, att_start, context)
156 attendance_obj.create(cr, uid, att_end, context)147 attendance_obj.create(cr, uid, att_end, context)
148
157 return {'type': 'ir.actions.act_window_close'}149 return {'type': 'ir.actions.act_window_close'}
158150
159FulfillTimesheet()151# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
160152
=== modified file 'hr_timesheet_fulfill/wizard/timesheet_fulfill_view.xml'
--- hr_timesheet_fulfill/wizard/timesheet_fulfill_view.xml 2011-08-12 12:53:16 +0000
+++ hr_timesheet_fulfill/wizard/timesheet_fulfill_view.xml 2013-11-07 15:51:52 +0000
@@ -1,43 +1,58 @@
1<?xml version="1.0" encoding="utf-8"?>1<?xml version="1.0" encoding="utf-8"?>
2<openerp>2<openerp>
3 <data>3 <data>
44
5 <record id="view_hr_timesheet_fulfill_form" model="ir.ui.view">5 <record id="view_hr_timesheet_fulfill_form" model="ir.ui.view">
6 <field name="name">hr.timesheet.fulfill.form</field>6 <field name="name">hr.timesheet.fulfill.form</field>
7 <field name="model">hr.timesheet.fulfill</field>7 <field name="model">hr.timesheet.fulfill</field>
8 <field name="type">form</field>8 <field name="arch" type="xml">
9 <field name="arch" type="xml">9 <form string="Enter the dates : ALL days between those dates will be completed." version="7.0">
10 <form string="Enter the dates : ALL days between those dates will be completed.">10 <group>
11 <field name="date_from"/>11 <group>
12 <field name="date_to"/>12 <field name="date_from"/>
13 <field name="analytic_account_id"/>13 </group>
14 <field name="task_id" domain="[('state','!=','cancel'),('state','!=','done')" context="{'account_id':analytic_account_id}"/>14 <group>
15 <field name="nb_hours" widget="float_time"/>15 <field name="date_to"/>
16 <field name="description" colspan="4"/>16 </group>
17 <group colspan="4" col="6">17 </group>
18 <button icon="gtk-cancel" special="cancel" string="Cancel"/>18 <group>
19 <button icon="gtk-ok" string="Fill in Timesheet" name="fulfill_timesheet" type="object"/>19 <group>
20 </group>20 <field name="analytic_account_id"/>
21 </form>21 </group>
22 </field>22 <group>
23 </record>23 <field name="task_id" domain="[('state','!=','cancel'),('state','!=','done')]" context="{'account_id':analytic_account_id}"/>
2424 </group>
25 <record id="action_hr_timesheet_fulfill" model="ir.actions.act_window">25 </group>
26 <field name="name">Fill in Timesheet</field>26 <group>
27 <field name="res_model">hr.timesheet.fulfill</field>27 <field name="nb_hours" widget="float_time"/>
28 <field name="view_type">form</field>28 </group>
29 <field name="view_mode">form</field>29 <group colspan="4">
30 <field name="view_id" ref="view_hr_timesheet_fulfill_form"/>30 <field name="description"/>
31 <field name="target">new</field>31 </group>
32 </record>32 <footer>
3333 <button icon="gtk-cancel" special="cancel" string="Cancel"/>
34 <record id="ir_action_hr_timesheet_fulfill_wizard" model="ir.values">34 <button icon="gtk-ok" string="Fill in Timesheet" name="fulfill_timesheet" type="object"/>
35 <field name="key2">client_action_multi</field>35 </footer>
36 <field name="model">hr_timesheet_sheet.sheet</field>36 </form>
37 <field name="name">Fill in Timesheet</field>37 </field>
38 <field eval="'ir.actions.act_window,%d'%action_hr_timesheet_fulfill" name="value"/>38 </record>
39 <field eval="True" name="object"/>39
40 </record>40 <record id="action_hr_timesheet_fulfill" model="ir.actions.act_window">
4141 <field name="name">Fill in Timesheet</field>
42</data>42 <field name="res_model">hr.timesheet.fulfill</field>
43 <field name="view_type">form</field>
44 <field name="view_mode">form</field>
45 <field name="view_id" ref="view_hr_timesheet_fulfill_form"/>
46 <field name="target">new</field>
47 </record>
48
49 <record id="ir_action_hr_timesheet_fulfill_wizard" model="ir.values">
50 <field name="key2">client_action_multi</field>
51 <field name="model">hr_timesheet_sheet.sheet</field>
52 <field name="name">Fill in Timesheet</field>
53 <field eval="'ir.actions.act_window,%d'%action_hr_timesheet_fulfill" name="value"/>
54 <field eval="True" name="object"/>
55 </record>
56
57 </data>
43</openerp>58</openerp>
4459
=== modified file 'hr_timesheet_holidays/__openerp__.py'
--- hr_timesheet_holidays/__openerp__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_holidays/__openerp__.py 2013-11-07 15:51:52 +0000
@@ -55,5 +55,5 @@
55 'company_view.xml',55 'company_view.xml',
56 ],56 ],
57 "active": False,57 "active": False,
58 "installable": True58 'installable': False
59}59}
6060
=== added directory 'hr_timesheet_improvement'
=== added file 'hr_timesheet_improvement/__init__.py'
--- hr_timesheet_improvement/__init__.py 1970-01-01 00:00:00 +0000
+++ hr_timesheet_improvement/__init__.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,22 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author : Yannick Vaucher (Camptocamp)
5# Copyright 2013 Camptocamp SA
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21import hr_timesheet
22import hr_attendance
023
=== added file 'hr_timesheet_improvement/__openerp__.py'
--- hr_timesheet_improvement/__openerp__.py 1970-01-01 00:00:00 +0000
+++ hr_timesheet_improvement/__openerp__.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,50 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Authors: Yannick Vaucher (Camptocamp)
5# Vincent Renaville (Camptocamp)
6# Copyright 2013 Camptocamp SA
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22{'name' : 'Timesheet improvements',
23 'version' : '0.1',
24 'author' : 'Camptocamp',
25 'maintainer': 'Camptocamp',
26 'category': 'Human Resources',
27 'depends' : ['hr_timesheet_sheet'],
28 'description': """
29 Modifies timesheet behavior:
30 - Ensure a DESC order on timesheet lines
31 - Set default date for manually entering attendance to max attendance date
32 - Redefine constraint on timesheets to check alternation of 'sign in' and
33 'sign out' only on current timesheet instead of doing it on all timesheets
34 of the employee
35 """,
36 'website': 'http://www.camptocamp.com',
37 'data': ['hr_timesheet_view.xml'],
38 'js' : [],
39 'css': [],
40 'qweb': [],
41 'demo': [],
42 'test': [],
43 'installable': True,
44 'images' : [],
45 'auto_install': False,
46 'license': 'AGPL-3',
47 'application': True,
48}
49
50# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
051
=== added file 'hr_timesheet_improvement/hr_attendance.py'
--- hr_timesheet_improvement/hr_attendance.py 1970-01-01 00:00:00 +0000
+++ hr_timesheet_improvement/hr_attendance.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,98 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Authors: Yannick Vaucher (Camptocamp)
5# Vincent Renaville (Camptocamp)
6# Copyright 2013 Camptocamp SA
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22import time
23
24from openerp.osv import orm
25from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
26
27
28class HrAttendance(orm.Model):
29 """
30 Alter the default date for manual setting
31 """
32 _inherit = "hr.attendance"
33
34 def _default_date(self, cr, uid, context=None):
35 sheet_id = context.get('sheet_id')
36 if not sheet_id:
37 return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
38
39 ts_obj = self.pool.get('hr_timesheet_sheet.sheet')
40 timesheet = ts_obj.browse(cr, uid, sheet_id, context=context)
41
42 dates = [a.name for a in timesheet.attendances_ids]
43
44 if not dates:
45 return timesheet.date_from
46
47 return max(dates)
48 """
49 Check sign in signout in the same timesheet only
50 """
51
52 def _altern_si_so(self, cr, uid, ids, context=None):
53 """ Alternance sign_in/sign_out check.
54 Previous (if exists) must be of opposite action.
55 Next (if exists) must be of opposite action.
56 """
57 sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
58 for att in self.browse(cr, uid, ids, context=context):
59 sheet_id = sheet_obj.search(
60 cr, uid, [
61 ('employee_id', '=', att.employee_id.id),
62 ('date_from', '<=', att.name),
63 ('date_to', '>=', att.name),
64 ],
65 limit=1,
66 context=context)
67 sheet_id = sheet_id and sheet_id[0] or False
68
69 # search and browse for first previous and first next records
70 prev_att_ids = self.search(cr, uid, [('employee_id', '=', att.employee_id.id),
71 ('sheet_id', '=', sheet_id),
72 ('name', '<', att.name),
73 ('action', 'in', ('sign_in', 'sign_out'))],
74 limit=1, order='name DESC',
75 context=context)
76 next_add_ids = self.search(cr, uid, [('employee_id', '=', att.employee_id.id),
77 ('sheet_id', '=', sheet_id),
78 ('name', '>', att.name),
79 ('action', 'in', ('sign_in', 'sign_out'))],
80 limit=1, order='name ASC',
81 context=context)
82 prev_atts = self.browse(cr, uid, prev_att_ids, context=context)
83 next_atts = self.browse(cr, uid, next_add_ids, context=context)
84 # check for alternance, return False if at least one condition is not satisfied
85 if prev_atts and prev_atts[0].action == att.action: # previous exists and is same action
86 return False
87 if next_atts and next_atts[0].action == att.action: # next exists and is same action
88 return False
89 if (not prev_atts) and (not next_atts) and att.action != 'sign_in': # first attendance must be sign_in
90 return False
91 return True
92
93 _constraints = [(_altern_si_so, 'Error ! Sign in (resp. Sign out) must follow Sign out (resp. Sign in)', ['action'])]
94
95 _defaults = {
96 'name': _default_date,
97 }
98
099
=== added file 'hr_timesheet_improvement/hr_timesheet.py'
--- hr_timesheet_improvement/hr_timesheet.py 1970-01-01 00:00:00 +0000
+++ hr_timesheet_improvement/hr_timesheet.py 2013-11-07 15:51:52 +0000
@@ -0,0 +1,51 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author : Yannick Vaucher (Camptocamp)
5# Copyright 2013 Camptocamp SA
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21from openerp.osv import orm, fields
22
23class HrAnalyticTimesheet(orm.Model):
24 """
25 Set order by line date and analytic account name instead of id
26 We create related stored values as _order cannot be used on inherited columns
27 """
28 _inherit = "hr.analytic.timesheet"
29 _order = "date_aal DESC, account_name ASC"
30
31 def _get_account_analytic_line(self, cr, uid, ids, context=None):
32 ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('line_id', 'in', ids)])
33 return ts_line_ids
34
35 def _get_account_analytic_account(self, cr, uid, ids, context=None):
36 ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('account_id', 'in', ids)])
37 return ts_line_ids
38
39 _columns = {
40 'date_aal': fields.related('line_id', 'date', string="Analytic Line Date", type='date',
41 store={
42 'account.analytic.line': (_get_account_analytic_line, ['date'], 10),
43 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
44 }),
45 'account_name': fields.related('account_id', 'name', string="Analytic Account Name", type='char', size=256,
46 store={
47 'account.analytic.account': (_get_account_analytic_account, ['name'], 10),
48 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
49 }
50 ),
51 }
052
=== added file 'hr_timesheet_improvement/hr_timesheet_view.xml'
--- hr_timesheet_improvement/hr_timesheet_view.xml 1970-01-01 00:00:00 +0000
+++ hr_timesheet_improvement/hr_timesheet_view.xml 2013-11-07 15:51:52 +0000
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="hr_timesheet_sheet_form_pass_active_id" model="ir.ui.view">
6 <field name="name">hr.timesheet.sheet.form</field>
7 <field name="model">hr_timesheet_sheet.sheet</field>
8 <field name="inherit_id" ref="hr_timesheet_sheet.hr_timesheet_sheet_form" />
9 <field name="arch" type="xml">
10 <field name="attendances_ids" position="attributes">
11 <attribute name="context">{'employee_id': employee_id, 'user_id':user_id, 'sheet_id':active_id}</attribute>
12 </field>
13 </field>
14 </record>
15
16 </data>
17</openerp>
018
=== modified file 'hr_timesheet_print/__openerp__.py'
--- hr_timesheet_print/__openerp__.py 2012-12-12 10:35:35 +0000
+++ hr_timesheet_print/__openerp__.py 2013-11-07 15:51:52 +0000
@@ -40,5 +40,5 @@
40 "report.xml",40 "report.xml",
41 ],41 ],
42 "active": False,42 "active": False,
43 "installable": True43 'installable': True
44}44}
4545
=== modified file 'hr_timesheet_reminder/__init__.py'
--- hr_timesheet_reminder/__init__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/__init__.py 2013-11-07 15:51:52 +0000
@@ -1,32 +1,21 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Arnaud Wüst (Camptocamp)
5# All Right Reserved5# Copyright 2011-2012 Camptocamp SA
6#6#
7# Author : Arnaud Wüst (Camptocamp)7# This program is free software: you can redistribute it and/or modify
8# Author : Guewen Baconnier (Camptocamp)8# it under the terms of the GNU Affero General Public License as
9#9# published by the Free Software Foundation, either version 3 of the
10# WARNING: This program as such is intended to be used by professional10# License, or (at your option) any later version.
11# programmers who take the whole responsability of assessing all potential11#
12# consequences resulting from its eventual inadequacies and bugs12# This program is distributed in the hope that it will be useful,
13# End users who are looking for a ready-to-use solution with commercial13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# garantees and support are strongly adviced to contract a Free Software14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# Service Company15# GNU Affero General Public License for more details.
16#16#
17# This program is Free Software; you can redistribute it and/or17# You should have received a copy of the GNU Affero General Public License
18# modify it under the terms of the GNU General Public License18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19# as published by the Free Software Foundation; either version 2
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#19#
31##############################################################################20##############################################################################
3221
3322
=== modified file 'hr_timesheet_reminder/__openerp__.py'
--- hr_timesheet_reminder/__openerp__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/__openerp__.py 2013-11-07 15:51:52 +0000
@@ -1,54 +1,52 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Arnaud Wüst (Camptocamp)
5# All Right Reserved5# Author: Nicolas Bessi (Camptocamp)
6#6# Author: Guewen Baconnier (Camptocamp) (port to v7)
7# Author : Arnaud Wüst (Camptocamp)7# Copyright 2011-2012 Camptocamp SA
8# Author : Nicolas Bessi (Camptocamp)8#
9# Author : Guewen Baconnier (Camptocamp)9# This program is free software: you can redistribute it and/or modify
10#10# it under the terms of the GNU Affero General Public License as
11# WARNING: This program as such is intended to be used by professional11# published by the Free Software Foundation, either version 3 of the
12# programmers who take the whole responsability of assessing all potential12# License, or (at your option) any later version.
13# consequences resulting from its eventual inadequacies and bugs13#
14# End users who are looking for a ready-to-use solution with commercial14# This program is distributed in the hope that it will be useful,
15# garantees and support are strongly adviced to contract a Free Software15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# Service Company16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17#17# GNU Affero General Public License for more details.
18# This program is Free Software; you can redistribute it and/or18#
19# modify it under the terms of the GNU General Public License19# You should have received a copy of the GNU Affero General Public License
20# as published by the Free Software Foundation; either version 220# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# of the License, or (at your option) any later version.
22#
23# This program is distributed in the hope that it will be useful,
24# but WITHOUT ANY WARRANTY; without even the implied warranty of
25# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26# GNU General Public License for more details.
27#
28# You should have received a copy of the GNU General Public License
29# along with this program; if not, write to the Free Software
30# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31#21#
32##############################################################################22##############################################################################
3323
34
35{24{
36 "name" : "Timesheet Reminder",25 "name": "Timesheet Reminder",
37 "version" : "2.0",26 "version": "2.0",
38 "author" : "Camptocamp",27 "author": "Camptocamp",
39 "category" : "",28 "license": 'AGPL-3',
40 "website" : "http://www.camptocamp.com",29 "category": "",
30 "website": "http://www.camptocamp.com",
41 "description": """31 "description": """
42Timesheet Reports Module:32Timesheet Reports Module
43 * Add a menu in Human Resources / Configuration / Timesheet Reminder. It allows to send automatic emails to those who did not complete their timesheet in the last 5 weeks.33========================
44 * Per employee, you can choose to send the reminder or not.34
45 * Add a report in Human Resources / Reporting / Timesheet / Timesheet Status which displays the state of the last 5 timesheets for all users per company.35 * Add a menu in `Human Resources / Configuration
4636 / Timesheet Reminder`.
47This module replaces the modules c2c_timesheet_reports in TinyERP 4 and OpenERP 5.37 It allows to send automatic emails to those who did
38 not complete their timesheet in the last 5 weeks.
39 * Per employee, you can choose to send the reminder or not.
40 * Add a report in `Human Resources / Reporting / Timesheet
41 / Timesheet Status` which displays the state of the last
42 5 timesheets for all users per company.
43
44This module replaces the modules c2c_timesheet_reports
45of TinyERP 4 and OpenERP 5.
48 """,46 """,
49 "depends" : ["hr_timesheet_sheet"],47 "depends": ["hr_timesheet_sheet"],
50 "init_xml" : [],48 "init_xml": [],
51 "update_xml" : [49 "update_xml": [
52 'security/ir.model.access.csv',50 'security/ir.model.access.csv',
53 'wizard/reminder_config_view.xml',51 'wizard/reminder_config_view.xml',
54 'wizard/reminder_status_view.xml',52 'wizard/reminder_status_view.xml',
@@ -56,5 +54,5 @@
56 'timesheet_report.xml',54 'timesheet_report.xml',
57 ],55 ],
58 "active": False,56 "active": False,
59 "installable": True57 'installable': True
60}58}
6159
=== modified file 'hr_timesheet_reminder/company.py'
--- hr_timesheet_reminder/company.py 2012-12-13 08:25:08 +0000
+++ hr_timesheet_reminder/company.py 2013-11-07 15:51:52 +0000
@@ -1,43 +1,32 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Arnaud Wüst (Camptocamp)
5# All Right Reserved5# Author: Guewen Baconnier (Camptocamp)
6#6# Copyright 2011-2012 Camptocamp SA
7# Author : Arnaud Wüst (Camptocamp)7#
8# Author : Guewen Baconnier (Camptocamp)8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
3222
3323from datetime import datetime
34from datetime import date, datetime24from dateutil.relativedelta import relativedelta, MO, SU
35from dateutil.relativedelta import *25from openerp.osv import osv, orm
36from osv import fields, osv26from openerp.tools.translate import _
37from tools.translate import _27
3828
3929class res_company(orm.Model):
40class res_company(osv.osv):
41 _inherit = 'res.company'30 _inherit = 'res.company'
4231
43 def get_reminder_recipients(self, cr, uid, ids, context=None):32 def get_reminder_recipients(self, cr, uid, ids, context=None):
@@ -46,12 +35,25 @@
4635
47 employee_obj = self.pool.get('hr.employee')36 employee_obj = self.pool.get('hr.employee')
4837
38<<<<<<< TREE
49 for company in self.browse(cr, uid, ids, context=context):39 for company in self.browse(cr, uid, ids, context=context):
50 employee_ids = employee_obj.search(40 employee_ids = employee_obj.search(
51 cr, uid,41 cr, uid,
52 [('company_id', '=', company.id),42 [('company_id', '=', company.id),
53 ('active', '=', True)],43 ('active', '=', True)],
54 context=context)44 context=context)
45=======
46 for company in self.browse(cr, uid, ids, context=context):
47 employee_ids = employee_obj.search(
48 cr, uid,
49 [('company_id', '=', company.id),
50 ('receive_timesheet_alerts', '=', True)],
51 context=context)
52
53 if not employee_ids:
54 continue
55
56>>>>>>> MERGE-SOURCE
55 employees = employee_obj.browse(cr, uid, employee_ids, context=context)57 employees = employee_obj.browse(cr, uid, employee_ids, context=context)
5658
57 #periods59 #periods
@@ -62,24 +64,26 @@
62 # for each employee64 # for each employee
63 for employee in employees:65 for employee in employees:
64 # is timesheet for a period not confirmed ?66 # is timesheet for a period not confirmed ?
65 for p_index in range(len(periods)):67 for period in periods:
66 period = periods[p_index]
67 status = employee_obj.compute_timesheet_status(cr, uid, employee.id, period, context)68 status = employee_obj.compute_timesheet_status(cr, uid, employee.id, period, context)
6869
69 # if there is a missing sheet or a draft sheet70 # if there is a missing sheet or a draft sheet
70 # and the user can receive alerts71 # and the user can receive alerts
71 # then we must alert the user72 # then we must alert the user
72 if status in ['Missing', 'Draft'] and employee.receive_timesheet_alerts:73 if status in ['Missing', 'Draft']:
73 res[company.id].append(employee)74 res[company.id].append(employee)
74 break # no need to go further for this user, he is now added in the list, go to the next one75 # no need to go further for this user,
76 # he is now added in the list, go to the next one
77 break
75 return res78 return res
7679
77 def compute_timesheet_periods(self, cr, uid, company, date, periods_number=5, context=None):80 def compute_timesheet_periods(self, cr, uid, company, date, periods_number=5, context=None):
78 """ return the timeranges to display. This is the 5 last timesheets"""81 """ return the timeranges to display. This is the 5 last timesheets"""
79 periods = []82 periods = []
80 last_start_date, last_end_date = self.get_last_period_dates(cr, uid, company, date, context=context)83 last_start_date, last_end_date = self.get_last_period_dates(
84 cr, uid, company, date, context=context)
81 for cpt in range(periods_number):85 for cpt in range(periods_number):
82 #find the delta between last_XXX_date to XXX_date86 # find the delta between last_XXX_date to XXX_date
83 if company.timesheet_range == 'month':87 if company.timesheet_range == 'month':
84 delta = relativedelta(months=-cpt)88 delta = relativedelta(months=-cpt)
85 elif company.timesheet_range == 'week':89 elif company.timesheet_range == 'week':
@@ -87,7 +91,9 @@
87 elif company.timesheet_range == 'year':91 elif company.timesheet_range == 'year':
88 delta = relativedelta(years=-cpt)92 delta = relativedelta(years=-cpt)
89 else:93 else:
90 raise osv.except_osv(_('Error'), _('Unknow timesheet range: %s') % (company.timesheet_range,))94 raise osv.except_osv(
95 _('Error'),
96 _('Unknow timesheet range: %s') % company.timesheet_range)
9197
92 start_date = last_start_date + delta98 start_date = last_start_date + delta
93 end_date = last_end_date + delta99 end_date = last_end_date + delta
@@ -97,26 +103,22 @@
97103
98 def get_last_period_dates(self, cr, uid, company, date, context=None):104 def get_last_period_dates(self, cr, uid, company, date, context=None):
99 """ return the start date and end date of the last period to display """105 """ return the start date and end date of the last period to display """
100 106
101 # return the first day and last day of the month107 # return the first day and last day of the month
102 if company.timesheet_range == 'month':108 if company.timesheet_range == 'month':
103 start_date = date109 start_date = date
104 end_date = start_date + relativedelta(months = +1)110 end_date = start_date + relativedelta(months=+1)
105111
106 #return the first and last days of the week112 #return the first and last days of the week
107 elif company.timesheet_range == 'week':113 elif company.timesheet_range == 'week':
108 # get monday of current week114 # get monday of current week
109 start_date = date + relativedelta(weekday=MO(-1))115 start_date = date + relativedelta(weekday=MO(-1))
110 # get sunday of current week 116 # get sunday of current week
111 end_date = date + relativedelta(weekday=SU(+1))117 end_date = date + relativedelta(weekday=SU(+1))
112118
113 # return the first and last days of the year119 # return the first and last days of the year
114 else:120 else:
115 start_date = datetime(date.year, 1, 1) 121 start_date = datetime(date.year, 1, 1)
116 end_date = datetime(date.year, 12, 31)122 end_date = datetime(date.year, 12, 31)
117123
118
119 return start_date, end_date124 return start_date, end_date
120
121
122res_company()
123125
=== modified file 'hr_timesheet_reminder/hr_employee.py'
--- hr_timesheet_reminder/hr_employee.py 2011-09-01 13:44:19 +0000
+++ hr_timesheet_reminder/hr_employee.py 2013-11-07 15:51:52 +0000
@@ -1,47 +1,38 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Arnaud Wüst (Camptocamp)
5# All Right Reserved5# Author: Guewen Baconnier (Camptocamp) (port to v7)
6#6# Copyright 2011-2012 Camptocamp SA
7# Author : Arnaud Wüst (Camptocamp)7#
8# Author : Guewen Baconnier (Camptocamp)8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
32from datetime import *22
3323from openerp.osv import fields, orm
34from osv import fields, osv24from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
3525
3626
37class hr_employee(osv.osv):27class hr_employee(orm.Model):
38 _inherit = 'hr.employee'28 _inherit = 'hr.employee'
29
39 _columns = {30 _columns = {
40 'receive_timesheet_alerts': fields.boolean('Receive Timesheet Alerts'),31 'receive_timesheet_alerts': fields.boolean('Receive Timesheet Alerts'),
41 }32 }
4233
43 _defaults = {34 _defaults = {
44 'receive_timesheet_alerts': lambda *a: True,35 'receive_timesheet_alerts': True,
45 }36 }
4637
47 def compute_timesheet_status(self, cr, uid, ids, period, context):38 def compute_timesheet_status(self, cr, uid, ids, period, context):
@@ -50,35 +41,33 @@
50 status = 'Error'41 status = 'Error'
5142
52 if isinstance(ids, list):43 if isinstance(ids, list):
44 assert len(ids) == 1, "Only 1 ID expected"
53 ids = ids[0]45 ids = ids[0]
5446
55 employee = self.browse(cr, uid, ids, context=context)47 employee = self.browse(cr, uid, ids, context=context)
5648
57 time_from = period[0]49 time_from, time_to = period
58 time_to = period[1]50
59 51 # does the timesheet exists in db and what is its status?
60 # does the timesheet exsists in db and what is its status?52 str_date_from = time_from.strftime(DEFAULT_SERVER_DATE_FORMAT)
61 timeformat = "%Y-%m-%d"53 str_date_to = time_to.strftime(DEFAULT_SERVER_DATE_FORMAT)
62 str_date_from = time_from.strftime(timeformat)54
63 str_date_to = time_to.strftime(timeformat)55 cr.execute(
6456 """SELECT state, date_from, date_to
65 cr.execute("""SELECT state, date_from, date_to57 FROM hr_timesheet_sheet_sheet
66 FROM hr_timesheet_sheet_sheet58 WHERE employee_id = %s
67 WHERE employee_id = %s59 AND date_from >= %s
68 AND date_from >= %s60 AND date_to <= %s""",
69 AND date_to <= %s""",
70 (employee.id, str_date_from, str_date_to))61 (employee.id, str_date_from, str_date_to))
71 sheets = cr.dictfetchall()62 sheets = cr.dictfetchall()
7263
73 #the timesheet does not exists in db64 # the timesheet does not exists in db
74 if not sheets:65 if not sheets:
75 status = 'Missing'66 status = 'Missing'
7667
77 if len(sheets) > 0:68 else:
78 status = 'Confirmed'69 status = 'Confirmed'
79 for s in sheets:70 for sheet in sheets:
80 if s['state'] == 'draft':71 if sheet['state'] == 'draft':
81 status = 'Draft'72 status = 'Draft'
82 return status73 return status
83
84hr_employee()
8574
=== modified file 'hr_timesheet_reminder/hr_employee_view.xml'
--- hr_timesheet_reminder/hr_employee_view.xml 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/hr_employee_view.xml 2013-11-07 15:51:52 +0000
@@ -5,7 +5,6 @@
5 <field name="name">hr.timesheet.employee.rmnd_form</field>5 <field name="name">hr.timesheet.employee.rmnd_form</field>
6 <field name="inherit_id" ref="hr_timesheet.hr_timesheet_employee_extd_form"/>6 <field name="inherit_id" ref="hr_timesheet.hr_timesheet_employee_extd_form"/>
7 <field name="model">hr.employee</field>7 <field name="model">hr.employee</field>
8 <field name="type">form</field>
9 <field name="arch" type="xml">8 <field name="arch" type="xml">
10 <field name="journal_id" widget="selection" position="after">9 <field name="journal_id" widget="selection" position="after">
11 <field name="receive_timesheet_alerts"/>10 <field name="receive_timesheet_alerts"/>
1211
=== modified file 'hr_timesheet_reminder/reminder.py'
--- hr_timesheet_reminder/reminder.py 2011-09-01 13:44:19 +0000
+++ hr_timesheet_reminder/reminder.py 2013-11-07 15:51:52 +0000
@@ -1,157 +1,170 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Arnaud Wüst (Camptocamp)
5# All Right Reserved5# Author: Guewen Baconnier (Camptocamp) (port to v7)
6#6# Copyright 2011-2012 Camptocamp SA
7# Author : Arnaud Wüst (Camptocamp)7#
8# Author : Guewen Baconnier (Camptocamp)8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
3222
33import tools
34import time
35
36from datetime import datetime, timedelta23from datetime import datetime, timedelta
37from osv import fields, osv24from openerp.osv import fields, orm
38from tools.translate import _25from openerp.tools.translate import _
3926from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
40class reminder(osv.osv):27
28
29class reminder(orm.Model):
41 _name = "hr.timesheet.reminder"30 _name = "hr.timesheet.reminder"
42 _description = "Handle the scheduling of timesheet reminders"31 _description = "Handle the scheduling of timesheet reminders"
4332
44 _columns = {33 _columns = {
45 'reply_to': fields.char('Reply To', size=100),34 'reply_to': fields.char('Reply To'),
46 'message': fields.text('Message'),35 'message': fields.html('Message'),
47 'subject': fields.char('Subject', size=200),36 'subject': fields.char('Subject'),
48 }37 }
4938
50 #default cron (the one created if missing)39 # default cron (the one created if missing)
51 cron = {'active': False,40 cron = {'active': False,
52 'priority': 1,41 'priority': 1,
53 'interval_number': 1,42 'interval_number': 1,
54 'interval_type': 'weeks',43 'interval_type': 'weeks',
55 'nextcall': time.strftime("%Y-%m-%d %H:%M:%S",44 'nextcall': False, # to set on the creation of the cron
56 (datetime.today()
57 + timedelta(days=1)).timetuple()), # tomorrow same time
58 'numbercall': -1,45 'numbercall': -1,
59 'doall': True,46 'doall': False,
60 'model': 'hr.timesheet.reminder',47 'model': 'hr.timesheet.reminder',
61 'function': 'run',48 'function': 'run',
62 'args': '()',49 'args': '()',
63 }50 }
6451
65 #default message (the one created if missing)52 # default message (the one created if missing)
66 message = {'reply_to': 'spam@camptocamp.com'}53 message = {'reply_to': 'spam@camptocamp.com'}
6754
68 def run(self, cr, uid, context=None):55 def run(self, cr, uid, context=None):
69 """ find the reminder recipients and send them an email """56 """ find the reminder recipients and send them an email """
70 context = context or {}
71
72 company_obj = self.pool.get('res.company')57 company_obj = self.pool.get('res.company')
73 #get all companies58 # get all companies
74 company_ids = company_obj.search(cr, uid, [], context=context)59 company_ids = company_obj.search(cr, uid, [], context=context)
7560
76 #for each company, get all recipients61 # for each company, get all recipients
77 recipients = []62 recipients = []
78 company_recipients = company_obj.get_reminder_recipients(cr, uid, company_ids, context=context)63 company_recipients = company_obj.get_reminder_recipients(
79 for company_id, rec in company_recipients.iteritems():64 cr, uid, company_ids, context=context)
65 for rec in company_recipients.itervalues():
80 recipients += rec66 recipients += rec
8167
82 #get the message to send68 # get the message to send
83 message_id = self.get_message_id(cr, uid, context)69 message_id = self.get_message_id(cr, uid, context)
84 message_data = self.browse(cr, uid, message_id, context=context)70 message_data = self.browse(cr, uid, message_id, context=context)
8571
86 #send them email if they have an email defined72 # send them email if they have an email defined
87 emails = []
88 for employee in recipients:73 for employee in recipients:
89 if employee.work_email:74 if not employee.work_email:
90 emails.append(employee.work_email)75 continue
9176 vals = {
92 if emails:77 'state': 'outgoing',
93 tools.email_send(message_data.reply_to, [], message_data.subject, message_data.message, email_bcc=emails)78 'subject': message_data.subject,
9479 'body_html': message_data.message,
95 def get_cron_id(self, cr, uid, context):80 'email_to': employee.work_email,
81 'email_from': message_data.reply_to,
82 }
83 self.pool.get('mail.mail').create(cr, uid, vals, context=context)
84
85 return True
86
87 def get_cron_id(self, cr, uid, context=None):
96 """return the reminder cron's id. Create one if the cron does not exists """88 """return the reminder cron's id. Create one if the cron does not exists """
89 if context is None:
90 context = {}
97 cron_obj = self.pool.get('ir.cron')91 cron_obj = self.pool.get('ir.cron')
98 # find the cron that send messages92 # find the cron that send messages
99 cron_id = cron_obj.search(cr, uid, [('function', 'ilike', self.cron['function']),93 ctx = dict(context, active_test=False)
100 ('model', 'ilike', self.cron['model'])],94 cron_ids = cron_obj.search(
101 context={'active_test': False})95 cr, uid,
102 if cron_id:96 [('function', 'ilike', self.cron['function']),
103 cron_id = cron_id[0]97 ('model', 'ilike', self.cron['model'])],
98 context=ctx)
99
100 cron_id = None
101 if cron_ids:
102 cron_id = cron_ids[0]
104103
105 # the cron does not exists104 # the cron does not exists
106 if not cron_id:105 if cron_id is None:
107 self.cron['name'] = _('timesheet status reminder')106 vals = dict(self.cron,
108 cron_id = cron_obj.create(cr, uid, self.cron, context)107 name=_('timesheet status reminder'),
108 nextcall=self._cron_nextcall())
109
110 cron_id = cron_obj.create(cr, uid, vals, context=context)
109111
110 return cron_id112 return cron_id
111113
112 def get_message_id(self, cr, uid, context):114 @staticmethod
113 """ return the message'id. create one if the message does not exists """115 def _cron_nextcall():
116 tomorrow = datetime.today() + timedelta(days=1)
117 return tomorrow.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
118
119 def get_message_id(self, cr, uid, context=None):
120 """ return the message's id. create one if the message does not exists """
114 #there is only one line in db, let's get it121 #there is only one line in db, let's get it
115 message_id = self.search(cr, uid, [], limit=1, context=context)122 message_ids = self.search(cr, uid, [], limit=1, context=context)
116123
117 if message_id:124 message_id = None
118 message_id = message_id[0]125 if message_ids:
126 message_id = message_ids[0]
119127
120 #the message does not exists128 #the message does not exists
121 if not message_id:129 if message_id is None:
122 #translate130 vals = dict(self.message,
123 self.message['subject'] = _('Timesheet Reminder')131 subject=_('Timesheet Reminder'),
124 self.message['message'] = _('At least one of your last timesheets is still in draft or is missing. Please take time to complete and confirm it.')132 message=_(
133 'At least one of your last timesheets is still '
134 'in draft or is missing. Please take time to '
135 'complete and confirm it.'))
125136
126 message_id = self.create(cr, uid, self.message, context)137 message_id = self.create(cr, uid, vals, context)
127138
128 return message_id139 return message_id
129140
130 def get_config(self, cr, uid, context):141 def get_config(self, cr, uid, context=None):
131 """return the reminder config from the db """142 """return the reminder config from the db """
132143
133 cron_id = self.get_cron_id(cr, uid, context)144 cron_id = self.get_cron_id(cr, uid, context)
134145
135 cron_data = self.pool.get('ir.cron').browse(cr, uid, cron_id)146 cron_data = self.pool.get('ir.cron').browse(
147 cr, uid, cron_id, context=context)
136148
137 #there is only one line in db, let's get it149 # there is only one line in db, let's get it
138 message_id = self.get_message_id(cr, uid, context)150 message_id = self.get_message_id(cr, uid, context=context)
139 message_data = self.browse(cr, uid, message_id)151 message_data = self.browse(cr, uid, message_id, context=context)
140 return {'reminder_active': cron_data.active,152 return {'reminder_active': cron_data.active,
141 'interval_type': cron_data.interval_type,153 'interval_type': cron_data.interval_type,
142 'interval_number': cron_data.interval_number,154 'interval_number': cron_data.interval_number,
143 'reply_to': message_data.reply_to,155 'reply_to': message_data.reply_to,
144 'message': message_data.message,156 'message': message_data.message,
145 'subject': message_data.subject,157 'subject': message_data.subject,
146 'nextcall': cron_data.nextcall,158 'nextcall': self._cron_nextcall(),
147 }159 }
148160
149 def save_config(self, cr, uid, ids, datas, context):161 def save_config(self, cr, uid, ids, datas, context=None):
150 """save the reminder config """162 """save the reminder config """
151163
152 #modify the cron164 #modify the cron
153 cron_id = self.get_cron_id(cr, uid, context)165 cron_id = self.get_cron_id(cr, uid, context=context)
154 self.pool.get('ir.cron').write(cr, uid, [cron_id],166 self.pool.get('ir.cron').write(
167 cr, uid, [cron_id],
155 {'active': datas['reminder_active'],168 {'active': datas['reminder_active'],
156 'interval_number': datas['interval_number'],169 'interval_number': datas['interval_number'],
157 'interval_type': datas['interval_type'],170 'interval_type': datas['interval_type'],
@@ -159,11 +172,10 @@
159 context=context)172 context=context)
160 #modify the message173 #modify the message
161 message_id = ids or self.get_message_id(cr, uid, context)174 message_id = ids or self.get_message_id(cr, uid, context)
162 self.write(cr, uid, [message_id],175 self.write(
176 cr, uid, [message_id],
163 {'reply_to': datas['reply_to'],177 {'reply_to': datas['reply_to'],
164 'message': datas['message'],178 'message': datas['message'],
165 'subject': datas['subject'],179 'subject': datas['subject'],
166 }, context=context)180 }, context=context)
167 return True181 return True
168
169reminder()
170182
=== modified file 'hr_timesheet_reminder/report/__init__.py'
--- hr_timesheet_reminder/report/__init__.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/report/__init__.py 2013-11-07 15:51:52 +0000
@@ -1,31 +1,22 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) Camptocamp SA4# Author: Arnaud Wüst (Camptocamp)
5# Author: Arnaud WÃŒst5# Copyright 2011-2012 Camptocamp SA
6#6#
7#7# This program is free software: you can redistribute it and/or modify
8#8# it under the terms of the GNU Affero General Public License as
9# WARNING: This program as such is intended to be used by professional9# published by the Free Software Foundation, either version 3 of the
10# programmers who take the whole responsability of assessing all potential10# License, or (at your option) any later version.
11# consequences resulting from its eventual inadequacies and bugs11#
12# End users who are looking for a ready-to-use solution with commercial12# This program is distributed in the hope that it will be useful,
13# garantees and support are strongly adviced to contract a Free Software13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# Service Company14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15#15# GNU Affero General Public License for more details.
16# This program is Free Software; you can redistribute it and/or16#
17# modify it under the terms of the GNU General Public License17# You should have received a copy of the GNU Affero General Public License
18# as published by the Free Software Foundation; either version 218# along with this program. If not, see <http://www.gnu.org/licenses/>.
19# of the License, or (at your option) any later version.
20#
21# This program is distributed in the hope that it will be useful,
22# but WITHOUT ANY WARRANTY; without even the implied warranty of
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24# GNU General Public License for more details.
25#
26# You should have received a copy of the GNU General Public License
27# along with this program; if not, write to the Free Software
28# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29#19#
30##############################################################################20##############################################################################
21
31import timesheet_status22import timesheet_status
3223
=== modified file 'hr_timesheet_reminder/report/timesheet_status.py'
--- hr_timesheet_reminder/report/timesheet_status.py 2011-09-01 13:44:19 +0000
+++ hr_timesheet_reminder/report/timesheet_status.py 2013-11-07 15:51:52 +0000
@@ -1,47 +1,40 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) Camptocamp SA4# Author: Arnaud Wüst (Camptocamp)
5# Author: Arnaud WÃŒst5# Author: Guewen Baconnier (Camptocamp) (port to v7)
6# Author: Guewen Baconnier6# Copyright 2011-2012 Camptocamp SA
7#7#
8#8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
3222
33import time23import time
3424
35from datetime import datetime25from datetime import datetime
36from report import report_sxw26from openerp.report import report_sxw
27from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
28from openerp.tools.translate import _
3729
3830
39class timesheet_status(report_sxw.rml_parse):31class timesheet_status(report_sxw.rml_parse):
40 _name = 'report.timesheet.reminder.status'32 _name = 'report.timesheet.reminder.status'
4133
42 def __init__(self, cr, uid, name, context):34 def __init__(self, cr, uid, name, context=None):
43 super(timesheet_status, self).__init__(cr, uid, name, context)35 super(timesheet_status, self).__init__(cr, uid, name, context=context)
44 self.data = {}36 self.data = {}
37 self.end_date = None
45 self.localcontext.update({38 self.localcontext.update({
46 'compute': self.compute,39 'compute': self.compute,
47 'time': time,40 'time': time,
@@ -60,64 +53,65 @@
60 """compute all datas and do all the calculations before to start the rml rendering53 """compute all datas and do all the calculations before to start the rml rendering
61 - objects are companies54 - objects are companies
62 """55 """
63 #init the data array56 # init the data array
64 self.data = {}57 self.data = {}
65 for o in objects:58 for o in objects:
66 self.data[o.id] = {}59 self.data[o.id] = {}
67 #get the list of employees ids to treat60 # get the list of employees ids to treat
68 for o in objects:61 for o in objects:
69 self.data[o.id]['employees'] = self._compute_employees_list(o)62 self.data[o.id]['employees'] = self._compute_employees_list(o)
7063
71 #get the time range for each company64 # get the time range for each company
65 end_date = datetime.strptime(self.end_date, DEFAULT_SERVER_DATE_FORMAT)
72 for o in objects:66 for o in objects:
73 self.data[o.id]['time_ranges'] = \67 self.data[o.id]['time_ranges'] = self._compute_periods(o, end_date)
74 self._compute_periods(o, datetime.strptime(self.end_date, "%Y-%m-%d"))
7568
76 #get the status of each timesheet for each employee69 # get the status of each timesheet for each employee
77 for o in objects:70 for o in objects:
78 self.data[o.id]['sheet_status'] = self._compute_all_status(o)71 self.data[o.id]['sheet_status'] = self._compute_all_status(o)
7972
80 def _compute_employees_list(self, company):73 def _compute_employees_list(self, company):
81 """ return a dictionnary of lists of employees ids linked to the companies (param company) """74 """ return a dictionnary of lists of employees ids linked
75 to the companies (param company) """
82 employee_obj = self.pool.get('hr.employee')76 employee_obj = self.pool.get('hr.employee')
83 employee_ids = employee_obj.search(self.cr, self.uid,77 employee_ids = employee_obj.search(self.cr, self.uid,
84 [('company_id', '=', company.id),78 [('company_id', '=', company.id),
85 ('active', '=', True)],79 ('active', '=', True)],
86 context=self.localcontext)80 context=self.localcontext)
87 return employee_obj.browse(self.cr, self.uid, employee_ids, context=self.localcontext)81 return employee_obj.browse(
82 self.cr, self.uid, employee_ids, context=self.localcontext)
8883
89 def _get_last_period_dates(self, company, date):84 def _get_last_period_dates(self, company, date):
90 """ return the start date of the last period to display """85 """ return the start date of the last period to display """
91 return self.pool.get('res.company').\86 return self.pool.get('res.company').get_last_period_dates(
92 get_last_period_dates(self.cr, self.uid, company, date, context=self.localcontext)87 self.cr,
88 self.uid,
89 company,
90 date,
91 context=self.localcontext)
9392
94 def _compute_periods(self, company, date):93 def _compute_periods(self, company, date):
95 """ return the timeranges to display. This is the 5 last timesheets """94 """ return the timeranges to display. This is the 5 last timesheets """
96 return self.pool.get('res.company').\95 return self.pool.get('res.company').compute_timesheet_periods(
97 compute_timesheet_periods(self.cr, self.uid, company, date, context=self.localcontext)96 self.cr,
97 self.uid,
98 company,
99 date,
100 context=self.localcontext)
98101
99 def get_title(self, obj):102 def get_title(self, obj):
100 """ return the title of the main table """103 """ return the title of the main table """
101 last_id = len(self.data[obj.id]['time_ranges']) - 1104 timerange = self.data[obj.id]['time_ranges']
102 start_date = time.strptime(str(self.data[obj.id]['time_ranges'][last_id][0]),105 start_date = self.formatLang(timerange[-1][0], date=True)
103 "%Y-%m-%d %H:%M:%S")106 end_date = self.formatLang(timerange[0][1], date=True)
104 start_date = time.strftime("%d.%m.%Y", start_date)107
105108 return obj.name + ", " + start_date + _(" to ") + end_date
106 end_date = time.strptime(str(self.data[obj.id]['time_ranges'][0][1]),
107 "%Y-%m-%d %H:%M:%S")
108 end_date = time.strftime("%d.%m.%Y", end_date)
109
110 return obj.name + ", " + start_date + " to " + end_date
111109
112 def get_timerange_title(self, obj, cpt):110 def get_timerange_title(self, obj, cpt):
113 """ return a header text for a periods column """111 """ return a header text for a periods column """
114 start_date = self.data[obj.id]['time_ranges'][cpt][0]112 timerange = self.data[obj.id]['time_ranges'][cpt]
115 start_date = time.strptime(str(start_date), "%Y-%m-%d %H:%M:%S")113 start_date = self.formatLang(timerange[0], date=True)
116 start_date = time.strftime("%d.%m.%Y", start_date)114 end_date = self.formatLang(timerange[1], date=True)
117
118 end_date = self.data[obj.id]['time_ranges'][cpt][1]
119 end_date = time.strptime(str(end_date), "%Y-%m-%d %H:%M:%S")
120 end_date = time.strftime("%d.%m.%Y", end_date)
121115
122 return start_date + "\n " + end_date116 return start_date + "\n " + end_date
123117
@@ -131,21 +125,25 @@
131125
132 def _compute_timesheet_status(self, employee_id, period):126 def _compute_timesheet_status(self, employee_id, period):
133 """ return the timesheet status for a user and a period """127 """ return the timesheet status for a user and a period """
134 return self.pool.get('hr.employee').\128 return self.pool.get('hr.employee').compute_timesheet_status(
135 compute_timesheet_status(self.cr, self.uid, employee_id, period, context=self.localcontext)129 self.cr,
130 self.uid,
131 employee_id,
132 period,
133 context=self.localcontext)
136134
137 def _compute_all_status(self, o):135 def _compute_all_status(self, obj):
138 """ compute all status for all employees for all periods """136 """ compute all status for all employees for all periods """
139 result = {}137 result = {}
140138
141 #for each periods139 #for each periods
142 for p_index in range(len(self.data[o.id]['time_ranges'])):140 for p_index, period in enumerate(self.data[obj.id]['time_ranges']):
143 result[p_index] = {}141 result[p_index] = {}
144 period = self.data[o.id]['time_ranges'][p_index]
145 #for each employees142 #for each employees
146 for employee in self.data[o.id]['employees']:143 for employee in self.data[obj.id]['employees']:
147 #compute the status144 #compute the status
148 result[p_index][employee.id] = self._compute_timesheet_status(employee.id, period)145 result[p_index][employee.id] = self._compute_timesheet_status(
146 employee.id, period)
149147
150 return result148 return result
151149
152150
=== modified file 'hr_timesheet_reminder/report/timesheet_status.rml'
--- hr_timesheet_reminder/report/timesheet_status.rml 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/report/timesheet_status.rml 2013-11-07 15:51:52 +0000
@@ -1,165 +1,159 @@
1<?xml version="1.0"?>1<?xml version="1.0"?>
2<document filename="timesheet_status.pdf">2<document filename="timesheet_status.pdf">
3##############################################################################3 <!--
4#4 ##############################################################################
5# Copyright (c) Camptocamp SA5 #
6# Author: Arnaud Wüst6 # Author: Arnaud Wüst (Camptocamp)
7#7 # Copyright 2011-2012 Camptocamp SA
8#8 #
9# WARNING: This program as such is intended to be used by professional9 # This program is free software: you can redistribute it and/or modify
10# programmers who take the whole responsability of assessing all potential10 # it under the terms of the GNU Affero General Public License as
11# consequences resulting from its eventual inadequacies and bugs11 # published by the Free Software Foundation, either version 3 of the
12# End users who are looking for a ready-to-use solution with commercial12 # License, or (at your option) any later version.
13# garantees and support are strongly adviced to contract a Free Software13 #
14# Service Company14 # This program is distributed in the hope that it will be useful,
15#15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16# This program is Free Software; you can redistribute it and/or16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# modify it under the terms of the GNU General Public License17 # GNU Affero General Public License for more details.
18# as published by the Free Software Foundation; either version 218 #
19# of the License, or (at your option) any later version.19 # You should have received a copy of the GNU Affero General Public License
20#20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21# This program is distributed in the hope that it will be useful,21 #
22# but WITHOUT ANY WARRANTY; without even the implied warranty of22 ##############################################################################
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the23 -->
24# GNU General Public License for more details.24
25#25
26# You should have received a copy of the GNU General Public License26 <!-- Process all datas -->
27# along with this program; if not, write to the Free Software27 <template pageSize="(21cm,29.7cm)" title="Timesheet Status" author="Camptocamp" allowSplitting="20">
28# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.28
29#29
30##############################################################################30 <!-- PAGE: template of all pages (= all pages except first and last if defined)-->
3131 <pageTemplate id="all">
32 <!-- Process all datas -->32 <pageGraphics>
33 <template pageSize="(21cm,29.7cm)" title="Timesheet Status" author="Camptocamp" allowSplitting="20">33 <setFont name="Helvetica-Bold" size="9"/>
34 34
35 35 <!--Header-->
36 <!-- PAGE: template of all pages (= all pages except first and last if defined)--> 36 <drawString x="1.2cm" y="28.1cm">[[ company.name ]]</drawString>
37 <pageTemplate id="all">37 <drawString x="17.0cm" y="28.1cm">Timesheet Status</drawString>
38 <pageGraphics>38
39 <setFont name="Helvetica-Bold" size="9"/>39 <lineMode width="0.7"/>
40 40 <lines>1.2cm 28.0cm 19.8cm 28.0cm</lines>
41 <!--Header-->41
42 <drawString x="1.2cm" y="28.1cm">[[ company.name ]]</drawString>42 <!-- Footer -->
43 <drawString x="17.0cm" y="28.1cm">Timesheet Status</drawString>43 <setFont name="Helvetica" size="9"/>
4444 <drawString x="1.2cm" y="1.3cm"> [[ time.strftime("%m-%d-%y %H:%M", time.localtime()) ]]</drawString>
45 <lineMode width="0.7"/>45 <drawString x="18.8cm" y="1.3cm">Page <pageNumber/></drawString>
46 <lines>1.2cm 28.0cm 19.8cm 28.0cm</lines>46
47 47 </pageGraphics>
48 <!-- Footer -->48 <frame id="all" x1="1.2cm" y1="1.7cm" width="18.6cm" height="25.8cm"/>
49 <setFont name="Helvetica" size="9"/>49 </pageTemplate>
50 <drawString x="1.2cm" y="1.3cm"> [[ time.strftime("%m-%d-%y %H:%M", time.localtime()) ]]</drawString>50
51 <drawString x="18.8cm" y="1.3cm">Page <pageNumber/></drawString>51 </template>
52 52 <stylesheet>
53 </pageGraphics>53
54 <frame id="all" x1="1.2cm" y1="1.7cm" width="18.6cm" height="25.8cm"/>54 <!--TABLE: standard table type "columns" (which means there is a title, a header of fields names and then lines of values) -->
55 </pageTemplate>55 <blockTableStyle id="std">
56 56 <blockAlignment value="LEFT"/>
57 </template>57 <blockValign value="TOP"/>
58 <stylesheet>58 <blockBottomPadding length="4"/>
5959 <blockFont name="Helvetica" size="9" start="0,0" stop="-1,-1"/>
60 <!--TABLE: standard table type "columns" (which means there is a title, a header of fields names and then lines of values) --> 60
61 <blockTableStyle id="std">61 <!-- first line: table name, fake as it was only one cell. grey bg -->
62 <blockAlignment value="LEFT"/>62 <lineStyle kind="BOX" colorName="black" start="0,0" stop="-1,0"/>
63 <blockValign value="TOP"/>63 <blockBackground colorName="#cccccc" start="0,0" stop="-1,0"/>
64 <blockBottomPadding length="4"/>64 <blockFont name="Helvetica" size="9" start="0,0" stop="-1,0"/>
65 <blockFont name="Helvetica" size="9" start="0,0" stop="-1,-1"/>65 <!-- second line: header of columns -->
66 66 <lineStyle kind="GRID" colorName="black" start="0,1" stop="-1,1"/>
67 <!-- first line: table name, fake as it was only one cell. grey bg -->67 <blockFont name="Helvetica-Oblique" start="0,1" stop="-1,1"/>
68 <lineStyle kind="BOX" colorName="black" start="0,0" stop="-1,0"/>68 <!-- next lines: light grey lines, strong black columns separator, reduce padding to write more data in cells -->
69 <blockBackground colorName="#cccccc" start="0,0" stop="-1,0"/>69 <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,2" stop="-1,-1"/>
70 <blockFont name="Helvetica" size="9" start="0,0" stop="-1,0"/>70 <lineStyle kind="LINEAFTER" colorName="black" start="0,2" stop="-1,-1"/>
71 <!-- second line: header of columns -->71 <!-- last line: line below -->
72 <lineStyle kind="GRID" colorName="black" start="0,1" stop="-1,1"/>72 <lineStyle kind="OUTLINE" colorName="black" start="0,0" stop="-1,-1"/>
73 <blockFont name="Helvetica-Oblique" start="0,1" stop="-1,1"/>73 <!-- all columns centered except the first two (1 system columns + employee) -->
74 <!-- next lines: light grey lines, strong black columns separator, reduce padding to write more data in cells -->74 <blockAlignment value="CENTER" start="2,1" stop="-1,-1" />
75 <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,2" stop="-1,-1"/> 75
76 <lineStyle kind="LINEAFTER" colorName="black" start="0,2" stop="-1,-1"/>76 </blockTableStyle>
77 <!-- last line: line below -->77
78 <lineStyle kind="OUTLINE" colorName="black" start="0,0" stop="-1,-1"/>78 <!-- default para in tables -->
79 <!-- all columns centered except the first two (1 system columns + employee) -->79 <paraStyle name="std"
80 <blockAlignment value="CENTER" start="2,1" stop="-1,-1" />80 fontName="Helvetica"
81 81 fontSize="9"
82 </blockTableStyle>82 alignment="LEFT"
8383 />
84 <!-- default para in tables -->84
85 <paraStyle name="std"85 <paraStyle name="Confirmed"
86 fontName="Helvetica" 86 fontName="Helvetica"
87 fontSize="9"87 fontSize="9"
88 alignment="LEFT"88 alignment="CENTER"
89 />89 backColor="green"
90 90 textColor="white"
91 <paraStyle name="Confirmed"91 />
92 fontName="Helvetica"92
93 fontSize="9"93 <paraStyle name="Missing"
94 alignment="CENTER"94 fontName="Helvetica"
95 backColor="green"95 fontSize="9"
96 textColor="white"96 alignment="CENTER"
97 />97 backColor="red"
9898 textColor="white"
99 <paraStyle name="Missing"99 />
100 fontName="Helvetica"100
101 fontSize="9"101 <paraStyle name="Draft"
102 alignment="CENTER"102 fontName="Helvetica"
103 backColor="red"103 fontSize="9"
104 textColor="white"104 alignment="CENTER"
105 />105 backColor="orange"
106106 textColor="black"
107 <paraStyle name="Draft"107 />
108 fontName="Helvetica"108
109 fontSize="9"109 <paraStyle name="Error"
110 alignment="CENTER"110 fontName="Helvetica"
111 backColor="orange"111 fontSize="9"
112 textColor="black"112 alignment="CENTER"
113 />113 textColor="red"
114114 />
115 <paraStyle name="Error"115
116 fontName="Helvetica"116 <paraStyle name="Not in Company"
117 fontSize="9"117 fontName="Helvetica"
118 alignment="CENTER"118 fontSize="9"
119 textColor="red"119 alignment="CENTER"
120 />120 textColor="lightgrey"
121121 />
122 <paraStyle name="Not in Company"122
123 fontName="Helvetica"123
124 fontSize="9"124 </stylesheet>
125 alignment="CENTER"125
126 textColor="lightgrey"126 <story>
127 />127
128128 [[repeatIn(objects, 'o')]]
129 129 <blockTable style="std" repeatRows="2" colWidths="0,5cm,2.7cm,2.7cm,2.7cm,2.7cm,2.7cm" >
130 </stylesheet>130
131 131 <tr>
132 <story> 132 <td/>
133 133 <td>[[get_title(o)]]</td>
134 [[repeatIn(objects, 'o')]]134 </tr>
135 <blockTable style="std" repeatRows="2" colWidths="0,5cm,2.7cm,2.7cm,2.7cm,2.7cm,2.7cm" >135
136136 <tr>
137 <tr> 137 <td/>
138 <td/>138 <td>Employees</td>
139 <td>[[get_title(o)]]</td>139 <td>[[ get_timerange_title(o, 4) ]]</td>
140 </tr>140 <td>[[ get_timerange_title(o, 3) ]]</td>
141141 <td>[[ get_timerange_title(o, 2) ]]</td>
142 <tr>142 <td>[[ get_timerange_title(o, 1) ]]</td>
143 <td/>143 <td>[[ get_timerange_title(o, 0) ]]</td>
144 <td>Employees</td>144 </tr>
145 <td>[[ get_timerange_title(o, 4) ]]</td>145
146 <td>[[ get_timerange_title(o, 3) ]]</td>146 <tr>
147 <td>[[ get_timerange_title(o, 2) ]]</td>147 <td>[[repeatIn(get_user_list(o),'u')]]</td>
148 <td>[[ get_timerange_title(o, 1) ]]</td>148 <td><para style="std">[[ u.name ]]</para></td>
149 <td>[[ get_timerange_title(o, 0) ]]</td>149 <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 4)}) ]][[ get_timesheet_status(o, u, 4) ]]</para></td>
150 </tr>150 <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 3)}) ]][[ get_timesheet_status(o, u, 3) ]]</para></td>
151151 <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 2)}) ]][[ get_timesheet_status(o, u, 2) ]]</para></td>
152 <tr> 152 <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 1)}) ]][[ get_timesheet_status(o, u, 1) ]]</para></td>
153 <td>[[repeatIn(get_user_list(o),'u')]]</td>153 <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 0)}) ]][[ get_timesheet_status(o, u, 0) ]]</para></td>
154 <td><para style="std">[[ u.name ]]</para></td>154 </tr>
155 <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 4)}) ]][[ get_timesheet_status(o, u, 4) ]]</para></td>155
156 <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 3)}) ]][[ get_timesheet_status(o, u, 3) ]]</para></td>156 </blockTable>
157 <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 2)}) ]][[ get_timesheet_status(o, u, 2) ]]</para></td>157
158 <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 1)}) ]][[ get_timesheet_status(o, u, 1) ]]</para></td>158 </story>
159 <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 0)}) ]][[ get_timesheet_status(o, u, 0) ]]</para></td>159</document>
160 </tr>
161
162 </blockTable>
163
164 </story>
165 </document>
166160
=== modified file 'hr_timesheet_reminder/timesheet_report.xml'
--- hr_timesheet_reminder/timesheet_report.xml 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/timesheet_report.xml 2013-11-07 15:51:52 +0000
@@ -1,15 +1,15 @@
1<?xml version="1.0"?>1<?xml version="1.0"?>
2<openerp>2<openerp>
3 <data>3 <data>
4 <report4 <report
5 id="timesheet_status"5 id="timesheet_status"
6 string="Timesheet Status"6 string="Timesheet Status"
7 model="res.company"7 model="res.company"
8 name="timesheet.reminder.status"8 name="timesheet.reminder.status"
9 rml="hr_timesheet_reminder/report/timesheet_status.rml"9 rml="hr_timesheet_reminder/report/timesheet_status.rml"
10 auto="False"10 auto="False"
11 header="True"11 header="True"
12 menu="False"/>12 menu="False"/>
13 13
14 </data>14 </data>
15</openerp>15</openerp>
1616
=== modified file 'hr_timesheet_reminder/wizard/reminder_config.py'
--- hr_timesheet_reminder/wizard/reminder_config.py 2011-08-12 12:53:16 +0000
+++ hr_timesheet_reminder/wizard/reminder_config.py 2013-11-07 15:51:52 +0000
@@ -1,53 +1,48 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)4# Author: Arnaud Wüst (Camptocamp)
5# All Right Reserved5# Author: Guewen Baconnier (Camptocamp) (port to v7)
6#6# Copyright 2011-2012 Camptocamp SA
7# Author : Guewen Baconnier (Camptocamp)7#
8# Author : Arnaud Wüst (Camptocamp)8# This program is free software: you can redistribute it and/or modify
9#9# it under the terms of the GNU Affero General Public License as
10# WARNING: This program as such is intended to be used by professional10# published by the Free Software Foundation, either version 3 of the
11# programmers who take the whole responsability of assessing all potential11# License, or (at your option) any later version.
12# consequences resulting from its eventual inadequacies and bugs12#
13# End users who are looking for a ready-to-use solution with commercial13# This program is distributed in the hope that it will be useful,
14# garantees and support are strongly adviced to contract a Free Software14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# Service Company15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16#16# GNU Affero General Public License for more details.
17# This program is Free Software; you can redistribute it and/or17#
18# modify it under the terms of the GNU General Public License18# You should have received a copy of the GNU Affero General Public License
19# as published by the Free Software Foundation; either version 219# along with this program. If not, see <http://www.gnu.org/licenses/>.
20# of the License, or (at your option) any later version.
21#
22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25# GNU General Public License for more details.
26#
27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30#20#
31##############################################################################21##############################################################################
3222
33from osv import osv, fields23from openerp.osv import orm, fields
3424
3525
36class reminder_config(osv.osv_memory):26class reminder_config(orm.TransientModel):
37 _name = 'hr.timesheet.reminder.config'27 _name = 'hr.timesheet.reminder.config'
3828
39 _columns = {29 _columns = {
40 'reminder_active': fields.boolean('Reminder Active'),30 'reminder_active': fields.boolean('Reminder Active'),
41 'interval_type': fields.selection([('days','Day(s)'), ('weeks', 'Week(s)'), ('months', 'Month(s)')],31 'interval_type': fields.selection(
42 'Periodicity Unit',),32 [('days', 'Day(s)'),
43 'interval_number': fields.integer('Periodicity Quantity',),33 ('weeks', 'Week(s)'),
44 'nextcall': fields.datetime('Next Run',),34 ('months', 'Month(s)')],
45 'message': fields.text('Message', required=True),35 'Periodicity Unit'),
46 'subject': fields.char('Subject', size=200, required=True),36 'interval_number': fields.integer('Periodicity Quantity'),
47 'reply_to': fields.char('Reply To', size=100, required=True),37 'nextcall': fields.datetime('Next Run'),
38 'message': fields.html('Message', required=True),
39 'subject': fields.char('Subject', required=True),
40 'reply_to': fields.char('Reply To', required=True),
48 }41 }
4942
50 def _check_interval_number(self, cr, uid, ids, context=None):43 def _check_interval_number(self, cr, uid, ids, context=None):
44 """This constraint should always have 1 id, we are in a TransientModel"""
45 assert len(ids) == 1, "Only 1 ID expected"
51 obj = self.browse(cr, uid, ids[0], context=context)46 obj = self.browse(cr, uid, ids[0], context=context)
52 if obj.interval_number < 1:47 if obj.interval_number < 1:
53 return False48 return False
@@ -60,23 +55,22 @@
60 def default_get(self, cr, uid, fields, context=None):55 def default_get(self, cr, uid, fields, context=None):
61 res = super(reminder_config, self).default_get(cr, uid, fields, context=context)56 res = super(reminder_config, self).default_get(cr, uid, fields, context=context)
62 data = self.pool.get('hr.timesheet.reminder').\57 data = self.pool.get('hr.timesheet.reminder').\
63 get_config(cr, uid, context)58 get_config(cr, uid, context=context)
64 res.update(data)59 res.update(data)
65 return res60 return res
6661
67 def run(self, cr, uid, ids, context):62 def run(self, cr, uid, ids, context=None):
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: