Merge lp:~camptocamp/hr-timesheet/7.0-hr_timesheet_reminder-migr into lp:~hr-core-editors/hr-timesheet/7.0
- 7.0-hr_timesheet_reminder-migr
- Merge into 7.0
Status: | Merged |
---|---|
Merged at revision: | 40 |
Proposed branch: | lp:~camptocamp/hr-timesheet/7.0-hr_timesheet_reminder-migr |
Merge into: | lp:~hr-core-editors/hr-timesheet/7.0 |
Diff against target: |
1734 lines (+626/-669) 14 files modified
hr_timesheet_reminder/__init__.py (+15/-26) hr_timesheet_reminder/__openerp__.py (+41/-43) hr_timesheet_reminder/company.py (+50/-57) 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) |
To merge this branch: | bzr merge lp:~camptocamp/hr-timesheet/7.0-hr_timesheet_reminder-migr |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandre Fayolle - camptocamp | code review, no test | Approve | |
Review via email: mp+139655@code.launchpad.net |
Commit message
[MIGR] migration of hr_timesheet_
Description of the change
Migration to OpenERP version 7 of the addon hr_timesheet_
* change of license from GPLv2 to AGPLv3
* replace openerp.
* the dates were not displayed on the report
* migrate the views using the new layout
* formatting and styling
* fix bug: lp:1089390
* fix a small issue on the nextcall of the cron which was initialized not 'tomorrow' but approximately the next day of the server date
- 47. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[MRG] from lp:hr-timesheet/7.0
- 48. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[MIGR] set installable to True
- 49. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[FIX] license in timesheet_
status. rml, formatting
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : | # |
- 50. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[FIX] use a correct name for a local variable, use tomorrow.strftime instead of time.strftime, so get rid of 'import time'
- 51. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[FIX] use fields.date.today() instead of time.strftime() for a default date
- 52. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[IMP] use a html field instead of a text field for the message
- 53. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[FIX] skip earlier the employees who do not receive the timesheet alerts, avoid unecessary computations/
queries - 54. By Guewen Baconnier @ Camptocamp <email address hidden>
-
[FIX] remove unused import
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
You are right on all the points. I fixed them in my proposal.
Some comments:
* There is now a fields.html. I use it now for the message. As a bonus, we gain a rich editing of the reminder message (yippee, 'Please take time to complete and confirm your timesheet' in *bold*).
* Learned the word Mojibake today :-)
* there is a constant for '%Y-%m-%d' but there is even better for the default values of date and datetime:
fields.
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : | # |
Yay !
<off-topic>
Mojibake (pronounce modjibaké in fr_FR) is one of these useful words I enjoy sharing :-)
For other illustrated funny / rare french words, you may want to check out http://
</off-topic>
Preview Diff
1 | === modified file 'hr_timesheet_reminder/__init__.py' | |||
2 | --- hr_timesheet_reminder/__init__.py 2011-08-12 12:53:16 +0000 | |||
3 | +++ hr_timesheet_reminder/__init__.py 2012-12-13 17:06:20 +0000 | |||
4 | @@ -1,32 +1,21 @@ | |||
5 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
6 | 2 | ############################################################################## | 2 | ############################################################################## |
7 | 3 | # | 3 | # |
34 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
35 | 5 | # All Right Reserved | 5 | # Copyright 2011-2012 Camptocamp SA |
36 | 6 | # | 6 | # |
37 | 7 | # Author : Arnaud Wüst (Camptocamp) | 7 | # This program is free software: you can redistribute it and/or modify |
38 | 8 | # Author : Guewen Baconnier (Camptocamp) | 8 | # it under the terms of the GNU Affero General Public License as |
39 | 9 | # | 9 | # published by the Free Software Foundation, either version 3 of the |
40 | 10 | # WARNING: This program as such is intended to be used by professional | 10 | # License, or (at your option) any later version. |
41 | 11 | # programmers who take the whole responsability of assessing all potential | 11 | # |
42 | 12 | # consequences resulting from its eventual inadequacies and bugs | 12 | # This program is distributed in the hope that it will be useful, |
43 | 13 | # End users who are looking for a ready-to-use solution with commercial | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
44 | 14 | # garantees and support are strongly adviced to contract a Free Software | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
45 | 15 | # Service Company | 15 | # GNU Affero General Public License for more details. |
46 | 16 | # | 16 | # |
47 | 17 | # This program is Free Software; you can redistribute it and/or | 17 | # You should have received a copy of the GNU Affero General Public License |
48 | 18 | # modify it under the terms of the GNU General Public License | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
23 | 19 | # as published by the Free Software Foundation; either version 2 | ||
24 | 20 | # of the License, or (at your option) any later version. | ||
25 | 21 | # | ||
26 | 22 | # This program is distributed in the hope that it will be useful, | ||
27 | 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
28 | 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
29 | 25 | # GNU General Public License for more details. | ||
30 | 26 | # | ||
31 | 27 | # You should have received a copy of the GNU General Public License | ||
32 | 28 | # along with this program; if not, write to the Free Software | ||
33 | 29 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
49 | 30 | # | 19 | # |
50 | 31 | ############################################################################## | 20 | ############################################################################## |
51 | 32 | 21 | ||
52 | 33 | 22 | ||
53 | === modified file 'hr_timesheet_reminder/__openerp__.py' | |||
54 | --- hr_timesheet_reminder/__openerp__.py 2012-12-13 12:20:12 +0000 | |||
55 | +++ hr_timesheet_reminder/__openerp__.py 2012-12-13 17:06:20 +0000 | |||
56 | @@ -1,54 +1,52 @@ | |||
57 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
58 | 2 | ############################################################################## | 2 | ############################################################################## |
59 | 3 | # | 3 | # |
87 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
88 | 5 | # All Right Reserved | 5 | # Author: Nicolas Bessi (Camptocamp) |
89 | 6 | # | 6 | # Author: Guewen Baconnier (Camptocamp) (port to v7) |
90 | 7 | # Author : Arnaud Wüst (Camptocamp) | 7 | # Copyright 2011-2012 Camptocamp SA |
91 | 8 | # Author : Nicolas Bessi (Camptocamp) | 8 | # |
92 | 9 | # Author : Guewen Baconnier (Camptocamp) | 9 | # This program is free software: you can redistribute it and/or modify |
93 | 10 | # | 10 | # it under the terms of the GNU Affero General Public License as |
94 | 11 | # WARNING: This program as such is intended to be used by professional | 11 | # published by the Free Software Foundation, either version 3 of the |
95 | 12 | # programmers who take the whole responsability of assessing all potential | 12 | # License, or (at your option) any later version. |
96 | 13 | # consequences resulting from its eventual inadequacies and bugs | 13 | # |
97 | 14 | # End users who are looking for a ready-to-use solution with commercial | 14 | # This program is distributed in the hope that it will be useful, |
98 | 15 | # garantees and support are strongly adviced to contract a Free Software | 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
99 | 16 | # Service Company | 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
100 | 17 | # | 17 | # GNU Affero General Public License for more details. |
101 | 18 | # This program is Free Software; you can redistribute it and/or | 18 | # |
102 | 19 | # modify it under the terms of the GNU General Public License | 19 | # You should have received a copy of the GNU Affero General Public License |
103 | 20 | # as published by the Free Software Foundation; either version 2 | 20 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
77 | 21 | # of the License, or (at your option) any later version. | ||
78 | 22 | # | ||
79 | 23 | # This program is distributed in the hope that it will be useful, | ||
80 | 24 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
81 | 25 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
82 | 26 | # GNU General Public License for more details. | ||
83 | 27 | # | ||
84 | 28 | # You should have received a copy of the GNU General Public License | ||
85 | 29 | # along with this program; if not, write to the Free Software | ||
86 | 30 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
104 | 31 | # | 21 | # |
105 | 32 | ############################################################################## | 22 | ############################################################################## |
106 | 33 | 23 | ||
107 | 34 | |||
108 | 35 | { | 24 | { |
114 | 36 | "name" : "Timesheet Reminder", | 25 | "name": "Timesheet Reminder", |
115 | 37 | "version" : "2.0", | 26 | "version": "2.0", |
116 | 38 | "author" : "Camptocamp", | 27 | "author": "Camptocamp", |
117 | 39 | "category" : "", | 28 | "license": 'AGPL-3', |
118 | 40 | "website" : "http://www.camptocamp.com", | 29 | "category": "", |
119 | 30 | "website": "http://www.camptocamp.com", | ||
120 | 41 | "description": """ | 31 | "description": """ |
127 | 42 | Timesheet Reports Module: | 32 | Timesheet Reports Module |
128 | 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 | ======================== |
129 | 44 | * Per employee, you can choose to send the reminder or not. | 34 | |
130 | 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 |
131 | 46 | 36 | / Timesheet Reminder`. | |
132 | 47 | This module replaces the modules c2c_timesheet_reports in TinyERP 4 and OpenERP 5. | 37 | It allows to send automatic emails to those who did |
133 | 38 | not complete their timesheet in the last 5 weeks. | ||
134 | 39 | * Per employee, you can choose to send the reminder or not. | ||
135 | 40 | * Add a report in `Human Resources / Reporting / Timesheet | ||
136 | 41 | / Timesheet Status` which displays the state of the last | ||
137 | 42 | 5 timesheets for all users per company. | ||
138 | 43 | |||
139 | 44 | This module replaces the modules c2c_timesheet_reports | ||
140 | 45 | of TinyERP 4 and OpenERP 5. | ||
141 | 48 | """, | 46 | """, |
145 | 49 | "depends" : ["hr_timesheet_sheet"], | 47 | "depends": ["hr_timesheet_sheet"], |
146 | 50 | "init_xml" : [], | 48 | "init_xml": [], |
147 | 51 | "update_xml" : [ | 49 | "update_xml": [ |
148 | 52 | 'security/ir.model.access.csv', | 50 | 'security/ir.model.access.csv', |
149 | 53 | 'wizard/reminder_config_view.xml', | 51 | 'wizard/reminder_config_view.xml', |
150 | 54 | 'wizard/reminder_status_view.xml', | 52 | 'wizard/reminder_status_view.xml', |
151 | @@ -56,5 +54,5 @@ | |||
152 | 56 | 'timesheet_report.xml', | 54 | 'timesheet_report.xml', |
153 | 57 | ], | 55 | ], |
154 | 58 | "active": False, | 56 | "active": False, |
156 | 59 | 'installable': False | 57 | 'installable': True |
157 | 60 | } | 58 | } |
158 | 61 | 59 | ||
159 | === modified file 'hr_timesheet_reminder/company.py' | |||
160 | --- hr_timesheet_reminder/company.py 2011-09-01 13:44:19 +0000 | |||
161 | +++ hr_timesheet_reminder/company.py 2012-12-13 17:06:20 +0000 | |||
162 | @@ -1,57 +1,50 @@ | |||
163 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
164 | 2 | ############################################################################## | 2 | ############################################################################## |
165 | 3 | # | 3 | # |
192 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
193 | 5 | # All Right Reserved | 5 | # Author: Guewen Baconnier (Camptocamp) |
194 | 6 | # | 6 | # Copyright 2011-2012 Camptocamp SA |
195 | 7 | # Author : Arnaud Wüst (Camptocamp) | 7 | # |
196 | 8 | # Author : Guewen Baconnier (Camptocamp) | 8 | # This program is free software: you can redistribute it and/or modify |
197 | 9 | # | 9 | # it under the terms of the GNU Affero General Public License as |
198 | 10 | # WARNING: This program as such is intended to be used by professional | 10 | # published by the Free Software Foundation, either version 3 of the |
199 | 11 | # programmers who take the whole responsability of assessing all potential | 11 | # License, or (at your option) any later version. |
200 | 12 | # consequences resulting from its eventual inadequacies and bugs | 12 | # |
201 | 13 | # End users who are looking for a ready-to-use solution with commercial | 13 | # This program is distributed in the hope that it will be useful, |
202 | 14 | # garantees and support are strongly adviced to contract a Free Software | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
203 | 15 | # Service Company | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
204 | 16 | # | 16 | # GNU Affero General Public License for more details. |
205 | 17 | # This program is Free Software; you can redistribute it and/or | 17 | # |
206 | 18 | # modify it under the terms of the GNU General Public License | 18 | # You should have received a copy of the GNU Affero General Public License |
207 | 19 | # as published by the Free Software Foundation; either version 2 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
182 | 20 | # of the License, or (at your option) any later version. | ||
183 | 21 | # | ||
184 | 22 | # This program is distributed in the hope that it will be useful, | ||
185 | 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
186 | 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
187 | 25 | # GNU General Public License for more details. | ||
188 | 26 | # | ||
189 | 27 | # You should have received a copy of the GNU General Public License | ||
190 | 28 | # along with this program; if not, write to the Free Software | ||
191 | 29 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
208 | 30 | # | 20 | # |
209 | 31 | ############################################################################## | 21 | ############################################################################## |
210 | 32 | 22 | ||
219 | 33 | 23 | from datetime import datetime | |
220 | 34 | from datetime import date, datetime | 24 | from dateutil.relativedelta import relativedelta, MO, SU |
221 | 35 | from dateutil.relativedelta import * | 25 | from openerp.osv import osv, orm |
222 | 36 | from osv import fields, osv | 26 | from openerp.tools.translate import _ |
223 | 37 | from tools.translate import _ | 27 | |
224 | 38 | 28 | ||
225 | 39 | 29 | class res_company(orm.Model): | |
218 | 40 | class res_company(osv.osv): | ||
226 | 41 | _inherit = 'res.company' | 30 | _inherit = 'res.company' |
227 | 42 | 31 | ||
228 | 43 | def get_reminder_recipients(self, cr, uid, ids, context=None): | 32 | def get_reminder_recipients(self, cr, uid, ids, context=None): |
229 | 44 | """Return the list of users that must receive the email""" | 33 | """Return the list of users that must receive the email""" |
231 | 45 | res = {}.fromkeys(ids, []) | 34 | res = dict((company_id, []) for company_id in ids) |
232 | 35 | |||
233 | 46 | employee_obj = self.pool.get('hr.employee') | 36 | employee_obj = self.pool.get('hr.employee') |
234 | 47 | 37 | ||
242 | 48 | companies = self.browse(cr, uid, ids, context=context) | 38 | for company in self.browse(cr, uid, ids, context=context): |
243 | 49 | 39 | employee_ids = employee_obj.search( | |
244 | 50 | for company in companies: | 40 | cr, uid, |
245 | 51 | employee_ids = employee_obj.search(cr, uid, | 41 | [('company_id', '=', company.id), |
246 | 52 | [('company_id', '=', company.id), | 42 | ('receive_timesheet_alerts', '=', True)], |
247 | 53 | ('active', '=', True)], | 43 | context=context) |
248 | 54 | context=context) | 44 | |
249 | 45 | if not employee_ids: | ||
250 | 46 | continue | ||
251 | 47 | |||
252 | 55 | employees = employee_obj.browse(cr, uid, employee_ids, context=context) | 48 | employees = employee_obj.browse(cr, uid, employee_ids, context=context) |
253 | 56 | 49 | ||
254 | 57 | #periods | 50 | #periods |
255 | @@ -62,24 +55,26 @@ | |||
256 | 62 | # for each employee | 55 | # for each employee |
257 | 63 | for employee in employees: | 56 | for employee in employees: |
258 | 64 | # is timesheet for a period not confirmed ? | 57 | # is timesheet for a period not confirmed ? |
261 | 65 | for p_index in range(len(periods)): | 58 | for period in periods: |
260 | 66 | period = periods[p_index] | ||
262 | 67 | status = employee_obj.compute_timesheet_status(cr, uid, employee.id, period, context) | 59 | status = employee_obj.compute_timesheet_status(cr, uid, employee.id, period, context) |
263 | 68 | 60 | ||
264 | 69 | # if there is a missing sheet or a draft sheet | 61 | # if there is a missing sheet or a draft sheet |
265 | 70 | # and the user can receive alerts | 62 | # and the user can receive alerts |
266 | 71 | # then we must alert the user | 63 | # then we must alert the user |
268 | 72 | if status in ['Missing', 'Draft'] and employee.receive_timesheet_alerts: | 64 | if status in ['Missing', 'Draft']: |
269 | 73 | res[company.id].append(employee) | 65 | res[company.id].append(employee) |
271 | 74 | break # no need to go further for this user, he is now added in the list, go to the next one | 66 | # no need to go further for this user, |
272 | 67 | # he is now added in the list, go to the next one | ||
273 | 68 | break | ||
274 | 75 | return res | 69 | return res |
275 | 76 | 70 | ||
276 | 77 | def compute_timesheet_periods(self, cr, uid, company, date, periods_number=5, context=None): | 71 | def compute_timesheet_periods(self, cr, uid, company, date, periods_number=5, context=None): |
277 | 78 | """ return the timeranges to display. This is the 5 last timesheets""" | 72 | """ return the timeranges to display. This is the 5 last timesheets""" |
278 | 79 | periods = [] | 73 | periods = [] |
280 | 80 | last_start_date, last_end_date = self.get_last_period_dates(cr, uid, company, date, context=context) | 74 | last_start_date, last_end_date = self.get_last_period_dates( |
281 | 75 | cr, uid, company, date, context=context) | ||
282 | 81 | for cpt in range(periods_number): | 76 | for cpt in range(periods_number): |
284 | 82 | #find the delta between last_XXX_date to XXX_date | 77 | # find the delta between last_XXX_date to XXX_date |
285 | 83 | if company.timesheet_range == 'month': | 78 | if company.timesheet_range == 'month': |
286 | 84 | delta = relativedelta(months=-cpt) | 79 | delta = relativedelta(months=-cpt) |
287 | 85 | elif company.timesheet_range == 'week': | 80 | elif company.timesheet_range == 'week': |
288 | @@ -87,7 +82,9 @@ | |||
289 | 87 | elif company.timesheet_range == 'year': | 82 | elif company.timesheet_range == 'year': |
290 | 88 | delta = relativedelta(years=-cpt) | 83 | delta = relativedelta(years=-cpt) |
291 | 89 | else: | 84 | else: |
293 | 90 | raise osv.except_osv(_('Error'), _('Unknow timesheet range: %s') % (company.timesheet_range,)) | 85 | raise osv.except_osv( |
294 | 86 | _('Error'), | ||
295 | 87 | _('Unknow timesheet range: %s') % company.timesheet_range) | ||
296 | 91 | 88 | ||
297 | 92 | start_date = last_start_date + delta | 89 | start_date = last_start_date + delta |
298 | 93 | end_date = last_end_date + delta | 90 | end_date = last_end_date + delta |
299 | @@ -97,26 +94,22 @@ | |||
300 | 97 | 94 | ||
301 | 98 | def get_last_period_dates(self, cr, uid, company, date, context=None): | 95 | def get_last_period_dates(self, cr, uid, company, date, context=None): |
302 | 99 | """ return the start date and end date of the last period to display """ | 96 | """ return the start date and end date of the last period to display """ |
304 | 100 | 97 | ||
305 | 101 | # return the first day and last day of the month | 98 | # return the first day and last day of the month |
306 | 102 | if company.timesheet_range == 'month': | 99 | if company.timesheet_range == 'month': |
307 | 103 | start_date = date | 100 | start_date = date |
309 | 104 | end_date = start_date + relativedelta(months = +1) | 101 | end_date = start_date + relativedelta(months=+1) |
310 | 105 | 102 | ||
311 | 106 | #return the first and last days of the week | 103 | #return the first and last days of the week |
312 | 107 | elif company.timesheet_range == 'week': | 104 | elif company.timesheet_range == 'week': |
313 | 108 | # get monday of current week | 105 | # get monday of current week |
314 | 109 | start_date = date + relativedelta(weekday=MO(-1)) | 106 | start_date = date + relativedelta(weekday=MO(-1)) |
316 | 110 | # get sunday of current week | 107 | # get sunday of current week |
317 | 111 | end_date = date + relativedelta(weekday=SU(+1)) | 108 | end_date = date + relativedelta(weekday=SU(+1)) |
318 | 112 | 109 | ||
319 | 113 | # return the first and last days of the year | 110 | # return the first and last days of the year |
320 | 114 | else: | 111 | else: |
322 | 115 | start_date = datetime(date.year, 1, 1) | 112 | start_date = datetime(date.year, 1, 1) |
323 | 116 | end_date = datetime(date.year, 12, 31) | 113 | end_date = datetime(date.year, 12, 31) |
324 | 117 | 114 | ||
325 | 118 | |||
326 | 119 | return start_date, end_date | 115 | return start_date, end_date |
327 | 120 | |||
328 | 121 | |||
329 | 122 | res_company() | ||
330 | 123 | 116 | ||
331 | === modified file 'hr_timesheet_reminder/hr_employee.py' | |||
332 | --- hr_timesheet_reminder/hr_employee.py 2011-09-01 13:44:19 +0000 | |||
333 | +++ hr_timesheet_reminder/hr_employee.py 2012-12-13 17:06:20 +0000 | |||
334 | @@ -1,47 +1,38 @@ | |||
335 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
336 | 2 | ############################################################################## | 2 | ############################################################################## |
337 | 3 | # | 3 | # |
364 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
365 | 5 | # All Right Reserved | 5 | # Author: Guewen Baconnier (Camptocamp) (port to v7) |
366 | 6 | # | 6 | # Copyright 2011-2012 Camptocamp SA |
367 | 7 | # Author : Arnaud Wüst (Camptocamp) | 7 | # |
368 | 8 | # Author : Guewen Baconnier (Camptocamp) | 8 | # This program is free software: you can redistribute it and/or modify |
369 | 9 | # | 9 | # it under the terms of the GNU Affero General Public License as |
370 | 10 | # WARNING: This program as such is intended to be used by professional | 10 | # published by the Free Software Foundation, either version 3 of the |
371 | 11 | # programmers who take the whole responsability of assessing all potential | 11 | # License, or (at your option) any later version. |
372 | 12 | # consequences resulting from its eventual inadequacies and bugs | 12 | # |
373 | 13 | # End users who are looking for a ready-to-use solution with commercial | 13 | # This program is distributed in the hope that it will be useful, |
374 | 14 | # garantees and support are strongly adviced to contract a Free Software | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
375 | 15 | # Service Company | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
376 | 16 | # | 16 | # GNU Affero General Public License for more details. |
377 | 17 | # This program is Free Software; you can redistribute it and/or | 17 | # |
378 | 18 | # modify it under the terms of the GNU General Public License | 18 | # You should have received a copy of the GNU Affero General Public License |
379 | 19 | # as published by the Free Software Foundation; either version 2 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
354 | 20 | # of the License, or (at your option) any later version. | ||
355 | 21 | # | ||
356 | 22 | # This program is distributed in the hope that it will be useful, | ||
357 | 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
358 | 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
359 | 25 | # GNU General Public License for more details. | ||
360 | 26 | # | ||
361 | 27 | # You should have received a copy of the GNU General Public License | ||
362 | 28 | # along with this program; if not, write to the Free Software | ||
363 | 29 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
380 | 30 | # | 20 | # |
381 | 31 | ############################################################################## | 21 | ############################################################################## |
388 | 32 | from datetime import * | 22 | |
389 | 33 | 23 | from openerp.osv import fields, orm | |
390 | 34 | from osv import fields, osv | 24 | from openerp.tools import DEFAULT_SERVER_DATE_FORMAT |
391 | 35 | 25 | ||
392 | 36 | 26 | ||
393 | 37 | class hr_employee(osv.osv): | 27 | class hr_employee(orm.Model): |
394 | 38 | _inherit = 'hr.employee' | 28 | _inherit = 'hr.employee' |
395 | 29 | |||
396 | 39 | _columns = { | 30 | _columns = { |
398 | 40 | 'receive_timesheet_alerts': fields.boolean('Receive Timesheet Alerts'), | 31 | 'receive_timesheet_alerts': fields.boolean('Receive Timesheet Alerts'), |
399 | 41 | } | 32 | } |
400 | 42 | 33 | ||
401 | 43 | _defaults = { | 34 | _defaults = { |
403 | 44 | 'receive_timesheet_alerts': lambda *a: True, | 35 | 'receive_timesheet_alerts': True, |
404 | 45 | } | 36 | } |
405 | 46 | 37 | ||
406 | 47 | def compute_timesheet_status(self, cr, uid, ids, period, context): | 38 | def compute_timesheet_status(self, cr, uid, ids, period, context): |
407 | @@ -50,35 +41,33 @@ | |||
408 | 50 | status = 'Error' | 41 | status = 'Error' |
409 | 51 | 42 | ||
410 | 52 | if isinstance(ids, list): | 43 | if isinstance(ids, list): |
411 | 44 | assert len(ids) == 1, "Only 1 ID expected" | ||
412 | 53 | ids = ids[0] | 45 | ids = ids[0] |
413 | 54 | 46 | ||
414 | 55 | employee = self.browse(cr, uid, ids, context=context) | 47 | employee = self.browse(cr, uid, ids, context=context) |
415 | 56 | 48 | ||
429 | 57 | time_from = period[0] | 49 | time_from, time_to = period |
430 | 58 | time_to = period[1] | 50 | |
431 | 59 | 51 | # does the timesheet exists in db and what is its status? | |
432 | 60 | # does the timesheet exsists in db and what is its status? | 52 | str_date_from = time_from.strftime(DEFAULT_SERVER_DATE_FORMAT) |
433 | 61 | timeformat = "%Y-%m-%d" | 53 | str_date_to = time_to.strftime(DEFAULT_SERVER_DATE_FORMAT) |
434 | 62 | str_date_from = time_from.strftime(timeformat) | 54 | |
435 | 63 | str_date_to = time_to.strftime(timeformat) | 55 | cr.execute( |
436 | 64 | 56 | """SELECT state, date_from, date_to | |
437 | 65 | cr.execute("""SELECT state, date_from, date_to | 57 | FROM hr_timesheet_sheet_sheet |
438 | 66 | FROM hr_timesheet_sheet_sheet | 58 | WHERE employee_id = %s |
439 | 67 | WHERE employee_id = %s | 59 | AND date_from >= %s |
440 | 68 | AND date_from >= %s | 60 | AND date_to <= %s""", |
428 | 69 | AND date_to <= %s""", | ||
441 | 70 | (employee.id, str_date_from, str_date_to)) | 61 | (employee.id, str_date_from, str_date_to)) |
442 | 71 | sheets = cr.dictfetchall() | 62 | sheets = cr.dictfetchall() |
443 | 72 | 63 | ||
445 | 73 | #the timesheet does not exists in db | 64 | # the timesheet does not exists in db |
446 | 74 | if not sheets: | 65 | if not sheets: |
447 | 75 | status = 'Missing' | 66 | status = 'Missing' |
448 | 76 | 67 | ||
450 | 77 | if len(sheets) > 0: | 68 | else: |
451 | 78 | status = 'Confirmed' | 69 | status = 'Confirmed' |
454 | 79 | for s in sheets: | 70 | for sheet in sheets: |
455 | 80 | if s['state'] == 'draft': | 71 | if sheet['state'] == 'draft': |
456 | 81 | status = 'Draft' | 72 | status = 'Draft' |
457 | 82 | return status | 73 | return status |
458 | 83 | |||
459 | 84 | hr_employee() | ||
460 | 85 | 74 | ||
461 | === modified file 'hr_timesheet_reminder/hr_employee_view.xml' | |||
462 | --- hr_timesheet_reminder/hr_employee_view.xml 2011-08-12 12:53:16 +0000 | |||
463 | +++ hr_timesheet_reminder/hr_employee_view.xml 2012-12-13 17:06:20 +0000 | |||
464 | @@ -5,7 +5,6 @@ | |||
465 | 5 | <field name="name">hr.timesheet.employee.rmnd_form</field> | 5 | <field name="name">hr.timesheet.employee.rmnd_form</field> |
466 | 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"/> |
467 | 7 | <field name="model">hr.employee</field> | 7 | <field name="model">hr.employee</field> |
468 | 8 | <field name="type">form</field> | ||
469 | 9 | <field name="arch" type="xml"> | 8 | <field name="arch" type="xml"> |
470 | 10 | <field name="journal_id" widget="selection" position="after"> | 9 | <field name="journal_id" widget="selection" position="after"> |
471 | 11 | <field name="receive_timesheet_alerts"/> | 10 | <field name="receive_timesheet_alerts"/> |
472 | 12 | 11 | ||
473 | === modified file 'hr_timesheet_reminder/reminder.py' | |||
474 | --- hr_timesheet_reminder/reminder.py 2011-09-01 13:44:19 +0000 | |||
475 | +++ hr_timesheet_reminder/reminder.py 2012-12-13 17:06:20 +0000 | |||
476 | @@ -1,157 +1,170 @@ | |||
477 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
478 | 2 | ############################################################################## | 2 | ############################################################################## |
479 | 3 | # | 3 | # |
506 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
507 | 5 | # All Right Reserved | 5 | # Author: Guewen Baconnier (Camptocamp) (port to v7) |
508 | 6 | # | 6 | # Copyright 2011-2012 Camptocamp SA |
509 | 7 | # Author : Arnaud Wüst (Camptocamp) | 7 | # |
510 | 8 | # Author : Guewen Baconnier (Camptocamp) | 8 | # This program is free software: you can redistribute it and/or modify |
511 | 9 | # | 9 | # it under the terms of the GNU Affero General Public License as |
512 | 10 | # WARNING: This program as such is intended to be used by professional | 10 | # published by the Free Software Foundation, either version 3 of the |
513 | 11 | # programmers who take the whole responsability of assessing all potential | 11 | # License, or (at your option) any later version. |
514 | 12 | # consequences resulting from its eventual inadequacies and bugs | 12 | # |
515 | 13 | # End users who are looking for a ready-to-use solution with commercial | 13 | # This program is distributed in the hope that it will be useful, |
516 | 14 | # garantees and support are strongly adviced to contract a Free Software | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
517 | 15 | # Service Company | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
518 | 16 | # | 16 | # GNU Affero General Public License for more details. |
519 | 17 | # This program is Free Software; you can redistribute it and/or | 17 | # |
520 | 18 | # modify it under the terms of the GNU General Public License | 18 | # You should have received a copy of the GNU Affero General Public License |
521 | 19 | # as published by the Free Software Foundation; either version 2 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
496 | 20 | # of the License, or (at your option) any later version. | ||
497 | 21 | # | ||
498 | 22 | # This program is distributed in the hope that it will be useful, | ||
499 | 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
500 | 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
501 | 25 | # GNU General Public License for more details. | ||
502 | 26 | # | ||
503 | 27 | # You should have received a copy of the GNU General Public License | ||
504 | 28 | # along with this program; if not, write to the Free Software | ||
505 | 29 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
522 | 30 | # | 20 | # |
523 | 31 | ############################################################################## | 21 | ############################################################################## |
524 | 32 | 22 | ||
525 | 33 | import tools | ||
526 | 34 | import time | ||
527 | 35 | |||
528 | 36 | from datetime import datetime, timedelta | 23 | from datetime import datetime, timedelta |
533 | 37 | from osv import fields, osv | 24 | from openerp.osv import fields, orm |
534 | 38 | from tools.translate import _ | 25 | from openerp.tools.translate import _ |
535 | 39 | 26 | from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT | |
536 | 40 | class reminder(osv.osv): | 27 | |
537 | 28 | |||
538 | 29 | class reminder(orm.Model): | ||
539 | 41 | _name = "hr.timesheet.reminder" | 30 | _name = "hr.timesheet.reminder" |
540 | 42 | _description = "Handle the scheduling of timesheet reminders" | 31 | _description = "Handle the scheduling of timesheet reminders" |
541 | 43 | 32 | ||
542 | 44 | _columns = { | 33 | _columns = { |
546 | 45 | 'reply_to': fields.char('Reply To', size=100), | 34 | 'reply_to': fields.char('Reply To'), |
547 | 46 | 'message': fields.text('Message'), | 35 | 'message': fields.html('Message'), |
548 | 47 | 'subject': fields.char('Subject', size=200), | 36 | 'subject': fields.char('Subject'), |
549 | 48 | } | 37 | } |
550 | 49 | 38 | ||
552 | 50 | #default cron (the one created if missing) | 39 | # default cron (the one created if missing) |
553 | 51 | cron = {'active': False, | 40 | cron = {'active': False, |
554 | 52 | 'priority': 1, | 41 | 'priority': 1, |
555 | 53 | 'interval_number': 1, | 42 | 'interval_number': 1, |
556 | 54 | 'interval_type': 'weeks', | 43 | 'interval_type': 'weeks', |
560 | 55 | 'nextcall': time.strftime("%Y-%m-%d %H:%M:%S", | 44 | 'nextcall': False, # to set on the creation of the cron |
558 | 56 | (datetime.today() | ||
559 | 57 | + timedelta(days=1)).timetuple()), # tomorrow same time | ||
561 | 58 | 'numbercall': -1, | 45 | 'numbercall': -1, |
563 | 59 | 'doall': True, | 46 | 'doall': False, |
564 | 60 | 'model': 'hr.timesheet.reminder', | 47 | 'model': 'hr.timesheet.reminder', |
565 | 61 | 'function': 'run', | 48 | 'function': 'run', |
566 | 62 | 'args': '()', | 49 | 'args': '()', |
567 | 63 | } | 50 | } |
568 | 64 | 51 | ||
570 | 65 | #default message (the one created if missing) | 52 | # default message (the one created if missing) |
571 | 66 | message = {'reply_to': 'spam@camptocamp.com'} | 53 | message = {'reply_to': 'spam@camptocamp.com'} |
572 | 67 | 54 | ||
573 | 68 | def run(self, cr, uid, context=None): | 55 | def run(self, cr, uid, context=None): |
574 | 69 | """ find the reminder recipients and send them an email """ | 56 | """ find the reminder recipients and send them an email """ |
575 | 70 | context = context or {} | ||
576 | 71 | |||
577 | 72 | company_obj = self.pool.get('res.company') | 57 | company_obj = self.pool.get('res.company') |
579 | 73 | #get all companies | 58 | # get all companies |
580 | 74 | company_ids = company_obj.search(cr, uid, [], context=context) | 59 | company_ids = company_obj.search(cr, uid, [], context=context) |
581 | 75 | 60 | ||
583 | 76 | #for each company, get all recipients | 61 | # for each company, get all recipients |
584 | 77 | recipients = [] | 62 | recipients = [] |
587 | 78 | company_recipients = company_obj.get_reminder_recipients(cr, uid, company_ids, context=context) | 63 | company_recipients = company_obj.get_reminder_recipients( |
588 | 79 | for company_id, rec in company_recipients.iteritems(): | 64 | cr, uid, company_ids, context=context) |
589 | 65 | for rec in company_recipients.itervalues(): | ||
590 | 80 | recipients += rec | 66 | recipients += rec |
591 | 81 | 67 | ||
593 | 82 | #get the message to send | 68 | # get the message to send |
594 | 83 | message_id = self.get_message_id(cr, uid, context) | 69 | message_id = self.get_message_id(cr, uid, context) |
595 | 84 | message_data = self.browse(cr, uid, message_id, context=context) | 70 | message_data = self.browse(cr, uid, message_id, context=context) |
596 | 85 | 71 | ||
599 | 86 | #send them email if they have an email defined | 72 | # send them email if they have an email defined |
598 | 87 | emails = [] | ||
600 | 88 | for employee in recipients: | 73 | for employee in recipients: |
608 | 89 | if employee.work_email: | 74 | if not employee.work_email: |
609 | 90 | emails.append(employee.work_email) | 75 | continue |
610 | 91 | 76 | vals = { | |
611 | 92 | if emails: | 77 | 'state': 'outgoing', |
612 | 93 | tools.email_send(message_data.reply_to, [], message_data.subject, message_data.message, email_bcc=emails) | 78 | 'subject': message_data.subject, |
613 | 94 | 79 | 'body_html': message_data.message, | |
614 | 95 | def get_cron_id(self, cr, uid, context): | 80 | 'email_to': employee.work_email, |
615 | 81 | 'email_from': message_data.reply_to, | ||
616 | 82 | } | ||
617 | 83 | self.pool.get('mail.mail').create(cr, uid, vals, context=context) | ||
618 | 84 | |||
619 | 85 | return True | ||
620 | 86 | |||
621 | 87 | def get_cron_id(self, cr, uid, context=None): | ||
622 | 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 """ |
623 | 89 | if context is None: | ||
624 | 90 | context = {} | ||
625 | 97 | cron_obj = self.pool.get('ir.cron') | 91 | cron_obj = self.pool.get('ir.cron') |
626 | 98 | # find the cron that send messages | 92 | # find the cron that send messages |
632 | 99 | cron_id = cron_obj.search(cr, uid, [('function', 'ilike', self.cron['function']), | 93 | ctx = dict(context, active_test=False) |
633 | 100 | ('model', 'ilike', self.cron['model'])], | 94 | cron_ids = cron_obj.search( |
634 | 101 | context={'active_test': False}) | 95 | cr, uid, |
635 | 102 | if cron_id: | 96 | [('function', 'ilike', self.cron['function']), |
636 | 103 | cron_id = cron_id[0] | 97 | ('model', 'ilike', self.cron['model'])], |
637 | 98 | context=ctx) | ||
638 | 99 | |||
639 | 100 | cron_id = None | ||
640 | 101 | if cron_ids: | ||
641 | 102 | cron_id = cron_ids[0] | ||
642 | 104 | 103 | ||
643 | 105 | # the cron does not exists | 104 | # the cron does not exists |
647 | 106 | if not cron_id: | 105 | if cron_id is None: |
648 | 107 | self.cron['name'] = _('timesheet status reminder') | 106 | vals = dict(self.cron, |
649 | 108 | cron_id = cron_obj.create(cr, uid, self.cron, context) | 107 | name=_('timesheet status reminder'), |
650 | 108 | nextcall=self._cron_nextcall()) | ||
651 | 109 | |||
652 | 110 | cron_id = cron_obj.create(cr, uid, vals, context=context) | ||
653 | 109 | 111 | ||
654 | 110 | return cron_id | 112 | return cron_id |
655 | 111 | 113 | ||
658 | 112 | def get_message_id(self, cr, uid, context): | 114 | @staticmethod |
659 | 113 | """ return the message'id. create one if the message does not exists """ | 115 | def _cron_nextcall(): |
660 | 116 | tomorrow = datetime.today() + timedelta(days=1) | ||
661 | 117 | return tomorrow.strftime(DEFAULT_SERVER_DATETIME_FORMAT) | ||
662 | 118 | |||
663 | 119 | def get_message_id(self, cr, uid, context=None): | ||
664 | 120 | """ return the message's id. create one if the message does not exists """ | ||
665 | 114 | #there is only one line in db, let's get it | 121 | #there is only one line in db, let's get it |
667 | 115 | message_id = self.search(cr, uid, [], limit=1, context=context) | 122 | message_ids = self.search(cr, uid, [], limit=1, context=context) |
668 | 116 | 123 | ||
671 | 117 | if message_id: | 124 | message_id = None |
672 | 118 | message_id = message_id[0] | 125 | if message_ids: |
673 | 126 | message_id = message_ids[0] | ||
674 | 119 | 127 | ||
675 | 120 | #the message does not exists | 128 | #the message does not exists |
680 | 121 | if not message_id: | 129 | if message_id is None: |
681 | 122 | #translate | 130 | vals = dict(self.message, |
682 | 123 | self.message['subject'] = _('Timesheet Reminder') | 131 | subject=_('Timesheet Reminder'), |
683 | 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=_( |
684 | 133 | 'At least one of your last timesheets is still ' | ||
685 | 134 | 'in draft or is missing. Please take time to ' | ||
686 | 135 | 'complete and confirm it.')) | ||
687 | 125 | 136 | ||
689 | 126 | message_id = self.create(cr, uid, self.message, context) | 137 | message_id = self.create(cr, uid, vals, context) |
690 | 127 | 138 | ||
691 | 128 | return message_id | 139 | return message_id |
692 | 129 | 140 | ||
694 | 130 | def get_config(self, cr, uid, context): | 141 | def get_config(self, cr, uid, context=None): |
695 | 131 | """return the reminder config from the db """ | 142 | """return the reminder config from the db """ |
696 | 132 | 143 | ||
697 | 133 | cron_id = self.get_cron_id(cr, uid, context) | 144 | cron_id = self.get_cron_id(cr, uid, context) |
698 | 134 | 145 | ||
700 | 135 | cron_data = self.pool.get('ir.cron').browse(cr, uid, cron_id) | 146 | cron_data = self.pool.get('ir.cron').browse( |
701 | 147 | cr, uid, cron_id, context=context) | ||
702 | 136 | 148 | ||
706 | 137 | #there is only one line in db, let's get it | 149 | # there is only one line in db, let's get it |
707 | 138 | message_id = self.get_message_id(cr, uid, context) | 150 | message_id = self.get_message_id(cr, uid, context=context) |
708 | 139 | message_data = self.browse(cr, uid, message_id) | 151 | message_data = self.browse(cr, uid, message_id, context=context) |
709 | 140 | return {'reminder_active': cron_data.active, | 152 | return {'reminder_active': cron_data.active, |
710 | 141 | 'interval_type': cron_data.interval_type, | 153 | 'interval_type': cron_data.interval_type, |
711 | 142 | 'interval_number': cron_data.interval_number, | 154 | 'interval_number': cron_data.interval_number, |
712 | 143 | 'reply_to': message_data.reply_to, | 155 | 'reply_to': message_data.reply_to, |
713 | 144 | 'message': message_data.message, | 156 | 'message': message_data.message, |
714 | 145 | 'subject': message_data.subject, | 157 | 'subject': message_data.subject, |
716 | 146 | 'nextcall': cron_data.nextcall, | 158 | 'nextcall': self._cron_nextcall(), |
717 | 147 | } | 159 | } |
718 | 148 | 160 | ||
720 | 149 | def save_config(self, cr, uid, ids, datas, context): | 161 | def save_config(self, cr, uid, ids, datas, context=None): |
721 | 150 | """save the reminder config """ | 162 | """save the reminder config """ |
722 | 151 | 163 | ||
723 | 152 | #modify the cron | 164 | #modify the cron |
726 | 153 | cron_id = self.get_cron_id(cr, uid, context) | 165 | cron_id = self.get_cron_id(cr, uid, context=context) |
727 | 154 | self.pool.get('ir.cron').write(cr, uid, [cron_id], | 166 | self.pool.get('ir.cron').write( |
728 | 167 | cr, uid, [cron_id], | ||
729 | 155 | {'active': datas['reminder_active'], | 168 | {'active': datas['reminder_active'], |
730 | 156 | 'interval_number': datas['interval_number'], | 169 | 'interval_number': datas['interval_number'], |
731 | 157 | 'interval_type': datas['interval_type'], | 170 | 'interval_type': datas['interval_type'], |
732 | @@ -159,11 +172,10 @@ | |||
733 | 159 | context=context) | 172 | context=context) |
734 | 160 | #modify the message | 173 | #modify the message |
735 | 161 | message_id = ids or self.get_message_id(cr, uid, context) | 174 | message_id = ids or self.get_message_id(cr, uid, context) |
737 | 162 | self.write(cr, uid, [message_id], | 175 | self.write( |
738 | 176 | cr, uid, [message_id], | ||
739 | 163 | {'reply_to': datas['reply_to'], | 177 | {'reply_to': datas['reply_to'], |
740 | 164 | 'message': datas['message'], | 178 | 'message': datas['message'], |
741 | 165 | 'subject': datas['subject'], | 179 | 'subject': datas['subject'], |
742 | 166 | }, context=context) | 180 | }, context=context) |
743 | 167 | return True | 181 | return True |
744 | 168 | |||
745 | 169 | reminder() | ||
746 | 170 | 182 | ||
747 | === modified file 'hr_timesheet_reminder/report/__init__.py' | |||
748 | --- hr_timesheet_reminder/report/__init__.py 2011-08-12 12:53:16 +0000 | |||
749 | +++ hr_timesheet_reminder/report/__init__.py 2012-12-13 17:06:20 +0000 | |||
750 | @@ -1,31 +1,22 @@ | |||
751 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
752 | 2 | ############################################################################## | 2 | ############################################################################## |
753 | 3 | # | 3 | # |
779 | 4 | # Copyright (c) Camptocamp SA | 4 | # Author: Arnaud Wüst (Camptocamp) |
780 | 5 | # Author: Arnaud WÃŒst | 5 | # Copyright 2011-2012 Camptocamp SA |
781 | 6 | # | 6 | # |
782 | 7 | # | 7 | # This program is free software: you can redistribute it and/or modify |
783 | 8 | # | 8 | # it under the terms of the GNU Affero General Public License as |
784 | 9 | # WARNING: This program as such is intended to be used by professional | 9 | # published by the Free Software Foundation, either version 3 of the |
785 | 10 | # programmers who take the whole responsability of assessing all potential | 10 | # License, or (at your option) any later version. |
786 | 11 | # consequences resulting from its eventual inadequacies and bugs | 11 | # |
787 | 12 | # End users who are looking for a ready-to-use solution with commercial | 12 | # This program is distributed in the hope that it will be useful, |
788 | 13 | # garantees and support are strongly adviced to contract a Free Software | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
789 | 14 | # Service Company | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
790 | 15 | # | 15 | # GNU Affero General Public License for more details. |
791 | 16 | # This program is Free Software; you can redistribute it and/or | 16 | # |
792 | 17 | # modify it under the terms of the GNU General Public License | 17 | # You should have received a copy of the GNU Affero General Public License |
793 | 18 | # as published by the Free Software Foundation; either version 2 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
769 | 19 | # of the License, or (at your option) any later version. | ||
770 | 20 | # | ||
771 | 21 | # This program is distributed in the hope that it will be useful, | ||
772 | 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
773 | 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
774 | 24 | # GNU General Public License for more details. | ||
775 | 25 | # | ||
776 | 26 | # You should have received a copy of the GNU General Public License | ||
777 | 27 | # along with this program; if not, write to the Free Software | ||
778 | 28 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
794 | 29 | # | 19 | # |
795 | 30 | ############################################################################## | 20 | ############################################################################## |
796 | 21 | |||
797 | 31 | import timesheet_status | 22 | import timesheet_status |
798 | 32 | 23 | ||
799 | === modified file 'hr_timesheet_reminder/report/timesheet_status.py' | |||
800 | --- hr_timesheet_reminder/report/timesheet_status.py 2011-09-01 13:44:19 +0000 | |||
801 | +++ hr_timesheet_reminder/report/timesheet_status.py 2012-12-13 17:06:20 +0000 | |||
802 | @@ -1,47 +1,40 @@ | |||
803 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
804 | 2 | ############################################################################## | 2 | ############################################################################## |
805 | 3 | # | 3 | # |
832 | 4 | # Copyright (c) Camptocamp SA | 4 | # Author: Arnaud Wüst (Camptocamp) |
833 | 5 | # Author: Arnaud WÃŒst | 5 | # Author: Guewen Baconnier (Camptocamp) (port to v7) |
834 | 6 | # Author: Guewen Baconnier | 6 | # Copyright 2011-2012 Camptocamp SA |
835 | 7 | # | 7 | # |
836 | 8 | # | 8 | # This program is free software: you can redistribute it and/or modify |
837 | 9 | # | 9 | # it under the terms of the GNU Affero General Public License as |
838 | 10 | # WARNING: This program as such is intended to be used by professional | 10 | # published by the Free Software Foundation, either version 3 of the |
839 | 11 | # programmers who take the whole responsability of assessing all potential | 11 | # License, or (at your option) any later version. |
840 | 12 | # consequences resulting from its eventual inadequacies and bugs | 12 | # |
841 | 13 | # End users who are looking for a ready-to-use solution with commercial | 13 | # This program is distributed in the hope that it will be useful, |
842 | 14 | # garantees and support are strongly adviced to contract a Free Software | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
843 | 15 | # Service Company | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
844 | 16 | # | 16 | # GNU Affero General Public License for more details. |
845 | 17 | # This program is Free Software; you can redistribute it and/or | 17 | # |
846 | 18 | # modify it under the terms of the GNU General Public License | 18 | # You should have received a copy of the GNU Affero General Public License |
847 | 19 | # as published by the Free Software Foundation; either version 2 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
822 | 20 | # of the License, or (at your option) any later version. | ||
823 | 21 | # | ||
824 | 22 | # This program is distributed in the hope that it will be useful, | ||
825 | 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
826 | 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
827 | 25 | # GNU General Public License for more details. | ||
828 | 26 | # | ||
829 | 27 | # You should have received a copy of the GNU General Public License | ||
830 | 28 | # along with this program; if not, write to the Free Software | ||
831 | 29 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
848 | 30 | # | 20 | # |
849 | 31 | ############################################################################## | 21 | ############################################################################## |
850 | 32 | 22 | ||
851 | 33 | import time | 23 | import time |
852 | 34 | 24 | ||
853 | 35 | from datetime import datetime | 25 | from datetime import datetime |
855 | 36 | from report import report_sxw | 26 | from openerp.report import report_sxw |
856 | 27 | from openerp.tools import DEFAULT_SERVER_DATE_FORMAT | ||
857 | 28 | from openerp.tools.translate import _ | ||
858 | 37 | 29 | ||
859 | 38 | 30 | ||
860 | 39 | class timesheet_status(report_sxw.rml_parse): | 31 | class timesheet_status(report_sxw.rml_parse): |
861 | 40 | _name = 'report.timesheet.reminder.status' | 32 | _name = 'report.timesheet.reminder.status' |
862 | 41 | 33 | ||
865 | 42 | def __init__(self, cr, uid, name, context): | 34 | def __init__(self, cr, uid, name, context=None): |
866 | 43 | super(timesheet_status, self).__init__(cr, uid, name, context) | 35 | super(timesheet_status, self).__init__(cr, uid, name, context=context) |
867 | 44 | self.data = {} | 36 | self.data = {} |
868 | 37 | self.end_date = None | ||
869 | 45 | self.localcontext.update({ | 38 | self.localcontext.update({ |
870 | 46 | 'compute': self.compute, | 39 | 'compute': self.compute, |
871 | 47 | 'time': time, | 40 | 'time': time, |
872 | @@ -60,64 +53,65 @@ | |||
873 | 60 | """compute all datas and do all the calculations before to start the rml rendering | 53 | """compute all datas and do all the calculations before to start the rml rendering |
874 | 61 | - objects are companies | 54 | - objects are companies |
875 | 62 | """ | 55 | """ |
877 | 63 | #init the data array | 56 | # init the data array |
878 | 64 | self.data = {} | 57 | self.data = {} |
879 | 65 | for o in objects: | 58 | for o in objects: |
880 | 66 | self.data[o.id] = {} | 59 | self.data[o.id] = {} |
882 | 67 | #get the list of employees ids to treat | 60 | # get the list of employees ids to treat |
883 | 68 | for o in objects: | 61 | for o in objects: |
884 | 69 | self.data[o.id]['employees'] = self._compute_employees_list(o) | 62 | self.data[o.id]['employees'] = self._compute_employees_list(o) |
885 | 70 | 63 | ||
887 | 71 | #get the time range for each company | 64 | # get the time range for each company |
888 | 65 | end_date = datetime.strptime(self.end_date, DEFAULT_SERVER_DATE_FORMAT) | ||
889 | 72 | for o in objects: | 66 | for o in objects: |
892 | 73 | self.data[o.id]['time_ranges'] = \ | 67 | self.data[o.id]['time_ranges'] = self._compute_periods(o, end_date) |
891 | 74 | self._compute_periods(o, datetime.strptime(self.end_date, "%Y-%m-%d")) | ||
893 | 75 | 68 | ||
895 | 76 | #get the status of each timesheet for each employee | 69 | # get the status of each timesheet for each employee |
896 | 77 | for o in objects: | 70 | for o in objects: |
897 | 78 | self.data[o.id]['sheet_status'] = self._compute_all_status(o) | 71 | self.data[o.id]['sheet_status'] = self._compute_all_status(o) |
898 | 79 | 72 | ||
899 | 80 | def _compute_employees_list(self, company): | 73 | def _compute_employees_list(self, company): |
901 | 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 |
902 | 75 | to the companies (param company) """ | ||
903 | 82 | employee_obj = self.pool.get('hr.employee') | 76 | employee_obj = self.pool.get('hr.employee') |
904 | 83 | employee_ids = employee_obj.search(self.cr, self.uid, | 77 | employee_ids = employee_obj.search(self.cr, self.uid, |
905 | 84 | [('company_id', '=', company.id), | 78 | [('company_id', '=', company.id), |
906 | 85 | ('active', '=', True)], | 79 | ('active', '=', True)], |
907 | 86 | context=self.localcontext) | 80 | context=self.localcontext) |
909 | 87 | return employee_obj.browse(self.cr, self.uid, employee_ids, context=self.localcontext) | 81 | return employee_obj.browse( |
910 | 82 | self.cr, self.uid, employee_ids, context=self.localcontext) | ||
911 | 88 | 83 | ||
912 | 89 | def _get_last_period_dates(self, company, date): | 84 | def _get_last_period_dates(self, company, date): |
913 | 90 | """ return the start date of the last period to display """ | 85 | """ return the start date of the last period to display """ |
916 | 91 | return self.pool.get('res.company').\ | 86 | return self.pool.get('res.company').get_last_period_dates( |
917 | 92 | get_last_period_dates(self.cr, self.uid, company, date, context=self.localcontext) | 87 | self.cr, |
918 | 88 | self.uid, | ||
919 | 89 | company, | ||
920 | 90 | date, | ||
921 | 91 | context=self.localcontext) | ||
922 | 93 | 92 | ||
923 | 94 | def _compute_periods(self, company, date): | 93 | def _compute_periods(self, company, date): |
924 | 95 | """ return the timeranges to display. This is the 5 last timesheets """ | 94 | """ return the timeranges to display. This is the 5 last timesheets """ |
927 | 96 | return self.pool.get('res.company').\ | 95 | return self.pool.get('res.company').compute_timesheet_periods( |
928 | 97 | compute_timesheet_periods(self.cr, self.uid, company, date, context=self.localcontext) | 96 | self.cr, |
929 | 97 | self.uid, | ||
930 | 98 | company, | ||
931 | 99 | date, | ||
932 | 100 | context=self.localcontext) | ||
933 | 98 | 101 | ||
934 | 99 | def get_title(self, obj): | 102 | def get_title(self, obj): |
935 | 100 | """ return the title of the main table """ | 103 | """ return the title of the main table """ |
946 | 101 | last_id = len(self.data[obj.id]['time_ranges']) - 1 | 104 | timerange = self.data[obj.id]['time_ranges'] |
947 | 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) |
948 | 103 | "%Y-%m-%d %H:%M:%S") | 106 | end_date = self.formatLang(timerange[0][1], date=True) |
949 | 104 | start_date = time.strftime("%d.%m.%Y", start_date) | 107 | |
950 | 105 | 108 | return obj.name + ", " + start_date + _(" to ") + end_date | |
941 | 106 | end_date = time.strptime(str(self.data[obj.id]['time_ranges'][0][1]), | ||
942 | 107 | "%Y-%m-%d %H:%M:%S") | ||
943 | 108 | end_date = time.strftime("%d.%m.%Y", end_date) | ||
944 | 109 | |||
945 | 110 | return obj.name + ", " + start_date + " to " + end_date | ||
951 | 111 | 109 | ||
952 | 112 | def get_timerange_title(self, obj, cpt): | 110 | def get_timerange_title(self, obj, cpt): |
953 | 113 | """ return a header text for a periods column """ | 111 | """ return a header text for a periods column """ |
961 | 114 | start_date = self.data[obj.id]['time_ranges'][cpt][0] | 112 | timerange = self.data[obj.id]['time_ranges'][cpt] |
962 | 115 | start_date = time.strptime(str(start_date), "%Y-%m-%d %H:%M:%S") | 113 | start_date = self.formatLang(timerange[0], date=True) |
963 | 116 | start_date = time.strftime("%d.%m.%Y", start_date) | 114 | end_date = self.formatLang(timerange[1], date=True) |
957 | 117 | |||
958 | 118 | end_date = self.data[obj.id]['time_ranges'][cpt][1] | ||
959 | 119 | end_date = time.strptime(str(end_date), "%Y-%m-%d %H:%M:%S") | ||
960 | 120 | end_date = time.strftime("%d.%m.%Y", end_date) | ||
964 | 121 | 115 | ||
965 | 122 | return start_date + "\n " + end_date | 116 | return start_date + "\n " + end_date |
966 | 123 | 117 | ||
967 | @@ -131,21 +125,25 @@ | |||
968 | 131 | 125 | ||
969 | 132 | def _compute_timesheet_status(self, employee_id, period): | 126 | def _compute_timesheet_status(self, employee_id, period): |
970 | 133 | """ return the timesheet status for a user and a period """ | 127 | """ return the timesheet status for a user and a period """ |
973 | 134 | return self.pool.get('hr.employee').\ | 128 | return self.pool.get('hr.employee').compute_timesheet_status( |
974 | 135 | compute_timesheet_status(self.cr, self.uid, employee_id, period, context=self.localcontext) | 129 | self.cr, |
975 | 130 | self.uid, | ||
976 | 131 | employee_id, | ||
977 | 132 | period, | ||
978 | 133 | context=self.localcontext) | ||
979 | 136 | 134 | ||
981 | 137 | def _compute_all_status(self, o): | 135 | def _compute_all_status(self, obj): |
982 | 138 | """ compute all status for all employees for all periods """ | 136 | """ compute all status for all employees for all periods """ |
983 | 139 | result = {} | 137 | result = {} |
984 | 140 | 138 | ||
985 | 141 | #for each periods | 139 | #for each periods |
987 | 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']): |
988 | 143 | result[p_index] = {} | 141 | result[p_index] = {} |
989 | 144 | period = self.data[o.id]['time_ranges'][p_index] | ||
990 | 145 | #for each employees | 142 | #for each employees |
992 | 146 | for employee in self.data[o.id]['employees']: | 143 | for employee in self.data[obj.id]['employees']: |
993 | 147 | #compute the status | 144 | #compute the status |
995 | 148 | result[p_index][employee.id] = self._compute_timesheet_status(employee.id, period) | 145 | result[p_index][employee.id] = self._compute_timesheet_status( |
996 | 146 | employee.id, period) | ||
997 | 149 | 147 | ||
998 | 150 | return result | 148 | return result |
999 | 151 | 149 | ||
1000 | 152 | 150 | ||
1001 | === modified file 'hr_timesheet_reminder/report/timesheet_status.rml' | |||
1002 | --- hr_timesheet_reminder/report/timesheet_status.rml 2011-08-12 12:53:16 +0000 | |||
1003 | +++ hr_timesheet_reminder/report/timesheet_status.rml 2012-12-13 17:06:20 +0000 | |||
1004 | @@ -1,165 +1,159 @@ | |||
1005 | 1 | <?xml version="1.0"?> | 1 | <?xml version="1.0"?> |
1006 | 2 | <document filename="timesheet_status.pdf"> | 2 | <document filename="timesheet_status.pdf"> |
1170 | 3 | ############################################################################## | 3 | <!-- |
1171 | 4 | # | 4 | ############################################################################## |
1172 | 5 | # Copyright (c) Camptocamp SA | 5 | # |
1173 | 6 | # Author: Arnaud Wüst | 6 | # Author: Arnaud Wüst (Camptocamp) |
1174 | 7 | # | 7 | # Copyright 2011-2012 Camptocamp SA |
1175 | 8 | # | 8 | # |
1176 | 9 | # WARNING: This program as such is intended to be used by professional | 9 | # This program is free software: you can redistribute it and/or modify |
1177 | 10 | # programmers who take the whole responsability of assessing all potential | 10 | # it under the terms of the GNU Affero General Public License as |
1178 | 11 | # consequences resulting from its eventual inadequacies and bugs | 11 | # published by the Free Software Foundation, either version 3 of the |
1179 | 12 | # End users who are looking for a ready-to-use solution with commercial | 12 | # License, or (at your option) any later version. |
1180 | 13 | # garantees and support are strongly adviced to contract a Free Software | 13 | # |
1181 | 14 | # Service Company | 14 | # This program is distributed in the hope that it will be useful, |
1182 | 15 | # | 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
1183 | 16 | # This program is Free Software; you can redistribute it and/or | 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1184 | 17 | # modify it under the terms of the GNU General Public License | 17 | # GNU Affero General Public License for more details. |
1185 | 18 | # as published by the Free Software Foundation; either version 2 | 18 | # |
1186 | 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 |
1187 | 20 | # | 20 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1188 | 21 | # This program is distributed in the hope that it will be useful, | 21 | # |
1189 | 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 22 | ############################################################################## |
1190 | 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 23 | --> |
1191 | 24 | # GNU General Public License for more details. | 24 | |
1192 | 25 | # | 25 | |
1193 | 26 | # You should have received a copy of the GNU General Public License | 26 | <!-- Process all datas --> |
1194 | 27 | # along with this program; if not, write to the Free Software | 27 | <template pageSize="(21cm,29.7cm)" title="Timesheet Status" author="Camptocamp" allowSplitting="20"> |
1195 | 28 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 28 | |
1196 | 29 | # | 29 | |
1197 | 30 | ############################################################################## | 30 | <!-- PAGE: template of all pages (= all pages except first and last if defined)--> |
1198 | 31 | 31 | <pageTemplate id="all"> | |
1199 | 32 | <!-- Process all datas --> | 32 | <pageGraphics> |
1200 | 33 | <template pageSize="(21cm,29.7cm)" title="Timesheet Status" author="Camptocamp" allowSplitting="20"> | 33 | <setFont name="Helvetica-Bold" size="9"/> |
1201 | 34 | 34 | ||
1202 | 35 | 35 | <!--Header--> | |
1203 | 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> |
1204 | 37 | <pageTemplate id="all"> | 37 | <drawString x="17.0cm" y="28.1cm">Timesheet Status</drawString> |
1205 | 38 | <pageGraphics> | 38 | |
1206 | 39 | <setFont name="Helvetica-Bold" size="9"/> | 39 | <lineMode width="0.7"/> |
1207 | 40 | 40 | <lines>1.2cm 28.0cm 19.8cm 28.0cm</lines> | |
1208 | 41 | <!--Header--> | 41 | |
1209 | 42 | <drawString x="1.2cm" y="28.1cm">[[ company.name ]]</drawString> | 42 | <!-- Footer --> |
1210 | 43 | <drawString x="17.0cm" y="28.1cm">Timesheet Status</drawString> | 43 | <setFont name="Helvetica" size="9"/> |
1211 | 44 | 44 | <drawString x="1.2cm" y="1.3cm"> [[ time.strftime("%m-%d-%y %H:%M", time.localtime()) ]]</drawString> | |
1212 | 45 | <lineMode width="0.7"/> | 45 | <drawString x="18.8cm" y="1.3cm">Page <pageNumber/></drawString> |
1213 | 46 | <lines>1.2cm 28.0cm 19.8cm 28.0cm</lines> | 46 | |
1214 | 47 | 47 | </pageGraphics> | |
1215 | 48 | <!-- Footer --> | 48 | <frame id="all" x1="1.2cm" y1="1.7cm" width="18.6cm" height="25.8cm"/> |
1216 | 49 | <setFont name="Helvetica" size="9"/> | 49 | </pageTemplate> |
1217 | 50 | <drawString x="1.2cm" y="1.3cm"> [[ time.strftime("%m-%d-%y %H:%M", time.localtime()) ]]</drawString> | 50 | |
1218 | 51 | <drawString x="18.8cm" y="1.3cm">Page <pageNumber/></drawString> | 51 | </template> |
1219 | 52 | 52 | <stylesheet> | |
1220 | 53 | </pageGraphics> | 53 | |
1221 | 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) --> |
1222 | 55 | </pageTemplate> | 55 | <blockTableStyle id="std"> |
1223 | 56 | 56 | <blockAlignment value="LEFT"/> | |
1224 | 57 | </template> | 57 | <blockValign value="TOP"/> |
1225 | 58 | <stylesheet> | 58 | <blockBottomPadding length="4"/> |
1226 | 59 | 59 | <blockFont name="Helvetica" size="9" start="0,0" stop="-1,-1"/> | |
1227 | 60 | <!--TABLE: standard table type "columns" (which means there is a title, a header of fields names and then lines of values) --> | 60 | |
1228 | 61 | <blockTableStyle id="std"> | 61 | <!-- first line: table name, fake as it was only one cell. grey bg --> |
1229 | 62 | <blockAlignment value="LEFT"/> | 62 | <lineStyle kind="BOX" colorName="black" start="0,0" stop="-1,0"/> |
1230 | 63 | <blockValign value="TOP"/> | 63 | <blockBackground colorName="#cccccc" start="0,0" stop="-1,0"/> |
1231 | 64 | <blockBottomPadding length="4"/> | 64 | <blockFont name="Helvetica" size="9" start="0,0" stop="-1,0"/> |
1232 | 65 | <blockFont name="Helvetica" size="9" start="0,0" stop="-1,-1"/> | 65 | <!-- second line: header of columns --> |
1233 | 66 | 66 | <lineStyle kind="GRID" colorName="black" start="0,1" stop="-1,1"/> | |
1234 | 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"/> |
1235 | 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 --> |
1236 | 69 | <blockBackground colorName="#cccccc" start="0,0" stop="-1,0"/> | 69 | <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,2" stop="-1,-1"/> |
1237 | 70 | <blockFont name="Helvetica" size="9" start="0,0" stop="-1,0"/> | 70 | <lineStyle kind="LINEAFTER" colorName="black" start="0,2" stop="-1,-1"/> |
1238 | 71 | <!-- second line: header of columns --> | 71 | <!-- last line: line below --> |
1239 | 72 | <lineStyle kind="GRID" colorName="black" start="0,1" stop="-1,1"/> | 72 | <lineStyle kind="OUTLINE" colorName="black" start="0,0" stop="-1,-1"/> |
1240 | 73 | <blockFont name="Helvetica-Oblique" start="0,1" stop="-1,1"/> | 73 | <!-- all columns centered except the first two (1 system columns + employee) --> |
1241 | 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" /> |
1242 | 75 | <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,2" stop="-1,-1"/> | 75 | |
1243 | 76 | <lineStyle kind="LINEAFTER" colorName="black" start="0,2" stop="-1,-1"/> | 76 | </blockTableStyle> |
1244 | 77 | <!-- last line: line below --> | 77 | |
1245 | 78 | <lineStyle kind="OUTLINE" colorName="black" start="0,0" stop="-1,-1"/> | 78 | <!-- default para in tables --> |
1246 | 79 | <!-- all columns centered except the first two (1 system columns + employee) --> | 79 | <paraStyle name="std" |
1247 | 80 | <blockAlignment value="CENTER" start="2,1" stop="-1,-1" /> | 80 | fontName="Helvetica" |
1248 | 81 | 81 | fontSize="9" | |
1249 | 82 | </blockTableStyle> | 82 | alignment="LEFT" |
1250 | 83 | 83 | /> | |
1251 | 84 | <!-- default para in tables --> | 84 | |
1252 | 85 | <paraStyle name="std" | 85 | <paraStyle name="Confirmed" |
1253 | 86 | fontName="Helvetica" | 86 | fontName="Helvetica" |
1254 | 87 | fontSize="9" | 87 | fontSize="9" |
1255 | 88 | alignment="LEFT" | 88 | alignment="CENTER" |
1256 | 89 | /> | 89 | backColor="green" |
1257 | 90 | 90 | textColor="white" | |
1258 | 91 | <paraStyle name="Confirmed" | 91 | /> |
1259 | 92 | fontName="Helvetica" | 92 | |
1260 | 93 | fontSize="9" | 93 | <paraStyle name="Missing" |
1261 | 94 | alignment="CENTER" | 94 | fontName="Helvetica" |
1262 | 95 | backColor="green" | 95 | fontSize="9" |
1263 | 96 | textColor="white" | 96 | alignment="CENTER" |
1264 | 97 | /> | 97 | backColor="red" |
1265 | 98 | 98 | textColor="white" | |
1266 | 99 | <paraStyle name="Missing" | 99 | /> |
1267 | 100 | fontName="Helvetica" | 100 | |
1268 | 101 | fontSize="9" | 101 | <paraStyle name="Draft" |
1269 | 102 | alignment="CENTER" | 102 | fontName="Helvetica" |
1270 | 103 | backColor="red" | 103 | fontSize="9" |
1271 | 104 | textColor="white" | 104 | alignment="CENTER" |
1272 | 105 | /> | 105 | backColor="orange" |
1273 | 106 | 106 | textColor="black" | |
1274 | 107 | <paraStyle name="Draft" | 107 | /> |
1275 | 108 | fontName="Helvetica" | 108 | |
1276 | 109 | fontSize="9" | 109 | <paraStyle name="Error" |
1277 | 110 | alignment="CENTER" | 110 | fontName="Helvetica" |
1278 | 111 | backColor="orange" | 111 | fontSize="9" |
1279 | 112 | textColor="black" | 112 | alignment="CENTER" |
1280 | 113 | /> | 113 | textColor="red" |
1281 | 114 | 114 | /> | |
1282 | 115 | <paraStyle name="Error" | 115 | |
1283 | 116 | fontName="Helvetica" | 116 | <paraStyle name="Not in Company" |
1284 | 117 | fontSize="9" | 117 | fontName="Helvetica" |
1285 | 118 | alignment="CENTER" | 118 | fontSize="9" |
1286 | 119 | textColor="red" | 119 | alignment="CENTER" |
1287 | 120 | /> | 120 | textColor="lightgrey" |
1288 | 121 | 121 | /> | |
1289 | 122 | <paraStyle name="Not in Company" | 122 | |
1290 | 123 | fontName="Helvetica" | 123 | |
1291 | 124 | fontSize="9" | 124 | </stylesheet> |
1292 | 125 | alignment="CENTER" | 125 | |
1293 | 126 | textColor="lightgrey" | 126 | <story> |
1294 | 127 | /> | 127 | |
1295 | 128 | 128 | [[repeatIn(objects, 'o')]] | |
1296 | 129 | 129 | <blockTable style="std" repeatRows="2" colWidths="0,5cm,2.7cm,2.7cm,2.7cm,2.7cm,2.7cm" > | |
1297 | 130 | </stylesheet> | 130 | |
1298 | 131 | 131 | <tr> | |
1299 | 132 | <story> | 132 | <td/> |
1300 | 133 | 133 | <td>[[get_title(o)]]</td> | |
1301 | 134 | [[repeatIn(objects, 'o')]] | 134 | </tr> |
1302 | 135 | <blockTable style="std" repeatRows="2" colWidths="0,5cm,2.7cm,2.7cm,2.7cm,2.7cm,2.7cm" > | 135 | |
1303 | 136 | 136 | <tr> | |
1304 | 137 | <tr> | 137 | <td/> |
1305 | 138 | <td/> | 138 | <td>Employees</td> |
1306 | 139 | <td>[[get_title(o)]]</td> | 139 | <td>[[ get_timerange_title(o, 4) ]]</td> |
1307 | 140 | </tr> | 140 | <td>[[ get_timerange_title(o, 3) ]]</td> |
1308 | 141 | 141 | <td>[[ get_timerange_title(o, 2) ]]</td> | |
1309 | 142 | <tr> | 142 | <td>[[ get_timerange_title(o, 1) ]]</td> |
1310 | 143 | <td/> | 143 | <td>[[ get_timerange_title(o, 0) ]]</td> |
1311 | 144 | <td>Employees</td> | 144 | </tr> |
1312 | 145 | <td>[[ get_timerange_title(o, 4) ]]</td> | 145 | |
1313 | 146 | <td>[[ get_timerange_title(o, 3) ]]</td> | 146 | <tr> |
1314 | 147 | <td>[[ get_timerange_title(o, 2) ]]</td> | 147 | <td>[[repeatIn(get_user_list(o),'u')]]</td> |
1315 | 148 | <td>[[ get_timerange_title(o, 1) ]]</td> | 148 | <td><para style="std">[[ u.name ]]</para></td> |
1316 | 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> |
1317 | 150 | </tr> | 150 | <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 3)}) ]][[ get_timesheet_status(o, u, 3) ]]</para></td> |
1318 | 151 | 151 | <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 2)}) ]][[ get_timesheet_status(o, u, 2) ]]</para></td> | |
1319 | 152 | <tr> | 152 | <td><para>[[ setTag('para', 'para', {'style': get_timesheet_status(o, u, 1)}) ]][[ get_timesheet_status(o, u, 1) ]]</para></td> |
1320 | 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> |
1321 | 154 | <td><para style="std">[[ u.name ]]</para></td> | 154 | </tr> |
1322 | 155 | <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 4)}) ]][[ get_timesheet_status(o, u, 4) ]]</para></td> | 155 | |
1323 | 156 | <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 3)}) ]][[ get_timesheet_status(o, u, 3) ]]</para></td> | 156 | </blockTable> |
1324 | 157 | <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 2)}) ]][[ get_timesheet_status(o, u, 2) ]]</para></td> | 157 | |
1325 | 158 | <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 1)}) ]][[ get_timesheet_status(o, u, 1) ]]</para></td> | 158 | </story> |
1326 | 159 | <td><para>[[ setTag('para','para',{'style':get_timesheet_status(o, u, 0)}) ]][[ get_timesheet_status(o, u, 0) ]]</para></td> | 159 | </document> |
1164 | 160 | </tr> | ||
1165 | 161 | |||
1166 | 162 | </blockTable> | ||
1167 | 163 | |||
1168 | 164 | </story> | ||
1169 | 165 | </document> | ||
1327 | 166 | 160 | ||
1328 | === modified file 'hr_timesheet_reminder/timesheet_report.xml' | |||
1329 | --- hr_timesheet_reminder/timesheet_report.xml 2011-08-12 12:53:16 +0000 | |||
1330 | +++ hr_timesheet_reminder/timesheet_report.xml 2012-12-13 17:06:20 +0000 | |||
1331 | @@ -1,15 +1,15 @@ | |||
1332 | 1 | <?xml version="1.0"?> | 1 | <?xml version="1.0"?> |
1333 | 2 | <openerp> | 2 | <openerp> |
1342 | 3 | <data> | 3 | <data> |
1343 | 4 | <report | 4 | <report |
1344 | 5 | id="timesheet_status" | 5 | id="timesheet_status" |
1345 | 6 | string="Timesheet Status" | 6 | string="Timesheet Status" |
1346 | 7 | model="res.company" | 7 | model="res.company" |
1347 | 8 | name="timesheet.reminder.status" | 8 | name="timesheet.reminder.status" |
1348 | 9 | rml="hr_timesheet_reminder/report/timesheet_status.rml" | 9 | rml="hr_timesheet_reminder/report/timesheet_status.rml" |
1349 | 10 | auto="False" | 10 | auto="False" |
1350 | 11 | header="True" | 11 | header="True" |
1351 | 12 | menu="False"/> | 12 | menu="False"/> |
1354 | 13 | 13 | ||
1355 | 14 | </data> | 14 | </data> |
1356 | 15 | </openerp> | 15 | </openerp> |
1357 | 16 | 16 | ||
1358 | === modified file 'hr_timesheet_reminder/wizard/reminder_config.py' | |||
1359 | --- hr_timesheet_reminder/wizard/reminder_config.py 2011-08-12 12:53:16 +0000 | |||
1360 | +++ hr_timesheet_reminder/wizard/reminder_config.py 2012-12-13 17:06:20 +0000 | |||
1361 | @@ -1,53 +1,48 @@ | |||
1362 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
1363 | 2 | ############################################################################## | 2 | ############################################################################## |
1364 | 3 | # | 3 | # |
1391 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
1392 | 5 | # All Right Reserved | 5 | # Author: Guewen Baconnier (Camptocamp) (port to v7) |
1393 | 6 | # | 6 | # Copyright 2011-2012 Camptocamp SA |
1394 | 7 | # Author : Guewen Baconnier (Camptocamp) | 7 | # |
1395 | 8 | # Author : Arnaud Wüst (Camptocamp) | 8 | # This program is free software: you can redistribute it and/or modify |
1396 | 9 | # | 9 | # it under the terms of the GNU Affero General Public License as |
1397 | 10 | # WARNING: This program as such is intended to be used by professional | 10 | # published by the Free Software Foundation, either version 3 of the |
1398 | 11 | # programmers who take the whole responsability of assessing all potential | 11 | # License, or (at your option) any later version. |
1399 | 12 | # consequences resulting from its eventual inadequacies and bugs | 12 | # |
1400 | 13 | # End users who are looking for a ready-to-use solution with commercial | 13 | # This program is distributed in the hope that it will be useful, |
1401 | 14 | # garantees and support are strongly adviced to contract a Free Software | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
1402 | 15 | # Service Company | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1403 | 16 | # | 16 | # GNU Affero General Public License for more details. |
1404 | 17 | # This program is Free Software; you can redistribute it and/or | 17 | # |
1405 | 18 | # modify it under the terms of the GNU General Public License | 18 | # You should have received a copy of the GNU Affero General Public License |
1406 | 19 | # as published by the Free Software Foundation; either version 2 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1381 | 20 | # of the License, or (at your option) any later version. | ||
1382 | 21 | # | ||
1383 | 22 | # This program is distributed in the hope that it will be useful, | ||
1384 | 23 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1385 | 24 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1386 | 25 | # GNU General Public License for more details. | ||
1387 | 26 | # | ||
1388 | 27 | # You should have received a copy of the GNU General Public License | ||
1389 | 28 | # along with this program; if not, write to the Free Software | ||
1390 | 29 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
1407 | 30 | # | 20 | # |
1408 | 31 | ############################################################################## | 21 | ############################################################################## |
1409 | 32 | 22 | ||
1414 | 33 | from osv import osv, fields | 23 | from openerp.osv import orm, fields |
1415 | 34 | 24 | ||
1416 | 35 | 25 | ||
1417 | 36 | class reminder_config(osv.osv_memory): | 26 | class reminder_config(orm.TransientModel): |
1418 | 37 | _name = 'hr.timesheet.reminder.config' | 27 | _name = 'hr.timesheet.reminder.config' |
1419 | 38 | 28 | ||
1420 | 39 | _columns = { | 29 | _columns = { |
1421 | 40 | 'reminder_active': fields.boolean('Reminder Active'), | 30 | 'reminder_active': fields.boolean('Reminder Active'), |
1429 | 41 | 'interval_type': fields.selection([('days','Day(s)'), ('weeks', 'Week(s)'), ('months', 'Month(s)')], | 31 | 'interval_type': fields.selection( |
1430 | 42 | 'Periodicity Unit',), | 32 | [('days', 'Day(s)'), |
1431 | 43 | 'interval_number': fields.integer('Periodicity Quantity',), | 33 | ('weeks', 'Week(s)'), |
1432 | 44 | 'nextcall': fields.datetime('Next Run',), | 34 | ('months', 'Month(s)')], |
1433 | 45 | 'message': fields.text('Message', required=True), | 35 | 'Periodicity Unit'), |
1434 | 46 | 'subject': fields.char('Subject', size=200, required=True), | 36 | 'interval_number': fields.integer('Periodicity Quantity'), |
1435 | 47 | 'reply_to': fields.char('Reply To', size=100, required=True), | 37 | 'nextcall': fields.datetime('Next Run'), |
1436 | 38 | 'message': fields.html('Message', required=True), | ||
1437 | 39 | 'subject': fields.char('Subject', required=True), | ||
1438 | 40 | 'reply_to': fields.char('Reply To', required=True), | ||
1439 | 48 | } | 41 | } |
1440 | 49 | 42 | ||
1441 | 50 | def _check_interval_number(self, cr, uid, ids, context=None): | 43 | def _check_interval_number(self, cr, uid, ids, context=None): |
1442 | 44 | """This constraint should always have 1 id, we are in a TransientModel""" | ||
1443 | 45 | assert len(ids) == 1, "Only 1 ID expected" | ||
1444 | 51 | obj = self.browse(cr, uid, ids[0], context=context) | 46 | obj = self.browse(cr, uid, ids[0], context=context) |
1445 | 52 | if obj.interval_number < 1: | 47 | if obj.interval_number < 1: |
1446 | 53 | return False | 48 | return False |
1447 | @@ -60,23 +55,22 @@ | |||
1448 | 60 | def default_get(self, cr, uid, fields, context=None): | 55 | def default_get(self, cr, uid, fields, context=None): |
1449 | 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) |
1450 | 62 | data = self.pool.get('hr.timesheet.reminder').\ | 57 | data = self.pool.get('hr.timesheet.reminder').\ |
1452 | 63 | get_config(cr, uid, context) | 58 | get_config(cr, uid, context=context) |
1453 | 64 | res.update(data) | 59 | res.update(data) |
1454 | 65 | return res | 60 | return res |
1455 | 66 | 61 | ||
1457 | 67 | def run(self, cr, uid, ids, context): | 62 | def run(self, cr, uid, ids, context=None): |
1458 | 68 | """ execute the timesheets check and send emails """ | 63 | """ execute the timesheets check and send emails """ |
1459 | 69 | reminder_obj = self.pool.get('hr.timesheet.reminder') | 64 | reminder_obj = self.pool.get('hr.timesheet.reminder') |
1460 | 70 | reminder_obj.run(cr, uid, context=context) | 65 | reminder_obj.run(cr, uid, context=context) |
1461 | 71 | return {'type': 'ir.actions.act_window_close'} | 66 | return {'type': 'ir.actions.act_window_close'} |
1462 | 72 | 67 | ||
1464 | 73 | def save(self, cr, uid, ids, context): | 68 | def save(self, cr, uid, ids, context=None): |
1465 | 74 | """ save defined settings in db """ | 69 | """ save defined settings in db """ |
1466 | 75 | |||
1467 | 76 | # get the wizard datas | 70 | # get the wizard datas |
1469 | 77 | wizard = self.browse(cr, uid, ids, context=context)[0] | 71 | wizard = self.browse(cr, uid, ids[0], context=context) |
1470 | 78 | 72 | ||
1472 | 79 | #retrieve the default cron values | 73 | # retrieve the default cron values |
1473 | 80 | reminder_obj = self.pool.get('hr.timesheet.reminder') | 74 | reminder_obj = self.pool.get('hr.timesheet.reminder') |
1474 | 81 | 75 | ||
1475 | 82 | values = {}.fromkeys(wizard._columns.keys(), False) | 76 | values = {}.fromkeys(wizard._columns.keys(), False) |
1476 | @@ -85,5 +79,3 @@ | |||
1477 | 85 | 79 | ||
1478 | 86 | reminder_obj.save_config(cr, uid, False, values, context=context) | 80 | reminder_obj.save_config(cr, uid, False, values, context=context) |
1479 | 87 | return {'type': 'ir.actions.act_window_close'} | 81 | return {'type': 'ir.actions.act_window_close'} |
1480 | 88 | |||
1481 | 89 | reminder_config() | ||
1482 | 90 | 82 | ||
1483 | === modified file 'hr_timesheet_reminder/wizard/reminder_config_view.xml' | |||
1484 | --- hr_timesheet_reminder/wizard/reminder_config_view.xml 2011-08-12 12:53:16 +0000 | |||
1485 | +++ hr_timesheet_reminder/wizard/reminder_config_view.xml 2012-12-13 17:06:20 +0000 | |||
1486 | @@ -5,29 +5,39 @@ | |||
1487 | 5 | <record id="view_hr_timesheet_reminder_config_form" model="ir.ui.view"> | 5 | <record id="view_hr_timesheet_reminder_config_form" model="ir.ui.view"> |
1488 | 6 | <field name="name">hr.timesheet.reminder.config.form</field> | 6 | <field name="name">hr.timesheet.reminder.config.form</field> |
1489 | 7 | <field name="model">hr.timesheet.reminder.config</field> | 7 | <field name="model">hr.timesheet.reminder.config</field> |
1490 | 8 | <field name="type">form</field> | ||
1491 | 9 | <field name="arch" type="xml"> | 8 | <field name="arch" type="xml"> |
1513 | 10 | <form string="Timesheet Reminder"> | 9 | <form string="Timesheet Reminder" version="7.0"> |
1514 | 11 | <group colspan="4" col="4" string="Periodicity" > | 10 | <sheet> |
1515 | 12 | <field name="reminder_active" /> | 11 | <group> |
1516 | 13 | <newline/> | 12 | <separator string="Periodicity" colspan="4"/> |
1517 | 14 | <field name="nextcall" attrs="{'required':[('reminder_active','==',True)]}"/> | 13 | <field name="reminder_active" /> |
1518 | 15 | <newline/> | 14 | <newline/> |
1519 | 16 | <field name="interval_number" string="Then send message every" attrs="{'required':[('reminder_active','==',True)]}"/> | 15 | <field name="nextcall" attrs="{'required':[('reminder_active','==',True)]}"/> |
1520 | 17 | <field name="interval_type" nolabel="1" attrs="{'required':[('reminder_active','==',True)]}"/> | 16 | <newline/> |
1521 | 18 | </group> | 17 | <label for="interval_number" string="Then send message every"/> |
1522 | 19 | 18 | <div> | |
1523 | 20 | <group colspan="4" col="2" string="Message"> | 19 | <field name="interval_number" class="oe_inline" |
1524 | 21 | <field name="reply_to"/> | 20 | attrs="{'required':[('reminder_active','==',True)]}"/> |
1525 | 22 | <field name="subject"/> | 21 | <field name="interval_type" nolabel="1" class="oe_inline" |
1526 | 23 | <field name="message" height="200"/> | 22 | attrs="{'required':[('reminder_active','==',True)]}"/> |
1527 | 24 | </group> | 23 | </div> |
1528 | 25 | 24 | </group> | |
1529 | 26 | <group colspan="4" col="6"> | 25 | |
1530 | 27 | <button icon="gtk-cancel" special="cancel" string="Cancel"/> | 26 | <group col="2"> |
1531 | 28 | <button icon="gtk-ok" string="Save configuration" name="save" type="object"/> | 27 | <separator string="Message" colspan="4"/> |
1532 | 29 | <button icon="gtk-ok" string="Run now" name="run" type="object"/> | 28 | <field name="reply_to"/> |
1533 | 30 | </group> | 29 | <field name="subject"/> |
1534 | 30 | <field name="message" height="200"/> | ||
1535 | 31 | </group> | ||
1536 | 32 | </sheet> | ||
1537 | 33 | |||
1538 | 34 | <footer> | ||
1539 | 35 | <button name="save" string="Save configuration" colspan="1" | ||
1540 | 36 | type="object" class="oe_highlight"/> or | ||
1541 | 37 | <button name="run" string="Run now" colspan="1" | ||
1542 | 38 | type="object" class="oe_highlight"/> or | ||
1543 | 39 | <button special="cancel" string="Cancel" class="oe_link"/> | ||
1544 | 40 | </footer> | ||
1545 | 31 | </form> | 41 | </form> |
1546 | 32 | </field> | 42 | </field> |
1547 | 33 | </record> | 43 | </record> |
1548 | @@ -42,11 +52,11 @@ | |||
1549 | 42 | </record> | 52 | </record> |
1550 | 43 | 53 | ||
1551 | 44 | <menuitem id="menu_hr_timesheet_reminder_config" | 54 | <menuitem id="menu_hr_timesheet_reminder_config" |
1557 | 45 | icon="STOCK_PRINT" | 55 | icon="STOCK_PRINT" |
1558 | 46 | action="action_hr_timesheet_reminder_config" | 56 | action="action_hr_timesheet_reminder_config" |
1559 | 47 | parent="hr.menu_hr_configuration" | 57 | parent="hr.menu_hr_configuration" |
1560 | 48 | name="Timesheet Reminder" | 58 | name="Timesheet Reminder" |
1561 | 49 | groups="base.group_hr_manager"/> | 59 | groups="base.group_hr_manager"/> |
1562 | 50 | 60 | ||
1564 | 51 | </data> | 61 | </data> |
1565 | 52 | </openerp> | 62 | </openerp> |
1566 | 53 | 63 | ||
1567 | === modified file 'hr_timesheet_reminder/wizard/reminder_status.py' | |||
1568 | --- hr_timesheet_reminder/wizard/reminder_status.py 2011-11-07 13:58:29 +0000 | |||
1569 | +++ hr_timesheet_reminder/wizard/reminder_status.py 2012-12-13 17:06:20 +0000 | |||
1570 | @@ -1,67 +1,58 @@ | |||
1571 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
1572 | 2 | ############################################################################## | 2 | ############################################################################## |
1573 | 3 | # | 3 | # |
1599 | 4 | # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com) | 4 | # Author: Arnaud Wüst (Camptocamp) |
1600 | 5 | # All Right Reserved | 5 | # Author: Guewen Baconnier (Camptocamp) (port to v7) |
1601 | 6 | # | 6 | # Copyright 2011-2012 Camptocamp SA |
1602 | 7 | # Author : Guewen Baconnier (Camptocamp) | 7 | # |
1603 | 8 | # | 8 | # This program is free software: you can redistribute it and/or modify |
1604 | 9 | # WARNING: This program as such is intended to be used by professional | 9 | # it under the terms of the GNU Affero General Public License as |
1605 | 10 | # programmers who take the whole responsability of assessing all potential | 10 | # published by the Free Software Foundation, either version 3 of the |
1606 | 11 | # consequences resulting from its eventual inadequacies and bugs | 11 | # License, or (at your option) any later version. |
1607 | 12 | # End users who are looking for a ready-to-use solution with commercial | 12 | # |
1608 | 13 | # garantees and support are strongly adviced to contract a Free Software | 13 | # This program is distributed in the hope that it will be useful, |
1609 | 14 | # Service Company | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
1610 | 15 | # | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1611 | 16 | # This program is Free Software; you can redistribute it and/or | 16 | # GNU Affero General Public License for more details. |
1612 | 17 | # modify it under the terms of the GNU General Public License | 17 | # |
1613 | 18 | # as published by the Free Software Foundation; either version 2 | 18 | # You should have received a copy of the GNU Affero General Public License |
1614 | 19 | # of the License, or (at your option) any later version. | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1590 | 20 | # | ||
1591 | 21 | # This program is distributed in the hope that it will be useful, | ||
1592 | 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1593 | 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1594 | 24 | # GNU General Public License for more details. | ||
1595 | 25 | # | ||
1596 | 26 | # You should have received a copy of the GNU General Public License | ||
1597 | 27 | # along with this program; if not, write to the Free Software | ||
1598 | 28 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
1615 | 29 | # | 20 | # |
1616 | 30 | ############################################################################## | 21 | ############################################################################## |
1617 | 31 | 22 | ||
1624 | 32 | import time | 23 | from openerp.osv import orm, fields |
1625 | 33 | 24 | ||
1626 | 34 | from osv import osv, fields | 25 | |
1627 | 35 | 26 | class reminder_status(orm.TransientModel): | |
1622 | 36 | |||
1623 | 37 | class reminder_status(osv.osv_memory): | ||
1628 | 38 | _name = 'hr.timesheet.reminder.status' | 27 | _name = 'hr.timesheet.reminder.status' |
1629 | 39 | 28 | ||
1630 | 40 | _columns = { | 29 | _columns = { |
1632 | 41 | 'company_ids': fields.many2many('res.company', 'reminder_company_rel', 'wid', 'rid', 'Company'), | 30 | 'company_ids': fields.many2many( |
1633 | 31 | 'res.company', | ||
1634 | 32 | 'reminder_company_rel', | ||
1635 | 33 | 'wid', | ||
1636 | 34 | 'rid', | ||
1637 | 35 | string='Company'), | ||
1638 | 42 | 'date': fields.date('End Date', required=True), | 36 | 'date': fields.date('End Date', required=True), |
1639 | 43 | } | 37 | } |
1640 | 44 | 38 | ||
1641 | 45 | _defaults = { | 39 | _defaults = { |
1643 | 46 | 'date': lambda *a: time.strftime('%Y-%m-%d'), | 40 | 'date': lambda *a: fields.date.today(), |
1644 | 47 | } | 41 | } |
1645 | 48 | 42 | ||
1652 | 49 | def print_report(self, cr, uid, ids, context): | 43 | def print_report(self, cr, uid, ids, context=None): |
1653 | 50 | if context is None: | 44 | form_values = self.read( |
1654 | 51 | context = {} | 45 | cr, uid, ids[0], ['company_ids', 'date'], context=context) |
1655 | 52 | 46 | ||
1656 | 53 | form_values = self.read(cr, uid, ids, ['company_ids', 'date'])[0] | 47 | # when no company is selected, select them all |
1651 | 54 | |||
1657 | 55 | if not form_values['company_ids']: | 48 | if not form_values['company_ids']: |
1658 | 56 | form_values['company_ids'] = self.pool.get('res.company').\ | 49 | form_values['company_ids'] = self.pool.get('res.company').\ |
1660 | 57 | search(cr, uid, [], context=context) | 50 | search(cr, uid, [], context=context) |
1661 | 51 | |||
1662 | 58 | data = {'ids': form_values['company_ids'], | 52 | data = {'ids': form_values['company_ids'], |
1663 | 59 | 'model': 'res.company', | 53 | 'model': 'res.company', |
1666 | 60 | 'form': {}} | 54 | 'form': form_values} |
1665 | 61 | data['form'].update(form_values) | ||
1667 | 62 | 55 | ||
1668 | 63 | return {'type': 'ir.actions.report.xml', | 56 | return {'type': 'ir.actions.report.xml', |
1669 | 64 | 'report_name': 'timesheet.reminder.status', | 57 | 'report_name': 'timesheet.reminder.status', |
1670 | 65 | 'datas': data} | 58 | 'datas': data} |
1671 | 66 | |||
1672 | 67 | reminder_status() | ||
1673 | 68 | 59 | ||
1674 | === modified file 'hr_timesheet_reminder/wizard/reminder_status_view.xml' | |||
1675 | --- hr_timesheet_reminder/wizard/reminder_status_view.xml 2011-08-12 12:53:16 +0000 | |||
1676 | +++ hr_timesheet_reminder/wizard/reminder_status_view.xml 2012-12-13 17:06:20 +0000 | |||
1677 | @@ -5,23 +5,24 @@ | |||
1678 | 5 | <record id="view_hr_timesheet_reminder_status_form" model="ir.ui.view"> | 5 | <record id="view_hr_timesheet_reminder_status_form" model="ir.ui.view"> |
1679 | 6 | <field name="name">hr.timesheet.reminder.status.form</field> | 6 | <field name="name">hr.timesheet.reminder.status.form</field> |
1680 | 7 | <field name="model">hr.timesheet.reminder.status</field> | 7 | <field name="model">hr.timesheet.reminder.status</field> |
1681 | 8 | <field name="type">form</field> | ||
1682 | 9 | <field name="arch" type="xml"> | 8 | <field name="arch" type="xml"> |
1688 | 10 | <form string="Reminder Status"> | 9 | <form string="Reminder Status" version="7.0"> |
1689 | 11 | <group colspan="4" col="4" string="Parameter"> | 10 | <sheet> |
1690 | 12 | <separator string="End Date (display the 5 timesheets previous to this date)" colspan="4"/> | 11 | <group> |
1691 | 13 | <field name="date" colspan="4" nolabel="1" width="400" /> | 12 | <label for="date" string="End Date (display the 5 timesheets previous to this date)"/> |
1692 | 14 | </group> | 13 | <field name="date" colspan="4" nolabel="1" width="400" /> |
1693 | 14 | </group> | ||
1694 | 15 | 15 | ||
1704 | 16 | <group colspan="4" col="4" string="Filter"> | 16 | <group> |
1705 | 17 | <separator string="Filter by a Company (leave empty to select all companies)" colspan="4"/> | 17 | <separator string="Companies" colspan="4"/> |
1706 | 18 | <field name="company_ids" colspan="4" nolabel="1"/> | 18 | <label for="company_ids" colspan="4" string="Filter by a Company (leave empty to select all companies)"/> |
1707 | 19 | <newline/> | 19 | <field name="company_ids" colspan="4" nolabel="1"/> |
1708 | 20 | </group> | 20 | </group> |
1709 | 21 | <group colspan="4" col="6"> | 21 | </sheet> |
1710 | 22 | <button icon="gtk-cancel" special="cancel" string="Cancel"/> | 22 | <footer> |
1711 | 23 | <button icon="gtk-print" string="Print" name="print_report" type="object" colspan="2" default_focus="1" /> | 23 | <button name="print_report" string="Print" colspan="1" type="object" class="oe_highlight"/> or |
1712 | 24 | </group> | 24 | <button special="cancel" string="Cancel" class="oe_link"/> |
1713 | 25 | </footer> | ||
1714 | 25 | </form> | 26 | </form> |
1715 | 26 | </field> | 27 | </field> |
1716 | 27 | </record> | 28 | </record> |
1717 | @@ -36,11 +37,11 @@ | |||
1718 | 36 | </record> | 37 | </record> |
1719 | 37 | 38 | ||
1720 | 38 | <menuitem id="menu_hr_timesheet_reminder_status" | 39 | <menuitem id="menu_hr_timesheet_reminder_status" |
1726 | 39 | icon="STOCK_PRINT" | 40 | icon="STOCK_PRINT" |
1727 | 40 | action="action_hr_timesheet_reminder_status" | 41 | action="action_hr_timesheet_reminder_status" |
1728 | 41 | parent="hr_timesheet.menu_hr_reporting_timesheet" | 42 | parent="hr.menu_hr_reporting_timesheet" |
1729 | 42 | name="Timesheet Status" | 43 | name="Timesheet Status" |
1730 | 43 | groups="base.group_hr_manager"/> | 44 | groups="base.group_hr_manager"/> |
1731 | 44 | 45 | ||
1733 | 45 | </data> | 46 | </data> |
1734 | 46 | </openerp> | 47 | </openerp> |
line 263: if status in ['Missing', 'Draft'] and employee. receive_ timesheet_ alerts
The second part of the test should be moved out to the outer loop (just after line 253) or even better to the search domain (line 243) : that way we don't make further queries for employees who don't recevie timesheet alerts (or am I missing something?)
653 + def _cron_nextcall(): SERVER_ DATETIME_ FORMAT,
654 + now = datetime.today() + timedelta(days=1)
655 + return time.strftime(
656 + DEFAULT_
657 + now.timetuple())
1 -> I'd rename 'now' to 'tomorrow' or 'when' DEFAULT_ SERVER_ DATETIME_ FORMAT) (or tomorrow. strftime. ..) instead of time.strftime(...) ?
2. why not use now.strftime(
There are probably a number of 'import time' which can be removed (pylint will tell you about unused imports)
607 + 'body_html': '<pre>%s</pre>' % message_ data.message,
I expect mail clients to be fairly tolerant in what they accept, but this makes me uncomfortable.
Have you tested what happens if the user has typed in:
* html (e.g. "fill your timesheet <b>now!</b>")
* a raw text message with HTML special chars inside (e.g: "I'm very disapointed you did not fill your timesheet <:-( I'll make sure your manager is notified & takes proper action.
Best chance would be to allow the edition of the body of the message with a rich text editor. Is there such widget available in the oerp7 client ?
803 -# Author: Arnaud WÃŒst
-> Mojibake! (but you fixed it)
1638 + 'date': time.strftime( '%Y-%m- %d'),
Is there not a constant defining this format?